SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Advanced/W-ZERO3 [es]で作ろう

Advanced/W-ZERO3 [es]でカメラアプリを作ろう

DirectShowを使ったWindows Mobile用カメラアプリケーションの構築例

  • X ポスト
  • このエントリーをはてなブックマークに追加

ダウンロード ソースコード (21.0 KB)

カメラ機能の実装

DirectShowにおけるカメラについて

 WindowsMobileのDirectShowでは、Video Capture Filterを使ってカメラの画像を取り込みます。フィルタには3つの出力ピンがあり、それぞれ役割が異なります。

図1. Video Capture Filter
図1. Video Capture Filter
表1. Video Capture Filterの出力ピン
ピンの名前 役割
Preview 画面プレビュー用
Capture 動画キャプチャ用
Still 静止画キャプチャ用

 動画を取り出せるのは、PreviewピンとCaptureピンのみです。本稿では画面に表示するだけなので、Previewピンを使うことにします。

フィルタグラフの構築

 DirectShowを使ってカメラ画像を表示するフィルタグラフを構築します。フィルタグラフのイメージを図2に示します。

図2. フィルタグラフ
図2. フィルタグラフ

 まずフィルタグラフを構築するために、キャプチャグラフビルダを用意します。

WM_Camera.h
#define SAFE_RELEASE(p) { if((p)) { (p)->Release(); (p)=NULL; }}
#define NEW_INTERNAL_COCLASS(coclass) new coclass

class WMCamera {
    IPersistPropertyBag *pPSBag;
    ICaptureGraphBuilder2 *pGB;
    IGraphBuilder  *pFG;
    IBaseFilter *pCam;
    IBaseFilter *pRender;
    IVideoWindow *pVW;
    IMediaControl *pMC;
public:
    WMCamera() {
    }
    ~WMCamera() {
    }
    void Build(HWND hwnd) {
        CoCreateInstance(CLSID_CaptureGraphBuilder, NULL
            , CLSCTX_INPROC, IID_ICaptureGraphBuilder2, (void**)&pGB);
        CoCreateInstance(CLSID_FilterGraph, NULL
            , CLSCTX_INPROC, IID_IGraphBuilder, (void**)&pFG);
        pGB->SetFiltergraph(pFG);
        pFG->QueryInterface(IID_IMediaControl, (void**)&pMC);

 次にVideo Capture Filterを作成します。手順を以下に示します。

  1. CLSID_VideoCaptureを指定してフィルタのインスタンスを作成する。
  2. カメラデバイスの名前を取得する。
  3. Video Capture Filterのプロパティにカメラデバイスの名前を設定する。

 カメラデバイスの名前はFindFirstDeviceを使って取得します。名前を取得したら、Video Capture Filterのプロパティに名前を設定します。プロパティはフィルタからIID_IPersistPropertyBagインターフェイスを取得し、Loadメソッドで設定します。このメソッドは引数にIPropertyBagインターフェイスへのポインタを渡すのですが、そのクラスは自分で実装する必要があります(後述)。

WM_Camera.h
// Build() の続き
const GUID CameraID =
{0xCB998A05,0x122C,0x4166,0x84,0x6A,0x93,0x3E,0x4D,0x7E,0x3C,0x86};
//Video Capture Filterの取得
CoCreateInstance(CLSID_VideoCapture, NULL
    , CLSCTX_INPROC, IID_IBaseFilter, (void**)&pCam);
HANDLE handle=NULL;
DEVMGR_DEVICE_INFORMATION di={sizeof(di)};
handle=FindFirstDevice(DeviceSearchByGuid, &CameraID, &di );
_variant_t var;
if(handle==NULL || di.hDevice==NULL) {
    return;
}
var=_bstr_t(di.szLegacyName);
FindClose( handle );
PropertyBag *bag=NEW_INTERNAL_COCLASS(PropertyBag);
bag->Write(L"VCapName", &var);
pCam->QueryInterface(IID_IPersistPropertyBag, (void**)&pPSBag);
pPSBag->Load(bag, NULL);
SAFE_RELEASE(bag);

 次に画像を表示するためのフィルタであるVideo Rendererを作成します。

 Video Capture FilterとVideo RendererはRenderStreamで接続します。引数に&PIN_CATEGORY_PREVIEWを指定するとPreviewピンからVideo Rendererに向かって接続されます。

WM_Camera.h
// Build() の続き
CoCreateInstance(CLSID_VideoRenderer, NULL
    , CLSCTX_INPROC, IID_IBaseFilter, (void**)&pRender);
pFG->AddFilter(pCam, L"");
pFG->AddFilter(pRender, L"");
pGB->RenderStream(&PIN_CATEGORY_PREVIEW
    , &MEDIATYPE_Video, pCam, NULL, pRender);
pMC->Run();

プロパティクラスの実装

 IPropertyBagインターフェイスを実装したクラスPropertyBagを実装します。IPropertyBagのメソッドを表2に示します。

表2. IPropertyBagのインターフェイス
名前 役割
Read 引数で指定された名前のプロパティの値を返す。
Write 引数で指定された名前のプロパティに対して、値をセットする。

 これはSTLのstd::mapを使うと簡単に実現できます。また、_bstr_t_variant_tを使えば、型もそれほど気にしなくて済みます。

stdafx.h
// 末尾に追加する
#pragma comment(lib, "comsuppw.lib")
#pragma comment(lib, "xlock.lib")
#include "propertybag.h"
propertybag.h
#pragma once
#include <map>
class PropertyBag : public IPropertyBag {
     ULONG m_RefCount;
     std::map<_bstr_t, _variant_t> m_Props;
public:
    // (IUnknownの実装は略)
    // IPropertyBag
    HRESULT STDMETHODCALLTYPE Read(LPCOLESTR pszPropName
        , VARIANT *pVar, IErrorLog *pErrorLog)
    {
        _bstr_t arg(pszPropName);
        if(m_Props.find(arg)==m_Props.end()){
            return E_INVALIDARG;
        }
        return VariantCopy(pVar, &m_Props[arg]);
    }
    HRESULT STDMETHODCALLTYPE Write(LPCOLESTR pszPropName, VARIANT *pVar) {
        m_Props.insert(
            std::map<_bstr_t, _variant_t>::value_type(_bstr_t(pszPropName)
            , _variant_t(*pVar, true)));
        return S_OK;
    }
};
std::mapを使うときの注意
 Windows Mobileでstd::mapを使うと、リンカーエラーLNK2019が発生します。これは、「xlock.lib」をリンクすると解決できます。
 

Previewピンの設定

 Previewピンは、QVGAとVGAのサイズをサポートしています。QVGAの方が若干フレームレートが高いので、RenderStreamを呼び出す直前に設定しておきます。手順を以下に示します。

  1. 出力ピンを列挙するIEnumPinsインターフェイスを取得する。
  2. Previewという名前が見つかるまでピンを列挙する。
  3. IAMStreamConfig::GetNumberOfCapabilitiesでサポートしているフォーマットの数を取得する。
  4. GetStreamCapsで画像フォーマットを取得し、サイズがQVGAであればSetFormatを呼び出して設定する。

 カメラ画像は縦長になっています。そのため、QVGAの場合は320×240ではなく240×320であることに注意してください。ちなみにVGAも480×640となります。

WM_Camera.h
// RenderStream の直前で、このメソッドを呼び出す
HRESULT SetPinProp(void) {
    // IEnumPinsインターフェイス取得
    IEnumPins *pEP=NULL;
    pCam->EnumPins(&pEP);
    IPin *pPin = NULL;
    while(pEP->Next(1,&pPin,NULL)==S_OK) {
        PIN_INFO pInfo;
        pPin->QueryPinInfo(&pInfo);
        if(lstrcmp(pInfo.achName,L"Preview")!=0){
            SAFE_RELEASE(pPin);
            continue;
        }
        // Previewピンであれば続きの処理
        IAMStreamConfig *pConfig = NULL;
        int count, size;
        pPin->QueryInterface(IID_IAMStreamConfig, (void**)&pConfig);
        pConfig->GetNumberOfCapabilities(&count,&size);
        if(size==sizeof(VIDEO_STREAM_CONFIG_CAPS)) {
            for(int i=0;i<count;i++) {
                VIDEO_STREAM_CONFIG_CAPS scc;
                AM_MEDIA_TYPE *pmtConfig=NULL;
                pConfig->GetStreamCaps(i, &pmtConfig, (BYTE*)&scc);
                if(pmtConfig->majortype==MEDIATYPE_Video &&
                    pmtConfig->formattype == FORMAT_VideoInfo)
                {
                    VIDEOINFOHEADER *vih
                        =(VIDEOINFOHEADER *)pmtConfig->pbFormat;
                    BITMAPINFOHEADER *bih=&vih->bmiHeader;
                    BITMAPINFO *bi=(BITMAPINFO*)&vih->bmiHeader;
                    if(bih->biHeight==320) {
                        //フォーマットの設定
                        pConfig->SetFormat(pmtConfig);
                    }
                }
                if(pmtConfig->cbFormat!=0) {
                    CoTaskMemFree((PVOID)pmtConfig->pbFormat);
                }
                SAFE_RELEASE(pmtConfig->pUnk);
            }
        }            
        SAFE_RELEASE(pConfig);
        SAFE_RELEASE(pPin);
    }
    SAFE_RELEASE(pEP);
    return S_OK;
}
カメラの画像フォーマットについて
 取得されたBITMAPINFOHEADERの内容をデバッガで覗くと分かりますが、RGBフォーマットではありません。
 biBitCountが12となっていること、biCompressionが0x3231d659となっていることからYUV12のようです。
 

実行方法

 ビルドして実機へ転送すると、カメラの画像が表示されます。画面左下の[OK]ボタンをタップすると終了します。

 横画面にすると表示が遅くなりますので、縦画面でご使用ください。

おわりに

 本稿では、DirectShowを使って、カメラから画像を取り込んで表示するWindows Mobile用アプリケーションを作成しました。

 Windows MobileではVideo Capture Filterを使うとカメラ画像を取り込むことができます。フィルタのプロパティに、カメラデバイスの名前を設定する方法を紹介しました。出力ピンのフォーマットを変更し、プレビューの解像度を変更できることを確認しました。

 本稿のサンプルプログラムは、プレビューするだけのシンプルなものでしたが、次回は撮影機能を実装していきたいと思います。

参考文献

  • MSDN 『Encoded Media』(英語)
  • Windows Mobile 6 SDK サンプルコード CameraCapture

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Advanced/W-ZERO3 [es]で作ろう連載記事一覧

もっと読む

この記事の著者

syu5()

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/2220 2008/08/26 13:58

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング