教程:使用 DRM 动态加密和许可证传送服务Tutorial: Use DRM dynamic encryption and license delivery service

Note

Google Widevine 目前在中国地区不可用。Google Widevine is not avaliable in China regions now.

Note

尽管本教程使用了 .NET SDK 示例,但 REST APICLI 或其他受支持的 SDK 的常规步骤是相同的。Even though the tutorial uses the .NET SDK examples, the general steps are the same for REST API, CLI, or other supported SDKs.

可以使用 Azure 媒体服务来传送通过 Microsoft PlayReady 或 Apple FairPlay 许可证加密的流。You can use Azure Media Services to deliver your streams encrypted with Microsoft PlayReady, or Apple FairPlay licenses. 如需深入说明,请参阅使用动态加密保护内容For in-depth explanation, see Content protection with dynamic encryption.

此外,媒体服务提供用于传送 PlayReady 和 FairPlay DRM 许可证的服务。Furthermore, Media Services provides a service for delivering PlayReady, and FairPlay DRM licenses. 当用户请求受 DRM 保护的内容时,播放器应用程序会从媒体服务许可证服务请求许可证。When a user requests DRM-protected content, 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. 许可证包含客户端播放器用来对内容进行解密和流式传输的解密密钥。A license contains the decryption key that can be used by the client player to decrypt and stream the content.

本文基于使用 DRM 进行加密示例。This article is based on the Encrypting with DRM sample.

本文中所述的示例生成以下结果:The sample described in this article produces the following result:

具有 DRM 保护的视频的 AMS

本教程演示如何:This tutorial shows you how to:

  • 创建编码转换Create an encoding Transform
  • 设置用于验证令牌的签名密钥Set the signing key used for verification of your token
  • 设置对内容密钥策略的要求Set requirements on the content key policy
  • 使用指定的流式处理策略创建 StreamingLocatorCreate a StreamingLocator with the specified streaming policy
  • 创建一个用于播放文件的 URLCreate a URL used to playback your file

先决条件Prerequisites

以下是完成本教程所需具备的条件。The following are required to complete the tutorial.

下载代码Download code

使用以下命令,将包含本文中所述完整 .NET 示例的 GitHub 存储库克隆到计算机:Clone a GitHub repository that contains the full .NET sample discussed in this article to your machine using the following command:

git clone https://github.com/Azure-Samples/media-services-v3-dotnet-tutorials.git

“使用 DRM 进行加密”示例位于 EncryptWithDRM 文件夹中。The "Encrypt with DRM" sample is located in the EncryptWithDRM folder.

Note

每次运行应用程序时,该示例就将创建唯一的资源。The sample creates unique resources every time you run the application. 通常,你会重复使用现有的资源,例如转换和策略(如果现有资源具有所需的配置)。Typically, you will reuse existing resources like transforms and policies (if existing resource have required configurations).

开始结合使用媒体服务 API 与 .NET SDKStart using Media Services APIs with .NET SDK

若要开始将媒体服务 API 与 .NET 结合使用,需要创建 AzureMediaServicesClient 对象 。To start using Media Services APIs with .NET, you need to create an AzureMediaServicesClient object. 若要创建对象,需要提供客户端所需凭据以使用 Azure AD 连接到 Azure。To create the object, you need to supply credentials needed for the client to connect to Azure using Azure AD. 在本文开头克隆的代码中,GetCredentialsAsync 函数根据本地配置文件中提供的凭据创建 ServiceClientCredentials 对象。In the code you cloned at the beginning of the article, the GetCredentialsAsync function creates the ServiceClientCredentials object based on the credentials supplied in the local configuration file.

private static async Task<IAzureMediaServicesClient> CreateMediaServicesClientAsync(ConfigWrapper config)
{
    var credentials = await GetCredentialsAsync(config);

    return new AzureMediaServicesClient(config.ArmEndpoint, credentials)
    {
        SubscriptionId = config.SubscriptionId,
    };
}

创建输出资产Create an output asset

输出资产会存储作业编码的结果。The output Asset stores the result of your encoding job.

