Product Updates | Contact Support | System Status
Page Contents

    Fallback HDCP with the Native SDKs

    In this topic, you will learn about using HDCP-protected content with the Brightcove Native SDKs to block streaming on unauthorized devices, and provide a standard definition fallback for devices that do not support HDCP.

    Introduction

    High-bandwidth Digital Content Protection (HDCP) is a form of digital copy protection used to protect high definition (HD) video and audio signals from being copied on unauthorized devices. The transmitting device first checks if the receiver is authorized to receive data. If yes, then the transmitter sends encrypted data to prevent eavesdropping.

    The receiving setup must be HDCP compliant, including devices, cables, adaptors, and software drivers. If the receiver is not HDCP compliant, then video will play in standard definition (SD) only. In general, newer HDTVs and HDMI or DVI cables should be HDCP compliant.

    For details, see the Fallback HDCP document.

    Requirements

    The following requirements are needed to support this feature:

    Brightcove SDK version

    • Native SDK for Android 6.17.1 and newer
    • Native SDK for iOS 6.10.1 and newer

    Android implementation

    When playing a video with a mix of SD and HD renditions, you will want to do the following:

    • On devices that can support both HD and SD playback, you want to ensure that the player can switch to an appropriate rendition when needed, for example when network bandwidth changes. This rendition switch may require the player to select an SD rendition, where before it was playing HD.

    • On devices without HDCP support, for example older Android mobile devices with older OS levels, you want to ensure appropriate rendition switching within SD renditions, as well as guarding that the player will not attempt to load an HDCP-protected rendition, at which point the license request will fail, with an error message like this:

      Error message

      2021-11-01 19:01:36.943 30131-30131/com.brightcove.player.samples.exoplayer.basic E/VideoDisplayComponent: onPlayerError
        com.google.android.exoplayer2.ExoPlaybackException: MediaCodecVideoRenderer error, index=0, format=Format(bf310894-59b5-4f1b-9a37-e110a3d6121d, null, null, video/avc, avc1.4D401F, 1712000, null, [1280, 720, 30.0], [-1, -1]), format_supported=YES
            at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:555)
            at android.os.Handler.dispatchMessage(Handler.java:98)
            at android.os.Looper.loop(Looper.java:148)
            at android.os.HandlerThread.run(HandlerThread.java:61)
          Caused by: android.media.MediaCodec$CryptoException: Unknown Error
            at android.media.MediaCodec.native_queueSecureInputBuffer(Native Method)
            at android.media.MediaCodec.queueSecureInputBuffer(MediaCodec.java:2292)
            at com.google.android.exoplayer2.mediacodec.SynchronousMediaCodecAdapter.queueSecureInputBuffer(SynchronousMediaCodecAdapter.java:143)
            at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.feedInputBuffer(MediaCodecRenderer.java:1380)
            at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.render(MediaCodecRenderer.java:845)
            at com.google.android.exoplayer2.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:945)
            at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:478)
            at android.os.Handler.dispatchMessage(Handler.java:98) 
            at android.os.Looper.loop(Looper.java:148) 
            at android.os.HandlerThread.run(HandlerThread.java:61) 
      

    A solution to this problem involves adding code to allow rendition switching across AdaptationSets in DASH, and to filter out renditions that are not expected to play on the current device.

    In the following example, HD renditions are being protected by stricter HDCP requirements which the current device doesn't support. To achieve this, you can use an EventListener on the SET_SOURCE event, as you need to add these supports before creating the ExoPlayer instance.

    An example like this would be added to your player’s Activity class, in the onCreate method:

    eventEmitter.on(EventType.SET_SOURCE, event -> {
      try {
          // Get an instance of the MediaDrm from the device
          MediaDrm mediaDrm = new MediaDrm(Constants.WIDEVINE_UUID);
    
          // Create a new DefaultTrackSelector.ParamsBuilder object
          DefaultTrackSelector.ParametersBuilder builder = new DefaultTrackSelector.ParametersBuilder(this);
    
      // Call this method to enable rendition switching across DASH AdaptationSets
          builder.setAllowMultipleAdaptiveSelections(true);
    
          // Get the values for hdcpLevel and maxHdcpLevel
          String connectedHdcpLevel = mediaDrm.getPropertyString("hdcpLevel");
          String maxHdcpLevel = mediaDrm.getPropertyString("maxHdcpLevel");
          Log.v(TAG, "HDCP Level: " + connectedHdcpLevel + " Max HDCP Level: " + maxHdcpLevel);
    
          // If either level is null or an empty String or reads "Unprotected"
          if ((TextUtils.isEmpty(connectedHdcpLevel) || TextUtils.isEmpty(maxHdcpLevel)) ||
                  ("Unprotected".equals(connectedHdcpLevel) || "Unprotected".equals(maxHdcpLevel))) {
              Log.v(TAG, "Restricting rendition selection to SD");
    
              // Set the max video size to SD
              builder.setMaxVideoSizeSd();
          }
    
      // Create a new DefaultTrackSelector object, and set the Parameters object created above
          DefaultTrackSelector defaultTrackSelector = new DefaultTrackSelector(this);
          defaultTrackSelector.setParameters(builder.build());
          // Set this DefaultTrackSelector object on the ExoPlayerVideoDisplayComponent
          videoDisplayComponent.setTrackSelector(defaultTrackSelector);
      }
      catch (Exception exception) {
          if (exception instanceof UnsupportedSchemeException) {
              Log.e(TAG, "UnsupportedSchemeException: " + exception.getLocalizedMessage());
          }
          else {
              Log.e(TAG, "An unexpected error occurred: " + exception.getLocalizedMessage());
          }
      }
    });             
        

    iOS implementation

    Fallback HDCP is supported with the Native SDK for iOS/tvOS, but is only enforced for content protected using FairPlay.

    You can detect the non-compliance of the device, by using KVC to detect changes to the value isOutputObscuredDueToInsufficientExternalProtection on the AVPlayer.

    AVPlayer.isOutputObscuredDueToInsufficientExternalProtection == true
    

    The above property's value changes to true for the following reasons:

    • The current item requires external protection
    • The device does not meet the protection level
    • The user observes video loss

    Since not all users have an HDCP compatable setup, Apple recommends including a variant (rendition) in the video manifest which does not require HDCP protection. Brightcove handles this for you when your account is enabled for Fallback HDCP.


    Page last updated on 06 Jan 2022