Using PlayReady and/or Widevine dynamic common encryption

Microsoft Azure Media Services enables you to deliver MPEG-DASH, Smooth Streaming, and HTTP-Live-Streaming (HLS) streams protected with Microsoft PlayReady DRM. It also enables you to deliver encrypted DASH streams with Widevine DRM licenses. Both PlayReady and Widevine are encrypted per the Common Encryption (ISO/IEC 23001-7 CENC) specification. You can use AMS .NET SDK (starting with the version 3.5.1) or REST API to configure your AssetDeliveryConfiguration to use Widevine.

Media Services provides a service for delivering PlayReady and Widevine DRM licenses. Media Services also provides APIs that let you configure the rights and restrictions that you want for the PlayReady or Widevine DRM runtime to enforce when a user plays back protected content. When a user requests a DRM protected content, the player application will request a license from the AMS license service. The AMS license service will issue a license to the player if it is authorized. A PlayReady or Widevine license contains the decryption key that can be used by the client player to decrypt and stream the content.

You can also use the following AMS partners to help you deliver Widevine licenses: Axinom, EZDRM, castLabs. For more information, see: integration with Axinom and castLabs.

Media Services supports multiple ways of authorizing users who make key requests. The content key authorization policy could have one or more authorization restrictions: open or token restriction. The token restricted policy must be accompanied by a token issued by a Secure Token Service (STS). Media Services supports tokens in the Simple Web Tokens (SWT) format and JSON Web Token (JWT) format. For more information, see Configure the content key’s authorization policy.

To take advantage of dynamic encryption, you need to have an asset that contains a set of multi-bitrate MP4 files or multi-bitrate Smooth Streaming source files. You also need to configure the delivery policies for the asset (described later in this topic). Then, based on the format specified in the streaming URL, the On-Demand Streaming server will ensure that the stream is delivered in the protocol you have chosen. As a result, you only need to store and pay for the files in a single storage format and Media Services will build and serve the appropriate HTTP response based on each request from a client.

This topic would be useful to developers that work on applications that deliver media protected with multiple DRMs, such as PlayReady and Widevine. The topic shows you how to configure the PlayReady license delivery service with authorization policies so that only authorized clients could receive PlayReady or Widevine licenses. It also shows how to use dynamic encryption encryption with PlayReady or Widevine DRM over DASH.

Note

When your AMS account is created a default streaming endpoint is added to your account in the Stopped state. To start streaming your content and take advantage of dynamic packaging and dynamic encryption, the streaming endpoint from which you want to stream content has to be in the Running state.

Download sample

You can download the sample described in this article from here.

Configuring Dynamic Common Encryption and DRM License Delivery Services

The following are general steps that you would need to perform when protecting your assets with PlayReady, using the Media Services license delivery service, and also using dynamic encryption.

  1. Create an asset and upload files into the asset.
  2. Encode the asset containing the file to the adaptive bitrate MP4 set.
  3. Create a content key and associate it with the encoded asset. In Media Services, the content key contains the asset’s encryption key.
  4. Configure the content key’s authorization policy. The content key authorization policy must be configured by you and met by the client in order for the content key to be delivered to the client.

    When creating the content key authorization policy, you need to specify the following: delivery method (PlayReady or Widevine), restrictions (open or token), and information specific to the key delivery type that defines how the key is delivered to the client (PlayReady or Widevine license template).

  5. Configure the delivery policy for an asset. The delivery policy configuration includes: delivery protocol (for example, MPEG DASH, HLS, Smooth Streaming or all), the type of dynamic encryption (for example, Common Encryption), PlayReady or Widevine license acquisition URL.

    You could apply different policy to each protocol on the same asset. For example, you could apply PlayReady encryption to Smooth/DASH and AES Envelope to HLS. Any protocols that are not defined in a delivery policy (for example, you add a single policy that only specifies HLS as the protocol) will be blocked from streaming. The exception to this is if you have no asset delivery policy defined at all. Then, all protocols will be allowed in the clear.

  6. Create an OnDemand locator in order to get a streaming URL.

You will find a complete .NET example at the end of the topic.

The following image demonstrates the workflow described above. Here the token is used for authentication.

Protect with PlayReady

The rest of this topic provides detailed explanations, code examples, and links to topics that show you how to achieve the tasks described above.

Current limitations

If you add or update an asset delivery policy, you must delete the associated locator (if any) and create a new locator.

Limitation when encrypting with Widevine with Azure Media Services: currently, multiple content keys are not supported.

Create an asset and upload files into the asset

In order to manage, encode, and stream your videos, you must first upload your content into Microsoft Azure Media Services. Once uploaded, your content is stored securely in the cloud for further processing and streaming.

For detailed information, see Upload Files into a Media Services account.

Encode the asset containing the file to the adaptive bitrate MP4 set

With dynamic encryption all you need is to create an asset that contains a set of multi-bitrate MP4 files or multi-bitrate Smooth Streaming source files. Then, based on the specified format in the manifest and fragment request, the On-Demand Streaming server will ensure that you receive the stream in the protocol you have chosen. As a result, you only need to store and pay for the files in single storage format and Media Services service will build and serve the appropriate response based on requests from a client. For more information, see the Dynamic Packaging Overview topic.

For instructions on how to encode, see How to encode an asset using Media Encoder Standard.

Create a content key and associate it with the encoded asset

In Media Services, the content key contains the key that you want to encrypt an asset with.

For detailed information, see Create content key.

Configure the content key’s authorization policy

Media Services supports multiple ways of authenticating users who make key requests. The content key authorization policy must be configured by you and met by the client (player) in order for the key to be delivered to the client. The content key authorization policy could have one or more authorization restrictions: open or token restriction.

For detailed information, see Configure Content Key Authorization Policy.

Configure asset delivery policy

Configure the delivery policy for your asset. Some things that the asset delivery policy configuration includes:

  • The DRM license acquisition URL.
  • The asset delivery protocol (for example, MPEG DASH, HLS, Smooth Streaming or all).
  • The type of dynamic encryption (in this case, Common Encryption).

For detailed information, see Configure asset delivery policy .

Create an OnDemand streaming locator in order to get a streaming URL

You will need to provide your user with the streaming URL for Smooth, DASH or HLS.

Note

If you add or update your asset’s delivery policy, you must delete an existing locator (if any) and create a new locator.

For instructions on how to publish an asset and build a streaming URL, see Build a streaming URL.

Get a test token

Get a test token based on the token restriction that was used for the key authorization policy.

// Deserializes a string containing an Xml representation of a TokenRestrictionTemplate
// back into a TokenRestrictionTemplate class instance.
TokenRestrictionTemplate tokenTemplate =
    TokenRestrictionTemplateSerializer.Deserialize(tokenTemplateString);

// Generate a test token based on the data in the given TokenRestrictionTemplate.
//The GenerateTestToken method returns the token without the word “Bearer” in front
//so you have to add it in front of the token string.
string testToken = TokenRestrictionTemplateSerializer.GenerateTestToken(tokenTemplate);
Console.WriteLine("The authorization token is:\nBearer {0}", testToken);

You can use the AMS Player to test your stream.

Create and configure a Visual Studio project

  1. Set up your development environment and populate the app.config file with connection information, as described in Media Services development with .NET.
  2. Add the following elements to appSettings defined in your app.config file:

     <add key="Issuer" value="http://testacs.com"/>
     <add key="Audience" value="urn:test"/>
    

Example

The following sample demonstrates functionality that was introduced in Azure Media Services SDK for .Net -Version 3.5.2 (specifically, the ability to define a Widevine license template and request a Widevine license from Azure Media Services).

Overwrite the code in your Program.cs file with the code shown in this section.

Note

There is a limit of 1,000,000 policies for different AMS policies (for example, for Locator policy or ContentKeyAuthorizationPolicy). You should use the same policy ID if you are always using the same days / access permissions, for example, policies for locators that are intended to remain in place for a long time (non-upload policies). For more information, see this topic.

Make sure to update variables to point to folders where your input files are located.

using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Threading;
using Microsoft.WindowsAzure.MediaServices.Client;
using Microsoft.WindowsAzure.MediaServices.Client.ContentKeyAuthorization;
using Microsoft.WindowsAzure.MediaServices.Client.DynamicEncryption;
using Microsoft.WindowsAzure.MediaServices.Client.Widevine;
using Newtonsoft.Json;

namespace DynamicEncryptionWithDRM
{
    class Program
    {
    // Read values from the App.config file.
    private static readonly string _AADTenantDomain =
    ConfigurationManager.AppSettings["AADTenantDomain"];
    private static readonly string _RESTAPIEndpoint =
    ConfigurationManager.AppSettings["MediaServiceRESTAPIEndpoint"];

    private static readonly Uri _sampleIssuer =
        new Uri(ConfigurationManager.AppSettings["Issuer"]);
    private static readonly Uri _sampleAudience =
        new Uri(ConfigurationManager.AppSettings["Audience"]);

    // Field for service context.
    private static CloudMediaContext _context = null;

    private static readonly string _mediaFiles =
        Path.GetFullPath(@"../..\Media");

    private static readonly string _singleMP4File =
        Path.Combine(_mediaFiles, @"BigBuckBunny.mp4");

    static void Main(string[] args)
    {
        var tokenCredentials = new AzureAdTokenCredentials(_AADTenantDomain, AzureEnvironments.AzureCloudEnvironment);
        var tokenProvider = new AzureAdTokenProvider(tokenCredentials);

        _context = new CloudMediaContext(new Uri(_RESTAPIEndpoint), tokenProvider);

        bool tokenRestriction = false;
        string tokenTemplateString = null;

        IAsset asset = UploadFileAndCreateAsset(_singleMP4File);
        Console.WriteLine("Uploaded asset: {0}", asset.Id);

        IAsset encodedAsset = EncodeToAdaptiveBitrateMP4Set(asset);
        Console.WriteLine("Encoded asset: {0}", encodedAsset.Id);

        IContentKey key = CreateCommonTypeContentKey(encodedAsset);
        Console.WriteLine("Created key {0} for the asset {1} ", key.Id, encodedAsset.Id);
        Console.WriteLine("PlayReady License Key delivery URL: {0}", key.GetKeyDeliveryUrl(ContentKeyDeliveryType.PlayReadyLicense));
        Console.WriteLine();

        if (tokenRestriction)
        tokenTemplateString = AddTokenRestrictedAuthorizationPolicy(key);
        else
        AddOpenAuthorizationPolicy(key);

        Console.WriteLine("Added authorization policy: {0}", key.AuthorizationPolicyId);
        Console.WriteLine();

        CreateAssetDeliveryPolicy(encodedAsset, key);
        Console.WriteLine("Created asset delivery policy. \n");
        Console.WriteLine();

        if (tokenRestriction && !String.IsNullOrEmpty(tokenTemplateString))
        {
        // Deserializes a string containing an Xml representation of a TokenRestrictionTemplate
        // back into a TokenRestrictionTemplate class instance.
        TokenRestrictionTemplate tokenTemplate =
            TokenRestrictionTemplateSerializer.Deserialize(tokenTemplateString);

        // Generate a test token based on the the data in the given TokenRestrictionTemplate.
        // Note, you need to pass the key id Guid because we specified
        // TokenClaim.ContentKeyIdentifierClaim in during the creation of TokenRestrictionTemplate.
        Guid rawkey = EncryptionUtils.GetKeyIdAsGuid(key.Id);
        string testToken = TokenRestrictionTemplateSerializer.GenerateTestToken(tokenTemplate, null, rawkey,
                                    DateTime.UtcNow.AddDays(365));
        Console.WriteLine("The authorization token is:\nBearer {0}", testToken);
        Console.WriteLine();
        }

        // You can use the http://amsplayer.azurewebsites.net/azuremediaplayer.html player to test streams.
        // Note that DASH works on IE 11 (via PlayReady), Edge (via PlayReady), Chrome (via Widevine).

        string url = GetStreamingOriginLocator(encodedAsset);
        Console.WriteLine("Encrypted DASH URL: {0}/manifest(format=mpd-time-csf)", url);

        Console.ReadLine();
    }

    static public IAsset UploadFileAndCreateAsset(string singleFilePath)
    {
        if (!File.Exists(singleFilePath))
        {
        Console.WriteLine("File does not exist.");
        return null;
        }

        var assetName = Path.GetFileNameWithoutExtension(singleFilePath);
        IAsset inputAsset = _context.Assets.Create(assetName, AssetCreationOptions.None);

        var assetFile = inputAsset.AssetFiles.Create(Path.GetFileName(singleFilePath));

        Console.WriteLine("Created assetFile {0}", assetFile.Name);

        Console.WriteLine("Upload {0}", assetFile.Name);

        assetFile.Upload(singleFilePath);
        Console.WriteLine("Done uploading {0}", assetFile.Name);

        return inputAsset;
    }

    static public IAsset EncodeToAdaptiveBitrateMP4Set(IAsset inputAsset)
    {
        var encodingPreset = "Adaptive Streaming";

        IJob job = _context.Jobs.Create(String.Format("Encoding into Mp4 {0} to {1}",
                    inputAsset.Name,
                    encodingPreset));

        var mediaProcessors =
        _context.MediaProcessors.Where(p => p.Name.Contains("Media Encoder Standard")).ToList();

        var latestMediaProcessor =
        mediaProcessors.OrderBy(mp => new Version(mp.Version)).LastOrDefault();

        ITask encodeTask = job.Tasks.AddNew("Encoding", latestMediaProcessor, encodingPreset, TaskOptions.None);
        encodeTask.InputAssets.Add(inputAsset);
        encodeTask.OutputAssets.AddNew(String.Format("{0} as {1}", inputAsset.Name, encodingPreset), AssetCreationOptions.StorageEncrypted);

        job.StateChanged += new EventHandler<JobStateChangedEventArgs>(JobStateChanged);
        job.Submit();
        job.GetExecutionProgressTask(CancellationToken.None).Wait();

        return job.OutputMediaAssets[0];
    }


    static public IContentKey CreateCommonTypeContentKey(IAsset asset)
    {

        Guid keyId = Guid.NewGuid();
        byte[] contentKey = GetRandomBuffer(16);

        IContentKey key = _context.ContentKeys.Create(
                    keyId,
                    contentKey,
                    "ContentKey",
                    ContentKeyType.CommonEncryption);

        // Associate the key with the asset.
        asset.ContentKeys.Add(key);

        return key;
    }

    static public void AddOpenAuthorizationPolicy(IContentKey contentKey)
    {

        // Create ContentKeyAuthorizationPolicy with Open restrictions
        // and create authorization policy          

        List<ContentKeyAuthorizationPolicyRestriction> restrictions = new List<ContentKeyAuthorizationPolicyRestriction>
            {
            new ContentKeyAuthorizationPolicyRestriction
            {
                Name = "Open",
                KeyRestrictionType = (int)ContentKeyRestrictionType.Open,
                Requirements = null
            }
            };

        // Configure PlayReady and Widevine license templates.
        string PlayReadyLicenseTemplate = ConfigurePlayReadyLicenseTemplate();

        string WidevineLicenseTemplate = ConfigureWidevineLicenseTemplate();

        IContentKeyAuthorizationPolicyOption PlayReadyPolicy =
        _context.ContentKeyAuthorizationPolicyOptions.Create("",
            ContentKeyDeliveryType.PlayReadyLicense,
            restrictions, PlayReadyLicenseTemplate);

        IContentKeyAuthorizationPolicyOption WidevinePolicy =
        _context.ContentKeyAuthorizationPolicyOptions.Create("",
            ContentKeyDeliveryType.Widevine,
            restrictions, WidevineLicenseTemplate);

        IContentKeyAuthorizationPolicy contentKeyAuthorizationPolicy = _context.
            ContentKeyAuthorizationPolicies.
            CreateAsync("Deliver Common Content Key with no restrictions").
            Result;


        contentKeyAuthorizationPolicy.Options.Add(PlayReadyPolicy);
        contentKeyAuthorizationPolicy.Options.Add(WidevinePolicy);
        // Associate the content key authorization policy with the content key.
        contentKey.AuthorizationPolicyId = contentKeyAuthorizationPolicy.Id;
        contentKey = contentKey.UpdateAsync().Result;
    }

