From: libvidcap c. <lib...@li...> - 2010-02-01 19:29:06
|
Revision: 102 http://libvidcap.svn.sourceforge.net/libvidcap/?rev=102&view=rev Author: bcholew Date: 2010-02-01 19:29:00 +0000 (Mon, 01 Feb 2010) Log Message: ----------- Merge Chris Cooksey's branches/framesize_enhancements back into trunk. Added support for DV cameras. In addition, added support for any frame size, any frame rate, or any of three color spaces requested by the client, regardless of whether they are physically supported by the device. NOTE: these enhancements have NOT been added to the Linux source. Also note that device aspect ratios are not preserved. If a 4:3 frame size is requested from a device that has a 16:9 resolution, the image will be scaled to fit. These changes do NOT alter the public interface. Modified Paths: -------------- trunk/examples/vidcapTester/Grabber.cpp trunk/src/conv.c trunk/src/directshow/DirectShowSource.cpp trunk/src/directshow/DirectShowSource.h trunk/src/directshow/SourceStateMachine.cpp trunk/src/directshow/SourceStateMachine.h trunk/src/sapi.c trunk/src/sapi_context.h trunk/src/sapi_dshow.cpp trunk/src/sapi_qt.c trunk/src/sapi_v4l.c trunk/src/vidcap.c Modified: trunk/examples/vidcapTester/Grabber.cpp =================================================================== --- trunk/examples/vidcapTester/Grabber.cpp 2010-02-01 19:00:34 UTC (rev 101) +++ trunk/examples/vidcapTester/Grabber.cpp 2010-02-01 19:29:00 UTC (rev 102) @@ -132,7 +132,6 @@ case VIDCAP_FOURCC_I420: rgb_buf.resize(width_ * height_ * 4); - vidcap_i420_to_rgb32(width_, height_, cap_info->video_data, rgb_buf.data()); break; Modified: trunk/src/conv.c =================================================================== --- trunk/src/conv.c 2010-02-01 19:00:34 UTC (rev 101) +++ trunk/src/conv.c 2010-02-01 19:29:00 UTC (rev 102) @@ -23,6 +23,10 @@ * */ +#if HAVE_QUICKTIME +#include <QuickTime/ImageCodec.h> +#endif + #include "string.h" #include "conv.h" #include "logging.h" @@ -101,7 +105,8 @@ int u_width, int u_height, int v_width, int v_height, int y_stride, int u_stride, int v_stride, - const char * src, char * dst) + const char * src_y, const char * src_u, const char * src_v, + char * dst) { char * dst_y_even = dst; char * dst_y_odd = dst + width; @@ -109,12 +114,12 @@ char * dst_u_odd = dst_u_even + u_width; char * dst_v_even = dst_u_even + u_width * u_height; char * dst_v_odd = dst_v_even + v_width; - const char * src_y_even = src; - const char * src_y_odd = src + y_stride; - const char * src_u_even = src + y_stride * height; - const char * src_u_odd = src_u_even + u_stride; - const char * src_v_even = src_u_even + u_stride * u_height; - const char * src_v_odd = src_v_even + v_stride; + const char * src_y_even = src_y; + const char * src_y_odd = src_y + y_stride; + const char * src_u_even = src_u; + const char * src_u_odd = src_u + u_stride; + const char * src_v_even = src_v; + const char * src_v_odd = src_v + v_stride; int i; @@ -164,15 +169,34 @@ switch ( fourcc ) { - case VIDCAP_FOURCC_I420: + case VIDCAP_FOURCC_I420: { + #if HAVE_QUICKTIME + /* src points to a PlanarPixmapInfoYUV420 struct, not raw data */ + PlanarPixmapInfoYUV420 *pmInfo = (PlanarPixmapInfoYUV420 *)src; + SInt32 y_offset = EndianS32_BtoN( pmInfo->componentInfoY.offset ); + SInt32 u_offset = EndianS32_BtoN( pmInfo->componentInfoCb.offset ); + SInt32 v_offset = EndianS32_BtoN( pmInfo->componentInfoCr.offset ); + UInt32 y_rowBytes = EndianU32_BtoN( pmInfo->componentInfoY.rowBytes ); + UInt32 u_rowBytes = EndianU32_BtoN( pmInfo->componentInfoCb.rowBytes ); + UInt32 v_rowBytes = EndianU32_BtoN( pmInfo->componentInfoCr.rowBytes ); + return destride_planar(width, height, + width / 2, height / 2, width / 2, height / 2, + y_rowBytes, u_rowBytes, v_rowBytes, + src + y_offset, src + u_offset, src + v_offset, dst); + #else + /* FIXME: only destride if necessary */ + const char * src_y = src; + const char * src_u = src_y + height * stride; + const char * src_v = src_u + (height * stride) / 4; log_error("UNTESTED: destriding i420\n"); - /* FIXME: only destride if necessary */ return destride_planar(width, height, /* fix these u and v strides to be 32-bit aligned? */ width / 2, height / 2, width / 2, height / 2, stride, stride / 2, stride / 2, - src, dst); + src_y, src_u, src_v, dst); + #endif break; + } case VIDCAP_FOURCC_YUY2: if ( stride == 2 * width ) return -1; @@ -198,15 +222,19 @@ return -1; return destride_packed(3 * width, height, stride, src, dst); break; - case VIDCAP_FOURCC_YVU9: + case VIDCAP_FOURCC_YVU9: { + /* FIXME: only destride if necessary */ + const char * src_y = src; + const char * src_u = src_y + height * stride; + const char * src_v = src_u + (height * stride) / 4; log_error("UNTESTED: destriding yvu9\n"); - /* FIXME: only destride if necessary */ return destride_planar(width, height, width / 4, height / 4, width / 4, height / 4, /* fix u and v strides to be 32-bit aligned? */ stride, stride / 4, stride / 4, - src, dst); + src_y, src_u, src_v, dst); break; + } default: log_error("Invalid fourcc [%s]\n", vidcap_fourcc_string_get(fourcc)); Modified: trunk/src/directshow/DirectShowSource.cpp =================================================================== --- trunk/src/directshow/DirectShowSource.cpp 2010-02-01 19:00:34 UTC (rev 101) +++ trunk/src/directshow/DirectShowSource.cpp 2010-02-01 19:29:00 UTC (rev 102) @@ -53,11 +53,16 @@ pMediaControlIF_(0), nativeMediaType_(0), graphIsSetup_(false), - graphHandle_(0) + graphHandle_(0), + nativeMediaTypeSufficient_(true), + buffer_(NULL), + bufferSize_(0) { IBindCtx * pBindCtx = 0; IMoniker * pMoniker = 0; + ZeroMemory( &outputMediaType_, sizeof( AM_MEDIA_TYPE ) ); + // Get the capture device - identified by it's long display name if ( !getCaptureDevice(getID(), &pBindCtx, &pMoniker) ){ log_warn("Failed to get device '%s'.\n", getID()); @@ -112,6 +117,9 @@ if ( nativeMediaType_ ) freeMediaType(*nativeMediaType_); + if( buffer_ != NULL ) + CoTaskMemFree( (PVOID)buffer_ ); + destroyCapGraphFoo(); pSource_->Release(); @@ -338,6 +346,7 @@ if ( !graphIsSetup_ ) { // set the stream's media type + // this tells the capture graph what output to produce HRESULT hr = pStreamConfig_->SetFormat(nativeMediaType_); if ( FAILED(hr) ) { @@ -347,6 +356,10 @@ } // Set sample grabber's media type + // this tells the sample grabber which output to produce. + if( !nativeMediaTypeSufficient_ ) + hr = pSampleGrabberIF_->SetMediaType(&outputMediaType_); + else hr = pSampleGrabberIF_->SetMediaType(nativeMediaType_); if ( FAILED(hr) ) { @@ -354,6 +367,7 @@ return -1; } + // add the sample grabber to the graph hr = pFilterGraph_->AddFilter(pSampleGrabber_, L"Sample Grabber"); if ( FAILED(hr) ) { @@ -361,6 +375,9 @@ return -1; } + // Set up a capture graph of type video. Source is the capture device. + // It will run data through our sample grabber object (which has a + // callback to us), and then discard the data in the Null renderer. hr = pCapGraphBuilder_->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pSource_, @@ -424,50 +441,109 @@ int DirectShowSource::validateFormat(const vidcap_fmt_info * fmtNominal, - vidcap_fmt_info * fmtNative) const + vidcap_fmt_info * fmtNative, int forBinding ) const { +/* This is called both when checking the hot list and when binding to a specific */ +/* format. fmtNative is ignored when checking the hot list, but when binding it */ +/* indicates the format of the sample buffer that will be generated. */ +/* forBinding indicates how this function is being used. If it is 0, we are */ +/* looking for formats that are natively, or very nearly natively supported by the */ +/* device, i.e. we are building the supported formats list. If it is 1, we are */ +/* attempting to bind to a format, i.e. a client is asking for a specific format. */ +/* In that case, we may relax the rules, and be more flexible about what can be */ +/* accepted. The quality in this latter case may suffer, but it will still work. */ +/* The function result is a bool. True on success. */ + AM_MEDIA_TYPE *mediaFormat; - if ( !findBestFormat(fmtNominal, fmtNative, &mediaFormat) ) - return 0; + if ( findBestFormat( fmtNominal, fmtNative, &mediaFormat ) ) + { + freeMediaType( *mediaFormat ); + return 1; + } + else + if( forBinding ) + { + nativeMediaTypeSufficient_ = false; + if( findUsableFormat( fmtNominal, fmtNative, &mediaFormat, false ) ) + { + freeMediaType( *mediaFormat ); + return 1; + } + } - freeMediaType(*mediaFormat); - return 1; + return 0; } int DirectShowSource::bindFormat(const vidcap_fmt_info * fmtNominal) { +/* Given the desired sample format in fmtNominal, find a device native format we can */ +/* use to get there. The native format does not have to match the nominal format, but */ +/* must be convertible to it. If we can't use a simple native format, we will need to */ +/* look for something that can get us part of the way there, and then potentially scale */ +/* the samples as they arrive in the callback. Further conversion from RGB may also be */ +/* needed. Basically we are setting the field nativeMediaType_ to an actual media */ +/* format that the device may legally be set to. If that can't get us to the nominal */ +/* format, we may need to further create a sample grabber media format that will */ +/* convert the data to RGB, which we will scale in the callback function. Note that */ +/* libvidcap gets information about the format of the sample buffer by calling */ +/* validateFormat(), not bindFormat(). The two functions need to mirror one another */ +/* when binding, so that they agree on how the samples will be processed. */ +/* The function result is an error code. 0 on success. */ + vidcap_fmt_info fmtNative; - // If we've already got one, free it + /* Create input format */ if ( nativeMediaType_ ) { freeMediaType(*nativeMediaType_); nativeMediaType_ = 0; } - if ( !findBestFormat(fmtNominal, &fmtNative, &nativeMediaType_) ) - return 1; - - // set the framerate - VIDEOINFOHEADER * vih = (VIDEOINFOHEADER *) nativeMediaType_->pbFormat; + /* If nativeMediaTypeSufficient_ is true, fmtNative will be the device format and */ + /* the sample grabber format. If nativeMediaTypeSufficient_ is false, fmtNative */ + /* will only describe the sample grabber format, it will not reflect what we */ + /* apply to the device internally. nativeMediaType_ should be applied to the sample */ + /* grabber element so that it will produce the samples in the format needed by the */ + /* sample callback. */ + if ( findBestFormat( fmtNominal, &fmtNative, &nativeMediaType_ ) ) + { + nativeMediaTypeSufficient_ = true; + // Adjust the native media type to match the desired fps, height and width + // We could actually do this in findBestFormat. It wouldn't affect hot + // list creation. But let's keep the changes to a minimum for now. + VIDEOINFOHEADER * vih = (VIDEOINFOHEADER *)nativeMediaType_->pbFormat; vih->AvgTimePerFrame = 10000000 * fmtNative.fps_denominator / fmtNative.fps_numerator; - - // set the dimensions vih->bmiHeader.biWidth = fmtNative.width; vih->bmiHeader.biHeight = fmtNative.height; + return 0; + } + else + { + /* We could not find a perfect or close match. Now we must do some work. */ + /* Indicate that the native media type is not sufficient to get the format we */ + /* want. Ask DirectShow for RGB samples, (which appears to be legal), and */ + /* allocate buffers into which we can scale the samples received to the correct */ + /* size (or simply flip them if that is sufficient). */ + nativeMediaTypeSufficient_ = false; + if( findUsableFormat( fmtNominal, &fmtNative, &nativeMediaType_, true ) ) + return 0; + } - return 0; + return 1; } bool DirectShowSource::findBestFormat(const vidcap_fmt_info * fmtNominal, vidcap_fmt_info * fmtNative, AM_MEDIA_TYPE **mediaFormat) const +{ +/* Given the nominal format, fill in the AM_MEDIA_TYPE structure with */ +/* the native device format to use as the filter graph source device */ +/* format. It has to be something the device actually supports. */ -{ bool needsFpsEnforcement = false; bool needsFmtConv = false; @@ -598,10 +674,8 @@ // take note of native media type, fps, dimensions, fourcc *mediaFormat = candidateFmtProps[bestFmtNum].mediaFormat; - fmtNative->fps_numerator = - fmtsNative[bestFmtNum].fps_numerator; - fmtNative->fps_denominator = - fmtsNative[bestFmtNum].fps_denominator; + fmtNative->fps_numerator = fmtsNative[bestFmtNum].fps_numerator; + fmtNative->fps_denominator = fmtsNative[bestFmtNum].fps_denominator; fmtNative->width = fmtsNative[bestFmtNum].width; fmtNative->height = fmtsNative[bestFmtNum].height; fmtNative->fourcc = fmtsNative[bestFmtNum].fourcc; @@ -613,6 +687,172 @@ return itCanWork; } + +bool +DirectShowSource::findUsableFormat( const vidcap_fmt_info *fmtNominal, + vidcap_fmt_info * fmtNative, AM_MEDIA_TYPE **mediaFormat, bool forSampling ) const +{ +/* Ask the device for RGB samples at any size (preferably one bigger than or equal */ +/* to what we want). We will resample and flip the frames received before passing */ +/* them on to the host. Note that fmtNative is set up as though the device actually */ +/* produced the resized RGB samples. This method could probably be combined with */ +/* findBestFormat but that one is too complex at this point. forSampling will be true */ +/* if we need to create the sample scaling / flipping buffer. */ + + /* Look for a capability that can provide equal or more data than requested */ + int bestFormat = 0; + if( !findBestCapability( fmtNominal, bestFormat ) ) + { + log_error("failed to find any suitable device interface\n"); + return false; + } + + /* Use the most suitable capability on the capture device */ + VIDEO_STREAM_CONFIG_CAPS scc; + HRESULT hr = pStreamConfig_->GetStreamCaps( bestFormat, mediaFormat, (BYTE *)&scc ); + if ( FAILED(hr) ) + { + log_error("failed to get device capabilities (%d)\n", hr); + return false; + } + + /* Pick maximum frame rate and maximum frame size. DV does not use FORMAT_VideoInfo */ + /* header. In theory. I have seen it there though so I don't know what that is all */ + /* about. */ + if( (*mediaFormat)->formattype == FORMAT_VideoInfo ) + { + VIDEOINFOHEADER *vih = (VIDEOINFOHEADER *)(*mediaFormat)->pbFormat; + vih->AvgTimePerFrame = scc.MinFrameInterval; + vih->bmiHeader.biWidth = scc.MaxOutputSize.cx; + vih->bmiHeader.biHeight = scc.MaxOutputSize.cy; + } + + /* Set special sample grabber media format to produce RGB32 */ + ZeroMemory( &outputMediaType_, sizeof( AM_MEDIA_TYPE ) ); + outputMediaType_.majortype = MEDIATYPE_Video; + outputMediaType_.subtype = MEDIASUBTYPE_RGB32; + + /* Allocate buffers into which we can convert and vertically flip the RGB sample to */ + /* then hand back to the client. We only need this buffer allocated if we plan to */ + /* sample frames. */ + if( forSampling ) + { + if( buffer_ != NULL ) + CoTaskMemFree( (PVOID)buffer_ ); + bufferSize_ = fmtNominal->width * fmtNominal->height * 4; + buffer_ = (BYTE *)CoTaskMemAlloc( bufferSize_ ); + if( buffer_ == NULL ) + { + freeMediaType( **mediaFormat ); + return 0; + } + ZeroMemory( buffer_, bufferSize_ ); + } + + /* Tell the caller what our faux native output will look like (remember, we will be */ + /* secretly resizing and vertically flipping the data before handing the sample to */ + /* the client). */ + fmtNative->fourcc = VIDCAP_FOURCC_RGB32; + fmtNative->fps_denominator = 100; + fmtNative->fps_numerator = (int)(1000000000 / scc.MinFrameInterval); + fmtNative->width = fmtNominal->width; + fmtNative->height = fmtNominal->height; + + /* For our secret converter, remember what we are getting from the device, and */ + /* remember what we promised to our client. */ + fmtRealSample_ = *fmtNative; + fmtRealSample_.width = scc.MaxOutputSize.cx; + fmtRealSample_.height = scc.MaxOutputSize.cy; + fmtFauxSample_ = *fmtNative; + + return 1; +} + +bool +DirectShowSource::findBestCapability( const vidcap_fmt_info *fmtNominal, int &bestFormat ) const +{ +/* Look through the device capabilities, and make sure we are using the largest */ +/* available size that satisfies the format we want. Our goal is to scale down, but to */ +/* scale the least possible amount of data down. Failing that, we will settle for */ +/* scaling the largest output format up. Frame size is preferred over framerate. Frames */ +/* that are large enough will always beat frames that are too small, regardless of the */ +/* frame rate. */ + + int i; + HRESULT hr; + VIDEO_STREAM_CONFIG_CAPS scc; + AM_MEDIA_TYPE *pMediaType; + int desiredWidth = fmtNominal->width; + int desiredHeight = fmtNominal->height; + LONGLONG desiredInterval = 1000000000 * fmtNominal->fps_denominator / fmtNominal->fps_numerator / 100; + + bestFormat = 0; + + /* Get device capability count */ + int iCount = 0; + int iSize = 0; + hr = pStreamConfig_->GetNumberOfCapabilities( &iCount, &iSize ); + if( hr != S_OK ) + { + log_error( "could not get device capability count\n" ); + return false; + } + if( iSize != sizeof( VIDEO_STREAM_CONFIG_CAPS ) ) + { + log_error( "capabilities struct is wrong size (%d not %d)\n", + iSize, sizeof( VIDEO_STREAM_CONFIG_CAPS ) ); + return false; + } + + /* Get first interface. Use as base for comparison */ + hr = pStreamConfig_->GetStreamCaps( 0, &pMediaType, (BYTE *)&scc ); + if ( FAILED(hr) ) + { + log_error("failed to get device capabilities (0, %d)\n", hr); + return false; + } + freeMediaType( *pMediaType ); + + int bestWidth = scc.MaxOutputSize.cx; + int bestHeight = scc.MaxOutputSize.cy; + LONGLONG bestInterval = scc.MinFrameInterval; + + for( i = 1; i < iCount; i++ ) + { + HRESULT hr = pStreamConfig_->GetStreamCaps( i, &pMediaType, (BYTE *)&scc ); + if( !FAILED( hr ) ) + freeMediaType( *pMediaType ); + + if( hr == S_OK ) + { + bool bestSizeSmallerThanDesired = bestWidth < desiredWidth || bestHeight < desiredHeight; + bool currentSizeBiggerThanOrEqualToDesired = scc.MaxOutputSize.cx >= desiredWidth && scc.MaxOutputSize.cy >= desiredHeight; + bool currentSizeSmallerThanBest = scc.MaxOutputSize.cx < bestWidth || scc.MaxOutputSize.cx < bestHeight; + bool currentSizeEqualToBest = scc.MaxOutputSize.cx == bestWidth && scc.MaxOutputSize.cy == bestHeight; + bool currentSizeBiggerThanBest = scc.MaxOutputSize.cx >= bestWidth && scc.MaxOutputSize.cy >= bestHeight && !currentSizeEqualToBest; + + bool bestRateSlowerThanDesired = bestInterval > desiredInterval; + bool currentRateFasterThanOrEqualToDesired = scc.MinFrameInterval <= desiredInterval; + bool currentRateSlowerThanBest = scc.MinFrameInterval > bestInterval; + bool currentRateFasterThanBestRate = scc.MinFrameInterval < bestInterval; + + if( bestSizeSmallerThanDesired && currentSizeBiggerThanBest || + currentSizeBiggerThanOrEqualToDesired && currentSizeSmallerThanBest || + currentSizeEqualToBest && ( + bestRateSlowerThanDesired && currentRateFasterThanBestRate || + currentRateFasterThanOrEqualToDesired && currentRateSlowerThanBest) ) + { + bestWidth = scc.MaxOutputSize.cx; + bestHeight = scc.MaxOutputSize.cy; + bestInterval = scc.MinFrameInterval; + bestFormat = i; + } + } + } + + return true; +} + // Evaluate one of perhaps several native formats for // suitability for providing the nominal format. // Fill-in output parameter 'mediaFormat'. @@ -676,10 +916,8 @@ } // calculate range of supported frame rates - double fpsMin = static_cast<double>( 1000000000 / scc.MaxFrameInterval) - / 100.0; - double fpsMax = static_cast<double>( 1000000000 / scc.MinFrameInterval) - / 100.0; + double fpsMin = static_cast<double>(1000000000 / scc.MaxFrameInterval) / 100.0; + double fpsMax = static_cast<double>(1000000000 / scc.MinFrameInterval) / 100.0; double fps = static_cast<double>(fmtNominal->fps_numerator) / static_cast<double>(fmtNominal->fps_denominator); @@ -772,9 +1010,84 @@ STDMETHODIMP DirectShowSource::BufferCB( double dblSampleTime, BYTE * pBuff, long buffSize ) { + /* I can find nothing in the DirectShow documentation about scaling an image. Not */ + /* a thing -after hours of searching. I can only conclude that it isn't an exposed */ + /* API. It may not even exist. If the native format does not match the output */ + /* format, and we need to, scale the sample */ + if( !nativeMediaTypeSufficient_ ) + { + /* If we are here, the nativeMediaType used was not sufficient to get us to the */ + /* desired output to which we bound. So we asked DirectShow to give us RGB */ + /* samples at any size it could manage. So now we have an RGB sample buffer */ + /* described by fmtRealSample_ and we wish to convert it to a sample buffer */ + /* that matches fmtFauxSample_ (which is what we told the client we would give */ + /* it). Note one more twist to the story -DirectShow vertically flips the */ + /* sample buffer, so even if the frame size matches, we need to copy it anyway. */ + ScaleAndFlipImage( + pBuff, fmtRealSample_.width, fmtRealSample_.height, + buffer_, fmtFauxSample_.width, fmtFauxSample_.height ); + pBuff = buffer_; + buffSize = bufferSize_; + } + return bufferCB_(dblSampleTime, pBuff, buffSize, parent_); } +void DirectShowSource::ScaleAndFlipImage( + const BYTE * inBuff, int inWidth, int inHeight, + BYTE * outBuff, int outWidth, int outHeight ) +{ + int i; + int j; + + if( inBuff != NULL && outBuff != NULL ) + { + if( inWidth == outWidth && + inHeight == outHeight ) + { + /* Just vertically flip the data */ + for( i = 0; i < outHeight; i++ ) + { + memcpy( + outBuff + i * outWidth * 4, + inBuff + (inHeight - i - 1) * inWidth * 4, + outWidth * 4 ); + } + } + else + { + /* Nearest neighbor. Not awesome, but not bad */ + int heightTally = max( inHeight, outHeight ); + int srcRowIndex = 0; + for( i = 0; i < outHeight; i++ ) + { + while( heightTally < inHeight ) + { + heightTally += outHeight; + srcRowIndex++; + } + heightTally -= inHeight; + + int widthTally = max( inWidth, outWidth ); + int srcColIndex = 0; + for( j = 0; j < outWidth; j++ ) + { + while( widthTally < inWidth ) + { + widthTally += outWidth; + srcColIndex++; + } + widthTally -= inWidth; + + *(__int32 *)(outBuff + (i * outWidth + j) * 4) = + *(__int32 *)(inBuff + ((inHeight - srcRowIndex - 1) * + inWidth + srcColIndex) * 4); + } + } + } + } +} + int DirectShowSource::mapDirectShowMediaTypeToVidcapFourcc(DWORD data, int & fourcc) { Modified: trunk/src/directshow/DirectShowSource.h =================================================================== --- trunk/src/directshow/DirectShowSource.h 2010-02-01 19:00:34 UTC (rev 101) +++ trunk/src/directshow/DirectShowSource.h 2010-02-01 19:29:00 UTC (rev 102) @@ -48,7 +48,7 @@ void stop(); int bindFormat(const vidcap_fmt_info * fmtInfo); int validateFormat(const vidcap_fmt_info * fmtNominal, - vidcap_fmt_info * fmtNative) const; + vidcap_fmt_info * fmtNative, int forBinding) const; typedef void (*graphEventCBFunc)(void *); static void processGraphEvent(void *); @@ -70,10 +70,15 @@ AM_MEDIA_TYPE **candidateMediaFormat) const; bool findBestFormat(const vidcap_fmt_info * fmtNominal, vidcap_fmt_info * fmtNative, AM_MEDIA_TYPE **mediaFormat) const; + bool findUsableFormat( const vidcap_fmt_info *fmtNominal, + vidcap_fmt_info * fmtNative, AM_MEDIA_TYPE **mediaFormat, bool forSampling ) const; + bool findBestCapability( const vidcap_fmt_info *fmtNominal, int &bestFormat ) const; void freeMediaType(AM_MEDIA_TYPE &) const; bool getCaptureDevice(const char *devLongName, IBindCtx **ppBindCtx, IMoniker **ppMoniker) const; + void ScaleAndFlipImage( const BYTE * inBuff, int inWidth, int inHeight, + BYTE * outBuff, int outWidth, int outHeight ); // Fake out COM STDMETHODIMP_(ULONG) AddRef() { return 2; } @@ -110,6 +115,13 @@ AM_MEDIA_TYPE *nativeMediaType_; HANDLE *graphHandle_; bool graphIsSetup_; + + mutable bool nativeMediaTypeSufficient_; + mutable AM_MEDIA_TYPE outputMediaType_; + mutable BYTE * buffer_; + mutable long bufferSize_; + mutable vidcap_fmt_info fmtRealSample_; + mutable vidcap_fmt_info fmtFauxSample_; }; #endif Modified: trunk/src/directshow/SourceStateMachine.cpp =================================================================== --- trunk/src/directshow/SourceStateMachine.cpp 2010-02-01 19:00:34 UTC (rev 101) +++ trunk/src/directshow/SourceStateMachine.cpp 2010-02-01 19:29:00 UTC (rev 102) @@ -427,9 +427,9 @@ int SourceStateMachine::validateFormat(const vidcap_fmt_info * fmtNominal, - vidcap_fmt_info * fmtNative) const + vidcap_fmt_info * fmtNative, int forBinding) const { - return src_->validateFormat(fmtNominal, fmtNative); + return src_->validateFormat(fmtNominal, fmtNative, forBinding); } int Modified: trunk/src/directshow/SourceStateMachine.h =================================================================== --- trunk/src/directshow/SourceStateMachine.h 2010-02-01 19:00:34 UTC (rev 101) +++ trunk/src/directshow/SourceStateMachine.h 2010-02-01 19:29:00 UTC (rev 102) @@ -39,7 +39,7 @@ int stop(); int bindFormat(const vidcap_fmt_info * fmtInfo); int validateFormat(const vidcap_fmt_info * fmtNominal, - vidcap_fmt_info * fmtNative) const; + vidcap_fmt_info * fmtNative, int forBinding) const; const char * getID() const { Modified: trunk/src/sapi.c =================================================================== --- trunk/src/sapi.c 2010-02-01 19:00:34 UTC (rev 101) +++ trunk/src/sapi.c 2010-02-01 19:29:00 UTC (rev 102) @@ -131,6 +131,11 @@ int sapi_src_format_list_build(struct sapi_src_context * src_ctx) { +/* Go through the "hot list" and see which ones are natively supported by the device. */ +/* Note that QuickTime supports all formats within reason (it only checks for sensible */ +/* framerates and frame sizes). Windows will do the same -all formats within reason. */ +/* Don't know what Linux will do for now. */ + struct vidcap_fmt_info fmt_info; struct vidcap_fmt_info * list = 0; int list_len = 0; @@ -138,7 +143,7 @@ if ( src_ctx->fmt_list ) { - log_error("source alread has format list\n"); + log_error("source already has format list\n"); return -1; } @@ -162,7 +167,8 @@ if ( !src_ctx->format_validate(src_ctx, &fmt_info, - &fmt_native) ) + &fmt_native, + 0) ) continue; list = realloc(list, Modified: trunk/src/sapi_context.h =================================================================== --- trunk/src/sapi_context.h 2010-02-01 19:00:34 UTC (rev 101) +++ trunk/src/sapi_context.h 2010-02-01 19:29:00 UTC (rev 102) @@ -49,7 +49,7 @@ int (*format_validate)(struct sapi_src_context *, const struct vidcap_fmt_info * fmt_nominal, - struct vidcap_fmt_info * fmt_native); + struct vidcap_fmt_info * fmt_native, int forBinding); int (*format_bind)(struct sapi_src_context *, const struct vidcap_fmt_info *); Modified: trunk/src/sapi_dshow.cpp =================================================================== --- trunk/src/sapi_dshow.cpp 2010-02-01 19:00:34 UTC (rev 101) +++ trunk/src/sapi_dshow.cpp 2010-02-01 19:29:00 UTC (rev 102) @@ -40,12 +40,12 @@ static int source_format_validate(struct sapi_src_context * src_ctx, const struct vidcap_fmt_info * fmt_nominal, - struct vidcap_fmt_info * fmt_native) + struct vidcap_fmt_info * fmt_native, int forBinding) { SourceStateMachine * dshow_src = static_cast<SourceStateMachine *>(src_ctx->priv); - return dshow_src->validateFormat(fmt_nominal, fmt_native); + return dshow_src->validateFormat(fmt_nominal, fmt_native, forBinding); } static int Modified: trunk/src/sapi_qt.c =================================================================== --- trunk/src/sapi_qt.c 2010-02-01 19:00:34 UTC (rev 101) +++ trunk/src/sapi_qt.c 2010-02-01 19:29:00 UTC (rev 102) @@ -151,6 +151,28 @@ } static int +map_nominal_fourcc_to_native_fourcc(int nominal_fourcc) +{ +/* Convert the format we want to the format to ask for from the device. */ + + switch ( nominal_fourcc ) + { + case VIDCAP_FOURCC_RGB32: + case VIDCAP_FOURCC_YUY2: + case VIDCAP_FOURCC_2VUY: + return nominal_fourcc; + + case VIDCAP_FOURCC_RGB555: + case VIDCAP_FOURCC_RGB24: + case VIDCAP_FOURCC_YVU9: + case VIDCAP_FOURCC_I420: + default: + return VIDCAP_FOURCC_RGB32; + } +} + + +static int src_info_from_src(struct vidcap_src_info * src_info, const struct sg_source * src) { @@ -516,28 +538,61 @@ static int source_format_validate(struct sapi_src_context * src_ctx, const struct vidcap_fmt_info * fmt_nominal, - struct vidcap_fmt_info * fmt_native) + struct vidcap_fmt_info * fmt_native, int forBinding) { +/* This is called in two cases: */ +/* 1) when the "hot list" of supported formats is built, i.e. those that libvidcap */ +/* tells the client the digitizer can offer. */ +/* 2) when the actual desired format is bound. */ +/* forBinding indicates how this function is being used. If it is 0, we are */ +/* looking for formats that are natively, or very nearly natively supported by the */ +/* device, i.e. we are building the supported formats list. If it is 1, we are */ +/* attempting to bind to a format, i.e. a client is asking for a specific format. */ +/* In that case, we may relax the rules, and be more flexible about what can be */ +/* accepted. The quality in this latter case may suffer, but it will still work. */ +/* The function result is a bool. True on success. */ + struct sapi_qt_src_context * qt_src_ctx = (struct sapi_qt_src_context *)src_ctx->priv; + /* Set the native device format to the requested format */ *fmt_native = *fmt_nominal; - if ( map_ostype_to_fourcc(qt_src_ctx->src->native_pixel_format, - &fmt_native->fourcc) ) - /* We can always squeeze RGB32 out of quicktime */ + if( !forBinding ) + { + /* Convert the QuickTime constant representing the device native color space */ + /* into an equivalent libvidcap constant. If the function returns non-zero, */ + /* that means we don't directly support the device native color space, and the */ + /* device should be asked for RGB instead (via behind the scenes QuickTime */ + /* translation) */ + if ( map_ostype_to_fourcc( qt_src_ctx->src->native_pixel_format, &fmt_native->fourcc ) ) fmt_native->fourcc = VIDCAP_FOURCC_RGB32; + /* If building the format list, we want strict validation of nominal format */ if ( !sg_source_format_validate(qt_src_ctx->src, fmt_native->width, fmt_native->height, (float)fmt_native->fps_numerator / (float)fmt_native->fps_denominator) ) return 0; - if ( !sapi_can_convert_native_to_nominal(fmt_native, fmt_nominal) ) return 0; + } + else + { + /* If binding, pretty much any format will be fine */ + if( fmt_native->width < 16 || fmt_native->width > 65536 ) + return 0; + if( fmt_native->height < 16 || fmt_native->height > 65536 ) + return 0; + if( (float)fmt_native->fps_numerator / + (float)fmt_native->fps_denominator <= 0 ) + return 0; + /* Find out what we can best set the device to to get the desired color space */ + fmt_native->fourcc = map_nominal_fourcc_to_native_fourcc( fmt_nominal->fourcc ); + } + return 1; } Modified: trunk/src/sapi_v4l.c =================================================================== --- trunk/src/sapi_v4l.c 2010-02-01 19:00:34 UTC (rev 101) +++ trunk/src/sapi_v4l.c 2010-02-01 19:29:00 UTC (rev 102) @@ -658,7 +658,7 @@ static int source_format_validate(struct sapi_src_context * src_ctx, const struct vidcap_fmt_info * fmt_nominal, - struct vidcap_fmt_info * fmt_native) + struct vidcap_fmt_info * fmt_native, int forBinding ) { struct sapi_v4l_src_context * v4l_src_ctx = (struct sapi_v4l_src_context *)src_ctx->priv; Modified: trunk/src/vidcap.c =================================================================== --- trunk/src/vidcap.c 2010-02-01 19:00:34 UTC (rev 101) +++ trunk/src/vidcap.c 2010-02-01 19:29:00 UTC (rev 102) @@ -454,7 +454,7 @@ fmt_info = &src_ctx->fmt_list[0]; } - if ( !src_ctx->format_validate(src_ctx, fmt_info, &fmt_native) ) + if ( !src_ctx->format_validate(src_ctx, fmt_info, &fmt_native, 1) ) { log_error("format not supported by source: %dx%d %s %d/%d fps\n", fmt_info->width, fmt_info->height, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |