Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DRM protected videos are playing in non-secure mode in few devices #1603

Closed
1 task
sharish opened this issue Aug 8, 2024 · 7 comments
Closed
1 task

DRM protected videos are playing in non-secure mode in few devices #1603

sharish opened this issue Aug 8, 2024 · 7 comments
Assignees

Comments

@sharish
Copy link

sharish commented Aug 8, 2024

Version

Media3 1.4.0

More version details

Issue

Since 1.4.0-alpha01, the DRM protected videos are playing in non-secure mode in few devices.

Why?

On Commit c872af4 -> We removed mediaCryptoRequiresSecureDecoder on MCR (MediaCodecRenderer) from field variable to local variable.

However the problem began on how we initialized the variable. Before this commit, the mediaCryptoRequiresSecureDecoder is initialized by following (Refer: MCR#1047)

mediaCryptoRequiresSecureDecoder =
          !frameworkCryptoConfig.forceAllowInsecureDecoderComponents
              && mediaCrypto.requiresSecureDecoderComponent(checkStateNotNull(mimeType));

But now, the initialization is happening based on codecDrmSession with the following code (Refer MCR#551)

boolean mediaCryptoRequiresSecureDecoder =
            codecDrmSession != null
                && codecDrmSession.requiresSecureDecoder(
                    checkStateNotNull(inputFormat.sampleMimeType));

The interesting point to note is the variable name is not changed to codecDrmSessionRequiresSecureDecoder with this change in initialisation but retained as mediaCryptoRequiresSecureDecoder even though initialisation is not done through MediaCrypto.

Note

The issue is not happening on all the devices but only few devices. The devices I noticed the issue are
Motorola G34 on Android 14
OnePlus Nord CE 3 Lite 5G on Android 13 (CPH2467)

Impact

  1. The devices are playing in non-secure mode - Causing screenshots are not being blacked out
  2. The playback at high speeds are stuttering.

Questions?

  1. Why does codecDrmSession.requiresSecureDecoder("video/avc") and mediaCrypto.requiresSecureDecoderComponent("video/avc") are not same on these devices? In fact Motorola G34 is a stock android where the issue happens. Does that mean it is a hardware level issue?

  2. Why do we have two function (mediaCrypto.requiresSecureDecoderComponent and codecDrmSession.requiresSecureDecoder) in two different classes if the purpose is same?

  3. How does secureness of the codec impact the audio playback? In both secure and non-secure mode, the codec initialised is the same hardware codec but in secure mode no stuttering is seen, but in non-secure mode stuttering is seen

Devices that reproduce the issue

Motorola G34 on Android 14
OnePlus Nord CE 3 Lite 5G on Android 13 (CPH2467)

Devices that do not reproduce the issue

Pixel 8 Pro on Android 14
OnePlus Pad on Android 14 (OPD2203)

Reproducible in the demo app?

Yes

Reproduction steps

  1. Check out demo app with commit c872af4
  2. Build the app and run the demo app on mentioned reproducible devices
  3. In demo app, Expand Widevine DASH (MP4, H264)
  4. Play HD (cenc) video
  5. Execute the adb command adb shell dumpsys media.resource_manager
  6. Take a screenshot

Expected result

At Step5, The expected codec attached is secure

Output of adb command ( In device CPH2467 on android 13)

      Client:
        Id: -5476376607769517440
        Name: c2.qti.avc.decoder.secure
        Resources:
          secure-codec/video-codec:[]:1
          battery/video-codec:[]:1

At Step6, The screenshot should be blacked-out (showcasing secureness)

Actual result

At Step5, The actual codec attached is non-secure

Output of adb command ( In device CPH2467 on android 13)

      Client:
        Id: -5476376610050332288
        Name: c2.qti.avc.decoder
        Resources:
          non-secure-codec/video-codec:[]:1
          battery/video-codec:[]:1

At Step6, The screenshot show the playback of video (showcasing insecure)

Media

Widevine DASH(MP4, H264) > HD (cenc)

Bug Report

@icbaker
Copy link
Collaborator

icbaker commented Aug 8, 2024

Thanks for reporting - would you be able to run the following code on the devices that do and don't reproduce the issue, and let us know the results?

try (MediaDrm mediaDrm = new MediaDrm(C.WIDEVINE_UUID)) {
  Log.w(
    "issue-1603", 
    "MediaDrm[version]=" 
        + mediaDrm.getPropertyString("version")
        + ", MediaDrm[oemCryptoApiVersion]=" 
        + mediaDrm.getPropertyString("oemCryptoApiVersion"));
} 

We have a hypothesis this might be related to the Widevine plug-in implementation underneath MediaDrm but having the version numbers would help confirm this.

@sharish
Copy link
Author

sharish commented Aug 9, 2024

Here are the logs:

Devices that reproduce the issue

Device OS Log info
Moto G34 Android 14 MediaDrm[version]=16.0.1@001, MediaDrm[oemCryptoApiVersion]=16
OnePlus Nord CE 3 Lite 5G Android 13 MediaDrm[version]=16.0.0, MediaDrm[oemCryptoApiVersion]=16

Devices that do not reproduce the issue

Device OS Log info
Pixel 8 Pro Android 14 MediaDrm[version]=19.0.1@AV1A.240428.001, MediaDrm[oemCryptoApiVersion]=18
OnePlus Pad Android 14 MediaDrm[version]=16.1.1@006, MediaDrm[oemCryptoApiVersion]=16

@icbaker
Copy link
Collaborator

icbaker commented Aug 12, 2024

Thanks for the additional information. I think there are two related, but separate, issues here:

  1. MediaDrm.requiresSecureDecoder incorrectly returns false for Widevine on version=16.0 (and earlier). It works correctly from version=16.1. I have an internal change under review to check the version in FrameworkMediaDrm before relying on this method.
  2. You seem surprised that your content can be played without Widevine L1 hardware secure playback. If this is a concern then you should enforce this restriction in your license policy. This part isn't a bug in ExoPlayer or the DRM framework.
    • Note: If you had L1 enforced in your license you would likely see a Operation not supported in this configuration: ERROR_DRM_CANNOT_HANDLE error currently, due to (1).
    • If you're a Widevine partner you can also get support for correctly configuring your license policy directly from them: https://www.widevine.com/contact.

@sharish
Copy link
Author

sharish commented Aug 13, 2024

Thanks for the details @icbaker.

We have L1 enforcement through our widevine partners and I feel that is not the issue. My question was

When the video plays in non-secure-codec mode, the audio stutters a lot while the same in secure-codec, it is playing fine. I'm aware about the buffer-underflow issue the devices generally faces for stuttering, however this seems to be a peculiar way for a device to stutter. If you have any insights on why devices stutter in 'non-secure-codec but hardwareAccelaration enbled codecs', it will be a great learning.

Thanks in advance.

@icbaker
Copy link
Collaborator

icbaker commented Aug 13, 2024

I'm afraid we don't have info on the device-specific behaviours of the different codecs - but I think your issue should be resolved once we stop relying on MediaDrm.requiresSecureDecoder on CDM version 16.0.0.

@rbugajewski
Copy link

@icbaker Thanks for confirmation. After we upgraded to 1.4.0 we are also seeing this issue on certain devices. I ran a debug session today, and got the following output on a Samsung Galaxy Tab A8 (Android 14):

MediaDrm[version]=16.0.1@005, MediaDrm[oemCryptoApiVersion]=16

This would confirm your hypothesis that the underlying plug-in implementation could be responsible.

copybara-service bot pushed a commit that referenced this issue Aug 14, 2024
This method was added in API 31 (S) but it's non-functional
(incorrectly, silently, returns `false`) on the Widevine plugin version
(`16.0`) from R (API 30), which some devices up to at least API 34 are
still using.

This results in ExoPlayer incorrectly selecting an insecure decoder for
L1 secure content, and subsequently calling
`MediaCodec.queueInputBuffer` instead of `queueSecureInputBuffer`,
which is not supported and generates the following error:
> Operation not supported in this configuration: ERROR_DRM_CANNOT_HANDLE

Issue: #1603

#cherrypick

PiperOrigin-RevId: 662852176
@icbaker
Copy link
Collaborator

icbaker commented Aug 14, 2024

The workaround for this issue is linked above. This should be included in the 1.4.1 release.

@icbaker icbaker closed this as completed Aug 14, 2024
copybara-service bot pushed a commit that referenced this issue Aug 14, 2024
Previous to this change, `FrameworkMediaDrm.requiresSecureDecoder`
ignores its `sessionId` parameter on API 31+, and uses only the
`mimeType` parameter. This means the result [assumes the session is
opened at the 'default security
level'](https://developer.android.com/reference/android/media/MediaDrm#requiresSecureDecoder(java.lang.String)):
> The default security level is defined as the highest security level
> supported on the device.

This change is a no-op in all (?) cases, because the `ExoMediaDrm`
interface only exposes the zero-arg `openSession()` method, which in the
framework case **also** assumes the highest security level is preferred:
> By default, sessions are opened at the native security level of the
> device.

However, it seems more obviously correct to only make this
"highest/native security level" assumption in one place
(`openSession()`), and check the session's **actual** security level
everywhere else.

Issue: #1603
PiperOrigin-RevId: 662872860
colinkho pushed a commit to colinkho/media that referenced this issue Aug 15, 2024
This method was added in API 31 (S) but it's non-functional
(incorrectly, silently, returns `false`) on the Widevine plugin version
(`16.0`) from R (API 30), which some devices up to at least API 34 are
still using.

This results in ExoPlayer incorrectly selecting an insecure decoder for
L1 secure content, and subsequently calling
`MediaCodec.queueInputBuffer` instead of `queueSecureInputBuffer`,
which is not supported and generates the following error:
> Operation not supported in this configuration: ERROR_DRM_CANNOT_HANDLE

Issue: androidx#1603

#cherrypick

PiperOrigin-RevId: 662852176
colinkho pushed a commit to colinkho/media that referenced this issue Aug 15, 2024
Previous to this change, `FrameworkMediaDrm.requiresSecureDecoder`
ignores its `sessionId` parameter on API 31+, and uses only the
`mimeType` parameter. This means the result [assumes the session is
opened at the 'default security
level'](https://developer.android.com/reference/android/media/MediaDrm#requiresSecureDecoder(java.lang.String)):
> The default security level is defined as the highest security level
> supported on the device.

This change is a no-op in all (?) cases, because the `ExoMediaDrm`
interface only exposes the zero-arg `openSession()` method, which in the
framework case **also** assumes the highest security level is preferred:
> By default, sessions are opened at the native security level of the
> device.

However, it seems more obviously correct to only make this
"highest/native security level" assumption in one place
(`openSession()`), and check the session's **actual** security level
everywhere else.

Issue: androidx#1603
PiperOrigin-RevId: 662872860
tianyif pushed a commit that referenced this issue Aug 22, 2024
This method was added in API 31 (S) but it's non-functional
(incorrectly, silently, returns `false`) on the Widevine plugin version
(`16.0`) from R (API 30), which some devices up to at least API 34 are
still using.

This results in ExoPlayer incorrectly selecting an insecure decoder for
L1 secure content, and subsequently calling
`MediaCodec.queueInputBuffer` instead of `queueSecureInputBuffer`,
which is not supported and generates the following error:
> Operation not supported in this configuration: ERROR_DRM_CANNOT_HANDLE

Issue: #1603

#cherrypick

PiperOrigin-RevId: 662852176
(cherry picked from commit ca455ee)
@androidx androidx locked and limited conversation to collaborators Oct 14, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants