Dynamic Packaging によるエンコードなしでのマルチデバイス対応するためのサンプルコード C#版

Smooth Streaming, HTTP Live Streaming, Progressive Download。それぞれのストリーミング方式に合わせて、ファイルもそれぞれ用意して、そのためにパッケージングをする必要があります。つまりバッチ処理の実行です。これは、大変時間のかかるものです。とはいえ、ビデオ/オーディオコーデックという観点で、マルチデバイスに流そうとすると:

  • ビデオコーデック: H.264
  • オーディオコーデック: AAC

に落ち着いている現状もあります。

Windows Azure Media Servicesでは、現在提供しているOrigin Server上のDynamic Packagingにて、それらにオンデマンド、つまりクライアント側から呼び出された際に作成してくれる機能があります。

出来ない事:

  • 複数ビットレートのファイルを作成する。これは必要でしたらWindows Azure Media Encoderにて行います。
  • DRMが出来ません

ファイルフォーマット

入力

  • Mp4 (単一/マルチビットレート)
  • Smooth Streaming

出力

  • Smooth Streaming
  • HLS v4

手順

以下の手順を踏みます。

  1. 動画ファイルのアップロード
  2. .ismファイルを作成してそのアップロード
  3. Locatorを作成する

     

逆に言えば、これだけです。通常はエンコード/トランスコード/パッケージング処理をするのですが、その時間/ファイルが不要になります。

以下、C#でのサンプルコードです。
通常はやらないといけないのですが、これによって不要になる部分はコメントアウトをしています。

多くの説明はここではしませんが、試してみてください。

------------------------------------------------------------
using System;
using System.Configuration;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
using System.Diagnostics;
using Microsoft.WindowsAzure.MediaServices.Client;
namespace StandardWAMS
{
    class Program
								
    {
        //!!! CloudMediaContext はスレッドセーフではないので注意すること
					
        private static CloudMediaContext context = null;
        private const int MediaProcessCheckInterval = 10000;
        static void Main(string[] args)
        {
            // 処理時間の計測
					
            var totalSw = new Stopwatch();
            totalSw.Start();
            var sw = new Stopwatch();
            /// ********* 1. Windows Azure Media Services 接続 ******
						
            Console.WriteLine("******** 1. Windows Azure Media Services 接続 ******");
            context = new CloudMediaContext(
                    ConfigurationManager.AppSettings["accountName"],
                    ConfigurationManager.AppSettings["accountKey"]
                    );
            context.ParallelTransferThreadCount = 100;
            context.NumberOfConcurrentTransfers = 100;
            /// ******** 2. Ingest (アップロードと登録) **************
						
            Console.WriteLine("******** 2. Ingest (アップロードと登録) **************");
            var targetFile = new FileInfo(ConfigurationManager.AppSettings["uploadfile"]);
            // 2.1. 空のAssetを作成
						
            var ingestAsset = context.Assets.Create(
                targetFile.Name,
                AssetCreationOptions.None); // 暗号化する際には必ずここでセット
					
            // 2.2. 空のAssetFileを作成
						
            var ingestAssetFile = ingestAsset.AssetFiles.Create(
                    targetFile.Name);
            ingestAssetFile.IsPrimary = true;
            ingestAssetFile.Update();
            // ***** [同期実行] *****
						
            // 2.3. Upload を実行します
					
            //ingestAssetFile.Upload(targetFile.FullName);
						
            // ***** 同期実行終わり *****
						
            // ***** [非同期実行] *****
						
            // 2.3. AccessPolicyを作成します。ここでは3時間のみ書き込み許可
						
            var uploadAccessPolicy = context.AccessPolicies.Create(targetFile.Name,
                        TimeSpan.FromHours(3),
                        AccessPermissions.Write | AccessPermissions.List);
            // 2.4. SAS Locatorを作成
					
            var locator = context.Locators.CreateLocator(LocatorType.Sas,
                            ingestAsset,
                            uploadAccessPolicy);
            // 2.5. Upload を実行します
					
            // ---- ファイル転送 途中経過取得用 ----
						
            BlobTransferClient transferClient = new BlobTransferClient();
            transferClient.NumberOfConcurrentTransfers = 100;
            transferClient.ParallelTransferThreadCount = 100;
            transferClient.TransferProgressChanged += (s, e) =>
            {
                Console.WriteLine(" ** 経過 {0}%", e.ProgressPercentage);
            };
            // ----------------------------------
						
            sw.Start();
            var uploadTask = ingestAssetFile.UploadAsync(
                    targetFile.FullName,
                    transferClient,
                    locator,
                    System.Threading.CancellationToken.None);
            uploadTask.Wait();
            // --- この段階でファイルの転送が完了 ---
						
            // ***** 非同期実行終わり *****
						
            // 2.6. Dynamic Package用のismファイルを作成して送付します。
						
            StringBuilder sb = new StringBuilder(1024);
            sb.AppendLine(@"<!--?xml version=""1.0"" encoding=""utf-8"" standalone=""yes""?>");
            sb.AppendLine(@"<smil xmlns=""http://www.w3.org/2001/SMIL20/Language"">");
            sb.AppendLine(@"  <head>");
            sb.AppendLine(@"    <meta name=""formats"" content=""" + targetFile.Extension.Replace(".""") + @""" />");
            sb.AppendLine(@"  </head>");
            sb.AppendLine(@"  <body>");
            sb.AppendLine(@"    <switch>");
            sb.AppendLine(@"       + targetFile.Name + @""" />");
            sb.AppendLine(@"       + targetFile.Name + @""" />");
            sb.AppendLine(@"    </switch>");
            sb.AppendLine(@"  </body>");
            sb.AppendLine(@"</smil>");
            
            string ismFileName = targetFile.Name.Replace(targetFile.Extension, "") + ".ism";
            WriteToFile(ismFileName, sb.ToString());
            // ファイルサイズが小さいので、同期実行
					
            var manifestFile = ingestAsset.AssetFiles.Create(ismFileName);
            manifestFile.Upload(ismFileName);
            sw.Stop();
            Console.WriteLine("*** ファイルアップロード処理完了 ***");
            Console.WriteLine(" ** アップロード時間: {0}", sw.Elapsed.ToString());
            WriteToFile(Environment.GetEnvironmentVariable("USERPROFILE") +
                                            @"\Desktop\AssetID.txt",
                                            ingestAsset.Id);
            ///// ********* 2.10. Management
						
            ///// クエリの参考例
					
            //var ingestAssets = from a in context.Assets
						
            //                     where a.Id == "nb:cid:UUID:397be6d3-11ed-4bc3-b307-b10ff4051054"
						
            //                     select a;
						
            //var ingestAsset = ingestAssets.FirstOrDefault();
						
            //var ingestAssetFiles = from a in ingestAsset.AssetFiles
						
            //                      select a;
						
            //var ingestAssetFile = ingestAssetFiles.FirstOrDefault();
						
            //// ******** 3. Process *********************************
						
            //Console.WriteLine("******** 3.Process - エンコード *********************");
						
            //// 3.1. Job 作成
					
            //var job = context.Jobs.Create("Windows Media Video  Dynamic Package エンコード : " + DateTime.Now.ToLongTimeString());
							
            //// ******** エンコード : WMV to fMP4
						
            //var windowsAzureMediaEncoder = context.MediaProcessors
						
            //                                    .Where(p => p.Name == "Windows Azure Media Encoder")
						
            //                                    .ToList().OrderBy(p => new Version(p.Version)).LastOrDefault();
						
            //// Media Encoder の一覧
					
            //// http://msdn.microsoft.com/en-us/library/jj129580.aspx
						
            //// 3.2. タスクの作成
					
            //var encodeTask = job.Tasks.AddNew("WMV to MP4 - " + DateTime.Now.ToLongTimeString(),
						
            //                    windowsAzureMediaEncoder,
						
            //                    "H264 Adaptive Bitrate MP4 Set SD 4x3",
						
            //                    //"H264 Broadband SD 4x3",
						
            //                    TaskOptions.None);
						
            //// 定義済みのTaskPreset
						
            //// http://msdn.microsoft.com/en-us/library/jj129582.aspx
						
            //// 3.3. 入出力Asset指定
						
            //encodeTask.InputAssets.Add(ingestAsset);
						
            //encodeTask.OutputAssets.AddNew(
						
            //    string.Format("{0} - encoded:{1}",
						
            //        ingestAssetFile.Name,
						
            //        DateTime.Now.ToLongTimeString()),
						
            //    AssetCreationOptions.None);
						
            //job.StateChanged += (s, e) =>
						
            //{
						
            //    Console.WriteLine(" ** Jobの状態が変化しました");
						
            //    Console.WriteLine(" **   Previous: " + e.PreviousState);
						
            //    Console.WriteLine(" **   Current : " + e.CurrentState);
						
            //};
						
            //sw.Reset();
						
            //sw.Start();
						
            //// 3.4. ジョブ実行
					
            //job.Submit(); // 同期はPOSTまで。ジョブの実行完了まで待つわけではありません。
						
            //// ----- ジョブ実行の途中経過の取得をする設定 ---------------------
						
            //var progressJobTask = job.GetExecutionProgressTask(CancellationToken.None);
						
            ////progressJobTask.Wait();
						
            //// ------------------------------------------------------------
						
            //var jobCompleted = false;
						
            //// ジョブの途中経過を取得
					
            //while (!jobCompleted)
						
            //{
						
            //    switch (job.State)
						
            //    {
						
            //        case JobState.Error:
						
            //            jobCompleted = true;
						
            //            StringBuilder errorDescription = new StringBuilder();
						
            //            errorDescription.AppendLine(" ** 詳細:");
						
            //            foreach (var task in job.Tasks)
						
            //            {
						
            //                foreach (ErrorDetail detail in task.ErrorDetails)
						
            //                {
						
            //                    errorDescription.AppendLine(" ** Task Id: " + task.Id);
						
            //                    errorDescription.AppendLine(" **  Error Code: " + detail.Code);
						
            //                    errorDescription.AppendLine(" **  Error Message: " + detail.Message);
						
            //                }
						
            //            }
						
            //            Console.WriteLine("エラーが発生しました");
						
            //            Console.WriteLine(errorDescription);
						
            //            Console.WriteLine("何かキーを押してください。");
						
            //            Console.ReadLine();
						
            //            // 完全停止
					
            //            return;
						
            //        case JobState.Processing:
						
            //            Console.WriteLine("  ** 処理中:{0} - {1}", job.Name, DateTime.Now.ToLongTimeString());
						
            //            foreach (var task in job.Tasks)
						
            //            {
						
            //                Console.WriteLine("  ** {0} - {1:0.00}%", task.Name, task.Progress);
						
            //            }
						
            //            Console.WriteLine("  ** 10秒毎に再確認します。");
						
            //            Console.WriteLine();
						
            //            break;
						
            //        case JobState.Finished:
						
            //            jobCompleted = true;
						
            //            break;
						
            //        default:
						
            //            Console.WriteLine(" *** Jobの状態: {0}", job.State);
						
            //            break;
						
            //    }
						
            //    Thread.Sleep(MediaProcessCheckInterval);
						
            //}
						
            //sw.Stop();
						
            //Console.WriteLine("*** エンコード処理完了 ***");
						
            //Console.WriteLine(" ** エンコード  実行時間: {0}",    job.RunningDuration);
							
            //Console.WriteLine(" ** WMV to MP4 実行時間: {0}",    job.Tasks[0].RunningDuration);
						
            //Console.WriteLine("*** エンコード    総時間: {0}",    sw.Elapsed.ToString());
							
            // ******** 4. Delivery *********************************
						
            // *** Dynamic Packagingを前提に、Smooth Streaming用、HTTP Live Streaming用のオリジンサーバーを設定しています。
						
            Console.WriteLine("******** 4. Delivery *********************************");
            // 4.1. Access Policy 作成
					
            var deliveryAccessPolicy = context.AccessPolicies.Create("Streaming",
                                    TimeSpan.FromHours(Double.Parse(ConfigurationManager.AppSettings["PublishDurationHour"])),
                                    AccessPermissions.Read | AccessPermissions.List);
            //var OutputAsset = job.OutputMediaAssets[0];
						
            // 4.2.1 a) Locator 作成 : Streaming
						
            var DynamicPackagingStreamingPointLocator = context.Locators.CreateLocator(
                            LocatorType.OnDemandOrigin,
//                            OutputAsset,
					
                            ingestAsset, // Inputファイルをそのまま公開する
					
                            deliveryAccessPolicy,
                            DateTime.UtcNow.AddMinutes(-5));
            // --- ここから30秒後には、オリジンサーバーの準備が完了
						
            // 4.2.2. プレイヤーに渡すURL生成
						
            //  通常は、これをアプリケーションに渡す
					
            var Manifests = from f in ingestAsset.AssetFiles
//            var Manifests = from f in OutputAsset.AssetFiles
					
                            where f.Name.EndsWith(".ism")
                           select f;
            var Manifest = Manifests.FirstOrDefault();
            if (Manifest != null)
            {
                WriteToFile(Environment.GetEnvironmentVariable("USERPROFILE") +
                            @"\Desktop\SSPath.txt",
                            DynamicPackagingStreamingPointLocator.Path + Manifest.Name + "/manifest");
                WriteToFile(Environment.GetEnvironmentVariable("USERPROFILE") +
                            @"\Desktop\HLSPath.txt",
                            DynamicPackagingStreamingPointLocator.Path + Manifest.Name + "/manifest(format=m3u8-aapl)");
            }
            // 4.3.1. b) Locator 作成 : Download
						
            var DownloadLocator = context.Locators.CreateLocator(
                            LocatorType.Sas,
                            ingestAsset,
//                            OutputAsset,
					
                            deliveryAccessPolicy,
                            DateTime.UtcNow.AddMinutes(-5));
            // 4.3.2 プレイヤーに渡すURL
						
            var downloadFiles = from f in ingestAsset.AssetFiles
//            var downloadFiles = from f in OutputAsset.AssetFiles
					
                            where f.Name.EndsWith(targetFile.Extension)
                           select f;
            var downloadFile = downloadFiles.FirstOrDefault();
            if (downloadFile != null)
            {
                var uriBuilder = new UriBuilder(DownloadLocator.Path);
                uriBuilder.Path += "/" + downloadFile.Name;
                WriteToFile(Environment.GetEnvironmentVariable("USERPROFILE") +
                    @"\Desktop\download.txt",
                    uriBuilder.Uri.AbsoluteUri);
            }
            
            //// 以下はダウンロード用なので、ストリーミングのみの場合は不要
					
            //// 4.3.2 download を実行します
					
            //BlobTransferClient transferClient4Download = new BlobTransferClient();
						
            //transferClient4Download.NumberOfConcurrentTransfers = 10;
						
            //transferClient4Download.ParallelTransferThreadCount = 20;
						
            //// 個別のファイルダウンロード実行
					
            //var downloadTasks = new List();
						
            //StringBuilder downloadFileList = new StringBuilder(1024);
						
            //foreach (var downloadAssetFile in OutputAsset.AssetFiles)
						
            //{
						
            //    downloadAssetFile.DownloadProgressChanged += (s, e) =>
						
            //    {
						
            //        Console.WriteLine(" ** ファイルのダウンロード中 {0} / 経過: {1}",
							
            //            ((IAssetFile)s).Name,
						
            //            e.Progress);
						
            //    };
						
            //    // ダウンロードファイルはデスクトップに
					
            //    string downloadPath = Environment.GetEnvironmentVariable("USERPROFILE")
						
            //                + @"\Desktop\downloaded\" + downloadAssetFile.Name;
						
            //    downloadFileList.AppendLine(downloadPath);
						
            //    downloadTasks.Add((System.Threading.Tasks.Task)downloadAssetFile.DownloadAsync(
						
            //        downloadPath,
						
            //        transferClient4Download,
						
            //        DownloadLocator,
						
            //        System.Threading.CancellationToken.None)
						
            //    );
						
            //}
						
            //System.Threading.Tasks.Task.WaitAll(downloadTasks.ToArray());
						
            //// --- この段階でファイルの転送が完了 ---
						
            Console.WriteLine("******************************************************");
            Console.WriteLine("全ての処理が終了しました。");
            Console.WriteLine("総処理時間: {0}", totalSw.Elapsed.ToString());
            Console.WriteLine("何かキーを押してください。");
            Console.ReadLine();
        }
        /// <summary>
								
        /// Utility: 文字列のファイル出力
					
        /// </summary>
								
        /// outFilePath">
								
        /// fileContent">
								
        static void WriteToFile(string outFilePath, string fileContent)
        {
            System.IO.StreamWriter sr = System.IO.File.CreateText(outFilePath);
            sr.Write(fileContent);
            sr.Close();
        }
    }
}
広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中