Compartilhar via


Azure Media Indexer の紹介

このポストは、9 月 10 日に投稿された Introducing Azure Media Indexer の翻訳です。

はじめに

インターネット ビデオの利用は驚くべき速度で成長しています。Cisco VNI Forecast の調査によると、2014 年には消費者によるすべてのインターネット トラフィックの 70% をビデオ コンテンツが占め、2018 年までに 79% に伸びると予測されています。ビデオ コンテンツは既に世界中でインターネット トラフィックの大半を占め、World Wide Web を席巻しています。この成長に伴い、コンテンツ検索の問題が生じています。

インターネットはテキストベースの文書を中心として誕生したため、Web 全体でテキストを検索して見つけるためのインフラストラクチャは成熟しています。一方、ビデオ ファイルは本質的に "検索不可能" なものであり、一般的には手動でタグ付けされた膨大なメタデータを中心とした複雑な分類システムが必要です。それでは、もし意味のあるメタデータを自動的に抽出する方法があったらどうでしょうか。Azure Media Indexer は、Microsoft Research の自然言語処理 (NLP) テクノロジを活用してメディア ファイルやコンテンツを検索可能にするメディア プロセッサです。キーワード ファイル (XML)、字幕ファイル一式 (SAMI/TTML)、強力なバイナリ インデックス ファイル (AIB) の形態で、意味のあるメタデータをエンド ユーザーに自動的に公開します。

マルチメディアの成長と共に、聴覚に障害のあるユーザーに対するビデオ コンテンツの使いやすさに注目が集まっています。現状では、字幕トラックを作成するには高いコストをかけてすべてのビデオの文字起こしを手動で行う必要があります。Azure Media Indexer では、音声認識エンジンにより、入力メディア ファイルの英語音声に対応する字幕トラック (時間整合済み) が自動的に作成されます。従来は多くの工数がかかっていた骨の折れる手動プロセスを、自動ジョブにすることができるのです。

開発者は Azure Media Indexer の出力ファイルと、SQL Server、Apache Lucene/Solr などの検索エンジンを組み合わせて利用し、全文検索を実現できます。ユーザーはテキスト クエリでコンテンツ ライブラリを簡単に検索でき、結果ページには単語が出現するタイムスタンプが付いています。このように、メタデータとビデオを高度に統合することで、膨大なコンテンツ ライブラリから適切な検索結果が得られるようになります。この検索レイヤーの実装については、この記事では取り上げません。Azure Media Indexer を使用してメディア ファイルの検索ポータルを作成する方法については、Azure ブログに今後投稿される記事をご覧ください。

アセットのインデックス作成

Azure Media Indexer では、ローカル ファイル システムまたはパブリック インターネットのさまざまなファイル タイプに対してインデックス作成ジョブを実行できます。初めて実践してみる Azure Media Indexer ジョブとして、ローカル ディスクのファイルを Azure Media Services にアップロードし、Azure のクラウド環境で処理してみましょう。このチュートリアルでは、サンプルとして Channel 9 のビデオ (英語) を使用します。MP4 ファイルをご自身のコンピューターに保存し、名前を Index.mp4 に変更します。このチュートリアルでは、対象のビデオ ファイルが "C:\Users\<<ユーザー名>>\Videos\Index.mp4" にあるものとします。

完成済みのサンプル プロジェクトは、こちらからダウンロードできます。

メモ : このチュートリアルでは、ユーザーが Azure Media Services アカウントを既にお持ちであることを前提としています。

プロジェクトの設定

まず、Visual Studio 2013 で新しい C# コンソール アプリケーション プロジェクトを作成します ([File] > [New] > [Project]、または Ctrl + Shift + N)。

次に、NuGet を使用して Azure Media Services SDK をインストールします。それには、ソリューション エクスプローラーでプロジェクトの References フォルダーを右クリックし、[Manage NuGet Packages] をクリックします。検索ボックスに「media services」と入力し、[Windows Azure Media Services .NET SDK] をインストールします。

