Skip to content
Permalink
2 contributors

Users who have contributed to this file

@Juliako @johndeu
791 lines (678 sloc) 36.5 KB
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Microsoft.Azure.Management.Media;
using Microsoft.Azure.Management.Media.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Rest;
using Microsoft.Rest.Azure.Authentication;
using Microsoft.WindowsAzure.Storage.Blob;
namespace EncryptWithDRM
{
class Program
{
private const string AdaptiveStreamingTransformName = "MyTransformWithAdaptiveStreamingPreset";
private static string Issuer = "myIssuer";
private static string Audience = "myAudience";
private static byte[] TokenSigningKey = new byte[40];
private static string ContentKeyPolicyName = "DRMContentKeyPolicy";
public static async Task Main(string[] args)
{
ConfigWrapper config = new ConfigWrapper(new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build());
try
{
await RunAsync(config);
}
catch (Exception exception)
{
Console.Error.WriteLine($"{exception.Message}");
ApiErrorException apiException = exception.GetBaseException() as ApiErrorException;
if (apiException != null)
{
Console.Error.WriteLine(
$"ERROR: API call failed with error code '{apiException.Body.Error.Code}' and message '{apiException.Body.Error.Message}'.");
}
}
Console.WriteLine("Press Enter to continue.");
Console.ReadLine();
}
/// <summary>
/// Run the sample async.
/// </summary>
/// <param name="config">The parm is of type ConfigWrapper. This class reads values from local configuration file.</param>
/// <returns></returns>
// <RunAsync>
private static async Task RunAsync(ConfigWrapper config)
{
IAzureMediaServicesClient client = await CreateMediaServicesClientAsync(config);
// Set the polling interval for long running operations to 2 seconds.
// The default value is 30 seconds for the .NET client SDK
client.LongRunningOperationRetryTimeout = 2;
// Creating a unique suffix so that we don't have name collisions if you run the sample
// multiple times without cleaning up.
string uniqueness = Guid.NewGuid().ToString("N");
string jobName = $"job-{uniqueness}";
string locatorName = $"locator-{uniqueness}";
string outputAssetName = $"output-{uniqueness}";
// Ensure that you have the desired encoding Transform. This is really a one time setup operation.
Transform transform = await GetOrCreateTransformAsync(client, config.ResourceGroup, config.AccountName, AdaptiveStreamingTransformName);
// Output from the encoding Job must be written to an Asset, so let's create one
Asset outputAsset = await CreateOutputAssetAsync(client, config.ResourceGroup, config.AccountName, outputAssetName);
Job job = await SubmitJobAsync(client, config.ResourceGroup, config.AccountName, AdaptiveStreamingTransformName, outputAsset.Name, jobName);
// In this demo code, we will poll for Job status
// Polling is not a recommended best practice for production applications because of the latency it introduces.
// Overuse of this API may trigger throttling. Developers should instead use Event Grid.
job = await WaitForJobToFinishAsync(client, config.ResourceGroup, config.AccountName, AdaptiveStreamingTransformName, jobName);
if (job.State == JobState.Finished)
{
// Set a token signing key that you want to use
TokenSigningKey = Convert.FromBase64String(config.SymmetricKey);
// Create the content key policy that configures how the content key is delivered to end clients
// via the Key Delivery component of Azure Media Services.
// We are using the ContentKeyIdentifierClaim in the ContentKeyPolicy which means that the token presented
// to the Key Delivery Component must have the identifier of the content key in it.
ContentKeyPolicy policy = await GetOrCreateContentKeyPolicyAsync(client, config.ResourceGroup, config.AccountName, ContentKeyPolicyName, TokenSigningKey);
StreamingLocator locator = await CreateStreamingLocatorAsync(client, config.ResourceGroup, config.AccountName, outputAsset.Name, locatorName, ContentKeyPolicyName);
// In this example, we want to play the PlayReady (CENC) encrypted stream.
// We need to get the key identifier of the content key where its type is CommonEncryptionCenc.
string keyIdentifier = locator.ContentKeys.Where(k => k.Type == StreamingLocatorContentKeyType.CommonEncryptionCenc).First().Id.ToString();
Console.WriteLine($"KeyIdentifier = {keyIdentifier}");
// In order to generate our test token we must get the ContentKeyId to put in the ContentKeyIdentifierClaim claim.
string token = GetTokenAsync(Issuer, Audience, keyIdentifier, TokenSigningKey);
string dashPath = await GetDASHStreamingUrlAsync(client, config.ResourceGroup, config.AccountName, locator.Name);
Console.WriteLine("Copy and paste the following URL in your browser to play back the file in the Azure Media Player.");
Console.WriteLine("You can use Edge/IE11 for PlayReady and Chrome/Firefox Widevine.");
Console.WriteLine();
Console.WriteLine($"https://ampdemo.azureedge.net/?url={dashPath}&playready=true&widevine=true&token=Bearer%3D{token}");
Console.WriteLine();
}
Console.WriteLine("When finished testing press enter to cleanup.");
Console.Out.Flush();
Console.ReadLine();
Console.WriteLine("Cleaning up...");
await CleanUpAsync(client, config.ResourceGroup, config.AccountName, AdaptiveStreamingTransformName, ContentKeyPolicyName);
}
// </RunAsync>
/// <summary>
/// Create the ServiceClientCredentials object based on the credentials
/// supplied in local configuration file.
/// </summary>
/// <param name="config">The parm is of type ConfigWrapper. This class reads values from local configuration file.</param>
/// <returns></returns>
// <GetCredentialsAsync>
private static async Task<ServiceClientCredentials> GetCredentialsAsync(ConfigWrapper config)
{
// Use ApplicationTokenProvider.LoginSilentWithCertificateAsync or UserTokenProvider.LoginSilentAsync to get a token using service principal with certificate
//// ClientAssertionCertificate
//// ApplicationTokenProvider.LoginSilentWithCertificateAsync
// Use ApplicationTokenProvider.LoginSilentAsync to get a token using a service principal with symetric key
ClientCredential clientCredential = new ClientCredential(config.AadClientId, config.AadSecret);
return await ApplicationTokenProvider.LoginSilentAsync(config.AadTenantId, clientCredential, ActiveDirectoryServiceSettings.Azure);
}
// </GetCredentialsAsync>
/// <summary>
/// Creates the AzureMediaServicesClient object based on the credentials
/// supplied in local configuration file.
/// </summary>
/// <param name="config">The parm is of type ConfigWrapper. This class reads values from local configuration file.</param>
/// <returns></returns>
// <CreateMediaServicesClient>
private static async Task<IAzureMediaServicesClient> CreateMediaServicesClientAsync(ConfigWrapper config)
{
var credentials = await GetCredentialsAsync(config);
return new AzureMediaServicesClient(config.ArmEndpoint, credentials)
{
SubscriptionId = config.SubscriptionId,
};
}
// </CreateMediaServicesClient>
/// <summary>
/// Create the content key policy that configures how the content key is delivered to end clients
/// via the Key Delivery component of Azure Media Services.
/// </summary>
/// <param name="client">The Media Services client.</param>
/// <param name="resourceGroupName">The name of the resource group within the Azure subscription.</param>
/// <param name="accountName"> The Media Services account name.</param>
/// <param name="contentKeyPolicyName">The name of the content key policy resource.</param>
/// <returns></returns>
// <GetOrCreateContentKeyPolicy>
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();
ContentKeyPolicyWidevineConfiguration widevineConfig = ConfigureWidevineLicenseTempate();
// 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
});
options.Add(
new ContentKeyPolicyOption()
{
Configuration = widevineConfig,
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;
}
// </GetOrCreateContentKeyPolicy>
/// <summary>
/// If the specified transform exists, get that transform.
/// If the it does not exist, creates a new transform with the specified output.
/// In this case, the output is set to encode a video using one of the built-in encoding presets.
/// </summary>
/// <param name="client">The Media Services client.</param>
/// <param name="resourceGroupName">The name of the resource group within the Azure subscription.</param>
/// <param name="accountName"> The Media Services account name.</param>
/// <param name="transformName">The name of the transform.</param>
/// <returns></returns>
// <EnsureTransformExists>
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;
}
// </EnsureTransformExists>
/// <summary>
/// Creates an ouput asset. The output from the encoding Job must be written to an Asset.
/// </summary>
/// <param name="client">The Media Services client.</param>
/// <param name="resourceGroupName">The name of the resource group within the Azure subscription.</param>
/// <param name="accountName"> The Media Services account name.</param>
/// <param name="assetName">The output asset name.</param>
/// <returns></returns>
// <CreateOutputAsset>
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);
}
// </CreateOutputAsset>
/// <summary>
/// Submits a request to Media Services to apply the specified Transform to a given input video.
/// </summary>
/// <param name="client">The Media Services client.</param>
/// <param name="resourceGroupName">The name of the resource group within the Azure subscription.</param>
/// <param name="accountName"> The Media Services account name.</param>
/// <param name="transformName">The name of the transform.</param>
/// <param name="outputAssetName">The (unique) name of the output asset that will store the result of the encoding job. </param>
/// <param name="jobName">The (unique) name of the job.</param>
/// <returns></returns>
// <SubmitJob>
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;
}
// </SubmitJob>
/// <summary>
/// Polls Media Services for the status of the Job.
/// </summary>
/// <param name="client">The Media Services client.</param>
/// <param name="resourceGroupName">The name of the resource group within the Azure subscription.</param>
/// <param name="accountName"> The Media Services account name.</param>
/// <param name="transformName">The name of the transform.</param>
/// <param name="jobName">The name of the job you submitted.</param>
/// <returns></returns>
// <WaitForJobToFinish>
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;
}
// </WaitForJobToFinish>
/// <summary>
/// Configures PlayReady license template.
/// </summary>
/// <returns></returns>
//<ConfigurePlayReadyLicenseTemplate>
private static ContentKeyPolicyPlayReadyConfiguration ConfigurePlayReadyLicenseTemplate()
{
ContentKeyPolicyPlayReadyLicense objContentKeyPolicyPlayReadyLicense;
objContentKeyPolicyPlayReadyLicense = new ContentKeyPolicyPlayReadyLicense
{
AllowTestDevices = true,
BeginDate = new DateTime(2016, 1, 1),
ContentKeyLocation = new ContentKeyPolicyPlayReadyContentEncryptionKeyFromHeader(),
ContentType = ContentKeyPolicyPlayReadyContentType.UltraVioletStreaming,
LicenseType = ContentKeyPolicyPlayReadyLicenseType.Persistent,
PlayRight = new ContentKeyPolicyPlayReadyPlayRight
{
ImageConstraintForAnalogComponentVideoRestriction = true,
ExplicitAnalogTelevisionOutputRestriction = new ContentKeyPolicyPlayReadyExplicitAnalogTelevisionRestriction(true, 2),
AllowPassingVideoContentToUnknownOutput = ContentKeyPolicyPlayReadyUnknownOutputPassingOption.Allowed
}
};
ContentKeyPolicyPlayReadyConfiguration objContentKeyPolicyPlayReadyConfiguration = new ContentKeyPolicyPlayReadyConfiguration
{
Licenses = new List<ContentKeyPolicyPlayReadyLicense> { objContentKeyPolicyPlayReadyLicense }
};
return objContentKeyPolicyPlayReadyConfiguration;
}
// </ConfigurePlayReadyLicenseTemplate>
/// <summary>
/// Configures Widevine license template.
/// </summary>
/// <returns></returns>
// <ConfigureWidevineLicenseTempate>
private static ContentKeyPolicyWidevineConfiguration ConfigureWidevineLicenseTempate()
{
WidevineTemplate template = new WidevineTemplate()
{
AllowedTrackTypes = "SD_HD",
ContentKeySpecs = new ContentKeySpec[]
{
new ContentKeySpec()
{
TrackType = "SD",
SecurityLevel = 1,
RequiredOutputProtection = new OutputProtection()
{
HDCP = "HDCP_NONE"
}
}
},
PolicyOverrides = new PolicyOverrides()
{
CanPlay = true,
CanPersist = true,
CanRenew = false,
RentalDurationSeconds = 2592000,
PlaybackDurationSeconds = 10800,
LicenseDurationSeconds = 604800,
}
};
ContentKeyPolicyWidevineConfiguration objContentKeyPolicyWidevineConfiguration = new ContentKeyPolicyWidevineConfiguration
{
WidevineTemplate = Newtonsoft.Json.JsonConvert.SerializeObject(template)
};
return objContentKeyPolicyWidevineConfiguration;
}
// </ConfigureWidevineLicenseTempate>
/// <summary>
/// Configures FairPlay policy options.
/// </summary>
/// <returns></returns>
// <ConfigureFairPlayPolicyOptions>
private static ContentKeyPolicyFairPlayConfiguration ConfigureFairPlayPolicyOptions()
{
string askHex = "";
string FairPlayPfxPassword = "";
var appCert = new X509Certificate2("FairPlayPfxPath", FairPlayPfxPassword, X509KeyStorageFlags.Exportable);
byte[] askBytes = Enumerable
.Range(0, askHex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(askHex.Substring(x, 2), 16))
.ToArray();
ContentKeyPolicyFairPlayConfiguration fairPlayConfiguration =
new ContentKeyPolicyFairPlayConfiguration
{
Ask = askBytes,
FairPlayPfx =
Convert.ToBase64String(appCert.Export(X509ContentType.Pfx, FairPlayPfxPassword)),
FairPlayPfxPassword = FairPlayPfxPassword,
RentalAndLeaseKeyType =
ContentKeyPolicyFairPlayRentalAndLeaseKeyType
.PersistentUnlimited,
RentalDuration = 2249
};
return fairPlayConfiguration;
}
// </ConfigureFairPlayPolicyOptions>
/// <summary>
/// Creates a StreamingLocator for the specified asset and with the specified streaming policy name.
/// Once the StreamingLocator is created the output asset is available to clients for playback.
///
/// This StreamingLocator uses "Predefined_MultiDrmCencStreaming"
/// because this sample encrypts with PlayReady and Widevine (CENC encryption).
///
/// </summary>
/// <param name="client">The Media Services client.</param>
/// <param name="resourceGroupName">The name of the resource group within the Azure subscription.</param>
/// <param name="accountName"> The Media Services account name.</param>
/// <param name="assetName">The name of the output asset.</param>
/// <param name="locatorName">The StreamingLocator name (unique in this case).</param>
/// <returns></returns>
// <CreateStreamingLocator>
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;
}
// </CreateStreamingLocator>
/// <summary>
/// Create a token that will be used to protect your stream.
/// Only authorized clients would be able to play the video.
/// </summary>
/// <param name="issuer">The issuer is the secure token service that issues the token. </param>
/// <param name="audience">The audience, sometimes called scope, describes the intent of the token or the resource the token authorizes access to. </param>
/// <param name="keyIdentifier">The content key ID.</param>
/// <param name="tokenVerificationKey">Contains the key that the token was signed with. </param>
/// <returns></returns>
// <GetToken>
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);
}
// </GetToken>
/// <summary>
/// Checks if the "default" streaming endpoint is in the running state,
/// if not, starts it.
/// Then, builds the streaming URLs.
/// </summary>
/// <param name="client">The Media Services client.</param>
/// <param name="resourceGroupName">The name of the resource group within the Azure subscription.</param>
/// <param name="accountName"> The Media Services account name.</param>
/// <param name="locatorName">The name of the StreamingLocator that was created.</param>
/// <returns></returns>
// <GetMPEGStreamingUrl>
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;
}
// </GetMPEGStreamingUrl>
/// <summary>
/// Downloads the results from the specified output asset, so you can see what you got.
/// </summary>
/// <param name="client">The Media Services client.</param>
/// <param name="resourceGroupName">The name of the resource group within the Azure subscription.</param>
/// <param name="accountName"> The Media Services account name.</param>
/// <param name="assetName">The output asset.</param>
/// <param name="outputFolderName">The name of the folder into which to download the results.</param>
// <DownloadResults>
private static async Task DownloadOutputAssetAsync(
IAzureMediaServicesClient client,
string resourceGroup,
string accountName,
string assetName,
string outputFolderName)
{
if (!Directory.Exists(outputFolderName))
{
Directory.CreateDirectory(outputFolderName);
}
AssetContainerSas assetContainerSas = await client.Assets.ListContainerSasAsync(
resourceGroup,
accountName,
assetName,
permissions: AssetContainerPermission.Read,
expiryTime: DateTime.UtcNow.AddHours(1).ToUniversalTime());
Uri containerSasUrl = new Uri(assetContainerSas.AssetContainerSasUrls.FirstOrDefault());
CloudBlobContainer container = new CloudBlobContainer(containerSasUrl);
string directory = Path.Combine(outputFolderName, assetName);
Directory.CreateDirectory(directory);
Console.WriteLine($"Downloading output results to '{directory}'...");
BlobContinuationToken continuationToken = null;
IList<Task> downloadTasks = new List<Task>();
do
{
// A non-negative integer value that indicates the maximum number of results to be returned at a time,
// up to the per-operation limit of 5000. If this value is null, the maximum possible number of results
// will be returned, up to 5000.
int? ListBlobsSegmentMaxResult = null;
BlobResultSegment segment = await container.ListBlobsSegmentedAsync(null, true, BlobListingDetails.None, ListBlobsSegmentMaxResult, continuationToken, null, null);
foreach (IListBlobItem blobItem in segment.Results)
{
CloudBlockBlob blob = blobItem as CloudBlockBlob;
if (blob != null)
{
string path = Path.Combine(directory, blob.Name);
downloadTasks.Add(blob.DownloadToFileAsync(path, FileMode.Create));
}
}
continuationToken = segment.ContinuationToken;
}
while (continuationToken != null);
await Task.WhenAll(downloadTasks);
Console.WriteLine("Download complete.");
}
// </DownloadResults>
/// <summary>
/// Deletes the jobs and assets that were created.
/// Generally, you should clean up everything except objects
/// that you are planning to reuse (typically, you will reuse Transforms, and you will persist StreamingLocators).
/// </summary>
/// <param name="client"></param>
/// <param name="resourceGroupName"></param>
/// <param name="accountName"></param>
/// <param name="transformName"></param>
// <CleanUp>
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);
}
// </CleanUp>
}
}
You can’t perform that action at this time.