kernel32: Get rid of the last parameter to PROFILE_CopyEntry().
[wine.git] / dlls / wineqtdecoder / qtvdecoder.c
bloba86cd432c22c6a6936ce9bc9e929c29b7b1141a4
1 /*
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
21 #include "config.h"
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>
66 #undef LoadResource
67 #undef CompareString
68 #undef GetCurrentThread
69 #undef _CDECL
70 #undef GetCurrentProcess
71 #undef AnimatePalette
72 #undef EqualRgn
73 #undef FillRgn
74 #undef FrameRgn
75 #undef GetPixel
76 #undef InvertRgn
77 #undef LineTo
78 #undef OffsetRgn
79 #undef PaintRgn
80 #undef Polygon
81 #undef ResizePalette
82 #undef SetRectRgn
83 #undef CheckMenuItem
84 #undef DeleteMenu
85 #undef DrawMenuBar
86 #undef EnableMenuItem
87 #undef EqualRect
88 #undef FillRect
89 #undef FrameRect
90 #undef GetCursor
91 #undef GetMenu
92 #undef InvertRect
93 #undef IsWindowVisible
94 #undef MoveWindow
95 #undef OffsetRect
96 #undef PtInRect
97 #undef SetCursor
98 #undef SetRect
99 #undef ShowCursor
100 #undef ShowWindow
101 #undef UnionRect
103 #undef ULONG
104 #undef HRESULT
105 #undef STDMETHODCALLTYPE
107 #define COBJMACROS
109 #include "windef.h"
110 #include "winbase.h"
111 #include "wtypes.h"
112 #include "winuser.h"
113 #include "dshow.h"
115 #include "uuids.h"
116 #include "amvideo.h"
117 #include "strmif.h"
118 #include "vfwmsgs.h"
119 #include "vfw.h"
120 #include "dvdmedia.h"
122 #include <assert.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;
138 AM_MEDIA_TYPE mt;
140 struct strmbase_source source;
141 IUnknown *seeking;
143 struct strmbase_sink sink;
145 ImageDescriptionHandle hImageDescription;
146 CFMutableDictionaryRef outputBufferAttributes;
147 ICMDecompressionSessionRef decompressionSession;
148 HRESULT decodeHR;
150 DWORD outputSize;
151 } QTVDecoderImpl;
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;
164 else
165 return E_NOINTERFACE;
167 IUnknown_AddRef((IUnknown *)*out);
168 return S_OK;
171 static void trackingCallback(
172 void *decompressionTrackingRefCon,
173 OSStatus result,
174 ICMDecompressionTrackingFlags decompressionTrackingFlags,
175 CVPixelBufferRef pixelBuffer,
176 TimeValue64 displayTime,
177 TimeValue64 displayDuration,
178 ICMValidTimeFlags validTimeFlags,
179 void *reserved,
180 void *sourceFrameRefCon )
182 QTVDecoderImpl *This = (QTVDecoderImpl*)decompressionTrackingRefCon;
183 IMediaSample *pSample = (IMediaSample*)sourceFrameRefCon;
184 HRESULT hr = S_OK;
185 IMediaSample* pOutSample = NULL;
186 LPBYTE pbDstStream;
187 DWORD cbDstStream;
189 if (result != noErr)
191 ERR("Error from Codec, no frame decompressed\n");
192 return;
195 if (!pixelBuffer)
197 ERR("No pixel buffer, no frame decompressed\n");
198 return;
201 EnterCriticalSection(&This->stream_cs);
202 hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->source, &pOutSample, NULL, NULL, 0);
203 if (FAILED(hr)) {
204 ERR("Unable to get delivery buffer (%x)\n", hr);
205 goto error;
208 hr = IMediaSample_SetActualDataLength(pOutSample, 0);
209 assert(hr == S_OK);
211 hr = IMediaSample_GetPointer(pOutSample, &pbDstStream);
212 if (FAILED(hr)) {
213 ERR("Unable to get pointer to buffer (%x)\n", hr);
214 goto error;
217 cbDstStream = IMediaSample_GetSize(pOutSample);
218 if (cbDstStream < This->outputSize) {
219 ERR("Sample size is too small %d < %d\n", cbDstStream, This->outputSize);
220 hr = E_FAIL;
221 goto error;
224 hr = AccessPixelBufferPixels(pixelBuffer, pbDstStream);
225 if (FAILED(hr))
226 goto error;
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));
234 if (!validTimeFlags)
235 IMediaSample_SetTime(pOutSample, NULL, NULL);
236 else
238 LONGLONG tStart, tStop;
240 if (validTimeFlags & kICMValidTime_DisplayTimeStampIsValid)
241 tStart = displayTime;
242 else
243 tStart = 0;
244 if (validTimeFlags & kICMValidTime_DisplayDurationIsValid)
245 tStop = tStart + displayDuration;
246 else
247 tStop = tStart;
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);
256 error:
257 LeaveCriticalSection(&This->stream_cs);
258 if (pOutSample)
259 IMediaSample_Release(pOutSample);
261 This->decodeHR = hr;
264 static HRESULT WINAPI video_decoder_sink_Receive(struct strmbase_sink *iface, IMediaSample *pSample)
266 QTVDecoderImpl *This = impl_from_strmbase_filter(iface->pin.filter);
267 HRESULT hr;
268 DWORD cbSrcStream;
269 LPBYTE pbSrcStream;
271 ICMFrameTimeRecord frameTime = {{0}};
272 TimeValue time = 1;
273 TimeScale timeScale = 1;
274 OSStatus err = noErr;
275 LONGLONG tStart, tStop;
277 EnterCriticalSection(&This->stream_cs);
279 hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
280 if (FAILED(hr))
282 ERR("Cannot get pointer to sample data (%x)\n", hr);
283 goto error;
286 cbSrcStream = IMediaSample_GetActualDataLength(pSample);
288 if (IMediaSample_GetTime(pSample, &tStart, &tStop) != S_OK)
289 tStart = tStop = 0;
291 time = tStart;
293 frameTime.recordSize = sizeof(ICMFrameTimeRecord);
294 *(TimeValue64 *)&frameTime.value = tStart;
295 frameTime.scale = 1;
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);
304 if (err != noErr)
306 ERR("Error with ICMDecompressionSessionDecodeFrame\n");
307 hr = E_FAIL;
308 goto error;
311 ICMDecompressionSessionSetNonScheduledDisplayTime(This->decompressionSession, time, timeScale, 0);
312 ICMDecompressionSessionFlush(This->decompressionSession);
313 hr = This->decodeHR;
315 error:
316 LeaveCriticalSection(&This->stream_cs);
317 return hr;
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;
324 OSErr err = noErr;
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;
344 else
346 ERR("Failed to create ImageDescription\n");
347 goto failed;
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;
357 OSType fourCC;
358 DecompressorComponent dc;
359 OSType format;
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;
366 else
367 goto failed;
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");
379 goto failed;
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");
399 goto failed;
402 n = CFNumberCreate(NULL, kCFNumberIntType, &outputWidth);
403 CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferWidthKey, n);
404 CFRelease(n);
406 n = CFNumberCreate(NULL, kCFNumberIntType, &outputHeight);
407 CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferHeightKey, n);
408 CFRelease(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);
414 CFRelease(n);
416 CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferCGBitmapContextCompatibilityKey, kCFBooleanTrue);
417 CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferCGImageCompatibilityKey, kCFBooleanTrue);
419 outputDepth = 3;
420 This->outputSize = outputWidth * outputHeight * outputDepth;
421 bmi->biCompression = BI_RGB;
422 bmi->biBitCount = 24;
423 outpmt->subtype = MEDIASUBTYPE_RGB24;
425 return S_OK;
428 failed:
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");
441 return hr;
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);
471 else
472 return E_NOINTERFACE;
474 IUnknown_AddRef((IUnknown *)*out);
475 return S_OK;
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)))
485 return S_OK;
486 return S_FALSE;
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);
494 if (index)
495 return VFW_S_NO_MORE_ITEMS;
496 CopyMediaType(mt, &filter->mt);
497 return S_OK;
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);
532 if (index == 0)
533 return &filter->sink.pin;
534 else if (index == 1)
535 return &filter->source.pin;
536 return NULL;
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);
559 free(filter);
562 static HRESULT video_decoder_init_stream(struct strmbase_filter *iface)
564 QTVDecoderImpl *This = impl_from_strmbase_filter(iface);
566 OSErr err = noErr;
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);
575 if (err != noErr)
577 ERR("Error with ICMDecompressionSessionCreate %i\n",err);
578 return E_FAIL;
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;
608 HRESULT hr;
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);
630 free(object);
631 return hr;
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;
640 return S_OK;