내보내기(0) 인쇄
모두 확장

장애 조치(failover) 스트리밍 시나리오 구현

업데이트 날짜: 2014년 8월

이 연습에서는 주문형 스트리밍의 중복성을 처리하기 위해 특정 자산에서 다른 자산으로 콘텐츠(blob)를 복사하는 방법을 보여 줍니다. 이 시나리오는 두 데이터 센터 중 한 곳에서 정전이 발생한 경우 두 곳 간에 장애 조치(failover)를 수행하도록 CDN을 설정하려는 고객에게 유용합니다.

이 연습에서는 Microsoft Azure 미디어 서비스 SDK, Microsoft Azure 미디어 서비스 REST API 및 Azure Storage SDK를 사용하여 다음 작업을 설명합니다.

  1. "데이터 센터 A"에서 미디어 서비스 계정을 설정합니다.

  2. 중 2층 파일을 원본 자산에 업로드합니다.

  3. 다중 비트 전송률 MP4 파일로 자산을 인코딩합니다.

  4. 원본 자산과 연관된 Storage 계정의 컨테이너에 대해 읽기 권한을 갖도록 해당 원본 자산에 대해 읽기 전용 SAS 로케이터를 만듭니다.

  5. 이전 단계에서 만든 읽기 전용 SAS 로케이터에서 원본 자산의 컨테이너 이름을 가져옵니다. 저장소 계정 간에 blob을 복사하려면 이 정보가 필요합니다(항목의 뒷부분에 설명).

  6. 인코딩 작업으로 만든 자산에 대해 원본 로케이터를 만듭니다.



    그런 다음 다음을 수행하여 장애 조치(failover)를 처리합니다.



  7. "데이터 센터 B"에서 미디어 서비스 계정을 설정합니다.

  8. 대상 미디어 서비스 계정에 빈 대상 자산을 만듭니다.

  9. 대상 자산과 연관된 Storage 계정의 컨테이너에 대해 쓰기 권한을 갖도록 해당 대상 자산에 대해 쓰기 SAS 로케이터를 만듭니다.

  10. Azure Storage SDK를 사용하여 "데이터 센터 A"의 원본 저장소 계정과 "데이터 센터 B"의 대상 저장소 계정 간에 blob(자산 파일)을 복사합니다(이러한 저장소 계정은 원하는 자산과 연관되어 있음).

  11. 복사한 blob(자산 파일)을 대상 자산이 있는 대상 blob 컨테이너에 연결합니다.

  12. "데이터 센터 B"의 자산에 대한 원본 로케이터를 만들고 "데이터 센터 A"의 자산에 생성된 로케이터 Id를 지정합니다.

    이렇게 하면 URL의 상태 경로가 같은 스트리밍 URL이 표시됩니다(기본 URL만 다름).

  13. 그런 다음 정전 문제를 처리할 수 있도록 이러한 원본 로케이터 위에 CDN을 만듭니다.

고려 사항은 다음과 같습니다.

  • 현재 버전의 미디어 서비스 SDK에서는 지정된 로케이터 ID를 사용하는 로케이터를 만들 수 없습니다. 이 작업을 수행하기 위해 미디어 서비스 REST API를 사용합니다.

  • 현재 버전의 미디어 서비스 SDK에서는 자산 파일과 자산을 연결하는 IAssetFile 정보를 프로그래밍 방식으로 생성할 수 없습니다. 이 작업을 수행하기 위해 CreateFileInfos 미디어 서비스 REST API를 사용합니다.

  • 미디어 서비스 계정에서 암호화 키가 모두 다르므로 저장소 암호화된 자산(AssetCreationOptions.StorageEncrypted)은 복제할 수 없습니다.

  • 동적 패키징을 이용하려면 먼저 하나 이상의 주문형 스트리밍 예약 단위를 가져와야 합니다. 자세한 내용은 동적 패키징을 참조하십시오.

Tip
장애 조치(failover) 스트리밍 시나리오를 수동으로 구현하는 다른 방법으로 미디어 서비스 복제기 도구 사용을 고려합니다. 이 도구를 사용하여 두 미디어 서비스 계정 간에 자산을 복제할 수 있습니다.

  • 신규 또는 기존 Azure 구독에 두 개의 미디어 서비스 계정이 있어야 합니다. 미디어 서비스 계정을 만드는 방법을 참조하십시오.

  • 운영 체제: Windows 7, Windows 2008 R2 또는 Windows 8

  • .NET Framework 4.5 또는 .NET Framework 4

  • Visual Studio 2012 또는 Visual Studio 2010 SP1(Professional, Premium, Ultimate 또는 Express)

이 섹션에서는 C# 콘솔 응용 프로그램 프로젝트를 만들고 설정합니다.

  1. Visual Studio 2012 또는 Visual Studio 2010 SP1을 사용하여 C# 콘솔 응용 프로그램 프로젝트가 포함된 새 솔루션을 만듭니다. 이름HandleRedundancyForOnDemandStreaming을 입력하고 확인을 클릭합니다.

  2. HandleRedundancyForOnDemandStreaming.csproj 프로젝트 파일과 동일한 수준에서 SupportFiles 폴더를 만듭니다. SupportFiles 폴더에 OutputFilesMP4Files 폴더를 만듭니다. MP4Files 폴더에 .mp4 파일을 복사합니다(이 예에서는 BigBuckBunny.mp4 파일이 사용됨).

  3. Nuget을 사용하여 미디어 서비스 관련 DLL에 대한 참조를 추가합니다. Visual Studio 주 메뉴에서 도구 -> 라이브러리 패키지 관리자 -> 패키지 관리자 콘솔을 선택합니다. 콘솔 창에서 Install-Package windowsazure.mediaservices를 입력하고 Enter 키를 누릅니다.

  4. 이 프로젝트에 필요한 System.Configuration, System.Runtime.SerializationSystem.Web의 세 가지 보호 수준을 사용할 수 있습니다.

  5. 기본적으로 Programs.cs 파일에 추가된 using 문을 다음으로 바꿉니다.

    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. appSettings 섹션을 .config 파일에 추가하고 미디어 서비스 기반 값과 저장소 키/이름 값을 업데이트합니다.

    <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>
    
    

  1. 다음과 같은 클래스 수준의 필드를 Program 클래스에 추가합니다.

    // 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. 기본 Main 메서드 정의를 다음으로 바꿉니다.

    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. Main에서 호출된 메서드 정의는 아래에 정의되어 있습니다.

    public static IAsset CreateAssetAndUploadSingleFile(CloudMediaContext context,
                                                    AssetCreationOptions assetCreationOptions,
                                                    string singleFilePath)
    {
        // For the AssetCreationOptions you can specify 
        // encryption options.
        //      None:  no encryption. By default, storage encryption is used. If you want to 
        //        create an unencrypted asset, you must set this option.
        //      StorageEncrypted:  storage encryption. Encrypts a clear input file 
        //        before it is uploaded to Azure storage. This is the default if not specified
        //      CommonEncryptionProtected:  for Common Encryption Protected (CENC) files. An 
        //        example is a set of files that are already PlayReady encrypted. 
    
        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,
                                                "Windows Azure Media Encoder");
    
        // Create a task with the encoding details, using a string preset.
        // In this case "H264 Adaptive Bitrate MP4 Set 720p" preset is used.
        ITask task = job.Tasks.AddNew("My encoding task",
            processor,
            "H264 Adaptive Bitrate MP4 Set 720p",
            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;
    }
    
    // Create a locator URL to a streaming media asset 
    // on an origin server.
    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. 
        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();
    
            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)
            {
                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)
    {
        // The possible strings that can be passed into the 
        // method for the mediaProcessor parameter:
        // Windows Azure Media Encoder
        // Windows Azure Media Packager
        // Windows Azure Media Encryptor
        //   Storage Decryption
    
        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;
    }
    
    
    

이제 트래픽 관리자를 사용하여 두 데이터 센터 간에 요청을 라우팅할 수 있으므로 정전이 발생할 경우 장애 조치(failover)를 수행할 수 있습니다.

표시:
© 2014 Microsoft