Search code examples
androiddrmexoplayerexoplayer2.x

Exoplayer2 DRM exception shouldWaitForKeys


Hi i'm switching my working DRM implementation from Exoplayer 1.x to Exoplayer 2.x but i have some exceptions and i can not playback the DRM licensed video. My drm session manager:

    protected DrmSessionManager<FrameworkMediaCrypto> generateDrmSessionManager() {
        // DRM is only supported on API 18 + in the ExoPlayer
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
            return null;
        }

        // Widevine will capture the majority of use cases however playready is supported on all AndroidTV devices
        UUID uuid = C.WIDEVINE_UUID;
        final String licenseURL = "http://video.aaa";

        HttpMediaDrmCallback drmCallback =
                new HttpMediaDrmCallback(
                        licenseURL, buildHttpDataSourceFactory(false), KEY_REQUEST_PROPERTIES);

        try {
            return new DefaultDrmSessionManager<>(
                    uuid, FrameworkMediaDrm.newInstance(uuid), drmCallback, null, mainHandler, capabilitiesListener);
        } catch (Exception e) {
            Log.d(TAG, "Unable to create a DrmSessionManager due to an exception", e);
            return null;
        }
    }

I have the drmSession and then when HttpMediaDrmCallback executes

executePost(dataSourceFactory, url, request.getData(), requestProperties);

inside there is the call that causes the onDrmSessionManagerError (java.lang.IllegalArgumentException) when calling:

HttpDataSource dataSource = dataSourceFactory.createDataSource();

at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.shouldWaitForKeys(MediaCodecRenderer.java:735) at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.feedInputBuffer(MediaCodecRenderer.java:676) at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.render(MediaCodecRenderer.java:511) at com.google.android.exoplayer2.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:479) at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:308) at android.os.Handler.dispatchMessage(Handler.java:98) at android.os.Looper.loop(Looper.java:135) at android.os.HandlerThread.run(HandlerThread.java:61) Caused by: com.google.android.exoplayer2.drm.DrmSession$DrmSessionException: java.lang.IllegalArgumentException at com.google.android.exoplayer2.drm.DefaultDrmSessionManager.onError(DefaultDrmSessionManager.java:604) at com.google.android.exoplayer2.drm.DefaultDrmSessionManager.onKeysError(DefaultDrmSessionManager.java:599) at com.google.android.exoplayer2.drm.DefaultDrmSessionManager.onKeyResponse(DefaultDrmSessionManager.java:559) at com.google.android.exoplayer2.drm.DefaultDrmSessionManager.access$900(DefaultDrmSessionManager.java:50) at com.google.android.exoplayer2.drm.DefaultDrmSessionManager$PostResponseHandler.handleMessage(DefaultDrmSessionManager.java:679) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:135)  at android.os.HandlerThread.run(HandlerThread.java:61)  Caused by: java.lang.IllegalArgumentException at com.google.android.exoplayer2.util.Assertions.checkNotEmpty(Assertions.java:138) at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.(DefaultHttpDataSource.java:148) at com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory.createDataSourceInternal(DefaultHttpDataSourceFactory.java:81) at com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory.createDataSourceInternal(DefaultHttpDataSourceFactory.java:22) at com.google.android.exoplayer2.upstream.HttpDataSource$BaseFactory.createDataSource(HttpDataSource.java:176) at com.google.android.exoplayer2.drm.HttpMediaDrmCallback.executePost(HttpMediaDrmCallback.java:139) at com.google.android.exoplayer2.drm.HttpMediaDrmCallback.executeKeyRequest(HttpMediaDrmCallback.java:134) at com.google.android.exoplayer2.drm.DefaultDrmSessionManager$PostRequestHandler.handleMessage(DefaultDrmSessionManager.java:702) at android.os.Handler.dispatchMessage(Handler.java:102)  at android.os.Looper.loop(Looper.java:135)  at android.os.HandlerThread.run(HandlerThread.java:61) 

I do not know why it crashes, as in my Exoplayer 1.x implementation was working good.


Solution

  • The exception is caused by the userAgent string being null or zero length here:

    public DefaultHttpDataSource(String userAgent, Predicate<String> contentTypePredicate,
          TransferListener<? super DefaultHttpDataSource> listener, int connectTimeoutMillis,
          int readTimeoutMillis, boolean allowCrossProtocolRedirects,
          RequestProperties defaultRequestProperties) {
        this.userAgent = Assertions.checkNotEmpty(userAgent);  <---- Exception is here
        this.contentTypePredicate = contentTypePredicate;
        this.listener = listener;
        this.requestProperties = new RequestProperties();
        this.connectTimeoutMillis = connectTimeoutMillis;
        this.readTimeoutMillis = readTimeoutMillis;
        this.allowCrossProtocolRedirects = allowCrossProtocolRedirects;
        this.defaultRequestProperties = defaultRequestProperties;
      }
    

    The userAgent is a combination of the application name and the ExoPlayer library version:

    /**
       * Returns a user agent string based on the given application name and the library version.
       *
       * @param context A valid context of the calling application.
       * @param applicationName String that will be prefix'ed to the generated user agent.
       * @return A user agent string generated using the applicationName and the library version.
       */
      public static String getUserAgent(Context context, String applicationName) {
        String versionName;
        try {
          String packageName = context.getPackageName();
          PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
          versionName = info.versionName;
        } catch (NameNotFoundException e) {
          versionName = "?";
        }
        return applicationName + "/" + versionName + " (Linux;Android " + Build.VERSION.RELEASE
            + ") " + ExoPlayerLibraryInfo.VERSION_SLASHY;
      }
    

    You can see examples of it being used and set in the demo application: https://github.com/google/ExoPlayer/tree/release-v2/demo

    There is also information about setting up the MediaPlayer in the documentation (https://google.github.io/ExoPlayer/guide.html) which includes this example showing the userAgent being set:

    // Measures bandwidth during playback. Can be null if not required.
    DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
    // Produces DataSource instances through which media data is loaded.
    DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(context,
        Util.getUserAgent(context, "yourApplicationName"), bandwidthMeter);
    // Produces Extractor instances for parsing the media data.
    ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
    // This is the MediaSource representing the media to be played.
    MediaSource videoSource = new ExtractorMediaSource(mp4VideoUri,
        dataSourceFactory, extractorsFactory, null, null);
    // Prepare the player with the source.
    player.prepare(videoSource);