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