diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..24bffe8 --- /dev/null +++ b/.hgignore @@ -0,0 +1,14 @@ +syntax: glob +Debug/* +Release/* +_UpgradeReport_Files/* +UpgradeLog?.XML +Thumbs.db +DSGrab.ncb +DSGrab.opensdf +DSGrab.sdf +DSGrab.suo +DSGrab.vcproj.SAPPHIRE.Sahab Yazdani.user +DSGrab.vcxproj.filters +DSGrab.vcxproj.user +UpgradeLog.XML diff --git a/DSGrab.cpp b/DSGrab.cpp new file mode 100644 index 0000000..d2e93d2 --- /dev/null +++ b/DSGrab.cpp @@ -0,0 +1,893 @@ +// DSGrab Version 1.0.0 + +//The MIT License +// +//Copyright (c) 2011 Sahab Yazdani +// +//Permission is hereby granted, free of charge, to any person obtaining a copy +//of this software and associated documentation files (the "Software"), to deal +//in the Software without restriction, including without limitation the rights +//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//copies of the Software, and to permit persons to whom the Software is +//furnished to do so, subject to the following conditions: +// +//The above copyright notice and this permission notice shall be included in +//all copies or substantial portions of the Software. +// +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +//THE SOFTWARE. + +// Win32 +#include +#include + +// COM + +/* +#pragma include_alias( "dxtrans.h", "qedit.h" ) +#define __IDxtCompositor_INTERFACE_DEFINED__ +#define __IDxtAlphaSetter_INTERFACE_DEFINED__ +#define __IDxtJpeg_INTERFACE_DEFINED__ +#define __IDxtKey_INTERFACE_DEFINED__ +*/ + +#include +#include "qedit.h" +#include +#include + +// GDI+ +#include +#include + +// std c++ +#include +#include +#include +#include +#include +#include + +// boost +#include + +typedef std::basic_string tstring; +typedef std::basic_stringstream tstringstream; + +#ifdef _UNICODE +#define tcout std::wcout +#define tcerr std::wcerr +#else +#define tcout std::cout +#define tcerr std::cerr +#endif // _UNICODE + +static ULONG gdiplusToken; + +namespace Exception { + class NoSuchDevice { + }; + class COMError { + public: + COMError( HRESULT hr ) : hr(hr) {}; + HRESULT hr; + }; + class NoSuchCLSID { + }; + class BadExtension { + public: + BadExtension( tstring extension ) : extension(extension) {}; + tstring extension; + }; +} + +class CaptureDevice { +public: + typedef std::vector IPinCollection; + +public: + CaptureDevice( IBaseFilter *f ); + ~CaptureDevice(); + + void EnumerateDeviceCaps( ); + + static void EnumerateCaptureDevices( std::map< tstring, IBaseFilter * > &filterMap ); + + void SetResolution( LONG width, LONG height, WORD bitDepth = 0 ); + + Gdiplus::Bitmap *GetSingleSnapshot( DWORD wait = 0 ); +protected: + IPin *FindVideoOutputPin( ); + + Gdiplus::Bitmap *SerializeFrame( ISampleGrabber *sampleGrabber ); + static IPin *_findPin( IBaseFilter *f, PIN_DIRECTION direction ); +private: + IBaseFilter *inputFilter; + IBaseFilter *outputFilter; SIZE outputDimensions; + IPin *videoOutputPin; +}; + +CaptureDevice::CaptureDevice( IBaseFilter *f ) : +inputFilter(f), outputFilter(NULL) { + outputDimensions.cx = 0; + outputDimensions.cy = 0; + try { + videoOutputPin = FindVideoOutputPin( ); + if ( videoOutputPin == NULL ) throw Exception::NoSuchDevice(); + } catch ( ... ) { + throw; + } +} + +CaptureDevice::~CaptureDevice() { + // cleanup Interfaces + videoOutputPin->Release(); + + inputFilter->Release(); + if ( outputFilter!=NULL ) outputFilter->Release(); +} + +void CaptureDevice::EnumerateDeviceCaps() { + HRESULT hr; + + IAMStreamConfig *streamConfig; + int capCount, capSize; + VIDEO_STREAM_CONFIG_CAPS caps; + AM_MEDIA_TYPE *media_type; + + hr = videoOutputPin->QueryInterface( IID_IAMStreamConfig, reinterpret_cast< LPVOID * >( &streamConfig ) ); + if ( FAILED( hr ) ) { + throw Exception::COMError( hr ); + } + hr = streamConfig->GetNumberOfCapabilities( &capCount, &capSize ); + + for ( int i=0;iGetStreamCaps( i, &media_type, reinterpret_cast< BYTE * >( &caps ) ); + + if ( media_type->majortype!=MEDIATYPE_Video ) continue; + + unsigned char bitDepth; + if ( media_type->subtype == MEDIASUBTYPE_RGB1 ) { bitDepth = 1; } + else if ( media_type->subtype == MEDIASUBTYPE_RGB4 ) { bitDepth = 4; } + else if ( media_type->subtype == MEDIASUBTYPE_RGB8 ) { bitDepth = 8; } + else if ( media_type->subtype == MEDIASUBTYPE_RGB555 ) { bitDepth = 15; } + else if ( media_type->subtype == MEDIASUBTYPE_RGB565 ) { bitDepth = 16; } + else if ( media_type->subtype == MEDIASUBTYPE_RGB24 ) { bitDepth = 24; } + else if ( media_type->subtype == MEDIASUBTYPE_RGB32 ) { bitDepth = 32; } + else { bitDepth = 0; } + + tcout << (i+1) << _T( ") Width: " ) << caps.InputSize.cx << + _T( "\tHeight:" ) << caps.InputSize.cy << + _T( "\tBit Depth:" ) << bitDepth << + std::endl; + + // free media_type + } + + streamConfig->Release(); +} + +Gdiplus::Bitmap *CaptureDevice::GetSingleSnapshot( DWORD wait /* = 0 */ ) { + using namespace Gdiplus; + + HRESULT hr; + + IGraphBuilder *graphBuilder; + IVideoWindow *videoWindow; + IMediaControl *control; + IMediaEvent *mediaEvent; + + ISampleGrabber *sampleGrabber; + + if ( outputFilter == NULL ) { + hr = ::CoCreateInstance( CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, reinterpret_cast< LPVOID * >( &outputFilter ) ); + hr = outputFilter->QueryInterface( IID_ISampleGrabber, reinterpret_cast< LPVOID * >( &sampleGrabber ) ); + + IAMStreamConfig *streamConfig; + AM_MEDIA_TYPE *sourceAM, am; + + hr = videoOutputPin->QueryInterface( IID_IAMStreamConfig, reinterpret_cast< LPVOID * >( &streamConfig ) ); + hr = streamConfig->GetFormat( &sourceAM ); + + ZeroMemory( &am, sizeof( AM_MEDIA_TYPE ) ); + am.majortype = sourceAM->majortype; + am.subtype = sourceAM->subtype; + + hr = sampleGrabber->SetMediaType( &am ); + + // TODO: release sourceAM + + streamConfig->Release(); + } else { + hr = outputFilter->QueryInterface( IID_ISampleGrabber, reinterpret_cast< LPVOID * >( &sampleGrabber ) ); + } + + hr = sampleGrabber->SetBufferSamples( TRUE ); + hr = sampleGrabber->SetOneShot( wait == 0 ? TRUE : FALSE ); + + hr = ::CoCreateInstance( CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, reinterpret_cast< LPVOID * >( &graphBuilder ) ); + + graphBuilder->AddFilter( inputFilter, L"Input" ); + graphBuilder->AddFilter( outputFilter, L"Output" ); + + graphBuilder->Connect( videoOutputPin, _findPin( outputFilter, PINDIR_INPUT ) ); + hr = graphBuilder->Render( _findPin( outputFilter, PINDIR_OUTPUT ) ); + + hr = graphBuilder->QueryInterface( IID_IVideoWindow, reinterpret_cast< LPVOID * >( &videoWindow ) ); + videoWindow->put_AutoShow( FALSE ); + videoWindow->Release(); + + hr = graphBuilder->QueryInterface( IID_IMediaControl, reinterpret_cast< LPVOID * >( &control ) ); + control->Run(); + + long eventCode; + hr = graphBuilder->QueryInterface( IID_IMediaEvent, reinterpret_cast< LPVOID * >( &mediaEvent ) ); + hr = mediaEvent->WaitForCompletion( wait == 0 ? INFINITE : wait, &eventCode ); + mediaEvent->Release(); + + Bitmap *target = SerializeFrame( sampleGrabber ); + + control->Stop(); + control->Release(); + + graphBuilder->Release(); + sampleGrabber->Release(); + + return target; +} + +Gdiplus::Bitmap *CaptureDevice::SerializeFrame( ISampleGrabber *sampleGrabber ) { + using namespace Gdiplus; + + HRESULT hr; + + long bufferSize; + hr = sampleGrabber->GetCurrentBuffer( &bufferSize, NULL ); + unsigned char *buffer = new unsigned char[bufferSize]; + hr = sampleGrabber->GetCurrentBuffer( &bufferSize, reinterpret_cast< long * >( buffer ) ); + + AM_MEDIA_TYPE connectedMediaType; + BITMAPINFOHEADER *bitmapHeader; + sampleGrabber->GetConnectedMediaType( &connectedMediaType ); + + if ( connectedMediaType.formattype == FORMAT_VideoInfo ) { + VIDEOINFOHEADER *vih = reinterpret_cast< VIDEOINFOHEADER * >( connectedMediaType.pbFormat ); + bitmapHeader = &vih->bmiHeader; + } else if ( connectedMediaType.formattype == FORMAT_VideoInfo2 ) { + VIDEOINFOHEADER2 *vih = reinterpret_cast< VIDEOINFOHEADER2 * >( connectedMediaType.pbFormat ); + bitmapHeader = &vih->bmiHeader; + } else { + throw Exception::NoSuchDevice(); + } + + PixelFormat bitDepth; + if ( connectedMediaType.subtype == MEDIASUBTYPE_RGB1 ) { bitDepth = PixelFormat1bppIndexed; } + else if ( connectedMediaType.subtype == MEDIASUBTYPE_RGB4 ) { bitDepth = PixelFormat4bppIndexed; } + else if ( connectedMediaType.subtype == MEDIASUBTYPE_RGB8 ) { bitDepth = PixelFormat8bppIndexed; } + else if ( connectedMediaType.subtype == MEDIASUBTYPE_RGB555 ) { bitDepth = PixelFormat16bppRGB555; } + else if ( connectedMediaType.subtype == MEDIASUBTYPE_RGB565 ) { bitDepth = PixelFormat16bppRGB565; } + else if ( connectedMediaType.subtype == MEDIASUBTYPE_RGB24 ) { bitDepth = PixelFormat24bppRGB; } + else if ( connectedMediaType.subtype == MEDIASUBTYPE_RGB32 ) { bitDepth = PixelFormat32bppRGB; } + else { throw Exception::NoSuchDevice(); } + + Bitmap *target = new Bitmap( bitmapHeader->biWidth, + bitmapHeader->biHeight, + bitDepth ); + Rect rect( 0, 0, bitmapHeader->biWidth, bitmapHeader->biHeight ); + BitmapData bitmapData; + Status s = target->LockBits( &rect, ImageLockModeWrite, bitDepth, &bitmapData ); + memcpy( reinterpret_cast< void * >( bitmapData.Scan0 ), reinterpret_cast< const void * >( buffer ), bufferSize ); + target->UnlockBits( &bitmapData ); + target->RotateFlip( RotateNoneFlipY ); // stream comes in upside down? + + // don't resize to 0, don't bother resizing to nothingness + if ( outputDimensions.cx != 0 && outputDimensions.cy !=0 && + outputDimensions.cx!=bitmapHeader->biWidth && outputDimensions.cy!=bitmapHeader->biHeight ) { + // scale the image to the desired size + Bitmap *scaledTarget = new Bitmap( outputDimensions.cx, outputDimensions.cy ); + Graphics graphics( scaledTarget ); + graphics.DrawImage( target, 0, 0, outputDimensions.cx, outputDimensions.cy ); + + delete target; + + target = scaledTarget; + } + + delete buffer; + + return target; +} + +// attempts to find the output pin that matches the type of input the caller gave +// if the method can't find any it throws an exception +// on error, throw an exception +void CaptureDevice::SetResolution( LONG width, LONG height, WORD bitDepth /* = 0 */ ) { + using namespace std; + + HRESULT hr; + + IAMStreamConfig *streamConfig; + int capCount, capSize; + VIDEO_STREAM_CONFIG_CAPS caps; + AM_MEDIA_TYPE *media_type; + + AM_MEDIA_TYPE *closestMediaType = NULL; + WORD closestDeltaBitDepth = 0xff; // max value + + // error checking + if ( width <= 0 || height <= 0 ) { + return; // error + } + + // enumerate all the caps, looking for a good match + hr = videoOutputPin->QueryInterface( IID_IAMStreamConfig, reinterpret_cast< LPVOID * >( &streamConfig ) ); + if ( FAILED( hr ) ) { + throw Exception::COMError( hr ); + } + hr = streamConfig->GetNumberOfCapabilities( &capCount, &capSize ); + for ( int i=0;iGetStreamCaps( i, &media_type, reinterpret_cast< BYTE * >( &caps ) ); + + if ( media_type->majortype!=MEDIATYPE_Video ) continue; + +// only uncompressed RGB is suitable input right now + unsigned char bD; + if ( media_type->subtype == MEDIASUBTYPE_RGB1 ) { bD = 1; } + else if ( media_type->subtype == MEDIASUBTYPE_RGB4 ) { bD = 4; } + else if ( media_type->subtype == MEDIASUBTYPE_RGB8 ) { bD = 8; } + else if ( media_type->subtype == MEDIASUBTYPE_RGB555 ) { bD = 15; } + else if ( media_type->subtype == MEDIASUBTYPE_RGB565 ) { bD = 16; } + else if ( media_type->subtype == MEDIASUBTYPE_RGB24 ) { bD = 24; } + else if ( media_type->subtype == MEDIASUBTYPE_RGB32 ) { bD = 32; } + else { continue; } + + WORD deltaBitDepth = abs( bD - bitDepth ); + bool use = false; + if ( caps.InputSize.cx == width && caps.InputSize.cy == height ) { + if ( closestMediaType == NULL || ( deltaBitDepth < closestDeltaBitDepth && bitDepth!=0 ) ) { + closestDeltaBitDepth = deltaBitDepth; + closestMediaType = media_type; + use = true; + } + } + } + + if ( closestMediaType == NULL ) { + streamConfig->Release(); + + throw Exception::NoSuchDevice(); + } else { + streamConfig->SetFormat( closestMediaType ); + } + + streamConfig->Release(); +} + +IPin *CaptureDevice::FindVideoOutputPin( ) { + BOOL bFound = FALSE; + IEnumPins *enumerator; + IPin *retVal; + + if ( inputFilter==NULL ) { + throw Exception::NoSuchDevice(); + } + + HRESULT hr = inputFilter->EnumPins(&enumerator); + if (FAILED(hr)) + throw Exception::COMError( hr ); + while(enumerator->Next(1, &retVal, 0) == S_OK) + { + PIN_DIRECTION PinDirThis; + retVal->QueryDirection(&PinDirThis); + if ( PinDirThis == PINDIR_OUTPUT ) { + // it's in the right direction... + IAMStreamConfig *streamConfig; + int capCount, capSize; + + hr = retVal->QueryInterface( IID_IAMStreamConfig, reinterpret_cast< LPVOID * >( &streamConfig ) ); + if ( FAILED( hr ) ) { + throw Exception::COMError( hr ); + } + hr = streamConfig->GetNumberOfCapabilities( &capCount, &capSize ); + if ( FAILED( hr ) ) { + throw Exception::COMError( hr ); + } + if ( capSize == sizeof( VIDEO_STREAM_CONFIG_CAPS ) ) { + // is a video output pin + streamConfig->Release(); + bFound = TRUE; + + break; + } + + streamConfig->Release(); + } + retVal->Release(); + } + enumerator->Release(); + + return bFound ? retVal : NULL; +} + +IPin *CaptureDevice::_findPin( IBaseFilter *f, PIN_DIRECTION direction ) { + BOOL bFound = FALSE; + IEnumPins *enumerator; + IPin *retVal; + + HRESULT hr = f->EnumPins(&enumerator); + if (FAILED(hr)) + throw Exception::COMError( hr ); + while(enumerator->Next(1, &retVal, 0) == S_OK) + { + PIN_DIRECTION PinDirThis; + retVal->QueryDirection(&PinDirThis); + if ( PinDirThis == direction ) { + // it's in the right direction... + bFound = TRUE; + break; + } + retVal->Release(); + } + enumerator->Release(); + + return bFound ? retVal : NULL; +} + +CLSID GetEncoderClsid(const WCHAR* format) +{ + using namespace Gdiplus; + + CLSID pClsid; + + UINT num = 0; // number of image encoders + UINT size = 0; // size of the image encoder array in bytes + + ImageCodecInfo* pImageCodecInfo = NULL; + + GetImageEncodersSize(&num, &size); + if(size == 0) + throw Exception::COMError( 0 ); + + pImageCodecInfo = (ImageCodecInfo*)(malloc(size)); + if(pImageCodecInfo == NULL) + throw Exception::COMError( 0 ); + + GetImageEncoders(num, size, pImageCodecInfo); + + for(UINT j = 0; j < num; ++j) + { + if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 ) + { + pClsid = pImageCodecInfo[j].Clsid; + free(pImageCodecInfo); + return pClsid; + } + } + + free(pImageCodecInfo); + throw Exception::NoSuchCLSID(); +} + +CLSID CreateEncoderClsid( const tstring &path ) { + CLSID pClsid; + tstring extension = path.substr( path.find_last_of( _T( '.' ) ) + 1 ); + + if ( extension == _T( "" ) ) { + throw Exception::BadExtension( extension ); + } + + try { + if ( extension == _T( "bmp" ) ) { + pClsid = GetEncoderClsid( L"image/bmp" ); + } else if ( extension == _T( "jpg" ) ) { + pClsid = GetEncoderClsid( L"image/jpeg" ); + } else if ( extension == _T( "png" ) ) { + pClsid = GetEncoderClsid( L"image/png" ); + } else if ( extension == _T( "tif" ) ) { + pClsid = GetEncoderClsid( L"image/tiff" ); + } else if ( extension == _T( "gif" ) ) { + pClsid = GetEncoderClsid( L"image/gif" ) ; + } else { + throw Exception::BadExtension( extension ); + } + } catch ( ... ) { + throw; + } + + return pClsid; +} + +void CaptureDevice::EnumerateCaptureDevices( std::map< tstring, IBaseFilter * > &filterMap ) { + HRESULT result; + + ICreateDevEnum *enumerator; + IEnumMoniker *enumMoniker; + IMoniker *moniker; + ULONG fetched; + + result = ::CoCreateInstance( CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, reinterpret_cast< LPVOID * >( &enumerator ) ); + + result = enumerator->CreateClassEnumerator( CLSID_VideoInputDeviceCategory, &enumMoniker, 0 ); + if ( result!=S_OK ) { + enumerator->Release(); + + throw Exception::COMError( result ); + } + + while ( enumMoniker->Next( 1, &moniker, &fetched )==S_OK ) { + IPropertyBag *bag; + VARIANT variant; + + result = moniker->BindToStorage( 0, 0, IID_IPropertyBag, reinterpret_cast< LPVOID * >( &bag ) ); + + ::VariantInit( &variant ); + + bag->Read( L"FriendlyName", &variant, NULL ); + _bstr_t friendlyName( variant.bstrVal, true ); + + bag->Release(); + ::VariantClear( &variant ); + + IBaseFilter *baseFilter; + result = moniker->BindToObject( 0, 0, IID_IBaseFilter, reinterpret_cast< LPVOID * >( &baseFilter ) ); + + filterMap[tstring( friendlyName )] = baseFilter; + + ::VariantClear( &variant ); + + moniker->Release(); + } + enumMoniker->Release(); + enumerator->Release(); +} + +void parseCommandLine( tstring cmdLine, std::map &collection ) { + const TCHAR PARAM_PREFIX = _T( '\\' ); + const TCHAR PARAM_QUOTE = _T( '\"' ); + + bool inQuotes = false; + bool haveSlash = false; + + cmdLine.push_back( _T( ' ' ) ); + tstring *newToken = new tstring(); + for ( tstring::iterator i = cmdLine.begin(); i!=cmdLine.end(); i++ ) { + switch ( *i ) { + case _T( ' ' ): + // the only real delimiter + + if ( !inQuotes ) { + tstring::size_type colonPos = newToken->find( _T( ':' ) ); + if ( colonPos != tstring::npos ) { + tstring partA = newToken->substr( 0, colonPos ); + tstring partB = newToken->substr( colonPos + 1 ); + if ( partB[0] == PARAM_QUOTE && partB[partB.length()-1] == PARAM_QUOTE ) { + partB = partB.substr( 1, partB.length() - 2 ); + } + + collection[partA] = partB; + } else { + collection[*newToken] = tstring(); + } + delete newToken; + newToken = new tstring(); + } else { + newToken->push_back( *i ); + } + break; + case PARAM_PREFIX: + // \'s are special because they can negate the quotes + + if ( haveSlash ) { + newToken->push_back( *i ); + haveSlash = false; + break; + } else { + haveSlash = true; + } + + break; + case PARAM_QUOTE: + // quotes are special cuz they can negate the space + if ( haveSlash ) { + haveSlash = false; + } else { + inQuotes = !inQuotes; + } + newToken->push_back( *i ); + + break; + default: + if ( haveSlash ) { + newToken->push_back( PARAM_PREFIX ); + haveSlash = false; + } + + newToken->push_back( *i ); + + break; + } + } +} + +void tokenize(const tstring& str, std::vector& tokens, const tstring& delimiters = _T( " " ) ) +{ + // Skip delimiters at beginning. + tstring::size_type lastPos = str.find_first_not_of(delimiters, 0); + // Find first "non-delimiter". + tstring::size_type pos = str.find_first_of(delimiters, lastPos); + + while (tstring::npos != pos || tstring::npos != lastPos) + { + // Found a token, add it to the vector. + tokens.push_back(str.substr(lastPos, pos - lastPos)); + // Skip delimiters. Note the "not_of" + lastPos = str.find_first_not_of(delimiters, pos); + // Find next "non-delimiter" + pos = str.find_first_of(delimiters, lastPos); + } +} + +void ShowHeader() { + using namespace std; + +/* + ::GetFileVersionInfoSize( lptstrFilename, lpdwHandle ); + ::GetFileVersionInfo( lptstrFilename, dwHandle, dwLen, lpData ); + ::VerQueryValue( pBlock, lpSubBlock, lplpBuffer, puLen ); +*/ + + tcout << _T( "DSGrab Version 1.0.0 By Sahab Yazdani" ) << endl; + tcout << _T( "http://www.saliences.com/projects/dsgrab/index.html" ) << endl << endl; +} + +class COMToken { +public: + COMToken() { + HRESULT hr = ::CoInitialize( NULL); + if ( FAILED( hr ) ) { + throw Exception::COMError( hr ); + } + } + ~COMToken() { + ::CoUninitialize(); + } +}; + +void ShowUsageInformation() { + using namespace std; + + tcout << _T( "[/list]: Lists all available Capture Devices on the system" ) << endl; + tcout << _T( "[/use:\"DEVICE NAME\"]: Uses the Capture Device with name \"DEVICE NAME\" for" ) << endl; + tcout << _T( " Capture" ) << endl; + tcout << _T( "[/caps]: Lists the output formats supported by the Capture" ) << endl; + tcout << _T( " Device specified with /use" ) << endl; + tcout << _T( "[/dim:WxHxBPP]: Sets the capture format to one supported by the Capture" ) << endl; + tcout << _T( " Device." ) << endl; + tcout << _T( "[/resize:WxHxBPP]: Resizes the captured frame to an image of size W by H" ) << endl; + tcout << _T( " with BPP bits of colour." ) << endl; + tcout << _T( "[/output:FILENAME]: Sets the location where the resulting captured image" ) << endl; + tcout << _T( " will be saved." ) << endl; + tcout << _T( "[/wait:TIME]: Specifies how long to wait before capturing the image" ) << endl; + tcout << _T( " (in milliseconds)" ) << endl; + tcout << _T( "[/silent]: Supresses all output." ) << endl; + tcout << _T( "[/help]: Display this help screen." ) << endl; + tcout << endl; + tcout << _T( "Examples:" ) << endl; + tcout << endl; + tcout << _T( "List the Capture Devices on the system." ) << endl; + tcout << _T( "\tDSGrab /list" ) << endl; + tcout << endl; + tcout << _T( "List the Capture Formats supported by the device \"ATI AVStream Analog Capture\"" ) << endl; + tcout << _T( "\tDSGrab /use:\"ATI AVStream Analog Capture\" /caps" ) << endl; + tcout << endl; + tcout << _T( "Capture a frame of size 320 by 240 with 24 bits of colour from the device" ) << endl; + tcout << _T( "ATI AVStream Analog Capture\" to the file \"capture.jpg\"" ) << endl; + tcout << _T( "\tDSGrab /use:\"ATI AVStream Analog Capture\" /dim:320x240x24 /output:capture.jpg" ) << endl; +} + +int _tmain( int argc, TCHAR *argv[] ) { + using namespace Gdiplus; + + GdiplusStartupInput gdiplusStartup; + + try { + COMToken comToken; + + Status s = GdiplusStartup( &gdiplusToken, &gdiplusStartup, NULL ); + + std::map cmdLineArgs; + parseCommandLine( tstring( ::GetCommandLine() ), cmdLineArgs ); + + std::map< tstring, IBaseFilter * > deviceMap; + try { + CaptureDevice::EnumerateCaptureDevices( deviceMap ); + } catch ( Exception::COMError ) { + tcerr << _T( "COM Error enumerating Capture Devices." ) << std::endl; + return -1; + } + + if ( cmdLineArgs.find( _T( "/silent" ) )==cmdLineArgs.end() ) { + ShowHeader(); + } + + if ( cmdLineArgs.find( _T( "/help" ) )!=cmdLineArgs.end() ) { + ShowUsageInformation(); + + return 0; + } + + if ( cmdLineArgs.find( _T( "/list" ) )!=cmdLineArgs.end() ) { + tcout << "Available Capture Devices:" << std::endl; + + int counter = 1; + for ( std::map< tstring, IBaseFilter * >::iterator i = deviceMap.begin(); i!=deviceMap.end(); i++, counter++ ) { + tcout << counter << _T( ") " ) << i->first << std::endl; + } + + return 0; + } + + if ( cmdLineArgs.find( _T( "/use" ) )!=cmdLineArgs.end() ) { + try { + CaptureDevice device( deviceMap[cmdLineArgs[_T("/use")]] ); + + if ( cmdLineArgs.find( _T( "/caps" ) )!=cmdLineArgs.end() ) { + tcout << "List of Available Output formats for device \"" << cmdLineArgs[_T("/use")] << "\":" << std::endl; + device.EnumerateDeviceCaps(); + + return 0; + } + + if ( cmdLineArgs.find( _T( "/dim" ) )!=cmdLineArgs.end() ) { + std::vector tokens; + tokenize( cmdLineArgs[_T( "/dim" )], tokens, _T( "x" ) ); + if ( tokens.size() < 2 ) { + tcerr << _T( "Not enough parameters for argument //dims." ) << std::endl; + + return -1; + } + + try { + LONG width = boost::lexical_cast( tokens[0] ); + LONG height = boost::lexical_cast( tokens[1] ); + + try { + if ( tokens.size() == 3 ) { + device.SetResolution( width, height, (WORD)( boost::lexical_cast( tokens[2] ) ) ); + } else { + device.SetResolution( width, height ); + } + } catch ( Exception::NoSuchDevice ) { + tcerr << _T( "The capture device doesn't support the selected image dimensions. Please use the \\caps parameter to see the support output resolutions." ) << std::endl; + } catch ( Exception::COMError ) { + // TODO: Error Recover + } + } catch ( boost::bad_lexical_cast ) { + tcerr << _T( "The //dim parameter has not been formatted correctly." ) << std::endl; + return -1; + } + } + + if ( cmdLineArgs.find( _T( "/output" ) )!=cmdLineArgs.end() ) { + // output something to a bitmap + DWORD wait = 0; + if ( cmdLineArgs.find( _T( "/wait" ) )!=cmdLineArgs.end() ) { + try { + wait = boost::lexical_cast( cmdLineArgs[_T("/wait")] ); + } catch ( boost::bad_lexical_cast ) { + tcerr << _T( "The //wait parameter has not been formatted correctly." ) << std::endl; + return -1; + } + } + std::auto_ptr bitmap( device.GetSingleSnapshot( wait ) ); + + try { + CLSID clsidEncoder = CreateEncoderClsid( cmdLineArgs[_T("/output")] ); + + if ( cmdLineArgs.find( _T( "/resize" ) )!=cmdLineArgs.end() ) { + std::vector tokens; + tokenize( cmdLineArgs[_T( "/resize" )], tokens, _T( "x" ) ); + if ( tokens.size() < 2 ) { + tcerr << _T( "Not enough parameters for argument //resize." ) << std::endl; + + return -1; + } + + LONG width = boost::lexical_cast( tokens[0] ); + LONG height = boost::lexical_cast( tokens[1] ); + PixelFormat bitDepth; + + if ( tokens.size() == 3 ) { + WORD bD = boost::lexical_cast( tokens[2] ); + + switch ( bD ) { + case 1: + bitDepth = PixelFormat1bppIndexed; + break; + case 4: + bitDepth = PixelFormat4bppIndexed; + break; + case 8: + bitDepth = PixelFormat8bppIndexed; + break; + case 16: + bitDepth = PixelFormat16bppRGB565; + break; + case 24: + bitDepth = PixelFormat24bppRGB; + break; + case 32: + bitDepth = PixelFormat32bppARGB; + default: + tcerr << _T( "Unsupported Bit Depth, defaulting to 32 bits." ) << std::endl; + return -1; + } + } else { + bitDepth = bitmap->GetPixelFormat(); + } + + std::auto_ptr backSurface( new Bitmap( width, height, bitDepth ) ); + s = backSurface->SetResolution( bitmap->GetHorizontalResolution(), bitmap->GetVerticalResolution() ); + if ( s!=Ok ) { + tcerr << _T( "Error setting output resolution." ) << std::endl; + return -1; + } + + Graphics transformer( backSurface.get() ); + s = transformer.SetInterpolationMode( InterpolationModeHighQualityBicubic ); + if ( s!=Ok ) { + tcerr << _T( "Error setting Interpolation Mode." ) << std::endl; + return -1; + } + s = transformer.DrawImage( bitmap.get(), + Rect( 0, 0, width, height ), + 0, 0, bitmap->GetWidth(), bitmap->GetHeight(), + UnitPixel, + NULL, NULL, NULL ); + if ( s!=Ok ) { + tcerr << _T( "Error resizing output image." ) << std::endl; + return -1; + } + + s = backSurface->Save( cmdLineArgs[_T("/output")].c_str(), &clsidEncoder, NULL ); + } else { + s = bitmap->Save( cmdLineArgs[_T("/output")].c_str(), &clsidEncoder, NULL ); + } + + if ( s!=Ok ) { + tcerr << _T( "Error saving output image." ) << std::endl; + return -1; + } + } catch ( Exception::NoSuchCLSID ) { + tcerr << _T( "Cannot convert to the image format specified." ) << std::endl; + return -1; + } catch ( Exception::BadExtension e ) { + tcerr << _T( "The extension " ) << e.extension << _T( " is not support by DSGrab." ) << std::endl; + return -1; + } catch ( boost::bad_lexical_cast ) { + tcerr << _T( "The //resize parameter has not been formatted correctly." ) << std::endl; + return -1; + } + } + } catch ( Exception::NoSuchDevice ) { + tcerr << _T( "No Capture Device with the name \"" ) << cmdLineArgs[_T("/use")] << _T( "\" exists. Use the /list argument to find the list of capture devices on your computer." ) << std::endl; + + return -1; + } catch ( Exception::COMError ) { + tcerr << _T( "COM Error initializing Capture Device." ) << std::endl; + + return -1; + } + } + } catch ( Exception::COMError ) { + return -1; + } + + return 0; +} \ No newline at end of file diff --git a/DSGrab.sln b/DSGrab.sln new file mode 100644 index 0000000..1cabd76 --- /dev/null +++ b/DSGrab.sln @@ -0,0 +1,19 @@ +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DSGrab", "DSGrab.vcxproj", "{CA6E496B-BE7B-4D8C-A3EA-17A769D59374}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CA6E496B-BE7B-4D8C-A3EA-17A769D59374}.Debug|Win32.ActiveCfg = Debug|Win32 + {CA6E496B-BE7B-4D8C-A3EA-17A769D59374}.Debug|Win32.Build.0 = Debug|Win32 + {CA6E496B-BE7B-4D8C-A3EA-17A769D59374}.Release|Win32.ActiveCfg = Release|Win32 + {CA6E496B-BE7B-4D8C-A3EA-17A769D59374}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/DSGrab.vcproj b/DSGrab.vcproj new file mode 100644 index 0000000..b16a023 --- /dev/null +++ b/DSGrab.vcproj @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DSGrab.vcxproj b/DSGrab.vcxproj new file mode 100644 index 0000000..4c3b4f1 --- /dev/null +++ b/DSGrab.vcxproj @@ -0,0 +1,110 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {CA6E496B-BE7B-4D8C-A3EA-17A769D59374} + DSGrab + Win32Proj + + + + Application + Unicode + + + Application + MultiByte + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + Debug\ + Debug\ + true + Release\ + Release\ + false + + + + Disabled + UNICODE;_UNICODE;_WIN32_WINNT=0x500;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + + + Level3 + EditAndContinue + + + Strmiids.lib;Ole32.lib;comsupp.lib;gdiplus.lib;%(AdditionalDependencies) + $(OutDir)DSGrab.exe + true + $(OutDir)DSGrab.pdb + Console + false + + + MachineX86 + + + + + UNICODE;_UNICODE;%(PreprocessorDefinitions) + MultiThreaded + + + Level3 + ProgramDatabase + + + Strmiids.lib;Ole32.lib;comsupp.lib;gdiplus.lib;%(AdditionalDependencies) + $(OutDir)DSGrab.exe + true + Console + true + true + false + + + MachineX86 + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..56efc9a --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2011 Sahab Yazdani + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/acknowledgements.txt b/acknowledgements.txt new file mode 100644 index 0000000..79d87c4 --- /dev/null +++ b/acknowledgements.txt @@ -0,0 +1,2 @@ +The icon for this application was taken from: +http://www.skinbase.org/gallery.php?uname=weboso diff --git a/mainicon.ico b/mainicon.ico new file mode 100644 index 0000000..e172c09 Binary files /dev/null and b/mainicon.ico differ diff --git a/qedit.h b/qedit.h new file mode 100644 index 0000000..176406e --- /dev/null +++ b/qedit.h @@ -0,0 +1,163 @@ +// +// qedit.h - cutted version of qedit.h from DirectX SDK +// +// Copyright (c) Shareaza Development Team, 2010. +// This file is part of SHAREAZA (shareaza.sourceforge.net) +// +// Shareaza 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 2 of +// the License, or (at your option) any later version. +// +// Shareaza 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 Shareaza; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + +#pragma once + +#ifndef __ISampleGrabberCB_INTERFACE_DEFINED__ +#define __ISampleGrabberCB_INTERFACE_DEFINED__ + +#include + +/* interface ISampleGrabberCB */ +/* [unique][helpstring][local][uuid][object] */ + +EXTERN_C const IID IID_ISampleGrabberCB; + +MIDL_INTERFACE("0579154A-2B53-4994-B0D0-E773148EFF85") +ISampleGrabberCB : public IUnknown +{ +public: + virtual HRESULT STDMETHODCALLTYPE SampleCB( + double SampleTime, + IMediaSample *pSample) = 0; + + virtual HRESULT STDMETHODCALLTYPE BufferCB( + double SampleTime, + BYTE *pBuffer, + long BufferLen) = 0; +}; + +#endif /* __ISampleGrabberCB_INTERFACE_DEFINED__ */ + +#ifndef __ISampleGrabber_INTERFACE_DEFINED__ +#define __ISampleGrabber_INTERFACE_DEFINED__ + +/* interface ISampleGrabber */ +/* [unique][helpstring][local][uuid][object] */ + +EXTERN_C const IID IID_ISampleGrabber; + +MIDL_INTERFACE("6B652FFF-11FE-4fce-92AD-0266B5D7C78F") +ISampleGrabber : public IUnknown +{ +public: + virtual HRESULT STDMETHODCALLTYPE SetOneShot( + BOOL OneShot) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetMediaType( + const AM_MEDIA_TYPE *pType) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetConnectedMediaType( + AM_MEDIA_TYPE *pType) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetBufferSamples( + BOOL BufferThem) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetCurrentBuffer( + /* [out][in] */ long *pBufferSize, + /* [out] */ long *pBuffer) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetCurrentSample( + /* [retval][out] */ IMediaSample **ppSample) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetCallback( + ISampleGrabberCB *pCallback, + long WhichMethodToCallback) = 0; +}; + +DEFINE_GUID(CLSID_SampleGrabber,0xc1f400a0,0x3f08,0x11d3,0x9f,0x0b,0x00,0x60,0x08,0x03,0x9e,0x37); + +#endif /* __ISampleGrabber_INTERFACE_DEFINED__ */ + +#ifndef __IMediaDet_INTERFACE_DEFINED__ +#define __IMediaDet_INTERFACE_DEFINED__ + +/* interface IMediaDet */ +/* [unique][helpstring][uuid][object] */ + +EXTERN_C const IID IID_IMediaDet; + +MIDL_INTERFACE("65BD0710-24D2-4ff7-9324-ED2E5D3ABAFA") +IMediaDet : public IUnknown +{ +public: + virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_Filter( + /* [retval][out] */ __RPC__deref_out_opt IUnknown **pVal) = 0; + + virtual /* [helpstring][id][propput] */ HRESULT STDMETHODCALLTYPE put_Filter( + /* [in] */ __RPC__in_opt IUnknown *newVal) = 0; + + virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_OutputStreams( + /* [retval][out] */ __RPC__out long *pVal) = 0; + + virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_CurrentStream( + /* [retval][out] */ __RPC__out long *pVal) = 0; + + virtual /* [helpstring][id][propput] */ HRESULT STDMETHODCALLTYPE put_CurrentStream( + /* [in] */ long newVal) = 0; + + virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_StreamType( + /* [retval][out] */ __RPC__out GUID *pVal) = 0; + + virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_StreamTypeB( + /* [retval][out] */ __RPC__deref_out_opt BSTR *pVal) = 0; + + virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_StreamLength( + /* [retval][out] */ __RPC__out double *pVal) = 0; + + virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_Filename( + /* [retval][out] */ __RPC__deref_out_opt BSTR *pVal) = 0; + + virtual /* [helpstring][id][propput] */ HRESULT STDMETHODCALLTYPE put_Filename( + /* [in] */ __RPC__in BSTR newVal) = 0; + + virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE GetBitmapBits( + double StreamTime, + __RPC__in long *pBufferSize, + __RPC__in char *pBuffer, + long Width, + long Height) = 0; + + virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE WriteBitmapBits( + double StreamTime, + long Width, + long Height, + __RPC__in BSTR Filename) = 0; + + virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_StreamMediaType( + /* [retval][out] */ __RPC__out AM_MEDIA_TYPE *pVal) = 0; + + virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE GetSampleGrabber( + /* [out] */ __RPC__deref_out_opt ISampleGrabber **ppVal) = 0; + + virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_FrameRate( + /* [retval][out] */ __RPC__out double *pVal) = 0; + + virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE EnterBitmapGrabMode( + double SeekTime) = 0; +}; + +#endif /* __IMediaDet_INTERFACE_DEFINED__ */ + +EXTERN_C const CLSID CLSID_MediaDet; + +class DECLSPEC_UUID("65BD0711-24D2-4ff7-9324-ED2E5D3ABAFA") +MediaDet; diff --git a/resource.aps b/resource.aps new file mode 100644 index 0000000..a26453b Binary files /dev/null and b/resource.aps differ diff --git a/resource.h b/resource.h new file mode 100644 index 0000000..8736c3b --- /dev/null +++ b/resource.h @@ -0,0 +1,17 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by resource.rc +// +#define IDI_MAINICON 5 +#define IDI_ICON1 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/resource.rc b/resource.rc new file mode 100644 index 0000000..e9acba6 --- /dev/null +++ b/resource.rc @@ -0,0 +1,122 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (Canada) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENC) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_CAN +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "100904b0" + BEGIN + VALUE "FileDescription", "DirectShow Capture Device Console Application" + VALUE "FileVersion", "1, 0, 0, 1" + VALUE "InternalName", "DSGrab" + VALUE "LegalCopyright", "Public Domain" + VALUE "OriginalFilename", "DSGrab.exe" + VALUE "ProductName", "DSGrab" + VALUE "ProductVersion", "1, 0, 0, 1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x1009, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_MAINICON ICON "mainicon.ico" +#endif // English (Canada) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/webcam.php b/webcam.php new file mode 100644 index 0000000..0caa770 --- /dev/null +++ b/webcam.php @@ -0,0 +1,91 @@ +"; +$PATH_TO_WINSCP = "C:\Program Files\winscp3\winscp3.com"; +# Do the Capture +$command = <<< EOT +start /b {$PATH_TODSGRAB} /silent /use:"{$CAPTUREDEVICE}" /dim:{$IMAGEWIDTH}x{$IMAGEHEIGHT}x24 /wait:3000 /output:{$PATH_TO_OUTPUT}.png +EOT; +$output = array(); +$returnValue = 0; +exec( $command, $output, $returnValue ); + +# Do the timestamping using GD2 +$imHandle = imagecreatefrompng( "{$PATH_TO_OUTPUT}.png" ); +if ( $imHandle ) { + $imageString = date( $DATEFORMAT ); + $fontPath = "C:\\Windows\\Fonts\\${FONTFILE}"; + + $box = imagettfbbox( $TEXTSIZE, 0, $fontPath, $imageString ); + $textwidth = abs( $box[4] - $box[0] ); + $textheight = abs( $box[5] - $box[1] ); + $x = ( $IMAGEWIDTH - $textwidth ) / 2; // centre on the x-axis + $y = $IMAGEHEIGHT - $textheight - 4; // magic number is the bottom padding + + $color = imagecolorallocate( $imHandle, 255, 0, 0 ); + + $boundingBox = imagettftext( $imHandle, $TEXTSIZE, 0, $x, $y, $color, $fontPath, $imageString ); + imageinterlace( $imHandle, 1 ); + imagejpeg( $imHandle, "{$PATH_TO_OUTPUT}.jpg", $JPEGQUALITY ); + imagedestroy( $imHandle ); + unlink( "{$PATH_TO_OUTPUT}.png" ); +} + +# Do the Actual Transfer +$command = <<< EOT +option batch on +option confirm off +open "{$REMOTE_NAME}" +option transfer binary +put "{$PATH_TO_OUTPUT}" {$PATH_TO_REMOTE} +close +exit +EOT; + +$tmpfile = tempnam( "%TMP", "wc" ); +$handle = fopen( $tmpfile, 'w' ); +fwrite( $handle, $command ); +fclose( $handle ); + +exec( "start /b {$PATH_TO_WINSCP} /script={$tmpfile} > nul" ); +unlink( $tmpfile ); +?> \ No newline at end of file