private static async Task<Asset> CreateOutputAssetAsync(IAzureMediaServicesClient client, string resourceGroupName, string accountName, string assetName)
{
    // Check if an Asset already exists
    Asset outputAsset = await client.Assets.GetAsync(resourceGroupName, accountName, assetName);
    Asset asset = new Asset();
    string outputAssetName = assetName;

    if (outputAsset != null)
    {
        // Name collision! In order to get the sample to work, let's just go ahead and create a unique asset name
        // Note that the returned Asset can have a different name than the one specified as an input parameter.
        // You may want to update this part to throw an Exception instead, and handle name collisions differently.
        string uniqueness = $"-{Guid.NewGuid().ToString("N")}";
        outputAssetName += uniqueness;
        
        Console.WriteLine("Warning – found an existing Asset with name = " + assetName);
        Console.WriteLine("Creating an Asset with this name instead: " + outputAssetName);                
    }

    return await client.Assets.CreateOrUpdateAsync(resourceGroupName, accountName, outputAssetName, asset);
}

获取或创建编码转换Get or create an encoding Transform

创建新实例时,需要指定希望生成的输出内容转换When creating a new Transform instance, you need to specify what you want it to produce as an output. 所需参数是 transformOutput 对象,如以下代码所示。The required parameter is a transformOutput object, as shown in the code below. 每个 TransformOutput 包含一个预设 。Each TransformOutput contains a Preset. 预设介绍了视频和/或音频处理操作的分步说明,这些操作将用于生成所需的 TransformOutput。Preset describes the step-by-step instructions of video and/or audio processing operations that are to be used to generate the desired TransformOutput. 本文中的示例使用名为 AdaptiveStreaming 的内置预设 。The sample described in this article uses a built-in Preset called AdaptiveStreaming. 此预设将输入的视频编码为基于输入的分辨率和比特率自动生成的比特率阶梯(比特率 - 分辨率对),并通过与每个比特率 - 分辨率对相对应的 H.264 视频和 AAC 音频生成 ISO MP4 文件。The Preset encodes the input video into an auto-generated bitrate ladder (bitrate-resolution pairs) based on the input resolution and bitrate, and produces ISO MP4 files with H.264 video and AAC audio corresponding to each bitrate-resolution pair.

在创建新的转换之前,应该先检查是否已存在使用 Get 方法的转换,如以下代码中所示。Before creating a new Transform, you should first check if one already exists using the Get method, as shown in the code that follows. 在 Media Services v3获取实体上的方法返回null如果实体不存在 (不区分大小写的名称检查)。In Media Services v3, Get methods on entities return null if the entity doesn’t exist (a case-insensitive check on the name).

private static async Task<Transform> GetOrCreateTransformAsync(
    IAzureMediaServicesClient client,
    string resourceGroupName,
    string accountName,
    string transformName)
{
    // Does a Transform already exist with the desired name? Assume that an existing Transform with the desired name
    // also uses the same recipe or Preset for processing content.
    Transform transform = await client.Transforms.GetAsync(resourceGroupName, accountName, transformName);

    if (transform == null)
    {
        // You need to specify what you want it to produce as an output
        TransformOutput[] output = new TransformOutput[]
        {
            new TransformOutput
            {
                // The preset for the Transform is set to one of Media Services built-in sample presets.
                // You can  customize the encoding settings by changing this to use "StandardEncoderPreset" class.
                Preset = new BuiltInStandardEncoderPreset()
                {
                    // This sample uses the built-in encoding preset for Adaptive Bitrate Streaming.
                    PresetName = EncoderNamedPreset.AdaptiveStreaming
                }
            }
        };

        // Create the Transform with the output defined above
        transform = await client.Transforms.CreateOrUpdateAsync(resourceGroupName, accountName, transformName, output);
    }

    return transform;
}

提交作业Submit Job

如上所述,转换对象为脚本,作业则是对媒体服务的实际请求,请求将转换应用到给定输入视频或音频内容 。As mentioned above, the Transform object is the recipe and a Job is the actual request to Media Services to apply that Transform to a given input video or audio content. 作业指定输入视频位置和输出位置等信息 。The Job specifies information like the location of the input video, and the location for the output.

本教程基于直接从 HTTPs 源 URL 引入的文件创建作业的输入。In this tutorial, we create the job's input based on a file that is ingested directly from an HTTPs source URL.