最後に App.config ファイルを開き、次のように appSettings セクションを追加します。Azure Media Services の資格情報の適切なキーと値の組み合わせを入力してください。

 <?xml version="1.0" encoding="utf-8"?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>

  <!-- 以下を設定します -->-->
  <appSettings>
    <add key="accountName" value="<<YOUR_ACCT_NAME>>"/>
    <add key="accountKey" value="<<YOUR_ACCT_KEY>>"/>
  </appSettings>
  <!-- <<YOUR_ACCT_NAME>> および <<YOUR_ACCT_KEY>> を置き換えてください -->

</configuration>

アセットの作成

アセットとは、Azure Media Services のメディア ファイルを格納するコンテナーです。アセットには、メディア ファイルそのものと、ストリーミング用のマニフェスト ファイルやプレビュー用のサムネイル ファイルなどの他の必要なファイルが含まれています。このサンプルでは、.NET SDK を使用してビデオ ファイルを保持するアセット ファイルを作成します。アセットのアップロードは Azure 管理ポータルでも可能です。メディアを処理するジョブで、入力アセットを取得し、指定した出力アセットに結果を保存します。

まず依存関係をインポートし、Program.cs で使用する定数を宣言します。

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Microsoft.WindowsAzure.MediaServices.Client;
using System.Configuration;
using System.IO;
using System.Threading;

namespace MyFirstIndexingJob
{
    class Program
    {

        private static CloudMediaContext _context = null;
        private const string _mediaProcessorName = "Azure Media Indexer";
        private const string _configurationFile = "<<PLACEHOLDER>>";
        private static readonly string _accountName =
            ConfigurationManager.AppSettings["accountName"];
        private static readonly string _accountKey =
            ConfigurationManager.AppSettings["accountKey"];

Media Services クラウド環境へプログラムで接続を確立するには、CloudMediaContext オブジェクトをインスタンス化する必要があります。ファイルをアップロードするには、新規アセットを作成した後、ファイルをアセット内に AssetFile としてアップロードします。まず、Main 関数に以下の行を追加し、ビデオを探す場所と出力ファイルの場所を指定します。

メモ: このサンプル プロジェクトと同じパスを使用する場合、<<USERNAME>> をローカルの Windows ユーザー名に置き換えてください。

 static void Main(string[] args)
{
    _context = new CloudMediaContext(_accountName, _accountKey);
    var inputFile = @“C:\Users\<<USERNAME>>\Videos\Index.mp4”;
    var outputFolder = @“C:\Users\<<USERNAME>>\Desktop”;
    RunIndexingJob(inputFile, outputFolder, _configurationFile);
}

次に、RunIndexingJob 関数に以下の数行を追加します。

 static bool RunIndexingJob(string inputFilePath, string outputFolder, string configurationFile = “”)
{
    IAsset asset = _context.Assets.Create(“Indexer_Asset”, AssetCreationOptions.None);    
    var assetFile = asset.AssetFiles.Create(Path.GetFileName(inputFilePath));
    assetFile.Upload(inputFilePath);
}

インデックス作成ジョブの送信

ファイルを Azure Media Services のアセットとして作成したら、Azure Content Indexer メディア プロセッサへの参照を取得し、ジョブそのものを作成します。Media Services のジョブは、処理操作の詳細 (エンコーディング、パッケージ化など) を指定する 1 つ以上のタスクで構成されます。

オプションで、タスクそのものの詳細を指定するタスク構成ファイルを追加できます。このサンプルでは、後述する有用なメタデータを含むオプションの構成ファイル "default.config" を使用して、新規アセットでのインデックス作成タスクを作成します。

タスク構成

Azure Content Indexer のタスク構成ファイルは、音声認識の精度を高めるキーと値の組み合わせを含む XML ファイルです。Azure Media Indexer の今回のリリースでは、構成の詳細に入力メディア ファイルのタイトルと説明を記述できるため、適応型の自然言語処理エンジンによって特定の事項に基づいて語彙を簡単に補完できます。たとえば、Geico (ガイコ、米国の保険会社) に関するビデオがある場合、タスク構成ファイルにこの用語を含めておくと、固有名詞 "Geico" の代わりに "guy co" と書き起こされる可能性が低くなります。さらに、たとえばタイトルに "hypertension (高血圧)" という用語が含まれている場合、言語モデルをさらに補完する関連文書がインターネットで検索されます。これにより、"aortic aneurism (大動脈動脈瘤)" という言葉が "A or tick canner is um" のように意味不明な言葉で誤って解釈される可能性が低くなり、出力ファイルの精度が大きく向上します。

メモ: 最適な結果を得るには、title と description のキーに関連付けた 4 ~ 5 つの文を使用してください。

[Project] を右クリックして [Add] > [New Item] をクリックし、XML ファイルを選択して、新規構成ファイルを作成します。新しいファイルに以下のテキストを貼り付け、"default.config" として保存します。ここでは、精度を向上するために、Channel 9 の Web サイトの情報に基づき、オプションの "title" と "description" のキーを構成ファイルに追加します。

 <?xml version="1.0" encoding="utf-8"?>
<configuration version="2.0"> 
  <input>
    <!-- [オプション] 入力メディア ファイルのメタデータ -->
    <!-- 形式: <メタデータ キー="..." 値="..." /> -->
    <metadata key="title" value="Integrative Mom and Windows Phone App Studio" />
    <metadata key="description" value="Holly Shore discusses her experience building her own app for Integrative Mom using Windows Phone App Studio, a free, web-based app builder that can help many small business owners like Holly get up and running quickly to extend their reach with a Windows Phone app." />
  </input>  
  <settings>
  </settings>  
</configuration>

Program.cs ファイルの上部に戻り、_configurationFile の文字列 "<<PLACEHOLDER>>" を新しい default.config ファイルのパスに変更します。

ヒント: ファイルをエディターにドラッグ アンド ドロップすると、ソリューション エクスプローラーからファイルの絶対パスを簡単に貼り付けることができます。

ジョブの作成

構成ファイルの準備ができたら、RunIndexingJob メソッドでジョブとそのコンポーネント タスクの作成に取りかかります。

     IMediaProcessor indexer = GetLatestMediaProcessorByName(_mediaProcessorName);
    IJob job = _context.Jobs.Create("My Indexing Job");
    string configuration = "";
    if (!String.IsNullOrEmpty(configurationFile))
    {
        configuration = File.ReadAllText(configurationFile);
    }
    ITask task = job.Tasks.AddNew("Indexing task",
                      indexer,
                      configuration,
                      TaskOptions.None);

    // インデックスを作成する入力アセットを指定する
    task.InputAssets.Add(asset);

    // ジョブの結果を格納する出力アセットを追加する
    task.OutputAssets.AddNew("Indexed video", AssetCreationOptions.None);

次に、ファイル下部に、指定するメディア プロセッサの最新バージョンを取得するために役立つヘルパー メソッドを定義します。

 private static IMediaProcessor GetLatestMediaProcessorByName(string mediaProcessorName)
{
    var processor = _context.MediaProcessors
                .Where(p => p.Name == mediaProcessorName)
                .ToList()
                .OrderBy(p => new Version(p.Version))
                .LastOrDefault();

    if (processor == null)
        throw new ArgumentException(string.Format("Unknown media processor", mediaProcessorName));

    return processor;
}

これで、このジョブの送信準備がほぼ整いました。進行状況をリアルタイムで追跡できるように、EventHandler のインスタンスをジョブに付け加えておくと便利です。

