Fairplay with React Native

The following guide helps you set up DRM with React Native Video

React Native Video is very popular player to play videos on react native applications. It can play both HLS and DASH streams and supports DRM.

🚧

Don't use simulator during development

iOS doesn't support fairplay protected playback on simulators, please use actual devices during development.

iOS:

React Native Video supports AVPlayer on iOS. Since AVPlayer does not support DRM out of the box, we need to explicitly configure React Native Video to pass certificate and licenses to the AVPlayer

To keep the scope of this document limited we expect you have already setup React Native development environment for iOS.

📹

Secure your video assets from piracy and maximize ROI on your content. Easily set up fairplay DRM on your React Native apps with Gumlet.

Step 1: Install the react-native-video package

npm i --save react-native-video

Step 2: Import react-native-video in your component

import Video, {DRMType} from 'react-native-video';

Step 3: Add the video component to your code

For the simplicity of this document we are making the changes in App.js itself and our app would only have a video player in it. We have also added some basic styles to our video component.

const App: () => Node = () => {
  return (
    <>
    <View style={{flex: 1}}>
      <Video 
        source={{
          uri: 'https://video.gumlet.io/5f462c1561cf8a766464ffc4/61ee745525fc01c00e08a2ec/1.m3u8',
        }}
        style={styles.mediaPlayer}
      />
    </View>
    </>
  );
};

const styles = StyleSheet.create({
  mediaPlayer: {
    position: 'absolute',
    top: 0,
    left: 0,
    bottom: 0,
    right: 0,
    justifyContent: 'center',
    backgroundColor: '#f8f8f8'
  },
});

Step 4: Request for a license URL to play a video encrypted by DRM

DRM playback also needs Licence Server URL along with playback URL to play the content. The licence server URL will inform player the place from where the licence can be acquired.

🚧

Remember!

For security reasons, Licence Server URL needs authentication token to be passed so it knows the request for licence is legitimate, due to this reason the code for generating a Licence Server URL should be on the server and not reside in the application itself.

Refer to this guide to generate the licence server URL on a backend server with your choice of programming language.

Let us assume that your server root URL is https://example.com and sending a GET request on https://example.com/licence-url returns a licence server URL. We will call the fetch method in the components useEffect hook and set it to a state variable to be used to initialise the video player with the Licence Server URL.

const App: () => Node = () => {
  const [isLoading, setLoading] = useState(true);
	const [signedLicenseURL, setSignedLicenseURL] = React.useState('');
	
  const getSignedLicenseURL = async () => {
     try {
      const response = await fetch('https://example.com/licence-url');
      const json = await response.json();
      setSignedLicenseURL(json.licence);
    } catch (error) {
      console.error(error);
    } finally {
      setLoading(false);
    }
  }

  useEffect(() => {
    getSignedLicenseURL();
  }, []);
  
  // Wait for the signedLicenseURL to be available until then you can show a placeholder..
  if (isLoading) {
    return (
      <View style={{flex: 1}}>
        <Text>Loading Signed URL</Text>
      </View>
    )
  }

  return (
    <>
    <View style={{flex: 1}}>
      <Video 
        source={{
          uri: 'https://video.gumlet.io/<org_id>/<asset_id>/main.m3u8',
          type: 'mpd'
        }}
        drm={{
          type: DRMType.FAIRPLAY,
          certificateUrl: fairplayCertificate,
          licenseServer: signedLicenseURL,
          getLicense: (spcString, contentId, licenseUrl, loadedLicenseUrl) => {
            const body = JSON.stringify({spc: spcString});
            
            return fetch(`${licenseUrl}`, {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json',
              },
              body: body,
            })
              .then((response) => response.json())
              .then((response) => {
                return response.ckc;
              })
              .catch((error) => {
                console.error('Error', error);
              });
          }
        }}
      />
    </View>
    </>
  );
};

👍

Done

You can now play your DRM protected videos in a React Native Application

Full Code Snippet

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow strict-local
 */

import React, {useEffect, useState} from 'react';
import type {Node} from 'react';
import {
  Text,
  useColorScheme,
  View,
} from 'react-native';

import {
  Colors,
} from 'react-native/Libraries/NewAppScreen';
import Video, {DRMType} from 'react-native-video';


const App: () => Node = () => {
  const [isLoading, setLoading] = useState(true);
	const [signedLicenseURL, setSignedLicenseURL] = React.useState('');
	
  // Make a request to get a signed Licence Server URL
  const getSignedLicenseURL = async () => {
     try {
      const response = await fetch('https://example.com/licence-url');
      const json = await response.json();
      setSignedLicenseURL(json.licence);
    } catch (error) {
      console.error(error);
    } finally {
      setLoading(false);
    }
  }

  useEffect(() => {
    getSignedLicenseURL();
  }, []);
  
  // Wait for the signedLicenseURL to be available until then you can show a placeholder..
  if (isLoading) {
    return (
      <View style={{flex: 1}}>
        <Text>Loading Signed URL</Text>
      </View>
    )
  }

  return (
    <>
    <View style={{flex: 1}}>
      <Video 
        source={{
          uri: 'https://video.gumlet.io/5f462c1561cf8a766464ffc4/627ccd5803f2239e6938820b/main.mpd',
          type: 'mpd'
        }}
        drm={{
          type: DRMType.FAIRPLAY,
          certificateUrl: fairplayCertificate,
          licenseServer: signedLicenseURL,
          getLicense: (spcString, contentId, licenseUrl, loadedLicenseUrl) => {
            const body = JSON.stringify({spc: spcString});
            
            return fetch(`${licenseUrl}`, {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json',
              },
              body: body,
            })
              .then((response) => response.json())
              .then((response) => {
                return response.ckc;
              })
              .catch((error) => {
                console.error('Error', error);
              });
          }
        }}
      />
    </View>
    </>
  );
};

const styles = StyleSheet.create({
  mediaPlayer: {
    position: 'absolute',
    top: 0,
    left: 0,
    bottom: 0,
    right: 0,
    justifyContent: 'center',
    backgroundColor: '#f8f8f8'
  },
});

export default App;