Release 1.5.29.
[wine] / dlls / wineqtdecoder / qtvdecoder.c
1 /*
2  * QuickTime Toolkit decoder filter for video
3  *
4  * Copyright 2010 Aric Stewart, CodeWeavers
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include "config.h"
22
23 #define ULONG CoreFoundation_ULONG
24 #define HRESULT CoreFoundation_HRESULT
25
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
42
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
62
63 #include <QuickTime/ImageCompression.h>
64 #include <CoreVideo/CVPixelBuffer.h>
65
66 #undef LoadResource
67 #undef CompareString
68 #undef GetCurrentThread
69 #undef _CDECL
70 #undef DPRINTF
71 #undef GetCurrentProcess
72 #undef AnimatePalette
73 #undef EqualRgn
74 #undef FillRgn
75 #undef FrameRgn
76 #undef GetPixel
77 #undef InvertRgn
78 #undef LineTo
79 #undef OffsetRgn
80 #undef PaintRgn
81 #undef Polygon
82 #undef ResizePalette
83 #undef SetRectRgn
84 #undef CheckMenuItem
85 #undef DeleteMenu
86 #undef DrawMenuBar
87 #undef EnableMenuItem
88 #undef EqualRect
89 #undef FillRect
90 #undef FrameRect
91 #undef GetCursor
92 #undef GetMenu
93 #undef InvertRect
94 #undef IsWindowVisible
95 #undef MoveWindow
96 #undef OffsetRect
97 #undef PtInRect
98 #undef SetCursor
99 #undef SetRect
100 #undef ShowCursor
101 #undef ShowWindow
102 #undef UnionRect
103
104 #undef ULONG
105 #undef HRESULT
106 #undef DPRINTF
107 #undef STDMETHODCALLTYPE
108
109 #define COBJMACROS
110
111 #include "windef.h"
112 #include "winbase.h"
113 #include "wtypes.h"
114 #include "winuser.h"
115 #include "dshow.h"
116
117 #include "uuids.h"
118 #include "amvideo.h"
119 #include "strmif.h"
120 #include "vfwmsgs.h"
121 #include "vfw.h"
122 #include "dvdmedia.h"
123
124 #include <assert.h>
125
126 #include "wine/unicode.h"
127 #include "wine/debug.h"
128 #include "wine/strmbase.h"
129
130 #include "qtprivate.h"
131
132 extern CLSID CLSID_QTVDecoder;
133
134 WINE_DEFAULT_DEBUG_CHANNEL(qtdecoder);
135
136 typedef struct QTVDecoderImpl
137 {
138     TransformFilter tf;
139
140     ImageDescriptionHandle hImageDescription;
141     CFMutableDictionaryRef outputBufferAttributes;
142     ICMDecompressionSessionRef decompressionSession;
143     HRESULT decodeHR;
144
145     DWORD outputSize;
146
147 } QTVDecoderImpl;
148
149 static inline QTVDecoderImpl *impl_from_IBaseFilter( IBaseFilter *iface )
150 {
151     return CONTAINING_RECORD(iface, QTVDecoderImpl, tf.filter.IBaseFilter_iface);
152 }
153
154 static inline QTVDecoderImpl *impl_from_TransformFilter( TransformFilter *iface )
155 {
156     return CONTAINING_RECORD(iface, QTVDecoderImpl, tf.filter);
157 }
158
159 static const IBaseFilterVtbl QTVDecoder_Vtbl;
160
161 static void trackingCallback(
162                     void *decompressionTrackingRefCon,
163                     OSStatus result,
164                     ICMDecompressionTrackingFlags decompressionTrackingFlags,
165                     CVPixelBufferRef pixelBuffer,
166                     TimeValue64 displayTime,
167                     TimeValue64 displayDuration,
168                     ICMValidTimeFlags validTimeFlags,
169                     void *reserved,
170                     void *sourceFrameRefCon )
171 {
172     QTVDecoderImpl *This = (QTVDecoderImpl*)decompressionTrackingRefCon;
173     IMediaSample *pSample = (IMediaSample*)sourceFrameRefCon;
174     HRESULT hr  = S_OK;
175     IMediaSample* pOutSample = NULL;
176     LPBYTE pbDstStream;
177     DWORD cbDstStream;
178
179     if (result != noErr)
180     {
181         ERR("Error from Codec, no frame decompressed\n");
182         return;
183     }
184
185     if (!pixelBuffer)
186     {
187         ERR("No pixel buffer, no frame decompressed\n");
188         return;
189     }
190
191     EnterCriticalSection(&This->tf.csReceive);
192     hr = BaseOutputPinImpl_GetDeliveryBuffer((BaseOutputPin*)This->tf.ppPins[1], &pOutSample, NULL, NULL, 0);
193     if (FAILED(hr)) {
194         ERR("Unable to get delivery buffer (%x)\n", hr);
195         goto error;
196     }
197
198     hr = IMediaSample_SetActualDataLength(pOutSample, 0);
199     assert(hr == S_OK);
200
201     hr = IMediaSample_GetPointer(pOutSample, &pbDstStream);
202     if (FAILED(hr)) {
203         ERR("Unable to get pointer to buffer (%x)\n", hr);
204         goto error;
205     }
206
207     cbDstStream = IMediaSample_GetSize(pOutSample);
208     if (cbDstStream < This->outputSize) {
209         ERR("Sample size is too small %d < %d\n", cbDstStream, This->outputSize);
210         hr = E_FAIL;
211         goto error;
212     }
213
214     hr = AccessPixelBufferPixels(pixelBuffer, pbDstStream);
215     if (FAILED(hr))
216         goto error;
217
218     IMediaSample_SetActualDataLength(pOutSample, This->outputSize);
219
220     IMediaSample_SetPreroll(pOutSample, (IMediaSample_IsPreroll(pSample) == S_OK));
221     IMediaSample_SetDiscontinuity(pOutSample, (IMediaSample_IsDiscontinuity(pSample) == S_OK));
222     IMediaSample_SetSyncPoint(pOutSample, (IMediaSample_IsSyncPoint(pSample) == S_OK));
223
224     if (!validTimeFlags)
225         IMediaSample_SetTime(pOutSample, NULL, NULL);
226     else
227     {
228         LONGLONG tStart, tStop;
229
230         if (validTimeFlags & kICMValidTime_DisplayTimeStampIsValid)
231             tStart = displayTime;
232          else
233             tStart = 0;
234         if (validTimeFlags & kICMValidTime_DisplayDurationIsValid)
235             tStop = tStart + displayDuration;
236         else
237             tStop = tStart;
238
239         IMediaSample_SetTime(pOutSample, &tStart, &tStop);
240     }
241
242     LeaveCriticalSection(&This->tf.csReceive);
243     hr = BaseOutputPinImpl_Deliver((BaseOutputPin*)This->tf.ppPins[1], pOutSample);
244     EnterCriticalSection(&This->tf.csReceive);
245     if (hr != S_OK && hr != VFW_E_NOT_CONNECTED)
246         ERR("Error sending sample (%x)\n", hr);
247
248 error:
249     LeaveCriticalSection(&This->tf.csReceive);
250     if (pOutSample)
251         IMediaSample_Release(pOutSample);
252
253     This->decodeHR = hr;
254 }
255
256 static HRESULT WINAPI QTVDecoder_StartStreaming(TransformFilter* pTransformFilter)
257 {
258     QTVDecoderImpl* This = impl_from_TransformFilter(pTransformFilter);
259     OSErr err = noErr;
260     ICMDecompressionSessionOptionsRef sessionOptions = NULL;
261     ICMDecompressionTrackingCallbackRecord trackingCallbackRecord;
262
263     TRACE("(%p)->()\n", This);
264
265     trackingCallbackRecord.decompressionTrackingCallback = trackingCallback;
266     trackingCallbackRecord.decompressionTrackingRefCon = (void*)This;
267
268     err = ICMDecompressionSessionCreate(NULL, This->hImageDescription, sessionOptions, This->outputBufferAttributes, &trackingCallbackRecord, &This->decompressionSession);
269
270     if (err != noErr)
271     {
272         ERR("Error with ICMDecompressionSessionCreate %i\n",err);
273         return E_FAIL;
274     }
275
276     return S_OK;
277 }
278
279 static HRESULT WINAPI QTVDecoder_Receive(TransformFilter *tf, IMediaSample *pSample)
280 {
281     QTVDecoderImpl* This = impl_from_TransformFilter(tf);
282     HRESULT hr;
283     DWORD cbSrcStream;
284     LPBYTE pbSrcStream;
285
286     ICMFrameTimeRecord frameTime = {{0}};
287     TimeValue time = 1;
288     TimeScale timeScale = 1;
289     OSStatus err = noErr;
290     LONGLONG tStart, tStop;
291
292     hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
293     if (FAILED(hr))
294     {
295         ERR("Cannot get pointer to sample data (%x)\n", hr);
296         goto error;
297     }
298
299     cbSrcStream = IMediaSample_GetActualDataLength(pSample);
300
301     if (IMediaSample_GetTime(pSample, &tStart, &tStop) != S_OK)
302         tStart = tStop = 0;
303
304     time = tStart;
305
306     frameTime.recordSize = sizeof(ICMFrameTimeRecord);
307     *(TimeValue64 *)&frameTime.value = tStart;
308     frameTime.scale = 1;
309     frameTime.rate = fixed1;
310     frameTime.duration = tStop - tStart;
311     frameTime.frameNumber = 0;
312     frameTime.flags = icmFrameTimeIsNonScheduledDisplayTime;
313
314     err = ICMDecompressionSessionDecodeFrame(This->decompressionSession,
315             (UInt8 *)pbSrcStream, cbSrcStream, NULL, &frameTime, pSample);
316
317     if (err != noErr)
318     {
319         ERR("Error with ICMDecompressionSessionDecodeFrame\n");
320         hr = E_FAIL;
321         goto error;
322     }
323
324     ICMDecompressionSessionSetNonScheduledDisplayTime(This->decompressionSession, time, timeScale, 0);
325     ICMDecompressionSessionFlush(This->decompressionSession);
326     hr = This->decodeHR;
327
328 error:
329     return hr;
330 }
331
332 static HRESULT WINAPI QTVDecoder_StopStreaming(TransformFilter* pTransformFilter)
333 {
334     QTVDecoderImpl* This = impl_from_TransformFilter(pTransformFilter);
335
336     TRACE("(%p)->()\n", This);
337
338     if (This->decompressionSession)
339         ICMDecompressionSessionRelease(This->decompressionSession);
340     This->decompressionSession = NULL;
341
342     return S_OK;
343 }
344
345 static HRESULT WINAPI QTVDecoder_SetMediaType(TransformFilter *tf, PIN_DIRECTION dir, const AM_MEDIA_TYPE * pmt)
346 {
347     QTVDecoderImpl* This = impl_from_TransformFilter(tf);
348     HRESULT hr = VFW_E_TYPE_NOT_ACCEPTED;
349     OSErr err = noErr;
350     AM_MEDIA_TYPE *outpmt = &This->tf.pmt;
351     CFNumberRef n = NULL;
352
353     TRACE("(%p)->(%p)\n", This, pmt);
354
355     if (dir != PINDIR_INPUT)
356         return S_OK;
357
358     FreeMediaType(outpmt);
359     CopyMediaType(outpmt, pmt);
360
361     if (This->hImageDescription)
362         DisposeHandle((Handle)This->hImageDescription);
363
364     This->hImageDescription = (ImageDescriptionHandle)
365         NewHandleClear(sizeof(ImageDescription));
366
367     if (This->hImageDescription != NULL)
368     {
369         (**This->hImageDescription).idSize = sizeof(ImageDescription);
370         (**This->hImageDescription).spatialQuality = codecNormalQuality;
371         (**This->hImageDescription).frameCount = 1;
372         (**This->hImageDescription).clutID = -1;
373     }
374     else
375     {
376         ERR("Failed to create ImageDescription\n");
377         goto failed;
378     }
379
380     /* Check root (GUID w/o FOURCC) */
381     if ((IsEqualIID(&pmt->majortype, &MEDIATYPE_Video)) &&
382         (!memcmp(((const char *)&pmt->subtype)+4, ((const char *)&MEDIATYPE_Video)+4, sizeof(GUID)-4)))
383     {
384         VIDEOINFOHEADER *format1 = (VIDEOINFOHEADER *)outpmt->pbFormat;
385         VIDEOINFOHEADER2 *format2 = (VIDEOINFOHEADER2 *)outpmt->pbFormat;
386         BITMAPINFOHEADER *bmi;
387         OSType fourCC;
388         DecompressorComponent dc;
389         OSType format;
390         DWORD outputWidth, outputHeight, outputDepth;
391
392         if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo))
393             bmi = &format1->bmiHeader;
394         else if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo2))
395             bmi = &format2->bmiHeader;
396         else
397             goto failed;
398
399         TRACE("Fourcc: %s\n", debugstr_an((const char *)&pmt->subtype.Data1, 4));
400         fourCC = ((const char *)&pmt->subtype.Data1)[3] |
401                  (((const char *)&pmt->subtype.Data1)[2]<<8) |
402                  (((const char *)&pmt->subtype.Data1)[1]<<16) |
403                  (((const char *)&pmt->subtype.Data1)[0]<<24);
404
405         err = FindCodec(fourCC,NULL,NULL,&dc);
406         if (err != noErr || dc == 0x0)
407         {
408             TRACE("Codec not found\n");
409             goto failed;
410         }
411
412         outputWidth = bmi->biWidth;
413         outputHeight = bmi->biHeight;
414
415         (**This->hImageDescription).cType = fourCC;
416         (**This->hImageDescription).width = outputWidth;
417         (**This->hImageDescription).height = outputHeight;
418         (**This->hImageDescription).depth = bmi->biBitCount;
419         (**This->hImageDescription).hRes = 72<<16;
420         (**This->hImageDescription).vRes = 72<<16;
421
422         if (This->outputBufferAttributes)
423             CFRelease(This->outputBufferAttributes);
424
425         This->outputBufferAttributes = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
426         if (!This->outputBufferAttributes)
427         {
428             ERR("Failed to create outputBufferAttributes\n");
429             goto failed;
430         }
431
432         n = CFNumberCreate(NULL, kCFNumberIntType, &outputWidth);
433         CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferWidthKey, n);
434         CFRelease(n);
435
436         n = CFNumberCreate(NULL, kCFNumberIntType, &outputHeight);
437         CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferHeightKey, n);
438         CFRelease(n);
439
440         /* yes this looks wrong.  but 32ARGB is 24 RGB with an alpha channel */
441         format = k32ARGBPixelFormat;
442         n = CFNumberCreate(NULL, kCFNumberIntType, &format);
443         CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferPixelFormatTypeKey, n);
444         CFRelease(n);
445
446         CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferCGBitmapContextCompatibilityKey, kCFBooleanTrue);
447         CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferCGImageCompatibilityKey, kCFBooleanTrue);
448
449         outputDepth = 3;
450         This->outputSize = outputWidth * outputHeight * outputDepth;
451         bmi->biCompression =  BI_RGB;
452         bmi->biBitCount =  24;
453         outpmt->subtype = MEDIASUBTYPE_RGB24;
454
455         return S_OK;
456     }
457
458 failed:
459     if (This->hImageDescription)
460     {
461         DisposeHandle((Handle)This->hImageDescription);
462         This->hImageDescription =  NULL;
463     }
464     if (This->outputBufferAttributes)
465     {
466         CFRelease(This->outputBufferAttributes);
467         This->outputBufferAttributes = NULL;
468     }
469
470     TRACE("Connection refused\n");
471     return hr;
472 }
473
474 static HRESULT WINAPI QTVDecoder_BreakConnect(TransformFilter *tf, PIN_DIRECTION dir)
475 {
476     QTVDecoderImpl *This = impl_from_TransformFilter(tf);
477
478     TRACE("(%p)->()\n", This);
479
480     if (This->hImageDescription)
481         DisposeHandle((Handle)This->hImageDescription);
482     if (This->outputBufferAttributes)
483         CFRelease(This->outputBufferAttributes);
484
485     This->hImageDescription = NULL;
486     This->outputBufferAttributes = NULL;
487
488     return S_OK;
489 }
490
491 static HRESULT WINAPI QTVDecoder_DecideBufferSize(TransformFilter *tf, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
492 {
493     QTVDecoderImpl *This = impl_from_TransformFilter(tf);
494     ALLOCATOR_PROPERTIES actual;
495
496     TRACE("()\n");
497
498     if (!ppropInputRequest->cbAlign)
499         ppropInputRequest->cbAlign = 1;
500
501     if (ppropInputRequest->cbBuffer < This->outputSize)
502             ppropInputRequest->cbBuffer = This->outputSize + ppropInputRequest->cbAlign;
503
504     if (!ppropInputRequest->cBuffers)
505         ppropInputRequest->cBuffers = 1;
506
507     return IMemAllocator_SetProperties(pAlloc, ppropInputRequest, &actual);
508 }
509
510 static const TransformFilterFuncTable QTVDecoder_FuncsTable = {
511     QTVDecoder_DecideBufferSize,
512     QTVDecoder_StartStreaming,
513     QTVDecoder_Receive,
514     QTVDecoder_StopStreaming,
515     NULL,
516     QTVDecoder_SetMediaType,
517     NULL,
518     QTVDecoder_BreakConnect,
519     NULL,
520     NULL,
521     NULL,
522     NULL
523 };
524
525 IUnknown * CALLBACK QTVDecoder_create(IUnknown * pUnkOuter, HRESULT* phr)
526 {
527     HRESULT hr;
528     QTVDecoderImpl * This;
529
530     TRACE("(%p, %p)\n", pUnkOuter, phr);
531
532     *phr = S_OK;
533
534     if (pUnkOuter)
535     {
536         *phr = CLASS_E_NOAGGREGATION;
537         return NULL;
538     }
539
540     hr = TransformFilter_Construct(&QTVDecoder_Vtbl, sizeof(QTVDecoderImpl), &CLSID_QTVDecoder, &QTVDecoder_FuncsTable, (IBaseFilter**)&This);
541
542     if (FAILED(hr))
543     {
544         *phr = hr;
545         return NULL;
546     }
547
548     *phr = hr;
549     return (IUnknown*)This;
550 }
551
552 static const IBaseFilterVtbl QTVDecoder_Vtbl =
553 {
554     TransformFilterImpl_QueryInterface,
555     BaseFilterImpl_AddRef,
556     TransformFilterImpl_Release,
557     BaseFilterImpl_GetClassID,
558     TransformFilterImpl_Stop,
559     TransformFilterImpl_Pause,
560     TransformFilterImpl_Run,
561     BaseFilterImpl_GetState,
562     BaseFilterImpl_SetSyncSource,
563     BaseFilterImpl_GetSyncSource,
564     BaseFilterImpl_EnumPins,
565     TransformFilterImpl_FindPin,
566     BaseFilterImpl_QueryFilterInfo,
567     BaseFilterImpl_JoinFilterGraph,
568     BaseFilterImpl_QueryVendorInfo
569 };