private static async Task<Job> SubmitJobAsync(IAzureMediaServicesClient client,
    string resourceGroup,
    string accountName,
    string transformName,
    string outputAssetName,
    string jobName)
{
    // This example shows how to encode from any HTTPs source URL - a new feature of the v3 API.  
    // Change the URL to any accessible HTTPs URL or SAS URL from Azure.
    JobInputHttp jobInput =
        new JobInputHttp(files: new[] { "https://nimbuscdn-nimbuspm.streaming.mediaservices.windows.net/2b533311-b215-4409-80af-529c3e853622/Ignite-short.mp4" });

    JobOutput[] jobOutputs =
    {
        new JobOutputAsset(outputAssetName),
    };

    // In this example, we are assuming that the job name is unique.
    //
    // If you already have a job with the desired name, use the Jobs.Get method
    // to get the existing job. In Media Services v3, the Get method on entities returns null 
    // if the entity doesn't exist (a case-insensitive check on the name).
    Job job = await client.Jobs.CreateAsync(
        resourceGroup,
        accountName,
        transformName,
        jobName,
        new Job
        {
            Input = jobInput,
            Outputs = jobOutputs,
        });

    return job;
}

等待作业完成Wait for the Job to complete

此作业需要一些时间才能完成,完成时可发出通知。The job takes some time to complete and when it does you want to be notified. 以下代码示例显示如何轮询服务以获取作业状态。The code sample below shows how to poll the service for the status of the Job.

作业通常会经历以下状态:已计划已排队正在处理已完成(最终状态)。The Job usually goes through the following states: Scheduled, Queued, Processing, Finished (the final state). 如果作业出错,则显示“错误”状态 。If the job has encountered an error, you get the Error state. 如果作业正处于取消过程中,则显示“正在取消”,完成时则显示“已取消” 。If the job is in the process of being canceled, you get Canceling and Canceled when it is done.

private static async Task<Job> WaitForJobToFinishAsync(IAzureMediaServicesClient client,
    string resourceGroupName,
    string accountName,
    string transformName,
    string jobName)
{
    const int SleepIntervalMs = 60 * 1000;

    Job job = null;

    do
    {
        job = await client.Jobs.GetAsync(resourceGroupName, accountName, transformName, jobName);

        Console.WriteLine($"Job is '{job.State}'.");
        for (int i = 0; i < job.Outputs.Count; i++)
        {
            JobOutput output = job.Outputs[i];
            Console.Write($"\tJobOutput[{i}] is '{output.State}'.");
            if (output.State == JobState.Processing)
            {
                Console.Write($"  Progress: '{output.Progress}'.");
            }

            Console.WriteLine();
        }

        if (job.State != JobState.Finished && job.State != JobState.Error && job.State != JobState.Canceled)
        {
            await Task.Delay(SleepIntervalMs);
        }
    }
    while (job.State != JobState.Finished && job.State != JobState.Error && job.State != JobState.Canceled);

    return job;
}

创建内容密钥策略Create a Content Key Policy

内容密钥提供对资产的安全访问。A content key provides secure access to your Assets. 通过 DRM 加密内容时,需要创建内容密钥策略You need to create a Content Key Policy when encrypting your content with a DRM. 此策略配置如何将内容密钥传送到最终的客户端。The policy configures how the content key is delivered to end clients. 内容密钥与流定位器相关联。The content key is associated with a Streaming Locator. 媒体服务还提供密钥传送服务,将加密密钥和许可证传送给已授权的用户。Media Services also provides the key delivery service that delivers encryption keys and licenses to authorized users.

需要在使用指定的配置传送密钥时必须满足的内容密钥策略中设置要求(限制)。You need to set the requirements (restrictions) on the Content Key Policy that must be met to deliver keys with the specified configuration. 此示例设置了以下配置和要求:In this example, we set the following configurations and requirements:

  • 配置Configuration

    配置了 PlayReady 许可证,因此只能由媒体服务许可证传送服务传送这些许可证。The PlayReady license is configured so they can be delivered by the Media Services license delivery service. 尽管此示例应用未配置 FairPlay 许可证,但它包含一个可用来配置 FairPlay 的方法。Even though, this sample app does not configure the FairPlay license, it contains a method that you can use to configure FairPlay. 可以添加 FairPlay 配置作为另一个选项。You can add FairPlay configuration as another option.

  • 限制Restriction

    该应用在策略中设置了 JWT 令牌类型限制。The app sets a JWT token type restriction on the policy.

当播放器请求流时,媒体服务将使用指定的密钥动态加密内容。When a stream is requested by a player, Media Services uses the specified key to dynamically encrypt your content. 为了解密流,播放器将从密钥传送服务请求密钥。To decrypt the stream, the player requests the key from the key delivery service. 为了确定是否已授权用户获取密钥,服务将评估你为密钥指定的内容密钥策略。To determine whether the user is authorized to get the key, the service evaluates the content key policy that you specified for the key.

