Skip to content

Commit

Permalink
Check in OfflinePlayReadyAndWidevine (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
v-jizhang authored and BrianBlum committed Sep 12, 2019
1 parent 7d1d5b1 commit 6b1cb98
Show file tree
Hide file tree
Showing 12 changed files with 1,433 additions and 0 deletions.
89 changes: 89 additions & 0 deletions ContentProtection/OfflinePlayReadyAndWidevine/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
topic: sample
languages:
- java
products:
- azure-media-services
---

# Offline playback with PlayReady and Widevine DRM

This sample demonstrates how to dynamically encrypt your content with PlayReady and Widevine DRM and play the content without requesting a license from license service. It shows how to perform the following tasks:

1. Creates a transform with built-in AdaptiveStreaming preset
1. Submits a job
1. Creates a ContentKeyPolicy with open restriction and PlayReady/Widevine persistent configuration
1. Associates the ContentKeyPolicy with a StreamingLocator
1. Prints a url for playback

When a user requests PlayReady or Widevine protected content for the first time, the player application requests a license from the Media Services license service. If the player application is authorized, the Media Services license service issues a license to the player and the license is persisted. Because the license is persisted, subsequent playback won't send a request to Media Services license service again.

> [!TIP]
> The `OfflinePlayReadyAndWidevine.java` file has extensive comments.
## Prerequisites

* Java JDK 1.8 or newer installed
* Maven installed
* An Azure Media Services account. See the steps described in [Create a Media Services account](https://docs.microsoft.com/azure/media-services/latest/create-account-cli-quickstart).

## Running the example

### Configure `appsettings.json` with appropriate access values

Get credentials needed to use Media Services APIs by following [Access APIs](https://docs.microsoft.com/azure/media-services/latest/access-api-cli-how-to). Open the `src/main/resources/conf/appsettings.json` configuration file and paste the values in the file.

### Clean and build the project

Open a terminal window, go to the root folder of this project, run `mvn clean compile`.

### Run this project

Execute `mvn exec:java`, then follow the instructions in the output console.

### Optional, do the following steps if you want to use Event Grid for job monitoring

Please note, there are costs for using Event Hub. For more details, refer [Event Hubs pricing](https://azure.microsoft.com/en-in/pricing/details/event-hubs/) and [FAQ](https://docs.microsoft.com/en-us/azure/event-hubs/event-hubs-faq#pricing)

* Enable Event Grid resource provider

`az provider register --namespace Microsoft.EventGrid`

* To check if registered, run the next command. You should see "Registered"

`az provider show --namespace Microsoft.EventGrid --query "registrationState"`

* Create an Event Hub

`namespace=<unique-namespace-name>`\
`hubname=<event-hub-name>`\
`az eventhubs namespace create --name $namespace --resource-group <resource-group>`\
`az eventhubs eventhub create --name $hubname --namespace-name $namespace --resource-group <resource-group>`

* Subscribe to Media Services events

`hubid=$(az eventhubs eventhub show --name $hubname --namespace-name $namespace --resource-group <resource-group> --query id --output tsv)`\
`amsResourceId=$(az ams account show --name <ams-account> --resource-group <resource-group> --query id --output tsv)`\
`az eventgrid event-subscription create --resource-id $amsResourceId --name <event-subscription-name> --endpoint-type eventhub --endpoint $hubid`

* Create a storage account and container for Event Processor Host if you don't have one
[Create a storage account for Event Processor Host
](https://docs.microsoft.com/en-us/azure/event-hubs/event-hubs-dotnet-standard-getstarted-send#create-a-storage-account-for-event-processor-host)

* Update appsettings.json with your Event Hub and Storage information
StorageAccountName: The name of your storage account.\
StorageAccountKey: The access key for your storage account. Navigate to Azure portal, "All resources", search your storage account, then "Access keys", copy key1.\
StorageContainerName: The name of your container. Click Blobs in your storage account, find you container and copy the name.\
EventHubConnectionString: The Event Hub connection string. search your namespace you just created. &lt;your namespace&gt; -&gt; Shared access policies -&gt; RootManageSharedAccessKey -&gt; Connection string-primary key.\
EventHubName: The Event Hub name. &lt;your namespace&gt; -&gt; Event Hubs.

## Key concepts

* [Dynamic packaging](https://docs.microsoft.com/azure/media-services/latest/dynamic-packaging-overview)
* [Content protection with dynamic encryption](https://docs.microsoft.com/azure/media-services/latest/content-protection-overview)
* [Streaming Policies](https://docs.microsoft.com/azure/media-services/latest/streaming-policy-concept)

## Next steps

* [Azure Media Services pricing](https://azure.microsoft.com/pricing/details/media-services/)
* [Azure Media Services v3 Documentation](https://docs.microsoft.com/azure/media-services/latest/)
108 changes: 108 additions & 0 deletions ContentProtection/OfflinePlayReadyAndWidevine/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>sample</groupId>
<artifactId>OfflinePlayReadyAndWidevine</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>OfflinePlayReadyAndWidevine</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>com.microsoft.azure.mediaservices.v2018_07_01</groupId>
<artifactId>azure-mgmt-media</artifactId>
<version>1.0.0-beta-4</version>
</dependency>
<dependency>
<groupId>com.microsoft.rest</groupId>
<artifactId>client-runtime</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-client-authentication</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-arm-client-runtime</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-client-runtime</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.10.5</version>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-storage-blob</artifactId>
<version>11.0.1</version>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-eventhubs</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-eventhubs-eph</artifactId>
<version>2.5.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.4.0</version>
<configuration>
<mainClass>sample.OfflinePlayReadyAndWidevine</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package sample;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;

/**
* This class reads values from local configuration file resources/conf/appsettings.json
* Please change the configuration using your account information. For more information, see
* https://docs.microsoft.com/azure/media-services/latest/access-api-cli-how-to. For security
* reasons, do not check in the configuration file to source control.
*/
public class ConfigWrapper {
private static final String AAD_CLIENT_ID = "AadClientId";
private static final String AAD_ENDPOINT = "AadEndpoint";
private static final String AAD_SECRET = "AadSecret";
private static final String AAD_TENANT_ID = "AadTenantId";
private static final String ACCOUNT_NAME = "AccountName";
private static final String ARM_AAD_AUDIENCE = "ArmAadAudience";
private static final String ARM_ENDPOINT = "ArmEndpoint";
private static final String REGION = "Region";
private static final String RESOURCE_GROUP = "ResourceGroup";
private static final String SUBSCRIPTION_ID = "SubscriptionId";
private static final String SYMMETRIC_KEY = "SymmetricKey";
private static final String NAMESPACE_NAME = "NamespaceName";
private static final String EVENT_HUB_CONNECTION_STRING = "EventHubConnectionString";
private static final String EVENT_HUB_NAME = "EventHubName";
private static final String STORAGE_CONTAINER_NAME = "StorageContainerName";
private static final String STORAGE_ACCOUNT_NAME = "StorageAccountName";
private static final String STORAGE_ACCOUNT_KEY = "StorageAccountKey";
private static final String CONF_JSON = "conf/appsettings.json";
private final JSONObject jsonObject;
private final InputStreamReader isReader;

public ConfigWrapper() {
InputStream inStream = ConfigWrapper.class.getClassLoader().getResourceAsStream(CONF_JSON);
isReader = new InputStreamReader(inStream);

JSONParser parser = new JSONParser();
Object obj = null;
try {
obj = parser.parse(isReader);
} catch (Exception ioe) {
System.err.println(ioe);
System.exit(1);
}

jsonObject = (JSONObject) obj;
}

public void close() {
try {
if (isReader != null) {
isReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}

public String getAadClientId() {
return (String)jsonObject.get(AAD_CLIENT_ID);
}

public String getAadEndpoint() {
return (String)jsonObject.get(AAD_ENDPOINT);
}

public String getAadSecret() {
return (String)jsonObject.get(AAD_SECRET);
}

public String getAadTenantId() {
return (String)jsonObject.get(AAD_TENANT_ID);
}

public String getAccountName() {
return (String)jsonObject.get(ACCOUNT_NAME);
}

public String getArmAadAudience() {
return (String)jsonObject.get(ARM_AAD_AUDIENCE);
}

public String getArmEndpoint() {
return (String)jsonObject.get(ARM_ENDPOINT);
}

public String getRegion() {
return (String)jsonObject.get(REGION);
}

public String getResourceGroup() {
return (String)jsonObject.get(RESOURCE_GROUP);
}

public String getSubscriptionId() {
return (String)jsonObject.get(SUBSCRIPTION_ID);
}

public String getSymmetricKey() {
return (String)jsonObject.get(SYMMETRIC_KEY);
}

public String getNamespaceName() {
return (String)jsonObject.get(NAMESPACE_NAME);
}

public String getEventHubConnectionString() {
return (String)jsonObject.get(EVENT_HUB_CONNECTION_STRING);
}

public String getEventHubName() {
return (String)jsonObject.get(EVENT_HUB_NAME);
}

public String getStorageContainerName() {
return (String)jsonObject.get(STORAGE_CONTAINER_NAME);
}

public String getStorageAccountName() {
return (String)jsonObject.get(STORAGE_ACCOUNT_NAME);
}

public String getStorageAccountKey() {
return (String)jsonObject.get(STORAGE_ACCOUNT_KEY);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package sample;

import com.fasterxml.jackson.annotation.JsonProperty;

public class ContentKeySpec {
@JsonProperty("TrackType")
private String trackType;

@JsonProperty("SecurityLevel")
private int securityLevel;

@JsonProperty("RequiredOutputProtection")
private OutputProtection requiredOutputProtection;

public String getTrackType() {
return trackType;
}

public void setTrackType(String trackType) {
this.trackType = trackType;
}

public int getSecurityLevel() {
return securityLevel;
}

public void setSecurityLevel(int securityLevel) {
this.securityLevel = securityLevel;
}

public OutputProtection getRequiredOutputProtection() {
return requiredOutputProtection;
}

public void setRequiredOutputProtection(OutputProtection requiredOutputProtection) {
this.requiredOutputProtection = requiredOutputProtection;
}
}
Loading

0 comments on commit 6b1cb98

Please sign in to comment.