    public static string AddTokenRestrictedAuthorizationPolicy(IContentKey contentKey)
    {
        string tokenTemplateString = GenerateTokenRequirements();

        List<ContentKeyAuthorizationPolicyRestriction> restrictions = new List<ContentKeyAuthorizationPolicyRestriction>
            {
            new ContentKeyAuthorizationPolicyRestriction
            {
                Name = "Token Authorization Policy",
                KeyRestrictionType = (int)ContentKeyRestrictionType.TokenRestricted,
                Requirements = tokenTemplateString,
            }
            };

        // Configure PlayReady and Widevine license templates.
        string PlayReadyLicenseTemplate = ConfigurePlayReadyLicenseTemplate();

        string WidevineLicenseTemplate = ConfigureWidevineLicenseTemplate();

        IContentKeyAuthorizationPolicyOption PlayReadyPolicy =
        _context.ContentKeyAuthorizationPolicyOptions.Create("Token option",
            ContentKeyDeliveryType.PlayReadyLicense,
            restrictions, PlayReadyLicenseTemplate);

        IContentKeyAuthorizationPolicyOption WidevinePolicy =
        _context.ContentKeyAuthorizationPolicyOptions.Create("Token option",
            ContentKeyDeliveryType.Widevine,
            restrictions, WidevineLicenseTemplate);

        IContentKeyAuthorizationPolicy contentKeyAuthorizationPolicy = _context.
            ContentKeyAuthorizationPolicies.
            CreateAsync("Deliver Common Content Key with token restrictions").
            Result;

        contentKeyAuthorizationPolicy.Options.Add(PlayReadyPolicy);
        contentKeyAuthorizationPolicy.Options.Add(WidevinePolicy);

        // Associate the content key authorization policy with the content key
        contentKey.AuthorizationPolicyId = contentKeyAuthorizationPolicy.Id;
        contentKey = contentKey.UpdateAsync().Result;

        return tokenTemplateString;
    }

    static private string GenerateTokenRequirements()
    {
        TokenRestrictionTemplate template = new TokenRestrictionTemplate(TokenType.SWT);

        template.PrimaryVerificationKey = new SymmetricVerificationKey();
        template.AlternateVerificationKeys.Add(new SymmetricVerificationKey());
        template.Audience = _sampleAudience.ToString();
        template.Issuer = _sampleIssuer.ToString();
        template.RequiredClaims.Add(TokenClaim.ContentKeyIdentifierClaim);

        return TokenRestrictionTemplateSerializer.Serialize(template);
    }

    static private string ConfigurePlayReadyLicenseTemplate()
    {
        // The following code configures PlayReady License Template using .NET classes
        // and returns the XML string.

        //The PlayReadyLicenseResponseTemplate class represents the template for the response sent back to the end user.
        //It contains a field for a custom data string between the license server and the application
        //(may be useful for custom app logic) as well as a list of one or more license templates.
        PlayReadyLicenseResponseTemplate responseTemplate = new PlayReadyLicenseResponseTemplate();

        // The PlayReadyLicenseTemplate class represents a license template for creating PlayReady licenses
        // to be returned to the end users.
        //It contains the data on the content key in the license and any rights or restrictions to be
        //enforced by the PlayReady DRM runtime when using the content key.
        PlayReadyLicenseTemplate licenseTemplate = new PlayReadyLicenseTemplate();
        //Configure whether the license is persistent (saved in persistent storage on the client)
        //or non-persistent (only held in memory while the player is using the license).  
        licenseTemplate.LicenseType = PlayReadyLicenseType.Nonpersistent;

        // AllowTestDevices controls whether test devices can use the license or not.  
        // If true, the MinimumSecurityLevel property of the license
        // is set to 150.  If false (the default), the MinimumSecurityLevel property of the license is set to 2000.
        licenseTemplate.AllowTestDevices = true;

        // You can also configure the Play Right in the PlayReady license by using the PlayReadyPlayRight class.
        // It grants the user the ability to playback the content subject to the zero or more restrictions
        // configured in the license and on the PlayRight itself (for playback specific policy).
        // Much of the policy on the PlayRight has to do with output restrictions
        // which control the types of outputs that the content can be played over and
        // any restrictions that must be put in place when using a given output.
        // For example, if the DigitalVideoOnlyContentRestriction is enabled,
        //then the DRM runtime will only allow the video to be displayed over digital outputs
        //(analog video outputs won’t be allowed to pass the content).

        //IMPORTANT: These types of restrictions can be very powerful but can also affect the consumer experience.
        // If the output protections are configured too restrictive,
        // the content might be unplayable on some clients. For more information, see the PlayReady Compliance Rules document.

        // For example:
        //licenseTemplate.PlayRight.AgcAndColorStripeRestriction = new AgcAndColorStripeRestriction(1);

        responseTemplate.LicenseTemplates.Add(licenseTemplate);

        return MediaServicesLicenseTemplateSerializer.Serialize(responseTemplate);
    }

    private static string ConfigureWidevineLicenseTemplate()
    {
        var template = new WidevineMessage
        {
        allowed_track_types = AllowedTrackTypes.SD_HD,
        content_key_specs = new[]
        {
                new ContentKeySpecs
                {
                required_output_protection = new RequiredOutputProtection { hdcp = Hdcp.HDCP_NONE},
                security_level = 1,
                track_type = "SD"
                }
            },
        policy_overrides = new
        {
            can_play = true,
            can_persist = true,
            can_renew = false
        }
        };

        string configuration = JsonConvert.SerializeObject(template);
        return configuration;
    }

