How to test MPEG-DASH WebM encryption using Shaka player

This article describes how to quickly test MPEG-DASH WebM encryption using Shaka player and an encryption key embedded in HTML code (not secure). We assume you're sending a WebM compatible VP8/VP9 stream directly to Wowza Streaming Engine. (You can also use Wowza Transcoder to generate the VP8/VP9 stream.) The examples in this article use the default Wowza Streaming Engine live application and a live WebM stream named myStream.

Note: Wowza Streaming Engine™ 4.4.0 or later is required.
  1. Download and build Shaka player:
     
    1. Navigate to the shaka-player project repository, and then clone or download the project files.
       
    2. Build the player (using Cygwin, change directory to build, and then run build.sh).
       
    3. Copy the player to aweb server.
  2. Install Wowza Streaming Engine 4.4.0 (or later), and configure the default live application for MPEG-DASH streaming. For configuration instructions, see How to do MPEG-DASH streaming.
     
  3. Add the following custom module that injects DRM information into the live stream.
    import com.wowza.wms.drm.cenc.*;
    import com.wowza.wms.module.*;
    import com.wowza.wms.stream.livepacketizer.*;
    
    public class ModuleWebMDashEncryption extends ModuleBase
    {
    	public static class WebMDashCustomDRMInfo implements ICencDRMInfo
    	{
    		String systemName = null;
    		String systemId = null;
    		String url = null;
    
    		public WebMDashCustomDRMInfo(String systemName)
    		{
    			this.systemName = systemName;
    		}
    
    		@Override
    		public void setSystemId(String systemId)
    		{
    			this.systemId = systemId;
    		}
    
    		@Override
    		public String getSystemId()
    		{
    			return systemId;
    		}
    
    		@Override
    		public String getURL()
    		{
    			return url;
    		}
    
    		@Override
    		public void setURL(String url)
    		{
    			this.url = url;
    		}
    
    		@Override
    		public String getNameSpaces()
    		{
    			return null;
    		}
    
    		@Override
    		public String getMPEGDashCPSubElements(boolean keyRotation)
    		{
    			return null;
    		}
    
    		@Override
    		public String getSystemName()
    		{
    			return this.systemName;
    		}
    
    		@Override
    		public boolean supportsKeyGeneration()
    		{
    			return false;
    		}
    
    		@Override
    		public byte[] generateKey(byte[] KID)
    		{
    			return null;
    		}
    
    		@Override
    		public void updateKeyInfo(byte[] KID, byte[] contentKey)
    		{
    		}
    
    		@Override
    		public byte[] serialize()
    		{
    			return null;
    		}
    
    		@Override
    		public void deserialize(byte[] data)
    		{
    		}
    
    		@Override
    		public byte[] getPsshData(boolean keyRotation)
    		{
    			return null;
    		}
    
    		@Override
    		public void setPsshData(byte[] psshData)
    		{
    		}
    
    		@Override
    		public boolean isComplete()
    		{
    			return true;
    		}
    	}
    
    	public void onHTTPMPEGDashEncryptionKeyLiveChunk(ILiveStreamPacketizer liveStreamPacketizer, String streamName, CencInfo cencInfo, long chunkId)
    	{
    		if (cencInfo.getKID() != null && cencInfo.getEncKeyString() != null)
    		{
    			try
    			{
    				String idStr = "test";
    				String url = "empty";
    				String systemId = "12345678-1234-1234-1234-123456789012";
    
    				WebMDashCustomDRMInfo drmInfo = new WebMDashCustomDRMInfo(idStr);
    
    				drmInfo.setURL(url);
    				drmInfo.setSystemId(systemId);
    
    				getLogger().info("ModuleWebMDashEncryption.onHTTPMPEGDashEncryptionKeyLiveChunk["+streamName+"]: Add DRM: "+idStr+":"+systemId);
    				cencInfo.addDRM(idStr+":"+systemId, drmInfo);
    				cencInfo.setKeyRotationType(CencInfo.KEY_ROTATION_TYPE_NONE);
    				cencInfo.setPSSHVersion(CencInfo.PSSH_VERSION_1);
    			}
    			catch (Exception e)
    			{
    				getLogger().error("ModuleWebMDashEncryption.onHTTPMPEGDashEncryptionKeyLiveChunk["+streamName+"]: ", e);
    			}
    		}
    	}
    }
  4. Add a key file named [install-dir]/keys/myStream.key to encrypt the live stream.
    mpegdashstreaming-cenc-key-id: 12345DCF-7F93-4B8E-85C7-F908840DA059
    mpegdashstreaming-cenc-content-key: Hh8gISIjJCUmJx4fICEiIw==
    mpegdashstreaming-cenc-algorithm: AESCTR
    mpegdashstreaming-cenc-pssh-version: 1
  5. Modify Shaka player to inject the key into the player. Edit appUtils.js and add the following if statement to the appUtils.interpretContentProtection function near the top of the function.
    if (schemeIdUri == "urn:uuid:12345678-1234-1234-1234-123456789012") {
    
    	var keyid = Uint8ArrayUtils.fromBase64('EjRdz3+TS46Fx/kIhA2gWQ'); // 12345DCF-7F93-4B8E-85C7-F908840DA059
    	var key = Uint8ArrayUtils.fromBase64('Hh8gISIjJCUmJx4fICEiIw');
    
    	var keyObj = {
    		kty: 'oct',
    		alg: 'A128KW',
    		kid: Uint8ArrayUtils.toBase64(keyid, false),
    		k: Uint8ArrayUtils.toBase64(key, false)
    	};
    
    	var jwkSet = {keys: [keyObj]};
    	license = JSON.stringify(jwkSet);
    
    	console.log("license:"+license);
    	console.log("keyid:"+keyid);
    	console.log("key:"+key);
    
    	var initData = {
    		'initData': keyid,
    		'initDataType': 'webm'
    	};
    
    	var licenseServerUrl = 'data:application/json;base64,' + window.btoa(license);
    
    	return [{
    		'keySystem': 'org.w3.clearkey',
    		'licenseServerUrl': licenseServerUrl,
    		'initData': initData
    	}];
    }
  6. Send a WebM-compatible live stream named myStream to the live application in Wowza Streaming Engine. You should see a log statement similar to the following that indicates the stream is being encrypted.
    MPEGDashWriterHandler.createChunkWebM[live/_definst_/myStream]: WebM encryption: kid:12345dcf7f934b8e85c7f908840da059
  7. Play the stream using your modified Shaka player (the stream should play properly).

Originally Published: For Wowza Streaming Engine 4.4.0 on 02-02-2016.
 

If you're having problems or want to discuss this article, post in our forum.