ライブ配信サーバーを1 Click、5分で立てる! (C#編)

Azure Media Servicesの最大の魅力は、API Platformであるという点です。これにより簡単なツールの作成から、大規模システムに必要な映像配信の既存のワークフローと統合できる点です。
VoDに関しては、別記事に概説がありますが、ここではPublic PreviewになったLive機能についても早速自動化するサンプルコードをご紹介します。Windows PCで動作するコンソールアプリケーションを作成しましょう! Azure 管理画面の無い世界です。
ちなみに、手元にWindowsが無い方は、無償でも使えるAzureの契約があればAzure上のWindows環境で同じ事が出来ます!

 

VoDのAPIご参考:

はじめての Windows Azure メディア サービス:
http://msdn.microsoft.com/ja-jp/windowsazure/dn133199

Developer Camp 2012 Japan Fall: マルチデバイスへの動画配信サービス – Windows Azure Media Services とは – :
http://channel9.msdn.com/Events/Windows-Azure-DevCamps/Developer-Camp-2012-Japan-Fall/Devcampjp_D2S2

キャッチアップ! Windows Azure メディアサービス:
http://gihyo.jp/dev/serial/01/azure_mediaservices

 

用意するもの

  • Azureの契約
    • こちらから、どうぞ: http://aka.ms/startazure
    • MSDNをお持ちの方は、Azure特典ご活用ください! 勿体ない…
  • Visual Studio 2010以上マルチデバイスへの動画配信サービス ~ Windows Azure Media Services とは ~マルチデバイスへの動画配信サービス ~ Windows Azure Media Services とは ~
    • やっぱり、最新のVisual Studio 2013 が欲しいですね!
    • Visual Studio 2013: http://www.visualstudio.com/
    • MSDN特典をお持ちの方はAzure利用特典があります。Azure上の仮想マシンギャラリーの中にVisual Studio 2013入りのイメージがあります。さくっとVisual Studioが使えますので、ご活用ください。

手順

  1. Visual Studio 2013を起動し、[Visual C#] – [コンソール アプリケーション] を選択し、プロジェクトを作成します。
  2. Azure Media Services .NET SDK ExtensionsのライブラリをNuGetから追加します。
    [依存関係] を見ていただきたいのですが、Azure Media Services .NET SDKのLive public previewの入っている3.0.0.7 以降を参照しています。
    ここからは、コードを書いていきます。

     

サンプルコード

利用する、クラスは主に以下の4つです。

クラス名 役割
Channel

取り込み先、およびPreviewを担っています。

Program

配信先情報を管理します。URLやアーカイブです。
単一のChannelで、アーカイブ時間の異なる複数のProgramを持てます。

Asset

アーカイブのためのBlobファイルになります。VoDのAPIと共通です。
Programと1:1で紐づきます。

Locator

配信URLになります。

 

Steaming Endpointも勿論作成したりできます。異なるドメイン名を作成できたりしますが、ここでは割愛します。

こちらがサンプルコード全文です。

コード

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using System.Diagnostics;

using System.Net;

 

using Microsoft.WindowsAzure.MediaServices.Client;

 

namespace ConsoleApplication2

{

class Program

{

static string ChannelName = “dahatakeChannel”

+ DateTime.Now.ToShortDateString().Replace(“/”, “”)

+ DateTime.Now.ToShortTimeString().Replace(“:”, “”);

 

static void Main(string[] args)

{

// 処理時間の計測

var totalSw = new Stopwatch();

totalSw.Start();

var sw = new Stopwatch();

 

var con = new CloudMediaContext(“<Azure Media Services Account Name>“, “<Azure Media Services Account Key>“);

 

// 1. Channel 作成と開始

Console.WriteLine(“1. Channel 作成”);

 

sw.Start();

var channel = con.Channels.Create(

new ChannelCreationOptions(

ChannelName,

StreamingProtocol.RTMP, //RTMPで取り込み実施

new List<IPRange>

{

new IPRange

{

Name = “All OK”,

Address = IPAddress.Parse(“0.0.0.0”),

SubnetPrefixLength = 0

}

}

));

 

sw.Stop();

Console.WriteLine(” Channel 作成時間: {0}”, sw.Elapsed.ToString());

 

Console.WriteLine(“2. Channel 開始”);

sw.Reset();

sw.Start();

channel.Start();

sw.Stop();

Console.WriteLine(” Channel 開始完了時間: {0}”, sw.Elapsed.ToString());

 

WriteToFile(

ChannelName + “_取り込みURL.txt”,

channel.Input.Endpoints.FirstOrDefault().Url.ToString());

WriteToFile(

ChannelName + “_PreviewURL.txt”,

channel.Preview.Endpoints.FirstOrDefault().Url.ToString());

 

Console.WriteLine(“Azure上でのエンコーダーからの取り込みの準備が完了しました。”);

Console.WriteLine(” ***URL出力先 (デスクトップにあります)***”);

Console.WriteLine(” ” + ChannelName + “_取り込みURL.txt ファイル”);

Console.WriteLine(” ” + ChannelName + “_PreviewURL.txt ファイル”);

Console.WriteLine(“”);

Console.WriteLine(“[次の手順]”);

Console.WriteLine(“エンコーダーからAzureへの配信を開始して、Azureに上で映像を受信できているのを確認してください。”);

Console.WriteLine(“確認後、Entry Keyを押すことで、Azureからインターネットへ配信を開始します。”);

Console.ReadLine();

 

Console.WriteLine(“3. Program 作成”);

sw.Reset();

sw.Start();

 

// 2. アーカイブ および DVRWindow のための Asset 作成

var archiveData = con.Assets.Create(

ChannelName + “_Archive”,

AssetCreationOptions.None);

 

// 3. プログラム作成と開始

var program = channel.Programs.Create(

ChannelName + “Program”,

TimeSpan.FromMinutes(5), //アーカイブ時間

archiveData.Id);

sw.Stop();

Console.WriteLine(” Program 作成時間: {0}”, sw.Elapsed.ToString());

 

Console.WriteLine(“4. Program 開始”);

sw.Reset();

sw.Start();

program.Start();

sw.Stop();

Console.WriteLine(” Program 開始完了時間: {0}”, sw.Elapsed.ToString());

 

// 4. Locator作成

Console.WriteLine(“5. Locator 作成”);

sw.Reset();

sw.Start();

var locator = con.Locators.CreateLocator(

LocatorType.OnDemandOrigin,

archiveData,

con.AccessPolicies.Create(

“Live Streaming”,

TimeSpan.FromDays(3650), // コンテンツの公開期間: 10年

AccessPermissions.Read

));

 

sw.Stop();

Console.WriteLine(” Locator 作成時間: {0}”, sw.Elapsed.ToString());

 

WriteToFile(

ChannelName + “_発行URL(Smooth).txt”,

locator.GetSmoothStreamingUri().AbsoluteUri);

 

Console.WriteLine();

Console.WriteLine(“全ての処理が終了しました。発行URLがデスクトップに出力されていますので、ご確認ください。”);

Console.WriteLine(“総処理時間: {0}”, totalSw.Elapsed.ToString());

Console.WriteLine(“何かキーを押してください。”);

Console.ReadLine();

 

}

 

 

/// <summary>

/// Utility: 文字列のファイル出力

/// </summary>

/// outFileName”>

/// fileContent”>

static void WriteToFile(string outFileName, string fileContent)

{

 

System.IO.StreamWriter sr = System.IO.File.CreateText(

Environment.GetEnvironmentVariable(“USERPROFILE”) +

@”Desktop” +

outFileName);

sr.Write(fileContent);

sr.Flush();

sr.Close();

}

}

}

 

解説

  • 取り込みプロコルはRTMPの指定がされています。Smooth Streaming形式にも対応できますので、適時変更ください。
  • 取り込みURLのアクセス制御ですがIP Addressでできます。このサンプルコードは、どの場所からでもOKの状態です。Preview URLと合わせて、適時変更ください。
  • 各種URLをデスクトップ上のテキストファイルに出力しています。
    • エンコーダー (Expression Encoder, Wirecastなど) に設定する「取り込みURL」
    • エンコーダーからの映像/音声が、Azureに届いているのを確認する「PreviewURL」
    • 配信を開始した後の「発行URL」
  • ライブ配信時に「見逃し視聴時間」「アーカイブ時間」が出来ますが、それは「5分」のみになっています。Program作成時の第二引数で指定してください。「見逃し視聴時間」と「アーカイブ時間」を1つのProgramで別設定は出来ません。別の設定が必要な場合は、Programを複数作成してください。それぞれにアーカイブ先のAssetと配信URLであるLocatorが必要です。
  • 見逃し視聴時間を超えても配信はし続けます。その際アーカイブファイルは上書きされていきます。

実際のアプリケーションの動き

  1. exeファイルをクリックします。
    コンソールが起動して、処理経過を表示してくれます。
    Channelが作成開始されると、取り込み用のURLと、プレビュー確認用のURLがデスクトップに出力されています。
    テキストのファイルが出力されていますね。
  2. エンコーダーに取り込みURLを設定して、配信を開始
    Azure Media Servicesはエンコーダーからの配信を受け付ける準備が出来ています。ここでは、TeleStream社のWirecastを使って、エンコードを行います。
    手順のご参考: Azure Media Services Live streaming などがPublic Previewになりました
    ご注意: Wirecastのエンコード設定にご注意ください。
    ご参考Wirecastのエンコード設定: Using Telestream Wirecast Encoder with Media Servicesデスクトップには、Wirecastと取り込みURLの設定が並んでいます。

  3. Wirecastで[ストリーム]ボタンを押して、Azureに配信を開始します。
    映像がAzureに正しく送信できているかを、同じくデスクトップに出力されているPreview URLを使って確認します。
    ご注意点: Azure Media ServicesのLive Previewで、Preview URLについては、Dynamic Packagingの実装がされていません。Smooth StreamingのPlayerにて確認ください。

    Smooth Streaming確認用のPlayer: http://amsplayer.azurewebsites.net/
    ご注意点: 勿論Smooth Streamingが再生できればどこでも良いです。

  4. コンソールアプリケーションに戻って[Enter Key]を押して、配信を開始します。
    ProgramとAsset/Locatorの作成に処理が進みます。
    全て処理が完了すると、以下の様に表示されます。
    ご注意点: ここでは「総処理時間が7分11秒」と表示されていますが、この時間は、Blogを書きながら、Wirecastの設定もしながら、という私の「操作時間」も含めてのものです。API呼び出しの総合計時間ではありませんので。
    デスクトップには、ファイルが3つ。
    このうちの「xxx発行URL(Smooth).txt」を各種プレイヤーに渡して再生できるか確認をしてください。

    DASH-IF.Orgのリファレンスプレイヤー: http://dashif.org/reference/players/javascript/1.2.0/index.html
    しつこいですが、IE11で見ましょう(笑)。

    ご参考:
    デバイス毎のURL作成に: Dynamic Packaging: http://msdn.microsoft.com/ja-jp/library/jj889436.aspx
    シンプルなマルチデバイス対応のブラウザービデオプレイヤーの作成に: https://daiyuhatakeyama.wordpress.com/2014/09/17/browser%e3%83%99%e3%83%bc%e3%82%b9%e3%81%ae%e3%83%93%e3%83%87%e3%82%aa%e3%83%97%e3%83%ac%e3%82%a4%e3%83%a4%e3%83%bc-version-0-7-%e3%81%a8azure-media-services/

 

まとめ

Public Previewとはいえ、APIの威力を感じさせてくれるサービスです。ネイディブはRESTのAPIですので、PHP, Rubyなど、皆さんの馴染みの環境から呼び出して試してみてください。

 

参考:

Creating a Live Streaming Application with the Media Services SDK for .NET: http://msdn.microsoft.com/en-us/library/dn783465.aspx

Azure Media Services REST API Reference: http://msdn.microsoft.com/en-us/library/hh973617.aspx

広告

Browserベースのビデオプレイヤー version 0.7 とAzure Media Services

Web Browserだけで、VoDやLive配信を行いたい、というケースも最近増えています。HTML5とともに、MPEG-DASHが語られるようになり、先日Public PreviewになったAzure Media ServicesのLive機能でも、MPEG-DASHがサポートされています

 

DASH Live Streaming with Azure Media Service:
http://azure.microsoft.com/blog/2014/09/13/dash-live-streaming-with-azure-media-service/

 

1つのWebページで、複数デバイスに対応するためには、以下の2つを行う必要があります。

・映像自身:        Azure Media Servicesがマルチデバイス向けの対応をお手伝いします

・プレイヤー:    Silverlightなど、そのデバイスで動作する適切なプレイヤーを設定する必要があります。例えばHTML5 VIDEOを使うのもその一つです。

フォールバックと呼ばれる手法を使って、例えばHTML5 Videoが動かない環境では、SilverlightやFlash Playerを表示させる、という事を行う事が多々あります。

 

ここでは、Azure Media ServicesのDynamic Packagingを前提にした簡単なビデオプレイヤーのサンプルコードを紹介します。
5月末のde:codeのAzure Media Servicesセッションのデモでも利用したものです。

 

必要なもの:

  • Web Server: ASP.NETが動作する環境にしてください。Azure Web Siteがおすすめです。
  • Azure Media Servicesの配信環境: VoD / Live どちらでも構いません。

 

アプリの構成:

C#で書いています。プロジェクトファイルの内容は以下になっています。

 

以下、2つのファイルは外部のものです。

  • Dash.all.js:                Dash Industry Forumのdash.js ファイルです。

                    https://github.com/Dash-Industry-Forum/dash.js

  • SmoothStreamingPlayer.xap:    Microsoft Media PlatformのSilverlight Playerの「サンプル」プレイヤーです。サンプルですので・・・

                    http://smf.codeplex.com/releases/view/88970

 

作成したのは、Webフォーム1つだけです。Webアプリですと、詳細画面、つまりビデオ再生ページを想定しています。

 

default.aspx

UI部分はこちらです。

——————————————————————————————————————

<%@ Page Language=”C#”

    AutoEventWireup=”true”

    CodeBehind=”default.aspx.cs”

    Inherits=”HTML5_VideoPlayer._default” %>

<!DOCTYPE html>

 

<html xmlns=”http://www.w3.org/1999/xhtml”&gt;

<head runat=”server”>

<meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″/>

<title>Web Browser Video Player</title>

    <script src=”dash.all.js”></script>

    <script>

        function setupVideo(url, videoPlayerName) {

            var context = new Dash.di.DashContext();

            var player = new MediaPlayer(context);

            player.startup();

            player.attachView(document.querySelector(videoPlayerName));

            player.attachSource(url);

        }

    </script>

</head>

<body style=”font-family: ‘Segoe UI’, Tahoma, Geneva, Verdana, sans-serif” font-size: x-large;”>

<form id=”form1″ runat=”server”>

    <h1>Web Browser Video Player</h1>

    <asp:Label ID=”deliveryMethodName” runat=”server” style=”font-size: xx-large; color: #0000FF”></asp:Label>

        <br />

    <asp:Label ID=”videoPlayerLabel” runat=”server”></asp:Label>

        <br />

        <asp:Label ID=”lblDebug” runat=”server”></asp:Label>

</form>

</body>

</html>

——————————————————————————————————————

[解説]

JavaScriptのsetupVideo関数は、MPEG-DASHのプレイヤーを作成する際によく使われているものです。
毎回必要ではないので…

ご参考: DASH.js を使用した HTML5 アプリケーションへの MPEG-DASH アダプティブ ストリーミング ビデオの埋め込み:

http://msdn.microsoft.com/ja-jp/library/dn593606.aspx

 

default.aspx.cs

サーバーサイドの実行ロジックです。

Dynamic Packagingの良い点は、プレイヤーのURLの末尾の文字列を変えるだけで、Smooth Streaming /HLSなどのトランスポートプロトコルを動的に変えてくれることです。ここでは、それを最大限活用しています。

Dynamic Packaging:
http://msdn.microsoft.com/ja-jp/library/jj889436.aspx

 

HTML5においては、ブラウザーのバージョンなどでなく、そのブラウザーが持っている能力で、機能判定をするのがいいのですが・・・ビデオに関してはそういったAPIが提供されていません。ですので、残念ながら、ここでは主にUser-Agentでの判定を使っています。

 

——————————————————————————————————————

using System;

using System.Collections.Generic;

using System.Configuration;

using System.Linq;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Text;

 

 

 

namespace HTML5_VideoPlayer

{

    public partial class _default : System.Web.UI.Page

    {

 

        // [Action] 再生するURLを設定してください

        private static string playerURL = “http://dahatakemediaeast.origin.mediaservices.windows.net/<xxx>/yyy.ism/Manifest&#8221;;

 

        private enum DeliveryType

        {

            Smooth,

            HLSv3,

            HLSv4,

            DASH

        }

 

        protected void Page_Load(object sender, EventArgs e)

        {

            buildPlayerHTML();

 

        }

 

        private void buildPlayerHTML()

        {

            // 配信方式の判定

            var deliveryType = DetermineDeliveryType(Request.UserAgent);

            deliveryMethodName.Text = deliveryType.ToString();

 

            videoPlayerLabel.Text = MakeHTMLVideoPlayerString(

                playerURL,

                deliveryType);

 

            #region debug

            /// for debug

            System.Text.StringBuilder sb = new System.Text.StringBuilder(1024).AppendFormat(“UAString: {0}<br/>”, Request.UserAgent)

                .AppendFormat(“Browser: {0}<br/>”, Request.Browser.Browser)

                .AppendFormat(“Platform: {0}<br/>”, Request.Browser.Platform)

                                .AppendFormat(“Platform: {0}<br/>”, Request)

                .AppendFormat(“Type: {0}<br/>”, Request.Browser.Type)

                .AppendFormat(“IsMobileDevice: {0}<br/>”, Request.Browser.IsMobileDevice)

                .AppendFormat(“MobileDeviceModel: {0}<br/>”, Request.Browser.MobileDeviceModel)

                .AppendFormat(“MobileDeviceManufacturer: {0}<br/>”, Request.Browser.MobileDeviceManufacturer)

                .AppendFormat(“Version: {0}<br/>”, Request.Browser.Version)

                .AppendLine(“<br />”)

                .AppendFormat(“delivery method: {0}<br/>”, deliveryType.ToString())

                ;

 

            lblDebug.Text = sb.ToString();

 

            #endregion

        }

 

        private string MakePlayerURL(string URL, DeliveryType type)

        {

            var result = URL;

 

 

            switch (type)

            {

                case DeliveryType.Smooth:

                    break;

                case DeliveryType.HLSv3:

                    result = URL + “(format=m3u8-aapl-v3)”;

                    break;

                case DeliveryType.HLSv4:

                    result = URL + “(format=m3u8-aapl)”;

                    break;

                case DeliveryType.DASH:

                    result = URL + “(format=mpd-time-csf)”;

                    break;

                default:

                    break;

            }

 

            return result;

 

        }

        

 

        private DeliveryType DetermineDeliveryType(String UserAgentString)

        {

 

            var result = DeliveryType.Smooth;

 

            if (Request.Browser.MobileDeviceModel.ToUpper().Equals(“IPHONE”)

                || Request.Browser.MobileDeviceModel.ToUpper().Equals(“IPAD”))

            {

                result = DeliveryType.HLSv4;

 

            }

            else if (Request.UserAgent.Contains(“Android”))

            {

                result = DeliveryType.HLSv3;

            }

            else if (Request.Browser.Browser.Equals(“InternetExplorer”)

                && (Request.UserAgent.Contains(“NT 6.2”) || Request.UserAgent.Contains(“NT 6.3”)))

            {

                result = DeliveryType.DASH;

            }

            else if (Request.Browser.Browser.Equals(“Chrome”) &&

                double.Parse(Request.Browser.Version) > 24) {

                result = DeliveryType.DASH;

            }

 

            return result;

 

        }

 

        private string MakeHTMLVideoPlayerString(String URL,

            DeliveryType deliveryType)

        {

 

            var result = new StringBuilder(2048)

                .AppendLine(@”<video autoplay controls”)

                .AppendLine(@” width=””480″” height=””270″” “)

                .Append(@”src=”””).Append(URL).Append(“@@@”).AppendLine(@””””)

                .AppendLine(@”</video>”)

                .ToString();

                ;

 

            switch (deliveryType)

            {

                case DeliveryType.Smooth:

                    result =

                    @”<div id=””silverlightControlHost””>” +

                    @”” +

                    @”” +

                    @”” +

                    @”” +

                    @”” +

                    @”” +

                    @”” +

                        @” ” +

                    @”” +

                    @”” +

                    @”</div>”;

 

                    break;

                case DeliveryType.HLSv3:

                    result = result.Replace(“@@@”, “(format=m3u8-aapl-v3)”);

                    break;

                case DeliveryType.HLSv4:

                    result = result.Replace(“@@@”, “(format=m3u8-aapl)”);

                    break;

                case DeliveryType.DASH:

                    result = new StringBuilder(1024)

                        .AppendLine(@”<video autoplay controls width=””80%”” id=””videoplayer””>”)

                        .AppendLine(@”</video>”)

                        .AppendLine(@”<script>”)

                        .Append(@”setupVideo(“””).Append(URL).Append(@”(format=mpd-time-csf)””, “”#videoplayer””);”)

                        .AppendLine(@”</script>”)

                        .ToString();

                    break;

 

                default:

                    break;

            }

            

            return result;

 

        }

        protected void Button1_Click(object sender, EventArgs e)

        {

            buildPlayerHTML();

        }

    }

}

 

——————————————————————————————————————

[解説]

複雑なことはしていませんので、読んでいただけると大よそご理解いただけると思います。ですので、主なものだけにとどめます。

MakePlayerURL 関数:            ビデオプレイヤーが参照するURL文字列を作成しています。Dynamic Packaging前提です。味気ないですが…

DetermineDeliveryType 関数:         Smooth / HLS / DASHなどのトランスポートプロトコルを判定しています。

MakeHTMLVideoPlayerString関数:    ビデオプレイヤー用のHTML文字列を作成しています。DASHでは、HTML側にsetupVideo関数があることが前提になっています。

 

まとめ

とても簡易的なものではあります。ですが、Azure Media ServicesのLive機能が公開された今、最高のUser Experienceを追及するNative Application作成とは別に、いわゆるWebフレンドリーといいますか、Socialで拡散も容易なHyperLinkの世界も活きていると思います。
なだらかに数年かけてMPEH-DASHへと業界は移行をしていきますが、その過渡期の簡易ビデオプレイヤーとしてご利用いただければと思います。

 

ご参考: MPEG-DASHのサンプル

Azure Media Services Live streaming などがPublic Previewになりました

9.11は、ビデオの世界にとっても大事な日になりました。

今日、Microsoftは、Azureの各種機能強化を発表し、その中にAzure Media ServicesのLiveの機能がついにPublic Previewになりました。つまり、クラウドのコンピューティングリソースを使って、幾つもの映像配信チャネルを簡単に作成できるようになりました!

Azure Media Services Launches Proven Live Streaming Platform:

http://azure.microsoft.com/blog/2014/09/10/azure-media-services-launches-proven-live-streaming-platform/

 

Live Streaming は以下のアーキテクチャになっています。「チャネル」に映像/音声を送信し、それは、Blob 「Storage」に保存されます。同時に、「ストリーミング エンドポイント」に送信され、各種デバイスに応じたストリーミング方式に変換がなされます。

 

早速、Live Streamingを使ってみましょう。コーディング無しでも結構いけますよ!

 

用意するもの:

  • こちらのドキュメントにあります通り、Wirecastに限らず、Smooth Streaming かRTMP出力できるエンコーダーであれば動作します。ここに挙げられているもの以外でも、動作すると思います。

        Smooth Streaming: Elemental, Envivio, Cisco, RGP

        RTMP: Adobe Flash Live, Wirecast and Tredek encoders

     

  • Azureの契約(笑)

    お持ちでないかたは、こちらから。

    Microsoft Azure 無償評価版:     http://azure.microsoft.com/ja-jp/pricing/free-trial/

 

手順:

注: 2014.9.16: Wirecastの手順を一部アップデートしました。

  1. Azure Media Services のアカウント作成

    これは、困らないと思いますが・・・

    メディア サービス アカウント の作成方法

     

  2. [ストリーミング ユニット] の有効化

    多様なストリーミングプロトコルに対応するために、Dynamic Packagingの機能を有効化します。

    [ストリーミング ユニット] を1以上にする手順

  • 上記ドキュメントでは「ストリーミング ユニット」が「オンデマンド ストリーミング占有ユニット」と表現されていますので、ご注意ください。頑張ってドキュメント更新します!

Dynamic Packaging: http://msdn.microsoft.com/library/azure/jj889436.aspx

 

  1. [チャネル] の作成

    チャネルの一つ一つが映像信号になります。そして、Playerに渡すURLが1つできます。これをたくさん作成できます!!!

    1. 画面下の[追加]を押して、新規チャネルを作成します。
      今回はWirecastからRTMPにて配信しますので、[取り込みプロトコル]をRTMPにしています。

    2. 右下のチェックボタンを押すことで、チャネルが作成されます。5-6分くらいですかね・・・

    はい! できました。

  2. Wirecastからの配信設定

    ここでは、無償評価版を使ってみます。
    Wirecastでは、配信用の設定を行うと、その設定情報をファイルとして保存できますので、何度も繰り返し使う場合には大変便利です。

     

    1. Wirecastのメニューの[配信] – [配信の設定]に移動します。

       

    2. 出力先を選択で[RTMPサーバー]が表示されている状態で[OK]を押します。エンコード方式や、出力先の設定などをこの先で行います。

    3. [エンコード]のメニューから[新規プリセット]を選択します。

    4. エンコーダー設定を行います。Wirecastのデフォルト設定では、ビデオ エンコーディングで「x264」のエンコーダーが設定されますが、現在のAzure Media Serviceでは、「H.264」のみがサポートされています。ライブエンコードの機能は提供されていませんので、ご注意ください。

      以下、サンプルです。

    5. 「保存」を押して、プリセットとして保存をしてください。
    6. [配信設定] の[アドレス] にAzure Media Servicesの[取り込みURL]を設定します。コピーアンドペーストします。

       

       

      ご注意: 配信先によっては、コーデック等、細かい設定が必要になるかもしれません。その場合は、Wirecastの中で設定をしてください。

       

  3. カメラの設定

    ライブっぽく、PCに接続されているカメラを使います。PCからカメラデバイスとして認識されていれば、Wirecastで取り込めます。
    画面、下部の[ブランクショット]の中で、カメラのアイコンがあります。これをクリックして[Integrated Camera ショットの追加]を押します。

     

    はい。映像が出てきました!

     

  4. ストリーミング配信開始

    Wirecastの場合、2画面表示されます。
    ・左の映像はこれから出そうとするもののプレビュー。つまり、作業エリア

    ・右の映像は、配信している映像

    これらの切り替え、つまり、プレビュー映像の本番への配信は、画面中央の「矢印」のボタンで行います。

     

     

    矢印のボタンを押すと、下図のように、本番へ配信している映像を確認できます。

     

    この時点では、Azureに配信されていません。画面上部の[ストリーム]というボタンを押します。押した瞬間から、Azureに配信が開始されます。

     

    配信中は、このボタンが赤枠で囲まれます。

     

  5. エンコーダーからの映像のAzure上での確認

    Azure 管理画面の画面下部に[再生]ボタンがあります。このメニューから[プレビューURLの再生]を押して、プレビュー映像を確認します。

     

    はい、来ていますね。

  6. 外部へ公開

    この時点では、外部からはアクセスできません。[ストリーミングの開始]ボタンを押して、インターネットでのライブ配信を開始します。

     

    はい、出来ました。

    1-2分ですね。

     

  7. PlayerへのURLの設定

    各種Playerに、[発行されたURL]を設定ください。

     

    Smooth Streaming の確認用Silverlight Player: http://smf.cloudapp.net/healthmonitor

     

    HLS – iOS/Android。HTML5 Videoタグにて。Webブラウザにて確認できます。

        iOSの場合は、URLの最後に、以下の文字列を追加してください。

            (format=m3u8-aapl)

        Androidの場合は、こちらを推奨します。

            (format=m3u8-aapl-v3)

     

    ご参考: Dynamic Packaging: http://msdn.microsoft.com/ja-jp/library/jj889436.aspx

     

    MPEG-DASH: http://dashif.org/reference/players/javascript/1.2.0/index.html

        今日時点の最新のJavaScript Reference Playerは、1.2.0です。
        同じく、URLの最後に以下の文字列を。

            (format=mpd-time-csf)

        IE11でみてくださいね(笑)

     

    これで、マルチデバイスにライブ配信が出来ました。

     

  8. Video on Demand (アーカイブ)の作成

    何もしなくても、アーカイブが作成されています。

    Wirecastからのエンコードを停止。チャネルの[ストリーミングの停止]をして、Azure 管理画面上から、「発行されたURL」が無くなっても。もっといえば、チャネルを削除しても。同じURLで再生できます。

    これは、LiveとVoDが透過的に連動しているからです。

    [コンテンツ]の画面に移動をしてみてください。過去行ったライブ配信のデータがアーカイブとして保存をされています。

     

    いかがですか?

    ここでは、Azure管理画面から、基本的なライブ配信を行いました。

     

    スケールアウトのためにどうするか、暗号化はどうするか、アプリケーションとどう連携するか、モニタリングはどうするか、可用性をどうするか、などはまた別の機会にと思います。

     

    まずは楽しんでください!!!

    ご参考: [MSDN]: Using Telestream Wirecast Encoder with Media Services:
    http://msdn.microsoft.com/en-us/library/dn783449.aspx

HTML5 へのストリーミング! MPEG-DASHでのストリーミングを行う (DRMなし版)

HTML5 Videoエレメントですが、以下の2つが出来ません。

  • ストリーミング配信: つまりライブ配信が出来ません。
  • コンテンツ保護

そのため、ストリーミング配信の実現のため、MPEG-DASH、コンテンツ保護ではCommon Encryptionという仕様がISOにて固まりつつあります。
それらをHTML5の中で処理するために、Media Source Extension (MSE)Encrypted Media ExtensionsもようやくW3Cにて勧告近くまで来ています。ちなみに、この2つのAPIですが、執筆時点のWebブラウザーですと、以下で動作確認ができます。いずれもPC用です。

  • IE 11 (ただし、Windows 8.1のみ)
    • ファイルフォーマット: ISO Base Media File Format
    • コーデック: H264 / AAC

・Chrome 23以降

いずれも最終系ではなく、フルサポートはまだなのですが、簡単に試す手順について説明をします。
手順以外については、別稿にて。

必要なもの

  • デバイス/Web Browser側
    • Windows 8.1
  • サーバー/配信側
    • Windows Azure Media Services
    • Web Server: 個人的にはWindows Azure WebSite を推奨!!!
  • ビデオファイル

手順

  1. ビデオファイルの用意
  2. MPEG-DASH用にトランスコードと配信設定を行う
  3. HTML5 と JavaScriptの記述
  4. HTML5およびdash.jsファイルの、Webサーバーへのアップロード

それでは、「2」から、見ていきましょう。

MPEG-DASH用にトランスコードと配信設定を行う

Dynamic Packagingを使ってMPEG-DASHのファイルを送信できるようにします。また、APIを使うのではなく、Windows Azure の管理コンソールから作業を行います。

Dynamic Packaging:
http://msdn.microsoft.com/ja-jp/library/jj889436.aspx

ファイルはSmooth Streamingのものを作成します。
MP4でも実装できますが、今日はWindows Azureの管理コンソールから、MPEG-DASH用の配信設定が出来ないためです。Windows Azure Media ServicesのAPIを使う場合は、勿論MP4でも大丈夫です。

Dynamic Packagingを有効にする

具体的には、オンデマンドストリーミングの占有ユニットを、1以上にします。

ファイルをアップロードする。

Windows Azure Media Servicesの管理画面で[コンテンツ]に移動します。
ファイルの一覧が見えますね。

画面下の [アップロード] から、用意したビデオファイルをアップロードします。

Smooth Streaming用のファイルを作成

アップロードしたファイルを「トランスコード」します。

該当ファイルを選択して、画面下の[エンコード] にて [PC/Mac での再生 (Flash/Silverlight を使用) ] を選択します。

トランスコードは、映像ファイルのサイズや長さで待ち時間が異なりますので、ご注意ください。途中経過は [ジョブ] 画面にて確認が出来ます。

配信の設定

[コンテンツ] 画面から、トランスコードのファイルを選択して [発行] を押します。
これで、プレイヤーが参照するURLが作成されます。

発行後ですが、該当ファイルを選択して、[発行URL] の一番右側にマウスカーソルを持っていくと、その文字列を「コピペ」出来ますので、そちらにて。

MPEG-DASH リファレンス クライアントでの動作確認

MPEG-DASHは、DASH Industry Forumにて仕様が策定されています。その中で、HTML5ブラウザーのみで動作する「リファレンス クライアント」が提供をされていますので、そちらにて作成したMPEG-DASHコンテンツの動作を確認します。

DASH Industry Forum:

http://dashif.org

MPEG-DASH リファレンス クライアント:

http://dashif.org/reference/players/javascript/index.html

Windows 8.1のIE11を起動して、リファレンス クライアントのサイトへ移動します。

先ほどの[発行URL]を、入力し (実際はコピペですが)、最後に以下の文字列を追加します。ここがマジックで、ここがストリーミングサーバー上で処理されているものになります。

(format=mpd-time-csf)

こんな感じです。

[Load] を押すと、再生がされます。

プラグイン不要ですね!

ちなみに、デスクトップ版と、モダンUI版のIE、どちらでも動きます!!! グレートですね。

以下、余談です。

MPEG-DASHは純粋なるHTTP GETリクエストだけで映像ファイルを取得します。要はサーバー側では、HTTP Byte-Rangeリクエストが出来ればいいのです。
つまり、純粋なるVODの場合、ストリーミングサーバーは実は不要です。
ただし、ライブ配信の場合はそうは行きません。ストリーミングサーバーが必須となります。

HTML5とJavaScriptの記述

さて、いよいよHTML5側に入ります。

Dash.js の入手

実は、Dash Industry Forum では、JavaScript のライブラリーを提供してくれています。Dash.js というものです。
これは、Githubにて開発されています。そのライセンスの元、誰でも無料で使う事が出来ます。

GitHubのDash.js :

https://github.com/Dash-Industry-Forum/dash.js

ここでは、そこから、dash.all.js というファイルをダウンロードして使います。

HTML5の記述

サンプルのソースコードは、以下になります。

HTML5 記述サンプル : index.htm

<html>

<head>

<span class=”hiddenSpellError” pre=””>HTML5</span> VIDEO

<script src=”dash.all.js”></script>

<script>

// Videoエレメントの設定と、Dash Playerのアタッチ

function setupVideo() {

var url = “<発行URL>”;

var context = new Dash.di.DashContext();

var player = new MediaPlayer(context);

player.startup();

player.attachView(document.querySelector(“#videoplayer”));

player.attachSource(url);

}

window.addEventListener(“load”, setupVideo, false);

</script>

</head>

<body>

autoplay controls id=”videoplayer” width=”80%” height=”80%”>

</video>

</body>

</html>

ポイントを。
dash.js のMediaPlayer は、HTTP GETリクエストで取得したデータを、Videoエレメントの裏で動いている、SourceBuffer に追加しています。その設定をするだけで、ある程度自動的に処理をしてくれます。

dash.js でのオブジェクト構造などは、また別の機会に。

HTML5およびdash.jsファイルの、Webサーバーへのアップロード

さて、作成したファイルをWebサーバーへアップロードします。

Windows Azure WebSite を使う場合は、こちらのBlog POSTもご参考にしてください。私はLove WebSiteなので(笑)。
ちなみに、WebSiteへFTPでアップロードした場合、こんなディレクトリー構成になります。先のサンプルですと、dash.all.js も一緒にアップロードする必要がありますから、お忘れなく。

さ、アクセスをしてみましょう!

映像の部分を右クリックしてみると、HTML5 Videoエレメントだという事が分かりますね。

これで、皆さんのWebページの画面に、組み込む方法が分かりましたね!!!

楽しんでください!

ご参考:

  • Dash.js関連

Build 2013: Building Media Streaming Apps and Sites Without Plug-Ins Using MPEG-DASHBuilding Media Streaming Apps and Sites Without Plug-Ins Using MPEG-DASHBuilding Media Streaming Apps and Sites Without Plug-Ins Using MPEG-DASH:

http://channel9.msdn.com/Events/Build/2013/3-089

MPEG-DASH Tutorial: Embedding an adaptive streaming video within your HTML5 application – by MS Open Tech:

http://msopentech.com/blog/2014/01/03/streaming_video_player/

  • Windows Azure Media Services

MPEG DASH preview from Windows Azure Media Services – by John Deutscher:

http://blog.johndeutscher.com/2013/06/10/mpeg-dash-preview-from-windows-azure-media-services/

  • Demo

IE Test Drive:

http://ie.microsoft.com/testdrive/HTML5/eme/

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();
        }
    }
}