📄 vidinput_directx.cxx
字号:
/*
* vidinput_directx.cxx
*
* Classes to support streaming video input (grabbing) and output.
*
* Portable Windows Library
*
* Copyright (c) 2007 Luc Saillard <luc@saillard.org>
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Portable Windows Library.
*
* The Initial Developer of the Original Code is Luc Saillard <luc@saillard.org>
*
* Contributor(s): Matthias Schneider <ma30002000@yahoo.de>
*/
#include <ptlib.h>
#ifdef P_DIRECTSHOW
#include "ptlib/msos/ptlib/vidinput_directx.h"
#ifdef P_DIRECTSHOW_LIBRARY1
#pragma comment(lib, P_DIRECTSHOW_LIBRARY1)
#endif
#ifdef P_DIRECTSHOW_LIBRARY2
#pragma comment(lib, P_DIRECTSHOW_LIBRARY2)
#endif
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
static HRESULT SetDevice(const PString & devName, IBaseFilter ** ppSrcFilter);
#ifndef _WIN32_WCE
static char *BSTR_to_ANSI(BSTR pSrc);
#endif
static GUID pwlib_format_to_media_format(const char *format);
static PString media_format_to_pwlib_format(const GUID guid);
#if defined(_WIN32_WCE) && !defined(HAVE_CE_SAMPLEGRABBER)
const IID IID_ISampleGrabber = { 0x6B652FFF, 0x11FE, 0x4fce, 0x92, 0xAD, 0x02, 0x66, 0xB5, 0xD7, 0xC7, 0x8F };
const CLSID CLSID_SampleGrabber = { 0xC1F400A4, 0x3F08, 0x11d3, 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 };
const CLSID CLSID_NullRenderer = { 0xC1F400A4, 0x3F08, 0x11d3, 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 };
#endif // _WIN32_WCE
PCREATE_VIDINPUT_PLUGIN(DirectShow);
#if PTRACING
static const char *ErrorMessage(HRESULT hr)
{
#ifndef _WIN32_WCE
static char string[1024];
DWORD dwMsgLen;
memset(string, 0, sizeof(string));
dwMsgLen = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
hr,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)string,
sizeof(string)-1,
NULL);
if (dwMsgLen)
return string;
memset(string, 0, sizeof(string));
dwMsgLen = AMGetErrorTextA(hr, string, sizeof(string));
if (dwMsgLen)
return string;
#ifdef __MINGW32__ // This function is not recognised in Windows
snprintf(string, sizeof(string), "0x%8.8x", hr);
return string;
#else
return PString();
#endif
#else // _WIN32_WCE
return PString("Error during video capture");
#endif // !_WIN32_WCE
}
#endif // PTRACING
static void MyDeleteMediaType(AM_MEDIA_TYPE *pmt)
{
if (pmt == NULL)
return;
if (pmt->cbFormat != 0)
{
CoTaskMemFree((PVOID)pmt->pbFormat);
pmt->cbFormat = 0;
pmt->pbFormat = NULL;
}
if (pmt->pUnk != NULL)
{
// Uncessessary because pUnk should not be used, but safest.
pmt->pUnk->Release();
pmt->pUnk = NULL;
}
CoTaskMemFree(pmt);
}
PVideoInputDevice_DirectShow::PVideoInputDevice_DirectShow()
{
PTRACE(1,"PVidDirectShow\tPVideoInputDevice_DirectShow: constructor" );
#ifndef _WIN32_WCE
CoInitialize(NULL);
#else
CoInitializeEx(NULL,COINIT_MULTITHREADED);
#endif
tempFrame = NULL;
pSrcFilter = NULL;
pGrabberFilter = NULL;
pNullFilter = NULL;
pGraph = NULL;
pMC = NULL;
pME = NULL;
pCapture = NULL;
pGrabber = NULL;
isCapturingNow = PFalse;
capturing_duration = 10000; // arbitrary large value suffices
}
PVideoInputDevice_DirectShow::~PVideoInputDevice_DirectShow()
{
if (tempFrame != NULL)
free(tempFrame);
Close();
::CoUninitialize();
}
HRESULT PVideoInputDevice_DirectShow::Initialize_Interfaces()
{
HRESULT hr;
PTRACE(1,"PVidDirectShow\tInitialize_Interfaces()");
// Create the filter graph
hr = CoCreateInstance (CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **) &pGraph);
if (FAILED(hr))
{
PTRACE(1,"PVidDirectShow\tFailed to create instance FilterGraph: " << ErrorMessage(hr));
return hr;
}
// Create the capture graph builder
#ifndef _WIN32_WCE
hr = CoCreateInstance (CLSID_CaptureGraphBuilder2 , NULL, CLSCTX_INPROC_SERVER,
IID_ICaptureGraphBuilder2, (void **) &pCapture);
#else
hr = CoCreateInstance (CLSID_CaptureGraphBuilder , NULL, CLSCTX_INPROC_SERVER,
IID_ICaptureGraphBuilder2, (void **) &pCapture);
#endif
if (FAILED(hr))
{
PTRACE(1,"PVidDirectShow\tFailed to create instance CaptureGraphBuilder2: " << ErrorMessage(hr));
return hr;
}
// Create the Sample Grabber Filter.
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
IID_IBaseFilter, (void**) &pGrabberFilter);
if (FAILED(hr))
{
PTRACE(1,"PVidDirectShow\tFailed to create instance SampleGrabber: " << ErrorMessage(hr));
return hr;
}
// Create the Null Renderer Filter.
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER,
IID_IBaseFilter, (void**) &pNullFilter);
if (FAILED(hr))
{
PTRACE(1,"PVidDirectShow\tFailed to create instance SampleGrabber: " << ErrorMessage(hr));
return hr;
}
// Obtain interfaces for media control and Video Window
hr = pGraph->QueryInterface(IID_IMediaControl,(LPVOID *) &pMC);
if (FAILED(hr))
{
PTRACE(1,"PVidDirectShow\tFailed to query interface MediaControl: " << ErrorMessage(hr));
return hr;
}
hr = pGraph->QueryInterface(IID_IMediaEvent, (LPVOID *) &pME);
if (FAILED(hr))
{
PTRACE(1,"PVidDirectShow\tFailed to query interface MediaEvent: " << ErrorMessage(hr));
return hr;
}
// Attach the filter graph to the capture graph
hr = pCapture->SetFiltergraph(pGraph);
if (FAILED(hr))
{
PTRACE(1,"PVidDirectShow\tFailed to set capture filter graph: " << ErrorMessage(hr));
return hr;
}
//Add the filter to the graph
hr = pGraph->AddFilter(pGrabberFilter, L"Sample Grabber");
if (FAILED(hr))
{
PTRACE(1, "PVidDirectShow\tCouldn't add the grabber filter to the graph: " << ErrorMessage(hr));
return hr;
}
// Obtain interfaces for Sample Grabber
pGrabberFilter->QueryInterface(IID_ISampleGrabber, (void**)&pGrabber);
hr = pGrabber->SetBufferSamples(PTrue);
if (FAILED(hr))
{
PTRACE(1, "PVidDirectShow\tFailed to SetBufferSamples: " << ErrorMessage(hr));
return hr;
}
hr = pGrabber->SetOneShot(PFalse);
if (FAILED(hr))
{
PTRACE(1, "PVidDirectShow\tFailed to SetOneShot: " << ErrorMessage(hr));
return hr;
}
//Set the Sample Grabber callback
//0: SampleCB (the buffer is the original buffer, not a copy)
//1: BufferCB (the buffer is a copy of the original buffer)
#if 0
hr = pGrabber->SetCallback(this, 0);
if (FAILED(hr))
{
PTRACE(1, "PVidDirectShow\tFailed to SetCallback: " << ErrorMessage(hr));
return hr;
}
#endif
return hr;
}
PBoolean PVideoInputDevice_DirectShow::InitialiseCapture()
{
HRESULT hr;
PTRACE(1,"PVidDirectShow\tInitializeCapture()");
hr = Initialize_Interfaces();
if (FAILED(hr))
{
PTRACE(1, "PVidDirectShow\tFailed to initialize interfaces: " << ErrorMessage(hr));
return PFalse;
}
hr = SetDevice(deviceName, &pSrcFilter);
if (FAILED(hr))
{
PTRACE(1, "PVidDirectShow\tFailed to select a device: " << ErrorMessage(hr));
return PFalse;
}
// Add Capture filter to our graph.
hr = pGraph->AddFilter(pSrcFilter, L"Video Capture");
if (FAILED(hr))
{
PTRACE(1, "PVidDirectShow\tCouldn't add the capture filter to the graph: " << ErrorMessage(hr));
return PFalse;
}
// Add the filter to our graph
hr = pGraph->AddFilter(pNullFilter, L"Null Renderer");
if (FAILED(hr))
{
PTRACE(1, "PVidDirectShow\tCouldn't add the grabber filter to the graph: " << ErrorMessage(hr));
return hr;
}
return PTrue;
}
PStringArray PVideoInputDevice_DirectShow::GetInputDeviceNames()
{
PStringArray devices;
PTRACE(1,"PVidDirectShow\tGetInputDeviceNames()");
#ifndef _WIN32_WCE
HRESULT hr;
IMoniker *pMoniker =NULL;
IEnumMoniker *pClassEnum = NULL;
ULONG cFetched;
ICreateDevEnum *pDevEnum =NULL;
::CoInitialize(NULL);
// Create the system device enumerator
hr = CoCreateInstance (CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
IID_ICreateDevEnum, (void **) &pDevEnum);
if (FAILED(hr))
{
PTRACE(1, "PVidDirectShow\tCouldn't create system enumerator. " << ErrorMessage(hr));
::CoUninitialize();
return devices;
}
// Create an enumerator for the video capture devices
hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pClassEnum, 0);
if (FAILED(hr))
{
PTRACE(1, "PVidDirectShow\tGetInputDeviceNames() Couldn't create class enumerator. " << ErrorMessage(hr));
::CoUninitialize();
return devices;
}
if (pClassEnum == NULL)
{
PTRACE(1, "PVidDirectShow\tGetInputDeviceNames() No video capture device was detected.");
::CoUninitialize();
return devices;
}
while (hr = pClassEnum->Next(1, &pMoniker, &cFetched), hr==S_OK)
{
// Get the property bag
IPropertyBag *pPropBag;
hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**)(&pPropBag));
if (FAILED(hr))
{
pMoniker->Release();
continue;
}
// Find the description or friendly name.
VARIANT DeviceName;
DeviceName.vt = VT_BSTR;
hr = pPropBag->Read(L"Description", &DeviceName, NULL);
if (FAILED(hr))
hr = pPropBag->Read(L"FriendlyName", &DeviceName, NULL);
if (SUCCEEDED(hr))
{
char *pDeviceName = BSTR_to_ANSI(DeviceName.bstrVal);
if (pDeviceName)
{
PTRACE(4, "PVidDirectShow\tGetInputDeviceNames() Found this capture device '"<< pDeviceName <<"'");
devices.AppendString(pDeviceName);
free(pDeviceName);
}
}
pPropBag->Release();
pMoniker->Release();
// Next Device
}
::CoUninitialize();
#else // !_WIN32_WCE
HANDLE handle = NULL;
char szDeviceName[8];
DEVMGR_DEVICE_INFORMATION di;
GUID guidCamera = { 0xCB998A05, 0x122C, 0x4166, 0x84, 0x6A, 0x93, 0x3E, 0x4D, 0x7E, 0x3C, 0x86 };
// Note about the above: The driver material doesn't ship as part of the SDK. This GUID is hardcoded
// here to be able to enumerate the camera drivers and pass the name of the driver to the video capture filter
di.dwSize = sizeof(di);
ZeroMemory( szDeviceName, 8 );
handle = FindFirstDevice( DeviceSearchByGuid, &guidCamera, &di );
if(( handle == NULL ) || ( di.hDevice == NULL ))
{
PTRACE(4, "PVidDirectShow\tGetInputDeviceNames() returns error: '"<< ::GetLastError() <<"'");
}
else
wcstombs( szDeviceName, di.szLegacyName, 8 );
FindClose( handle );
PTRACE(4, "PVidDirectShow\tGetInputDeviceNames() Found this capture device '"<< szDeviceName <<"'");
devices.AppendString(szDeviceName);
#endif
return devices;
}
PBoolean PVideoInputDevice_DirectShow::Open(const PString & devName, PBoolean startImmediate)
{
PTRACE(1,"PVidDirectShow\tOpen("<<devName<<"," << startImmediate<<")");
/* FIXME: If the device is already open, close it */
if (IsOpen())
return TRUE;
deviceName = devName;
if (!InitialiseCapture())
return PFalse;
ListSupportedFormats();
GetDefaultFormat();
if (startImmediate)
return Start();
return PTrue;
}
PBoolean PVideoInputDevice_DirectShow::IsOpen()
{
PTRACE(1,"PVidDirectShow\tIsOpen()");
return pCapture != NULL;
}
PBoolean PVideoInputDevice_DirectShow::Close()
{
HRESULT hr;
if (!IsOpen() || (NULL == pGrabber))
return PFalse;
hr = pGrabber->SetCallback(NULL, 0);
if (FAILED(hr))
{
PTRACE(1, "PVidDirectShow\tFailed to remove the callback: " << ErrorMessage(hr));
return hr;
}
if (pMC)
pMC->StopWhenReady();
SAFE_RELEASE(pMC);
SAFE_RELEASE(pME);
SAFE_RELEASE(pNullFilter)
SAFE_RELEASE(pGrabberFilter)
SAFE_RELEASE(pSrcFilter)
SAFE_RELEASE(pGraph);
SAFE_RELEASE(pCapture);
SAFE_RELEASE(pGrabber);
return PTrue;
}
PBoolean PVideoInputDevice_DirectShow::Start()
{
HRESULT hr;
long evCode;
unsigned int count;
PTRACE(1,"PVidDirectShow\tStart()");
if (IsCapturing())
return PTrue;
// http://msdn2.microsoft.com/en-us/library/ms784859.aspx
hr = pCapture->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
pSrcFilter, /* Source Filter */
NULL, /* Intermediate Filter */
pGrabberFilter /* Sink Filter */
);
if (FAILED(hr))
{
PTRACE(1, "PVidDirectShow\tCouldn't render the video capture stream: " << ErrorMessage(hr));
return hr;
}
// Start previewing video data
hr = pMC->Run();
if (FAILED(hr))
{
PTRACE(1, "PVidDirectShow\tCouldn't run the graph: " << ErrorMessage(hr));
return PFalse;
}
hr = pME->WaitForCompletion(INFINITE, &evCode);
if (FAILED(hr))
{
PTRACE(1, "PVidDirectShow\tCouldn't wait for completion: " << ErrorMessage(hr));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -