1st step to understand DirectShow その5 (SampleGrabber SampleCB)



 1.Callback関数を設定する

     私のような、comの知識がないものにとって、Callback関数の設定は、難しいです。

       メディアサンプルの獲得は、DirectX8のdocumentですが、

       ここの例のCallback関数をコピーすると、えらい難儀します。



     このcallback関数は、以下の通りです。

#include <streams.h>
// Strmbase.lib にリンクする

class CGrabCB: public CUnknown, public ISampleGrabberCB
{
public:
    DECLARE_IUNKNOWN;

    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv)
    {
        if( riid == IID_ISampleGrabberCB )
        {
            return GetInterface((ISampleGrabberCB*)this, ppv);
        }
        return CUnknown::NonDelegatingQueryInterface(riid, ppv);
    }

    // ISampleGrabberCB のメソッド
    STDMETHODIMP SampleCB(double SampleTime, IMediaSample *pSample)
    {
        printf("Sample time: %f\n", SampleTime);
        return S_OK;
    }

    STDMETHODIMP BufferCB(double SampleTime, BYTE *pBuffer, long BufferLen)
    {
        return E_NOTIMPL;
    }

    // コンストラクタ
    CGrabCB( ) : CUnknown("SGCB", NULL)
    { }
};

    CUnknownを継承しているのですが、このCUnknownの定義は、一体何処に?

    探し回って、みつけました。

       Windows SDK ....BaseClasses



         Streams.h も、同じdirectoryにあります。

     で、やっと、Build すると、

 エラー 1 error C3861: 'CheckPointer': 識別子が見つかりませんでした c:\documents and settings\junzo\my documents\visualc\directx\capture_graph1    
\combase.h 275 

     と、エラーがでます。

     止む無く、CheckPointerを、combase.hに、付け加えると

        #define CheckPointer(p,ret) { if ((p) == NULL) return (ret); }

     やっと、Buildが通ります。

========== ビルド: 1 正常終了、0 失敗、0 更新、0 スキップ ==========


 
 2.上記の方法は、スマートではない

   なんとか、CUnknownを使わない方法が、ないものか.... 探し回りました。

    見つけました!

     静止画ピンからの画像のキャプチャ



    ここに、スマートな方法が書いてあります。

    こうやって、探し回るのが、楽しみでも、ありますね。

class SampleGrabberCallback : public ISampleGrabberCB
{
public:
    // 参照カウントに見せかける。
    STDMETHODIMP_(ULONG) AddRef() { return 1; }
    STDMETHODIMP_(ULONG) Release() { return 2; }

    STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject)
    {
        if (NULL == ppvObject) return E_POINTER;
        if (riid == __uuidof(IUnknown))
        {
            *ppvObject = static_cast<IUnknown*>(this);
             return S_OK;
        }
        if (riid == __uuidof(ISampleGrabberCB))
        {
            *ppvObject = static_cast<ISampleGrabberCB*>(this);
             return S_OK;
        }
        return E_NOTIMPL;
    }

    STDMETHODIMP SampleCB(double Time, IMediaSample *pSample)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP BufferCB(double Time, BYTE *pBuffer, long BufferLen)
    {
        if ((g_StillMediaType.majortype != MEDIATYPE_Video) ||
            (g_StillMediaType.formattype != FORMAT_VideoInfo) ||
            (g_StillMediaType.cbFormat < sizeof(VIDEOINFOHEADER)) ||
            (g_StillMediaType.pbFormat == NULL))
        {
            return VFW_E_INVALIDMEDIATYPE;
        }
        HANDLE hf = CreateFile("C:\\Example.bmp", GENERIC_WRITE, 
        FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
        if (hf == INVALID_HANDLE_VALUE)
        {
            return E_FAIL;
        }
        long cbBitmapInfoSize = g_StillMediaType.cbFormat - SIZE_PREHEADER;
        VIDEOINFOHEADER *pVideoHeader =
           (VIDEOINFOHEADER*)g_StillMediaType.pbFormat;

        BITMAPFILEHEADER bfh;
        ZeroMemory(&bfh, sizeof(bfh));
        bfh.bfType = 'MB';  // "MB" のリトルエンディアン。
        bfh.bfSize = sizeof( bfh ) + BufferLen + cbBitmapInfoSize;
        bfh.bfOffBits = sizeof( BITMAPFILEHEADER ) + cbBitmapInfoSize;
        
        // ファイル ヘッダーを書き込む。
        DWORD dwWritten = 0;
        WriteFile( hf, &bfh, sizeof( bfh ), &dwWritten, NULL );
        WriteFile(hf, HEADER(pVideoHeader), cbBitmapInfoSize, &dwWritten, NULL);        
        WriteFile( hf, pBuffer, BufferLen, &dwWritten, NULL );
        CloseHandle( hf );
        return S_OK;

    }
};    (原文通り)


     combase.h streams.h 共に不要です...... うれしい

