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
70 #undef GetCurrentProcess
93 #undef IsWindowVisible
105 #undef STDMETHODCALLTYPE
120 #include "dvdmedia.h"
124 #include "wine/unicode.h"
125 #include "wine/debug.h"
126 #include "wine/strmbase.h"
128 #include "qtprivate.h"
129 #include "wineqtdecoder_classes.h"
131 WINE_DEFAULT_DEBUG_CHANNEL(qtdecoder
);
133 typedef struct QTVDecoderImpl
135 struct strmbase_filter filter
;
136 CRITICAL_SECTION stream_cs
;
140 struct strmbase_source source
;
143 struct strmbase_sink sink
;
145 ImageDescriptionHandle hImageDescription
;
146 CFMutableDictionaryRef outputBufferAttributes
;
147 ICMDecompressionSessionRef decompressionSession
;
153 static inline QTVDecoderImpl
*impl_from_strmbase_filter(struct strmbase_filter
*iface
)
155 return CONTAINING_RECORD(iface
, QTVDecoderImpl
, filter
);
158 static HRESULT
video_decoder_sink_query_interface(struct strmbase_pin
*iface
, REFIID iid
, void **out
)
160 QTVDecoderImpl
*filter
= impl_from_strmbase_filter(iface
->filter
);
162 if (IsEqualGUID(iid
, &IID_IMemInputPin
))
163 *out
= &filter
->sink
.IMemInputPin_iface
;
165 return E_NOINTERFACE
;
167 IUnknown_AddRef((IUnknown
*)*out
);
171 static void trackingCallback(
172 void *decompressionTrackingRefCon
,
174 ICMDecompressionTrackingFlags decompressionTrackingFlags
,
175 CVPixelBufferRef pixelBuffer
,
176 TimeValue64 displayTime
,
177 TimeValue64 displayDuration
,
178 ICMValidTimeFlags validTimeFlags
,
180 void *sourceFrameRefCon
)
182 QTVDecoderImpl
*This
= (QTVDecoderImpl
*)decompressionTrackingRefCon
;
183 IMediaSample
*pSample
= (IMediaSample
*)sourceFrameRefCon
;
185 IMediaSample
* pOutSample
= NULL
;
191 ERR("Error from Codec, no frame decompressed\n");
197 ERR("No pixel buffer, no frame decompressed\n");
201 EnterCriticalSection(&This
->stream_cs
);
202 hr
= BaseOutputPinImpl_GetDeliveryBuffer(&This
->source
, &pOutSample
, NULL
, NULL
, 0);
204 ERR("Unable to get delivery buffer (%x)\n", hr
);
208 hr
= IMediaSample_SetActualDataLength(pOutSample
, 0);
211 hr
= IMediaSample_GetPointer(pOutSample
, &pbDstStream
);
213 ERR("Unable to get pointer to buffer (%x)\n", hr
);
217 cbDstStream
= IMediaSample_GetSize(pOutSample
);
218 if (cbDstStream
< This
->outputSize
) {
219 ERR("Sample size is too small %d < %d\n", cbDstStream
, This
->outputSize
);
224 hr
= AccessPixelBufferPixels(pixelBuffer
, pbDstStream
);
228 IMediaSample_SetActualDataLength(pOutSample
, This
->outputSize
);
230 IMediaSample_SetPreroll(pOutSample
, (IMediaSample_IsPreroll(pSample
) == S_OK
));
231 IMediaSample_SetDiscontinuity(pOutSample
, (IMediaSample_IsDiscontinuity(pSample
) == S_OK
));
232 IMediaSample_SetSyncPoint(pOutSample
, (IMediaSample_IsSyncPoint(pSample
) == S_OK
));
235 IMediaSample_SetTime(pOutSample
, NULL
, NULL
);
238 LONGLONG tStart
, tStop
;
240 if (validTimeFlags
& kICMValidTime_DisplayTimeStampIsValid
)
241 tStart
= displayTime
;
244 if (validTimeFlags
& kICMValidTime_DisplayDurationIsValid
)
245 tStop
= tStart
+ displayDuration
;
249 IMediaSample_SetTime(pOutSample
, &tStart
, &tStop
);
252 hr
= IMemInputPin_Receive(This
->source
.pMemInputPin
, pOutSample
);
253 if (hr
!= S_OK
&& hr
!= VFW_E_NOT_CONNECTED
)
254 ERR("Error sending sample (%x)\n", hr
);
257 LeaveCriticalSection(&This
->stream_cs
);
259 IMediaSample_Release(pOutSample
);
264 static HRESULT WINAPI
video_decoder_sink_Receive(struct strmbase_sink
*iface
, IMediaSample
*pSample
)
266 QTVDecoderImpl
*This
= impl_from_strmbase_filter(iface
->pin
.filter
);
271 ICMFrameTimeRecord frameTime
= {{0}};
273 TimeScale timeScale
= 1;
274 OSStatus err
= noErr
;
275 LONGLONG tStart
, tStop
;
277 EnterCriticalSection(&This
->stream_cs
);
279 hr
= IMediaSample_GetPointer(pSample
, &pbSrcStream
);
282 ERR("Cannot get pointer to sample data (%x)\n", hr
);
286 cbSrcStream
= IMediaSample_GetActualDataLength(pSample
);
288 if (IMediaSample_GetTime(pSample
, &tStart
, &tStop
) != S_OK
)
293 frameTime
.recordSize
= sizeof(ICMFrameTimeRecord
);
294 *(TimeValue64
*)&frameTime
.value
= tStart
;
296 frameTime
.rate
= fixed1
;
297 frameTime
.duration
= tStop
- tStart
;
298 frameTime
.frameNumber
= 0;
299 frameTime
.flags
= icmFrameTimeIsNonScheduledDisplayTime
;
301 err
= ICMDecompressionSessionDecodeFrame(This
->decompressionSession
,
302 (UInt8
*)pbSrcStream
, cbSrcStream
, NULL
, &frameTime
, pSample
);
306 ERR("Error with ICMDecompressionSessionDecodeFrame\n");
311 ICMDecompressionSessionSetNonScheduledDisplayTime(This
->decompressionSession
, time
, timeScale
, 0);
312 ICMDecompressionSessionFlush(This
->decompressionSession
);
316 LeaveCriticalSection(&This
->stream_cs
);
320 static HRESULT
video_decoder_sink_connect(struct strmbase_sink
*iface
, IPin
*peer
, const AM_MEDIA_TYPE
*pmt
)
322 QTVDecoderImpl
*This
= impl_from_strmbase_filter(iface
->pin
.filter
);
323 HRESULT hr
= VFW_E_TYPE_NOT_ACCEPTED
;
325 AM_MEDIA_TYPE
*outpmt
= &This
->mt
;
326 CFNumberRef n
= NULL
;
328 FreeMediaType(outpmt
);
329 CopyMediaType(outpmt
, pmt
);
331 if (This
->hImageDescription
)
332 DisposeHandle((Handle
)This
->hImageDescription
);
334 This
->hImageDescription
= (ImageDescriptionHandle
)
335 NewHandleClear(sizeof(ImageDescription
));
337 if (This
->hImageDescription
!= NULL
)
339 (**This
->hImageDescription
).idSize
= sizeof(ImageDescription
);
340 (**This
->hImageDescription
).spatialQuality
= codecNormalQuality
;
341 (**This
->hImageDescription
).frameCount
= 1;
342 (**This
->hImageDescription
).clutID
= -1;
346 ERR("Failed to create ImageDescription\n");
350 /* Check root (GUID w/o FOURCC) */
351 if ((IsEqualIID(&pmt
->majortype
, &MEDIATYPE_Video
)) &&
352 (!memcmp(((const char *)&pmt
->subtype
)+4, ((const char *)&MEDIATYPE_Video
)+4, sizeof(GUID
)-4)))
354 VIDEOINFOHEADER
*format1
= (VIDEOINFOHEADER
*)outpmt
->pbFormat
;
355 VIDEOINFOHEADER2
*format2
= (VIDEOINFOHEADER2
*)outpmt
->pbFormat
;
356 BITMAPINFOHEADER
*bmi
;
358 DecompressorComponent dc
;
360 DWORD outputWidth
, outputHeight
, outputDepth
;
362 if (IsEqualIID(&pmt
->formattype
, &FORMAT_VideoInfo
))
363 bmi
= &format1
->bmiHeader
;
364 else if (IsEqualIID(&pmt
->formattype
, &FORMAT_VideoInfo2
))
365 bmi
= &format2
->bmiHeader
;
369 TRACE("Fourcc: %s\n", debugstr_an((const char *)&pmt
->subtype
.Data1
, 4));
370 fourCC
= ((const char *)&pmt
->subtype
.Data1
)[3] |
371 (((const char *)&pmt
->subtype
.Data1
)[2]<<8) |
372 (((const char *)&pmt
->subtype
.Data1
)[1]<<16) |
373 (((const char *)&pmt
->subtype
.Data1
)[0]<<24);
375 err
= FindCodec(fourCC
,NULL
,NULL
,&dc
);
376 if (err
!= noErr
|| dc
== 0x0)
378 TRACE("Codec not found\n");
382 outputWidth
= bmi
->biWidth
;
383 outputHeight
= bmi
->biHeight
;
385 (**This
->hImageDescription
).cType
= fourCC
;
386 (**This
->hImageDescription
).width
= outputWidth
;
387 (**This
->hImageDescription
).height
= outputHeight
;
388 (**This
->hImageDescription
).depth
= bmi
->biBitCount
;
389 (**This
->hImageDescription
).hRes
= 72<<16;
390 (**This
->hImageDescription
).vRes
= 72<<16;
392 if (This
->outputBufferAttributes
)
393 CFRelease(This
->outputBufferAttributes
);
395 This
->outputBufferAttributes
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
396 if (!This
->outputBufferAttributes
)
398 ERR("Failed to create outputBufferAttributes\n");
402 n
= CFNumberCreate(NULL
, kCFNumberIntType
, &outputWidth
);
403 CFDictionaryAddValue(This
->outputBufferAttributes
, kCVPixelBufferWidthKey
, n
);
406 n
= CFNumberCreate(NULL
, kCFNumberIntType
, &outputHeight
);
407 CFDictionaryAddValue(This
->outputBufferAttributes
, kCVPixelBufferHeightKey
, n
);
410 /* yes this looks wrong. but 32ARGB is 24 RGB with an alpha channel */
411 format
= k32ARGBPixelFormat
;
412 n
= CFNumberCreate(NULL
, kCFNumberIntType
, &format
);
413 CFDictionaryAddValue(This
->outputBufferAttributes
, kCVPixelBufferPixelFormatTypeKey
, n
);
416 CFDictionaryAddValue(This
->outputBufferAttributes
, kCVPixelBufferCGBitmapContextCompatibilityKey
, kCFBooleanTrue
);
417 CFDictionaryAddValue(This
->outputBufferAttributes
, kCVPixelBufferCGImageCompatibilityKey
, kCFBooleanTrue
);
420 This
->outputSize
= outputWidth
* outputHeight
* outputDepth
;
421 bmi
->biCompression
= BI_RGB
;
422 bmi
->biBitCount
= 24;
423 outpmt
->subtype
= MEDIASUBTYPE_RGB24
;
429 if (This
->hImageDescription
)
431 DisposeHandle((Handle
)This
->hImageDescription
);
432 This
->hImageDescription
= NULL
;
434 if (This
->outputBufferAttributes
)
436 CFRelease(This
->outputBufferAttributes
);
437 This
->outputBufferAttributes
= NULL
;
440 TRACE("Connection refused\n");
444 static void video_decoder_sink_disconnect(struct strmbase_sink
*iface
)
446 QTVDecoderImpl
*This
= impl_from_strmbase_filter(iface
->pin
.filter
);
448 if (This
->hImageDescription
)
449 DisposeHandle((Handle
)This
->hImageDescription
);
450 if (This
->outputBufferAttributes
)
451 CFRelease(This
->outputBufferAttributes
);
453 This
->hImageDescription
= NULL
;
454 This
->outputBufferAttributes
= NULL
;
457 static const struct strmbase_sink_ops sink_ops
=
459 .base
.pin_query_interface
= video_decoder_sink_query_interface
,
460 .pfnReceive
= video_decoder_sink_Receive
,
461 .sink_connect
= video_decoder_sink_connect
,
462 .sink_disconnect
= video_decoder_sink_disconnect
,
465 static HRESULT
video_decoder_source_query_interface(struct strmbase_pin
*iface
, REFIID iid
, void **out
)
467 QTVDecoderImpl
*filter
= impl_from_strmbase_filter(iface
->filter
);
469 if (IsEqualGUID(iid
, &IID_IMediaSeeking
))
470 return IUnknown_QueryInterface(filter
->seeking
, iid
, out
);
472 return E_NOINTERFACE
;
474 IUnknown_AddRef((IUnknown
*)*out
);
478 static HRESULT
video_decoder_source_query_accept(struct strmbase_pin
*iface
, const AM_MEDIA_TYPE
*mt
)
480 QTVDecoderImpl
*filter
= impl_from_strmbase_filter(iface
->filter
);
482 if (IsEqualGUID(&mt
->majortype
, &filter
->mt
.majortype
)
483 && (IsEqualGUID(&mt
->subtype
, &filter
->mt
.subtype
)
484 || IsEqualGUID(&filter
->mt
.subtype
, &GUID_NULL
)))
489 static HRESULT
video_decoder_source_get_media_type(struct strmbase_pin
*iface
,
490 unsigned int index
, AM_MEDIA_TYPE
*mt
)
492 QTVDecoderImpl
*filter
= impl_from_strmbase_filter(iface
->filter
);
495 return VFW_S_NO_MORE_ITEMS
;
496 CopyMediaType(mt
, &filter
->mt
);
500 static HRESULT WINAPI
video_decoder_source_DecideBufferSize(struct strmbase_source
*iface
,
501 IMemAllocator
*pAlloc
, ALLOCATOR_PROPERTIES
*ppropInputRequest
)
503 QTVDecoderImpl
*This
= impl_from_strmbase_filter(iface
->pin
.filter
);
504 ALLOCATOR_PROPERTIES actual
;
506 if (!ppropInputRequest
->cbAlign
)
507 ppropInputRequest
->cbAlign
= 1;
509 if (ppropInputRequest
->cbBuffer
< This
->outputSize
)
510 ppropInputRequest
->cbBuffer
= This
->outputSize
+ ppropInputRequest
->cbAlign
;
512 if (!ppropInputRequest
->cBuffers
)
513 ppropInputRequest
->cBuffers
= 1;
515 return IMemAllocator_SetProperties(pAlloc
, ppropInputRequest
, &actual
);
518 static const struct strmbase_source_ops source_ops
=
520 .base
.pin_query_interface
= video_decoder_source_query_interface
,
521 .base
.pin_query_accept
= video_decoder_source_query_accept
,
522 .base
.pin_get_media_type
= video_decoder_source_get_media_type
,
523 .pfnAttemptConnection
= BaseOutputPinImpl_AttemptConnection
,
524 .pfnDecideAllocator
= BaseOutputPinImpl_DecideAllocator
,
525 .pfnDecideBufferSize
= video_decoder_source_DecideBufferSize
,
528 static struct strmbase_pin
*video_decoder_get_pin(struct strmbase_filter
*iface
, unsigned int index
)
530 QTVDecoderImpl
*filter
= impl_from_strmbase_filter(iface
);
533 return &filter
->sink
.pin
;
535 return &filter
->source
.pin
;
539 static void video_decoder_destroy(struct strmbase_filter
*iface
)
541 QTVDecoderImpl
*filter
= impl_from_strmbase_filter(iface
);
543 if (filter
->sink
.pin
.peer
)
544 IPin_Disconnect(filter
->sink
.pin
.peer
);
545 IPin_Disconnect(&filter
->sink
.pin
.IPin_iface
);
547 if (filter
->source
.pin
.peer
)
548 IPin_Disconnect(filter
->source
.pin
.peer
);
549 IPin_Disconnect(&filter
->source
.pin
.IPin_iface
);
551 strmbase_sink_cleanup(&filter
->sink
);
552 strmbase_source_cleanup(&filter
->source
);
554 filter
->stream_cs
.DebugInfo
->Spare
[0] = 0;
555 DeleteCriticalSection(&filter
->stream_cs
);
556 FreeMediaType(&filter
->mt
);
557 IUnknown_Release(filter
->seeking
);
558 strmbase_filter_cleanup(&filter
->filter
);
562 static HRESULT
video_decoder_init_stream(struct strmbase_filter
*iface
)
564 QTVDecoderImpl
*This
= impl_from_strmbase_filter(iface
);
567 ICMDecompressionSessionOptionsRef sessionOptions
= NULL
;
568 ICMDecompressionTrackingCallbackRecord trackingCallbackRecord
;
570 trackingCallbackRecord
.decompressionTrackingCallback
= trackingCallback
;
571 trackingCallbackRecord
.decompressionTrackingRefCon
= (void*)This
;
573 err
= ICMDecompressionSessionCreate(NULL
, This
->hImageDescription
, sessionOptions
, This
->outputBufferAttributes
, &trackingCallbackRecord
, &This
->decompressionSession
);
577 ERR("Error with ICMDecompressionSessionCreate %i\n",err
);
581 return BaseOutputPinImpl_Active(&This
->source
);
584 static HRESULT
video_decoder_cleanup_stream(struct strmbase_filter
*iface
)
586 QTVDecoderImpl
* This
= impl_from_strmbase_filter(iface
);
588 if (This
->decompressionSession
)
589 ICMDecompressionSessionRelease(This
->decompressionSession
);
590 This
->decompressionSession
= NULL
;
592 return BaseOutputPinImpl_Inactive(&This
->source
);
595 static const struct strmbase_filter_ops filter_ops
=
597 .filter_get_pin
= video_decoder_get_pin
,
598 .filter_destroy
= video_decoder_destroy
,
599 .filter_init_stream
= video_decoder_init_stream
,
600 .filter_cleanup_stream
= video_decoder_cleanup_stream
,
603 HRESULT
video_decoder_create(IUnknown
*outer
, IUnknown
**out
)
605 static const WCHAR inW
[] = { 'I','n',0 };
606 static const WCHAR outW
[] = { 'O','u','t',0 };
607 QTVDecoderImpl
*object
;
609 ISeekingPassThru
*passthrough
;
611 if (!(object
= calloc(1, sizeof(*object
))))
612 return E_OUTOFMEMORY
;
614 strmbase_filter_init(&object
->filter
, outer
, &CLSID_QTVDecoder
, &filter_ops
);
616 InitializeCriticalSection(&object
->stream_cs
);
617 object
->stream_cs
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": QTVDecoderImpl.stream_cs");
619 strmbase_sink_init(&object
->sink
, &object
->filter
, inW
, &sink_ops
, NULL
);
621 strmbase_source_init(&object
->source
, &object
->filter
, outW
, &source_ops
);
623 if (FAILED(hr
= CoCreateInstance(&CLSID_SeekingPassThru
,
624 (IUnknown
*)&object
->source
.pin
.IPin_iface
, CLSCTX_INPROC_SERVER
,
625 &IID_IUnknown
, (void **)&object
->seeking
)))
627 strmbase_sink_cleanup(&object
->sink
);
628 strmbase_source_cleanup(&object
->source
);
629 strmbase_filter_cleanup(&object
->filter
);
634 IUnknown_QueryInterface(object
->seeking
, &IID_ISeekingPassThru
, (void **)&passthrough
);
635 ISeekingPassThru_Init(passthrough
, FALSE
, &object
->sink
.pin
.IPin_iface
);
636 ISeekingPassThru_Release(passthrough
);
638 TRACE("Created video decoder %p.\n", object
);
639 *out
= &object
->filter
.IUnknown_inner
;