Implementare lo streaming del failover con Servizi multimediali di AzureImplement failover streaming with Azure Media Services

Questa procedura dettagliata illustra come copiare il contenuto (BLOB) da un asset all'altro per gestire la ridondanza dello streaming on demand.This walkthrough demonstrates how to copy content (blobs) from one asset into another in order to handle redundancy for on-demand streaming. Questo scenario è utile se si desidera configurare la rete di distribuzione dei contenuti di Azure affinché esegua il failover tra due data center, in caso di interruzione in uno di essi.This scenario is useful if you want to set up Azure Content Delivery Network to fail over between two datacenters, in case of an outage in one datacenter. In questa procedura dettagliata viene usato l'SDK dei Servizi multimediali di Azure, l'API REST di Servizi multimediali di Microsoft Azure e l'SDK di Archiviazione di Azure per dimostrare queste attività:This walkthrough uses the Azure Media Services SDK, the Azure Media Services REST API, and the Azure Storage SDK to demonstrate the following tasks:

  1. Configurare un account di Servizi multimediali nel "Data center A".Set up a Media Services account in "Data Center A."
  2. Caricare in un asset di origine un file in formato intermedio.Upload a mezzanine file into a source asset.
  3. Codificare l'asset in file MP4 a bit multipli.Encode the asset into multi-bit rate MP4 files.
  4. Creare un localizzatore per la firma di accesso condiviso di sola lettura.Create a read-only shared access signature locator. Questo consente all'asset di origine di avere l'accesso in lettura al contenitore nell'account di archiviazione associato all'asset di origine.This is for the source asset to have read access to the container in the storage account that is associated with the source asset.
  5. Ottenere il nome del contenitore dell'asset di origine dal localizzatore firma di accesso condiviso di sola lettura creato nel passaggio precedente.Get the container name of the source asset from the read-only shared access signature locator created in the previous step. Questa operazione è necessaria per copiare i BLOB tra gli account di archiviazione, operazione descritta più avanti in questo argomento.This is necessary for copying blobs between storage accounts (explained later in the topic.)
  6. Creare un localizzatore di origine per l'asset creato mediante l'attività di codifica.Create an origin locator for the asset that was created by the encoding task.

A quel punto, per gestire il failover:Then, to handle the failover:

  1. Configurare un account di Servizi multimediali nel "Data center B".Set up a Media Services account in "Data Center B."
  2. Creare un asset di destinazione vuoto nell'account di Servizi multimediali di destinazione.Create a target empty asset in the target Media Services account.
  3. Creare un localizzatore per la firma di accesso condiviso di scrittura.Create a write shared access signature locator. Questo consente all'asset vuoto di destinazione di avere l'accesso in scrittura al contenitore nell'account di archiviazione di destinazione associato all'asset di destinazione.This is for the target empty asset to have write access to the container in the target storage account that is associated with the target asset.
  4. Usare SDK di Archiviazione di Azure per copiare i BLOB (file di asset) tra l'account di archiviazione di origine nel "Data center A" e l'account di archiviazione di destinazione nel "Data center B".Use the Azure Storage SDK to copy blobs (asset files) between the source storage account in "Data Center A" and the target storage account in "Data Center B." Questi account di archiviazione sono associati agli asset di interesse.These storage accounts are associated with the assets of interest.
  5. Associare all'asset di destinazione i BLOB (file di asset) copiati nel contenitore BLOB di destinazione.Associate blobs (asset files) that were copied to the target blob container with the target asset.
  6. Creare un localizzatore di origine per l'asset nel "Data center B" e specificarne l'ID generato per l'asset nel "Data center A".Create an origin locator for the asset in "Data Center B", and specify the locator ID that was generated for the asset in "Data Center A."

In questo modo si ottengono URL di streaming in cui i percorsi relativi sono uguali (differiscono solo gli URL di base).This gives you the streaming URLs where the relative paths of the URLs are the same (only the base URLs are different).

Per gestire eventuali guasti, è quindi possibile creare una rete per la distribuzione di contenuti basata su tali localizzatori di origine.Then, to handle any outages, you can create a Content Delivery Network on top of these origin locators.

Si applicano le considerazioni seguenti:The following considerations apply:

  • La versione corrente di Media Services SDK non supporta la generazione a livello di codice delle informazioni IAssetFile per l'associazione di un asset ai file di asset.The current version of Media Services SDK does not support programmatically generating IAssetFile information that would associate an asset with asset files. Usare invece l'API REST CreateFileInfos dell'API REST di Servizi multimediali per farlo.Instead, use the CreateFileInfos Media Services REST API to do this.
  • Gli asset con crittografia di archiviazione (AssetCreationOptions.StorageEncrypted) non sono supportati per la replica, in quanto la chiave di crittografia è diversa nei due account di Servizi multimediali.Storage encrypted assets (AssetCreationOptions.StorageEncrypted) are not supported for replication (because the encryption key is different in both Media Services accounts).
  • Se si vuole sfruttare i vantaggi della creazione dinamica dei pacchetti, verificare che l'endpoint di streaming da cui trasmettere i contenuti si trovi nello stato In esecuzione.If you want to take advantage of dynamic packaging, make sure the streaming endpoint from which you want to stream your content is in the Running state.

Nota

Considerare la possibilità di usare il replicatore di Servizi multimediali come alternativa all'implementazione manuale di uno scenario di streaming con failover.Consider using the Media Services Replicator Tool as an alternative to implementing a failover streaming scenario manually. Tale strumento consente di replicare gli asset tra due account di Servizi multimediali.This tool allows you to replicate assets across two Media Services accounts.