#include "stdafx.h"
#include "Dshow.h"

     これだけの hファイルでOKです。

     こうして、callback関数を実行したものが、最初の画像です。

     以下は、今回のプログラム全体

//Sample Grabber{C1F400A0-3F08-11D3-9F0B-006008039E37}
#include "stdafx.h"
#include "Dshow.h"


// Since we are using DX9, we cannot include qedit.h. Necessary API elements, copied below.

EXTERN_C const CLSID CLSID_SampleGrabber;
EXTERN_C const CLSID CLSID_NullRenderer;

EXTERN_C const IID IID_ISampleGrabberCB;

MIDL_INTERFACE("0579154A-2B53-4994-B0D0-E773148EFF85")

ISampleGrabberCB : public IUnknown {

public:

virtual HRESULT STDMETHODCALLTYPE SampleCB( double SampleTime,IMediaSample *pSample) = 0;

virtual HRESULT STDMETHODCALLTYPE BufferCB( double SampleTime,BYTE *pBuffer,long BufferLen) = 0;

};

EXTERN_C const IID IID_ISampleGrabber;

MIDL_INTERFACE("6B652FFF-11FE-4fce-92AD-0266B5D7C78F")

ISampleGrabber : public IUnknown {

public:

virtual HRESULT STDMETHODCALLTYPE SetOneShot( BOOL OneShot) = 0;

virtual HRESULT STDMETHODCALLTYPE SetMediaType( const AM_MEDIA_TYPE *pType) = 0;

virtual HRESULT STDMETHODCALLTYPE GetConnectedMediaType( AM_MEDIA_TYPE *pType) = 0;

virtual HRESULT STDMETHODCALLTYPE SetBufferSamples( BOOL BufferThem) = 0;

virtual HRESULT STDMETHODCALLTYPE GetCurrentBuffer(  long *pBufferSize, long *pBuffer) = 0;

virtual HRESULT STDMETHODCALLTYPE GetCurrentSample(  IMediaSample **ppSample) = 0;

virtual HRESULT STDMETHODCALLTYPE SetCallback( ISampleGrabberCB *pCallback,long WhichMethodToCallback) = 0;

};

// === END ELEMENTS FROM QEDIT.H ===


////////// Callback function class //////////////////////////////////////////////////////////////////////////////////

class CGrabCB:  public ISampleGrabberCB
{
public:
   
    // ISampleGrabberCB のメソッド
    STDMETHODIMP SampleCB(double SampleTime, IMediaSample *pSample)
    {

  //pSample->SetActualDataLength(2048);

  long x=pSample->GetActualDataLength();
        printf("Sample time: %f....", SampleTime);
  printf("Bufferlen:%ld\n",x);
  // printf("Sample time: %f\n", SampleTime);
        return S_OK;
    }

    STDMETHODIMP BufferCB(double SampleTime, BYTE *pBuffer, long BufferLen)
    {
  //printf("Sample time: %f....", SampleTime);
  //printf("Bufferlen:%ld\n",BufferLen);
        //return S_OK;

        return E_NOTIMPL;

 }

    // 参照カウントに見せかける。
    STDMETHODIMP_(ULONG) AddRef() { return 1; }
    STDMETHODIMP_(ULONG) Release() { return 2; }

    STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject)
    {
        if (NULL == ppvObject) return E_POINTER;
        if (riid == __uuidof(IUnknown))
        {
            *ppvObject = static_cast<IUnknown*>(this);
             return S_OK;
        }
        if (riid == __uuidof(ISampleGrabberCB))
        {
            *ppvObject = static_cast<ISampleGrabberCB*>(this);
             return S_OK;
        }
        return E_NOTIMPL;
    }

};


////////////////////////////////////////////////////////////////////////////////////////////

HRESULT AddToRot(IUnknown *pUnkGraph, DWORD *pdwRegister)
{
    IMoniker * pMoniker;
    IRunningObjectTable *pROT;
    if (FAILED(GetRunningObjectTable(0, &pROT))) {
        return E_FAIL;
    }
    WCHAR wsz[256];
    wsprintfW(wsz, L"FilterGraph %08p pid %08x", (DWORD_PTR)pUnkGraph, GetCurrentProcessId());
    HRESULT hr = CreateItemMoniker(L"!", wsz, &pMoniker);
    if (SUCCEEDED(hr)) {
        hr = pROT->Register(0, pUnkGraph, pMoniker, pdwRegister);
        pMoniker->Release();
    }
    pROT->Release();
    return hr;
}

void RemoveFromRot(DWORD pdwRegister)
{
    IRunningObjectTable *pROT;
    if (SUCCEEDED(GetRunningObjectTable(0, &pROT))) {
        pROT->Revoke(pdwRegister);
        pROT->Release();
    }
}

HRESULT GetUnconnectedPin(
    IBaseFilter *pFilter,   // フィルタへのポインタ。
    PIN_DIRECTION PinDir,   // 検索するピンの方向。
    IPin **ppPin)           // ピンへのポインタを受け取る。
{
    *ppPin = 0;
    IEnumPins *pEnum = 0;
    IPin *pPin = 0;
    HRESULT hr = pFilter->EnumPins(&pEnum);
    if (FAILED(hr))
    {
        return hr;
    }
    while (pEnum->Next(1, &pPin, NULL) == S_OK)
    {
        PIN_DIRECTION ThisPinDir;
        pPin->QueryDirection(&ThisPinDir);
        if (ThisPinDir == PinDir)
        {
            IPin *pTmp = 0;
            hr = pPin->ConnectedTo(&pTmp);
            if (SUCCEEDED(hr))  // 既に接続済み、必要なピンではない。
            {
                pTmp->Release();
            }
            else  // 未接続、これが必要なピンである。
            {
                pEnum->Release();
                *ppPin = pPin;
                return S_OK;
            }
        }
        pPin->Release();
    }
    pEnum->Release();
    // 一致するピンが見つからなかった。
    return E_FAIL;
}

 


HRESULT AddFilterByCLSID(
    IGraphBuilder *pGraph,  // フィルタ グラフ マネージャへのポインタ。
    const GUID& clsid,      // 作成するフィルタの CLSID。
    LPCWSTR wszName,        // フィルタの名前。
    IBaseFilter **ppF)      // フィルタへのポインタを受け取る。
{
    if (!pGraph || ! ppF) return E_POINTER;
    *ppF = 0;
    IBaseFilter *pF = 0;
    HRESULT hr = CoCreateInstance(clsid, 0, CLSCTX_INPROC_SERVER,
        IID_IBaseFilter, reinterpret_cast<void**>(&pF));
    if (SUCCEEDED(hr))
    {
        hr = pGraph->AddFilter(pF, wszName);
        if (SUCCEEDED(hr))
            *ppF = pF;
        else
            pF->Release();
    }
    return hr;
}

HRESULT ConnectFilters(
    IGraphBuilder *pGraph, // フィルタ グラフ マネージャ。
    IPin *pOut,            // アップストリーム フィルタの出力ピン。
    IBaseFilter *pDest)    // ダウンストリーム フィルタ。
{
    if ((pGraph == NULL) || (pOut == NULL) || (pDest == NULL))
    {
        return E_POINTER;
    }
#ifdef debug
        PIN_DIRECTION PinDir;
        pOut->QueryDirection(&PinDir);
        _ASSERTE(PinDir == PINDIR_OUTPUT);
#endif

    // ダウンストリーム フィルタの入力ピンを検索する。
    IPin *pIn = 0;
    HRESULT hr = GetUnconnectedPin(pDest, PINDIR_INPUT, &pIn);
    if (FAILED(hr))
    {
        return hr;
    }
    // 接続を試す。
    hr = pGraph->Connect(pOut, pIn);
    pIn->Release();
    return hr;
}


HRESULT ConnectFilters(
    IGraphBuilder *pGraph,
    IBaseFilter *pSrc,
    IBaseFilter *pDest)
{
    if ((pGraph == NULL) || (pSrc == NULL) || (pDest == NULL))
    {
        return E_POINTER;
    }

    // 最初のフィルタの出力ピンを検索する。
    IPin *pOut = 0;
    HRESULT hr = GetUnconnectedPin(pSrc, PINDIR_OUTPUT, &pOut);
    if (FAILED(hr))
    {
        return hr;
    }
    hr = ConnectFilters(pGraph, pOut, pDest);
    pOut->Release();
    return hr;
}


int _tmain(int argc, _TCHAR* argv[])
{
 
    HRESULT hr;
  
 IGraphBuilder *pGraph;

CoInitialize(NULL);


  // フィルタ グラフ マネージャを作成する。
  CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
      IID_IGraphBuilder, (void**)&pGraph);

  //////////////////////////////////////////////////////////////
   DWORD dwRegister;
#ifdef _DEBUG
hr = AddToRot(pGraph, &dwRegister);
#endif

  ////////////////////////////////////////////////////////////// 

CGrabCB *pGrabCB=new CGrabCB();
   int *buffer = NULL; //32bit=int
   long bufsize = 0;

 

  IBaseFilter *pGrabberF=0;
  hr=CoCreateInstance(CLSID_SampleGrabber,NULL,CLSCTX_INPROC_SERVER,
   IID_IBaseFilter,(void**)&pGrabberF);
  hr=pGraph->AddFilter(pGrabberF,L"Sample Grabber");

  ISampleGrabber *pGrabber=0;

  hr=pGrabberF->QueryInterface( IID_ISampleGrabber, (void**)&pGrabber );
  AM_MEDIA_TYPE mt;
  if(hr==S_OK)
  {
   ZeroMemory(&mt,sizeof(AM_MEDIA_TYPE));
   mt.majortype=MEDIATYPE_Audio;
   //mt.subtype=MEDIASUBTYPE_None;
   mt.formattype=FORMAT_WaveFormatEx ;
   pGrabber->SetMediaType(&mt);

  }

  IBaseFilter *pSrc;
  hr= pGraph->AddSourceFilter(L"mywave.wav",L"Source",&pSrc);
  if(hr!=S_OK)
  {
  }
  hr=ConnectFilters(pGraph,pSrc,pGrabberF);
  if(hr!=S_OK)
  {
  }
  IBaseFilter *pNullRenderer;
  hr=CoCreateInstance(CLSID_NullRenderer,NULL,CLSCTX_INPROC_SERVER,
   IID_IBaseFilter,(void**)&pNullRenderer);
  hr=pGraph->AddFilter(pNullRenderer,L"Null Renderer");

  hr=ConnectFilters(pGraph,pGrabberF,pNullRenderer);

  hr=pGrabber->SetOneShot(FALSE);
  hr=pGrabber->SetBufferSamples(FALSE);
  hr=pGrabber->SetCallback((ISampleGrabberCB *)pGrabCB,0);

    IMediaControl *pMediaControl = NULL;
   pGraph->QueryInterface(IID_IMediaControl, (void **)&pMediaControl);


   pMediaControl->Run();


   ::getchar();
   pMediaControl->Stop();


   delete[] buffer;
  //////////////////////////////////////////////////////////////
#ifdef _DEBUG
RemoveFromRot(dwRegister);
#endif

  //////////////////////////////////////////////////////////////


done:
  pGrabCB->Release();
  pNullRenderer->Release();
  pSrc->Release();
  pMediaControl->Release();
  pGrabber->Release();
  pGrabberF->Release();

  pGraph->Release();

CoUninitialize();
}

   H.20.3.6