private static async Task<ContentKeyPolicy> GetOrCreateContentKeyPolicyAsync(
    IAzureMediaServicesClient client,
    string resourceGroupName,
    string accountName,
    string contentKeyPolicyName,
    byte[] tokenSigningKey)
{
    ContentKeyPolicy policy = await client.ContentKeyPolicies.GetAsync(resourceGroupName, accountName, contentKeyPolicyName);

    if (policy == null)
    {
        ContentKeyPolicySymmetricTokenKey primaryKey = new ContentKeyPolicySymmetricTokenKey(tokenSigningKey);
        List<ContentKeyPolicyTokenClaim> requiredClaims = new List<ContentKeyPolicyTokenClaim>()
        {
            ContentKeyPolicyTokenClaim.ContentKeyIdentifierClaim
        };
        List<ContentKeyPolicyRestrictionTokenKey> alternateKeys = null;
        ContentKeyPolicyTokenRestriction restriction 
            = new ContentKeyPolicyTokenRestriction(Issuer, Audience, primaryKey, ContentKeyPolicyRestrictionTokenType.Jwt, alternateKeys, requiredClaims);

        ContentKeyPolicyPlayReadyConfiguration playReadyConfig = ConfigurePlayReadyLicenseTemplate();
        // ContentKeyPolicyFairPlayConfiguration fairplayConfig = ConfigureFairPlayPolicyOptions();

        List<ContentKeyPolicyOption> options = new List<ContentKeyPolicyOption>();

        options.Add(
            new ContentKeyPolicyOption()
            {
                Configuration = playReadyConfig,
                // If you want to set an open restriction, use
                // Restriction = new ContentKeyPolicyOpenRestriction()
                Restriction = restriction
            });

     // add CBCS ContentKeyPolicyOption into the list
     //   options.Add(
     //       new ContentKeyPolicyOption()
     //       {
     //           Configuration = fairplayConfig,
     //           Restriction = restriction,
     //           Name = "ContentKeyPolicyOption_CBCS"
     //       });

        policy = await client.ContentKeyPolicies.CreateOrUpdateAsync(resourceGroupName, accountName, contentKeyPolicyName, options);
    }
    else
    {
        // Get the signing key from the existing policy.
        var policyProperties = await client.ContentKeyPolicies.GetPolicyPropertiesWithSecretsAsync(resourceGroupName, accountName, contentKeyPolicyName);
        var restriction = policyProperties.Options[0].Restriction as ContentKeyPolicyTokenRestriction;
        if (restriction != null)
        {
            var signingKey = restriction.PrimaryVerificationKey as ContentKeyPolicySymmetricTokenKey;
            if (signingKey != null)
            {
                TokenSigningKey = signingKey.KeyValue;
            }
        }
    }
    return policy;
}

创建流定位符Create a Streaming Locator

完成编码并设置内容密钥策略后,下一步是使输出资产中的视频可供客户端播放。After the encoding is complete, and the content key policy is set, the next step is to make the video in the output Asset available to clients for playback. 通过两个步骤实现此目的:You accomplish this in two steps:

  1. 创建流式处理定位符Create a Streaming Locator
  2. 生成客户端可以使用的流式处理 URL。Build the streaming URLs that clients can use.

创建流定位器的过程称为发布。The process of creating the Streaming Locator is called publishing. 默认情况下,除非配置可选的开始和结束时间,否则调用 API 后,流定位符立即生效,并持续到被删除为止。By default, the Streaming Locator is valid immediately after you make the API calls, and lasts until it is deleted, unless you configure the optional start and end times.

创建流定位器时,需要指定所需的 StreamingPolicyNameWhen creating a Streaming Locator, you need to specify the desired StreamingPolicyName. 本教程使用某个预定义的流式处理策略来告知 Azure 媒体服务如何发布流式处理的内容。In this tutorial, we are using one of the predefined Streaming Policies, which tells Azure Media Services how to publish the content for streaming. 在此示例中,请将 StreamingLocator.StreamingPolicyName 设置为“Predefined_MultiDrmCencStreaming”策略。In this example, we set StreamingLocator.StreamingPolicyName to the "Predefined_MultiDrmCencStreaming" policy. 将应用 PlayReady 加密,并根据配置的 DRM 许可证将密钥传送到播放客户端。The PlayReady encryption is applied, the key is delivered to the playback client based on the configured DRM licenses. 如果还要使用 CBCS (FairPlay) 加密流,请使用“Predefined_MultiDrmStreaming”。If you also want to encrypt your stream with CBCS (FairPlay), use "Predefined_MultiDrmStreaming".