PrerequisitiPrerequisites

  • Due account di Servizi multimediali in una sottoscrizione di Azure nuova o esistente.Two Media Services accounts in a new or existing Azure subscription. Vedere l'articolo relativo alla creazione di un account di Servizi multimediali.See How to Create a Media Services Account.
  • Sistemi operativi: Windows 7, Windows 2008 R2 o Windows 8.Operating system: Windows 7, Windows 2008 R2, or Windows 8.
  • .NET Framework 4.5 o .NET Framework 4..NET Framework 4.5 or .NET Framework 4.
  • Visual Studio 2010 SP1 o versioni successive (Professional, Premium, Ultimate o Express).Visual Studio 2010 SP1 or later version (Professional, Premium, Ultimate, or Express).

Configurare il progettoSet up your project

In questa sezione si crea e si configura un progetto di applicazione console in C#.In this section, you create and set up a C# Console Application project.

  1. Usare Visual Studio per creare una nuova soluzione contenente il progetto di applicazione console in C#.Use Visual Studio to create a new solution that contains the C# Console Application project. Immettere HandleRedundancyForOnDemandStreaming come nome e quindi fare clic su OK.Enter HandleRedundancyForOnDemandStreaming for the name, and then click OK.
  2. Creare la cartella SupportFiles allo stesso livello del file di progetto HandleRedundancyForOnDemandStreaming.csproj.Create the SupportFiles folder on the same level as the HandleRedundancyForOnDemandStreaming.csproj project file. Nella cartella SupportFiles creare le cartelle OutputFiles e MP4Files.Under the SupportFiles folder, create the OutputFiles and MP4Files folders. Copiare un file con estensione .mp4 nella cartella MP4Files.Copy an .mp4 file into the MP4Files folder. (In questo esempio, viene usato il file BigBuckBunny.mp4.)(In this example, the BigBuckBunny.mp4 file is used.)
  3. Usare NuGet per aggiungere riferimenti alle DLL correlate a Servizi multimediali.Use Nuget to add references to DLLs related to Media Services. Nel menu principale di Visual Studio scegliere STRUMENTI > Library Package Manager (Gestione pacchetti libreria) > Console di Gestione pacchetti.In Visual Studio Main Menu, select TOOLS > Library Package Manager > Package Manager Console. Nella finestra della console digitare Install-Package windowsazure.mediaservices e premere Invio.In the console window, type Install-Package windowsazure.mediaservices, and press Enter.
  4. Aggiungere gli altri riferimenti necessari per il progetto: System.Configuration, System.Runtime.Serialization e System.Web.Add other references that are required for this project: System.Configuration, System.Runtime.Serialization, and System.Web.
  5. Sostituire le istruzioni using aggiunte per impostazione predefinita al file Programs.cs con le seguenti:Replace using statements that were added to the Programs.cs file by default with the following ones:

     using System;
     using System.Configuration;
     using System.Globalization;
     using System.IO;
     using System.Net;
     using System.Runtime.Serialization.Json;
     using System.Text;
     using System.Threading;
     using System.Threading.Tasks;
     using System.Web;
     using System.Xml;
     using System.Linq;
     using Microsoft.WindowsAzure;
     using Microsoft.WindowsAzure.MediaServices.Client;
     using Microsoft.WindowsAzure.Storage;
     using Microsoft.WindowsAzure.Storage.Blob;
     using Microsoft.WindowsAzure.Storage.Auth;
    
  6. Aggiungere la sezione appSettings al file .config e aggiornare i valori in base ai valori di chiave e nome dell'account di Servizi multimediali e di archiviazione.Add the appSettings section to the .config file, and update the values based on your Media Services and Storage key and name values.

     <appSettings>
       <add key="MediaServicesAccountNameSource" value="Media-Services-Account-Name-Source"/>
       <add key="MediaServicesAccountKeySource" value="Media-Services-Account-Key-Source"/>
       <add key="MediaServicesStorageAccountNameSource" value="Media-Services-Storage-Account-Name-Source"/>
       <add key="MediaServicesStorageAccountKeySource" value="Media-Services-Storage-Account-Key-Source"/>
       <add key="MediaServicesAccountNameTarget" value="Media-Services-Account-Name-Target" />
       <add key="MediaServicesAccountKeyTarget" value=" Media-Services-Account-Key-Target" />
       <add key="MediaServicesStorageAccountNameTarget" value=" Media-Services-Storage-Account-Name-Target" />
       <add key="MediaServicesStorageAccountKeyTarget" value=" Media-Services-Storage-Account-Key-Target" />
     </appSettings>
    

Aggiungere un codice in grado di gestire la ridondanza per lo streaming on demandAdd code that handles redundancy for on-demand streaming

