4

I'm working on a project where I need to set up DRM content protection for my videos. It's working fine for Widevine and PlayReady and now I need to configure FairPlay.

I followed the instructions in the FPS Credential Creation Guide, so I have the certificate and the ASk. I created the content key policy option, following this guide: Apple FairPlay license requirements and configuration. Using the CLI, I can see that it has the ask, fairPlayPfx and fairPlayPfxPassword defined.

I'm using the Predefined_MultiDrmStreaming streaming policy and I have a streaming locator that returns five streaming URLs (two for DASH, two for HLS and one for SmoothStreaming).

I'm using video-js and videojs-contrib-eme to play the video. To configure the FairPlay keySystems, I need the certificate URL and the FairPlay license URL, according to the documentation: Get Certificate/License by URL.

My problem is that I don't know where to find that license URL. I checked the HLS manifest file and it's not there. I'm using the default Azure Media Services license service, so I'm not overriding the customLicenseAcquisitionUrlTemplate in the streaming policy.

Thank you in advance!

-- edit --

My manifest(format=m3u8-cmaf,encryption=cbcs-aapl) file:

#EXTM3U
#EXT-X-VERSION:7
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",NAME="aac_und_2_127998_2_1",LANGUAGE="und",DEFAULT=YES,AUTOSELECT=YES,CHANNELS="2",URI="QualityLevels(127998)/Manifest(aac_und_2_127998_2_1,format=m3u8-cmaf,encryption=cbcs-aapl)"
#EXT-X-STREAM-INF:BANDWIDTH=239626,RESOLUTION=320x180,CODECS="avc1.64000d,mp4a.40.2",AUDIO="audio"
QualityLevels(90502)/Manifest(video,format=m3u8-cmaf,encryption=cbcs-aapl)
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=239626,RESOLUTION=320x180,CODECS="avc1.64000d",URI="QualityLevels(90502)/Manifest(video,format=m3u8-cmaf,type=keyframes,encryption=cbcs-aapl)"
#EXT-X-STREAM-INF:BANDWIDTH=310148,RESOLUTION=480x270,CODECS="avc1.640015,mp4a.40.2",AUDIO="audio"
QualityLevels(159506)/Manifest(video,format=m3u8-cmaf,encryption=cbcs-aapl)
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=310148,RESOLUTION=480x270,CODECS="avc1.640015",URI="QualityLevels(159506)/Manifest(video,format=m3u8-cmaf,type=keyframes,encryption=cbcs-aapl)"
#EXT-X-STREAM-INF:BANDWIDTH=413000,RESOLUTION=640x360,CODECS="avc1.64001e,mp4a.40.2",AUDIO="audio"
QualityLevels(260144)/Manifest(video,format=m3u8-cmaf,encryption=cbcs-aapl)
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=413000,RESOLUTION=640x360,CODECS="avc1.64001e",URI="QualityLevels(260144)/Manifest(video,format=m3u8-cmaf,type=keyframes,encryption=cbcs-aapl)"
#EXT-X-STREAM-INF:BANDWIDTH=633193,RESOLUTION=960x540,CODECS="avc1.64001f,mp4a.40.2",AUDIO="audio"
QualityLevels(475597)/Manifest(video,format=m3u8-cmaf,encryption=cbcs-aapl)
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=633193,RESOLUTION=960x540,CODECS="avc1.64001f",URI="QualityLevels(475597)/Manifest(video,format=m3u8-cmaf,type=keyframes,encryption=cbcs-aapl)"
#EXT-X-STREAM-INF:BANDWIDTH=138973,CODECS="mp4a.40.2",AUDIO="audio"
QualityLevels(127998)/Manifest(aac_und_2_127998_2_1,format=m3u8-cmaf,encryption=cbcs-aapl)

My manifest(format=m3u8-aapl,encryption=cbcs-aapl) file:

#EXTM3U
#EXT-X-VERSION:5
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",NAME="aac_und_2_127998_2_1",LANGUAGE="und",DEFAULT=YES,AUTOSELECT=YES,URI="QualityLevels(127998)/Manifest(aac_und_2_127998_2_1,format=m3u8-aapl)"
#EXT-X-STREAM-INF:BANDWIDTH=239626,RESOLUTION=320x180,CODECS="avc1.64000d,mp4a.40.2",AUDIO="audio"
QualityLevels(90502)/Manifest(video,format=m3u8-aapl)
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=239626,RESOLUTION=320x180,CODECS="avc1.64000d",URI="QualityLevels(90502)/Manifest(video,format=m3u8-aapl,type=keyframes)"
#EXT-X-STREAM-INF:BANDWIDTH=310148,RESOLUTION=480x270,CODECS="avc1.640015,mp4a.40.2",AUDIO="audio"
QualityLevels(159506)/Manifest(video,format=m3u8-aapl)
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=310148,RESOLUTION=480x270,CODECS="avc1.640015",URI="QualityLevels(159506)/Manifest(video,format=m3u8-aapl,type=keyframes)"
#EXT-X-STREAM-INF:BANDWIDTH=413000,RESOLUTION=640x360,CODECS="avc1.64001e,mp4a.40.2",AUDIO="audio"
QualityLevels(260144)/Manifest(video,format=m3u8-aapl)
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=413000,RESOLUTION=640x360,CODECS="avc1.64001e",URI="QualityLevels(260144)/Manifest(video,format=m3u8-aapl,type=keyframes)"
#EXT-X-STREAM-INF:BANDWIDTH=633193,RESOLUTION=960x540,CODECS="avc1.64001f,mp4a.40.2",AUDIO="audio"
QualityLevels(475597)/Manifest(video,format=m3u8-aapl)
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=633193,RESOLUTION=960x540,CODECS="avc1.64001f",URI="QualityLevels(475597)/Manifest(video,format=m3u8-aapl,type=keyframes)"
#EXT-X-STREAM-INF:BANDWIDTH=138973,CODECS="mp4a.40.2",AUDIO="audio"
QualityLevels(127998)/Manifest(aac_und_2_127998_2_1,format=m3u8-aapl)
Lucas Silva
  • 81
  • 10

2 Answers2

1

I managed to get the license URL in the manifest file. I had to create a custom streaming policy with the allowPersistentLicense set to true in the FairPlay DRM configuration. The Predefined_MultiDrmStreaming streaming policy has that value set to false.

Lucas Silva
  • 81
  • 10
  • Hi @Lucas, I am working on similar functionality, I am able to play the video in Android with `manifest URL`, `JWT Token` & `Widevine License URL`. I am getting `Widevine License URL` from manifest file & I am passing it in the player code. The video is getting played properly as I have hardcoded this data. My question is how can we extract this license URL from the manifest file programmatically & pass it to the Exo player code. – Kavita Patil Apr 15 '21 at 11:43
  • Hi @Kavita_p, I have an API that returns the manifest file. So I call that endpoint in the front-end and I search for `licenseUrl="` using the IndexOf function. I add 12 to the result, because `licenseUrl="` has 12 characters, and that gives me the position where the license URL starts. After that, I use the IndexOf again passing the position where the license URL starts to search for the closing quotes after the license URL. And that returns the position where the license URL ends. Now I can use the Slice function passing the start and end position to extract the license URL. Hope that helps. – Lucas Silva Apr 21 '21 at 12:32
  • Thanks for the reply @Lucas, is this the only way to extract the `licenseUrl`? I am using Azure Media Service, doesn't it provide an API that can return `licenseUrl`? – Kavita Patil Apr 21 '21 at 12:39
  • I'm also using Azure Media Service and as far as I know, they don't have an API that returns just the license URL. What they have is an API that returns the manifest file and the license URL is in that file. So we need to extract it from the manifest file. If you find a better way of doing it, please share it with me. – Lucas Silva Apr 22 '21 at 13:10
0

For FairPlay license URL, you can find it in HLS top level (or second level) playlist, it is in the form: URI="skd://eventgridmediaservice.keydelivery.westus2-2.media.azure.net/FairPlay/?kid=70d14e71-4380-470c-acff-61dea6382ccf"

You need to replace skd by https.

  • I don't have anything like that URI in my HLS files. All URIs are followed by `QualityLevels`. I edited my question showing part of the first three lines of each file. Do you why it's not there? – Lucas Silva Dec 15 '20 at 07:56
  • That is strange. Here is a sample URL with FairPlay: https://eventgridmediaservice-usw22.streaming.media.azure.net/2fa069bd-2b0c-4674-bff7-26ebd6cdc224/MicrosoftElite01.ism/manifest(format=m3u8-cmaf,encryption=cbcs-aapl). I'm suspecting maybe you have issue in your FairPlay configuration such as StreamingLocator. If you are using .NET SDK, could you try using the enum for streaming policy name: PredefinedStreamingPolicy.MultiDrmStreaming? – William Zhang Dec 16 '20 at 15:12
  • I created the streaming locator using the enum, but I got the same result. It has five URLs, but none of the HLS ones has the license URI. Is there anything else I could try? – Lucas Silva Dec 17 '20 at 10:17
  • Did you also look into the layer 2 playlist of HLS? – William Zhang Dec 18 '20 at 15:20
  • I'm not sure what the layer 2 is, but the streaming URLs has two HLS URLs, one for the m3u8-cmaf and the other for the m3u8-aapl manifest file. If that is what you mean, I checked both files and they don't have the license URL. – Lucas Silva Dec 29 '20 at 13:42
  • By layer 2, I mean the bitrate level playlist: suppose you pick m3u8-aapl, get the first layer playlist, in which you can find the URLs for each bitrate. – William Zhang Dec 30 '20 at 15:05
  • I found this blog post: https://azure.microsoft.com/en-us/blog/how-to-make-token-authorized-aes-encrypted-hls-stream-working-in-safari/ It has a workaround to add the license URL to the manifest file using a proxy, but it is for AES + token and I'm using DRM + JWT token. I'm not sure if I should try something similar to that proxy implementation. I updated my question with the full manifest file, so you can see the layer 2 playlist of HLS. Answering your previous question, the license URL doesn't seem to be there as well. – Lucas Silva Jan 08 '21 at 12:09
  • That doc is for HLS + AES scenario only in which case Safari on iOS does not allow API to provide access token for decryption key acquisition. So do not use it for DRM (FairPlay) case. I still think your issue may be due to your ContentKeyPolicy not being properly setup. If set up correctly, you should have the license URL. Here is a sample of such HLS+FairPlay: https://eventgridmediaservice-usw22.streaming.media.azure.net/2fa069bd-2b0c-4674-bff7-26ebd6cdc224/MicrosoftElite01.ism/manifest(format=m3u8-aapl,encryption=cbcs-aapl) – William Zhang Jan 09 '21 at 21:41
  • 1
    The problem was in the streaming policy, the allowPersistentLicense was set to false. When I changed it to true, it added the license URL in the manifest file. My Fairplay ContentKeyPolicy was copied from this page: https://learn.microsoft.com/en-us/azure/media-services/latest/fairplay-license-overview. Then I just add it to the list of ContentKeyPolicyOption with the restriction. My full implementation is very similar to this one: https://github.com/Azure-Samples/media-services-v3-dotnet-tutorials/blob/master/AMSV3Tutorials/EncryptWithDRM/Program.cs Thank you for looking into this. – Lucas Silva Jan 10 '21 at 10:23