Enable DRM with JW Stream

Learn a simplified approach to protecting your iOS content with DRM.


JW Player provides a simplified approach to protecting your content with industry-standard Digital Rights Management (DRM). By enabling DRM on a property from your JW Player dashboard, the complex aspects of DRM management are managed by JW Player on your behalf:

   • Several configured DRM Policies
  • DRM media content key generation and management for FairPlay Streaming
  • License delivery services for content playback on any Apple device

With JW Player managing the technical aspects of DRM, you can focus on the design and implementation of engaging content experiences.

📘

NOTE

You can also Play DRM-protected content if you have not enabled DRM with JW Stream.



Requirements



Tutorial

📘

GOAL

After completing this tutorial, you should be able to answer the following question:
• How can I playback DRM-protected content when using DRM with JW Stream?


  1. Create a signed content URL. Follow steps 1-4 in the Tutorial section of Enable DRM with JW Stream. The signed content URL returns a JSON object of the content metadata similar to the following example:
{
   "title":"Tears of Steel FPS Test",
   "description":"",
   "kind":"Single Item",
   "playlist":[
      {
         "title":"Tears of Steel FPS Test",
         "mediaid":"WlvsLi24",
         ...
         "images":[...],
         "duration":30,
         "pubdate":1603891888,
         "description":"",
         "sources":[
            {
               "drm":{
                  "fairplay":{
                     "certificateUrl":"{certificate_url}",
                     "spcProcessUrl":"{spc_process_url}"
                  },
               },
               "file":"{signed_video_url}",
               "type":"application/vnd.apple.mpegurl"
            },
            ...         
         ],
         "tracks":[...],
         ...
   ],
   "feed_instance_id":"{feed_instance_id}"
}

  1. From the signed content URL response, extract the file URL (playlist[].sources[].file), the certificate URL (playlist[].sources[].drm.fairplay.certificateUrl) and the SPC Process URL (playlist[].sources[].drm.fairplay.spcProcessUrl).

❗️

WARNING

The ordering of items within playlist[].sources[] is not static. Therefore, do not use a defined index (playlist[].sources[0]) as part of your extraction process.


  1. Use the extracted file to set up the player.
JWConfig *config = [JWConfig configWithContentURL:@"{parsed_file_url}"];
self.player = [[JWPlayerController alloc] initWithConfig:config delegate:self drmDataSource:self];
[self.view addSubview:self.player.view];
let config = JWConfig(contentUrl: "{parsed_file_url}")
self.player = JWPlayerController(config: config, delegate: self, drmDataSource: self)
self.view.addSubview(self.player.view)

  1. Conform your implementation class to the JWDRMDataSource protocol. Doing this provides the necessary data for playing protected content.
- (void)fetchContentIdentifierForRequest:(nonnull NSURL *)loadingRequestURL
                           forEncryption:(JWEncryption)encryption
                          withCompletion:(nonnull void (^)(NSData * _Nonnull))completion {
    completion([loadingRequestURL.host dataUsingEncoding:NSUTF8StringEncoding]);
}


- (void)fetchAppIdentifierForRequest:(nonnull NSURL *)loadingRequestURL
                       forEncryption:(JWEncryption)encryption
                      withCompletion:(nonnull void (^)(NSData * _Nonnull))completion {
    NSURL *url = [NSURL URLWithString:@"{parsed_certificate_url}"];
    [[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (error) {
            return;
        }
        completion(data);
    }] resume];
}

- (void)fetchContentKeyWithRequest:(nonnull NSData *)requestBytes
                     forEncryption:(JWEncryption)encryption
                    withCompletion:(nonnull void (^)(NSData * _Nonnull, NSDate * _Nullable, NSString * _Nullable))completion {
    NSURL *url = [NSURL URLWithString:@"{parsed_spc_process_url}"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request setHTTPMethod:@"POST"];
    [request setValue:@"application/octet-stream" forHTTPHeaderField:@"Content-type"];
    [request setHTTPBody:requestBytes];

    [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (error) {
            return;
        }
        completion(data, nil, @"application/octet-stream");
    }] resume];
}
func fetchContentIdentifier(forRequest loadingRequestURL: URL,
                                for encryption: JWEncryption,
                                withCompletion completion: @escaping (Data) -> Void) {
        completion(loadingRequestURL.host!.data(using: .utf8)!)
    }
    
    func fetchAppIdentifier(forRequest loadingRequestURL: URL,
                            for encryption: JWEncryption,
                            withCompletion completion: @escaping (Data) -> Void) {
        let url = URL(string: "{parsed_certificate_url}")!
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            guard error != nil, let data = data else {
                return
            }
            completion(data)
        }.resume()
    }

    func fetchContentKey(withRequest requestBytes: Data,
                         for encryption: JWEncryption,
                         withCompletion completion: @escaping (Data, Date?, String?) -> Void) {
        let url = URL(string: "{parsed_spc_process_url}")!
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/octet-stream", forHTTPHeaderField: "Content-Type")
        request.httpBody = requestBytes
        URLSession.shared.dataTask(with: request) { (data, response, error) in
            guard error != nil, let data = data else {
                return
            }
            completion(data, nil, "application/octet-stream")
        }.resume()
    }


Did this page help you?