Important

使用自定义的流策略时,应为媒体服务帐户设计有限的一组此类策略,并在需要同样的加密选项和协议时重新将这些策略用于 StreamingLocators。When using a custom Streaming Policy, you should design a limited set of such policies for your Media Service account, and re-use them for your StreamingLocators whenever the same encryption options and protocols are needed. 媒体服务帐户具有对应于 StreamingPolicy 条目数的配额。Your Media Service account has a quota for the number of StreamingPolicy entries. 不应为每个 StreamingLocator 创建新的 StreamingPolicy。You should not be creating a new StreamingPolicy for each StreamingLocator.

private static async Task<StreamingLocator> CreateStreamingLocatorAsync(
    IAzureMediaServicesClient client,
    string resourceGroup,
    string accountName,
    string assetName,
    string locatorName,
    string contentPolicyName)
{
    // If you also added FairPlay, use "Predefined_MultiDrmStreaming
    StreamingLocator locator = await client.StreamingLocators.CreateAsync(
        resourceGroup,
        accountName,
        locatorName,
        new StreamingLocator
        {
            AssetName = assetName,
            // "Predefined_MultiDrmCencStreaming" policy supports envelope and cenc encryption
            // And sets two content keys on the StreamingLocator
            StreamingPolicyName = "Predefined_MultiDrmCencStreaming",
            DefaultContentKeyPolicyName = contentPolicyName
        });

    return locator;
}

获取测试令牌Get a test token

本教程在内容密钥策略中指定使用令牌限制。In this tutorial, we specify for the content key policy to have a token restriction. 令牌限制策略必须附带由安全令牌服务 (STS) 颁发的令牌。The token-restricted policy must be accompanied by a token issued by a security token service (STS). 媒体服务支持采用 JSON Web 令牌 (JWT) 格式的令牌,我们在示例中配置了此格式。Media Services supports tokens in the JSON Web Token (JWT) formats and that is what we configure in the sample.

ContentKeyPolicy 中使用了 ContentKeyIdentifierClaim,这意味着,提供给密钥传送服务的令牌必须包含 ContentKey 的标识符。The ContentKeyIdentifierClaim is used in the ContentKeyPolicy, which means that the token presented to the key delivery service must have the identifier of the ContentKey in it. 本示例未指定内容密钥,在创建流定位器时,系统会创建一个随机内容密钥。In the sample, we don't specify a content key when creating the Streaming Locator, the system creates a random one for us. 若要生成测试令牌,必须获取要放入 ContentKeyIdentifierClaim 声明中的 ContentKeyId。In order to generate the test token, we must get the ContentKeyId to put in the ContentKeyIdentifierClaim claim.

private static string GetTokenAsync(string issuer, string audience, string keyIdentifier, byte[] tokenVerificationKey)
{
    var tokenSigningKey = new SymmetricSecurityKey(tokenVerificationKey);

    SigningCredentials cred = new SigningCredentials(
        tokenSigningKey,
        // Use the  HmacSha256 and not the HmacSha256Signature option, or the token will not work!
        SecurityAlgorithms.HmacSha256,
        SecurityAlgorithms.Sha256Digest);

    Claim[] claims = new Claim[]
    {
        new Claim(ContentKeyPolicyTokenClaim.ContentKeyIdentifierClaim.ClaimType, keyIdentifier)
    };
    
    // To set a limit on how many times the same token can be used to request a key or a license.
    // add  the "urn:microsoft:azure:mediaservices:maxuses" claim.
    // For example, claims.Add(new Claim("urn:microsoft:azure:mediaservices:maxuses", 4));
    
    JwtSecurityToken token = new JwtSecurityToken(
        issuer: issuer,
        audience: audience,
        claims: claims,
        notBefore: DateTime.Now.AddMinutes(-5),
        expires: DateTime.Now.AddMinutes(60),
        signingCredentials: cred);

    JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();

    return handler.WriteToken(token);
}

生成流 URLBuild a streaming URL

创建 StreamingLocator 后,可以获取流 URL。Now that the StreamingLocator has been created, you can get the streaming URLs. 若要生成 URL,需要连接 StreamingEndpoint 主机名称和流定位器路径 。To build a URL, you need to concatenate the StreamingEndpoint host name and the Streaming Locator path. 此示例使用默认的流式处理终结点In this sample, the default Streaming Endpoint is used. 首次创建媒体服务帐户时,默认的流式处理终结点处于停止状态,因此需要调用 StartWhen you first create a Media Service account, this default Streaming Endpoint will be in a stopped state, so you need to call Start.

private static async Task<string> GetDASHStreamingUrlAsync(
    IAzureMediaServicesClient client,
    string resourceGroupName,
    string accountName,
    string locatorName)
{
    const string DefaultStreamingEndpointName = "default";

    string dashPath = "";

    StreamingEndpoint streamingEndpoint = await client.StreamingEndpoints.GetAsync(resourceGroupName, accountName, DefaultStreamingEndpointName);

    if (streamingEndpoint != null)
    {
        if (streamingEndpoint.ResourceState != StreamingEndpointResourceState.Running)
        {
            await client.StreamingEndpoints.StartAsync(resourceGroupName, accountName, DefaultStreamingEndpointName);
        }
    }

    ListPathsResponse paths = await client.StreamingLocators.ListPathsAsync(resourceGroupName, accountName, locatorName);

    foreach (StreamingPath path in paths.StreamingPaths)
    {
        UriBuilder uriBuilder = new UriBuilder();
        uriBuilder.Scheme = "https";
        uriBuilder.Host = streamingEndpoint.HostName;

        // Look for just the DASH path and generate a URL for the Azure Media Player to playback the encrypted DASH content. 
        // Note that the JWT token is set to expire in 1 hour. 
        if (path.StreamingProtocol == StreamingPolicyStreamingProtocol.Dash)
        {
            uriBuilder.Path = path.Paths[0];

            dashPath = uriBuilder.ToString();

        }
    }

    return dashPath;
}

运行应用时看到以下内容:When you run the app, you see the following:

使用 DRM 提供保护

可以打开浏览器并粘贴生成的 URL 来启动 Azure Media Player 演示页,其中已经填充了该 URL 和令牌。You can open a browser and paste the resulting URL to launch the Azure Media Player demo page with the URL and token filled out for you already.

清理媒体服务帐户中的资源Clean up resources in your Media Services account

通常情况下,除了打算重复使用的对象,用户应清理所有内容(通常将重复使用转换并保留 StreamingLocators 等)。Generally, you should clean up everything except objects that you are planning to reuse (typically, you will reuse Transforms, and you will persist StreamingLocators, etc.). 如果希望帐户在试验后保持干净状态,则应删除不打算重复使用的资源。If you want for your account to be clean after experimenting, you should delete the resources that you do not plan to reuse. 例如,以下代码可删除作业。For example, the following code deletes Jobs.

private static async Task CleanUpAsync(
    IAzureMediaServicesClient client,
    string resourceGroupName,
    string accountName,
    string transformName,
    string contentKeyPolicyName)
{

    var jobs = await client.Jobs.ListAsync(resourceGroupName, accountName, transformName);
    foreach (var job in jobs)
    {
        await client.Jobs.DeleteAsync(resourceGroupName, accountName, transformName, job.Name);
    }

    var streamingLocators = await client.StreamingLocators.ListAsync(resourceGroupName, accountName);
    foreach (var locator in streamingLocators)
    {
        await client.StreamingLocators.DeleteAsync(resourceGroupName, accountName, locator.Name);
    }

    var assets = await client.Assets.ListAsync(resourceGroupName, accountName);
    foreach (var asset in assets)
    {
        await client.Assets.DeleteAsync(resourceGroupName, accountName, asset.Name);
    }

    client.ContentKeyPolicies.Delete(resourceGroupName, accountName, contentKeyPolicyName);

}

清理资源Clean up resources

如果不再需要资源组中的任何一个资源(包括为本教程创建的媒体服务和存储帐户),请删除之前创建的资源组。If you no longer need any of the resources in your resource group, including the Media Services and storage accounts you created for this tutorial, delete the resource group you created earlier.

执行以下 CLI 命令:Execute the following CLI command:

az group delete --name amsResourceGroup

后续步骤Next steps

签出Check out