In questa sezione, si crea la possibilità di gestire la ridondanza.In this section, you create the ability to handle redundancy.

  1. Aggiungere alla classe Program i campi a livello di classe seguenti.Add the following class-level fields to the Program class.

     // Read values from the App.config file.
     private static readonly string MediaServicesAccountNameSource = ConfigurationManager.AppSettings["MediaServicesAccountNameSource"];
     private static readonly string MediaServicesAccountKeySource = ConfigurationManager.AppSettings["MediaServicesAccountKeySource"];
     private static readonly string StorageNameSource = ConfigurationManager.AppSettings["MediaServicesStorageAccountNameSource"];
     private static readonly string StorageKeySource = ConfigurationManager.AppSettings["MediaServicesStorageAccountKeySource"];
    
     private static readonly string MediaServicesAccountNameTarget = ConfigurationManager.AppSettings["MediaServicesAccountNameTarget"];
     private static readonly string MediaServicesAccountKeyTarget = ConfigurationManager.AppSettings["MediaServicesAccountKeyTarget"];
     private static readonly string StorageNameTarget = ConfigurationManager.AppSettings["MediaServicesStorageAccountNameTarget"];
     private static readonly string StorageKeyTarget = ConfigurationManager.AppSettings["MediaServicesStorageAccountKeyTarget"];
    
     // Base support files path.  Update this field to point to the base path  
     // for the local support files folder that you create. 
     private static readonly string SupportFiles = Path.GetFullPath(@"../..\SupportFiles");
    
     // Paths to support files (within the above base path). 
     private static readonly string SingleInputMp4Path = Path.GetFullPath(SupportFiles + @"\MP4Files\BigBuckBunny.mp4");
     private static readonly string OutputFilesFolder = Path.GetFullPath(SupportFiles + @"\OutputFiles");
    
     // Class-level field used to keep a reference to the service context.
     static private CloudMediaContext _contextSource = null;
     static private CloudMediaContext _contextTarget = null;
     static private MediaServicesCredentials _cachedCredentialsSource = null;
     static private MediaServicesCredentials _cachedCredentialsTarget = null;
    
  2. Sostituire la definizione predefinita del metodo Main con quella indicata di seguito.Replace the default Main method definition with the following one. Di seguito sono specificate le definizioni dei metodi chiamate da Main.Method definitions that are called from Main are defined below.

     static void Main(string[] args)
     {
         _cachedCredentialsSource = new MediaServicesCredentials(
                         MediaServicesAccountNameSource,
                         MediaServicesAccountKeySource);
    
         _cachedCredentialsTarget = new MediaServicesCredentials(
                         MediaServicesAccountNameTarget,
                         MediaServicesAccountKeyTarget);
    
         // Get server context.    
         _contextSource = new CloudMediaContext(_cachedCredentialsSource);
         _contextTarget = new CloudMediaContext(_cachedCredentialsTarget);
    
         IAsset assetSingleFile = CreateAssetAndUploadSingleFile(_contextSource,
                                     AssetCreationOptions.None,
                                     SingleInputMp4Path);
    
         IJob job = CreateEncodingJob(_contextSource, assetSingleFile);
    
         if (job.State != JobState.Error)
         {
             IAsset sourceOutputAsset = job.OutputMediaAssets[0];
             // Get the locator for Smooth Streaming
             var sourceOriginLocator = GetStreamingOriginLocator(_contextSource, sourceOutputAsset);
    
             Console.WriteLine("Locator Id: {0}", sourceOriginLocator.Id);
    
             // 1.Create a read-only SAS locator for the source asset to have read access to the container in the source Storage account (associated with the source Media Services account)
             var readSasLocator = GetSasReadLocator(_contextSource, sourceOutputAsset);
    
             // 2.Get the container name of the source asset from the read-only SAS locator created in the previous step
             string containerName = (new Uri(readSasLocator.Path)).Segments[1];
    
             // 3.Create a target empty asset in the target Media Services account
             var targetAsset = CreateTargetEmptyAsset(_contextTarget, containerName);
    
             // 4.Create a write SAS locator for the target empty asset to have write access to the container in the target Storage account (associated with the target Media Services account)
             ILocator writeSasLocator = CreateSasWriteLocator(_contextTarget, targetAsset);
    
             // Get asset container name.
             string targetContainerName = (new Uri(writeSasLocator.Path)).Segments[1];
    
             // 5.Copy the blobs in the source container (source asset) to the target container (target empty asset)
             CopyBlobsFromDifferentStorage(containerName, targetContainerName, StorageNameSource, StorageKeySource, StorageNameTarget, StorageKeyTarget);
    
             // 6.Use the CreateFileInfos Media Services REST API to automatically generate all the IAssetFile’s for the target asset. 
             //      This API call is not supported in the current Media Services SDK for .NET. 
             CreateFileInfosForAssetWithRest(_contextTarget, targetAsset, MediaServicesAccountNameTarget, MediaServicesAccountKeyTarget);
    
             // Check if the AssetFiles are now  associated with the asset.
             Console.WriteLine("Asset files assocated with the {0} asset:", targetAsset.Name);
             foreach (var af in targetAsset.AssetFiles)
             {
                 Console.WriteLine(af.Name);
             }
    
             // 7.Copy the Origin locator of the source asset to the target asset by using the same Id
             var replicatedLocatorPath = CreateOriginLocatorWithRest(_contextTarget,
                         MediaServicesAccountNameTarget, MediaServicesAccountKeyTarget,
                         sourceOriginLocator.Id, targetAsset.Id);
    
             // Create a full URL to the manifest file. Use this for playback
             // in streaming media clients. 
             string originalUrlForClientStreaming = sourceOriginLocator.Path + GetPrimaryFile(sourceOutputAsset).Name + "/manifest";
    
             Console.WriteLine("Original Locator Path: {0}\n", originalUrlForClientStreaming);
    
             string replicatedUrlForClientStreaming = replicatedLocatorPath + GetPrimaryFile(sourceOutputAsset).Name + "/manifest";
    
             Console.WriteLine("Replicated Locator Path: {0}", replicatedUrlForClientStreaming);
    
             readSasLocator.Delete();
             writeSasLocator.Delete();
     }
    
  3. Le definizioni seguenti dei metodi sono chiamati dal metodo Main.The following method definitions are called from Main.

    Nota

    È previsto un limite di 1.000.000 di criteri per i diversi criteri di Servizi multimediali (ad esempio per i criteri Locator o ContentKeyAuthorizationPolicy).There is a limit of 1,000,000 policies for different Media Services policies (for example, for Locator policy or ContentKeyAuthorizationPolicy). Se si usano sempre gli stessi giorni e le stesse autorizzazioni di accesso, è necessario usare lo stesso ID criterio.You should use the same policy ID if you are always using the same days and access permissions. Ad esempio, usare lo stesso ID per i criteri dei localizzatori che devono rimanere sul posto per molto tempo (criteri di non-caricamento).For example, use the same ID for policies for locators that are intended to remain in place for a long time (non-upload policies). Per altre informazioni, vedere questo argomento.For more information, see this topic.

     public static IAsset CreateAssetAndUploadSingleFile(CloudMediaContext context,
                                                     AssetCreationOptions assetCreationOptions,
                                                     string singleFilePath)
     {
         var assetName = "UploadSingleFile_" + DateTime.UtcNow.ToString();
    
         var asset = context.Assets.Create(assetName, assetCreationOptions);
    
         Console.WriteLine("Asset name: " + asset.Name);
    
         var fileName = Path.GetFileName(singleFilePath);
    
         var assetFile = asset.AssetFiles.Create(fileName);
    
         Console.WriteLine("Created assetFile {0}", assetFile.Name);
    
         Console.WriteLine("Upload {0}", assetFile.Name);
    
         assetFile.Upload(singleFilePath);
         Console.WriteLine("Done uploading of {0}", assetFile.Name);
    
         return asset;
     }
    
     public static IJob CreateEncodingJob(CloudMediaContext context, IAsset asset)
     {
         // Declare a new job.
         IJob job = context.Jobs.Create("My encoding job");
    
         // Get a media processor reference, and pass to it the name of the 
         // processor to use for the specific task.
         IMediaProcessor processor = GetLatestMediaProcessorByName(context,
                                                 "Media Encoder Standard");
    
         // Create a task with the encoding details, using a string preset.
         // In this case "Adaptive Streaming" preset is used.
         ITask task = job.Tasks.AddNew("My encoding task",
             processor,
             "Adaptive Streaming",
             TaskOptions.ProtectedConfiguration);
    
         // Specify the input asset to be encoded.
         task.InputAssets.Add(asset);
    
         // Add an output asset to contain the results of the job. 
         // This output is specified as AssetCreationOptions.None, which 
         // means the output asset is in the clear (unencrypted). 
         var outputAssetName = "OutputAsset_" + Guid.NewGuid();
         task.OutputAssets.AddNew(outputAssetName,
             AssetCreationOptions.None);
    
         // Use the following event handler to check job progress.  
         job.StateChanged += new
                 EventHandler<JobStateChangedEventArgs>(StateChanged);
    
         // Launch the job.
         job.Submit();
    
         // Optionally log job details. This displays basic job details
         // to the console and saves them to a JobDetails-{JobId}.txt file 
         // in your output folder.
         LogJobDetails(context, job.Id);
    
         // Check job execution and wait for job to finish. 
         Task progressJobTask = job.GetExecutionProgressTask(CancellationToken.None);
         progressJobTask.Wait();
    
         // Get an updated job reference.
         job = GetJob(context, job.Id);
    
         // Since we the output asset contains a set of Smooth Streaming files,
         // set the .ism file to be the primary file
         if (job.State != JobState.Error)
             SetPrimaryFile(job.OutputMediaAssets[0]);
    
         return job;
     }
    
     public static ILocator GetStreamingOriginLocator(CloudMediaContext context, IAsset assetToStream)
     {
         // Get a reference to the streaming manifest file from the  
         // collection of files in the asset. 
         IAssetFile manifestFile = GetPrimaryFile(assetToStream);
    
         // Create a 30-day readonly access policy. 
         // You cannot create a streaming locator using an AccessPolicy that includes write or delete permissions.            
    
         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,
             assetToStream,
             policy,
             DateTime.UtcNow.AddMinutes(-5));
    
         // Return the locator. 
         return originLocator;
     }
    
     public static ILocator GetSasReadLocator(CloudMediaContext context, IAsset asset)
     {
         IAccessPolicy accessPolicy = context.AccessPolicies.Create("File Download Policy",
             TimeSpan.FromDays(30), AccessPermissions.Read);
    
         ILocator sasLocator = context.Locators.CreateLocator(LocatorType.Sas,
             asset, accessPolicy);
    
         return sasLocator;
     }
    
     public static ILocator CreateSasWriteLocator(CloudMediaContext context, IAsset asset)
     {
    
         IAccessPolicy writePolicy = context.AccessPolicies.Create("Write Policy",
             TimeSpan.FromDays(30), AccessPermissions.Write);
    
         ILocator sasLocator = context.Locators.CreateLocator(LocatorType.Sas,
             asset, writePolicy);
    
         return sasLocator;
     }
    
     public static IAsset CreateTargetEmptyAsset(CloudMediaContext context, string containerName)
     {
         // Create a new asset.
         IAsset assetToBeProcessed = context.Assets.Create(containerName,
             AssetCreationOptions.None);
    
         return assetToBeProcessed;
     }
    
     public static void CreateFileInfosForAssetWithRest(CloudMediaContext context, IAsset asset, string mediaServicesAccountNameTarget,
         string mediaServicesAccountKeyTarget)
     {
         string apiServer = "";
         string scope = "";
         string acsBaseAddress = "";
    
         string acsToken = GetAcsBearerToken(mediaServicesAccountNameTarget,
                                 mediaServicesAccountKeyTarget, scope, acsBaseAddress);
    
         if (!string.IsNullOrEmpty(acsToken))
         {
             CreateFileInfos(apiServer, acsToken, asset.Id);
         }
     }
    
     public static string CreateOriginLocatorWithRest(CloudMediaContext context, string mediaServicesAccountNameTarget,
         string mediaServicesAccountKeyTarget, string locatorIdToReplicate, string targetAssetId)
     {
         //Make sure we are not asking for a duplicate:
         var locator = context.Locators.Where(l => l.Id == locatorIdToReplicate).FirstOrDefault();
         if (locator != null)
             return "";
    
         string locatorNewPath = "";
         string apiServer = "";
         string scope = "";
         string acsBaseAddress = "";
    
         string acsToken = GetAcsBearerToken(mediaServicesAccountNameTarget,
                                 mediaServicesAccountKeyTarget, scope, acsBaseAddress);
    
         if (!string.IsNullOrEmpty(acsToken))
         {
             var asset = context.Assets.Where(a => a.Id == targetAssetId).FirstOrDefault();
    
             // You cannot create a streaming locator using an AccessPolicy that includes write or delete permissions.            
             var accessPolicy = context.AccessPolicies.Create("RestTest", TimeSpan.FromDays(100),
                                                                 AccessPermissions.Read);
             if (asset != null)
             {
                 string redirectedServiceUri = null;
    
                 var xmlResponse = CreateLocator(apiServer, out redirectedServiceUri, acsToken,
                                                             asset.Id, accessPolicy.Id,
                                                             (int)LocatorType.OnDemandOrigin,
                                                             DateTime.UtcNow.AddMinutes(-10), locatorIdToReplicate);
    
                 Console.WriteLine("Redirected to: " + redirectedServiceUri);
                 if (xmlResponse != null)
                 {
                     Console.WriteLine(String.Format("Locator Id: {0}",
                                                     xmlResponse.GetElementsByTagName("Id")[0].InnerText));
                     Console.WriteLine(String.Format("Locator Path: {0}",
                             xmlResponse.GetElementsByTagName("Path")[0].InnerText));
    
                     locatorNewPath = xmlResponse.GetElementsByTagName("Path")[0].InnerText;
                 }
             }
         }
    
         return locatorNewPath;
     }
    
     public static void SetPrimaryFile(IAsset asset)
     {
    
         var ismAssetFiles = asset.AssetFiles.ToList().
                     Where(f => f.Name.EndsWith(".ism", StringComparison.OrdinalIgnoreCase))
                     .ToArray();
    
         if (ismAssetFiles.Count() != 1)
             throw new ArgumentException("The asset should have only one, .ism file");
    
         ismAssetFiles.First().IsPrimary = true;
         ismAssetFiles.First().Update();
     }
    
     public static IAssetFile GetPrimaryFile(IAsset asset)
     {
         var theManifest =
                 from f in asset.AssetFiles
                 where f.Name.EndsWith(".ism")
                 select f;
    
         // Cast the reference to a true IAssetFile type. 
         IAssetFile manifestFile = theManifest.First();
    
         return manifestFile;
     }
    
     public static IAsset RefreshAsset(CloudMediaContext context, IAsset asset)
     {
         asset = context.Assets.Where(a => a.Id == asset.Id).FirstOrDefault();
         return asset;
     }
    
     public static void CopyBlobsFromDifferentStorage(string sourceContainerName, string targetContainerName,
                                         string srcAccountName, string srcAccountKey,
                                         string destAccountName, string destAccountKey)
     {
         var srcAccount = new CloudStorageAccount(new StorageCredentials(srcAccountName, srcAccountKey), true);
         var destAccount = new CloudStorageAccount(new StorageCredentials(destAccountName, destAccountKey), true);
    
         var cloudBlobClient = srcAccount.CreateCloudBlobClient();
         var targetBlobClient = destAccount.CreateCloudBlobClient();
    
         var sourceContainer = cloudBlobClient.GetContainerReference(sourceContainerName);
         var targetContainer = targetBlobClient.GetContainerReference(targetContainerName);
         targetContainer.CreateIfNotExists();
    
         string blobToken = sourceContainer.GetSharedAccessSignature(new SharedAccessBlobPolicy()
         {
             // Specify the expiration time for the signature.
             SharedAccessExpiryTime = DateTime.Now.AddDays(1),
             // Specify the permissions granted by the signature.
             Permissions = SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.Read
         });
    
         foreach (var sourceBlob in sourceContainer.ListBlobs())
         {
             string fileName = (sourceBlob as ICloudBlob).Name;
             var sourceCloudBlob = sourceContainer.GetBlockBlobReference(fileName);
             sourceCloudBlob.FetchAttributes();
    
             if (sourceCloudBlob.Properties.Length > 0)
             {
                 // In Azure Media Services, the files are stored as block blobs. 
                 // Page blobs are not supported by Azure Media Services.  
                 var destinationBlob = targetContainer.GetBlockBlobReference(fileName);
                 destinationBlob.StartCopyFromBlob(new Uri(sourceBlob.Uri.AbsoluteUri + blobToken));
    
                 while (true)
                 {
                     // The StartCopyFromBlob is an async operation, 
                     // so we want to check if the copy operation is completed before proceeding. 
                     // To do that, we call FetchAttributes on the blob and check the CopyStatus. 
                     destinationBlob.FetchAttributes();
                     if (destinationBlob.CopyState.Status != CopyStatus.Pending)
                     {
                         break;
                     }
                     //It's still not completed. So wait for some time.
                     System.Threading.Thread.Sleep(1000);
                 }
             }
    
             Console.WriteLine(fileName);
         }
    
         Console.WriteLine("Done copying.");
     }
    
     private static IMediaProcessor GetLatestMediaProcessorByName(CloudMediaContext context, string mediaProcessorName)
     {
    
         var processor = context.MediaProcessors.Where(p => p.Name == mediaProcessorName).
             ToList().OrderBy(p => new Version(p.Version)).LastOrDefault();
    
         if (processor == null)
             throw new ArgumentException(string.Format("Unknown media processor", mediaProcessorName));
    
         return processor;
     }
    
     // This method is a handler for events that track job progress.   
     private static void StateChanged(object sender, JobStateChangedEventArgs e)
     {
         Console.WriteLine("Job state changed event:");
         Console.WriteLine("  Previous state: " + e.PreviousState);
         Console.WriteLine("  Current state: " + e.CurrentState);
    
         switch (e.CurrentState)
         {
             case JobState.Finished:
                 Console.WriteLine();
                 Console.WriteLine("********************");
                 Console.WriteLine("Job is finished.");
                 Console.WriteLine("Please wait while local tasks or downloads complete...");
                 Console.WriteLine("********************");
                 Console.WriteLine();
                 Console.WriteLine();
                 break;
             case JobState.Canceling:
             case JobState.Queued:
             case JobState.Scheduled:
             case JobState.Processing:
                 Console.WriteLine("Please wait...\n");
                 break;
             case JobState.Canceled:
             case JobState.Error:
                 // Cast sender as a job.
                 IJob job = (IJob)sender;
                 // Display or log error details as needed.
                 LogJobStop(null, job.Id);
                 break;
             default:
                 break;
         }
     }
    
     private static void LogJobStop(CloudMediaContext context, string jobId)
     {
         StringBuilder builder = new StringBuilder();
         IJob job = GetJob(context, jobId);
    
         builder.AppendLine("\nThe job stopped due to cancellation or an error.");
         builder.AppendLine("***************************");
         builder.AppendLine("Job ID: " + job.Id);
         builder.AppendLine("Job Name: " + job.Name);
         builder.AppendLine("Job State: " + job.State.ToString());
         builder.AppendLine("Job started (server UTC time): " + job.StartTime.ToString());
         // Log job errors if they exist.  
         if (job.State == JobState.Error)
         {
             builder.Append("Error Details: \n");
             foreach (ITask task in job.Tasks)
             {
                 foreach (ErrorDetail detail in task.ErrorDetails)
                 {
                     builder.AppendLine("  Task Id: " + task.Id);
                     builder.AppendLine("    Error Code: " + detail.Code);
                     builder.AppendLine("    Error Message: " + detail.Message + "\n");
                 }
             }
         }
         builder.AppendLine("***************************\n");
         // Write the output to a local file and to the console. The template 
         // for an error output file is:  JobStop-{JobId}.txt
         string outputFile = OutputFilesFolder + @"\JobStop-" + JobIdAsFileName(job.Id) + ".txt";
         WriteToFile(outputFile, builder.ToString());
         Console.Write(builder.ToString());
     }
    
     private static void LogJobDetails(CloudMediaContext context, string jobId)
     {
         StringBuilder builder = new StringBuilder();
         IJob job = GetJob(context, jobId);
    
         builder.AppendLine("\nJob ID: " + job.Id);
         builder.AppendLine("Job Name: " + job.Name);
         builder.AppendLine("Job submitted (client UTC time): " + DateTime.UtcNow.ToString());
    
         // Write the output to a local file and to the console. The template 
         // for an error output file is:  JobDetails-{JobId}.txt
         string outputFile = OutputFilesFolder + @"\JobDetails-" + JobIdAsFileName(job.Id) + ".txt";
         WriteToFile(outputFile, builder.ToString());
         Console.Write(builder.ToString());
     }
    
     // Replace ":" with "_" in Job id values so they can 
     // be used as log file names.  
     private static string JobIdAsFileName(string jobID)
     {
         return jobID.Replace(":", "_");
     }
    
     // Write method output to the output files folder.
     private static void WriteToFile(string outFilePath, string fileContent)
     {
         StreamWriter sr = File.CreateText(outFilePath);
         sr.WriteLine(fileContent);
         sr.Close();
     }
    
     private static IJob GetJob(CloudMediaContext context, string jobId)
     {
         // Use a Linq select query to get an updated 
         // reference by Id. 
         var jobInstance =
             from j in context.Jobs
             where j.Id == jobId
             select j;
    
         // Return the job reference as an Ijob. 
         IJob job = jobInstance.FirstOrDefault();
    
         return job;
     }
    
     private static IAsset GetAsset(CloudMediaContext context, string assetId)
     {
         // Use a LINQ Select query to get an asset.
         var assetInstance =
             from a in context.Assets
             where a.Id == assetId
             select a;
    
         // Reference the asset as an IAsset.
         IAsset asset = assetInstance.FirstOrDefault();
    
         return asset;
     }
    
     public static void DeleteAllAssets(CloudMediaContext context)
     {
         // Loop through and delete all assets.
         foreach (IAsset asset in context.Assets)
         {
             DeleteLocatorsForAsset(context, asset);
    
             asset.Delete();
         }
     }
    
     public static void DeleteLocatorsForAsset(CloudMediaContext context, IAsset asset)
     {
         string assetId = asset.Id;
         var locators = from a in context.Locators
                         where a.AssetId == assetId
                         select a;
         foreach (var locator in locators)
         {
             Console.WriteLine("Deleting locator {0} for asset {1}", locator.Path, assetId);
    
             locator.Delete();
         }
     }
    
     public static void DeleteAccessPolicy(CloudMediaContext context, string existingPolicyId)
     {
         // To delete a specific access policy, get a reference to the policy.  
         // based on the policy Id passed to the method.
         var policyInstance =
                 from p in context.AccessPolicies
                 where p.Id == existingPolicyId
                 select p;
    
         IAccessPolicy policy = policyInstance.FirstOrDefault();
    
         policy.Delete();
    
     }
    
     //////////////////////////////////////////////////////
     /// The following methods use REST calls.
     //////////////////////////////////////////////////////
    
     public static string GetAcsBearerToken(string clientId, string clientSecret, string scope, string accessControlServiceUri)
     {
         if (string.IsNullOrEmpty(clientId))
             throw new ArgumentNullException("clientId");
    
         if (string.IsNullOrEmpty(clientSecret))
             throw new ArgumentNullException("clientSecret");
    
         if (string.IsNullOrEmpty(scope))
         {
             scope = "urn:WindowsAzureMediaServices";
         }
         else if (!scope.ToLower().StartsWith("urn:"))
         {
             scope = "urn:" + scope;
         }
    
         if (string.IsNullOrEmpty(accessControlServiceUri))
         {
             accessControlServiceUri = "https://wamsprodglobal001acs.accesscontrol.windows.net/v2/OAuth2-13";
         }
         else if (!accessControlServiceUri.ToLower().EndsWith("/v2/oauth2-13"))
         {
             accessControlServiceUri = accessControlServiceUri.TrimEnd('/') + "/v2/OAuth2-13";
         }
    
         HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(accessControlServiceUri);
         request.Method = "POST";
         request.ContentType = "application/x-www-form-urlencoded";
         request.KeepAlive = true;
         string acsBearerToken = null;
    
         var requestBytes = Encoding.ASCII.GetBytes("grant_type=client_credentials&client_id=" +
             clientId + "&client_secret=" + HttpUtility.UrlEncode(clientSecret) +
             "&scope=" + HttpUtility.UrlEncode(scope));
         request.ContentLength = requestBytes.Length;
    
         var requestStream = request.GetRequestStream();
         requestStream.Write(requestBytes, 0, requestBytes.Length);
         requestStream.Close();
    
         var response = (HttpWebResponse)request.GetResponse();
    
         if (response.StatusCode == HttpStatusCode.OK)
         {
             using (Stream responseStream = response.GetResponseStream())
             {
                 using (StreamReader stream = new StreamReader(responseStream))
                 {
                     string responseString = stream.ReadToEnd();
                     var reader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(responseString),
                         new XmlDictionaryReaderQuotas());
    
                     while (reader.Read())
                     {
                         if ((reader.Name == "access_token") && (reader.NodeType == XmlNodeType.Element))
                         {
                             if (reader.Read())
                             {
                                 acsBearerToken = reader.Value;
                                 break;
                             }
                         }
                     }
                 }
             }
         }
    
         return acsBearerToken;
     }
    
     public static XmlDocument CreateLocator(string mediaServicesApiServerUri,
                                             out string redirectedMediaServicesApiServerUri,
                                             string acsBearerToken, string assetId,
                                             string accessPolicyId, int locatorType,
                                             DateTime startTime, string locatorIdToReplicate = null,
                                             bool autoRedirect = true)
     {
         if (string.IsNullOrEmpty(mediaServicesApiServerUri))
         {
             mediaServicesApiServerUri = "https://media.windows.net/api/";
         }
         if (!mediaServicesApiServerUri.EndsWith("/"))
             mediaServicesApiServerUri = mediaServicesApiServerUri + "/";
    
         if (string.IsNullOrEmpty(acsBearerToken)) throw new ArgumentNullException("acsBearerToken");
         if (string.IsNullOrEmpty(assetId)) throw new ArgumentNullException("assetId");
         if (string.IsNullOrEmpty(accessPolicyId)) throw new ArgumentNullException("accessPolicyId");
    
         redirectedMediaServicesApiServerUri = null;
         XmlDocument xmlResponse = null;
    
         StringBuilder sb = new StringBuilder();
         sb.Append("{ \"AssetId\" : \"" + assetId + "\"");
         sb.Append(", \"AccessPolicyId\" : \"" + accessPolicyId + "\"");
         sb.Append(", \"Type\" : \"" + locatorType + "\"");
         if (startTime != DateTime.MinValue)
             sb.Append(", \"StartTime\" : \"" + startTime.ToString("G", CultureInfo.CreateSpecificCulture("en-us")) + "\"");
         if (!string.IsNullOrEmpty(locatorIdToReplicate))
             sb.Append(", \"Id\" : \"" + locatorIdToReplicate + "\"");
         sb.Append("}");
    
         string requestbody = sb.ToString();
    
         try
         {
             var request = GenerateRequest("POST", mediaServicesApiServerUri, "Locators",
                 null, acsBearerToken, requestbody);
             var response = (HttpWebResponse)request.GetResponse();
    
             switch (response.StatusCode)
             {
                 case HttpStatusCode.MovedPermanently:
                     //Recurse once with the mediaServicesApiServerUri redirect Location:
                     if (autoRedirect)
                     {
                         redirectedMediaServicesApiServerUri = response.Headers["Location"];
                         string secondRedirection = null;
                         xmlResponse = CreateLocator(redirectedMediaServicesApiServerUri,
                                                     out secondRedirection, acsBearerToken,
                                                     assetId, accessPolicyId, locatorType,
                                                     startTime, locatorIdToReplicate, false);
                     }
                     else
                     {
                         Console.WriteLine("Redirection to {0} failed.",
                             mediaServicesApiServerUri);
                         return null;
                     }
                     break;
                 case HttpStatusCode.Created:
                     using (Stream responseStream = response.GetResponseStream())
                     {
                         using (StreamReader stream = new StreamReader(responseStream))
                         {
                             string responseString = stream.ReadToEnd();
                             var reader = JsonReaderWriterFactory.
                                 CreateJsonReader(Encoding.UTF8.GetBytes(responseString),
                                     new XmlDictionaryReaderQuotas());
    
                             xmlResponse = new XmlDocument();
                             reader.Read();
                             xmlResponse.LoadXml(reader.ReadInnerXml());
                         }
                     }
                     break;
    
                 default:
                     Console.WriteLine(response.StatusDescription);
                     break;
             }
         }
         catch (WebException ex)
         {
             Console.WriteLine(ex.Message);
         }
    
         return xmlResponse;
     }
    
     public static void CreateFileInfos(string mediaServicesApiServerUri,
                                 string acsBearerToken,
                                 string assetId
                                 )
     {
         if (String.IsNullOrEmpty(mediaServicesApiServerUri))
         {
             mediaServicesApiServerUri = "https://media.windows.net/api/";
         }
         if (!mediaServicesApiServerUri.EndsWith("/"))
             mediaServicesApiServerUri = mediaServicesApiServerUri + "/";
    
         if (String.IsNullOrEmpty(acsBearerToken)) throw new ArgumentNullException("acsBearerToken");
         if (String.IsNullOrEmpty(assetId)) throw new ArgumentNullException("assetId");
    
        string id = assetId.Replace(":", "%");

        UriBuilder builder = new UriBuilder(mediaServicesApiServerUri);
        builder.Path = Path.Combine(builder.Path, "CreateFileInfos");
        builder.Query = String.Format(CultureInfo.InvariantCulture, "assetid='{0}'", assetId);

        try
        {
            var request = GenerateRequest("GET", mediaServicesApiServerUri, "CreateFileInfos",
                String.Format(CultureInfo.InvariantCulture, "assetid='{0}'", assetId), acsBearerToken, null);

            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
            {
                if (response.StatusCode == HttpStatusCode.MovedPermanently)
                {
                    string redirectedMediaServicesApiUrl = response.Headers["Location"];

                    CreateFileInfos(redirectedMediaServicesApiUrl, acsBearerToken, assetId);
                }
                else if ((response.StatusCode != HttpStatusCode.OK) &&
                    (response.StatusCode != HttpStatusCode.Accepted) &&
                    (response.StatusCode != HttpStatusCode.Created) &&
                    (response.StatusCode != HttpStatusCode.NoContent))
                {
                    // TODO: Throw a more specific exception.
                    throw new Exception("Invalid response received ");
                }
            }
        }
        catch (WebException ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    private static HttpWebRequest GenerateRequest(string verb,
                                                    string mediaServicesApiServerUri,
                                                    string resourcePath, string query,
                                                    string acsBearerToken, string requestbody)
    {
        var uriBuilder = new UriBuilder(mediaServicesApiServerUri);
        uriBuilder.Path += resourcePath;
        if (query != null)
        {
            uriBuilder.Query = query;
        }
        HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uriBuilder.Uri);
        request.AllowAutoRedirect = false; //We manage our own redirects.
        request.Method = verb;

        if (resourcePath == "$metadata")
            request.MediaType = "application/xml";
        else
        {
            request.ContentType = "application/json;odata=verbose";
            request.Accept = "application/json;odata=verbose";
        }

        request.Headers.Add("DataServiceVersion", "3.0");
        request.Headers.Add("MaxDataServiceVersion", "3.0");
        request.Headers.Add("x-ms-version", "2.1");
        request.Headers.Add(HttpRequestHeader.Authorization, "Bearer " + acsBearerToken);

        if (requestbody != null)
        {
            var requestBytes = Encoding.ASCII.GetBytes(requestbody);
            request.ContentLength = requestBytes.Length;

            var requestStream = request.GetRequestStream();
            requestStream.Write(requestBytes, 0, requestBytes.Length);
            requestStream.Close();
        }
        else
        {
            request.ContentLength = 0;
        }
        return request;
    }

Passaggi successiviNext steps

È ora possibile usare uno strumento di gestione del traffico per instradare le richieste tra i due data center e quindi il failover in caso di guasti.You can now use a traffic manager to route requests between the two datacenters, and thus fail over in case of any outages.

Percorsi di apprendimento di Media ServicesMedia Services learning paths

Altre informazioni sui percorsi di apprendimento di Servizi multimediali di Azure:Read about the Azure Media Services learning paths:

Fornire commenti e suggerimentiProvide feedback

Usare il forum di suggerimenti degli utenti per fornire commenti e suggerimenti su come migliorare Servizi multimediali di Azure.Use the User Voice forum to provide feedback and make suggestions on how to improve Azure Media Services. È anche possibile passare direttamente a una delle categorie seguenti:You also can go directly to one of the following categories: