2 * QuickTime Toolkit decoder filter for video
4 * Copyright 2010 Aric Stewart, CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #define ULONG CoreFoundation_ULONG
24 #define HRESULT CoreFoundation_HRESULT
26 #define LoadResource __carbon_LoadResource
27 #define CompareString __carbon_CompareString
28 #define GetCurrentThread __carbon_GetCurrentThread
29 #define GetCurrentProcess __carbon_GetCurrentProcess
30 #define AnimatePalette __carbon_AnimatePalette
31 #define EqualRgn __carbon_EqualRgn
32 #define FillRgn __carbon_FillRgn
33 #define FrameRgn __carbon_FrameRgn
34 #define GetPixel __carbon_GetPixel
35 #define InvertRgn __carbon_InvertRgn
36 #define LineTo __carbon_LineTo
37 #define OffsetRgn __carbon_OffsetRgn
38 #define PaintRgn __carbon_PaintRgn
39 #define Polygon __carbon_Polygon
40 #define ResizePalette __carbon_ResizePalette
41 #define SetRectRgn __carbon_SetRectRgn
43 #define CheckMenuItem __carbon_CheckMenuItem
44 #define DeleteMenu __carbon_DeleteMenu
45 #define DrawMenuBar __carbon_DrawMenuBar
46 #define EnableMenuItem __carbon_EnableMenuItem
47 #define EqualRect __carbon_EqualRect
48 #define FillRect __carbon_FillRect
49 #define FrameRect __carbon_FrameRect
50 #define GetCursor __carbon_GetCursor
51 #define GetMenu __carbon_GetMenu
52 #define InvertRect __carbon_InvertRect
53 #define IsWindowVisible __carbon_IsWindowVisible
54 #define MoveWindow __carbon_MoveWindow
55 #define OffsetRect __carbon_OffsetRect
56 #define PtInRect __carbon_PtInRect
57 #define SetCursor __carbon_SetCursor
58 #define SetRect __carbon_SetRect
59 #define ShowCursor __carbon_ShowCursor
60 #define ShowWindow __carbon_ShowWindow
61 #define UnionRect __carbon_UnionRect
63 #include <QuickTime/ImageCompression.h>
64 #include <CoreVideo/CVPixelBuffer.h>
68 #undef GetCurrentThread
71 #undef GetCurrentProcess
94 #undef IsWindowVisible
107 #undef STDMETHODCALLTYPE
122 #include "dvdmedia.h"
126 #include "wine/unicode.h"
127 #include "wine/debug.h"
128 #include "wine/strmbase.h"
130 extern CLSID CLSID_QTVDecoder;
132 WINE_DEFAULT_DEBUG_CHANNEL(qtdecoder);
134 typedef struct QTVDecoderImpl
137 IUnknown *seekthru_unk;
139 ImageDescriptionHandle hImageDescription;
140 CFMutableDictionaryRef outputBufferAttributes;
141 ICMDecompressionSessionRef decompressionSession;
145 DWORD outputWidth, outputHeight, outputDepth;
150 UInt8 a; /* Alpha Channel */
151 UInt8 r; /* red component */
152 UInt8 g; /* green component */
153 UInt8 b; /* blue component */
154 } ARGBPixelRecord, *ARGBPixelPtr, **ARGBPixelHdl;
156 static const IBaseFilterVtbl QTVDecoder_Vtbl;
158 static void trackingCallback(
159 void *decompressionTrackingRefCon,
161 ICMDecompressionTrackingFlags decompressionTrackingFlags,
162 CVPixelBufferRef pixelBuffer,
163 TimeValue64 displayTime,
164 TimeValue64 displayDuration,
165 ICMValidTimeFlags validTimeFlags,
167 void *sourceFrameRefCon )
169 QTVDecoderImpl *This = (QTVDecoderImpl*)decompressionTrackingRefCon;
170 IMediaSample *pSample = (IMediaSample*)sourceFrameRefCon;
172 IMediaSample* pOutSample = NULL;
175 LPBYTE pPixels = NULL;
177 size_t bytesPerRow = 0;
183 ERR("Error from Codec, no frame decompressed\n");
189 ERR("No pixel buffer, no frame decompressed\n");
193 EnterCriticalSection(&This->tf.filter.csFilter);
194 hr = BaseOutputPinImpl_GetDeliveryBuffer((BaseOutputPin*)This->tf.ppPins[1], &pOutSample, NULL, NULL, 0);
196 ERR("Unable to get delivery buffer (%x)\n", hr);
200 hr = IMediaSample_SetActualDataLength(pOutSample, 0);
203 hr = IMediaSample_GetPointer(pOutSample, &pbDstStream);
205 ERR("Unable to get pointer to buffer (%x)\n", hr);
209 cbDstStream = IMediaSample_GetSize(pOutSample);
210 if (cbDstStream < This->outputSize) {
211 ERR("Sample size is too small %d < %d\n", cbDstStream, This->outputSize);
216 /* ACCESS THE PIXELS */
217 CVPixelBufferLockBaseAddress(pixelBuffer,0);
218 pPixels = (LPBYTE)CVPixelBufferGetBaseAddress(pixelBuffer);
219 bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer);
221 for (out = pbDstStream, i = 0; i < This->outputHeight; i++)
224 for (j = 0; j < This->outputWidth; j++)
226 *((DWORD*)out) = (((ARGBPixelPtr)pPixels)[j].r) << 16
227 | (((ARGBPixelPtr)pPixels)[j].g) << 8
228 | (((ARGBPixelPtr)pPixels)[j].b);
229 out+=This->outputDepth;
231 pPixels += bytesPerRow;
233 CVPixelBufferUnlockBaseAddress(pixelBuffer,0);
235 IMediaSample_SetActualDataLength(pOutSample, This->outputSize);
237 IMediaSample_SetPreroll(pOutSample, (IMediaSample_IsPreroll(pSample) == S_OK));
238 IMediaSample_SetDiscontinuity(pOutSample, (IMediaSample_IsDiscontinuity(pSample) == S_OK));
239 IMediaSample_SetSyncPoint(pOutSample, (IMediaSample_IsSyncPoint(pSample) == S_OK));
242 IMediaSample_SetTime(pOutSample, NULL, NULL);
245 LONGLONG tStart, tStop;
247 if (validTimeFlags & kICMValidTime_DisplayTimeStampIsValid)
248 tStart = displayTime;
251 if (validTimeFlags & kICMValidTime_DisplayDurationIsValid)
252 tStop = tStart + displayDuration;
256 IMediaSample_SetTime(pOutSample, &tStart, &tStop);
259 LeaveCriticalSection(&This->tf.filter.csFilter);
260 hr = BaseOutputPinImpl_Deliver((BaseOutputPin*)This->tf.ppPins[1], pOutSample);
261 if (hr != S_OK && hr != VFW_E_NOT_CONNECTED)
262 ERR("Error sending sample (%x)\n", hr);
266 IMediaSample_Release(pOutSample);
271 static HRESULT WINAPI QTVDecoder_StartStreaming(TransformFilter* pTransformFilter)
273 QTVDecoderImpl* This = (QTVDecoderImpl*)pTransformFilter;
275 ICMDecompressionSessionOptionsRef sessionOptions = NULL;
276 ICMDecompressionTrackingCallbackRecord trackingCallbackRecord;
278 TRACE("(%p)->()\n", This);
280 trackingCallbackRecord.decompressionTrackingCallback = trackingCallback;
281 trackingCallbackRecord.decompressionTrackingRefCon = (void*)This;
283 err = ICMDecompressionSessionCreate(NULL, This->hImageDescription, sessionOptions, This->outputBufferAttributes, &trackingCallbackRecord, &This->decompressionSession);
287 ERR("Error with ICMDecompressionSessionCreate %i\n",err);
294 static HRESULT WINAPI QTVDecoder_Receive(TransformFilter *tf, IMediaSample *pSample)
296 QTVDecoderImpl* This = (QTVDecoderImpl *)tf;
301 ICMFrameTimeRecord frameTime = {{0}};
303 TimeScale timeScale = 1;
304 OSStatus err = noErr;
305 LONGLONG tStart, tStop;
307 EnterCriticalSection(&This->tf.filter.csFilter);
308 hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
311 ERR("Cannot get pointer to sample data (%x)\n", hr);
315 cbSrcStream = IMediaSample_GetActualDataLength(pSample);
317 if (IMediaSample_GetTime(pSample, &tStart, &tStop) != S_OK)
322 frameTime.recordSize = sizeof(ICMFrameTimeRecord);
323 *(TimeValue64 *)&frameTime.value = tStart;
325 frameTime.rate = fixed1;
326 frameTime.duration = tStop - tStart;
327 frameTime.frameNumber = 0;
328 frameTime.flags = icmFrameTimeIsNonScheduledDisplayTime;
330 err = ICMDecompressionSessionDecodeFrame(This->decompressionSession,
331 (UInt8 *)pbSrcStream, cbSrcStream, NULL, &frameTime, pSample);
335 ERR("Error with ICMDecompressionSessionDecodeFrame\n");
340 ICMDecompressionSessionSetNonScheduledDisplayTime(This->decompressionSession, time, timeScale, 0);
341 ICMDecompressionSessionFlush(This->decompressionSession);
345 LeaveCriticalSection(&This->tf.filter.csFilter);
349 static HRESULT WINAPI QTVDecoder_StopStreaming(TransformFilter* pTransformFilter)
351 QTVDecoderImpl* This = (QTVDecoderImpl*)pTransformFilter;
353 TRACE("(%p)->()\n", This);
355 if (This->decompressionSession)
356 ICMDecompressionSessionRelease(This->decompressionSession);
357 This->decompressionSession = NULL;
362 static HRESULT WINAPI QTVDecoder_SetMediaType(TransformFilter *tf, PIN_DIRECTION dir, const AM_MEDIA_TYPE * pmt)
364 QTVDecoderImpl* This = (QTVDecoderImpl*)tf;
365 HRESULT hr = VFW_E_TYPE_NOT_ACCEPTED;
367 AM_MEDIA_TYPE *outpmt = &This->tf.pmt;
368 CFNumberRef n = NULL;
370 TRACE("(%p)->(%p)\n", This, pmt);
372 if (dir != PINDIR_INPUT)
375 FreeMediaType(outpmt);
376 CopyMediaType(outpmt, pmt);
378 if (This->hImageDescription)
379 DisposeHandle((Handle)This->hImageDescription);
381 This->hImageDescription = (ImageDescriptionHandle)
382 NewHandleClear(sizeof(ImageDescription));
384 if (This->hImageDescription != NULL)
386 (**This->hImageDescription).idSize = sizeof(ImageDescription);
387 (**This->hImageDescription).spatialQuality = codecNormalQuality;
388 (**This->hImageDescription).frameCount = 1;
389 (**This->hImageDescription).clutID = -1;
393 ERR("Failed to create ImageDescription\n");
397 /* Check root (GUID w/o FOURCC) */
398 if ((IsEqualIID(&pmt->majortype, &MEDIATYPE_Video)) &&
399 (!memcmp(((const char *)&pmt->subtype)+4, ((const char *)&MEDIATYPE_Video)+4, sizeof(GUID)-4)))
401 VIDEOINFOHEADER *format1 = (VIDEOINFOHEADER *)outpmt->pbFormat;
402 VIDEOINFOHEADER2 *format2 = (VIDEOINFOHEADER2 *)outpmt->pbFormat;
403 BITMAPINFOHEADER *bmi;
405 DecompressorComponent dc;
408 if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo))
409 bmi = &format1->bmiHeader;
410 else if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo2))
411 bmi = &format2->bmiHeader;
415 TRACE("Fourcc: %s\n", debugstr_an((const char *)&pmt->subtype.Data1, 4));
416 fourCC = ((const char *)&pmt->subtype.Data1)[0] |
417 (((const char *)&pmt->subtype.Data1)[1]<<8) |
418 (((const char *)&pmt->subtype.Data1)[2]<<16) |
419 (((const char *)&pmt->subtype.Data1)[3]<<24);
421 err = FindCodec(fourCC,NULL,NULL,&dc);
422 if (err != noErr || dc == 0x0)
424 TRACE("Codec not found\n");
428 This->outputWidth = bmi->biWidth;
429 This->outputHeight = bmi->biHeight;
431 (**This->hImageDescription).cType = fourCC;
432 (**This->hImageDescription).width = This->outputWidth;
433 (**This->hImageDescription).height = This->outputHeight;
434 (**This->hImageDescription).depth = bmi->biBitCount;
435 (**This->hImageDescription).hRes = 72<<16;
436 (**This->hImageDescription).vRes = 72<<16;
438 if (This->outputBufferAttributes)
439 CFRelease(This->outputBufferAttributes);
441 This->outputBufferAttributes = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
442 if (!This->outputBufferAttributes)
444 ERR("Failed to create outputBufferAttributes\n");
448 n = CFNumberCreate(NULL, kCFNumberIntType, &This->outputWidth);
449 CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferWidthKey, n);
452 n = CFNumberCreate(NULL, kCFNumberIntType, &This->outputHeight);
453 CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferHeightKey, n);
456 /* yes this looks wrong. but 32ARGB is 24 RGB with an alpha channel */
457 format = k32ARGBPixelFormat;
458 n = CFNumberCreate(NULL, kCFNumberIntType, &format);
459 CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferPixelFormatTypeKey, n);
462 CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferCGBitmapContextCompatibilityKey, kCFBooleanTrue);
463 CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferCGImageCompatibilityKey, kCFBooleanTrue);
465 This->outputDepth = 3;
466 This->outputSize = This->outputWidth * This->outputHeight * This->outputDepth;
467 bmi->biCompression = BI_RGB;
468 bmi->biBitCount = 24;
469 outpmt->subtype = MEDIASUBTYPE_RGB24;
470 if (bmi->biHeight > 0)
471 bmi->biHeight = -bmi->biHeight;
477 if (This->hImageDescription)
479 DisposeHandle((Handle)This->hImageDescription);
480 This->hImageDescription = NULL;
482 if (This->outputBufferAttributes)
484 CFRelease(This->outputBufferAttributes);
485 This->outputBufferAttributes = NULL;
488 TRACE("Connection refused\n");
492 static HRESULT WINAPI QTVDecoder_BreakConnect(TransformFilter *tf, PIN_DIRECTION dir)
494 QTVDecoderImpl *This = (QTVDecoderImpl *)tf;
496 TRACE("(%p)->()\n", This);
498 if (This->hImageDescription)
499 DisposeHandle((Handle)This->hImageDescription);
500 if (This->outputBufferAttributes)
501 CFRelease(This->outputBufferAttributes);
503 This->hImageDescription = NULL;
504 This->outputBufferAttributes = NULL;
509 static HRESULT WINAPI QTVDecoder_DecideBufferSize(TransformFilter *tf, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
511 QTVDecoderImpl *This = (QTVDecoderImpl*)tf;
512 ALLOCATOR_PROPERTIES actual;
516 if (!ppropInputRequest->cbAlign)
517 ppropInputRequest->cbAlign = 1;
519 if (ppropInputRequest->cbBuffer < This->outputSize)
520 ppropInputRequest->cbBuffer = This->outputSize;
522 if (!ppropInputRequest->cBuffers)
523 ppropInputRequest->cBuffers = 1;
525 return IMemAllocator_SetProperties(pAlloc, ppropInputRequest, &actual);
528 static const TransformFilterFuncTable QTVDecoder_FuncsTable = {
529 QTVDecoder_DecideBufferSize,
530 QTVDecoder_StartStreaming,
532 QTVDecoder_StopStreaming,
534 QTVDecoder_SetMediaType,
536 QTVDecoder_BreakConnect,
543 IUnknown * CALLBACK QTVDecoder_create(IUnknown * pUnkOuter, HRESULT* phr)
546 QTVDecoderImpl * This;
548 TRACE("(%p, %p)\n", pUnkOuter, phr);
554 *phr = CLASS_E_NOAGGREGATION;
558 hr = TransformFilter_Construct(&QTVDecoder_Vtbl, sizeof(QTVDecoderImpl), &CLSID_QTVDecoder, &QTVDecoder_FuncsTable, (IBaseFilter**)&This);
567 ISeekingPassThru *passthru;
568 hr = CoCreateInstance(&CLSID_SeekingPassThru, (IUnknown*)This, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void**)&This->seekthru_unk);
569 IUnknown_QueryInterface(This->seekthru_unk, &IID_ISeekingPassThru, (void**)&passthru);
570 ISeekingPassThru_Init(passthru, FALSE, (IPin*)This->tf.ppPins[0]);
571 ISeekingPassThru_Release(passthru);
575 return (IUnknown*)This;
578 HRESULT WINAPI QTVDecoder_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
581 QTVDecoderImpl *This = (QTVDecoderImpl *)iface;
582 TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
584 if (IsEqualIID(riid, &IID_IMediaSeeking))
585 return IUnknown_QueryInterface(This->seekthru_unk, riid, ppv);
587 hr = TransformFilterImpl_QueryInterface(iface, riid, ppv);
592 static const IBaseFilterVtbl QTVDecoder_Vtbl =
594 QTVDecoder_QueryInterface,
595 BaseFilterImpl_AddRef,
596 TransformFilterImpl_Release,
597 BaseFilterImpl_GetClassID,
598 TransformFilterImpl_Stop,
599 TransformFilterImpl_Pause,
600 TransformFilterImpl_Run,
601 BaseFilterImpl_GetState,
602 BaseFilterImpl_SetSyncSource,
603 BaseFilterImpl_GetSyncSource,
604 BaseFilterImpl_EnumPins,
605 TransformFilterImpl_FindPin,
606 BaseFilterImpl_QueryFilterInfo,
607 BaseFilterImpl_JoinFilterGraph,
608 BaseFilterImpl_QueryVendorInfo