    static public void CreateAssetDeliveryPolicy(IAsset asset, IContentKey key)
    {
        // Get the PlayReady license service URL.
        Uri acquisitionUrl = key.GetKeyDeliveryUrl(ContentKeyDeliveryType.PlayReadyLicense);

        // GetKeyDeliveryUrl for Widevine attaches the KID to the URL.
        // For example: https://amsaccount1.keydelivery.mediaservices.windows.net/Widevine/?KID=268a6dcb-18c8-4648-8c95-f46429e4927c.  
        // The WidevineBaseLicenseAcquisitionUrl (used below) also tells Dynamaic Encryption
        // to append /? KID =< keyId > to the end of the url when creating the manifest.
        // As a result Widevine license acquisition URL will have KID appended twice,
        // so we need to remove the KID that in the URL when we call GetKeyDeliveryUrl.

        Uri widevineUrl = key.GetKeyDeliveryUrl(ContentKeyDeliveryType.Widevine);
        UriBuilder uriBuilder = new UriBuilder(widevineUrl);
        uriBuilder.Query = String.Empty;
        widevineUrl = uriBuilder.Uri;

        Dictionary<AssetDeliveryPolicyConfigurationKey, string> assetDeliveryPolicyConfiguration =
        new Dictionary<AssetDeliveryPolicyConfigurationKey, string>
        {
                {AssetDeliveryPolicyConfigurationKey.PlayReadyLicenseAcquisitionUrl, acquisitionUrl.ToString()},
                {AssetDeliveryPolicyConfigurationKey.WidevineBaseLicenseAcquisitionUrl, widevineUrl.ToString()}

        };

        // In this case we only specify Dash streaming protocol in the delivery policy,
        // All other protocols will be blocked from streaming.
        var assetDeliveryPolicy = _context.AssetDeliveryPolicies.Create(
            "AssetDeliveryPolicy",
        AssetDeliveryPolicyType.DynamicCommonEncryption,
        AssetDeliveryProtocol.Dash,
        assetDeliveryPolicyConfiguration);


        // Add AssetDelivery Policy to the asset
        asset.DeliveryPolicies.Add(assetDeliveryPolicy);

    }

    /// <summary>
    /// Gets the streaming origin locator.
    /// </summary>
    /// <param name="assets"></param>
    /// <returns></returns>
    static public string GetStreamingOriginLocator(IAsset asset)
    {

        // Get a reference to the streaming manifest file from the  
        // collection of files in the asset.

        var assetFile = asset.AssetFiles.Where(f => f.Name.ToLower().
                     EndsWith(".ism")).
                     FirstOrDefault();

        // Create a 30-day readonly access policy.
        IAccessPolicy policy = _context.AccessPolicies.Create("Streaming policy",
        TimeSpan.FromDays(30),
        AccessPermissions.Read);

        // Create a locator to the streaming content on an origin.
        ILocator originLocator = _context.Locators.CreateLocator(LocatorType.OnDemandOrigin, asset,
        policy,
        DateTime.UtcNow.AddMinutes(-5));

        // Create a URL to the manifest file.
        return originLocator.Path + assetFile.Name;
    }

    static private void JobStateChanged(object sender, JobStateChangedEventArgs e)
    {
        Console.WriteLine(string.Format("{0}\n  State: {1}\n  Time: {2}\n\n",
        ((IJob)sender).Name,
        e.CurrentState,
        DateTime.UtcNow.ToString(@"yyyy_M_d__hh_mm_ss")));
    }

    static private byte[] GetRandomBuffer(int length)
    {
        var returnValue = new byte[length];

        using (var rng =
        new System.Security.Cryptography.RNGCryptoServiceProvider())
        {
        rng.GetBytes(returnValue);
        }

        return returnValue;
    }
    }
}

Next step

Review Media Services learning paths.

You can view Azure Media Services learning paths here:

Provide feedback

Use the User Voice forum to provide feedback and make suggestions on how to improve Azure Media Services. You can also go directly to one of the following categories:

See also

CENC with Multi-DRM and Access Control

Configure Widevine packaging with AMS

Announcing Google Widevine license delivery services in Azure Media Services