From 1ebf5a5fcda0c9154e22ed02404fd46525a7fd9f Mon Sep 17 00:00:00 2001 From: bossiel Date: Wed, 10 Aug 2011 22:59:15 +0000 Subject: Move deprecated v1.0 from trunk to branches --- tinyDSHOW/src/DSCaptureFormat.cxx | 64 ++ tinyDSHOW/src/DSCaptureGraph.cxx | 443 +++++++++++++ tinyDSHOW/src/DSCaptureUtils.cxx | 376 +++++++++++ tinyDSHOW/src/DSDisplay.cxx | 581 ++++++++++++++++ tinyDSHOW/src/DSDisplayGraph.cxx | 330 ++++++++++ tinyDSHOW/src/DSDisplayOverlay.VMR.cxx | 179 +++++ tinyDSHOW/src/DSDisplayOverlay.VMR9.cxx | 211 ++++++ tinyDSHOW/src/DSDisplayOverlay.cxx | 71 ++ tinyDSHOW/src/DSFrameRateFilter.cxx | 126 ++++ tinyDSHOW/src/DSGrabber.cxx | 255 +++++++ tinyDSHOW/src/DSOutputFilter.cxx | 113 ++++ tinyDSHOW/src/DSOutputStream.cxx | 308 +++++++++ tinyDSHOW/src/DSUtils.cxx | 155 +++++ tinyDSHOW/src/Resizer.cxx | 1097 +++++++++++++++++++++++++++++++ tinyDSHOW/src/VideoDisplayName.cxx | 41 ++ tinyDSHOW/src/VideoGrabberName.cxx | 41 ++ tinyDSHOW/src/plugin/DSConsumer.cxx | 278 ++++++++ tinyDSHOW/src/plugin/DSProducer.cxx | 278 ++++++++ 18 files changed, 4947 insertions(+) create mode 100644 tinyDSHOW/src/DSCaptureFormat.cxx create mode 100644 tinyDSHOW/src/DSCaptureGraph.cxx create mode 100644 tinyDSHOW/src/DSCaptureUtils.cxx create mode 100644 tinyDSHOW/src/DSDisplay.cxx create mode 100644 tinyDSHOW/src/DSDisplayGraph.cxx create mode 100644 tinyDSHOW/src/DSDisplayOverlay.VMR.cxx create mode 100644 tinyDSHOW/src/DSDisplayOverlay.VMR9.cxx create mode 100644 tinyDSHOW/src/DSDisplayOverlay.cxx create mode 100644 tinyDSHOW/src/DSFrameRateFilter.cxx create mode 100644 tinyDSHOW/src/DSGrabber.cxx create mode 100644 tinyDSHOW/src/DSOutputFilter.cxx create mode 100644 tinyDSHOW/src/DSOutputStream.cxx create mode 100644 tinyDSHOW/src/DSUtils.cxx create mode 100644 tinyDSHOW/src/Resizer.cxx create mode 100644 tinyDSHOW/src/VideoDisplayName.cxx create mode 100644 tinyDSHOW/src/VideoGrabberName.cxx create mode 100644 tinyDSHOW/src/plugin/DSConsumer.cxx create mode 100644 tinyDSHOW/src/plugin/DSProducer.cxx (limited to 'tinyDSHOW/src') diff --git a/tinyDSHOW/src/DSCaptureFormat.cxx b/tinyDSHOW/src/DSCaptureFormat.cxx new file mode 100644 index 0000000..844f917 --- /dev/null +++ b/tinyDSHOW/src/DSCaptureFormat.cxx @@ -0,0 +1,64 @@ +/* +* Copyright (C) 2009-2010 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include +#include + + +int DSCaptureFormat::getMatchScore(int w, int h) +{ + int factor; + + if ((w == width) && (h = height)){ + factor = 100; + } + else if ((w > this->width) && (h > this->height)){ + factor = 0; + } + else{ + factor = (50 * w) / this->width + (50 * h) / this->height; + } + + if (isRGB()){ + factor *= 2; + } + + return factor; +} + +bool DSCaptureFormat::isRGB() +{ + // Order used is optimized for most used RGB types + if (IsEqualGUID(this->chroma, MEDIASUBTYPE_RGB32)) return true; + if (IsEqualGUID(this->chroma, MEDIASUBTYPE_RGB24)) return true; + if (IsEqualGUID(this->chroma, MEDIASUBTYPE_RGB565)) return true; + if (IsEqualGUID(this->chroma, MEDIASUBTYPE_RGB555)) return true; + if (IsEqualGUID(this->chroma, MEDIASUBTYPE_RGB8)) return true; + if (IsEqualGUID(this->chroma, MEDIASUBTYPE_RGB4)) return true; + if (IsEqualGUID(this->chroma, MEDIASUBTYPE_RGB1)) return true; +#ifndef _WIN32_WCE + if (IsEqualGUID(this->chroma, MEDIASUBTYPE_ARGB32)) return true; + if (IsEqualGUID(this->chroma, MEDIASUBTYPE_ARGB4444)) return true; + if (IsEqualGUID(this->chroma, MEDIASUBTYPE_ARGB1555)) return true; +#endif + + return false; +} diff --git a/tinyDSHOW/src/DSCaptureGraph.cxx b/tinyDSHOW/src/DSCaptureGraph.cxx new file mode 100644 index 0000000..8c89d9f --- /dev/null +++ b/tinyDSHOW/src/DSCaptureGraph.cxx @@ -0,0 +1,443 @@ +/* +* Copyright (C) 2009-2010 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include +#include +#include + +#include "tsk_debug.h" + +#include + +using namespace std; + +#ifdef _WIN32_WCE +DSCaptureGraph::DSCaptureGraph(InxbISampleGrabberCB* callback, HRESULT *hr) +#else +DSCaptureGraph::DSCaptureGraph(ISampleGrabberCB* callback, HRESULT *hr) +#endif +{ + this->grabberCallback = callback; + + this->captureFormat = NULL; + this->captureGraphBuilder = NULL; + this->graphBuilder = NULL; + + this->sourceFilter = NULL; + this->sampleGrabberFilter = NULL; + +#ifdef _WIN32_WCE + this->colorConvertor565 = NULL; +#else + this->frameRateFilter = NULL; + this->decompressorFilter = NULL; +#endif + + this->nullRendererFilter = NULL; + this->grabberController = NULL; + this->mediaController = NULL; + this->mediaEventController = NULL; + + this->streamConfiguration = NULL; + + this->running = FALSE; + this->deviceId = ""; + + *hr = this->createCaptureGraph(); +} + +DSCaptureGraph::~DSCaptureGraph() +{ + SAFE_RELEASE(this->streamConfiguration); + + SAFE_RELEASE(this->mediaEventController); + SAFE_RELEASE(this->mediaController); + SAFE_RELEASE(this->grabberController); + +#ifdef _WIN32_WCE + SAFE_RELEASE(this->colorConvertor565); +#else + SAFE_RELEASE(this->decompressorFilter); +#endif + + SAFE_RELEASE(this->nullRendererFilter); + SAFE_RELEASE(this->sampleGrabberFilter); + SAFE_RELEASE(this->sourceFilter); + + SAFE_RELEASE(this->graphBuilder); + SAFE_RELEASE(this->captureGraphBuilder); +} + +HRESULT DSCaptureGraph::setSource(const std::string &devicePath) +{ + HRESULT hr = E_FAIL; + + if (this->sourceFilter){ + this->graphBuilder->RemoveFilter(this->sourceFilter); + } + + SAFE_RELEASE(this->streamConfiguration); + SAFE_RELEASE(this->sourceFilter); + + // Create the filter + this->deviceId = devicePath; + hr = createSourceFilter(&this->deviceId, &this->sourceFilter); + + if (this->sourceFilter){ + // Gets the supported formats + this->supportedFormats.clear(); + getSupportedFormats(this->sourceFilter, &this->supportedFormats); + + // Query for video stream config + hr = this->captureGraphBuilder->FindInterface( + &PIN_CATEGORY_CAPTURE, + &MEDIATYPE_Video, + this->sourceFilter, + IID_IAMStreamConfig, + reinterpret_cast(&this->streamConfiguration)); + + hr = this->graphBuilder->AddFilter(this->sourceFilter, FILTER_WEBCAM); + } + + return hr; +} + +HRESULT DSCaptureGraph::setParameters(DSCaptureFormat *format, int framerate) +{ + HRESULT hr = E_FAIL; + AM_MEDIA_TYPE *mediaType = NULL; + + if (!this->streamConfiguration) goto bail; + + hr = this->streamConfiguration->GetFormat(&mediaType); + if (FAILED(hr)) goto bail; + + VIDEOINFOHEADER* vih = reinterpret_cast(mediaType->pbFormat); + BITMAPINFOHEADER* bih = &vih->bmiHeader; + + int w = format->getWidth(); + int h = format->getHeight(); + + bool wN = (bih->biWidth<0); + bool hN = (bih->biHeight<0); + + // DIBS are DWORD aligned + int data_size = h * ((w * bih->biBitCount + 31) / 32) * 4; + + bih->biSize = sizeof(BITMAPINFOHEADER); + bih->biWidth = w*(wN?-1:1); + bih->biHeight = h*(hN?-1:1); + bih->biSizeImage = data_size; + + //vih->dwBitRate = framerate * data_size; + //vih->AvgTimePerFrame = SECONDS_TO_100NS(framerate); + + mediaType->cbFormat = sizeof(VIDEOINFOHEADER); + //mediaType->lSampleSize = data_size; + mediaType->subtype = format->getChroma(); + + hr = this->streamConfiguration->SetFormat(mediaType); + if (FAILED(hr)) goto bail; + +#ifdef _WIN32_WCE + hr = this->grabberController->SetFps((int) SECONDS_FROM_100NS(vih->AvgTimePerFrame)/*format->getFramerate()*/, framerate); + if (FAILED(hr)) goto bail; + hr = this->grabberController->SetSize(w,h); +#else + // Set fps using tdshow filter + hr = this->frameRateFilter->SetFps((int) ((float)vih->AvgTimePerFrame/10000.f)/*format->getFramerate()*/, framerate); +#endif + if (FAILED(hr)) goto bail; + + this->captureFormat = format; + +bail: + DeleteMediaType(mediaType); + + return hr; +} + +#ifdef _WIN32_WCE +# include +#endif + +HRESULT DSCaptureGraph::connect() +{ + HRESULT hr; + + if (!this->sourceFilter){ + TSK_DEBUG_ERROR("Invalid source filter"); + return E_FAIL; + } + + if (!this->captureFormat){ + TSK_DEBUG_ERROR("Invalid capture format"); + return E_FAIL; + } + + if (this->captureFormat->isRGB()) + { +#if _WIN32_WCE + hr = ConnectFilters(this->graphBuilder, this->sourceFilter, this->colorConvertor565) ; if(FAILED(hr))return hr; + hr = ConnectFilters(this->graphBuilder, this->colorConvertor565, this->sampleGrabberFilter) ; if(FAILED(hr))return hr; + hr = ConnectFilters(this->graphBuilder, this->sampleGrabberFilter, this->nullRendererFilter); if(FAILED(hr))return hr; +#else + hr = ConnectFilters(this->graphBuilder, this->sourceFilter, this->frameRateFilter); if(FAILED(hr))return hr; + hr = ConnectFilters(this->graphBuilder, this->frameRateFilter, this->sampleGrabberFilter); if(FAILED(hr))return hr; + hr = ConnectFilters(this->graphBuilder, this->sampleGrabberFilter, this->nullRendererFilter); if(FAILED(hr))return hr; +#endif + } + else + { +#ifdef _WIN32_WCE + hr = ConnectFilters(this->graphBuilder, this->sourceFilter, this->colorConvertor565) ; if(FAILED(hr))return hr; + hr = ConnectFilters(this->graphBuilder, this->colorConvertor565, this->sampleGrabberFilter) ; if(FAILED(hr))return hr; + hr = ConnectFilters(this->graphBuilder, this->sampleGrabberFilter, this->nullRendererFilter); if(FAILED(hr))return hr; +#else + hr = ConnectFilters(this->graphBuilder, this->sourceFilter, this->decompressorFilter); if(FAILED(hr))return hr; + hr = ConnectFilters(this->graphBuilder, this->decompressorFilter, this->frameRateFilter); if(FAILED(hr))return hr; + hr = ConnectFilters(this->graphBuilder, this->frameRateFilter, this->sampleGrabberFilter); if(FAILED(hr))return hr; + hr = ConnectFilters(this->graphBuilder, this->sampleGrabberFilter, this->nullRendererFilter); if(FAILED(hr))return hr; +#endif + } + + return hr; +} + +HRESULT DSCaptureGraph::disconnect() +{ + HRESULT hr; + + if (!this->sourceFilter) + { + return E_FAIL; + } + + if (!this->captureFormat) + { + return E_FAIL; + } + + if (this->captureFormat->isRGB()) + { +#ifdef _WIN32_WCE + hr = DisconnectFilters(this->graphBuilder, this->sourceFilter, this->colorConvertor565); + hr = DisconnectFilters(this->graphBuilder, this->colorConvertor565, this->sampleGrabberFilter); + hr = DisconnectFilters(this->graphBuilder, this->sampleGrabberFilter, this->nullRendererFilter); +#else + hr = DisconnectFilters(this->graphBuilder, this->sourceFilter, this->frameRateFilter); + hr = DisconnectFilters(this->graphBuilder, this->frameRateFilter, this->sampleGrabberFilter); + hr = DisconnectFilters(this->graphBuilder, this->sampleGrabberFilter, this->nullRendererFilter); +#endif + } + else + { +#ifdef _WIN32_WCE + hr = DisconnectFilters(this->graphBuilder, this->sourceFilter, this->colorConvertor565); if(FAILED(hr))return hr; + hr = DisconnectFilters(this->graphBuilder, this->colorConvertor565, this->sampleGrabberFilter); if(FAILED(hr))return hr; + hr = DisconnectFilters(this->graphBuilder, this->sampleGrabberFilter, this->nullRendererFilter); if(FAILED(hr))return hr; +#else + hr = DisconnectFilters(this->graphBuilder, this->sourceFilter, this->decompressorFilter); + hr = DisconnectFilters(this->graphBuilder, this->decompressorFilter, this->frameRateFilter); + hr = DisconnectFilters(this->graphBuilder, this->frameRateFilter, this->sampleGrabberFilter); + hr = DisconnectFilters(this->graphBuilder, this->sampleGrabberFilter, this->nullRendererFilter); +#endif + } + + return hr; +} + +HRESULT DSCaptureGraph::start() +{ + HRESULT hr; + + if(this->running) return S_OK; + + //this->mediaController->Stop(); + + hr = this->mediaController->Run(); + /*if (hr == S_FALSE) + { + cerr << "DSCaptureGraph::mediaController->Start() has failed with " << hr << ". Waiting for transition." << endl; + FILTER_STATE pfs; + hr = this->mediaController->GetState(2500, (OAFilterState*) &pfs); + hr = this->mediaController->Run(); + }*/ + + if (!SUCCEEDED(hr)) + { +#ifdef _WIN32_WCE + MessageBox(NULL, _T("Starting DirectShow Graph Failed"), _T("Failure"), MB_OK); + //assert(1==15); +#endif + TSK_DEBUG_ERROR("DSCaptureGraph::mediaController->Run() has failed with %ld", hr); + } + this->running = true; + return hr; +} + +HRESULT DSCaptureGraph::stop() +{ + HRESULT hr; + hr = this->mediaController->Pause(); + if (hr == S_FALSE) + { + TSK_DEBUG_ERROR("DSCaptureGraph::mediaController->Pause() has failed with %ld. Waiting for transition.", hr); + FILTER_STATE pfs; + hr = this->mediaController->GetState(2500, (OAFilterState*) &pfs); + } + hr = this->mediaController->Stop(); + if (!SUCCEEDED(hr)) + { + TSK_DEBUG_ERROR("DSCaptureGraph::mediaController->Stop() has failed with %ld", hr); + } + this->running = false; + return hr; +} + +bool DSCaptureGraph::isRunning() +{ + return this->running; +} + +HRESULT DSCaptureGraph::getConnectedMediaType(AM_MEDIA_TYPE *mediaType) +{ +#ifdef _WIN32_WCE + memmove(mediaType, &this->grabberController->GetMediaType(), sizeof(AM_MEDIA_TYPE)); + return S_OK; +#else + return this->grabberController->GetConnectedMediaType(mediaType); +#endif +} + +HRESULT DSCaptureGraph::createCaptureGraph() +{ + HRESULT hr; + +#ifdef _WIN32_WCE + + // Create capture graph builder + hr = COCREATE(CLSID_CaptureGraphBuilder, IID_ICaptureGraphBuilder2, this->captureGraphBuilder) ; if(FAILED(hr)) return hr; + hr = COCREATE(CLSID_FilterGraph, IID_IGraphBuilder, this->graphBuilder) ; if(FAILED(hr)) return hr; + hr = this->captureGraphBuilder->SetFiltergraph(this->graphBuilder) ; if(FAILED(hr)) return hr; + + // Create filters + LPUNKNOWN pUnk1 = NULL, pUnk2 = NULL; + hr = COCREATE(CLSID_Colour, IID_IBaseFilter, this->colorConvertor565) ;if(FAILED(hr)) return hr; + this->sampleGrabberFilter = new DSSampleGrabber(FITLER_SAMPLE_GRABBER, pUnk1, &hr) ;if(FAILED(hr)) return hr; + this->nullRendererFilter = new DSInxbNullFilter(/*FILTER_NULL_RENDERER,*/ pUnk2, &hr) ;if(FAILED(hr)) return hr; + this->grabberController = (DSSampleGrabber*)(this->sampleGrabberFilter); ;if(!this->grabberController) return E_FAIL; + + // Add Filters + hr = this->graphBuilder->AddFilter(this->colorConvertor565, FILTER_COLOR_CONVERTOR_565) ;if(FAILED(hr)) return hr; + hr = this->graphBuilder->AddFilter(this->sampleGrabberFilter, FITLER_SAMPLE_GRABBER) ;if(FAILED(hr)) return hr; + hr = this->graphBuilder->AddFilter(this->nullRendererFilter, FILTER_NULL_RENDERER) ;if(FAILED(hr)) return hr; + + // Find media control + hr = QUERY(this->graphBuilder, IID_IMediaControl, this->mediaController); + if(FAILED(hr)) return hr; + + // Set callback + hr = this->grabberController->SetCallback(this->grabberCallback); + if(FAILED(hr)) return hr; +#else + // Create capture graph builder + hr = COCREATE(CLSID_CaptureGraphBuilder2, IID_ICaptureGraphBuilder2, this->captureGraphBuilder); + if(FAILED(hr)) return hr; + + // Create the graph builder + hr = COCREATE(CLSID_FilterGraph, IID_IGraphBuilder, this->graphBuilder); + if(FAILED(hr)) return hr; + + // Initialize the Capture Graph Builder. + hr = this->captureGraphBuilder->SetFiltergraph(this->graphBuilder); + if(FAILED(hr)) return hr; + + // Create the sample grabber filter + hr = COCREATE(CLSID_SampleGrabber, IID_IBaseFilter, this->sampleGrabberFilter); + if(FAILED(hr)) return hr; + + // Create the AVI decoder filter + hr = COCREATE(CLSID_AVIDec, IID_IBaseFilter, this->decompressorFilter); + if(FAILED(hr)) return hr; + + // Create tdshow filter + LPUNKNOWN pUnk = NULL; + this->frameRateFilter = new DSFrameRateFilter(FILTER_FRAMERATE, pUnk, &hr); + if(FAILED(hr) || this->frameRateFilter == NULL) return hr; + + // Create the NULL renderer + hr = COCREATE(CLSID_NullRenderer, IID_IBaseFilter, this->nullRendererFilter); + if(FAILED(hr)) return hr; + + // Add sample grabber to the graph + hr = this->graphBuilder->AddFilter(this->sampleGrabberFilter, FITLER_SAMPLE_GRABBER); + if(FAILED(hr)) return hr; + + // Add null renderer to the graph + hr = this->graphBuilder->AddFilter(this->nullRendererFilter, FILTER_NULL_RENDERER); + if(FAILED(hr)) return hr; + + // Add tdshow filter + hr = this->graphBuilder->AddFilter(this->frameRateFilter, FILTER_FRAMERATE); + if(FAILED(hr)) return hr; + + // Add AVIDec to the graph + hr = this->graphBuilder->AddFilter(this->decompressorFilter, FILTER_AVI_DECOMPRESSOR); + if(FAILED(hr)) return hr; + + // Find media control + hr = QUERY(this->graphBuilder, IID_IMediaControl, this->mediaController); + if(FAILED(hr)) return hr; + + // Disable timing + /*IMediaFilter *mediaFilterController; + hr = QUERY(this->graphBuilder, IID_IMediaFilter, mediaFilterController); + if(FAILED(hr)) return hr; + + mediaFilterController->SetSyncSource(NULL); + SAFE_RELEASE(mediaFilterController);*/ + + // Create the sample grabber + hr = QUERY(this->sampleGrabberFilter, IID_ISampleGrabber, this->grabberController); + if(FAILED(hr)) return hr; + + // Set the sample grabber media type (RGB24) + // TODO : CHECK + AM_MEDIA_TYPE mt; + ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE)); + mt.majortype = MEDIATYPE_Video; + mt.subtype = MEDIASUBTYPE_RGB24; + mt.formattype = FORMAT_VideoInfo; + + hr = this->grabberController->SetMediaType(&mt); + if(FAILED(hr)) return hr; + + // Set sample grabber media type + this->grabberController->SetOneShot(FALSE); + this->grabberController->SetBufferSamples(FALSE); + + hr = this->grabberController->SetCallback(this->grabberCallback, 1); + if(FAILED(hr)) return hr; +#endif + + return hr; +} \ No newline at end of file diff --git a/tinyDSHOW/src/DSCaptureUtils.cxx b/tinyDSHOW/src/DSCaptureUtils.cxx new file mode 100644 index 0000000..745473d --- /dev/null +++ b/tinyDSHOW/src/DSCaptureUtils.cxx @@ -0,0 +1,376 @@ +/* +* Copyright (C) 2009-2010 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "tsk_debug.h" + +#ifdef _WIN32_WCE +# include +#endif + +HRESULT enumerateCaptureDevices(const std::string &prefix, std::vector *names) +{ + HRESULT hr = S_OK; + +#ifdef _WIN32_WCE + + // FIXME: use FindNextDevice to query all devices + HANDLE handle = NULL; + DEVMGR_DEVICE_INFORMATION di; + + TCHAR pwzName[MAX_PATH]; memset(pwzName,NULL,MAX_PATH); + + GUID guidCamera = { 0xCB998A05, 0x122C, 0x4166, 0x84, 0x6A, + 0x93, 0x3E, 0x4D, 0x7E, 0x3C, 0x86 }; // http://msdn.microsoft.com/en-us/library/aa918757.aspx + + di.dwSize = sizeof(di); + + for( int i=0; ; i++) + { + if(0 == i) + { /* 1st time */ + handle = FindFirstDevice( DeviceSearchByGuid, &guidCamera, &di ); + if(!handle || !di.hDevice) + { + hr = ( HRESULT_FROM_WIN32( GetLastError() )); + goto bail; + } + } + else if(handle) + { /* 2nd or 3rd time */ + BOOL ret = FindNextDevice(handle, &di); + if(!ret || !di.hDevice) + { + /* No 2nd or 3rd camera ==> do not return error*/ + goto bail; + } + } + else assert(0); + + StringCchCopy( pwzName, MAX_PATH, di.szDeviceName ); + + /* from LPWSTR to LPSTR */ + char mbstr_name[MAX_PATH]; memset(mbstr_name,NULL,MAX_PATH); + wcstombs(mbstr_name, pwzName, MAX_PATH); + + VideoGrabberName grabberName(std::string((const char*)mbstr_name), std::string((const char*)mbstr_name)); + names->push_back(grabberName); + } + +bail: + /* close */ + if(handle) FindClose( handle ); + +#else + ICreateDevEnum *deviceEnum; + IEnumMoniker *enumerator; + IMoniker *moniker; + + // Create the System Device Enumerator + hr = COCREATE(CLSID_SystemDeviceEnum, IID_ICreateDevEnum, deviceEnum); + if (FAILED(hr)) goto bail; + + // Ask for a device enumerator + hr = deviceEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &enumerator, INCLUDE_CATEGORY_FLAG); + if (FAILED(hr)) goto bail; + + // hr = S_FALSE and enumerator is NULL if there is no device to enumerate + if (!enumerator) goto bail; + + USES_CONVERSION; + + while (enumerator->Next(1, &moniker, NULL) == S_OK) + { + // Get the properties bag for each device + IPropertyBag *propBag; + hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, reinterpret_cast(&propBag)); + if (FAILED(hr)) + { + SAFE_RELEASE(moniker); + continue; + } + + std::string name; + std::string description; + + VARIANT varName; + VariantInit(&varName); + VARIANT varDescription; + VariantInit(&varDescription); + + // Find the device path (uniqueness is guaranteed) + hr = propBag->Read(L"DevicePath", &varName, 0); + if (SUCCEEDED(hr)) + { + if (prefix != "") name = prefix + ":"; + name = name + std::string(W2A(varName.bstrVal)); + } + + // Find friendly name or the description + hr = propBag->Read(L"FriendlyName", &varDescription, 0); + if (SUCCEEDED(hr)) + { + description = std::string(W2A(varDescription.bstrVal)); + } + else + { + hr = propBag->Read(L"Description", &varDescription, 0); + if (SUCCEEDED(hr)) description = std::string(W2A(varDescription.bstrVal)); + } + + hr = VariantClear(&varName); + hr = VariantClear(&varDescription); + + SAFE_RELEASE(propBag); + SAFE_RELEASE(moniker); + + // Add it to the list + if (name != "") + { + VideoGrabberName grabberName(name, description); + names->push_back(grabberName); + } + } + +bail: + SAFE_RELEASE(enumerator); + SAFE_RELEASE(deviceEnum); +#endif + + return hr; +} + +HRESULT createSourceFilter(std::string *devicePath, IBaseFilter **sourceFilter) +{ + HRESULT hr; + + IEnumMoniker *enumerator = NULL; + IMoniker *moniker = NULL; + bool found = false; + + // Set sourceFilter to null + SAFE_RELEASE((*sourceFilter)); + +#ifdef _WIN32_WCE + CPropertyBag pBag; + HANDLE handle = NULL; + DEVMGR_DEVICE_INFORMATION di; + TCHAR pwzName[MAX_PATH]; + CComVariant varCamName; + IPersistPropertyBag *propBag = NULL; + GUID guidCamera = { 0xCB998A05, 0x122C, 0x4166, 0x84, 0x6A, + 0x93, 0x3E, 0x4D, 0x7E, 0x3C, 0x86 }; // http://msdn.microsoft.com/en-us/library/aa918757.aspx + + di.dwSize = sizeof(di); + + for( int i=0; ; i++) + { + if(0 == i) + { /* 1st time */ + handle = FindFirstDevice( DeviceSearchByGuid, &guidCamera, &di ); + if(!handle || !di.hDevice) + { + hr = ( HRESULT_FROM_WIN32( GetLastError() )); + goto bail; + } + } + else if(handle) + { /* 2nd or 3rd time */ + BOOL ret = FindNextDevice(handle, &di); + if(!ret || !di.hDevice) + { + /* No 2nd or 3rd camera ==> do not return error*/ + goto bail; + } + } + else assert(0); + + StringCchCopy( pwzName, MAX_PATH, di.szDeviceName ); + + /* from LPWSTR to LPSTR */ + char mbstr_name[MAX_PATH]; + memset(mbstr_name,NULL,MAX_PATH); + wcstombs(mbstr_name, pwzName, MAX_PATH); + + if(equals(std::string((const char*)mbstr_name), (*devicePath)) || ("0" == (*devicePath))) + { + varCamName = pwzName; + if( varCamName.vt != VT_BSTR ) + { + hr = E_OUTOFMEMORY; + goto bail; + } + + // Create Source filter + hr = COCREATE(CLSID_VideoCapture, IID_IBaseFilter, *sourceFilter); + if(FAILED(hr)) goto bail; + + // Query PropertyBag + hr = QUERY((*sourceFilter), IID_IPersistPropertyBag, propBag); + if(FAILED(hr)) goto bail; + + hr = pBag.Write( L"VCapName", &varCamName ); + if(FAILED(hr)) goto bail; + + hr = propBag->Load( &pBag, NULL ); + if(FAILED(hr)) goto bail; + } + } +#else + ICreateDevEnum *deviceEnum = NULL; + IPropertyBag *propBag = NULL; + + // Create the System Device Enumerator + hr = COCREATE(CLSID_SystemDeviceEnum, IID_ICreateDevEnum, deviceEnum); + if (FAILED(hr)){ + goto bail; + } + + // Ask for a device enumerator + hr = deviceEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &enumerator, INCLUDE_CATEGORY_FLAG); + if(FAILED(hr)){ + goto bail; + } + + // hr = S_FALSE and enumerator is NULL if there is no device to enumerate + if(!enumerator){ + goto bail; + } + + USES_CONVERSION; + + while (!found && (enumerator->Next(1, &moniker, NULL) == S_OK)){ + // Get the properties bag for each device + hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, reinterpret_cast(&propBag)); + if (FAILED(hr)){ + SAFE_RELEASE(moniker); + continue; + } + + std::string name; + + VARIANT varName; + VariantInit(&varName); + + // Find the device path (uniqueness is guaranteed) + hr = propBag->Read(L"DevicePath", &varName, 0); + if (SUCCEEDED(hr)) name = std::string(W2A(varName.bstrVal)); + + // Check for device path + // "Null" means first found + if ((name == (*devicePath)) || + ("Null" == (*devicePath))) + { + hr = moniker->BindToObject(0, 0, IID_IBaseFilter, reinterpret_cast(&(*sourceFilter))); + if (SUCCEEDED(hr)){ + (*devicePath) = name; + found = true; + } + } + + hr = VariantClear(&varName); + + SAFE_RELEASE(propBag); + SAFE_RELEASE(moniker); + } +#endif + +bail: +#ifdef _WIN32_WCE + if(handle) FindClose(handle); +#else + SAFE_RELEASE(deviceEnum); +#endif + SAFE_RELEASE(moniker); + SAFE_RELEASE(enumerator); + SAFE_RELEASE(propBag); + + return hr; +} + +HRESULT getSupportedFormats(IBaseFilter *sourceFilter, std::vector *formats) +{ + HRESULT hr = E_FAIL; + IPin *pinOut = NULL; + IAMStreamConfig *streamConfig = NULL; + AM_MEDIA_TYPE *mediaType = NULL; + int count, size; + + // Check source filter pointer + if (!sourceFilter) goto bail; + + pinOut = GetPin(sourceFilter, PINDIR_OUTPUT); + if(!pinOut) goto bail; + + // Retrieve the stream config interface + hr = QUERY(pinOut, IID_IAMStreamConfig, streamConfig); + if (FAILED(hr)) goto bail; + + // Get the number of capabilities + hr = streamConfig->GetNumberOfCapabilities(&count, &size); + if (FAILED(hr)) goto bail; + + hr = streamConfig->GetFormat(&mediaType); + if (FAILED(hr)) goto bail; + + // Iterate through the formats + for (int i = 0; i < count; i++){ + VIDEO_STREAM_CONFIG_CAPS streamConfigCaps; + + hr = streamConfig->GetStreamCaps(i, &mediaType, reinterpret_cast(&streamConfigCaps)); + + if (FAILED(hr)){ + TSK_DEBUG_ERROR("Failed to get Stream caps"); + break; + } + + if (streamConfigCaps.guid == FORMAT_VideoInfo){ + VIDEOINFOHEADER* vih = reinterpret_cast(mediaType->pbFormat); + BITMAPINFOHEADER* bih = &vih->bmiHeader; + + int width = abs(bih->biWidth); + int height = abs(bih->biHeight); + int fps = (int) ((float)(vih->AvgTimePerFrame)/10000.f); + GUID chroma = mediaType->subtype; + + // Add format to the list + DSCaptureFormat format(width, height, fps, chroma); + formats->push_back(format); + } + + DeleteMediaType(mediaType); + } + +bail: + SAFE_RELEASE(streamConfig); + SAFE_RELEASE(pinOut); + + return hr; +} diff --git a/tinyDSHOW/src/DSDisplay.cxx b/tinyDSHOW/src/DSDisplay.cxx new file mode 100644 index 0000000..9c8c372 --- /dev/null +++ b/tinyDSHOW/src/DSDisplay.cxx @@ -0,0 +1,581 @@ +/* +* Copyright (C) 2009-2010 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include +#include + +#include "tsk_list.h" +#include "tsk_debug.h" + +#include + +using namespace std; + +#define USE_OVERLAY 0 +#define OVERLAY_TIMEOUT 3 +#define WM_GRAPHNOTIFY WM_APP + 1 + +#define FSCREEN_MIN_IDEAL_WIDTH 352 +#define FSCREEN_MIN_IDEAL_HEIGHT 288 + +typedef struct tdshow_display_s +{ + TSK_DECLARE_OBJECT; + + HWND hwnd; + DSDisplay* display; +} +tdshow_display_t; +typedef tsk_list_t tdshow_displays_L_t; +TINYDSHOW_GEXTERN const tsk_object_def_t *tdshow_display_def_t; + +// Static list to find which display is link to a given hWnd +static tdshow_displays_L_t* __directshow__Displays = tsk_null; + +/*== Predicate function to find tdshow_display_t object by HWND. */ +static int __pred_find_display_by_hwnd(const tsk_list_item_t *item, const void *hWnd) +{ + if(item && item->data){ + const tdshow_display_t *display = (const tdshow_display_t *)item->data; + return (display->hwnd - *((HWND*)hWnd)); + } + return -1; +} + +// C Callback that dispatch event to the right display +LRESULT CALLBACK __directshow__WndProcWindow(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + LRESULT result = FALSE; + + if(__directshow__Displays){ + tsk_list_lock(__directshow__Displays); + + const tdshow_display_t *display = (const tdshow_display_t *)tsk_list_find_object_by_pred(__directshow__Displays, __pred_find_display_by_hwnd, &hWnd); + if(display && display->display){ + result = display->display->handleEvents(hWnd, uMsg, wParam, lParam); + } + + tsk_list_unlock(__directshow__Displays); + } + + return result; +} + + + +DSDisplay::DSDisplay(HRESULT *hr) +{ + this->window = NULL; + this->parentWindowProc = NULL; + this->hooked = FALSE; + this->fullscreen = FALSE; + this->width = 176; + this->height = 144; + this->fps = 15; + + this->graph = new DSDisplayGraph(hr); + if (FAILED(*hr)) return; +#if USE_OVERLAY + this->overlay = new DSDisplayOverlay(); +#else + this->overlay = NULL; +#endif + + this->graph->getVideoWindow()->put_Visible(OAFALSE); +} + +DSDisplay::~DSDisplay() +{ + this->unhook(); + + SAFE_DELETE_PTR(this->overlay); + SAFE_DELETE_PTR(this->graph); +} + +void DSDisplay::start() +{ + if (!this->graph->isRunning()){ + this->hook(); + this->graph->start(); + } + this->graph->getVideoWindow()->put_Visible(OATRUE); +} + +void DSDisplay::stop() +{ + if (this->graph->isRunning()){ + this->setFullscreen(false); + + this->graph->stop(); + this->unhook(); + } +} + +void DSDisplay::attach(INT64 parent) +{ + this->attach((void*)parent); +} + +void DSDisplay::attach(void *parent) +{ + // Don't reattach if this is the same parent + if (this->isAttached() && parent){ + HWND hwnd = reinterpret_cast(parent); + if (hwnd != this->window){ + this->detach(); + } + } + + // Gets the handle of the parent + this->window = reinterpret_cast(parent); + // Hook to the parent WindowProc + this->hook(); + +#if USE_OVERLAY + // Allows the overlay to initialize + this->overlay->attach(this->window, this->graph); +#endif +} + +void DSDisplay::detach(void *parent) +{ + // The detach action is only valid and if this is the same parent + if (parent){ + HWND hwnd = reinterpret_cast(parent); + if (hwnd == this->window){ + this->detach(); + } + } +} + +void DSDisplay::detach() +{ + if (!this->isAttached()){ + return; + } + +#if USE_OVERLAY + // Clean up overlay + this->overlay->detach(); +#endif + + // Unhook from the parent WindowProc + this->unhook(); + + // Set the handle of the parent to NULL + this->window = NULL; +} + +bool DSDisplay::isAttached() +{ + return (this->window != NULL); +} + +int DSDisplay::getWidth() +{ + return this->width; +} + +int DSDisplay::getHeight() +{ + return this->height; +} + +void DSDisplay::setSize(int w, int h) +{ + this->width = w; + this->height = h; + + if (!this->fullscreen){ + this->graph->setImageFormat(w, h); + if(this->hooked){ +#if defined(VMR9_WINDOWLESS) + RECT rc; + SetRect(&rc, 0, 0, w, h); + this->graph->getWindowlessControl()->SetVideoPosition(&rc, &rc); +#else + this->graph->getVideoWindow()->SetWindowPosition(0, 0, this->width , this->height); +#endif + } + } +} + +void DSDisplay::applyRatio(RECT rect) +{ + long w = rect.right - rect.left; + long h = rect.bottom - rect.top; + float ratio = ((float)176/(float)144);//FIXME + // (w/h)=ratio => + // 1) h=w/ratio + // and + // 2) w=h*ratio + this->width = (int)(w/ratio) > h ? (int)(h * ratio) : w; + this->height = (int)(this->width/ratio) > h ? h : (int)(this->width/ratio); +} + +bool DSDisplay::isFullscreen() +{ +#if defined(VMR9_WINDOWLESS) + // TODO +#else + long result; + HRESULT hr = this->graph->getVideoWindow()->get_FullScreenMode(&result); + if (SUCCEEDED(hr)){ + this->fullscreen = (result == OATRUE); + } + else{ + TSK_DEBUG_ERROR("get_FullScreenMode failed with %ld", hr); + this->fullscreen = FALSE; + } +#endif + return this->fullscreen; +} + +void DSDisplay::setFullscreen(bool value) +{ + if(!this->canFullscreen()){ + return; + } + + HRESULT hr; + +#if defined(VMR9_WINDOWLESS) + // TODO +#else + if (this->isFullscreen() == value){ + return; + } + + hr = this->graph->getVideoWindow()->put_FullScreenMode(value ? OATRUE : OAFALSE); + if (SUCCEEDED(hr)){ + this->fullscreen = value; +#if USE_OVERLAY + this->overlay->show(this->fullscreen ? (OVERLAY_TIMEOUT * this->graph->getDisplayFps()) : 0); +#endif + } + else{ + TSK_DEBUG_ERROR("put_FullScreenMode failed with %ld", hr); + } +#endif +} + +bool DSDisplay::canFullscreen() +{ +#if defined(VMR9_WINDOWLESS) + // TODO +#else + if(this->graph){ + UINT image_w, image_h; + + if( this->graph->getImageFormat(image_w, image_h) ){ + //this->graph->getVideoWindow()->GetMinIdealImageSize(&ideal_w, &ideal_h); + return (((long)image_w >= FSCREEN_MIN_IDEAL_WIDTH) && ((long)image_h >= FSCREEN_MIN_IDEAL_HEIGHT)); + } + } +#endif + return false; +} + +void DSDisplay::setFps(int fps_) +{ + this->fps = fps_; + this->graph->setDisplayFps(fps_); +} + + +// w and h are the size of the buffer not the display +void DSDisplay::handleVideoFrame(const void* data, int w, int h) +{ + if (this->graph->isRunning()){ + // The graph will take care of changing the source filter if needed + // in case of dimension change or anything else... + this->graph->handleFrame(data, w, h); +#if USE_OVERLAY + this->overlay->update(); +#endif + } +} + +LRESULT DSDisplay::handleEvents(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_CREATE: + case WM_SIZE: + case WM_MOVE: + { + RECT rect = {0}; + GetWindowRect(hWnd, &rect); + applyRatio(rect); + +#if defined(VMR9_WINDOWLESS) + this->graph->getWindowlessControl()->SetVideoPosition(&rect, &rect); +#else + this->graph->getVideoWindow()->SetWindowPosition(0, 0, this->width , this->height); +#endif + } + break; + + case WM_LBUTTONDBLCLK: + if(this->canFullscreen()){ + this->setFullscreen(true); + } + + break; + + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_KEYDOWN: + if(this->isFullscreen()) + { +#if USE_OVERLAY + // Re-Show overlay + this->overlay->show(OVERLAY_TIMEOUT * this->graph->getDisplayFps()); +#endif + } + break; + + case WM_CHAR: + case WM_KEYUP: + if(this->isFullscreen() && (wParam == 0x1B || wParam == VK_ESCAPE)) + { + // escape + this->setFullscreen(false); + } + + break; + + case WM_GRAPHNOTIFY: + { + long evCode, param1, param2; + HRESULT hr; + while (hr = this->graph->getMediaEvent()->GetEvent(&evCode, ¶m1, ¶m2, 0), SUCCEEDED(hr)) + { + hr = this->graph->getMediaEvent()->FreeEventParams(evCode, param1, param2); + + switch(evCode) + { + case EC_FULLSCREEN_LOST: +#if USE_OVERLAY + this->overlay->show(0); +#endif + break; + case EC_COMPLETE: + case EC_USERABORT: + default: + break; + } + } + } + break; + +#if defined(VMR9_WINDOWLESS) + case WM_DISPLAYCHANGE: + { + this->graph->getWindowlessControl()->DisplayModeChanged(); + } + break; + case WM_PAINT: + { + RECT rect = {0}; + GetWindowRect(hWnd, &rect); + + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + + this->graph->getWindowlessControl()->RepaintVideo(hWnd, hdc); + + EndPaint(hWnd, &ps); + } + break; +#endif + } + + return CallWindowProc(this->parentWindowProc, hWnd, uMsg, wParam, lParam); +} + +void DSDisplay::hook() +{ + HRESULT hr; + + if (!this->window){ + return; + } + + if(this->hooked){ + return; + } + this->hooked = TRUE; + + tsk_list_lock(__directshow__Displays); + { + // Gets the parent Window procedure +#if defined(_WIN32_WCE) + // Workaround for bug in SetWindowLong, call twice the API + //this->parentWindowProc = (WNDPROC)SetWindowLong( this->window, GWL_WNDPROC, (LONG) __directshow__WndProcWindow ); + //this->parentWindowProc = (WNDPROC)SetWindowLong( this->window, GWL_WNDPROC, (LONG) __directshow__WndProcWindow ); + //__directshow__Displays[this->window] = this; +#else + this->parentWindowProc = (WNDPROC) SetWindowLongPtr(this->window, GWL_WNDPROC, (LONG) __directshow__WndProcWindow); + // Add this instance to the callback map + tsk_object_new(tdshow_display_def_t, this->window, this); +#endif + } + tsk_list_unlock(__directshow__Displays); + + RECT rect; + GetWindowRect(this->window, &rect); + applyRatio(rect); + +#if defined(VMR9_WINDOWLESS) + rect.left = 0; + rect.top = 0; + rect.right = this->width; + rect.bottom = this->height; + + // TODO : Review + hr = this->graph->getWindowlessControl()->SetVideoClippingWindow(this->window); + hr = this->graph->getWindowlessControl()->SetBorderColor(RGB(0, 0, 128)); + hr = this->graph->getWindowlessControl()->SetVideoPosition(NULL, &rect); +#else + // TODO : Review the order + hr = this->graph->getVideoWindow()->put_Owner((OAHWND) this->window); + hr = this->graph->getVideoWindow()->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); + hr = this->graph->getVideoWindow()->SetWindowPosition(0, 0, this->width, this->height); + hr = this->graph->getVideoWindow()->put_MessageDrain((OAHWND) this->window); + hr = this->graph->getVideoWindow()->put_Visible(OATRUE); +#endif + + hr = this->graph->getMediaEvent()->SetNotifyWindow((OAHWND) this->window, WM_GRAPHNOTIFY, 0); +} + +void DSDisplay::unhook() +{ + HRESULT hr; + + if(!this->window){ + return; + } + + if(!this->hooked){ + return; + } + + hr = this->graph->getMediaEvent()->SetNotifyWindow(NULL, WM_GRAPHNOTIFY, 0); + +#if defined(VMR9_WINDOWLESS) + // TODO : Review + hr = this->graph->getWindowlessControl()->SetVideoClippingWindow(NULL); +#else + // TODO : Review the order + hr = this->graph->getVideoWindow()->put_Visible(OAFALSE); + hr = this->graph->getVideoWindow()->put_MessageDrain((OAHWND) NULL); + hr = this->graph->getVideoWindow()->put_Owner((OAHWND) NULL); + hr = this->graph->getVideoWindow()->put_AutoShow(OAFALSE); +#endif + + + tsk_list_lock(__directshow__Displays); + { + // Remove this instance from the callback map + tsk_list_remove_item_by_pred(__directshow__Displays, __pred_find_display_by_hwnd, &this->window); + // Restore parent Window procedure +#if defined(_WIN32_WCE) + // Workaround for bug in SetWindowLong, call twice the API + //this->parentWindowProc = (WNDPROC)SetWindowLong( this->window, GWL_WNDPROC, (LONG) this->parentWindowProc ); + //this->parentWindowProc = (WNDPROC)SetWindowLong( this->window, GWL_WNDPROC, (LONG) this->parentWindowProc ); +#else + SetWindowLongPtr(this->window, GWL_WNDPROC, (LONG) this->parentWindowProc); +#endif + } + tsk_list_unlock(__directshow__Displays); + + this->hooked = FALSE; +} + + + + + + + + + + + + + + +//================================================================================================= +// String object definition +// +static tsk_object_t* tdshow_display_ctor(tsk_object_t * self, va_list * app) +{ + tdshow_display_t *display = (tdshow_display_t *)self; + + if(display){ + display->hwnd = va_arg(*app, HWND); + display->display = va_arg(*app, DSDisplay*); + + if(!__directshow__Displays){ + __directshow__Displays = tsk_list_create(); + } + tsk_list_push_back_data(__directshow__Displays, (void**)&display); + } + + return self; +} + +static tsk_object_t* tdshow_display_dtor(tsk_object_t * self) +{ + tdshow_display_t *display = (tdshow_display_t *)self; + if(display){ + if(__directshow__Displays){ + tsk_list_remove_item_by_data(__directshow__Displays, display); + if(TSK_LIST_IS_EMPTY(__directshow__Displays)){ + TSK_OBJECT_SAFE_FREE(__directshow__Displays); + } + } + } + + return self; +} + +static int tdshow_display_cmp(const tsk_object_t *_d1, const tsk_object_t *_d2) +{ + const tdshow_display_t *d1 = (const tdshow_display_t *)_d1; + const tdshow_display_t *d2 = (const tdshow_display_t *)_d2; + + if(d1 && d2){ + return (d1->hwnd - d2->hwnd); + } + else if(!d1 && !d2) return 0; + else return -1; +} + +static const tsk_object_def_t tdshow_display_def_s = +{ + sizeof(tdshow_display_t), + tdshow_display_ctor, + tdshow_display_dtor, + tdshow_display_cmp, +}; +extern const tsk_object_def_t *tdshow_display_def_t = &tdshow_display_def_s; diff --git a/tinyDSHOW/src/DSDisplayGraph.cxx b/tinyDSHOW/src/DSDisplayGraph.cxx new file mode 100644 index 0000000..58f9533 --- /dev/null +++ b/tinyDSHOW/src/DSDisplayGraph.cxx @@ -0,0 +1,330 @@ +/* +* Copyright (C) 2009-2010 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#if defined(VMR9) +#define DIRECT3D_VERSION 0x0900 +#endif + +#include +#include +#include + +#include "tsk_debug.h" + +#include + +using namespace std; + +DSDisplayGraph::DSDisplayGraph(HRESULT *hr) +{ + this->running = FALSE; + this->fps = 15; + + this->graphBuilder = NULL; + + this->sourceFilter = NULL; + this->colorspaceConverterFilter = NULL; + this->videoRendererFilter = NULL; + + this->mediaController = NULL; + this->mediaEvent = NULL; + this->videoWindow = NULL; + +#if defined(VMR) ||defined(VMR9) || defined(VMR9_WINDOWLESS) + this->mixerBitmap = NULL; + this->filterConfig = NULL; +#endif + +#if defined(VMR9_WINDOWLESS) + this->windowlessControl = NULL; +#endif + + *hr = this->createDisplayGraph(); + if (FAILED(*hr)) return; + + *hr = this->connect(); + if (FAILED(*hr)) return; +} + +DSDisplayGraph::~DSDisplayGraph() +{ + this->disconnect(); + +#if defined(VMR9_WINDOWLESS) + SAFE_RELEASE(this->windowlessControl); +#endif + +#if defined(VMR) ||defined(VMR9) || defined(VMR9_WINDOWLESS) + SAFE_RELEASE(this->filterConfig); + SAFE_RELEASE(this->mixerBitmap); +#endif + + SAFE_RELEASE(this->videoWindow); + SAFE_RELEASE(this->mediaEvent); + SAFE_RELEASE(this->mediaController); + + SAFE_RELEASE(this->colorspaceConverterFilter); + SAFE_RELEASE(this->videoRendererFilter); + //SAFE_RELEASE(this->sourceFilter); + + SAFE_RELEASE(this->graphBuilder); +} + +void DSDisplayGraph::setDisplayFps(int fps_) +{ + this->fps = fps_; + if(this->sourceFilter){ + this->sourceFilter->setFps(fps_); + } +} + +bool DSDisplayGraph::getImageFormat(UINT &width, UINT &height) +{ + if(this->sourceFilter){ + return this->sourceFilter->getImageFormat(width, height); + } + return false; +} + +bool DSDisplayGraph::setImageFormat(UINT width, UINT height) +{ + bool ret = true; + if(this->sourceFilter){ + UINT w=width, h = height; + if(this->sourceFilter->getImageFormat(w, h)){ + if(w!= width || h!=height){ // Image format has changed + bool reconnect = this->connected; // IMPORTANT: Must reconnect all elements + HRESULT hr; + if(reconnect){ + if((hr = this->disconnect()) != S_OK){ + return false; + } + } + ret = (this->sourceFilter->setImageFormat(width, height) == S_OK); + if(reconnect){ + if((hr = this->connect())){ + return false; + } + } + } + } + } + return ret; +} + +HRESULT DSDisplayGraph::connect() +{ + HRESULT hr; + + if((hr = ConnectFilters(this->graphBuilder, this->sourceFilter, this->colorspaceConverterFilter)) != S_OK){ + TSK_DEBUG_ERROR("Failed to connect sourcefilter with the colorspace"); + return hr; + } + if((hr = ConnectFilters(this->graphBuilder, this->colorspaceConverterFilter, this->videoRendererFilter)) != S_OK){ + TSK_DEBUG_ERROR("Failed to connect colorspace with the videorenderer"); + return hr; + } + + this->connected = true; + return S_OK; +} + +HRESULT DSDisplayGraph::disconnect() +{ + HRESULT hr; + + if((hr = DisconnectFilters(this->graphBuilder, this->sourceFilter, this->colorspaceConverterFilter)) != S_OK){ + TSK_DEBUG_ERROR("Failed to disconnect sourcefilter with the colorspace"); + return hr; + } + if((hr = DisconnectFilters(this->graphBuilder, this->colorspaceConverterFilter, this->videoRendererFilter)) != S_OK){ + TSK_DEBUG_ERROR("Failed to connect colorspace with the videorenderer"); + return hr; + } + + this->connected = false; + return S_OK; +} + +HRESULT DSDisplayGraph::start() +{ + HRESULT hr; + this->running = true; + this->sourceFilter->reset(); + + hr = this->mediaController->Run(); + if (!SUCCEEDED(hr)){ + TSK_DEBUG_ERROR("DSDisplayGraph::mediaController->Run() has failed with %ld", hr); + } + return hr; +} + +HRESULT DSDisplayGraph::stop() +{ + HRESULT hr; + + hr = this->mediaController->Pause(); + if (hr == S_FALSE){ + TSK_DEBUG_ERROR("DSDisplayGraph::mediaController->Pause() has failed with %ld. Waiting for transition.", hr); + FILTER_STATE pfs; + hr = this->mediaController->GetState(2500, (OAFilterState*) &pfs); + } + + hr = this->mediaController->Stop(); + if (!SUCCEEDED(hr)){ + TSK_DEBUG_ERROR("DSDisplayGraph::mediaController->Stop() has failed with %ld", hr); + } + + this->running = false; + + return hr; +} + +bool DSDisplayGraph::isRunning() +{ + return this->running; +} + +void DSDisplayGraph::handleFrame(const void* data, int w, int h) +{ + HRESULT hr; + + if(!this->sourceFilter){ + TSK_DEBUG_ERROR("Invalid parameter"); + return; + } + + if(!data || !this->running){ + this->sourceFilter->setBuffer(NULL, (w*h*3)); + return; + } + + hr = this->sourceFilter->setImageFormat(w, h); + if (hr == S_OK){ + this->stop(); + + this->disconnect(); + this->connect(); + + this->start(); + } + + this->sourceFilter->setBuffer((void*)data, (w*h*3)); +} + +HRESULT DSDisplayGraph::createDisplayGraph() +{ + HRESULT hr; + + // Create the graph builder + hr = COCREATE(CLSID_FilterGraph, IID_IGraphBuilder, this->graphBuilder); + if(FAILED(hr)) return hr; + + + // Create my custom filter + LPUNKNOWN pUnk = NULL; + this->sourceFilter = new DSOutputFilter(pUnk, &hr /*, this*/); + if(FAILED(hr) || this->sourceFilter == NULL) return hr; + + // Create the color space convertor filter + hr = COCREATE(CLSID_Colour, IID_IBaseFilter, this->colorspaceConverterFilter); + if(FAILED(hr)) return hr; + +#if defined(VMR) + // Create the video mixing renderer based on Direct X + hr = COCREATE(CLSID_VideoMixingRenderer, IID_IBaseFilter, this->videoRendererFilter); + if(FAILED(hr)) return hr; +#elif defined(VMR9) || defined(VMR9_WINDOWLESS) + // Create the video mixing renderer based on Direct X 9.0 + hr = COCREATE(CLSID_VideoMixingRenderer9, IID_IBaseFilter, this->videoRendererFilter); + if(FAILED(hr)) return hr; +#else + // Create the video renderer + hr = COCREATE(CLSID_VideoRenderer, IID_IBaseFilter, this->videoRendererFilter); + if(FAILED(hr)) return hr; +#endif + + + // Add dource filter to the graph + hr = this->graphBuilder->AddFilter(this->sourceFilter, FILTER_OUTPUT); + if(FAILED(hr)) return hr; + + // Add the color space convertor to the graph + hr = this->graphBuilder->AddFilter(this->colorspaceConverterFilter, FILTER_COLORSPACE_CONVERTOR); + if(FAILED(hr)) return hr; + + // Add video renderer to the graph + hr = this->graphBuilder->AddFilter(this->videoRendererFilter, FILTER_VIDEO_RENDERER); + if(FAILED(hr)) return hr; + + + // Find media control + hr = QUERY(this->graphBuilder, IID_IMediaControl, this->mediaController); + if(FAILED(hr)) return hr; + + // Find media event + hr = QUERY(this->graphBuilder, IID_IMediaEventEx, this->mediaEvent); + if(FAILED(hr)) return hr; + // hr = this->mediaEvent->SetNotifyFlags(AM_MEDIAEVENT_NONOTIFY); + + +#if defined(VMR) + // Find the bitmap mixer (Direct X) + hr = QUERY(this->videoRendererFilter, IID_IVMRMixerBitmap, this->mixerBitmap); + if(FAILED(hr)) return hr; + + // Find the bitmap configurer (Direct X) + hr = QUERY(this->videoRendererFilter, IID_IVMRFilterConfig, this->filterConfig); + if(FAILED(hr)) return hr; + + // Set the number of streams (Direct X) + hr = this->filterConfig->SetNumberOfStreams(1); + if(FAILED(hr)) return hr; +#elif defined(VMR9) || defined(VMR9_WINDOWLESS) + // Find the bitmap mixer (Direct X 9.0) + hr = QUERY(this->videoRendererFilter, IID_IVMRMixerBitmap9, this->mixerBitmap); + if(FAILED(hr)) return hr; + + // Find the bitmap configurer (Direct X 9.0) + hr = QUERY(this->videoRendererFilter, IID_IVMRFilterConfig9, this->filterConfig); + if(FAILED(hr)) return hr; + + // Set the number of streams (Direct X 9.0) + hr = this->filterConfig->SetNumberOfStreams(1); + if(FAILED(hr)) return hr; +#endif + +#if defined(VMR9_WINDOWLESS) + // Set the rendering mode (Direct X 9.0) + hr = this->filterConfig->SetRenderingMode(VMR9Mode_Windowless); + if(FAILED(hr)) return hr; + + // Find the windowless control (Direct X 9.0) + hr = QUERY(this->videoRendererFilter, IID_IVMRWindowlessControl9, this->windowlessControl); + if(FAILED(hr)) return hr; +#else + // Find IVideoWindow interface + hr = QUERY(this->graphBuilder, IID_IVideoWindow, this->videoWindow); + if(FAILED(hr)) return hr; +#endif + + return hr; +} diff --git a/tinyDSHOW/src/DSDisplayOverlay.VMR.cxx b/tinyDSHOW/src/DSDisplayOverlay.VMR.cxx new file mode 100644 index 0000000..37b0b11 --- /dev/null +++ b/tinyDSHOW/src/DSDisplayOverlay.VMR.cxx @@ -0,0 +1,179 @@ +/* +* Copyright (C) 2009-2010 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#if defined(VMR) + +#include +#include +#include +#include "../../resource.h" + +using namespace std; + +#define ALPHA_VALUE_START 0.8f +#define ALPHA_VALUE_STOP 0.0f + + +// Hack to get module of the current code +// Only works with Microsoft Linker and could break in the future +EXTERN_C IMAGE_DOS_HEADER __ImageBase; + + +DSDisplayOverlay::DSDisplayOverlay() +{ + this->window = NULL; + this->hdcBmp = NULL; + this->hbmOld = NULL; +} + +DSDisplayOverlay::~DSDisplayOverlay() +{ +} + +void DSDisplayOverlay::attach(HWND parent, DSDisplayGraph *graph) +{ + HRESULT hr; + + // Gets the handle of the parent and the graph + this->window = parent; + this->displayGraph = graph; + + if (this->window) + { + // Hack to get module of the current code + TCHAR *modulePath = (TCHAR *) calloc(255, sizeof(TCHAR)); + GetModuleFileName((HINSTANCE)&__ImageBase, modulePath, 255); + HMODULE module = GetModuleHandle(modulePath); + delete[] modulePath; + if (!module) + { + cout << "Failed to get current module"; + return; + } + + HBITMAP bitmap = LoadBitmap(module, MAKEINTRESOURCE(IDB_BITMAP_OVERLAY)); + if (!bitmap) + { + cout << "Failed to load overlay bitmap" << endl; + return; + } + + RECT rect; + hr = GetWindowRect(this->window, &rect); + if (FAILED(hr)) + { + cout << "Failed to get window size" << endl; + return; + } + + BITMAP bm; + HDC hdc = GetDC(this->window); + this->hdcBmp = CreateCompatibleDC(hdc); + ReleaseDC(this->window, hdc); + + GetObject(bitmap, sizeof(bm), &bm); + this->hbmOld= (HBITMAP) SelectObject(this->hdcBmp, bitmap); + + ZeroMemory(&this->alphaBitmap, sizeof(VMRALPHABITMAP)); + this->alphaBitmap.dwFlags = VMRBITMAP_HDC | VMRBITMAP_SRCCOLORKEY; + this->alphaBitmap.hdc = this->hdcBmp; + this->alphaBitmap.clrSrcKey = 0x00FF00FF; + // Source rectangle + this->alphaBitmap.rSrc.left = 0; + this->alphaBitmap.rSrc.top = 0; + this->alphaBitmap.rSrc.right = bm.bmWidth; + this->alphaBitmap.rSrc.bottom = bm.bmHeight; + // Destination rectangle + this->alphaBitmap.rDest.left = (rect.right - rect.left - bm.bmWidth) / 2.0; + this->alphaBitmap.rDest.top = (rect.bottom - rect.top - bm.bmHeight) / 2.0; + this->alphaBitmap.rDest.right = this->alphaBitmap.rDest.left + bm.bmWidth; + this->alphaBitmap.rDest.bottom = this->alphaBitmap.rDest.top + bm.bmHeight; + this->alphaBitmap.rDest.left /= (rect.right - rect.left); + this->alphaBitmap.rDest.top /= (rect.bottom - rect.top); + this->alphaBitmap.rDest.right /= (rect.right - rect.left); + this->alphaBitmap.rDest.bottom /= (rect.bottom - rect.top); + // Alpha value for start + this->alphaBitmap.fAlpha = ALPHA_VALUE_START; + + } +} + +void DSDisplayOverlay::detach() +{ + // Clean up + DeleteObject(SelectObject(this->hdcBmp, this->hbmOld)); + DeleteDC(this->hdcBmp); + + this->hdcBmp = NULL; + this->hbmOld = NULL; + this->displayGraph = NULL; + this->window = NULL; +} + +void DSDisplayOverlay::show(int value) +{ + // Store the ticks to count down + this->ticks = value; + + // Compute alpha value decrement + this->alphaStep = (this->ticks > 0) ? ((ALPHA_VALUE_START - ALPHA_VALUE_STOP) / this->ticks) : 0; + this->alphaBitmap.fAlpha = ALPHA_VALUE_START; + + this->internalUpdate(); +} + +void DSDisplayOverlay::update() +{ + if (this->displayGraph && (this->ticks > 0)) + { + this->ticks--; + + // Be sure alpha is in 0.0 .. 1.0 range. + float value = this->alphaBitmap.fAlpha; + value -= this->alphaStep; + this->alphaBitmap.fAlpha = (value >= 0.0f) ? value : 0.0f; + + this->internalUpdate(); + } +} + +void DSDisplayOverlay::internalUpdate() +{ + HRESULT hr; + + if (this->ticks > 0) + { + this->alphaBitmap.dwFlags = VMRBITMAP_HDC | VMRBITMAP_SRCCOLORKEY; + } + else + { + this->alphaBitmap.dwFlags = VMRBITMAP_DISABLE; + } + + hr = this->displayGraph->getMixerBitmap()->SetAlphaBitmap(&this->alphaBitmap); + if (FAILED(hr)) + { + cout << "Failed to mix overylay (" << hr << ")" << endl; + return; + } +} + +#endif diff --git a/tinyDSHOW/src/DSDisplayOverlay.VMR9.cxx b/tinyDSHOW/src/DSDisplayOverlay.VMR9.cxx new file mode 100644 index 0000000..fd1e661 --- /dev/null +++ b/tinyDSHOW/src/DSDisplayOverlay.VMR9.cxx @@ -0,0 +1,211 @@ +/* +* Copyright (C) 2009-2010 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#if defined(VMR9) || defined(VMR9_WINDOWLESS) + +#define DIRECT3D_VERSION 0x0900 + +#include +#include +#include + +using namespace std; + +#define FILENAME _T("Overlay.png") +#define ALPHA_VALUE_START 0.8f +#define ALPHA_VALUE_STOP 0.0f + + +DSDisplayOverlay::DSDisplayOverlay() +{ + this->window = NULL; + this->direct3DDevice = NULL; + this->direct3DSurface = NULL; + + this->direct3D = Direct3DCreate9(D3D_SDK_VERSION); + if (!this->direct3D) + { + cout << "Cannot create Direct3D environment" << endl; + return; + } +} + +DSDisplayOverlay::~DSDisplayOverlay() +{ + SAFE_RELEASE(this->direct3D); +} + +void DSDisplayOverlay::attach(HWND parent, DSDisplayGraph *graph) +{ + HRESULT hr; + + // Gets the handle of the parent and the graph + this->window = parent; + this->displayGraph = graph; + + if (this->window) + { + D3DPRESENT_PARAMETERS d3dpp; + ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS)); + d3dpp.Windowed = TRUE; + d3dpp.SwapEffect = D3DSWAPEFFECT_COPY; + + hr = this->direct3D->CreateDevice( + D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + this->window, + D3DCREATE_SOFTWARE_VERTEXPROCESSING, + &d3dpp, + &this->direct3DDevice); + if (FAILED(hr)) + { + cout << "Cannot create Direct3D device" << endl; + return; + } + + ZeroMemory(&this->overlayInfo, sizeof(D3DXIMAGE_INFO)); + hr = D3DXGetImageInfoFromFile(FILENAME, &this->overlayInfo); + if (FAILED(hr)) + { + cout << "Cannot stat overlay file" << endl; + return; + } + + hr = this->direct3DDevice->CreateOffscreenPlainSurface( + this->overlayInfo.Width, + this->overlayInfo.Height, + D3DFMT_A8R8G8B8, + D3DPOOL_SYSTEMMEM, + &this->direct3DSurface, + NULL); + if (FAILED(hr)) + { + cout << "Cannot create Direct3D surface" << endl; + return; + } + + D3DCOLOR alphaKey = 0xFF000000; + + hr = D3DXLoadSurfaceFromFile(this->direct3DSurface, + NULL, + NULL, + FILENAME, + NULL, + D3DX_FILTER_NONE, + alphaKey, + &this->overlayInfo); + if (FAILED(hr)) + { + cout << "Cannot load overlay file" << endl; + return; + } + + D3DVIEWPORT9 viewport; + ZeroMemory(&viewport, sizeof(D3DVIEWPORT9)); + + hr= this->direct3DDevice->GetViewport(&viewport); + if (FAILED(hr)) + { + cout << "Cannot get view port" << endl; + return; + } + + ZeroMemory(&this->alphaBitmap, sizeof(VMR9AlphaBitmap)); + this->alphaBitmap.dwFlags = VMR9AlphaBitmap_EntireDDS; + this->alphaBitmap.hdc = NULL; + this->alphaBitmap.pDDS = this->direct3DSurface; + // Source rectangle + this->alphaBitmap.rSrc.left = 0; + this->alphaBitmap.rSrc.top = 0; + this->alphaBitmap.rSrc.right = this->overlayInfo.Width; + this->alphaBitmap.rSrc.bottom = this->overlayInfo.Height; + // Destination rectangle + this->alphaBitmap.rDest.left = (viewport.Width - this->overlayInfo.Width) / 2.0; + this->alphaBitmap.rDest.top = (viewport.Height - this->overlayInfo.Height) / 2.0; + this->alphaBitmap.rDest.right = this->alphaBitmap.rDest.left + this->overlayInfo.Width; + this->alphaBitmap.rDest.bottom = this->alphaBitmap.rDest.top + this->overlayInfo.Height; + this->alphaBitmap.rDest.left /= viewport.Width; + this->alphaBitmap.rDest.top /= viewport.Height; + this->alphaBitmap.rDest.right /= viewport.Width; + this->alphaBitmap.rDest.bottom /= viewport.Height; + // Alpha value for start + this->alphaBitmap.fAlpha = ALPHA_VALUE_START; + } +} + +void DSDisplayOverlay::detach() +{ + SAFE_RELEASE(this->direct3DSurface); + SAFE_RELEASE(this->direct3DDevice); + + this->displayGraph = NULL; + this->window = NULL; +} + +void DSDisplayOverlay::show(int value) +{ + // Store the ticks to count down + this->ticks = value; + + // Compute alpha value decrement + this->alphaStep = (this->ticks > 0) ? ((ALPHA_VALUE_START - ALPHA_VALUE_STOP) / this->ticks) : 0; + this->alphaBitmap.fAlpha = ALPHA_VALUE_START; + + this->internalUpdate(); +} + +void DSDisplayOverlay::update() +{ + if (this->displayGraph && (this->ticks > 0)) + { + this->ticks--; + + // Be sure alpha is in 0.0 .. 1.0 range. + float value = this->alphaBitmap.fAlpha; + value -= this->alphaStep; + this->alphaBitmap.fAlpha = (value >= 0.0f) ? value : 0.0f; + + this->internalUpdate(); + } +} + +void DSDisplayOverlay::internalUpdate() +{ + HRESULT hr; + + if (this->ticks > 0) + { + this->alphaBitmap.dwFlags = VMR9AlphaBitmap_EntireDDS; + } + else + { + this->alphaBitmap.dwFlags = VMR9AlphaBitmap_Disable; + } + + hr = this->displayGraph->getMixerBitmap()->SetAlphaBitmap(&this->alphaBitmap); + if (FAILED(hr)) + { + cout << "Failed to mix overylay (" << hr << ")" << endl; + return; + } +} + +#endif diff --git a/tinyDSHOW/src/DSDisplayOverlay.cxx b/tinyDSHOW/src/DSDisplayOverlay.cxx new file mode 100644 index 0000000..e9a2e10 --- /dev/null +++ b/tinyDSHOW/src/DSDisplayOverlay.cxx @@ -0,0 +1,71 @@ +/* +* Copyright (C) 2009-2010 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#if !defined(VMR) && !defined(VMR9) && !defined(VMR9_WINDOWLESS) + +#include +#include + +#include + +using namespace std; + +DSDisplayOverlay::DSDisplayOverlay() +{ +} + +DSDisplayOverlay::~DSDisplayOverlay() +{ +} + +void DSDisplayOverlay::attach(HWND parent, DSDisplayGraph *graph) +{ + this->displayGraph = graph; +} + +void DSDisplayOverlay::detach() +{ + this->displayGraph = NULL; +} + +void DSDisplayOverlay::show(int value) +{ + // Store the ticks to count down + this->ticks = value; + + this->internalUpdate(); +} + +void DSDisplayOverlay::update() +{ + if (this->displayGraph && (this->ticks > 0)) + { + this->ticks--; + this->internalUpdate(); + } +} + +void DSDisplayOverlay::internalUpdate() +{ + this->displayGraph->getSourceFilter()->showOverlay(this->ticks); +} + +#endif diff --git a/tinyDSHOW/src/DSFrameRateFilter.cxx b/tinyDSHOW/src/DSFrameRateFilter.cxx new file mode 100644 index 0000000..ba474ae --- /dev/null +++ b/tinyDSHOW/src/DSFrameRateFilter.cxx @@ -0,0 +1,126 @@ +/* +* Copyright (C) 2009-2010 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include + +#include +#include + +using namespace std; + +#define FPS_INPUT 30 +#define FPS_OUTPUT 5 + +DSFrameRateFilter::DSFrameRateFilter(TCHAR *tszName, LPUNKNOWN punk, HRESULT *phr) +:CTransInPlaceFilter (tszName, punk, CLSID_DSFrameRateFilter, phr) +{ + this->m_rtFrameLength = (10000000)/FPS_OUTPUT; + + this->m_inputFps = FPS_INPUT; + this->m_outputFps = FPS_OUTPUT; + + this->m_iFrameNumber = 0; + this->m_progress = 0; + this->m_bProcessFrame = true; +} + +DSFrameRateFilter::~DSFrameRateFilter() +{ +} + +HRESULT DSFrameRateFilter::SetFps(int inputFps, int outputFps) +{ + if(inputFps <= 0 || outputFps <= 0) + { + return E_FAIL; + } + + // Stop prcessing + this->m_bProcessFrame = false; + + if(inputFps < outputFps) + { + this->m_inputFps = this->m_outputFps = inputFps; + } + else + { + this->m_outputFps = outputFps; + this->m_inputFps = inputFps; + } + + // Restart processing + this->m_iFrameNumber = 0; + this->m_progress = 0; + this->m_bProcessFrame = true; + + return S_OK; +} + +HRESULT DSFrameRateFilter::Transform(IMediaSample *pSample) +{ + if(!this->m_bProcessFrame) return S_FALSE; + + CheckPointer(pSample, E_POINTER); + + HRESULT hr = S_OK; + HRESULT ret = S_FALSE; + + pSample->SetTime(NULL, NULL); + + // Drop frame? + if(this->m_iFrameNumber == 0) + { + ret = S_OK; + } + else if(this->m_progress >= this->m_inputFps) + { + this->m_progress -= this->m_inputFps; + ret = S_OK; + } + + // Mark frame as accepted + if (ret == S_OK) + { + // Set TRUE on every sample for uncompressed frames + pSample->SetSyncPoint(TRUE); + } + + this->m_progress += this->m_outputFps; + this->m_iFrameNumber++; + + return ret; +} + +HRESULT DSFrameRateFilter::CheckInputType(const CMediaType* mtIn) +{ + return S_OK; +} + +//Implement CreateInstance for your filter object. Typically, CreateInstance calls the constructor of your filter clas +CUnknown * WINAPI DSFrameRateFilter::CreateInstance(LPUNKNOWN punk, HRESULT *phr) +{ + DSFrameRateFilter *pNewObject = new DSFrameRateFilter(_T("Tdshow DirectShow Framerate Limiter Filter."), punk, phr ); + if (pNewObject == NULL) + { + *phr = E_OUTOFMEMORY; + } + return pNewObject; +} diff --git a/tinyDSHOW/src/DSGrabber.cxx b/tinyDSHOW/src/DSGrabber.cxx new file mode 100644 index 0000000..be87868 --- /dev/null +++ b/tinyDSHOW/src/DSGrabber.cxx @@ -0,0 +1,255 @@ +/* +* Copyright (C) 2009-2010 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include +#include +#include +#include +#include + +#include + +#include "tsk_debug.h" + +using namespace std; + +DSGrabber::DSGrabber(HRESULT *hr) +: mutex_buffer(NULL), preview(NULL) +{ +#ifdef _WIN32_WCE + this->graph = new DSCaptureGraph(this, hr); +#else + this->graph = new DSCaptureGraph(this, hr); + if(!FAILED(*hr)){ + this->preview = new DSDisplay(hr); + } +#endif + if (FAILED(*hr)){ + TSK_DEBUG_ERROR("Failed to create Grabber"); + return; + } + + // Init the bitmap info header with default values + memset(&(this->bitmapInfo), 0, sizeof(BITMAPINFOHEADER)); + this->bitmapInfo.biSize = sizeof(BITMAPINFOHEADER); + this->bitmapInfo.biWidth = 176; + this->bitmapInfo.biHeight = 144; + this->bitmapInfo.biPlanes = 1; + this->bitmapInfo.biBitCount = 24; + this->bitmapInfo.biCompression = 0; + this->bitmapInfo.biXPelsPerMeter = 0; + this->bitmapInfo.biYPelsPerMeter = 0; + this->bitmapInfo.biClrUsed = 0; + this->bitmapInfo.biClrImportant = 0; + + this->plugin_cb = NULL; + this->buffer = NULL; + this->mutex_buffer = tsk_mutex_create(); +} + +DSGrabber::~DSGrabber() +{ + SAFE_DELETE_PTR ( this->graph ); + SAFE_DELETE_PTR ( this->preview ); + SAFE_DELETE_ARRAY ( this->buffer ); + tsk_mutex_destroy(&this->mutex_buffer); +} + +void DSGrabber::setCaptureDevice(const std::string &devicePath) +{ + this->graph->setSource(devicePath); +} + +void DSGrabber::setCallback(tmedia_producer_enc_cb_f callback, const void* callback_data) +{ + this->plugin_cb = callback; + this->plugin_cb_data = callback_data; +} + +void DSGrabber::start() +{ + if (!this->graph->isRunning()){ + first_buffer = true; + + this->preview->start(); + this->graph->connect(); + this->graph->start(); + } +} +void DSGrabber::stop() +{ + if (this->graph->isRunning()){ + this->preview->stop(); + this->graph->stop(); + this->graph->disconnect(); + } +} + +bool DSGrabber::setCaptureParameters(int format, int f) +{ + tsk_mutex_lock(this->mutex_buffer); + + // Get size from the format + VIDEOFORMAT_TO_SIZE(format, this->width, this->height) + + // Store the framerate + this->fps = f; + + // Store the required dimensions + this->bitmapInfo.biWidth = this->width; + this->bitmapInfo.biHeight = this->height; + this->bitmapInfo.biBitCount = 24; + this->bitmapInfo.biSizeImage = (this->width * this->height * 3); + + // Change the intermediate buffer + SAFE_DELETE_ARRAY ( this->buffer ); + this->buffer = new BYTE[this->bitmapInfo.biSizeImage]; + memset(this->buffer,0,this->bitmapInfo.biSizeImage); + + // Find closest matching format to drive the source filter + DSCaptureFormat *fmt = NULL; + int score = 0; + std::vector *formats = this->graph->getFormats(); + std::vector::iterator iter; + std::vector::iterator last = formats->end(); + for(iter = formats->begin(); iter != last; iter++){ + int value = (*iter).getMatchScore(this->width, this->height); + if (value > score || !fmt){ + score = value; + fmt = &(*iter); + } + } + + // Setup source filter in the graph + HRESULT hr = this->graph->setParameters(fmt, this->fps); + // Set preview parameters + this->preview->setFps(this->fps); + this->preview->setSize(this->width, this->height); + + tsk_mutex_unlock(this->mutex_buffer); + + return SUCCEEDED(hr); +} + +VIDEOFORMAT DSGrabber::getCaptureFormat() +{ + VIDEOFORMAT format; + SIZE_TO_VIDEOFORMAT(this->width, this->height, format); + return format; +} + +int DSGrabber::getFramerate() +{ + return this->fps; +} + +HRESULT DSGrabber::SampleCB(double SampleTime, IMediaSample *pSample) +{ + return S_OK; +} + +HRESULT DSGrabber::BufferCB(double SampleTime, BYTE *pBuffer, long BufferLen) +{ + HRESULT hr; + + tsk_mutex_lock(this->mutex_buffer); + + AM_MEDIA_TYPE mediaType; + hr = this->graph->getConnectedMediaType(&mediaType); + if (FAILED(hr) || !this->buffer){ + return hr; + } + + if(first_buffer){ + first_buffer = false; + + tsk_mutex_unlock(this->mutex_buffer); + return hr; + } + + // Examine the format block. + if ((mediaType.formattype == FORMAT_VideoInfo) && (mediaType.cbFormat >= sizeof(VIDEOINFOHEADER)) && (mediaType.pbFormat != NULL) ) + { + VIDEOINFOHEADER *pVih = reinterpret_cast(mediaType.pbFormat); + BITMAPINFOHEADER* bih = &pVih->bmiHeader; + + //int framerate = pVih->AvgTimePerFrame; + if( (bih->biHeight == this->bitmapInfo.biHeight) && (bih->biWidth == this->bitmapInfo.biWidth) && (bih->biBitCount == this->bitmapInfo.biBitCount) ) + { + memmove(this->buffer, pBuffer, this->bitmapInfo.biSizeImage); + } + else + { + ResizeRGB( + bih, + (const unsigned char *) pBuffer, + &this->bitmapInfo, + (unsigned char *) this->buffer, + this->width, + this->height); + } + + // for the network + if(this->plugin_cb){ + this->plugin_cb(this->plugin_cb_data, this->buffer, (this->width*this->height*3)); + } + + // for the preview + if(this->preview){ + this->preview->handleVideoFrame(this->buffer, this->width, this->height); + } + } + + // Free the format +#ifdef _WIN32_WCE + // Nothing had been allocated +#else + FreeMediaType(mediaType); +#endif + + tsk_mutex_unlock(this->mutex_buffer); + + return hr; +} + +HRESULT DSGrabber::QueryInterface(REFIID iid, LPVOID *ppv) +{ +#ifdef _WIN32_WCE + assert(1==0); +#else + if( iid == IID_ISampleGrabberCB || iid == IID_IUnknown ) + { + *ppv = (void *) static_cast(this); + return NOERROR; + } +#endif + return E_NOINTERFACE; +} + +ULONG DSGrabber::AddRef() +{ + return 2; +} + +ULONG DSGrabber::Release() +{ + return 1; +} \ No newline at end of file diff --git a/tinyDSHOW/src/DSOutputFilter.cxx b/tinyDSHOW/src/DSOutputFilter.cxx new file mode 100644 index 0000000..97f5182 --- /dev/null +++ b/tinyDSHOW/src/DSOutputFilter.cxx @@ -0,0 +1,113 @@ +/* +* Copyright (C) 2009-2010 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include +#include +#include + +#include "tsk_memory.h" + +DSOutputFilter::DSOutputFilter(LPUNKNOWN pUnk, HRESULT *phr) +: CSource(_T("TDSHOW_OUTPUT"), pUnk, CLSID_TdshowOutputFilter) +{ + CAutoLock cAutoLock(&m_cStateLock); + + // Add one source stream (output pin)! + this->outputStream = new DSOutputStream(phr, this, _T("Out")); +} + +DSOutputFilter::~DSOutputFilter() +{ + //SAFE_RELEASE(this->outputStream); +} + +void DSOutputFilter::setBuffer(void *pointer, int size) +{ + this->outputStream->lockBuffer(); + if(pointer && size){ + if(this->outputStream->buffer_size != size){ + if((this->outputStream->buffer = tsk_realloc(this->outputStream->buffer, size))){ + this->outputStream->buffer_size = size; + } + else goto done; + } + memcpy(this->outputStream->buffer, pointer, size); + } +done: + this->outputStream->unlockBuffer(); +} + +void DSOutputFilter::getMediaType(AM_MEDIA_TYPE* &pmt) +{ + //if(pmt) + //{ + // memcpy(pmt, &this->outputStream->pmt, sizeof(AM_MEDIA_TYPE)); + //} +} + +HRESULT DSOutputFilter::setMediaType(const AM_MEDIA_TYPE* pmt) +{ + return this->ReconnectPin(this->outputStream, pmt); +} + +HRESULT DSOutputFilter::setImageFormat(UINT width, UINT height) +{ + return this->outputStream->setImageFormat(width, height); +} + +bool DSOutputFilter::getImageFormat(UINT &width, UINT &height) +{ + if(this->outputStream){ + return this->outputStream->getImageFormat(width, height); + } + return false; +} + +void DSOutputFilter::setFps(int fps_) +{ + this->outputStream->setFps(fps_); +} + +void DSOutputFilter::showOverlay(int value) +{ + this->outputStream->showOverlay(value); +} + +void DSOutputFilter::reset() +{ + this->outputStream->frameNumber = 0; + this->outputStream->lockBuffer(); + this->outputStream->buffer = NULL; + this->outputStream->buffer_size = 0; + this->outputStream->unlockBuffer(); +} + +#ifdef _WIN32_WCE +STDMETHODIMP_(ULONG) DSOutputFilter::NonDelegatingRelease() +{ + if(InterlockedDecrement(&m_cRef) == 0) + { + delete this; + return 0; + } + return m_cRef; +} +#endif diff --git a/tinyDSHOW/src/DSOutputStream.cxx b/tinyDSHOW/src/DSOutputStream.cxx new file mode 100644 index 0000000..dfc1393 --- /dev/null +++ b/tinyDSHOW/src/DSOutputStream.cxx @@ -0,0 +1,308 @@ +/* +* Copyright (C) 2009-2010 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include +#include +#include + +#include + +using namespace std; + +#define DEFAULT_FPS 15 + +#define MEMCPY_WORKAROUND 1 + +// Overlay +#define OVERLAY 0 +#define OVERLAY_TEXT TEXT("Press ESC to exit full screen mode") +#define OVERLAY_DURATION 3 // in seconds + +DSOutputStream::DSOutputStream(HRESULT *phr, DSOutputFilter *pParent, LPCWSTR pPinName) +: CSourceStream(_T("DSOutputStream"), phr, pParent, pPinName) +{ +//#ifndef _WIN32_WCE + CAutoLock cAutoLock(m_pFilter->pStateLock()); +//#endif + + this->buffer = NULL; + this->buffer_size = NULL; + + this->frameNumber = 0; + this->frameLength = (1000)/DEFAULT_FPS; + this->fps = DEFAULT_FPS; + + this->width = 176; + this->height = 144; + + this->overlay = false; + + this->paintBuffer = NULL; + this->paintDC = NULL; + this->hDibSection = NULL; + this->hObject = NULL; + + this->mutex = tsk_mutex_create(); +} + +DSOutputStream::~DSOutputStream() +{ + SAFE_DELETE_PTR(this->buffer); + tsk_mutex_destroy(&this->mutex); + // TODO : Is there anything to free ??? +} + +void DSOutputStream::setFps(int fps_) +{ + this->fps = fps_; + this->frameLength = (1000)/this->fps; +} + +void DSOutputStream::showOverlay(int value) +{ + if (value == 0){ + this->overlay = false; + } + this->overlay = (value > 0); +} + +HRESULT DSOutputStream::setImageFormat(UINT width, UINT height) +{ + if ((this->width == width) && (this->height == height)) return S_FALSE; + + this->width = width; + this->height = height; + + this->frameNumber = 0; + + return S_OK; +} + +bool DSOutputStream::getImageFormat(UINT &width, UINT &height) +{ + width = this->width; + height = this->height; + return true; +} + +HRESULT DSOutputStream::GetMediaType(CMediaType *pMediaType) +{ + HRESULT hr = S_OK; + + CAutoLock lock(m_pFilter->pStateLock()); + + ZeroMemory(pMediaType, sizeof(CMediaType)); + + VIDEOINFO *pvi = (VIDEOINFO *)pMediaType->AllocFormatBuffer(sizeof(VIDEOINFO)); + if (NULL == pvi) + return E_OUTOFMEMORY; + + ZeroMemory(pvi, sizeof(VIDEOINFO)); + + pvi->bmiHeader.biCompression = BI_RGB; + pvi->bmiHeader.biBitCount = 24; + pvi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + pvi->bmiHeader.biWidth = this->width; + pvi->bmiHeader.biHeight = this->height; + pvi->bmiHeader.biPlanes = 1; + pvi->bmiHeader.biSizeImage = GetBitmapSize(&pvi->bmiHeader); + pvi->bmiHeader.biClrImportant = 0; + + // Frame rate + pvi->AvgTimePerFrame = DS_MILLIS_TO_100NS(1000/this->fps); + + SetRectEmpty(&(pvi->rcSource)); // we want the whole image area rendered. + SetRectEmpty(&(pvi->rcTarget)); // no particular destination rectangle + + pMediaType->SetType(&MEDIATYPE_Video); + pMediaType->SetFormatType(&FORMAT_VideoInfo); + pMediaType->SetTemporalCompression(FALSE); + + pMediaType->SetSubtype(&MEDIASUBTYPE_RGB24); + pMediaType->SetSampleSize(pvi->bmiHeader.biSizeImage); + + bitmapInfo.bmiHeader = pvi->bmiHeader; + + return hr; +} + +HRESULT DSOutputStream::DecideBufferSize(IMemAllocator *pMemAlloc, ALLOCATOR_PROPERTIES *pProperties) +{ + CheckPointer(pMemAlloc, E_POINTER); + CheckPointer(pProperties, E_POINTER); + + CAutoLock cAutoLock(m_pFilter->pStateLock()); + HRESULT hr = NOERROR; + + VIDEOINFO *pvi = (VIDEOINFO *) m_mt.Format(); + pProperties->cBuffers = 1; + pProperties->cbBuffer = pvi->bmiHeader.biSizeImage; + + // Ask the allocator to reserve us some sample memory. NOTE: the function + // can succeed (return NOERROR) but still not have allocated the + // memory that we requested, so we must check we got whatever we wanted. + ALLOCATOR_PROPERTIES Actual; + hr = pMemAlloc->SetProperties(pProperties,&Actual); + if(FAILED(hr)){ + return hr; + } + + // Is this allocator unsuitable? + if(Actual.cbBuffer < pProperties->cbBuffer) + { + return E_FAIL; + } + + // Make sure that we have only 1 buffer (we erase the ball in the + // old buffer to save having to zero a 200k+ buffer every time + // we draw a frame) + return NOERROR; +} + +HRESULT DSOutputStream::OnThreadCreate() +{ +#if OVERLAY + hDibSection = CreateDIBSection(NULL, (BITMAPINFO *) &bitmapInfo, DIB_RGB_COLORS, &paintBuffer, NULL, 0); + + HDC hDC = GetDC(NULL); + paintDC = CreateCompatibleDC(hDC); + SetMapMode(paintDC, GetMapMode(hDC)); + SetBkMode(paintDC, TRANSPARENT); + SetTextColor(paintDC, RGB(255,255,255)); + + hObject = SelectObject(paintDC, hDibSection); +#endif + + return CSourceStream::OnThreadCreate(); +} + +HRESULT DSOutputStream::OnThreadDestroy() +{ +#if OVERLAY + if (paintDC) DeleteDC(paintDC); + if (hObject) DeleteObject(hObject); + + if (paintBuffer) + { + //delete[] paintBuffer; // will be done + //paintBuffer = NULL; + } +#endif + return CSourceStream::OnThreadDestroy(); +} + +inline HRESULT DSOutputStream::DrawOverLay(void *pBuffer, long lSize) +{ + // called only #if OVERLAY + CopyMemory(paintBuffer, pBuffer, lSize); + + // Draw the current frame +#ifdef _WIN32_WCE + +#else + if( !TextOut( paintDC, 0, 0, OVERLAY_TEXT, _tcslen( OVERLAY_TEXT ) ) ) return E_FAIL; +#endif + + CopyMemory(pBuffer, paintBuffer, lSize); + + return S_OK; +} + +/*inline*/ void DSOutputStream::TransfertBuffer(void* src, void* dest, long lSize) +{ + __try + { +#if MEMCPY_WORKAROUND + //#ifdef _WIN32_WCE + memmove(dest, src, lSize); + /*#else + unsigned char * pDst = (unsigned char *) dest; + + if(src){ + unsigned char const * pSrc = (unsigned char const *) src; + for( register int i=0; ((i< lSize) && src); i++) *pDst++ = *pSrc++; + }else{ + for( register int i=0; i< lSize; i++) *pDst++ = 0; + } + #endif*/ +#else + CopyMemory(dest, src, lSize); //BUGGY +#endif + } + __except(EXCEPTION_ACCESS_VIOLATION == GetExceptionCode()) + { + //ZeroMemory(dest, sizeof(void*)); + } +} + +HRESULT DSOutputStream::FillBuffer(IMediaSample *pSample) +{ + CheckPointer(pSample, E_POINTER); + CAutoLock lock(m_pFilter->pStateLock()); + + HRESULT hr; + BYTE *pBuffer = NULL; + long lSize; + + hr = pSample->GetPointer(&pBuffer); + if (SUCCEEDED(hr)) + { + lSize = pSample->GetSize(); + + // Check that we're still using video + //ASSERT(m_mt.formattype == FORMAT_VideoInfo); + + if (this->buffer) + { +#if OVERLAY + if (this->overlay) + { + DrawOverLay(this->buffer, lSize); + } +#endif + // Why try do not work, see: http://msdn2.microsoft.com/en-us/library/xwtb73ad(vs.80).aspx + this->lockBuffer(); + this->TransfertBuffer(this->buffer, (void*)pBuffer, TSK_MIN(lSize, this->buffer_size)); + this->unlockBuffer(); + } + else + { + // Avoid catching last image + memset((void*)pBuffer, NULL, lSize); + } + + REFERENCE_TIME rtStart = DS_MILLIS_TO_100NS(this->frameNumber * this->frameLength); + REFERENCE_TIME rtStop = rtStart + DS_MILLIS_TO_100NS(this->frameLength); + + this->frameNumber++; + + pSample->SetTime(&rtStart, &rtStop); + //pSample->SetMediaTime(&rtStart, &rtStop); + pSample->SetActualDataLength(lSize); + pSample->SetPreroll(FALSE); + pSample->SetDiscontinuity(FALSE); + } + + // Set TRUE on every sample for uncompressed frames (KEYFRAME) + pSample->SetSyncPoint(TRUE); + + return S_OK; +} diff --git a/tinyDSHOW/src/DSUtils.cxx b/tinyDSHOW/src/DSUtils.cxx new file mode 100644 index 0000000..736984b --- /dev/null +++ b/tinyDSHOW/src/DSUtils.cxx @@ -0,0 +1,155 @@ +/* +* Copyright (C) 2009-2010 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include + +#include +#include + +bool IsMainThread() +{ + HWND hWnd = GetActiveWindow(); + if(!hWnd) hWnd = GetForegroundWindow(); + if(!hWnd) hWnd = GetConsoleWindow(); + + if(hWnd){ + DWORD mainTid = GetWindowThreadProcessId(hWnd, NULL); + DWORD currentTid = GetCurrentThreadId(); + return (mainTid == currentTid); + } + return false; +} + +IPin *GetPin(IBaseFilter *filter, PIN_DIRECTION direction) +{ + IEnumPins *enumPins = NULL; + IPin *pin = NULL; + + HRESULT hr = filter->EnumPins(&enumPins); + if(!enumPins){ + return NULL; + } + + for(;;){ + ULONG fetched = 0; + PIN_DIRECTION pinDir = PIN_DIRECTION(-1); + pin = NULL; + + if (FAILED(enumPins->Next(1, &pin, &fetched))){ + enumPins->Release(); + return NULL; + } + + if (fetched == 1 && pin){ + pin->QueryDirection(&pinDir); + if(pinDir == direction){ + break; + } + pin->Release(); + } + } + + enumPins->Release(); + return pin; +} + +HRESULT ConnectFilters(IGraphBuilder *graphBuilder, IBaseFilter *source, IBaseFilter *destination, AM_MEDIA_TYPE *mediaType) +{ + HRESULT hr; + + IPin *outPin = GetPin(source, PINDIR_OUTPUT); + IPin *inPin = GetPin(destination, PINDIR_INPUT); + + if (mediaType != NULL){ + hr = graphBuilder->ConnectDirect(outPin, inPin, mediaType); + } + else{ + hr = graphBuilder->Connect(outPin, inPin); + } + + SAFE_RELEASE(outPin); + SAFE_RELEASE(inPin); + + return hr; +} + +HRESULT DisconnectFilters(IGraphBuilder *graphBuilder, IBaseFilter *source, IBaseFilter *destination) +{ + HRESULT hr; + + IPin *outPin = GetPin(source, PINDIR_OUTPUT); + IPin *inPin = GetPin(destination, PINDIR_INPUT); + + if (inPin){ + hr = graphBuilder->Disconnect(inPin); + } + + if (outPin){ + hr = graphBuilder->Disconnect(outPin); + } + + SAFE_RELEASE(outPin); + SAFE_RELEASE(inPin); + + return hr; +} + +bool DisconnectAllFilters(IGraphBuilder *graphBuilder) +{ + CComPtr filterEnum = NULL; + CComPtr currentFilter = NULL; + ULONG fetched; + HRESULT hr; + + hr = graphBuilder->EnumFilters(&filterEnum); + if (FAILED(hr)) return false; + + while(filterEnum->Next(1, ¤tFilter, &fetched) == S_OK){ + hr = DisconnectFilters(graphBuilder, currentFilter, currentFilter); + } + + filterEnum.Release(); + return true; +} + +bool RemoveAllFilters(IGraphBuilder *graphBuilder) +{ + CComPtr filterEnum = NULL; + CComPtr currentFilter = NULL; + ULONG fetched; + HRESULT hr; + + hr = graphBuilder->EnumFilters(&filterEnum); + if (FAILED(hr)) return false; + + while(filterEnum->Next(1, ¤tFilter, &fetched) == S_OK){ + hr = graphBuilder->RemoveFilter(currentFilter); + if (FAILED(hr)){ + filterEnum.Release(); + return false; + } + currentFilter.Release(); + filterEnum->Reset(); + } + + filterEnum.Release(); + return true; +} diff --git a/tinyDSHOW/src/Resizer.cxx b/tinyDSHOW/src/Resizer.cxx new file mode 100644 index 0000000..2604c42 --- /dev/null +++ b/tinyDSHOW/src/Resizer.cxx @@ -0,0 +1,1097 @@ +/* +* Copyright (C) 2009-2010 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + */ +#include + +// +// Resize function +// +void ResizeRGB( BITMAPINFOHEADER *pbiIn, //Src's BitMapInFoHeader + const unsigned char * dibBits, //Src bits + BITMAPINFOHEADER *pbiOut, + unsigned char *pFrame, //Dst bits + int iNewWidth, //new W in pixel + int iNewHeight) //new H in pixel +{ + StretchDIB( pbiOut, // --> BITMAPINFO of destination + pFrame, // --> to destination bits + 0, // Destination origin - x coordinate + 0, // Destination origin - y coordinate + iNewWidth, // x extent of the BLT + iNewHeight, // y extent of the BLT + pbiIn, // --> BITMAPINFO of destination + (void*) dibBits, // --> to source bits + 0, // Source origin - x coordinate + 0, // Source origin - y coordinate + pbiIn->biWidth, // x extent of the BLT + pbiIn->biHeight // y extent of the BLT + ); + + return; +} + + +/* -------------------------------------------------------------------- */ + +/* +* StretchFactor +* +* calculate the stretch factor (proportion of source extent to destination +* extent: 1:1, 1:2, 1:4, 1:N, N:1, 4:1,or 2:1) and also the +* delta fraction (see above comment on X_FUNC). This is the ratio of +* the smaller extent to the larger extent, represented as a fraction +* multiplied by 65536. +* +* returns: the stretch factor (stores the delta fraction in *pfract) +*/ + +int +StretchFactor(int SrcE, int DstE, int *pfract) +{ + + + if (SrcE == DstE) { + if (pfract != NULL) { + pfract = 0; + } + + return(STRETCH_1_1); + + } + + + if (SrcE > DstE) { + if (pfract != NULL) { + *pfract = ( (DstE << 16) / SrcE) & 0xffff; + } + + if (SrcE == (DstE * 2)) { + return(STRETCH_2_1); + } else if (SrcE == (DstE * 4)) { + return(STRETCH_4_1); + } else { + return(STRETCH_N_1); + } + + } else { + + /* calculate delta fraction based on smallest / largest */ + if (pfract != NULL) { + *pfract = ( (SrcE << 16) / DstE) & 0xffff; + } + + if (DstE == (SrcE * 2)) { + return(STRETCH_1_2); + } else if (DstE == (SrcE * 4)) { + return(STRETCH_1_4); + } else { + return(STRETCH_1_N); + } + } +} + + +/* -------------------------------------------------------------------- */ + +/* +* StretchDIB +* +*/ + +void FAR PASCAL +StretchDIB( + LPBITMAPINFOHEADER biDst, // --> BITMAPINFO of destination + LPVOID lpvDst, // --> to destination bits + int DstX, // Destination origin - x coordinate + int DstY, // Destination origin - y coordinate + int DstXE, // x extent of the BLT + int DstYE, // y extent of the BLT + LPBITMAPINFOHEADER biSrc, // --> BITMAPINFO of source + LPVOID lpvSrc, // --> to source bits + int SrcX, // Source origin - x coordinate + int SrcY, // Source origin - y coordinate + int SrcXE, // x extent of the BLT + int SrcYE // y extent of the BLT + ) +{ + + int nBits; + int SrcWidth, DstWidth; + LPBYTE lpDst = (LPBYTE)lpvDst, lpSrc = (LPBYTE)lpvSrc; + int x_fract; + int x_factor; + int y_factor; + X_FUNC xfunc; + + + /* + * chek that sizes are not same + */ + /*if(DstXE == SrcXE && DstYE == SrcYE) + { + return; + }*/ + /* + * check that bit depths are same and 8, 16 or 24 + */ + + if ((nBits = biDst->biBitCount) != biSrc->biBitCount) { + return; + } + + if ( (nBits != 8 ) && (nBits != 16) && (nBits != 24) && + (nBits != 32)) { + return; + } + + /* + * check that extents are not bad + */ + if ( (SrcXE <= 0) || (SrcYE <= 0) || (DstXE <= 0) || (DstYE <= 0)) { + return; + } + + /* + * calculate width of one scan line in bytes, rounded up to + * DWORD boundary. + */ + SrcWidth = (((biSrc->biWidth * nBits) + 31) & ~31) / 8; + DstWidth = (((biDst->biWidth * nBits) + 31) & ~31) / 8; + + /* + * set initial source and dest pointers + */ + lpSrc += (SrcY * SrcWidth) + ((SrcX * nBits) / 8); + lpDst += (DstY * DstWidth) + ((DstX * nBits) / 8); + + + /* + * calculate stretch proportions (1:1, 1:2, 1:N, N:1 etc) and + * also the fractional stretch factor. (we are not interested in + * the y stretch fraction - this is only used in x stretching. + */ + + y_factor = StretchFactor(SrcYE, DstYE, NULL); + x_factor = StretchFactor(SrcXE, DstXE, &x_fract); + + /* + * we have special case routines for 1:2 in both dimensions + * for 8 and 16 bits + */ + if ((y_factor == x_factor) && (y_factor == STRETCH_1_2)) { + + if (nBits == 8) { + //StartCounting(); + Stretch_1_2_8Bits(lpSrc, lpDst, SrcXE, SrcYE, + DstXE, DstYE, SrcWidth, DstWidth, + x_fract); + //EndCounting("8 bit"); + return; + + } else if (nBits == 16) { + //StartCounting(); + Stretch_1_2_16Bits(lpSrc, lpDst, SrcXE, SrcYE, + DstXE, DstYE, SrcWidth, DstWidth, + x_fract); + //EndCounting("16 bit"); + return; + } + } + + + /* pick an X stretch function */ + switch(nBits) { + + case 8: + switch(x_factor) { + case STRETCH_1_1: + xfunc = X_Stretch_1_1_8Bits; + break; + + case STRETCH_1_2: + xfunc = X_Stretch_1_2_8Bits; + break; + + case STRETCH_1_4: + xfunc = X_Stretch_1_4_8Bits; + break; + + case STRETCH_1_N: + xfunc = X_Stretch_1_N_8Bits; + break; + + case STRETCH_N_1: + case STRETCH_4_1: + case STRETCH_2_1: + xfunc = X_Stretch_N_1_8Bits; + break; + + } + break; + + case 16: + switch(x_factor) { + case STRETCH_1_1: + xfunc = X_Stretch_1_1_16Bits; + break; + + case STRETCH_1_2: + xfunc = X_Stretch_1_2_16Bits; + break; + + case STRETCH_1_4: + case STRETCH_1_N: + xfunc = X_Stretch_1_N_16Bits; + break; + + case STRETCH_N_1: + case STRETCH_4_1: + case STRETCH_2_1: + xfunc = X_Stretch_N_1_16Bits; + break; + + } + break; + + case 24: + switch(x_factor) { + case STRETCH_1_1: + xfunc = X_Stretch_1_1_24Bits; + break; + + case STRETCH_1_2: + case STRETCH_1_4: + case STRETCH_1_N: + xfunc = X_Stretch_1_N_24Bits; + break; + + case STRETCH_N_1: + case STRETCH_4_1: + case STRETCH_2_1: + xfunc = X_Stretch_N_1_24Bits; + break; + + } + break; + + case 32: + switch(x_factor) { + case STRETCH_1_1: + xfunc = X_Stretch_1_1_32Bits; + break; + + case STRETCH_1_2: + case STRETCH_1_4: + case STRETCH_1_N: + xfunc = X_Stretch_1_N_32Bits; + break; + + case STRETCH_N_1: + case STRETCH_4_1: + case STRETCH_2_1: + xfunc = X_Stretch_N_1_32Bits; + break; + + } + break; + + } + + + /* + * now call appropriate stretching function depending + * on the y stretch factor + */ + switch (y_factor) { + case STRETCH_1_1: + case STRETCH_1_2: + case STRETCH_1_4: + case STRETCH_1_N: + Y_Stretch_1_N(lpSrc, lpDst, SrcXE, SrcYE, + DstXE, DstYE, SrcWidth, DstWidth, x_fract, xfunc, nBits); + break; + + case STRETCH_N_1: + case STRETCH_4_1: + case STRETCH_2_1: + Y_Stretch_N_1(lpSrc, lpDst, SrcXE, SrcYE, + DstXE, DstYE, SrcWidth, DstWidth, x_fract, xfunc); + break; + + } + return; +} + + +/* ---- y stretching -------------------------------------------- */ + +/* +* call an X_FUNC to copy scanlines from lpSrc to lpDst. Duplicate or +* omit scanlines to stretch SrcYE to DstYE. +*/ + + +/* +* Y_Stretch_1_N +* +* write DstYE scanlines based on SrcYE scanlines, DstYE > SrcYE +* +*/ + +void +Y_Stretch_1_N(LPBYTE lpSrc, + LPBYTE lpDst, + int SrcXE, + int SrcYE, + int DstXE, + int DstYE, + int SrcWidth, + int DstWidth, + int x_fract, + X_FUNC x_func, + int nBits) +{ + + int ydelta; + register int i; + LPBYTE lpPrev = NULL; + + ydelta = DstYE -1; + + for (i = 0; i < DstYE; i++) { + + /* have we already stretched this scanline ? */ + if (lpPrev == NULL) { + /* no - copy one scanline */ + (*x_func)(lpSrc, lpDst, SrcXE, DstXE, x_fract); + lpPrev = lpDst; + } else { + /* yes - this is a duplicate scanline. do + * a straight copy of one that has already + * been stretched/shrunk + */ + X_CopyScanline(lpPrev, lpDst, DstXE * nBits / 8); + } + + /* advance dest pointer */ + lpDst += DstWidth; + + /* should we advance source pointer this time ? */ + if ( (ydelta -= SrcYE) < 0) { + ydelta += DstYE; + lpSrc += SrcWidth; + lpPrev = NULL; + } + } +} + + +/* +* Y_Stretch_N_1 +* +* write DstYE scanlines based on SrcYE scanlines, DstYE < SrcYE +* +*/ +void +Y_Stretch_N_1(LPBYTE lpSrc, + LPBYTE lpDst, + int SrcXE, + int SrcYE, + int DstXE, + int DstYE, + int SrcWidth, + int DstWidth, + int x_fract, + X_FUNC x_func) +{ + + int ydelta; + register int i; + + ydelta = SrcYE -1; + + for (i = 0; i < DstYE; i++) { + + /* copy one scanline */ + (*x_func)(lpSrc, lpDst, SrcXE, DstXE, x_fract); + + /* advance dest pointer */ + lpDst += DstWidth; + + /* how many times do we advance source pointer this time ? */ + do { + lpSrc += SrcWidth; + ydelta -= DstYE; + } while (ydelta >= 0); + + ydelta += SrcYE; + } +} + +/* ---8-bit X stretching -------------------------------------------------- */ + +/* +* X_Stretch_1_N_8Bits +* +* copy one scan line, stretching 1:N (DstXE > SrcXE). For 8-bit depth. +*/ +void +X_Stretch_1_N_8Bits(LPBYTE lpSrc, + LPBYTE lpDst, + int SrcXE, + int DstXE, + int x_fract) +{ + int xdelta; + register int i; + + xdelta = DstXE -1; + + for (i = 0; i < DstXE; i++) { + + /* copy one byte and advance dest */ + *lpDst++ = *lpSrc; + + /* should we advance source pointer this time ? */ + if ( (xdelta -= SrcXE) < 0) { + xdelta += DstXE; + lpSrc++; + } + } +} + + +/* +* X_Stretch_N_1_8Bits +* +* copy one scan line, shrinking N:1 (DstXE < SrcXE). For 8-bit depth. +*/ +void +X_Stretch_N_1_8Bits(LPBYTE lpSrc, + LPBYTE lpDst, + int SrcXE, + int DstXE, + int x_fract) +{ + int xdelta; + register int i; + + xdelta = SrcXE -1; + + for (i = 0; i < DstXE; i++) { + + /* copy one byte and advance dest */ + *lpDst++ = *lpSrc; + + /* how many times do we advance source pointer this time ? */ + do { + lpSrc++; + xdelta -= DstXE; + } while (xdelta >= 0); + + xdelta += SrcXE; + } +} + +/* +* copy one scanline of count bytes from lpSrc to lpDst. used by 1:1 +* scanline functions for all bit depths +*/ +void +X_CopyScanline(LPBYTE lpSrc, LPBYTE lpDst, int count) +{ + register int i; + + /* + * if the alignment of lpSrc and lpDst is the same, then + * we can get them aligned and do a faster copy + */ + if (((DWORD_PTR) lpSrc & 0x3) == ( (DWORD_PTR) lpDst & 0x3)) { + + /* align on WORD boundary */ + if ( (DWORD_PTR) lpSrc & 0x1) { + *lpDst++ = *lpSrc++; + count--; + } + + /* align on DWORD boundary */ + if ((DWORD_PTR) lpSrc & 0x2) { + * ((LPWORD) lpDst) = *((LPWORD) lpSrc); + lpDst += sizeof(WORD); + lpSrc += sizeof(WORD); + count -= sizeof(WORD); + } + + /* copy whole DWORDS */ + for ( i = (count / 4); i > 0; i--) { + *((LPDWORD) lpDst) = *((LPDWORD) lpSrc); + lpSrc += sizeof(DWORD); + lpDst += sizeof(DWORD); + } + } else { + /* the lpSrc and lpDst pointers are different + * alignment, so leave them unaligned and + * copy all the whole DWORDs + */ + for (i = (count / 4); i> 0; i--) { + *( (DWORD UNALIGNED FAR *) lpDst) = + *((DWORD UNALIGNED FAR *) lpSrc); + lpSrc += sizeof(DWORD); + lpDst += sizeof(DWORD); + } + } + + /* in either case, copy last (up to 3) bytes. */ + for ( i = count % 4; i > 0; i--) { + *lpDst++ = *lpSrc++; + } +} + +/* +* X_Stretch_1_1_8Bits +* +* copy a scanline with no change (1:1) +*/ +void +X_Stretch_1_1_8Bits(LPBYTE lpSrc, + LPBYTE lpDst, + int SrcXE, + int DstXE, + int x_fract) +{ + + X_CopyScanline(lpSrc, lpDst, DstXE); +} + + +/* +* X_Stretch_1_2_8Bits +* +* copy a scanline, doubling all the pixels (1:2) +*/ +void +X_Stretch_1_2_8Bits(LPBYTE lpSrc, + LPBYTE lpDst, + int SrcXE, + int DstXE, + int x_fract) +{ + WORD wPix; + register int i; + + for (i = 0; i < SrcXE; i++) { + + /* get a pixel and double it */ + wPix = *lpSrc++; + wPix |= (wPix << 8); + * ((WORD UNALIGNED *) lpDst) = wPix; + lpDst += sizeof(WORD); + } +} + + +/* +* X_Stretch_1_4_8Bits +* +* copy a scanline, quadrupling all the pixels (1:4) +*/ +void +X_Stretch_1_4_8Bits(LPBYTE lpSrc, + LPBYTE lpDst, + int SrcXE, + int DstXE, + int x_fract) +{ + DWORD dwPix; + register int i; + + for (i = 0; i < SrcXE; i++) { + + /* get a pixel and make four copies of it */ + dwPix = *lpSrc++; + dwPix |= (dwPix <<8); + dwPix |= (dwPix << 16); + * ((DWORD UNALIGNED *) lpDst) = dwPix; + lpDst += sizeof(DWORD); + } +} + + +/* -- 16-bit X functions -----------------------------------------------*/ + +/* +* copy one scan-line of 16 bits with no change (1:1) +*/ +void +X_Stretch_1_1_16Bits(LPBYTE lpSrc, + LPBYTE lpDst, + int SrcXE, + int DstXE, + int x_fract) +{ + + X_CopyScanline(lpSrc, lpDst, DstXE * sizeof(WORD)); + +} + + +/* +* copy one scanline of 16 bpp duplicating each pixel +*/ +void +X_Stretch_1_2_16Bits(LPBYTE lpSrc, + LPBYTE lpDst, + int SrcXE, + int DstXE, + int x_fract) +{ + + DWORD dwPix; + register int i; + + for (i = 0; i < SrcXE; i++) { + + /* get a pixel and double it */ + dwPix = * ((WORD *)lpSrc); + dwPix |= (dwPix << 16); + * ((DWORD UNALIGNED *) lpDst) = dwPix; + + lpDst += sizeof(DWORD); + lpSrc += sizeof(WORD); + } + +} + +/* +* copy one scanline of 16 bits, stretching 1:n (dest > source) +*/ +void +X_Stretch_1_N_16Bits(LPBYTE lpSrc, + LPBYTE lpDst, + int SrcXE, + int DstXE, + int x_fract) +{ + int xdelta; + register int i; + + xdelta = DstXE -1; + + for (i = 0; i < DstXE; i++) { + + /* copy one pixel and advance dest */ + *((WORD *) lpDst) = *((WORD *) lpSrc); + + lpDst += sizeof(WORD); + + /* should we advance source pointer this time ? */ + if ( (xdelta -= SrcXE) < 0) { + xdelta += DstXE; + lpSrc += sizeof(WORD); + } + } +} + +/* +* copy one scanline of 16bits, shrinking n:1 (dest < source) +*/ +void +X_Stretch_N_1_16Bits(LPBYTE lpSrc, + LPBYTE lpDst, + int SrcXE, + int DstXE, + int x_fract) +{ + + int xdelta; + register int i; + + xdelta = SrcXE -1; + + for (i = 0; i < DstXE; i++) { + + /* copy one pixel and advance dest */ + *((WORD *) lpDst) = *((WORD *)lpSrc); + + lpDst += sizeof(WORD); + + /* how many times do we advance source pointer this time ? */ + do { + lpSrc += sizeof(WORD); + xdelta -= DstXE; + } while (xdelta >= 0); + + xdelta += SrcXE; + } + +} + + +/* 24-bits ---------------------------------------------------------*/ + +/* +* copy one 24-bpp scanline as is (1:1) +*/ +void +X_Stretch_1_1_24Bits(LPBYTE lpSrc, + LPBYTE lpDst, + int SrcXE, + int DstXE, + int x_fract) +{ + X_CopyScanline(lpSrc, lpDst, DstXE * 3); +} + +/* +* copy one 24-bpp scanline stretching 1:n (dest > source) +*/ +void +X_Stretch_1_N_24Bits(LPBYTE lpSrc, + LPBYTE lpDst, + int SrcXE, + int DstXE, + int x_fract) +{ + + int xdelta; + register int i; + + xdelta = DstXE -1; + + for (i = 0; i < DstXE; i++) { + /* copy first word of pixel and advance dest */ + *((WORD UNALIGNED *) lpDst) = *((WORD UNALIGNED *) lpSrc); + + lpDst += sizeof(WORD); + + /* copy third byte and advance dest */ + *lpDst++ = lpSrc[sizeof(WORD)]; + + /* should we advance source pointer this time ? */ + if ( (xdelta -= SrcXE) < 0) { + xdelta += DstXE; + lpSrc += 3; + } + } +} + +/* +* copy one scanline of 24 bits, shrinking n:1 (dest < source) +*/ +void +X_Stretch_N_1_24Bits(LPBYTE lpSrc, + LPBYTE lpDst, + int SrcXE, + int DstXE, + int x_fract) +{ + int xdelta; + register int i; + + xdelta = SrcXE -1; + + for (i = 0; i < DstXE; i++) { + + /* copy first word of pixel and advance dest */ + *((WORD UNALIGNED *) lpDst) = *((WORD UNALIGNED *) lpSrc); + + lpDst += sizeof(WORD); + + /* copy third byte and advance dest */ + *lpDst++ = lpSrc[sizeof(WORD)]; + + + /* how many times do we advance source pointer this time ? */ + do { + lpSrc += 3; + xdelta -= DstXE; + } while (xdelta >= 0); + + xdelta += SrcXE; + } +} + + +/* 32-bits ---------------------------------------------------------*/ + +/* +* copy one 32-bpp scanline as is (1:1) +*/ +void +X_Stretch_1_1_32Bits(LPBYTE lpSrc, + LPBYTE lpDst, + int SrcXE, + int DstXE, + int x_fract) +{ + X_CopyScanline((BYTE*) lpSrc, (BYTE*) lpDst, DstXE * sizeof( RGBQUAD ) ); +} + +/* +* copy one 32-bpp scanline stretching 1:n (dest > source) +*/ +void +X_Stretch_1_N_32Bits(LPBYTE lpSrc0, + LPBYTE lpDst0, + int SrcXE, + int DstXE, + int x_fract) +{ + + int xdelta; + register int i; + + RGBQUAD *lpSrc=(RGBQUAD *)lpSrc0; + RGBQUAD *lpDst=(RGBQUAD *)lpDst0; + + + xdelta = DstXE -1; + + for (i = 0; i < DstXE; i++) + { + /* copy first word of pixel and advance dest */ + *lpDst = *lpSrc; + lpDst++; + + /* should we advance source pointer this time ? */ + if ( (xdelta -= SrcXE) < 0) + { + xdelta += DstXE; + lpSrc++; + } + } +} + +/* +* copy one scanline of 32 bits, shrinking n:1 (dest < source) +*/ +void +X_Stretch_N_1_32Bits(LPBYTE lpSrc0, + LPBYTE lpDst0, + int SrcXE, + int DstXE, + int x_fract) +{ + int xdelta; + register int i; + + RGBQUAD *lpSrc=(RGBQUAD *)lpSrc0; + RGBQUAD *lpDst=(RGBQUAD *)lpDst0; + + xdelta = SrcXE -1; + + for (i = 0; i < DstXE; i++) + { + *lpDst = *lpSrc; + lpDst++; + + /* how many times do we advance source pointer this time ? */ + do + { + lpSrc++; + xdelta -= DstXE; + } while (xdelta >= 0); + + xdelta += SrcXE; + } +} + + + + +/* -- special-case 1:2 -------------------------------------------*/ + +/* +* stretch 1:2 in both directions, for 8 bits. +* +* An experiment was done on x86 to only write every other line during +* the stretch and when the whole frame was done to use memcpy to fill +* in the gaps. This is slower than doing the stretch in a single pass. +*/ +void +Stretch_1_2_8Bits(LPBYTE lpSrc, LPBYTE lpDst, int SrcXE,int SrcYE, int DstXE, + int DstYE, int SrcWidth, int DstWidth, int x_fract) +{ + + int SrcInc, DstInc; + register int i, j; + WORD wPix; + DWORD dwPix4; + + /* amount to advance source by at the end of each scan */ + SrcInc = SrcWidth - SrcXE; + + + /* amount to advance dest by at the end of each scan - note + * that we write two scans at once, so advance past the next + * scan line + */ + DstInc = (DstWidth * 2) - DstXE; + + /* + * we would like to copy the pixels DWORD at a time. this means + * being aligned. if we are currently aligned on a WORD boundary, + * then copy one pixel to get aligned. If we are on a byte + * boundary, we can never get aligned, so use the slower loop. + */ + if ( ((DWORD_PTR)lpDst) & 1) { + + /* + * dest is byte aligned - so we can never align it + * by writing WORDs - use slow loop. + */ + for (i = 0; i < SrcYE; i++) { + + for (j = 0; j < SrcXE; j++) { + + /* get a pixel and double it */ + + wPix = *lpSrc++; + wPix |= (wPix<<8); + + + /* write doubled pixel to this scanline */ + + *( (WORD UNALIGNED *) lpDst) = wPix; + + /* write double pixel to next scanline */ + *( (WORD UNALIGNED *) (lpDst + DstWidth)) = wPix; + + lpDst += sizeof(WORD); + } + lpSrc += SrcInc; + lpDst += DstInc; + } + return; + } + + /* + * this will be the aligned version. align each scan line + */ + for ( i = 0; i < SrcYE; i++) { + + /* count of pixels remaining */ + j = SrcXE; + + /* align this scan line */ + if (((DWORD_PTR)lpDst) & 2) { + + /* word aligned - copy one doubled pixel and we are ok */ + wPix = *lpSrc++; + wPix |= (wPix << 8); + + *( (WORD *) lpDst) = wPix; + *( (WORD *) (lpDst + DstWidth)) = wPix; + lpDst += sizeof(WORD); + + j -= 1; + } + + + /* now dest is aligned - so loop eating two pixels at a time + * until there is at most one left + */ + for ( ; j > 1; j -= 2) { + + /* read two pixels and double them */ + wPix = * ((WORD UNALIGNED *) lpSrc); + lpSrc += sizeof(WORD); + + dwPix4 = (wPix & 0xff) | ((wPix & 0xff) << 8); + dwPix4 |= ((wPix & 0xff00) << 8) | ((wPix & 0xff00) << 16); + *((DWORD *) lpDst) = dwPix4; + *((DWORD *) (lpDst + DstWidth)) = dwPix4; + + lpDst += sizeof(DWORD); + } + + /* odd byte remaining ? */ + if (j > 0) { + /* word aligned - copy one doubled pixel and we are ok */ + wPix = *lpSrc++; + wPix |= (wPix << 8); + + *( (WORD *) lpDst) = wPix; + *( (WORD *) (lpDst + DstWidth)) = wPix; + lpDst += sizeof(WORD); + + j -= 1; + } + lpSrc += SrcInc; + lpDst += DstInc; + } +} + + + +/* ----------------------------------------------------------------*/ + +/* +* stretch 1:2 in both directions, for 16-bits +*/ + +void +Stretch_1_2_16Bits(LPBYTE lpSrc, LPBYTE lpDst, int SrcXE,int SrcYE, int DstXE, + int DstYE, int SrcWidth, int DstWidth, int x_fract) + +{ + int SrcInc, DstInc; + register int i, j; + DWORD dwPix; + + /* amount to advance source by at the end of each scan */ + SrcInc = SrcWidth - (SrcXE * sizeof(WORD)); + + + /* amount to advance dest by at the end of each scan - note + * that we write two scans at once, so advance past the next + * scan line + */ + DstInc = (DstWidth * 2) - (DstXE * sizeof(WORD)); + + for (i = 0; i < SrcYE; i++) { + + for (j = 0; j < SrcXE; j++) { + + /* get a pixel and double it */ + + dwPix = *((WORD *)lpSrc); + dwPix |= (dwPix<<16); + + lpSrc += sizeof(WORD); + + /* write doubled pixel to this scanline */ + + *( (DWORD UNALIGNED *) lpDst) = dwPix; + + /* write double pixel to next scanline */ + *( (DWORD UNALIGNED *) (lpDst + DstWidth)) = dwPix; + + lpDst += sizeof(DWORD); + } + lpSrc += SrcInc; + lpDst += DstInc; + + } +} diff --git a/tinyDSHOW/src/VideoDisplayName.cxx b/tinyDSHOW/src/VideoDisplayName.cxx new file mode 100644 index 0000000..a97f9e1 --- /dev/null +++ b/tinyDSHOW/src/VideoDisplayName.cxx @@ -0,0 +1,41 @@ +/* +* Copyright (C) 2009-2010 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include "tinydshow/VideoDisplayName.h" + +VideoDisplayName::VideoDisplayName(std::string name_, std::string descr) : name(name_), description(descr) +{ +} + +std::string VideoDisplayName::getName() const +{ + return this->name; +} + +std::string VideoDisplayName::getDescription() const +{ + return this->description; +} + +int VideoDisplayName::operator==(const VideoDisplayName &dev) const +{ + return this->name == dev.name; +} diff --git a/tinyDSHOW/src/VideoGrabberName.cxx b/tinyDSHOW/src/VideoGrabberName.cxx new file mode 100644 index 0000000..a0c151c --- /dev/null +++ b/tinyDSHOW/src/VideoGrabberName.cxx @@ -0,0 +1,41 @@ +/* +* Copyright (C) 2009-2010 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include "tinydshow/VideoGrabberName.h" + +VideoGrabberName::VideoGrabberName(std::string name_, std::string descr) : name(name_), description(descr) +{ +} + +std::string VideoGrabberName::getName() const +{ + return this->name; +} + +std::string VideoGrabberName::getDescription() const +{ + return this->description; +} + +int VideoGrabberName::operator==(const VideoGrabberName &dev) const +{ + return this->name == dev.name; +} \ No newline at end of file diff --git a/tinyDSHOW/src/plugin/DSConsumer.cxx b/tinyDSHOW/src/plugin/DSConsumer.cxx new file mode 100644 index 0000000..d525af6 --- /dev/null +++ b/tinyDSHOW/src/plugin/DSConsumer.cxx @@ -0,0 +1,278 @@ +/* +* Copyright (C) 2009-2010 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include "tinydshow/plugin/DSConsumer.h" + +#include +#include + +#include "tsk_debug.h" + + +#define DSCONSUMER(self) ((tdshow_consumer_t*)(self)) + +typedef struct tdshow_consumer_s +{ + TMEDIA_DECLARE_CONSUMER; + + DSDisplay* display; + INT64 window; + + tsk_bool_t started; +} +tdshow_consumer_t; + + + +/* ============ Media Producer Interface ================= */ +int tdshow_consumer_set(tmedia_consumer_t *self, const tmedia_param_t* param) +{ + int ret = 0; + + if(!self || !param){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if(param->value_type == tmedia_pvt_int64){ + if(tsk_striequals(param->key, "remote-hwnd")){ + INT64 hWnd = (INT64)*((int64_t*)param->value); + if(DSCONSUMER(self)->display){ + if(hWnd){ + DSCONSUMER(self)->display->attach(hWnd); + } + else{ + DSCONSUMER(self)->display->detach(); + } + } + else{ + DSCONSUMER(self)->window = hWnd; + } + } + } + + return ret; +} + + +int tdshow_consumer_prepare(tmedia_consumer_t* self, const tmedia_codec_t* codec) +{ + tdshow_consumer_t* consumer = (tdshow_consumer_t*)self; + + if(!consumer || !codec && codec->plugin){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + TMEDIA_CONSUMER(consumer)->video.fps = TMEDIA_CODEC_VIDEO(codec)->fps; + TMEDIA_CONSUMER(consumer)->video.in.width = TMEDIA_CODEC_VIDEO(codec)->width; + TMEDIA_CONSUMER(consumer)->video.in.height = TMEDIA_CODEC_VIDEO(codec)->height; + + if(!TMEDIA_CONSUMER(consumer)->video.display.width){ + TMEDIA_CONSUMER(consumer)->video.display.width = TMEDIA_CONSUMER(consumer)->video.in.width; + } + if(!TMEDIA_CONSUMER(consumer)->video.display.height){ + TMEDIA_CONSUMER(consumer)->video.display.height = TMEDIA_CONSUMER(consumer)->video.in.height; + } + + return 0; +} + +int tdshow_consumer_start(tmedia_consumer_t* self) +{ + tdshow_consumer_t* consumer = (tdshow_consumer_t*)self; + HRESULT hr; + + if(!consumer){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if(consumer->started){ + TSK_DEBUG_WARN("Producer already started"); + return 0; + } + + if(!consumer->display){ /* Last chance to create the display */ + if(!IsMainThread()){ + TSK_DEBUG_WARN("Creating DirectShow objects outside the MainThread"); + } + // create display + consumer->display = new DSDisplay(&hr); + if(FAILED(hr)){ + TSK_DEBUG_ERROR("Failed to created DirectShow Display"); + SAFE_DELETE_PTR(consumer->display); + return -2; + } + } + // Set parameters + consumer->display->setFps(TMEDIA_CONSUMER(consumer)->video.fps); + // do not change the display size: see hook() + // consumer->display->setSize(TMEDIA_CONSUMER(consumer)->video.display.width, TMEDIA_CONSUMER(consumer)->video.display.height); + if(consumer->window){ + consumer->display->attach(consumer->window); + } + + // Start display + TSK_DEBUG_INFO("Before starting DirectShow consumer"); + consumer->display->start(); + consumer->started = tsk_true; + TSK_DEBUG_INFO("After starting DirectShow consumer"); + + return 0; +} + +int tdshow_consumer_consume(tmedia_consumer_t* self, void** buffer, tsk_size_t size, const tsk_object_t* proto_hdr) +{ + tdshow_consumer_t* consumer = (tdshow_consumer_t*)self; + if(consumer && consumer->display && buffer){ + consumer->display->handleVideoFrame(*buffer, TMEDIA_CONSUMER(consumer)->video.display.width, TMEDIA_CONSUMER(consumer)->video.display.height); + return 0; + } + else{ + TSK_DEBUG_ERROR("Invlide parameter"); + return -1; + } +} + +int tdshow_consumer_pause(tmedia_consumer_t* self) +{ + tdshow_consumer_t* consumer = (tdshow_consumer_t*)self; + + if(!consumer){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if(!consumer->display){ + TSK_DEBUG_ERROR("Invalid internal grabber"); + return -2; + } + + //consumer->display->pause(); + + return 0; +} + +int tdshow_consumer_stop(tmedia_consumer_t* self) +{ + tdshow_consumer_t* consumer = (tdshow_consumer_t*)self; + + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if(!consumer->started){ + TSK_DEBUG_WARN("Consumer not started"); + return 0; + } + + if(!consumer->display){ + TSK_DEBUG_ERROR("Invalid internal display"); + return -2; + } + + TSK_DEBUG_INFO("Before stopping DirectShow consumer"); + consumer->display->stop(); + consumer->started = tsk_false; + TSK_DEBUG_INFO("After stopping DirectShow consumer"); + + return 0; +} + + +// +// WaveAPI consumer object definition +// +/* constructor */ +static tsk_object_t* tdshow_consumer_ctor(tsk_object_t * self, va_list * app) +{ + CoInitialize(NULL); + + tdshow_consumer_t *consumer = (tdshow_consumer_t *)self; + if(consumer){ + HRESULT hr; + + /* init base */ + tmedia_consumer_init(TMEDIA_CONSUMER(consumer)); + TMEDIA_CONSUMER(consumer)->video.display.chroma = tmedia_bgr24; // RGB24 on x86 (little endians) stored as BGR24 + + /* init self */ + TMEDIA_CONSUMER(consumer)->video.fps = 15; + TMEDIA_CONSUMER(consumer)->video.display.width = 352; + TMEDIA_CONSUMER(consumer)->video.display.height = 288; + TMEDIA_CONSUMER(consumer)->video.display.auto_resize = tsk_true; + + if(IsMainThread()){ + consumer->display = new DSDisplay(&hr); + if(FAILED(hr)){ + TSK_DEBUG_ERROR("Failed to created DirectShow Display"); + SAFE_DELETE_PTR(consumer->display); + } + } + } + return self; +} +/* destructor */ +static tsk_object_t* tdshow_consumer_dtor(tsk_object_t * self) +{ + tdshow_consumer_t *consumer = (tdshow_consumer_t *)self; + if(consumer){ + + /* stop */ + if(consumer->started){ + tdshow_consumer_stop((tmedia_consumer_t*)self); + } + + /* deinit base */ + tmedia_consumer_deinit(TMEDIA_CONSUMER(consumer)); + /* deinit self */ + SAFE_DELETE_PTR(consumer->display); + + } + + return self; +} +/* object definition */ +static const tsk_object_def_t tdshow_consumer_def_s = +{ + sizeof(tdshow_consumer_t), + tdshow_consumer_ctor, + tdshow_consumer_dtor, + tsk_null, +}; +/* plugin definition*/ +static const tmedia_consumer_plugin_def_t tdshow_consumer_plugin_def_s = +{ + &tdshow_consumer_def_s, + + tmedia_video, + "Microsoft DirectShow consumer", + + tdshow_consumer_set, + tdshow_consumer_prepare, + tdshow_consumer_start, + tdshow_consumer_consume, + tdshow_consumer_pause, + tdshow_consumer_stop +}; +extern const tmedia_consumer_plugin_def_t *tdshow_consumer_plugin_def_t = &tdshow_consumer_plugin_def_s; diff --git a/tinyDSHOW/src/plugin/DSProducer.cxx b/tinyDSHOW/src/plugin/DSProducer.cxx new file mode 100644 index 0000000..52b110f --- /dev/null +++ b/tinyDSHOW/src/plugin/DSProducer.cxx @@ -0,0 +1,278 @@ +/* +* Copyright (C) 2009-2010 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include "tinydshow/plugin/DSProducer.h" + +#include +#include +#include + +#include "tsk_debug.h" + + +#define DSPRODUCER(self) ((tdshow_producer_t*)(self)) + +typedef struct tdshow_producer_s +{ + TMEDIA_DECLARE_PRODUCER; + + DSGrabber* grabber; + INT64 previewHwnd; + + int fps; + int width; + int height; + + tsk_bool_t started; +} +tdshow_producer_t; + +// Producer callback (From DirectShow Grabber to our plugin) +static int tdshow_plugin_cb(const void* callback_data, const void* buffer, tsk_size_t size) +{ + const tdshow_producer_t* producer = (const tdshow_producer_t*)callback_data; + + if(producer && TMEDIA_PRODUCER(producer)->enc_cb.callback){ + TMEDIA_PRODUCER(producer)->enc_cb.callback(TMEDIA_PRODUCER(producer)->enc_cb.callback_data, buffer, size); + } + + return 0; +} + + +/* ============ Media Producer Interface ================= */ +int tdshow_producer_set(tmedia_producer_t *self, const tmedia_param_t* param) +{ + int ret = 0; + + if(!self || !param){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if(param->value_type == tmedia_pvt_int64){ + if(tsk_striequals(param->key, "local-hwnd")){ + if(DSPRODUCER(self)->grabber && DSPRODUCER(self)->grabber->preview){ + DSPRODUCER(self)->grabber->preview->attach((INT64)*((int64_t*)param->value)); + } + else{ + DSPRODUCER(self)->previewHwnd = (INT64)*((int64_t*)param->value); + } + } + } + + return ret; +} + +int tdshow_producer_prepare(tmedia_producer_t* self, const tmedia_codec_t* codec) +{ + tdshow_producer_t* producer = (tdshow_producer_t*)self; + + if(!producer || !codec && codec->plugin){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + producer->fps = TMEDIA_CODEC_VIDEO(codec)->fps; + producer->width = TMEDIA_CODEC_VIDEO(codec)->width; + producer->height = TMEDIA_CODEC_VIDEO(codec)->height; + + return 0; +} + +int tdshow_producer_start(tmedia_producer_t* self) +{ + tdshow_producer_t* producer = (tdshow_producer_t*)self; + VIDEOFORMAT format; + HRESULT hr; + + if(!producer){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if(producer->started){ + TSK_DEBUG_WARN("Producer already started"); + return 0; + } + + if(!producer->grabber){ /* Last chance to greate the graber */ + if(!IsMainThread()){ + TSK_DEBUG_WARN("Creating DirectShow objects outside the MainThread"); + } + producer->grabber = new DSGrabber(&hr); + if(FAILED(hr)){ + TSK_DEBUG_ERROR("Failed to created DirectShow Grabber"); + SAFE_DELETE_PTR(producer->grabber); + return -2; + } + } + + //set Source device + producer->grabber->setCaptureDevice("Null"); + + // set parameters + SIZE_TO_VIDEOFORMAT(producer->width, producer->height, format); + producer->grabber->setCaptureParameters(format, producer->fps); + + // set callback function + producer->grabber->setCallback(tdshow_plugin_cb, producer); + + // attach preview + if(producer->grabber->preview){ + if(producer->previewHwnd){ + producer->grabber->preview->attach(producer->previewHwnd); + } + producer->grabber->preview->setSize(producer->width, producer->height); + } + + // start grabber + TSK_DEBUG_INFO("Before starting DirectShow producer"); + producer->grabber->start(); + producer->started = tsk_true; + TSK_DEBUG_INFO("After starting DirectShow producer"); + + return 0; +} + +int tdshow_producer_pause(tmedia_producer_t* self) +{ + tdshow_producer_t* producer = (tdshow_producer_t*)self; + + if(!producer){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if(!producer->grabber){ + TSK_DEBUG_ERROR("Invalid internal grabber"); + return -2; + } + + //producer->grabber->pause(); + + return 0; +} + +int tdshow_producer_stop(tmedia_producer_t* self) +{ + tdshow_producer_t* producer = (tdshow_producer_t*)self; + + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if(!producer->started){ + TSK_DEBUG_WARN("Producer not started"); + return 0; + } + + if(!producer->grabber){ + TSK_DEBUG_ERROR("Invalid internal grabber"); + return -2; + } + + producer->grabber->stop(); + producer->started = tsk_false; + + return 0; +} + + +// +// WaveAPI producer object definition +// +/* constructor */ +static tsk_object_t* tdshow_producer_ctor(tsk_object_t * self, va_list * app) +{ + CoInitialize(NULL); + + tdshow_producer_t *producer = (tdshow_producer_t *)self; + if(producer){ + HRESULT hr; + + /* init base */ + tmedia_producer_init(TMEDIA_PRODUCER(producer)); + TMEDIA_PRODUCER(producer)->video.chroma = tmedia_bgr24; // RGB24 on x86 (little endians) stored as BGR24 + /* init self with default values*/ + producer->fps = 15; + producer->width = 176; + producer->height = 144; + + if(IsMainThread()){ + producer->grabber = new DSGrabber(&hr); + if(FAILED(hr)){ + TSK_DEBUG_ERROR("Failed to created DirectShow Grabber"); + SAFE_DELETE_PTR(producer->grabber); + } + } + } + return self; +} +/* destructor */ +static tsk_object_t* tdshow_producer_dtor(tsk_object_t * self) +{ + tdshow_producer_t *producer = (tdshow_producer_t *)self; + if(producer){ + + /* stop */ + if(producer->started){ + tdshow_producer_stop((tmedia_producer_t*)self); + } + + /* for safety */ + if(producer->grabber){ + producer->grabber->setCallback(tsk_null, tsk_null); + } + + /* deinit base */ + tmedia_producer_deinit(TMEDIA_PRODUCER(producer)); + /* deinit self */ + SAFE_DELETE_PTR(producer->grabber); + + } + + return self; +} +/* object definition */ +static const tsk_object_def_t tdshow_producer_def_s = +{ + sizeof(tdshow_producer_t), + tdshow_producer_ctor, + tdshow_producer_dtor, + tsk_null, +}; +/* plugin definition*/ +static const tmedia_producer_plugin_def_t tdshow_producer_plugin_def_s = +{ + &tdshow_producer_def_s, + + tmedia_video, + "Microsoft DirectShow producer", + + tdshow_producer_set, + tdshow_producer_prepare, + tdshow_producer_start, + tdshow_producer_pause, + tdshow_producer_stop +}; +extern const tmedia_producer_plugin_def_t *tdshow_producer_plugin_def_t = &tdshow_producer_plugin_def_s; -- cgit v1.1