| /* | |
| * QuickTime Toolkit decoder filter for video | |
| * | |
| * Copyright 2010 Aric Stewart, CodeWeavers | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library 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 | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #include "config.h" | |
| #define ULONG CoreFoundation_ULONG | |
| #define HRESULT CoreFoundation_HRESULT | |
| #define LoadResource __carbon_LoadResource | |
| #define CompareString __carbon_CompareString | |
| #define GetCurrentThread __carbon_GetCurrentThread | |
| #define GetCurrentProcess __carbon_GetCurrentProcess | |
| #define AnimatePalette __carbon_AnimatePalette | |
| #define EqualRgn __carbon_EqualRgn | |
| #define FillRgn __carbon_FillRgn | |
| #define FrameRgn __carbon_FrameRgn | |
| #define GetPixel __carbon_GetPixel | |
| #define InvertRgn __carbon_InvertRgn | |
| #define LineTo __carbon_LineTo | |
| #define OffsetRgn __carbon_OffsetRgn | |
| #define PaintRgn __carbon_PaintRgn | |
| #define Polygon __carbon_Polygon | |
| #define ResizePalette __carbon_ResizePalette | |
| #define SetRectRgn __carbon_SetRectRgn | |
| #define CheckMenuItem __carbon_CheckMenuItem | |
| #define DeleteMenu __carbon_DeleteMenu | |
| #define DrawMenuBar __carbon_DrawMenuBar | |
| #define EnableMenuItem __carbon_EnableMenuItem | |
| #define EqualRect __carbon_EqualRect | |
| #define FillRect __carbon_FillRect | |
| #define FrameRect __carbon_FrameRect | |
| #define GetCursor __carbon_GetCursor | |
| #define GetMenu __carbon_GetMenu | |
| #define InvertRect __carbon_InvertRect | |
| #define IsWindowVisible __carbon_IsWindowVisible | |
| #define MoveWindow __carbon_MoveWindow | |
| #define OffsetRect __carbon_OffsetRect | |
| #define PtInRect __carbon_PtInRect | |
| #define SetCursor __carbon_SetCursor | |
| #define SetRect __carbon_SetRect | |
| #define ShowCursor __carbon_ShowCursor | |
| #define ShowWindow __carbon_ShowWindow | |
| #define UnionRect __carbon_UnionRect | |
| #include <QuickTime/ImageCompression.h> | |
| #include <CoreVideo/CVPixelBuffer.h> | |
| #undef LoadResource | |
| #undef CompareString | |
| #undef GetCurrentThread | |
| #undef _CDECL | |
| #undef DPRINTF | |
| #undef GetCurrentProcess | |
| #undef AnimatePalette | |
| #undef EqualRgn | |
| #undef FillRgn | |
| #undef FrameRgn | |
| #undef GetPixel | |
| #undef InvertRgn | |
| #undef LineTo | |
| #undef OffsetRgn | |
| #undef PaintRgn | |
| #undef Polygon | |
| #undef ResizePalette | |
| #undef SetRectRgn | |
| #undef CheckMenuItem | |
| #undef DeleteMenu | |
| #undef DrawMenuBar | |
| #undef EnableMenuItem | |
| #undef EqualRect | |
| #undef FillRect | |
| #undef FrameRect | |
| #undef GetCursor | |
| #undef GetMenu | |
| #undef InvertRect | |
| #undef IsWindowVisible | |
| #undef MoveWindow | |
| #undef OffsetRect | |
| #undef PtInRect | |
| #undef SetCursor | |
| #undef SetRect | |
| #undef ShowCursor | |
| #undef ShowWindow | |
| #undef UnionRect | |
| #undef ULONG | |
| #undef HRESULT | |
| #undef DPRINTF | |
| #undef STDMETHODCALLTYPE | |
| #define COBJMACROS | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "wtypes.h" | |
| #include "winuser.h" | |
| #include "dshow.h" | |
| #include "uuids.h" | |
| #include "amvideo.h" | |
| #include "strmif.h" | |
| #include "vfwmsgs.h" | |
| #include "vfw.h" | |
| #include "dvdmedia.h" | |
| #include <assert.h> | |
| #include "wine/unicode.h" | |
| #include "wine/debug.h" | |
| #include "wine/strmbase.h" | |
| #include "qtprivate.h" | |
| extern CLSID CLSID_QTVDecoder; | |
| WINE_DEFAULT_DEBUG_CHANNEL(qtdecoder); | |
| typedef struct QTVDecoderImpl | |
| { | |
| TransformFilter tf; | |
| ImageDescriptionHandle hImageDescription; | |
| CFMutableDictionaryRef outputBufferAttributes; | |
| ICMDecompressionSessionRef decompressionSession; | |
| HRESULT decodeHR; | |
| DWORD outputSize; | |
| } QTVDecoderImpl; | |
| static inline QTVDecoderImpl *impl_from_IBaseFilter( IBaseFilter *iface ) | |
| { | |
| return CONTAINING_RECORD(iface, QTVDecoderImpl, tf.filter.IBaseFilter_iface); | |
| } | |
| static inline QTVDecoderImpl *impl_from_TransformFilter( TransformFilter *iface ) | |
| { | |
| return CONTAINING_RECORD(iface, QTVDecoderImpl, tf.filter); | |
| } | |
| static const IBaseFilterVtbl QTVDecoder_Vtbl; | |
| static void trackingCallback( | |
| void *decompressionTrackingRefCon, | |
| OSStatus result, | |
| ICMDecompressionTrackingFlags decompressionTrackingFlags, | |
| CVPixelBufferRef pixelBuffer, | |
| TimeValue64 displayTime, | |
| TimeValue64 displayDuration, | |
| ICMValidTimeFlags validTimeFlags, | |
| void *reserved, | |
| void *sourceFrameRefCon ) | |
| { | |
| QTVDecoderImpl *This = (QTVDecoderImpl*)decompressionTrackingRefCon; | |
| IMediaSample *pSample = (IMediaSample*)sourceFrameRefCon; | |
| HRESULT hr = S_OK; | |
| IMediaSample* pOutSample = NULL; | |
| LPBYTE pbDstStream; | |
| DWORD cbDstStream; | |
| if (result != noErr) | |
| { | |
| ERR("Error from Codec, no frame decompressed\n"); | |
| return; | |
| } | |
| if (!pixelBuffer) | |
| { | |
| ERR("No pixel buffer, no frame decompressed\n"); | |
| return; | |
| } | |
| EnterCriticalSection(&This->tf.csReceive); | |
| hr = BaseOutputPinImpl_GetDeliveryBuffer((BaseOutputPin*)This->tf.ppPins[1], &pOutSample, NULL, NULL, 0); | |
| if (FAILED(hr)) { | |
| ERR("Unable to get delivery buffer (%x)\n", hr); | |
| goto error; | |
| } | |
| hr = IMediaSample_SetActualDataLength(pOutSample, 0); | |
| assert(hr == S_OK); | |
| hr = IMediaSample_GetPointer(pOutSample, &pbDstStream); | |
| if (FAILED(hr)) { | |
| ERR("Unable to get pointer to buffer (%x)\n", hr); | |
| goto error; | |
| } | |
| cbDstStream = IMediaSample_GetSize(pOutSample); | |
| if (cbDstStream < This->outputSize) { | |
| ERR("Sample size is too small %d < %d\n", cbDstStream, This->outputSize); | |
| hr = E_FAIL; | |
| goto error; | |
| } | |
| hr = AccessPixelBufferPixels(pixelBuffer, pbDstStream); | |
| if (FAILED(hr)) | |
| goto error; | |
| IMediaSample_SetActualDataLength(pOutSample, This->outputSize); | |
| IMediaSample_SetPreroll(pOutSample, (IMediaSample_IsPreroll(pSample) == S_OK)); | |
| IMediaSample_SetDiscontinuity(pOutSample, (IMediaSample_IsDiscontinuity(pSample) == S_OK)); | |
| IMediaSample_SetSyncPoint(pOutSample, (IMediaSample_IsSyncPoint(pSample) == S_OK)); | |
| if (!validTimeFlags) | |
| IMediaSample_SetTime(pOutSample, NULL, NULL); | |
| else | |
| { | |
| LONGLONG tStart, tStop; | |
| if (validTimeFlags & kICMValidTime_DisplayTimeStampIsValid) | |
| tStart = displayTime; | |
| else | |
| tStart = 0; | |
| if (validTimeFlags & kICMValidTime_DisplayDurationIsValid) | |
| tStop = tStart + displayDuration; | |
| else | |
| tStop = tStart; | |
| IMediaSample_SetTime(pOutSample, &tStart, &tStop); | |
| } | |
| LeaveCriticalSection(&This->tf.csReceive); | |
| hr = BaseOutputPinImpl_Deliver((BaseOutputPin*)This->tf.ppPins[1], pOutSample); | |
| EnterCriticalSection(&This->tf.csReceive); | |
| if (hr != S_OK && hr != VFW_E_NOT_CONNECTED) | |
| ERR("Error sending sample (%x)\n", hr); | |
| error: | |
| LeaveCriticalSection(&This->tf.csReceive); | |
| if (pOutSample) | |
| IMediaSample_Release(pOutSample); | |
| This->decodeHR = hr; | |
| } | |
| static HRESULT WINAPI QTVDecoder_StartStreaming(TransformFilter* pTransformFilter) | |
| { | |
| QTVDecoderImpl* This = impl_from_TransformFilter(pTransformFilter); | |
| OSErr err = noErr; | |
| ICMDecompressionSessionOptionsRef sessionOptions = NULL; | |
| ICMDecompressionTrackingCallbackRecord trackingCallbackRecord; | |
| TRACE("(%p)->()\n", This); | |
| trackingCallbackRecord.decompressionTrackingCallback = trackingCallback; | |
| trackingCallbackRecord.decompressionTrackingRefCon = (void*)This; | |
| err = ICMDecompressionSessionCreate(NULL, This->hImageDescription, sessionOptions, This->outputBufferAttributes, &trackingCallbackRecord, &This->decompressionSession); | |
| if (err != noErr) | |
| { | |
| ERR("Error with ICMDecompressionSessionCreate %i\n",err); | |
| return E_FAIL; | |
| } | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI QTVDecoder_Receive(TransformFilter *tf, IMediaSample *pSample) | |
| { | |
| QTVDecoderImpl* This = impl_from_TransformFilter(tf); | |
| HRESULT hr; | |
| DWORD cbSrcStream; | |
| LPBYTE pbSrcStream; | |
| ICMFrameTimeRecord frameTime = {{0}}; | |
| TimeValue time = 1; | |
| TimeScale timeScale = 1; | |
| OSStatus err = noErr; | |
| LONGLONG tStart, tStop; | |
| hr = IMediaSample_GetPointer(pSample, &pbSrcStream); | |
| if (FAILED(hr)) | |
| { | |
| ERR("Cannot get pointer to sample data (%x)\n", hr); | |
| goto error; | |
| } | |
| cbSrcStream = IMediaSample_GetActualDataLength(pSample); | |
| if (IMediaSample_GetTime(pSample, &tStart, &tStop) != S_OK) | |
| tStart = tStop = 0; | |
| time = tStart; | |
| frameTime.recordSize = sizeof(ICMFrameTimeRecord); | |
| *(TimeValue64 *)&frameTime.value = tStart; | |
| frameTime.scale = 1; | |
| frameTime.rate = fixed1; | |
| frameTime.duration = tStop - tStart; | |
| frameTime.frameNumber = 0; | |
| frameTime.flags = icmFrameTimeIsNonScheduledDisplayTime; | |
| err = ICMDecompressionSessionDecodeFrame(This->decompressionSession, | |
| (UInt8 *)pbSrcStream, cbSrcStream, NULL, &frameTime, pSample); | |
| if (err != noErr) | |
| { | |
| ERR("Error with ICMDecompressionSessionDecodeFrame\n"); | |
| hr = E_FAIL; | |
| goto error; | |
| } | |
| ICMDecompressionSessionSetNonScheduledDisplayTime(This->decompressionSession, time, timeScale, 0); | |
| ICMDecompressionSessionFlush(This->decompressionSession); | |
| hr = This->decodeHR; | |
| error: | |
| return hr; | |
| } | |
| static HRESULT WINAPI QTVDecoder_StopStreaming(TransformFilter* pTransformFilter) | |
| { | |
| QTVDecoderImpl* This = impl_from_TransformFilter(pTransformFilter); | |
| TRACE("(%p)->()\n", This); | |
| if (This->decompressionSession) | |
| ICMDecompressionSessionRelease(This->decompressionSession); | |
| This->decompressionSession = NULL; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI QTVDecoder_SetMediaType(TransformFilter *tf, PIN_DIRECTION dir, const AM_MEDIA_TYPE * pmt) | |
| { | |
| QTVDecoderImpl* This = impl_from_TransformFilter(tf); | |
| HRESULT hr = VFW_E_TYPE_NOT_ACCEPTED; | |
| OSErr err = noErr; | |
| AM_MEDIA_TYPE *outpmt = &This->tf.pmt; | |
| CFNumberRef n = NULL; | |
| TRACE("(%p)->(%p)\n", This, pmt); | |
| if (dir != PINDIR_INPUT) | |
| return S_OK; | |
| FreeMediaType(outpmt); | |
| CopyMediaType(outpmt, pmt); | |
| if (This->hImageDescription) | |
| DisposeHandle((Handle)This->hImageDescription); | |
| This->hImageDescription = (ImageDescriptionHandle) | |
| NewHandleClear(sizeof(ImageDescription)); | |
| if (This->hImageDescription != NULL) | |
| { | |
| (**This->hImageDescription).idSize = sizeof(ImageDescription); | |
| (**This->hImageDescription).spatialQuality = codecNormalQuality; | |
| (**This->hImageDescription).frameCount = 1; | |
| (**This->hImageDescription).clutID = -1; | |
| } | |
| else | |
| { | |
| ERR("Failed to create ImageDescription\n"); | |
| goto failed; | |
| } | |
| /* Check root (GUID w/o FOURCC) */ | |
| if ((IsEqualIID(&pmt->majortype, &MEDIATYPE_Video)) && | |
| (!memcmp(((const char *)&pmt->subtype)+4, ((const char *)&MEDIATYPE_Video)+4, sizeof(GUID)-4))) | |
| { | |
| VIDEOINFOHEADER *format1 = (VIDEOINFOHEADER *)outpmt->pbFormat; | |
| VIDEOINFOHEADER2 *format2 = (VIDEOINFOHEADER2 *)outpmt->pbFormat; | |
| BITMAPINFOHEADER *bmi; | |
| OSType fourCC; | |
| DecompressorComponent dc; | |
| OSType format; | |
| DWORD outputWidth, outputHeight, outputDepth; | |
| if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo)) | |
| bmi = &format1->bmiHeader; | |
| else if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo2)) | |
| bmi = &format2->bmiHeader; | |
| else | |
| goto failed; | |
| TRACE("Fourcc: %s\n", debugstr_an((const char *)&pmt->subtype.Data1, 4)); | |
| fourCC = ((const char *)&pmt->subtype.Data1)[3] | | |
| (((const char *)&pmt->subtype.Data1)[2]<<8) | | |
| (((const char *)&pmt->subtype.Data1)[1]<<16) | | |
| (((const char *)&pmt->subtype.Data1)[0]<<24); | |
| err = FindCodec(fourCC,NULL,NULL,&dc); | |
| if (err != noErr || dc == 0x0) | |
| { | |
| TRACE("Codec not found\n"); | |
| goto failed; | |
| } | |
| outputWidth = bmi->biWidth; | |
| outputHeight = bmi->biHeight; | |
| (**This->hImageDescription).cType = fourCC; | |
| (**This->hImageDescription).width = outputWidth; | |
| (**This->hImageDescription).height = outputHeight; | |
| (**This->hImageDescription).depth = bmi->biBitCount; | |
| (**This->hImageDescription).hRes = 72<<16; | |
| (**This->hImageDescription).vRes = 72<<16; | |
| if (This->outputBufferAttributes) | |
| CFRelease(This->outputBufferAttributes); | |
| This->outputBufferAttributes = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
| if (!This->outputBufferAttributes) | |
| { | |
| ERR("Failed to create outputBufferAttributes\n"); | |
| goto failed; | |
| } | |
| n = CFNumberCreate(NULL, kCFNumberIntType, &outputWidth); | |
| CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferWidthKey, n); | |
| CFRelease(n); | |
| n = CFNumberCreate(NULL, kCFNumberIntType, &outputHeight); | |
| CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferHeightKey, n); | |
| CFRelease(n); | |
| /* yes this looks wrong. but 32ARGB is 24 RGB with an alpha channel */ | |
| format = k32ARGBPixelFormat; | |
| n = CFNumberCreate(NULL, kCFNumberIntType, &format); | |
| CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferPixelFormatTypeKey, n); | |
| CFRelease(n); | |
| CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferCGBitmapContextCompatibilityKey, kCFBooleanTrue); | |
| CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferCGImageCompatibilityKey, kCFBooleanTrue); | |
| outputDepth = 3; | |
| This->outputSize = outputWidth * outputHeight * outputDepth; | |
| bmi->biCompression = BI_RGB; | |
| bmi->biBitCount = 24; | |
| outpmt->subtype = MEDIASUBTYPE_RGB24; | |
| return S_OK; | |
| } | |
| failed: | |
| if (This->hImageDescription) | |
| { | |
| DisposeHandle((Handle)This->hImageDescription); | |
| This->hImageDescription = NULL; | |
| } | |
| if (This->outputBufferAttributes) | |
| { | |
| CFRelease(This->outputBufferAttributes); | |
| This->outputBufferAttributes = NULL; | |
| } | |
| TRACE("Connection refused\n"); | |
| return hr; | |
| } | |
| static HRESULT WINAPI QTVDecoder_BreakConnect(TransformFilter *tf, PIN_DIRECTION dir) | |
| { | |
| QTVDecoderImpl *This = impl_from_TransformFilter(tf); | |
| TRACE("(%p)->()\n", This); | |
| if (This->hImageDescription) | |
| DisposeHandle((Handle)This->hImageDescription); | |
| if (This->outputBufferAttributes) | |
| CFRelease(This->outputBufferAttributes); | |
| This->hImageDescription = NULL; | |
| This->outputBufferAttributes = NULL; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI QTVDecoder_DecideBufferSize(TransformFilter *tf, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest) | |
| { | |
| QTVDecoderImpl *This = impl_from_TransformFilter(tf); | |
| ALLOCATOR_PROPERTIES actual; | |
| TRACE("()\n"); | |
| if (!ppropInputRequest->cbAlign) | |
| ppropInputRequest->cbAlign = 1; | |
| if (ppropInputRequest->cbBuffer < This->outputSize) | |
| ppropInputRequest->cbBuffer = This->outputSize + ppropInputRequest->cbAlign; | |
| if (!ppropInputRequest->cBuffers) | |
| ppropInputRequest->cBuffers = 1; | |
| return IMemAllocator_SetProperties(pAlloc, ppropInputRequest, &actual); | |
| } | |
| static const TransformFilterFuncTable QTVDecoder_FuncsTable = { | |
| QTVDecoder_DecideBufferSize, | |
| QTVDecoder_StartStreaming, | |
| QTVDecoder_Receive, | |
| QTVDecoder_StopStreaming, | |
| NULL, | |
| QTVDecoder_SetMediaType, | |
| NULL, | |
| QTVDecoder_BreakConnect, | |
| NULL, | |
| NULL, | |
| NULL, | |
| NULL | |
| }; | |
| IUnknown * CALLBACK QTVDecoder_create(IUnknown * pUnkOuter, HRESULT* phr) | |
| { | |
| HRESULT hr; | |
| QTVDecoderImpl * This; | |
| TRACE("(%p, %p)\n", pUnkOuter, phr); | |
| *phr = S_OK; | |
| if (pUnkOuter) | |
| { | |
| *phr = CLASS_E_NOAGGREGATION; | |
| return NULL; | |
| } | |
| hr = TransformFilter_Construct(&QTVDecoder_Vtbl, sizeof(QTVDecoderImpl), &CLSID_QTVDecoder, &QTVDecoder_FuncsTable, (IBaseFilter**)&This); | |
| if (FAILED(hr)) | |
| { | |
| *phr = hr; | |
| return NULL; | |
| } | |
| *phr = hr; | |
| return (IUnknown*)This; | |
| } | |
| static const IBaseFilterVtbl QTVDecoder_Vtbl = | |
| { | |
| TransformFilterImpl_QueryInterface, | |
| BaseFilterImpl_AddRef, | |
| TransformFilterImpl_Release, | |
| BaseFilterImpl_GetClassID, | |
| TransformFilterImpl_Stop, | |
| TransformFilterImpl_Pause, | |
| TransformFilterImpl_Run, | |
| BaseFilterImpl_GetState, | |
| BaseFilterImpl_SetSyncSource, | |
| BaseFilterImpl_GetSyncSource, | |
| BaseFilterImpl_EnumPins, | |
| TransformFilterImpl_FindPin, | |
| BaseFilterImpl_QueryFilterInfo, | |
| BaseFilterImpl_JoinFilterGraph, | |
| BaseFilterImpl_QueryVendorInfo | |
| }; |