zdirectshow的原理大概大家都知道,基本就是用微软封装的接口来实现硬件无关性,但是最终调用的接口都要在驱动层有对应的实现:
为了更清楚地演示directshow的数据传输过程,我必须说明的这个程序的基本流程。我采用的是vs2005 + windows mobile 6。0 professional 仿真模拟器,驱动层传出的是176*144格式的rgb565的数据,最后我将保存图片为RGB24的bmppdf图片。
说明: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 <windows.h>
- #include <mmsystem.h>
- #include "streams.h"
- #include <cs.h>
- #include <csmedia.h>
- #include <camera.h>
- #include <aygshell.h>
- #include "ddcam.h"
- #include "grabber.h"
- #include <windef.h>
- #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);
- }
- }
#include <windows.h> #include <mmsystem.h> #include "streams.h" #include <cs.h> #include <csmedia.h> #include <camera.h> #include <aygshell.h> #include "ddcam.h" #include "grabber.h" #include <windef.h> #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 <streams.h> // Active Movie (includes windows.h)
- #include <initguid.h> // 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;
- }
//------------------------------------------------------------------------------ // File: Grabber.cpp // // Desc: DirectShow sample code - Implementation file for the SampleGrabber // example filter // // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ #include <streams.h> // Active Movie (includes windows.h) #include <initguid.h> // 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 );
- };
//------------------------------------------------------------------------------ // 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下的。