借助媒體服務,可以傳送使用 AES 通過 128 位加密密鑰加密的 HTTP Live Streaming (HLS)、MPEG-DASH 和平滑流。 媒體服務還提供密鑰傳送服務,將加密密鑰傳送給已授權的用戶。 如果希望媒體服務來動態加密你的視頻,您將加密密鑰與流式處理定位符相關聯,並還配置內容密鑰的策略。 當播放器請求流時,媒體服務將使用指定的密鑰來動態加密使用 AES-128 內容。 為解密流,播放器從密鑰傳送服務請求密鑰。 為了確定是否已授權用戶獲取密鑰,服務將評估你為密鑰指定的內容密鑰策略。
可以使用多個加密類型(AES-128、PlayReady、Widevine、FairPlay)來加密每個資產。 請參閱流式處理協議和加密類型,以了解有效的組合方式。 此外,請參閱如何使用 DRM 進行保護。
該示例的輸出這篇文章包括指向 Azure 媒體播放器的 URL、 清單 URL 和播放內容所需的 AES 令牌。 該示例將 JWT 令牌的過期時間設置為 1 小時。 可以打開瀏覽器並粘貼生成的 URL 來啟動 Azure Media Player 演示頁,其中已經填充了該 URL 和令牌(采用以下格式:https://ampdemo.azureedge.net/?url= {dash Manifest URL} &aes=true&aestoken=Bearer%3D{ JWT Token here}
)。
本教程演示如何:
- 下載EncryptWithAES本文所述的示例
- 開始結合使用媒體服務 API 與 .NET SDK
- 創建輸出資產
- 創建編碼的轉換
- 提交作業
- 等待作業完成
- 創建內容密鑰策略
- 將策略配置為使用 JWT 令牌限制
- 創建流定位符
- 配置流式處理定位符來加密使用 AES (ClearKey) 視頻
- 獲取測試令牌
- 生成流 URL
- 清理資源
如果還沒有 Azure 訂閱,可以在開始前創建一個免費帳戶。
必備組件
以下是完成本教程所需具備的條件。
下載代碼
使用以下命令,將包含本文中所述完整 .NET 示例的 GitHub 存儲庫克隆到計算機:
git clone https://github.com/Azure-Samples/media-services-v3-dotnet-tutorials.git
“使用 AES-128 加密”示例位於 EncryptWithAES 文件夾中。
備注
每次運行應用程序時,該示例就將創建唯一的資源。 通常,你會重復使用現有的資源,例如轉換和策略(如果現有資源具有所需的配置)。
開始結合使用媒體服務 API 與 .NET SDK
若要開始將媒體服務 API 與 .NET 結合使用,需要創建 AzureMediaServicesClient 對象。 若要創建對象,需要提供客戶端所需憑據以使用 Azure AD 連接到 Azure。 在本文開頭克隆的代碼中,GetCredentialsAsync 函數根據本地配置文件中提供的憑據創建 ServiceClientCredentials 對象。
private static async Task<IAzureMediaServicesClient> CreateMediaServicesClientAsync(ConfigWrapper config) { var credentials = await GetCredentialsAsync(config); return new AzureMediaServicesClient(config.ArmEndpoint, credentials) { SubscriptionId = config.SubscriptionId, }; }
創建輸出資產
輸出資產會存儲作業編碼的結果。
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); }
獲取或創建編碼轉換
創建新轉換實例時,需要指定希望生成的輸出內容。 所需參數是 TransformOutput 對象,如以下代碼所示。 每個 TransformOutput 包含一個預設。 預設介紹了視頻和/或音頻處理操作的分步說明,這些操作將用於生成所需的 TransformOutput。 本文中的示例使用名為 AdaptiveStreaming 的內置預設。 此預設將輸入的視頻編碼為基於輸入的分辨率和比特率自動生成的比特率階梯(比特率 - 分辨率對),並通過與每個比特率 - 分辨率對相對應的 H.264 視頻和 AAC 音頻生成 ISO MP4 文件。
在創建新的轉換之前,應該先檢查是否已存在使用 Get 方法的轉換,如以下代碼中所示。 在 Media Services v3獲取實體上的方法返回null如果實體不存在 (不區分大小寫的名稱檢查)。
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; }
提交作業
如上所述,轉換對象為腳本,作業則是對媒體服務的實際請求,請求將轉換應用到給定輸入視頻或音頻內容。 作業指定輸入視頻位置和輸出位置等信息。
本教程基於直接從 HTTPs 源 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; }
等待作業完成
此作業需要一些時間才能完成,完成時可發出通知。 以下代碼示例顯示如何輪詢服務以獲取作業狀態。 對於生產應用程序,由於可能出現延遲,並不建議將輪詢作為最佳做法。 如果在帳戶上過度使用輪詢,輪詢會受到限制。 開發者應改用事件網格。 請參閱將事件路由到自定義 Web 終結點。
作業通常會經歷以下狀態:已計划、已排隊、正在處理、已完成(最終狀態)。 如果作業出錯,則顯示“錯誤”狀態。 如果作業正處於取消過程中,則顯示“正在取消”,完成時則顯示“已取消”。
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; }
創建內容密鑰策略
內容密鑰提供對資產的安全訪問。 您需要創建內容密鑰策略用於配置內容密鑰傳遞的方式來結束客戶端。 與關聯的內容密鑰流式處理定位符。 媒體服務還提供密鑰傳送服務,將加密密鑰傳送給已授權的用戶。
當播放器請求流時,媒體服務將使用指定的密鑰通過 AES 加密來動態加密內容(本例中使用 AES 加密)。為解密流,播放器從密鑰傳送服務請求密鑰。 為了確定是否已授權用戶獲取密鑰,服務將評估你為密鑰指定的內容密鑰策略。
private static async Task<ContentKeyPolicy> GetOrCreateContentKeyPolicyAsync( IAzureMediaServicesClient client, string resourceGroupName, string accountName, string contentKeyPolicyName) { ContentKeyPolicy policy = await client.ContentKeyPolicies.GetAsync(resourceGroupName, accountName, contentKeyPolicyName); if (policy == null) { ContentKeyPolicySymmetricTokenKey primaryKey = new ContentKeyPolicySymmetricTokenKey(TokenSigningKey); List<ContentKeyPolicyRestrictionTokenKey> alternateKeys = null; List<ContentKeyPolicyTokenClaim> requiredClaims = new List<ContentKeyPolicyTokenClaim>() { ContentKeyPolicyTokenClaim.ContentKeyIdentifierClaim }; List<ContentKeyPolicyOption> options = new List<ContentKeyPolicyOption>() { new ContentKeyPolicyOption( new ContentKeyPolicyClearKeyConfiguration(), new ContentKeyPolicyTokenRestriction(Issuer, Audience, primaryKey, ContentKeyPolicyRestrictionTokenType.Jwt, alternateKeys, requiredClaims)) }; // Since we are randomly generating the signing key each time, make sure to create or update the policy each time. // Normally you would use a long lived key so you would just check for the policies existence with Get instead of // ensuring to create or update it each time. policy = await client.ContentKeyPolicies.CreateOrUpdateAsync(resourceGroupName, accountName, contentKeyPolicyName, options); } return policy; }
創建流定位符
完成編碼並設置內容密鑰策略后,下一步是使輸出資產中的視頻可供客戶端播放。 通過兩個步驟實現此目的:
- 創建流式處理定位符
- 生成客戶端可以使用的流式處理 URL。
創建的過程流式處理定位符稱為發布。 默認情況下,除非配置可選的開始和結束時間,否則調用 API 后,流定位符立即生效,並持續到被刪除為止。
創建流式處理定位符時,需要指定所需的 StreamingPolicyName。 本教程使用某個 PredefinedStreamingPolicies 來告知 Azure 媒體服務如何發布流式處理的內容。 此示例中應用了 AES 信封加密(也稱為 ClearKey 加密,因為密鑰是通過 HTTPS 而不是 DRM 許可證傳送到播放客戶端的)。
重要
使用自定義的 StreamingPolicy 時,應為媒體服務帳戶設計有限的一組此類策略,並在需要同樣的加密選項和協議時重新將這些策略用於流式處理定位符。 媒體服務帳戶具有對應於 StreamingPolicy 條目數的配額。 您應該不需要創建新 StreamingPolicy 為每個流式處理定位符。
private static async Task<StreamingLocator> CreateStreamingLocatorAsync( IAzureMediaServicesClient client, string resourceGroup, string accountName, string assetName, string locatorName, string contentPolicyName) { StreamingLocator locator = await client.StreamingLocators.CreateAsync( resourceGroup, accountName, locatorName, new StreamingLocator { AssetName = assetName, StreamingPolicyName = PredefinedStreamingPolicy.ClearKey, DefaultContentKeyPolicyName = contentPolicyName }); return locator; }
獲取測試令牌
本教程在內容密鑰策略中指定使用令牌限制。 令牌限制策略必須附帶由安全令牌服務 (STS) 頒發的令牌。 媒體服務支持采用 JSON Web 令牌 (JWT) 格式的令牌,我們在示例中配置了此格式。
在中使用 ContentKeyIdentifierClaim內容密鑰策略,這意味着提供給密鑰傳送服務的令牌,必須具有在其中的內容密鑰的標識符。 在示例中,我們並未指定內容密鑰創建流式處理定位符時,系統將創建一個隨機的。 若要生成測試令牌,必須獲取要放入 ContentKeyIdentifierClaim 聲明中的 ContentKeyId。
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) }; 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); }
生成 DASH 流 URL
既然流式處理定位符已創建,可以獲取流 Url。 若要生成的 URL,您需要串聯StreamingEndpoint主機名和流式處理定位符路徑。 此示例使用默認的流式處理終結點。 首次創建媒體服務帳戶時,默認的流式處理終結點處於停止狀態,因此需要調用 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 content with the AES token to decrypt. // 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; }
清理媒體服務帳戶中的資源
通常情況下,您應清除想要重復使用的對象以外的所有內容 (通常情況下,將重復使用轉換,並且您將會保留流式處理定位符,等等。)。 如果希望帳戶在試驗后保持干凈狀態,則應刪除不打算重復使用的資源。 例如,以下代碼可刪除作業。
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 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); }
清理資源
如果不再需要資源組中的任何一個資源(包括為本教程創建的媒體服務和存儲帳戶),請刪除之前創建的資源組。
執行以下 CLI 命令:
az group delete --name amsResourceGroup
提出問題、 提供反饋,獲取更新
查看 Azure 媒體服務社區文章,了解可以提出問題、提供反饋和獲取有關媒體服務的更新的不同方法。