 job.StateChanged += new EventHandler<JobStateChangedEventArgs>(StateChanged);
    job.Submit();
    // ジョブの実行をチェックし、ジョブの完了を待つ
    Task progressPrintTask = new Task(() =>
    {
        IJob jobQuery = null;
        do
        {
            var progressContext = new CloudMediaContext(_accountName, 
                            _accountKey);
            jobQuery = progressContext.Jobs.Where(j => j.Id == job.Id).First();
            Console.WriteLine(string.Format("{0}\t{1}\t{2}", 
                  DateTime.Now, 
                  jobQuery.State, 
                  jobQuery.Tasks[0].Progress));
            Thread.Sleep(10000);
        }
        while (jobQuery.State != JobState.Finished &&
               jobQuery.State != JobState.Error &&
               jobQuery.State != JobState.Canceled);
     });
     progressPrintTask.Start();

    // ジョブの実行をチェックし、ジョブの完了を待つ
    Task progressJobTask = job.GetExecutionProgressTask(CancellationToken.None);
    progressJobTask.Wait();

    // ジョブの状態がエラーである場合、
    // ジョブ進行状況のイベント処理メソッドにエラーが記録される
    // ここでエラー状態の有無をチェックし、必要な場合は終了する
    if (job.State == JobState.Error)
    {
        Console.WriteLine("Exiting method due to job error.");
        return false;
    }

    // ジョブによる出力をダウンロードする
    DownloadAsset(task.OutputAssets.First(), outputFolder);

    return true;
}

    // ヘルパー関数: ジョブの状態のイベント ハンドラー
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("Job finished. Please wait for local tasks/downloads");
            break;
        case JobState.Canceling:
        case JobState.Queued:
        case JobState.Scheduled:
        case JobState.Processing:
            Console.WriteLine("Please wait...\n");
            break;
        case JobState.Canceled:
            Console.WriteLine("Job is canceled.\n");
            break;
        case JobState.Error:
            Console.WriteLine("Job failed.\n");
            break;
        default:
            break;
    }
}

// 出力アセットをダウンロードするためのヘルパー メソッド
static void DownloadAsset(IAsset asset, string outputDirectory)
{
    foreach (IAssetFile file in asset.AssetFiles)
    {
        file.Download(Path.Combine(outputDirectory, file.Name));
    }
}

出力

インデックスを作成するファイルごとに 4 つの出力があります。

  1. 字幕ファイル (SAMI 形式)
  2. 字幕ファイル (TTML: Timed Text Markup Language 形式)
  3. キーワード ファイル (XML)
  4. SQL サーバーで使用するためのオーディオ インデックス処理 BLOB ファイル (AIB)

この記事で紹介するサンプルでは、これらすべてのファイルをローカル フォルダーにダウンロードします。個々の出力の具体的な使用方法は、今後のブログ記事で取り上げる予定です。簡単に言うと、SAMI および TTML ファイルには、ビデオの音声のテキストとそのタイムスタンプに関する構造化データが含まれており、ビデオの字幕のドラフトとして使用できます。キーワード ファイルには、アルゴリズムによって決定された入力ビデオ内のキーワードとその信頼度が格納されます。AIB ファイルには、SAMI および TTML ファイルと同じデータのバイナリ データ構造と、文字起こしの信頼度が 100% ではない単語の幅広い候補が含まれています。これにより、検索機能を充実させることができるほか、出力ファイルの精度を大幅に高めることが可能になります。

AIB ファイルを使用するには、Azure Media Indexer SQL Add-on を含む SQL Server インスタンスが必要です。

その他の情報

  • このブログ記事では、Azure Media Indexer の概要を紹介することを目的としているため、すべての活用シナリオを網羅しているわけではありません。たとえば Azure Media Indexer を使用して、マニフェスト ファイルを含むジョブを送信して複数のファイルのインデックスを作成できるようにしたり、パブリック インターネットのリモート URL からファイルのインデックスを作成したりすることもできます。
  • Media Indexer は、速度よりも精度を最適化するシナリオで有効です (入力時間が約 3 倍になります)。ほぼリアルタイムの結果を必要とするシナリオにはあまり適していません。
  • 完成済みのサンプル プロジェクトは、こちらからダウンロードできます。