Media Foundation ⑤ WebCam + WPF プロジェクトの作成とC++の実装
06 July 09 11:14 AM | hiroyuk | 0 Comments   

以前紹介したMedia Foundation の MFCaptureD3D サンプルを基に、WebCam のビデオ出力を D3DImage 経由で WPF で表示してみましょう。WPF で表示できれば、回転やスケールは思いのままですし、ブラーなどのエフェクトも容易です。

image

WPFソリューションの作成と混合アセンブリに変換

D3DImage チュートリアルを参考にして、WPFソリューションを作成し、MFCaptureD3Dプロジェクトを追加し、C++/CLIの混合アセンブリに変換します。マニフェストは明示的に追加しなくても、自動生成のもので構いません。ついでに、WPFプロジェクトの設定もしておいてください。

C++コードの編集

まず、キャプチャしたビデオを表示するウィンドウを生成し、メッセージポンプを定義している winmain.cpp を「プロジェクトから除外」します。次にD3DWrapper.cppを追加します。D3DWrapper.cppの内容は以下の通りです。初期化するときにD3Dサーフェイスとビデオの幅と高さを取得して、呼び出し元(つまりC#側)に返します。あとは、SampleメソッドでのMFReaderの非同期サンプリングの命令と、クリーンアップだけです。非同期サンプリングなので、このSampleメソッドが返っても、サンプリングは終了していません。初期化時にMFStartupを、クリーンアップ時にMFShutdownを呼び出していることにも注意してください。

#include "MFcaptureD3D.h"
#include <vcclr.h> 
using namespace System; 
CPreview* g_pPreview;

namespace MFCaptureViewer
{
  public ref class D3DWrapper 
 
  public
    IntPtr Initialize(IntPtr hwnd, int% width, int% height) 
   
      LPDIRECT3DSURFACE9 g_pd3dSurface; 
      MFStartup(MF_VERSION);
      if (SUCCEEDED(CPreview::CreateInstance((HWND)     
           hwnd.ToPointer(),
           (HWND)hwnd.ToPointer(), 
           &g_pPreview)))
      {
        // Get Surface
        g_pPreview->m_draw.m_pSwapChain->GetBackBuffer
         (0,D3DBACKBUFFER_TYPE_MONO, &g_pd3dSurface);
        // Video width & height
        width = g_pPreview->m_draw.m_width;
        height = g_pPreview->m_draw.m_height;
        return IntPtr(g_pd3dSurface);
      }
    return IntPtr::Zero; 
    }

    VOID Sample() 
   
        g_pPreview->m_pReader->ReadSample(
          (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
          0, NULL, NULL, NULL, NULL);
    }

    VOID Cleanup() 
   
       g_pPreview->Release(); 
       MFShutdown();
   
  };
}

device.h と device.cpp

device.cppでは主にD3D系の処理を行っています。まずバックバッファのD3DSrufaceとビデオの幅・高さを D3DWrapperで取得できるように、device.hで4つのプロパティをパブリックにします。

public:
    UINT m_width;  // moved to public 
    UINT m_height; // moved to public 
    IDirect3DSwapChain9 *m_pSwapChain; // moved to public 
    DrawDevice(); // moved to public

    virtual ~DrawDevice();

device.cpp では、マルチスレッドの解決、およびスワップチェーンのブリットは不要なので、次の2か所を変更します。

    hr = m_pD3D->CreateDevice(
        D3DADAPTER_DEFAULT,
        D3DDEVTYPE_HAL,
        hwnd,
        D3DCREATE_HARDWARE_VERTEXPROCESSING | 
        D3DCREATE_FPU_PRESERVE |
        D3DCREATE_MULTITHREADED, // Add
        &pp,
        &m_pDevice
        );

    // Present the frame.
    // Removed
    // hr = m_pDevice->Present(NULL, NULL, NULL, NULL);

preview.hと preview.cpp

preview.cpp では主にMF関連の処理を行っています。まず、D3DWrapperで使えるように preview.h で2つのプロパティをパブリックにします。

public:
    IMFSourceReader  *m_pReader; // moved to public 
    DrawDevice        m_draw;    // moved to public

preview.cpp では、以下の2つのメソッドを変更します。前者で追加しているのは winmain.cppで行われていたWebCamデバイスを取得するコードです。後者では、非同期のコールバック内で次のサンプリングを呼び出していたのを、WM_PAINT メッセージの送付に変更しています(必ずしもWM_PAINTでなければならないわけではありません)。WPFではこのメッセージを基にD3DImageへの書き込みを行います。灰色は変更しないコードです。

HRESULT CPreview::Initialize()
{
    HRESULT hr = S_OK;
    hr = m_draw.CreateDevice(m_hwndVideo);
   
// Add to get WebCam
    IMFActivate **ppDevices;
    UINT32  count = 0;
    IMFAttributes *pAttributes=NULL;
    hr = MFCreateAttributes(&pAttributes,1);
    hr = pAttributes->SetGUID(
      MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
      MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
    hr = MFEnumDeviceSources(pAttributes, &ppDevices, &count);
    if (count >0)
      hr = this->SetDevice(ppDevices[0]);
    // done
    return hr;
}

HRESULT CPreview::OnReadSample(
    HRESULT hrStatus,
    DWORD ,
    DWORD ,
    LONGLONG ,
    IMFSample *pSample      // Can be NULL
    )
{
    HRESULT hr = S_OK;
    IMFMediaBuffer *pBuffer = NULL;
    EnterCriticalSection(&m_critsec);
    if (FAILED(hrStatus))
    {
        hr = hrStatus;
        goto done;
    }
    if (pSample)
    {
        // Get the video frame buffer from the sample.
        hr = pSample->GetBufferByIndex(0, &pBuffer);
        if (FAILED(hr)) { goto done; }
        // Draw the frame.
        hr = m_draw.DrawFrame(pBuffer);
        if (FAILED(hr)) { goto done; }
    }
    // Add
    hr = SendMessage(m_hwndEvent, WM_PAINT, 0, 0);
    // Request the next frame.
    // Removed
    // hr = m_pReader->ReadSample(
    //    (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
    //    0,
    //    NULL,   // actual
    //    NULL,   // flags
    //    NULL,   // timestamp
    //    NULL    // sample
    //    );
done:
    if (FAILED(hr))
    {
        NotifyError(hr);
    }
    SafeRelease(&pBuffer);
    LeaveCriticalSection(&m_critsec);
    return hr;
}

ここで追加した SendMessage が非同期サンプリング時のC++とC#との同期問題を解決するカギの一つです。

この時点で、ソリューション エクスプローラーでこのMFCaptureD3Dプロジェクトを右クリックして、[プロジェクトのみ]→[MFCaptureD3Dのみをリビルド]を実行して、エラーが出ないことを確認してください。

つづく

並行ランタイムと並列パターン ライブラリのサンプル
29 June 09 01:02 PM | hiroyuk | 0 Comments   

以前、「並列プログラミング サンプル .NET 4 Beta 1」を紹介しましたが、ネイティブ Win32 のサンプルも「並行ランタイムと並列パターン ライブラリ(PPL)のコードサンプル」として公開されています。「フィボナッチ数」を求めたり、「食事をする哲学者」などのサンプルがあります。

Filed under: , ,
H.264 / AVC デコード DXVAの仕様書の更新
29 June 09 12:09 PM | hiroyuk | 0 Comments   

DXVAはビデオなどのメディアをGPUでハードウェア デコードするための Win32 API です。ダウンロードセンターに以前からあったH.264 ハードウェア デコードの仕様書が少しだけ更新されました(バグフィックス)。

WPF Toolkit (June 2009)
27 June 09 11:34 AM | hiroyuk | 0 Comments   

WPF Toolkit の June 2009 版がリリースされ、グラフ チャートが追加されました。

  • チャート コントロールの追加
  • 以下のバグ修正:
    • DataGrid.DeleteCommand が現在のセルを必要とする
    • DataGridColumn の DisplayIndex で ArgumentOutOfRange 例外
    • DatePicker の IsEnabled=False でグレーアウトしない
    • DatePicker の Enter, Escape, Spacebar 入力が編集を正しくコミット/キャンセルせず、ポップアップを閉じる
    • VSM の steady ステート アニメーションの動作失敗
RDP 7 の変更
26 June 09 10:46 AM | hiroyuk | 0 Comments   

Windows 7 の RDP(リモート デスクトップ プロトコル)では、以下のように(PDC 08の資料)多くのテクノロジーがクライアントでレンダリングされる予定でした(RCでは有効です)。

クライアントでレンダリングできれば、ホストから送られるデータが減らせるためネットワーク帯域の負荷が減らせるとともに、ホスト側の負荷も軽減できます。ホストレンダリングでは、レンダリング結果を毎フレーム 画像として送るので(もちろん何らかの圧縮は掛けますが...)、非効率なため高品質なデスクトップを生成できません。Direct3D や Direct2D のクライアント レンダリング能力は、リモートデスクトップでクライアント側にあるGPU性能がフルに活用できる興味深い仕様でした。

image

ところが、RDP チームのブログによると、Direct2DとDirect3D 10.1 のクライアント レンダリングは Windows 7 RTM 版から取り除かれたそうです(こんな感じ)。

image

しかし、RDP 7 で Aero Glass は有効ですし、GDIとメディア プレイヤーはクライアント レンダリングなので、クライアント側にある GPU 上のコンポジターはリモートで利用可能なままのようです。

しかし、RDP チームはクライアント レンダリング機能の強化を続けているので、将来のバージョンアップでは D3D10などもクライアントレンダリング可能になるのでしょう。

RDPでAero Glassを有効にするには Windows 7 のリモートデスクトップ接続のオプションからデスクトップ コンポジションを有効にする必要があります。Windows Server 2008 R2での設定については、RDPチームのブログを参照してください。

image

Media Foundation ④ WebCam + WPF 同期か非同期か
25 June 09 02:57 PM | hiroyuk | 0 Comments   

Windows 7 SDk (RC) にある Media Foundation のサンプル MFCaptureD3D を使って WPF で WebCam のビデオを表示してみましょう。D3DSurface → D3DImage → WPF という流れは、以前 D3DImage チュートリアルで紹介したとおりですが、WebCam の画像をWPFで表示するときには、同期サンプリングか非同期サンプリングかのどちらの戦略をとるかを選ばなければなりません。

非同期サンプリング

MFCaptureD3D では元々サンプリングを非同期で行っています。アトリビュート ストアでMF_SOURCE_READER_ASYNC_CALLBACKを指定し、非同期コールバックを設定しています。

// ③非同期コールバックを設定
hr = pAttributes->SetUnknown(
  MF_SOURCE_READER_ASYNC_CALLBACK,
  this
  );

サンプリングを待つ時間を他のUIスレッドで使えるので、WPFのようなアプリには非同期が理想的です。

このサンプルコードでは 非同期コールバックの場所として this が指定されているので、サンプリングが完了すると自クラスの OnReadSample メソッドが呼び出されます。このメソッドで、サンプリングした画像をD3DSurfaceに書き込んでから、C++ から C# に何らかのメッセージを送ってD3DSurfaceにビデオ画像が書き込まれたことを通知する仕組みが必要です。

同期サンプリング

以前の D3DImage チュートリアルでは、C# 側から Render メソッドで C++ の Direct3D レンダリングを実行し、それが戻ったら、ロックして AddDirtyRect メソッドでブリットしていました。同期サンプリングの戦略であれば、WebCam から画像を取得し D3DSrufaceへの書き込みを待ってブリットするという、同じ手順でレンダリングできます。

しかし、WebCam デバイスのサンプリングには待ち時間が発生します。しかも1秒間に30フレーム程度確保しようとすると、待ち時間は全体のかなりの割合を占める可能性があります。その間 UI を操作できず、パフォーマンスが悪化する可能性があります。

同期サンプリングを実装するには、アトリビュートストアに前述の非同期指定(MF_SOURCE_READER_ASYNC_CALLBACK)を入れずに、ソースリーダーを取得して、次のようにReadSampleを呼び出すだけです。同期サンプリングでは ReadSample が帰ったときにサンプル画像が取得できます(実装では取得できるまでサンプリングを繰り返します)。

DWORD dwStream;
DWORD dwFlags;
IMFSample* pSample = NULL;
LONGLONG llTimeStamp;
while( !pSample )
  {
    hr = m_pReader->ReadSample(
          (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
          0,
          &dwStream,
          &dwFlags,
          &llTimeStamp,
          &pSample
        );
   }

結論として、今回のサンプルコードでは、非同期サンプリングの戦略で実装することにしました。両方とも実装してテストしてみましたが、やはり同期サンプリングでは UI 操作の引っ掛かりが発生することと、C++ コードの修正が多くなるためです。

TechEd 2009 オープンステージ
23 June 09 12:37 PM | hiroyuk | 0 Comments   

8月26日-28日に開催されるTechEd 2009 では、オープンステージで「Media Foundation (Win32) の新機能 @ Windows 7」を話します。短い時間ですが、このブログでも紹介した、ビデオキャプチャやMFPlayの話をしようと思います。

Filed under: ,
ホームメイド マルチタッチ
20 June 09 12:17 PM | hiroyuk | 0 Comments   

世の中には、すごい人がいますね。自分で Microsoft Surface のようなマルチタッチPCを組み立てて、マルチタッチ センサーだけではなく、非接触センサーも追加してマイノリティ レポートもどきを実現しています。

http://www.wimp.com/coolcomputer/ 

Filed under: ,
Texturing & Modeling, A Procedural Approach日本語版
18 June 09 11:07 AM | hiroyuk | 0 Comments   

私の翻訳した Texturing & Modeling, A Procedural Approach, Third Edition 日本語版がボーンデジタルから出版されます。3Dモデルやテクスチャをデータからではなく、プロシージャルに生成する手法を詳細に解説した本です。

「プロシージャル」というコンピュータ用語は「手続型」というのが伝統的な訳語なのですが、アルゴリズム的にコードで生成するという意味で「手続」という言葉がしっくりこないのと、昨年のCEDEC 2008での講演でプロシージャルという言葉が用語として用いられていたので、「プロシージャル」という訳語にしました。いつものことながら、専門書の翻訳って難しいですね。

原著はこの第3版ですら2002年出版で、決して最新の書籍ではありませんが、3Dグラフィックスにおけるプロシージャル テクニックを詳しく解説した唯一無二の書籍です。著者らは、ノイズでアカデミー賞を受賞したKen Perlinをはじめ、Mandelbrotの弟子でMojoWorldの創設者であるKen Musgrave、RenderManの開発者のDarwyn Peacheyなど、そうそうたるメンバーです。

本書では特に、雲・炎・地形などの自然界に見られるものをどのようにプロシージャルに生成・レンダリングするか、発生するエイリアシングを防ぐにはどうすればよいのか、フラクタルやノイズのような再現可能なランダムさをどう発生させ、どう利用すればよいのかなどが詳細に解説されています。

プログラマとして最新の技術を学ぼうとするとき、その背景・基盤にあるアルゴリズムや概念を理解していることは非常に大切です。Game Programming Gems シリーズなどを翻訳していてつくづく思いました。リアルタイム レンダリングもそういった基盤作りのために必要な書籍だと考えて翻訳しました。

出版していただいた㈱ボーンデジタルに感謝します。Amazon からも購入できます。

目次

1章 はじめに
2章 プロシージャル テクスチャの構築
3章 リアルタイム プログラマブル シェーディング
4章 セルテクスチャ
5章 高度なアンチエイリアシング
6章 テクスチャ デザインのための実践的な手法
7章 気体のプロシージャル モデリング
8章 ソリッド空間のアニメーション
9章 陰関数を使ったボリューム雲のモデリング
10章 プロシージャル テクニックのハードウェア高速化に関する課題と戦略
11章 ジオメトリの手続き的な合成
12章 ノイズ、ハイパーテクスチャ、アンチエイリアシング、ジェスチャ
13章 リアルタイム プロシージャル ソリッド テクスチャリング
14章 フラクタルの概要紹介
15章 フラクタル ソリッド テクスチャ:いくつかの例
16章 プロシージャル フラクタル地形
17章 プロシージャル モデル用のQAEBレンダリング
18章 大気モデル
19章 遺伝的テクスチャ
20章 MOJOWORLD:手続き的な惑星の構築方法
21章 将来:電脳空間の外観の工学

Filed under:
Windows API Code Pack for Microsoft .NET Framework (v0.9)
15 June 09 02:12 PM | hiroyuk | 0 Comments   

Windows API Code Pack が v0.9 に更新されました。内容は以下の通りで、緑色がv0.85からの追加部分です。バグフィックスとともに、VBを含む新しいサンプルも追加されています。

Direct3D 10/10.1 も追加されました! D3DX10も含まれ、チュートリアルを含むサンプルもたくさん用意されているのでかなり役に立ちそうです。チュートリアルにはWinForm用、WinFormコントロール用、WPF用が用意されています。

しかしマルチタッチは含まれていません。.NET のマルチタッチについては、.NET Framework 4を待つしかないかもしれません。マルチタッチは Visual Studio 2010 Beta1 にもまだ含まれていませんでした。

  • Windows 7 タスクバー ジャンプリスト、アイコン オーバーレイ、プログレスバー、タブ サムネイル、サムネイル ツールバー
  • 既知のフォルダー、Windows 7 ライブラリ、非ファイルシステム コンテナ、シェル名前空間エンティティの階層
  • Windows 7 エクスプローラー ブラウザー コントロール
  • シェル プロパティ システム
  • Windows Vista と Windows 7 コモン ファイル ダイアログ(カスタム ファイル ダイアログを含む)
  • Windows Vista と Windows 7 のタスクダイアログ
  • Direct3D 11.0、Direct3D 10.0/10.1、DXGI 1.0/1.1、Direct2D 1.0、DirectWrite、Windows Imaging Component (WIC) API(DirectWriteとWICは部分的なサポート)
  • センサー プラットフォーム API
  • 拡張言語サービス API
  • 電源管理 API
  • アプリケーション再起動・リカバリー API
  • ネットワーク リスト マネージャ API
  • コマンドリンク コントロールとシステム定義シェル アイコン

ビルドには以下の環境が必要です。

  • .NET Framework 3.5 以上、
  • Windows SDK for Windows 7 RC
  • DirectX SDK (March 2009)
D3D10チュートリアル ビルド時の注意

Direct3Dでビルドするときには、DirectX SDKのインクルード フォルダとライブラリ フォルダを[ツール]→[オプション]から[プロジェクトおよびソリューション]→[VC++ディレクトリ]で追加する必要があります。

しかし、D3D10のサンプルをビルドしようとしたとき、なぜか「D3DX10.hが見つからない」というビルドエラーで失敗しました。Direct3DX10プロジェクトを右クリックして[プロパティ]を選び、[構成プロパティ]→[C/C++]→[全般]で、[追加のインクルード ディレクトリ]にDirectX SDKのインクルードフォルダ(C:\Program Files\Microsoft DirectX SDK (March 2009)\Include)を指定します。さらに[構成プロパティ]→[リンカ]で[追加のライブラリ ディレクトリ]にDirectX SDKライブラリフォルダ(C:\Program Files\Microsoft DirectX SDK (March 2009)\Lib\x86)を追加します。

image

D3DTutorial09_WPFの結果。ライティングが指定されていないので暗い。

Filed under: , ,
.NET Framework でのプログラミングを並列します
11 June 09 03:12 PM | hiroyuk | 0 Comments   

Visual Studio 2010 の日本語ベータ版が公開されました。それに伴い.NET Framework 4 などのドキュメントの日本語がMSDN Libraryで公開されています。ただし、機械翻訳なのでかなり読みにくいです。英語も一緒に表示されるので、変な日本語ですががまんしてお読みください。RTM版はちゃんと人間が翻訳した日本語になるはずです。

.NET Framework 4 の並列プログラミングのドキュメントの表題が今日のブログの表題「.NET Framework でのプログラミングを並列します」です。今のところ、TPL(Task Parallel Library)やPLINQ(Parellel Linq)などの日本語ドキュメントはこれしかありません。

ネイティブC++の Concurrency Runtimeの日本語ドキュメントのタイトルは「同時実行制御のランタイム」です。並行ランタイムのほうが良いと思いますが...

Filed under:
Windows 7 コミュニティ 勉強会 with Tech Fielders
10 June 09 12:13 PM | hiroyuk | 0 Comments   

INETA、Culminis 加盟コミュニティ、MVP、そしてエバンジェリストが協力して、全国で Windows 7 に関連するコミュニティ 勉強会が以下のスケジュールで開催されるそうです。それぞれの登録サイトにリンクしています。

また、「Windows 7 勉強会キット」はこちらから申し込みできます。

Filed under:
Media Foundation ③ MFCaptureD3D
09 June 09 02:15 PM | hiroyuk | 0 Comments   

Windows 7 SDK (RC) 内の Media Foundation のサンプル コードの一つに MFCaptureD3D があります。Windows 7 SDK (RC) がインストールされていれば、C:\Program Files\Microsoft SDKs\Windows\v7.0\Samples\Multimedia\MediaFoundation\MFCaptureD3D にプロジェクトはあります。このサンプル コードでは、Media FoundationでキャプチャしたWebCamの「画像」を Direct3D 9 Surface(スワップ チェーンのバックバッファ)にコピーして Direct3D 9 で表示します。

MFPlayを使った場合には、WebCam デバイスからメディア ソースを作成するだけで、1枚1枚の画像をサンプリング必要はありませんでした(MFPlayがやってくれました)。このサンプルではメディア ソースからMFReaderを生成して、画像を1枚1枚サンプリングしています。この作業は preview.cpp に実装されています。

さらにDirect3D 9 Surfaceにコピーするとき、サンプリング結果をRGB32に変換しています。現在の WebCam ではエンコード動画(ex. VC1やH.264)を出力するデバイスはないと思いますが、エンコード画像( JPEGなど)を出力するデバイスもありますし(ex. Microsoft LifeCam NX-6000)、YUVを出力するデバイスもあります(Media Foundationでのビデオ フォーマットの詳細は Video Subtype GUIDs を参照してください)。

いずれにせよDirect3D 9 Surfaceに渡すにはRGBにする必要があります。この作業は device.cpp に実装されています。このサンプル コードではこれをソフトウェアで実装しています。

ここでは Media Foundation を使って非同期で WebCam から画像をサンプリングする方法を preview.cpp を基に解説します。サンプリングに必要な手順は以下の通りです(CPreview::SetDevice()メソッド)。実際にビルドして、デバッグ実行しながらお読みください。

  • MFCreateAttribure関数を呼び出しアトリビュートストアを作成
  • MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE に MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID を指定
  • MFEnumDeviceSource 関数を呼び出し、デバイスを列挙
    [ここまでは winmain.cpp の OnChooseDeviceメソッドで実装され、SetDeviceを呼び出している]
  • IMFActivate::ActivateObjectメソッドを呼び出し、そのアクティベーション オブジェクトからメディア ソース オブジェクトを作成
  • シンボリック リンクを作成(デバイス消失のチェックに必要)
    [ここまではMFPlayを使ったときと同じ]
  1. 再度MFCreateAttributes関数を呼び出し、アトリビュートストアを作成
  2. そのアトリビュートストアでMF_READWRITE_DISABLE_CONVERTERSをTRUE にする(このサンプルコードではソフトウェアでフォーマット変換するため)。
  3. そのアトリビュートストアで非同期サンプリングを指定、MF_SOURCE_READER_ASYNC_CALLBACK を this にする。すると、サンプリングが完了したとき、このクラスの OnReadSample コールバックが呼び出される。
  4. MFCreateSourceReaderFromMediaSource関数にこのアトリビュートストアを渡し、ソースリーダー(m_pReader)を作成。
  5. ソースリーダーのGetNativeMediaTypeメソッドで取得可能なメディアタイプを取得。
  6. 変換可能なメディアタイプを選び、ソースリーダーのSetCurrentMediaTypeメソッドにそのメディアタイプを渡す。
    [TryMediaTypeで実装]

これで準備は終わりで、最後にソースリーダーのReadSampleメソッドを呼び出します。サンプリングが終了すると、前述のようにOnReadSampleコールバックが呼び出されます。

OnReadSampleではIMFSample型のサンプルが渡されるので、そのサンプルから最初のインデックスのバッファ(IMediaBuffer)をGetBudderByIndexで取得し、device.cppで実装されているDrawFrameメソッドに渡して、バッファの結果をデコード・変換・コピー・表示します(ここでは紹介しません、興味のある方はdevice.cppを参照してください)。このサンプル コードではこのコールバックの中で再度ReadSampleを呼び出し、連続的に画像を取得しています。

 

HRESULT CPreview::SetDevice(IMFActivate *pActivate)
{
    HRESULT hr = S_OK;
    IMFMediaSource  *pSource = NULL;
    IMFAttributes   *pAttributes = NULL;
    IMFMediaType    *pType = NULL;
    EnterCriticalSection(&m_critsec);
    // もしあれば現在のデバイスをリリース
    hr = CloseDevice();
    if (FAILED(hr)) { goto done; }
    // デバイス用のメディアソースを作成
    hr = pActivate->ActivateObject(
        __uuidof(IMFMediaSource), 
        (void**)&pSource
        );
    if (FAILED(hr)) { goto done; }
   // シンボリックリンクを取得
    hr = pActivate->GetAllocatedString(
     MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
        &m_pwszSymbolicLink,
        &m_cchSymbolicLink
        );
    if (FAILED(hr)) { goto done; }
    //
    // ①ソースリーダーの作成
    // 初期設定を保持するアトリビュートストアを作成
    hr = MFCreateAttributes(&pAttributes, 2);
    if (FAILED(hr)) { goto done; }

    // ②コンバーター非使用の設定
    hr = pAttributes->SetUINT32
      MF_READWRITE_DISABLE_CONVERTERS, TRUE);
    if (FAILED(hr)) { goto done; }
    // ③非同期コールバックを設定
    hr = pAttributes->SetUnknown(
    
 MF_SOURCE_READER_ASYNC_CALLBACK,
        this
        );
    if (FAILED(hr)) { goto done; }
    // ④メディア ソースの作成
    hr = MFCreateSourceReaderFromMediaSource(
        pSource,
        pAttributes,
        &m_pReader
        );
    if (FAILED(hr)) { goto done; }

    for (DWORD i = 0; ; i++)
   {
              // ⑤メディアタイプを取得
        hr = m_pReader->GetNativeMediaType(
            (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
            i,
            &pType
            );
        if (FAILED(hr)) { goto done; }
        hr = TryMediaType(pType);
              // ⑤変換可能なメディアタイプを見つける
        SafeRelease(&pType);
        if (SUCCEEDED(hr))  { break; }
    }
    if (FAILED(hr))    { goto done; }
    // ⑥最初のサンプリング開始
    hr = m_pReader->ReadSample(
        (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
        0,
        NULL,
        NULL,
        NULL,
        NULL
        );
done:
    SafeRelease(&pSource);
    SafeRelease(&pAttributes);
    SafeRelease(&pType);
    LeaveCriticalSection(&m_critsec);
    return hr;
}

MFPlayを使う場合に比べて少し複雑になりましたが(実は device.cpp 側はもっと複雑です)、これで WebCamからのビデオ画像を Direct3D 9 Surface にコピーできました。

ということは D3DImage を使えば WPF でこの WevCam の出力を表示できます。WPF で WebCam のビデオ出力が表示できれば、拡大縮小などの加工が楽になるだけではなく、WPFアプリケーションにWebCamの機能を追加できます。次回からは、これに挑戦してみましょう。

注意:Media Foundationを使うとき、初めに MFStartup(MF_VERSION) を、最後に MFShutdown() を呼び出す必要があります

Media Foundation ② WebCam キャプチャ
04 June 09 05:08 PM | hiroyuk | 0 Comments   

Media Foundation で WebCam からビデオをキャプチャしてMFPlayで表示する方法を Audio/Video Capure in Media Foundation を基に解説します。Windows 7 SDK (RC) がインストールしてあれば、サンプルコードは C:\Program Files\Microsoft SDKs\Windows\v7.0\Samples\Multimedia\MediaFoundation\SimpleCapture にあります。

手順は以下の通りです。

  1. MFCreateAttribure関数を呼び出しアトリビュートストアを作成
  2. MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE に MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID を指定
  3. MFEnumDeviceSource 関数を呼び出し、デバイスを列挙
  4. IMFActivate::ActivateObjectメソッドを呼び出し、そのアクティベーション オブジェクトからメディア ソース オブジェクトを作成
  5. シンボリック リンクを作成(デバイス ロストのチェックに必要)
  6. MFPlay プレイヤーを作成
  7. 4 で作成したメディア ソースを第1引数にして、6 で作成したMFPlayプレイヤーの CreateMediaItemFromObject メソッドを呼び出し、メディア アイテムを作成。

#define CHECK_HR(hr) { if (FAILED(hr)) { goto done; } }

HRESULT CreateVideoCaptureDevice()
{
    HRESULT hr = S_OK;
    UINT32 count = 0;

    IMFAttributes *pConfig = NULL;
    IMFActivate **ppDevices = NULL;
    IMFMediaSource *pSource = NULL;

    // ①アトリビュート ストアを作成
    CHECK_HR(hr = MFCreateAttributes(&pConfig, 1));

      // ②ビデオ キャプチャ デバイスを要求
    CHECK_HR(hr = pConfig->SetGUID(
        MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
        MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID
        ));
      // ③デバイスの列挙
    CHECK_HR(hr = MFEnumDeviceSources(
                                pConfig, &ppDevices, &count));

      // ④リスト内の最初のデバイスからメディア ソースを作成
    if (count > 0)
    {
        CHECK_HR(hr = ppDevices[0]->ActivateObject(
            __uuidof(IMFMediaSource),
            (void**)&pSource
            )); 
    }
    else
    {
        hr = MF_E_NOT_FOUND;
    }

// ⑤シンボリックリンクを作成
CHECK_HR(hr = pActivate->GetAllocatedString(    MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
    &m_pwszSymbolicLink,
    &m_cchSymbolicLink
    ));

// ⑥MFPlayプレイヤーを作成
CHECK_HR(hr = MFPCreateMediaPlayer(
     NULL,   
     FALSE,
     0,      
     NULL, 
     m_hwnd,
     &m_pPlayer
     ));

// ⑦このメディア ソース用のメディア アイテムを作成
CHECK_HR(hr = m_pPlayer->CreateMediaItemFromObject(
    pSource,
    FALSE,  
    0,
    NULL
    ));

done:
    for (DWORD i = 0; i < count; i++)
    {
        ppDevices[i]->Release();
    }
    CoTaskMemFree(ppDevices);
    SAFE_RELEASE(pSource);
    return hr;
}

注意:Media Foundationを使うとき、初めに MFStartup(MF_VERSION) を、最後に MFShutdown() を呼び出す必要があります。

Media Foundation ① MFPlay
01 June 09 01:41 PM | hiroyuk | 0 Comments   

以前紹介したように、Media Foundation は Windows 7 でかなり使いやすくなりました。今回はメディア再生APIの MFPlay の使い方をGetting Started with MFPlayを基に解説します。

MFPlayを使うとき、インクルードファイルはmfplay.h、ライブラリはmfplay.libが必要です。また、WINVERは_WIN32_WINNT_WIN7です。サンプルコードは C:\Program Files\Microsoft SDKs\Windows\v7.0\Samples\Multimedia\MediaFoundation\SimplePlay にありますので、実際にコードを見て、ビルドしてみてください。

メディアファイルの再生

メディアを再生するには、以下のようにMFPCreateMediaPlayer関数を呼び出すだけです。

IMFPMediaPlayer         *g_pPlayer = NULL;

hr = MFPCreateMediaPlayer(
    sURL,       // ファイル名
    TRUE,        // 自動的に開始するかどうか
    0,             // フラグ
    NULL,        // コールバック ポインター
    hwnd,       // 表示ウィンドウ
    &g_pPlayer
    );

  • 第1引数はファイル名(あるいはURL)
  • 第2引数はファイルロード後自動的に再生を開始するかどうかを指定する真偽値、TRUEが自動再生
  • 第3引数はオプションで、デフォルト値は0.
  • 第4引数はオプションで、コールバック インターフェイスのポインターMFPlayは非同期でメディアを処理するので、多くの場合このコールバック関数を使用
  • 第5引数はウィンドウ ハンドル。オーディオのみの場合はNULL
  • 第6引数は返されるプレイヤー オブジェクトのIMFPlayerインターフェイス、このオブジェクトを使ってスタート、ポーズなどの制御をおこなう

再生の制御

Stop(), Play(), Pause()などのIMFPMediaPlayerインターフェイスのメソッドを使う。現在の状態はGetState()で取得する。

再生レート(速度)を変更するにはSetRate()メソッドを使う。1.0が標準速度、0.5が0.5倍速、2.0が2倍速です。マイナスにすると巻き戻し再生になります。

プレイヤーのイベントを処理

以下のようにMFPCreateMediPlayerの第4引数にコールバック インターフェイス ポインターを渡すと、MediaPlayerCallback::OnMediaPlayerEvent()コールバックを実装して、プレイヤーからのイベントに対応できます。

MediaPlayerCallback     *g_pPlayerCB = NULL;

g_pPlayerCB = new (std::nothrow) MediaPlayerCallback();

hr = MFPCreateMediaPlayer(
    sURL,
    FALSE,          
    0,              
    g_pPlayerCB,    
    hwnd,          
    &g_pPlayer
    );

...

void MediaPlayerCallback::OnMediaPlayerEvent(
                                  MFP_EVENT_HEADER * pEventHeader)
{
    if (FAILED(pEventHeader->hrEvent))
    {
        ShowErrorMessage(L"Playback error", pEventHeader->hrEvent);
        return;
    }

    switch (pEventHeader->eEventType)
    {
        ...
    }
}

メディア ファイルの情報を取得

MFPlayは渡されたメディアから、IMFPMediaItemインターフェイスを公開するメディア アイテムと呼ばれるオブジェクトを生成します。明示的にCreateMediaItemFromURL()メソッドを呼び出してメディア アイテムを作成することもできます。また、SetMediaItem()メソッドを使うと、同じプレイヤーで別のメディアを再生できます。

    hr = MFPCreateMediaPlayer(
        NULL,
        FALSE,       
        0,              
        g_pPlayerCB,
        hwnd,           
        &g_pPlayer
        );
    hr = g_pPlayer->CreateMediaItemFromURL(sURL, FALSE, 0, NULL);

IMFPMediaItemインターフェイスを使うと、メディアにビデオが含まれているか、オーディオが含まれているか、メディアの尺(時間幅)などの情報を取得できます。

MFPlayの制限

  • デフォルトではストリーミングに対応していません。rtsp*, mms*, mcastプロトコルには対応していないので、その場合は Source Resolver が必要。
  • DRMで保護されたメディアに対応していません。
  • サーバーサイド プレイリストに対応していません。
  • 動作には Windows 7 以降が必要です。
More Posts Next page »

Search

Go

This Blog

Syndication

Page view tracker