前些日子在CSDN上遇到很多人问directshow是如何驱动摄象头的,看来很多人对于directshow的一些基本知识还不够清晰,于是我就写一篇文章介绍一下:
directshow的原理大概大家都知道,基本就是用微软封装的接口来实现硬件无关性,但是最终调用的接口都要在驱动层有对应的实现:
为了更清楚地演示directshow的数据传输过程,我必须说明的这个程序的基本流程。我采用的是vs2005 + windows mobile 6。0 professional 仿真模拟器,驱动层传出的是176*144格式的rgb565的数据,最后我将保存图片为RGB24的bmp图片。
说明:source filter从驱动层获取数据后一般分成两个pin将数据传出,一个是still pin用于传输静态数据,一帧的数据,一个是capture pin用于传出连续的视频数据,用RenderStream的方式gcap.pBuilder->RenderStream(&PIN_CATEGORY_PREVIEW,&MEDIATYPE_Video, gcap.pCap, NULL, gcap.pRenderP);默认会产生Smart Tee这个filter,这个filter将接收到的数据分成两份,同样也是分成两个pin传出,本例中我只用到smartTee传出的preview这个pin,连接到Render Filter以显示图象数据.
以下是主要程序部分(DDCam.cpp):
#include #include #include "streams.h" #include #include #include #include #include "ddcam.h" #include "grabber.h" #include #define MAX_LOADSTRING 100 #define WM_GRAPHNOTIFY WM_APP + 1 #define SAFE_RELEASE(x) { if (x) x->Release(); x = NULL; } #define CHK( x ) do{ if( FAILED( hr = ( x ))) { goto Cleanup; }} while( FALSE ); #define ERR( x ) do{ hr = x; goto Cleanup; } while( FALSE ); #define ARRAYSIZE(s) (sizeof(s) / sizeof(s[0])) struct _capstuff { TCHAR szCaptureFile[_MAX_PATH]; WORD wCapFileSize; ICaptureGraphBuilder2 *pBuilder; IVideoWindow *pVWS, *pVWP; IMediaEventEx *pME; IAMDroppedFrames *pDF; IAMVideoCompression *pVC; IAMVideoControl *pAMVidControl; IAMCameraControl *pCamControl; IAMVideoProcAmp *pVProcAmp; IAMStreamConfig *pConfigP; //Preview config IAMStreamConfig *pVSC; // for video cap IBaseFilter *pRenderS; //Still render IBaseFilter *pRenderP; //Preview render IBaseFilter *pCap; IGraphBuilder *pGraph; CSampleGrabber *pGrab; IFileSinkFilter *pSink; BOOL fStillGraphBuilt; BOOL fPreviewGraphBuilt; BOOL fStillCapturing; BOOL fPreviewing; } gcap; // Global Variables: HINSTANCE g_hInstance = NULL; // The current instance HWND g_hWnd; //The window instance HWND g_hWndMenu; //Menu handle HWND hWndMenuStill = NULL; // Forward declarations of functions included in this code module: ATOM MyRegisterClass (HINSTANCE, LPTSTR); BOOL InitInstance (HINSTANCE, int); LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK About (HWND, UINT, WPARAM, LPARAM); HRESULT SetCapMode(IBaseFilter *pCap); HRESULT OpenCamera(LPCOLESTR lpFile,BOOL bCapture,BOOL bStill,BOOL bPreview); BOOL WriteBMPToDisk(unsigned char *pStillImageBuffer,long size); void UpdatePictureNumber(); BOOL WriteBMPToTXT(unsigned char *pStillImageBuffer,long lBufferSize); HRESULT ActivatePreviousInstance(const TCHAR* pszClass,const TCHAR* pszTitle,BOOL* pfActivated); int g_PicNumber=0; HRESULT Callback( IMediaSample * pSample, REFERENCE_TIME * StartTime, REFERENCE_TIME * StopTime,BOOL TypeChanged ) { unsigned char *pbuf; HRESULT hr = S_OK; // NOTE: We cannot do anything with this sample until we call GetConnectedMediaType // on the filter to find out what format these samples are. RETAILMSG(1, (TEXT("Callback with sample %lx for time %ld"), pSample, long( *StartTime / 10000 ) ) ); hr = pSample->GetPointer(&pbuf); LONG lSize = pSample->GetActualDataLength(); BOOL bReturn = WriteBMPToDisk(pbuf,lSize); WriteBMPToTXT(pbuf,lSize); if(bReturn == FALSE) { return S_FALSE; } return hr ; } BOOL StartPreview() { HRESULT hr; IMediaControl *pMC = NULL; hr = gcap.pGraph->QueryInterface(IID_IMediaControl, (void **)&pMC); if(SUCCEEDED(hr)) { hr = pMC->Run(); if(FAILED(hr)) { // stop parts that ran pMC->Stop(); } pMC->Release(); } if(FAILED(hr)) { return FALSE; } return TRUE; } // stop the preview graph // BOOL StopPreview() { // way ahead of you if(!gcap.fPreviewing) { return FALSE; } // stop the graph IMediaControl *pMC = NULL; HRESULT hr = gcap.pGraph->QueryInterface(IID_IMediaControl, (void **)&pMC); if(SUCCEEDED(hr)) { hr = pMC->Stop(); pMC->Release(); } if(FAILED(hr)) { return FALSE; } gcap.fPreviewing = FALSE; return TRUE; } BOOL CloseCamera() { SAFE_RELEASE(gcap.pCap); SAFE_RELEASE(gcap.pConfigP); SAFE_RELEASE(gcap.pVWS); SAFE_RELEASE(gcap.pVWP); SAFE_RELEASE(gcap.pGraph); SAFE_RELEASE(gcap.pBuilder); return TRUE; } HRESULT CaptureStillImage() { HRESULT hr; hr = SetCapMode(gcap.pCap); //Run still pin return hr; } HRESULT InitCapFilter() { HRESULT hr = S_OK; GUID clsid = DEVCLASS_CAMERA_GUID; IPersistPropertyBag *pPropertyBag = NULL; // Create Capture Filter CHK( hr = CoCreateInstance(CLSID_VideoCapture, NULL, CLSCTX_INPROC_SERVER,IID_IBaseFilter, (void **)&gcap.pCap) ); DEVMGR_DEVICE_INFORMATION pdi; HANDLE hand = FindFirstDevice(DeviceSearchByGuid,&clsid,&pdi); RETAILMSG(1, (TEXT("CamTest: Find device: %x %x/r/n"),hand,pdi.szDeviceName)); CHK( hr = gcap.pCap->QueryInterface(IID_IPersistPropertyBag, (void **)&pPropertyBag) ); if (!SUCCEEDED(hr)) { return hr; } VARIANT varCamName; IPropertyBag *propBag = NULL; varCamName.byref = L"CAM1:" ; CHK( hr = pPropertyBag->Load(propBag,NULL) ); SAFE_RELEASE(pPropertyBag); Cleanup: if(FAILED(hr)) { OutputDebugString(L"Initial Error!"); SendMessage(g_hWnd,WM_CLOSE,0,0); } return hr; } HRESULT SetupVideoWindow(IVideoWindow *pVW) { HRESULT hr = S_OK; if (pVW) { CHK( hr = pVW->SetWindowPosition(0,0,240,268) ); CHK( hr = pVW->put_Owner((OAHWND)g_hWnd) ); CHK( hr = pVW->put_WindowStyle(WS_CHILD) ); } Cleanup: if(FAILED(hr)) { OutputDebugString(L"Setup window Error!"); } return hr; } HRESULT ConnectFilters(IGraphBuilder *pGraph,IBaseFilter *pF1, int iPin1,IBaseFilter *pF2,int iPin2,IPin **ppPinout) { IPin *pPin1, *pPin2; IEnumPins *pEnum; unsigned long fetched; HRESULT hr = S_OK; hr = pF1->EnumPins(&pEnum); while (iPin1>0) { hr = pEnum->Next(1,&pPin1,&fetched); //Skip Capture pin iPin1--; } hr = pEnum->Next(1,&pPin1,&fetched); hr = pF2->EnumPins(&pEnum); while (iPin2>0) { hr = pEnum->Next(1,&pPin2,&fetched); //Skip Capture pin iPin2--; } hr = pEnum->Next(1,&pPin2,&fetched); hr = pGraph->Connect(pPin1,pPin2); if (ppPinout) { *ppPinout = pPin1; } if (!SUCCEEDED(hr)) RETAILMSG(1, (TEXT("CamTest: Fail to Connect Pin! %x/r/n"),hr)); return hr; } HRESULT BuildGraph() { HRESULT hr; gcap.pGrab = new CSampleGrabber(NULL,&hr,FALSE); gcap.pGrab->AddRef(); gcap.pGrab->SetCallback(&Callback); CMediaType mt; mt.SetType(&MEDIATYPE_Video); mt.SetSubtype(&MEDIASUBTYPE_RGB24); gcap.pGrab->SetAcceptedMediaType(&mt); // Create the Filter Graph Manager. hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&gcap.pGraph); // Create the Capture Graph Builder. hr = CoCreateInstance(CLSID_CaptureGraphBuilder, NULL, CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (void **)&gcap.pBuilder); hr = gcap.pGraph->AddFilter(gcap.pCap,L"Video Capture Source"); hr = gcap.pGraph->AddFilter(gcap.pGrab,L"SampleGrabber"); gcap.pBuilder->SetFiltergraph(gcap.pGraph); hr = CoCreateInstance(CLSID_VideoRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&gcap.pRenderP); hr = CoCreateInstance(CLSID_VideoRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&gcap.pRenderS); hr = gcap.pGraph->AddFilter(gcap.pRenderP,L"Video Render"); hr = gcap.pGraph->AddFilter(gcap.pRenderS,L"Video Render"); hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_PREVIEW,&MEDIATYPE_Video, gcap.pCap, NULL, gcap.pRenderP); hr = gcap.pRenderP->QueryInterface(IID_IVideoWindow, (void**)&gcap.pVWP); hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_STILL,&MEDIATYPE_Video, gcap.pCap, gcap.pGrab, gcap.pRenderS); // Query for video interfaces, which may not be relevant for audio files //hr = gcap.pGraph->QueryInterface(IID_IMediaEventEx, (void **)&gcap.pME); //hr = gcap.pCap->QueryInterface(IID_IAMVideoProcAmp,(void **)&gcap.pVProcAmp); //// Query the output pin for IAMStreamConfig (not shown). //hr = gcap.pBuilder->FindInterface( // &PIN_CATEGORY_PREVIEW, // Preview pin. // 0, // Any media type. // gcap.pCap, // Pointer to the capture filter. // IID_IAMStreamConfig, (void**)&gcap.pConfigP); // Have the graph signal event via window callbacks for performance SetupVideoWindow(gcap.pVWP); gcap.pVWP->put_MessageDrain((OAHWND)g_hWnd); gcap.pVWP->put_Owner((OAHWND)g_hWnd); //hr = gcap.pME->SetNotifyWindow((OAHWND)g_hWnd, WM_GRAPHNOTIFY, 0); return hr; } HRESULT SetCapMode(IBaseFilter *pCap) { HRESULT hr; IPin *pPin = NULL; hr = gcap.pCap->FindPin(L"Still",&pPin); if (SUCCEEDED(hr)) { hr = gcap.pCap->QueryInterface(IID_IAMVideoControl,(void **)&gcap.pAMVidControl); hr = gcap.pAMVidControl->SetMode(pPin, VideoControlFlag_Trigger); MessageBox(NULL,L"拍照成功,生成的图片保存在根目录下",L"成功",64); pPin->Release(); } else { RETAILMSG(1, (TEXT("CamTest: Fail to Find Pin! %x/r/n"),hr)); } return hr; } int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { MSG msg; HACCEL hAccelTable; //Init COM // Get COM interfaces if(FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED))) { RETAILMSG(1, (TEXT("CoInitialize Failed!/r/n"))); return FALSE; } // Perform application initialization: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_WCETEST); // Main message loop: while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } // Finished with COM CoUninitialize(); return msg.wParam; } // // FUNCTION: InitInstance(HANDLE, int) // // PURPOSE: Saves instance handle and creates main window // // COMMENTS: // // In this function, we save the instance handle in a global variable and // create and display the main program window. // BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HRESULT hr; BOOL fActivated; TCHAR szTitle[MAX_LOADSTRING]; // The title bar text TCHAR szWindowClass[MAX_LOADSTRING]; // The window class name g_hInstance = hInstance; // Store instance handle in our global variable // Initialize global strings LoadString(hInstance, IDC_WCETEST, szWindowClass, MAX_LOADSTRING); WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WCETEST)); wc.hCursor = 0; wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); wc.lpszMenuName = 0; wc.lpszClassName = szWindowClass; RegisterClass(&wc); LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); if(FAILED(ActivatePreviousInstance(szWindowClass, szTitle, &fActivated)) || fActivated) { return(0); } g_hWnd = CreateWindow(szWindowClass, szTitle, WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), NULL, NULL, hInstance, NULL); if (!g_hWnd) { return FALSE; } ShowWindow(g_hWnd, nCmdShow); UpdateWindow(g_hWnd); hr = InitCapFilter(); if (SUCCEEDED(hr)) { BuildGraph(); StartPreview(); } else { RETAILMSG(1,(TEXT("CamTest: Fail to create Capture filter. /r/n"))); } return TRUE; } /************************************************************************************** OnCreate **************************************************************************************/ LRESULT OnCreate( HWND hwnd, CREATESTRUCT* lParam ) { // create the menu bar SHMENUBARINFO mbi; ZeroMemory(&mbi, sizeof(SHMENUBARINFO)); mbi.cbSize = sizeof(SHMENUBARINFO); mbi.hwndParent = hwnd; mbi.nToolBarId = IDM_MENU; mbi.hInstRes = g_hInstance; mbi.dwFlags = SHCMBF_HMENU; if(!SHCreateMenuBar(&mbi)) { // Couldn't create the menu bar. Fail creation of the window. return(-1); } return(0); // continue creation of the window } // FUNCTION: WndProc(HWND, unsigned, WORD, LONG) // // PURPOSE: Processes messages for the main window. // // WM_COMMAND - process the application menu // WM_PAINT - Paint the main window // WM_DESTROY - post a quit message and return // // /************************************************************************************** WndProc **************************************************************************************/ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { LRESULT lResult = TRUE; switch(message) { case WM_CLOSE: StopPreview(); CloseCamera(); DestroyWindow(hWnd); break; case WM_CREATE: lResult = OnCreate(hWnd, (CREATESTRUCT*)lParam); break; case WM_COMMAND: switch (wParam) { case ID_CAPTURE: CaptureStillImage(); break; } break; case WM_DESTROY: PostQuitMessage(0); break; default: lResult = DefWindowProc(hWnd, message, wParam, lParam); break; } return(lResult); } BOOL WriteBMPToTXT(unsigned char *pStillImageBuffer,long lBufferSize) { TCHAR x[256]; const TCHAR *picture_path = TEXT("//My Documents//My Pictures") ; UpdatePictureNumber(); wsprintf(x, TEXT("%s//%d.txt"), picture_path, g_PicNumber++); HANDLE hf = CreateFile(x,GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS,NULL,NULL); if(hf == INVALID_HANDLE_VALUE) return FALSE; DWORD dwWritten=0; if( !WriteFile(hf,pStillImageBuffer,lBufferSize,&dwWritten,NULL) ) { return FALSE; } CloseHandle(hf); return TRUE; } BOOL WriteBMPToDisk(unsigned char *pStillImageBuffer,long lBufferSize)//保存为24位的图片 { TCHAR x[256]; UpdatePictureNumber(); wsprintf(x, TEXT("%d.bmp"), g_PicNumber++); HANDLE hf = CreateFile(x,GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS,NULL,NULL); if(hf == INVALID_HANDLE_VALUE) return FALSE; BITMAPFILEHEADER bfh; memset(&bfh,0,sizeof(bfh)); bfh.bfType=0x4D42/*((WORD) ('M' << 8) | 'B')*/; bfh.bfSize=sizeof(bfh)+lBufferSize+sizeof(BITMAPFILEHEADER); bfh.bfOffBits=sizeof(BITMAPINFOHEADER)+sizeof(BITMAPFILEHEADER); DWORD dwWritten=0; WriteFile(hf,&bfh,sizeof(bfh),&dwWritten,NULL); BITMAPINFOHEADER bih; memset(&bih,0,sizeof(bih)); bih.biSize=sizeof(bih); bih.biWidth=144; bih.biHeight=176; bih.biPlanes=1; bih.biBitCount=24; if( !WriteFile(hf,&bih,sizeof(bih),&dwWritten,NULL) ) { return FALSE; } if( !WriteFile(hf,pStillImageBuffer,lBufferSize,&dwWritten,NULL) ) { return FALSE; } CloseHandle(hf); return TRUE; } // //// Look for cam.cfg //// If it doesn't exist, create it, and set picture number to 1. //// If it exists, read the value stored inside, increment the number, and write it back. void UpdatePictureNumber() { DWORD dwSize; HANDLE hFile; char *buffer; buffer = (char *)malloc(1024); hFile = CreateFile(TEXT("//temp//cam.cfg"), GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); dwSize = 0; if (hFile == INVALID_HANDLE_VALUE) { // File did not exist, so we are going to create it, and initialize the counter. g_PicNumber = 1; hFile = CreateFile(TEXT("//temp//cam.cfg"), GENERIC_WRITE | GENERIC_READ, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); buffer[0] = g_PicNumber & 0x00FF; buffer[1] = (g_PicNumber & 0xFF00) >> 8; WriteFile(hFile, buffer, 2, &dwSize, NULL); CloseHandle(hFile); } else { dwSize = 0; ReadFile(hFile, buffer, 2, &dwSize, NULL); g_PicNumber = buffer[1]; g_PicNumber <<= 8; g_PicNumber |= buffer[0]; g_PicNumber++; CloseHandle(hFile); hFile = CreateFile(TEXT("//temp//cam.cfg"), GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); buffer[0] = g_PicNumber & 0x00FF; buffer[1] = (g_PicNumber & 0xFF00) >> 8; dwSize = 0; WriteFile(hFile, buffer, 2, &dwSize, NULL); CloseHandle(hFile); } free(buffer); } /**************************************************************************** ActivatePreviousInstance ****************************************************************************/ HRESULT ActivatePreviousInstance( const TCHAR* pszClass, const TCHAR* pszTitle, BOOL* pfActivated ) { HRESULT hr = S_OK; int cTries; HANDLE hMutex = NULL; *pfActivated = FALSE; cTries = 5; while(cTries > 0) { hMutex = CreateMutex(NULL, FALSE, pszClass); // NOTE: We don't want to own the object. if(NULL == hMutex) { // Something bad happened, fail. hr = E_FAIL; goto Exit; } if(GetLastError() == ERROR_ALREADY_EXISTS) { HWND hwnd; CloseHandle(hMutex); hMutex = NULL; // There is already an instance of this app // running. Try to bring it to the foreground. hwnd = FindWindow(pszClass, pszTitle); if(NULL == hwnd) { // It's possible that the other window is in the process of being created... Sleep(500); hwnd = FindWindow(pszClass, pszTitle); } if(NULL != hwnd) { // Set the previous instance as the foreground window // The "| 0x01" in the code below activates // the correct owned window of the // previous instance's main window. SetForegroundWindow((HWND) (((ULONG) hwnd) | 0x01)); // We are done. *pfActivated = TRUE; break; } // It's possible that the instance we found isn't coming up, // but rather is going down. Try again. cTries--; } else { // We were the first one to create the mutex // so that makes us the main instance. 'leak' // the mutex in this function so it gets cleaned // up by the OS when this instance exits. break; } } if(cTries <= 0) { // Someone else owns the mutex but we cannot find // their main window to activate. hr = E_FAIL; goto Exit; } Exit: return(hr); } void setscreenMetrics(HWND hWnd,int width,int height) { DEVMODE lpDevMode; lpDevMode.dmBitsPerPel=24; lpDevMode.dmPelsWidth=width; lpDevMode.dmPelsHeight=height; lpDevMode.dmSize=sizeof(lpDevMode); lpDevMode.dmFields=DM_PELSWIDTH|DM_PELSHEIGHT; LONG result; result=ChangeDisplaySettingsEx(NULL,&lpDevMode,hWnd,0,NULL); if(result==DISP_CHANGE_SUCCESSFUL) { MessageBoxW(hWnd,_T("success!"),_T("alert"),MB_OK); } else { MessageBoxW(hWnd,_T("failure!"),_T("alert"),MB_OK); } }
主要构建Graph的代码:
HRESULT BuildGraph()
{
HRESULT hr;
gcap.pGrab = new CSampleGrabber(NULL,&hr,FALSE);
gcap.pGrab->AddRef();
gcap.pGrab->SetCallback(&Callback);
CMediaType mt;
mt.SetType(&MEDIATYPE_Video);
mt.SetSubtype(&MEDIASUBTYPE_RGB24);
gcap.pGrab->SetAcceptedMediaType(&mt);
// Create the Filter Graph Manager.
hr = CoCreateInstance(CLSID_FilterGraph, NULL,
CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&gcap.pGraph);
// Create the Capture Graph Builder.
hr = CoCreateInstance(CLSID_CaptureGraphBuilder, NULL,
CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2,
(void **)&gcap.pBuilder);
hr = gcap.pGraph->AddFilter(gcap.pCap,L"Video Capture Source");
hr = gcap.pGraph->AddFilter(gcap.pGrab,L"SampleGrabber");
gcap.pBuilder->SetFiltergraph(gcap.pGraph);
hr = CoCreateInstance(CLSID_VideoRenderer, NULL,
CLSCTX_INPROC_SERVER, IID_IBaseFilter,
(void **)&gcap.pRenderP);
hr = CoCreateInstance(CLSID_VideoRenderer, NULL,
CLSCTX_INPROC_SERVER, IID_IBaseFilter,
(void **)&gcap.pRenderS);
hr = gcap.pGraph->AddFilter(gcap.pRenderP,L"Video Render");
hr = gcap.pGraph->AddFilter(gcap.pRenderS,L"Video Render");
hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_PREVIEW,&MEDIATYPE_Video, gcap.pCap, NULL, gcap.pRenderP);
hr = gcap.pRenderP->QueryInterface(IID_IVideoWindow, (void**)&gcap.pVWP);
hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_STILL,&MEDIATYPE_Video, gcap.pCap, gcap.pGrab, gcap.pRenderS);
// Query for video interfaces, which may not be relevant for audio files
//hr = gcap.pGraph->QueryInterface(IID_IMediaEventEx, (void **)&gcap.pME);
//hr = gcap.pCap->QueryInterface(IID_IAMVideoProcAmp,(void **)&gcap.pVProcAmp);
//// Query the output pin for IAMStreamConfig (not shown).
//hr = gcap.pBuilder->FindInterface(
// &PIN_CATEGORY_PREVIEW, // Preview pin.
// 0, // Any media type.
// gcap.pCap, // Pointer to the capture filter.
// IID_IAMStreamConfig, (void**)&gcap.pConfigP);
// Have the graph signal event via window callbacks for performance
SetupVideoWindow(gcap.pVWP);
gcap.pVWP->put_MessageDrain((OAHWND)g_hWnd);
gcap.pVWP->put_Owner((OAHWND)g_hWnd);
//hr = gcap.pME->SetNotifyWindow((OAHWND)g_hWnd, WM_GRAPHNOTIFY, 0);
return hr;
}
另外SampleGrabber这个filter是要一个transform filter,可以在 directx 的directshow sample里找到,主要代码如下(Grabber.cpp):
//------------------------------------------------------------------------------ // File: Grabber.cpp // // Desc: DirectShow sample code - Implementation file for the SampleGrabber // example filter // // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ #include // Active Movie (includes windows.h) #include // declares DEFINE_GUID to declare an EXTERN_C const. #include "grabber.h" //#pragma warning(disable: 4800) const AMOVIESETUP_PIN psudSampleGrabberPins[] = { { L"Input" // strName , FALSE // bRendered , FALSE // bOutput , FALSE // bZero , FALSE // bMany , &CLSID_NULL // clsConnectsToFilter , L"" // strConnectsToPin , 0 // nTypes , NULL // lpTypes } , { L"Output" // strName , FALSE // bRendered , TRUE // bOutput , FALSE // bZero , FALSE // bMany , &CLSID_NULL // clsConnectsToFilter , L"" // strConnectsToPin , 0 // nTypes , NULL // lpTypes } }; const AMOVIESETUP_FILTER sudSampleGrabber = { &CLSID_GrabberSample // clsID , L"SampleGrabber Example" // strName , MERIT_DO_NOT_USE // dwMerit , 2 // nPins , psudSampleGrabberPins }; // lpPin // Needed for the CreateInstance mechanism CFactoryTemplate g_Templates[]= { { L"Sample Grabber Example" , &CLSID_GrabberSample , CSampleGrabber::CreateInstance , NULL , &sudSampleGrabber } }; int g_cTemplates = sizeof(g_Templates)/sizeof(g_Templates[0]); //////////////////////////////////////////////////////////////////////// // // Exported entry points for registration and unregistration // (in this case they only call through to default implementations). // //////////////////////////////////////////////////////////////////////// STDAPI DllRegisterServer() { return AMovieDllRegisterServer2(TRUE); } STDAPI DllUnregisterServer() { return AMovieDllRegisterServer2(FALSE); } // // DllMain // extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID); BOOL WINAPI DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) { return DllEntryPoint((HINSTANCE)(hModule), dwReason, lpReserved); } // // CreateInstance // // Provide the way for COM to create a CSampleGrabber object // CUnknown * WINAPI CSampleGrabber::CreateInstance(LPUNKNOWN punk, HRESULT *phr) { ASSERT(phr); // assuming we don't want to modify the data CSampleGrabber *pNewObject = new CSampleGrabber(punk, phr, FALSE); if(pNewObject == NULL) { if (phr) *phr = E_OUTOFMEMORY; } return pNewObject; } // CreateInstance //---------------------------------------------------------------------------- // //---------------------------------------------------------------------------- CSampleGrabber::CSampleGrabber( IUnknown * pOuter, HRESULT * phr, BOOL ModifiesData ) : CTransInPlaceFilter( TEXT("SampleGrabber"), (IUnknown*) pOuter, //CLSID_GrabberSample, phr, (BOOL)ModifiesData ) CLSID_GrabberSample, phr) , m_callback( NULL ) { // this is used to override the input pin with our own m_pInput = (CTransInPlaceInputPin*) new CSampleGrabberInPin( this, phr ); if( !m_pInput ) { if (phr) *phr = E_OUTOFMEMORY; } // Ensure that the output pin gets created. This is necessary because our // SetDeliveryBuffer() method assumes that the input/output pins are created, but // the output pin isn't created until GetPin() is called. The // CTransInPlaceFilter::GetPin() method will create the output pin, since we // have not already created one. IPin *pOutput = GetPin(1); // The pointer is not AddRef'ed by GetPin(), so don't release it } STDMETHODIMP CSampleGrabber::NonDelegatingQueryInterface( REFIID riid, void ** ppv) { CheckPointer(ppv,E_POINTER); if(riid == IID_IGrabberSample) { return GetInterface((IGrabberSample *) this, ppv); } else { return CTransInPlaceFilter::NonDelegatingQueryInterface(riid, ppv); } } //---------------------------------------------------------------------------- // This is where you force the sample grabber to connect with one type // or the other. What you do here is crucial to what type of data your // app will be dealing with in the sample grabber's callback. For instance, // if you don't enforce right-side-up video in this call, you may not get // right-side-up video in your callback. It all depends on what you do here. //---------------------------------------------------------------------------- HRESULT CSampleGrabber::CheckInputType( const CMediaType * pmt ) { CheckPointer(pmt,E_POINTER); CAutoLock lock( &m_Lock ); // if the major type is not set, then accept anything GUID g = *m_mtAccept.Type( ); if( g == GUID_NULL ) { return NOERROR; } // if the major type is set, don't accept anything else if( g != *pmt->Type( ) ) { return VFW_E_INVALID_MEDIA_TYPE; } // subtypes must match, if set. if not set, accept anything g = *m_mtAccept.Subtype( ); if( g == GUID_NULL ) { return NOERROR; } if( g != *pmt->Subtype( ) ) { return VFW_E_INVALID_MEDIA_TYPE; } // format types must match, if one is set g = *m_mtAccept.FormatType( ); if( g == GUID_NULL ) { return NOERROR; } if( g != *pmt->FormatType( ) ) { return VFW_E_INVALID_MEDIA_TYPE; } // at this point, for this sample code, this is good enough, // but you may want to make it more strict return NOERROR; } //---------------------------------------------------------------------------- // This bit is almost straight out of the base classes. // We override this so we can handle Transform( )'s error // result differently. //---------------------------------------------------------------------------- HRESULT CSampleGrabber::Receive( IMediaSample * pms ) { CheckPointer(pms,E_POINTER); HRESULT hr; AM_SAMPLE2_PROPERTIES * const pProps = m_pInput->SampleProps(); RETAILMSG(1, (TEXT("Grabber: Receive! %x/r/n"))); if (pProps->dwStreamId != AM_STREAM_MEDIA) { if( m_pOutput->IsConnected() ) return m_pOutput->Deliver(pms); else return NOERROR; } /* if (UsingDifferentAllocators()) { // We have to copy the data. pms = Copy(pms); if (pms == NULL) { return E_UNEXPECTED; } } */ // have the derived class transform the data hr = Transform(pms); if (FAILED(hr)) { // DbgLog((LOG_TRACE, 1, TEXT("Error from TransInPlace"))); /* if (UsingDifferentAllocators()) { pms->Release(); } */ return hr; } if (hr == NOERROR) { hr = m_pOutput->Deliver(pms); } // release the output buffer. If the connected pin still needs it, // it will have addrefed it itself. /* if (UsingDifferentAllocators()) { pms->Release(); } */ return hr; } //---------------------------------------------------------------------------- // Transform //---------------------------------------------------------------------------- HRESULT CSampleGrabber::Transform ( IMediaSample * pms ) { CheckPointer(pms,E_POINTER); CAutoLock lock( &m_Lock ); RETAILMSG(1, (TEXT("Grabber: Transform! %x/r/n"))); if( m_callback ) { REFERENCE_TIME StartTime, StopTime; pms->GetTime( &StartTime, &StopTime); StartTime += m_pInput->CurrentStartTime( ); StopTime += m_pInput->CurrentStartTime( ); BOOL * pTypeChanged = &((CSampleGrabberInPin*) m_pInput)->m_bMediaTypeChanged; HRESULT hr = m_callback( pms, &StartTime, &StopTime, *pTypeChanged ); *pTypeChanged = FALSE; // now that we notified user, we can clear it return hr; } return NOERROR; } //---------------------------------------------------------------------------- // SetAcceptedMediaType //---------------------------------------------------------------------------- STDMETHODIMP CSampleGrabber::SetAcceptedMediaType( const CMediaType * pmt ) { CAutoLock lock( &m_Lock ); if( !pmt ) { m_mtAccept = CMediaType( ); return NOERROR; } HRESULT hr = TRUE; CopyMediaType( &m_mtAccept, pmt ); return hr; } //---------------------------------------------------------------------------- // GetAcceptedMediaType //---------------------------------------------------------------------------- STDMETHODIMP CSampleGrabber::GetConnectedMediaType( CMediaType * pmt ) { if( !m_pInput || !m_pInput->IsConnected( ) ) { return VFW_E_NOT_CONNECTED; } return m_pInput->ConnectionMediaType( pmt ); } //---------------------------------------------------------------------------- // SetCallback //---------------------------------------------------------------------------- STDMETHODIMP CSampleGrabber::SetCallback( SAMPLECALLBACK Callback ) { CAutoLock lock( &m_Lock ); m_callback = Callback; return NOERROR; } //---------------------------------------------------------------------------- // inform the input pin of the allocator buffer we wish to use. See the // input pin's SetDeliverBuffer method for comments. //---------------------------------------------------------------------------- STDMETHODIMP CSampleGrabber::SetDeliveryBuffer( ALLOCATOR_PROPERTIES props, BYTE * m_pBuffer ) { // have the input/output pins been created? if( !InputPin( ) || !OutputPin( ) ) { return E_POINTER; } // they can't be connected if we're going to be changing delivery buffers // if( InputPin( )->IsConnected( ) || OutputPin( )->IsConnected( ) ) { return E_INVALIDARG; } return ((CSampleGrabberInPin*)m_pInput)->SetDeliveryBuffer( props, m_pBuffer ); } //---------------------------------------------------------------------------- // used to help speed input pin connection times. We return a partially // specified media type - only the main type is specified. If we return // anything BUT a major type, some codecs written improperly will crash //---------------------------------------------------------------------------- HRESULT CSampleGrabberInPin::GetMediaType( int iPosition, CMediaType * pMediaType ) { CheckPointer(pMediaType,E_POINTER); if (iPosition < 0) { return E_INVALIDARG; } if (iPosition > 0) { return VFW_S_NO_MORE_ITEMS; } mt=*pMediaType; *pMediaType = CMediaType( ); pMediaType->SetType( ((CSampleGrabber*)m_pFilter)->m_mtAccept.Type()); return S_OK; } //---------------------------------------------------------------------------- // override the CTransInPlaceInputPin's method, and return a new enumerator // if the input pin is disconnected. This will allow GetMediaType to be // called. If we didn't do this, EnumMediaTypes returns a failure code // and GetMediaType is never called. //---------------------------------------------------------------------------- STDMETHODIMP CSampleGrabberInPin::EnumMediaTypes( IEnumMediaTypes **ppEnum ) { CheckPointer(ppEnum,E_POINTER); ValidateReadWritePtr(ppEnum,sizeof(IEnumMediaTypes *)); // if the output pin isn't connected yet, offer the possibly // partially specified media type that has been set by the user if( !((CSampleGrabber*)m_pTIPFilter)->OutputPin( )->IsConnected() ) { // Create a new reference counted enumerator *ppEnum = new CEnumMediaTypes( this, NULL ); return (*ppEnum) ? NOERROR : E_OUTOFMEMORY; } // if the output pin is connected, offer it's fully qualified media type return ((CSampleGrabber*)m_pTIPFilter)->OutputPin( )->GetConnected()->EnumMediaTypes( ppEnum ); } //---------------------------------------------------------------------------- // //---------------------------------------------------------------------------- STDMETHODIMP CSampleGrabberInPin::NotifyAllocator( IMemAllocator *pAllocator, BOOL bReadOnly ) { if( m_pPrivateAllocator ) { if( pAllocator != m_pPrivateAllocator ) { return E_FAIL; } else { // if the upstream guy wants to be read only and we don't, then that's bad // if the upstream guy doesn't request read only, but we do, that's okay if( bReadOnly && !SampleGrabber( )->IsReadOnly( ) ) { return E_FAIL; } } } return CTransInPlaceInputPin::NotifyAllocator( pAllocator, bReadOnly ); } //---------------------------------------------------------------------------- // //---------------------------------------------------------------------------- STDMETHODIMP CSampleGrabberInPin::GetAllocator( IMemAllocator **ppAllocator ) { if( m_pPrivateAllocator ) { CheckPointer(ppAllocator,E_POINTER); *ppAllocator = m_pPrivateAllocator; m_pPrivateAllocator->AddRef( ); return NOERROR; } else { return CTransInPlaceInputPin::GetAllocator( ppAllocator ); } } //---------------------------------------------------------------------------- // GetAllocatorRequirements: The upstream filter calls this to get our // filter's allocator requirements. If the app has set the buffer, then // we return those props. Otherwise, we use the default TransInPlace behavior. //---------------------------------------------------------------------------- HRESULT CSampleGrabberInPin::GetAllocatorRequirements( ALLOCATOR_PROPERTIES *pProps ) { CheckPointer(pProps,E_POINTER); if (m_pPrivateAllocator) { *pProps = m_allocprops; return S_OK; } else { return CTransInPlaceInputPin::GetAllocatorRequirements(pProps); } } //---------------------------------------------------------------------------- // //---------------------------------------------------------------------------- HRESULT CSampleGrabberInPin::SetDeliveryBuffer( ALLOCATOR_PROPERTIES props, BYTE * pBuffer ) { // don't allow more than one buffer if( props.cBuffers != 1 ) { return E_INVALIDARG; } if( !pBuffer ) { return E_POINTER; } m_allocprops = props; m_pBuffer = pBuffer; // If there is an existing allocator, make sure that it is released // to prevent a memory leak if (m_pPrivateAllocator) { m_pPrivateAllocator->Release(); m_pPrivateAllocator = NULL; } HRESULT hr = S_OK; m_pPrivateAllocator = new CSampleGrabberAllocator( this, &hr ); if( !m_pPrivateAllocator ) { return E_OUTOFMEMORY; } m_pPrivateAllocator->AddRef( ); return hr; } //---------------------------------------------------------------------------- // //---------------------------------------------------------------------------- HRESULT CSampleGrabberInPin::SetMediaType( const CMediaType *pmt ) { m_bMediaTypeChanged = TRUE; return CTransInPlaceInputPin::SetMediaType( pmt ); } //---------------------------------------------------------------------------- // don't allocate the memory, just use the buffer the app provided //---------------------------------------------------------------------------- HRESULT CSampleGrabberAllocator::Alloc( ) { // look at the base class code to see where this came from! CAutoLock lck(this); // Check he has called SetProperties HRESULT hr = CBaseAllocator::Alloc(); if (FAILED(hr)) { return hr; } // If the requirements haven't changed then don't reallocate if (hr == S_FALSE) { ASSERT(m_pBuffer); return NOERROR; } ASSERT(hr == S_OK); // we use this fact in the loop below // Free the old resources if (m_pBuffer) { ReallyFree(); } // Compute the aligned size LONG lAlignedSize = m_lSize + m_lPrefix; if (m_lAlignment > 1) { LONG lRemainder = lAlignedSize % m_lAlignment; if (lRemainder != 0) { lAlignedSize += (m_lAlignment - lRemainder); } } // Create the contiguous memory block for the samples // making sure it's properly aligned (64K should be enough!) ASSERT(lAlignedSize % m_lAlignment == 0); // don't create the buffer - use what was passed to us // m_pBuffer = m_pPin->m_pBuffer; if (m_pBuffer == NULL) { return E_OUTOFMEMORY; } LPBYTE pNext = m_pBuffer; CMediaSample *pSample; ASSERT(m_lAllocated == 0); // Create the new samples - we have allocated m_lSize bytes for each sample // plus m_lPrefix bytes per sample as a prefix. We set the pointer to // the memory after the prefix - so that GetPointer() will return a pointer // to m_lSize bytes. for (; m_lAllocated < m_lCount; m_lAllocated++, pNext += lAlignedSize) { pSample = new CMediaSample( NAME("Sample Grabber memory media sample"), this, &hr, pNext + m_lPrefix, // GetPointer() value m_lSize); // not including prefix ASSERT(SUCCEEDED(hr)); if (pSample == NULL) return E_OUTOFMEMORY; // This CANNOT fail m_lFree.Add(pSample); } m_bChanged = FALSE; return NOERROR; } //---------------------------------------------------------------------------- // don't really free the memory //---------------------------------------------------------------------------- void CSampleGrabberAllocator::ReallyFree() { // look at the base class code to see where this came from! // Should never be deleting this unless all buffers are freed ASSERT(m_lAllocated == m_lFree.GetCount()); // Free up all the CMediaSamples CMediaSample *pSample; for (;;) { pSample = m_lFree.RemoveHead(); if (pSample != NULL) { delete pSample; } else { break; } } m_lAllocated = 0; // don't free the buffer - let the app do it } //---------------------------------------------------------------------------- // SetProperties: Called by the upstream filter to set the allocator // properties. The application has already allocated the buffer, so we reject // anything that is not compatible with that, and return the actual props. //---------------------------------------------------------------------------- HRESULT CSampleGrabberAllocator::SetProperties( ALLOCATOR_PROPERTIES *pRequest, ALLOCATOR_PROPERTIES *pActual ) { HRESULT hr = CMemAllocator::SetProperties(pRequest, pActual); if (FAILED(hr)) { return hr; } ALLOCATOR_PROPERTIES *pRequired = &(m_pPin->m_allocprops); if (pRequest->cbAlign != pRequired->cbAlign) { return VFW_E_BADALIGN; } if (pRequest->cbPrefix != pRequired->cbPrefix) { return E_FAIL; } if (pRequest->cbBuffer > pRequired->cbBuffer) { return E_FAIL; } if (pRequest->cBuffers > pRequired->cBuffers) { return E_FAIL; } *pActual = *pRequired; m_lCount = pRequired->cBuffers; m_lSize = pRequired->cbBuffer; m_lAlignment = pRequired->cbAlign; m_lPrefix = pRequired->cbPrefix; return S_OK; }
还有个头文件Grabber.h:
//------------------------------------------------------------------------------ // File: Grabber.h // // Desc: DirectShow sample code - Header file for the SampleGrabber // example filter // // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // Define new GUID and IID for the sample grabber example so that they do NOT // conflict with the official DirectX SampleGrabber filter //------------------------------------------------------------------------------ // {2FA4F053-6D60-4cb0-9503-8E89234F3F73} DEFINE_GUID(CLSID_GrabberSample, 0x2fa4f053, 0x6d60, 0x4cb0, 0x95, 0x3, 0x8e, 0x89, 0x23, 0x4f, 0x3f, 0x73); DEFINE_GUID(IID_IGrabberSample, 0x6b652fff, 0x11fe, 0x4fce, 0x92, 0xad, 0x02, 0x66, 0xb5, 0xd7, 0xc7, 0x8f); // We define a callback typedef for this example. // Normally, you would make the SampleGrabber support a COM interface, // and in one of its methods you would pass in a pointer to a COM interface // used for calling back. See the DirectX documentation for the SampleGrabber // for more information. typedef HRESULT (*SAMPLECALLBACK) ( IMediaSample * pSample, REFERENCE_TIME * StartTime, REFERENCE_TIME * StopTime, BOOL TypeChanged ); // We define the interface the app can use to program us MIDL_INTERFACE("6B652FFF-11FE-4FCE-92AD-0266B5D7C78F") IGrabberSample : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE SetAcceptedMediaType( const CMediaType *pType) = 0; virtual HRESULT STDMETHODCALLTYPE GetConnectedMediaType( CMediaType *pType) = 0; virtual HRESULT STDMETHODCALLTYPE SetCallback( SAMPLECALLBACK Callback) = 0; virtual HRESULT STDMETHODCALLTYPE SetDeliveryBuffer( ALLOCATOR_PROPERTIES props, BYTE *pBuffer) = 0; }; class CSampleGrabberInPin; class CSampleGrabber; //---------------------------------------------------------------------------- // This is a special allocator that KNOWS that the person who is creating it // will only create one of them. It allocates CMediaSamples that only // reference the buffer location that is set in the pin's renderer's // data variable //---------------------------------------------------------------------------- class CSampleGrabberAllocator : public CMemAllocator { friend class CSampleGrabberInPin; friend class CSampleGrabber; protected: // our pin who created us // CSampleGrabberInPin * m_pPin; public: CSampleGrabberAllocator( CSampleGrabberInPin * pParent, HRESULT *phr ) : CMemAllocator( TEXT("SampleGrabberAllocator/0"), NULL, phr ) , m_pPin( pParent ) { }; ~CSampleGrabberAllocator( ) { // wipe out m_pBuffer before we try to delete it. It's not an allocated // buffer, and the default destructor will try to free it! m_pBuffer = NULL; } HRESULT Alloc( ); void ReallyFree(); // Override this to reject anything that does not match the actual buffer // that was created by the application STDMETHODIMP SetProperties(ALLOCATOR_PROPERTIES *pRequest, ALLOCATOR_PROPERTIES *pActual); }; //---------------------------------------------------------------------------- // we override the input pin class so we can provide a media type // to speed up connection times. When you try to connect a filesourceasync // to a transform filter, DirectShow will insert a splitter and then // start trying codecs, both audio and video, video codecs first. If // your sample grabber's set to connect to audio, unless we do this, it // will try all the video codecs first. Connection times are sped up x10 // for audio with just this minor modification! //---------------------------------------------------------------------------- class CSampleGrabberInPin : public CTransInPlaceInputPin { friend class CSampleGrabberAllocator; friend class CSampleGrabber; CSampleGrabberAllocator * m_pPrivateAllocator; ALLOCATOR_PROPERTIES m_allocprops; BYTE * m_pBuffer; BOOL m_bMediaTypeChanged; protected: CSampleGrabber * SampleGrabber( ) { return (CSampleGrabber*) m_pFilter; } HRESULT SetDeliveryBuffer( ALLOCATOR_PROPERTIES props, BYTE * m_pBuffer ); public: CMediaType mt; CSampleGrabberInPin( CTransInPlaceFilter * pFilter, HRESULT * pHr ) : CTransInPlaceInputPin( TEXT("SampleGrabberInputPin/0"), pFilter, pHr, L"Input/0" ) , m_pPrivateAllocator( NULL ) , m_pBuffer( NULL ) , m_bMediaTypeChanged( FALSE ) { memset( &m_allocprops, 0, sizeof( m_allocprops ) ); } ~CSampleGrabberInPin( ) { if( m_pPrivateAllocator ) delete m_pPrivateAllocator; } // override to provide major media type for fast connects HRESULT GetMediaType( int iPosition, CMediaType *pMediaType ); // override this or GetMediaType is never called STDMETHODIMP EnumMediaTypes( IEnumMediaTypes **ppEnum ); // override this to refuse any allocators besides // the one the user wants, if this is set STDMETHODIMP NotifyAllocator( IMemAllocator *pAllocator, BOOL bReadOnly ); // override this so we always return the special allocator, if necessary STDMETHODIMP GetAllocator( IMemAllocator **ppAllocator ); HRESULT SetMediaType( const CMediaType *pmt ); // we override this to tell whoever's upstream of us what kind of // properties we're going to demand to have // STDMETHODIMP GetAllocatorRequirements( ALLOCATOR_PROPERTIES *pProps ); }; //---------------------------------------------------------------------------- // //---------------------------------------------------------------------------- class CSampleGrabber : public CTransInPlaceFilter, public IGrabberSample { friend class CSampleGrabberInPin; friend class CSampleGrabberAllocator; protected: CMediaType m_mtAccept; BOOL m_bModifiesData; SAMPLECALLBACK m_callback; CCritSec m_Lock; // serialize access to our data BOOL IsReadOnly( ) { return !m_bModifiesData; } // PURE, override this to ensure we get // connected with the right media type HRESULT CheckInputType( const CMediaType * pmt ); // PURE, override this to callback // the user when a sample is received HRESULT Transform( IMediaSample * pms ); // override this so we can return S_FALSE directly. // The base class CTransInPlace // Transform( ) method is called by it's // Receive( ) method. There is no way // to get Transform( ) to return an S_FALSE value // (which means "stop giving me data"), // to Receive( ) and get Receive( ) to return S_FALSE as well. HRESULT Receive( IMediaSample * pms ); public: static CUnknown *WINAPI CreateInstance(LPUNKNOWN punk, HRESULT *phr); // Expose ISampleGrabber STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv); DECLARE_IUNKNOWN; CSampleGrabber( IUnknown * pOuter, HRESULT * pHr, BOOL ModifiesData ); // IGrabberSample STDMETHODIMP SetAcceptedMediaType( const CMediaType * pmt ); STDMETHODIMP GetConnectedMediaType( CMediaType * pmt ); STDMETHODIMP SetCallback( SAMPLECALLBACK Callback ); STDMETHODIMP SetDeliveryBuffer( ALLOCATOR_PROPERTIES props, BYTE * m_pBuffer ); };
另外还可以自己编译baseclasses里的工程生成mobile下的strmbasd.lib 和 strmbase.lib,当然,windows mobile6是有自己的strmbase.lib的,只有strmbasd.lib没有而已,不过你可以通过AKU目录下的baseclasses来编译生成wm下的baseclasses工程,这样便于调试. 其中strmbasd.lib和strmbase.lib分别用于Debug和Release下的。
很简单吧
代码下载处: http://download.csdn.net/source/2185421
有问题多多联系!