shaka-project / shaka-player-embedded

Shaka Player in a C++ Framework
Apache License 2.0
239 stars 62 forks source link

Different Widevine DRM errors for online or offline contents #143

Closed OmarPedraza closed 4 years ago

OmarPedraza commented 4 years ago

Hi there, We're working on updating our current Widevine CDM 14.4.1 for offline playback to newer Widevine CDM 15.2.3, and, as you know, that implies to use your Shaka Player Embedded SDK.

First of all, we've used the following commands for embedding Widevine into Shaka Player:

For debugging in simulator:

> ../configure --ios --eme-impl ../../WidevineCDM/shaka_plugin/dev_cdm.json --ide xcode --debug --cpu x64
> make

For debugging in devices:

> ../configure --ios --eme-impl ../../WidevineCDM/shaka_plugin/dev_cdm.json --ide xcode --debug --cpu arm64
> make

For release version:

> ../configure --ios --eme-impl ../../WidevineCDM/shaka_plugin/release_cdm.json --ide xcode --release --cpu arm64
> make

After running those commands, we joined both debug frameworks using lipo into one containing all needed architectures.

Finally we embedded ShakaPlayerEmbedded.FFmpeg.framework, ShakaPlayerEmbedded.framework and widevine_cdm_secured_ios.framework in our project's target.

Nevertheless, when trying to start downloading an asset, I'm receiving the following error:

Optional(Error Domain=ShakaPlayerErrorDomain Code=6001 "Shaka Error DRM.REQUESTED_KEY_SYSTEM_CONFIG_UNAVAILABLE ()" UserInfo={NSLocalizedDescription=Shaka Error DRM.REQUESTED_KEY_SYSTEM_CONFIG_UNAVAILABLE (), ShakaPlayerErrorSeverityKey=2, ShakaPlayerErrorCategoryKey=6})

This error should appear when DRM is not supported and, even reading as much documentation and issues as I found, I've not been able to fix it.

This is the code I've using right now:

element.getAsset(forOffline: true) { (asset, error) in
    if error == nil, let asset = asset as? DRMAsset, case .widevine(let releasePID) = asset.security, let mpx = self.mpxToken {
        self.showOfflineConfiguration(force: false) {
            self.storage?.configure("drm.servers.\(Constants.Downloads.widevineServerName)", with: "\(self.playbackConfiguration.widevineLicenseServer)?account=\(self.playbackConfiguration.mpxAccountID)&form=json&_releasePid=\(releasePID)&schema=1.0&token=\(mpx.token)&_widevineChallenge=")
            self.storage?.store(asset.url.absoluteString) { (content, failure) in
                print(failure)
            }
        }
    } else {
        completion?(error)
    }
}

self.storage is an instance of ShakaPlayerStorage initialized in the following way:

storage = try? ShakaPlayerStorage()
storage?.configure("drm.advanced.\(Constants.Downloads.widevineServerName).persistentStateRequired", with: true)

being Constants.Downloads.widevineServerName equal to "com\\.widevine\\.alpha".

Regarding DRM server, I've tried several things for it: use just our Widevine license server URL, add all parameters you can see in the code, refresh MPX token,... but nothing worked.

This is the whole log I'm receiving in the app:

[Info]: "No Period ID given for Period with start time 0,  Assigning a default"
WARNING: Logging before InitGoogleLogging() is written to STDERR
E0618 13:48:19.606775 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.607038 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.607079 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.607115 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.607151 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.607342 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.607384 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.607419 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.607456 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.607492 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.607527 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.607563 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.607599 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.607633 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.607667 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.607867 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.608242 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.608294 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.608386 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.608428 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.608464 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.608592 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.608640 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.608676 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.608712 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.608748 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.608935 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.608975 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.609102 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.609139 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.609175 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.609211 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.609246 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.609338 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.609375 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.609419 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.609611 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.609652 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.609689 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.609725 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.609812 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.609850 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.609894 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.609930 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.610059 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.610095 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.610286 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.610343 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
E0618 13:48:19.610378 1865887744 media_source.cc:97] Unable to find a MediaPlayer instance to query
Optional(Error Domain=ShakaPlayerErrorDomain Code=6001 "Shaka Error DRM.REQUESTED_KEY_SYSTEM_CONFIG_UNAVAILABLE ()" UserInfo={NSLocalizedDescription=Shaka Error DRM.REQUESTED_KEY_SYSTEM_CONFIG_UNAVAILABLE (), ShakaPlayerErrorSeverityKey=2, ShakaPlayerErrorCategoryKey=6})

It seems like the SDK is not able to find MediaPlayer from Widevine CDM SDK, have we done anything wrong while integrating it?

But if that's the problem, there's something that we don't understand.

We tried to play the same content online just for seeing if problem is related to offline storage and with the following code:

viewModel.playableElement?.getAsset(forOffline: true) { (asset, _) in
    // Make a Shaka Player with its corresponding view.
    if let player = try? ShakaPlayer(), let asset = asset as? DRMAsset, case .widevine(let releasePID) = asset.security, let mpx = self.mpxToken {
        player.configure("drm.servers.\(Constants.Downloads.widevineServerName)", with: "\(self.playbackConfiguration.widevineLicenseServer)?account=\(self.playbackConfiguration.mpxAccountID)&form=json&_releasePid=\(releasePID)&schema=1.0&token=\(mpx.token)&_widevineChallenge=")

        let playerView = ShakaPlayerView(player: player)
        playerView.frame = self.view.bounds
        self.view.addSubview(playerView)

        // Load and play an asset.
        player.load(asset.url.absoluteString) {
            if let error = $0 {
                print("Error loading manifest: \(error.message)")
            } else {
                player.play()
            }
        }
    }
}

Which uses the same configuration for DRM server and asset (offline flag is for returning a MPD instead of the M3U8 format we're using for online playback), the error we're receiving is not the same, as you can see here:

[Info]: "Starting attach..."
[Info]: "Starting load of https://loudamdprod.akamaized.net/prod/dash/protected/NBCU_INTL_Production_-_Main/818/256/191120_4071183_Training_Days_4000_1579017934259.mpd..."
[Info]: "No Period ID given for Period with start time 0,  Assigning a default"
[Log]: "Found variant with audio and video content, so filtering out audio-only content in all periods."
[Info]: "Created MediaKeys object for key system"   "com.widevine.alpha"
[Log]: "codecs" "avc1-mp4a" "avg bandwidth" 1658195.4285714286
[Log]: "onChooseStreams_"   {startTime:0, textStreams:[...], variants:[...]}
[Log]: "Choosing new streams after period changed"
[Log]: "init: completed initial Stream setup"
[Warn]: "No preferred audio language set.  We will choose an arbitrary language initially"
[Log]: "(audio:15)" "looking up segment:"   "presentationTime=0"    "currentPeriod.startTime=0"
[Log]: "(video:7)"  "looking up segment:"   "presentationTime=0"    "currentPeriod.startTime=0"
[mov,mp4,m4a,3gp,3g2,mj2 @ 0x109af3a00] Protocol name not provided, cannot determine if input is local or a network protocol, buffers and access patterns cannot be configured optimally without knowing the protocol
[INFO:/Users/modmaker/Code/ios-sdk/oemcrypto-arxan/third_party/cdm/core/src/cdm_engine.cpp(120):OpenSession] CdmEngine::OpenSession
[DEBUG:/Users/modmaker/Code/ios-sdk/oemcrypto-arxan/third_party/cdm/core/src/crypto_session.cpp(644):Open] CryptoSession::Open: requested_security_level: Default
[INFO:/Users/modmaker/Code/ios-sdk/oemcrypto-arxan/third_party/cdm/core/src/usage_table_header.cpp(38):Init] UsageTableHeader::Init: security level: 3
[INFO:/Users/modmaker/Code/ios-sdk/oemcrypto-arxan/third_party/cdm/core/src/usage_table_header.cpp(70):Init] UsageTableHeader::Init: number of usage entries: 0
[INFO:/Users/modmaker/Code/ios-sdk/oemcrypto-arxan/third_party/cdm/core/src/device_files.cpp(155):ExtractDeviceInfo] ExtractDeviceInfo Entry
[INFO:/Users/modmaker/Code/ios-sdk/oemcrypto-arxan/third_party/cdm/core/src/cdm_engine.cpp(156):OpenSession] CdmEngine::OpenSession: ksidCEE8C6DF
[mov,mp4,m4a,3gp,3g2,mj2 @ 0x109915000] Failed to seek for auxiliary info, will only parse senc atoms for encryption info
[mov,mp4,m4a,3gp,3g2,mj2 @ 0x109915000] Protocol name not provided, cannot determine if input is local or a network protocol, buffers and access patterns cannot be configured optimally without knowing the protocol
[Log]: "Ignoring duplicate init data."
[Log]: "(video:7)"  "startup complete"
[Log]: "(all) setting up Period 0"
[ERROR:/Users/modmaker/Code/ios-sdk/oemcrypto-arxan/third_party/cdm/core/src/cdm_engine.cpp(1712):Decrypt] CdmEngine::Decrypt: session not found: Empty session ID
[ERROR:/Users/modmaker/Code/ios-sdk/oemcrypto-arxan/third_party/cdm/cdm/src/cdm.cpp(1203):decrypt] Key not available.
[Log]: "(all) Stream 7 is being or has been set up"
[Log]: "(all) Stream 15 is being or has been set up"
[Log]: "(all) Period 0 is being or has been set up"
[ERROR:/Users/modmaker/Code/ios-sdk/oemcrypto-arxan/third_party/cdm/core/src/cdm_engine.cpp(1712):Decrypt] CdmEngine::Decrypt: session not found: Empty session ID
[ERROR:/Users/modmaker/Code/ios-sdk/oemcrypto-arxan/third_party/cdm/cdm/src/cdm.cpp(1203):decrypt] Key not available.
WARNING: Logging before InitGoogleLogging() is written to STDERR
E0618 14:28:46.860808 1864069120 xml_http_request.cc:509] Error returned by curl: 56
[ERROR:/Users/modmaker/Code/ios-sdk/oemcrypto-arxan/third_party/cdm/core/src/cdm_engine.cpp(1712):Decrypt] CdmEngine::Decrypt: session not found: Empty session ID
[ERROR:/Users/modmaker/Code/ios-sdk/oemcrypto-arxan/third_party/cdm/cdm/src/cdm.cpp(1203):decrypt] Key not available.

We're sure we're missing something or we did something wrong but what? Can you help us?

Thanks!

TheModMaker commented 4 years ago

The MediaPlayer error could probably be ignored, but I'm not really sure why that is happening. Can you look at the Shaka Player demo assets and try one of the Widevine assets? Can you also set the GLOG_v=1 environment variable and see what you get? When playing content, you're getting a network error, right? It appears you are getting a CURLE_RECV_ERROR error about receiving data from the server. Are you adding a ShakaPlayerClient, and are you getting onPlayer:error: calls?

OmarPedraza commented 4 years ago

Hi @TheModMaker, I've been doing some new tests with GLOG_v=1 environment variable set.

First of all, I've had the same result for offline storage by doing the following test:

self.storage?.configure("drm.servers.\(Constants.Downloads.widevineServerName)", with: "https://cwip-shaka-proxy.appspot.com/no_auth")
self.storage?.store("https://storage.googleapis.com/shaka-demo-assets/angel-one-widevine/dash.mpd") { (content, failure) in
    print(failure)
}
WARNING: Logging before InitGoogleLogging() is written to STDERR
I0619 12:56:53.547833 1866674176 js_engine.cc:33] Begin GC run
I0619 12:56:53.558997 1866674176 object_tracker.cc:143] Deleted 830 object(s).
I0619 12:56:53.561655 1866674176 js_engine.cc:44] End GC run
E0619 12:56:54.158365 1866674176 media_source.cc:97] Unable to find a MediaPlayer instance to query
...
E0619 12:56:54.171846 1866674176 media_source.cc:97] Unable to find a MediaPlayer instance to query
I0619 12:56:54.171926 1866674176 search_registry.cc:207] None of the video capabilities are supported
Optional(Error Domain=ShakaPlayerErrorDomain Code=6001 "Shaka Error DRM.REQUESTED_KEY_SYSTEM_CONFIG_UNAVAILABLE ()" UserInfo={NSLocalizedDescription=Shaka Error DRM.REQUESTED_KEY_SYSTEM_CONFIG_UNAVAILABLE (), ShakaPlayerErrorSeverityKey=2, ShakaPlayerErrorCategoryKey=6})
I0619 12:57:23.546689 1866674176 js_engine.cc:33] Begin GC run
I0619 12:57:23.566701 1866674176 object_tracker.cc:143] Deleted 269 object(s).
I0619 12:57:23.569460 1866674176 js_engine.cc:44] End GC run

Nevertheless, I've added a ShakaPlayerClient and made a test for online playback using the same demo asset as you can see here:

// Make a Shaka Player with its corresponding view.
if let player = try? ShakaPlayer() {
    player.client = self

    player.configure("drm.servers.\(Constants.Downloads.widevineServerName)", with: "https://cwip-shaka-proxy.appspot.com/no_auth")

    let playerView = ShakaPlayerView(player: player)
    playerView.frame = self.view.bounds
    self.view.addSubview(playerView)

    // Load and play an asset.
    player.load("https://storage.googleapis.com/shaka-demo-assets/angel-one-widevine/dash.mpd") {
        if let error = $0 {
            print("Error loading manifest: \(error.message)")
        } else {
            player.play()
        }
    }
}
extension ShowHeaderController: ShakaPlayerClient {
    func onPlayer(_ player: ShakaPlayer, error: ShakaPlayerError) {
        print("error: \(error)")
    }
}

And it worked correctly. Is that demo asset not ready for persistent storage?

After these results, it seems like something is wrong with our server, but it was working with the previous version of Widevine. What has to be changed? Any other idea?

Thanks @TheModMaker!

TheModMaker commented 4 years ago

What's happening is when we query EME for support, we need to check which codecs are supported. To do this, we need a MediaPlayer instance to query. Normally, one exists while playing and we can just pick one. But if you are storing something, you don't need a MediaPlayer to be alive.

A workaround is to create a ShakaPlayer instance, which should create the default MediaPlayer instance. But we should fix this to work even without any ShakaPlayer objects alive.

As for the network error, it make be a red herring. It may be caused by us aborting the request when the codec error happens. If you create a ShakaPlayer instance first, does the error still appear?

OmarPedraza commented 4 years ago

Hi @TheModMaker, Thanks for you help!

After creating a persistent instance of ShakaPlayer (very important, it has to be persistent during ShakaPlayerStorage lifecycle) it started downloading the asset. Nevertheless, we're eager to see the fix for downloading without the need of a player instance.

We're now facing a problem with license download, but that's another story 😅