mciseq: wNotifyDeviceID is redundant.
[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     IUnknown *seekthru_unk;
140
141     ImageDescriptionHandle hImageDescription;
142     CFMutableDictionaryRef outputBufferAttributes;
143     ICMDecompressionSessionRef decompressionSession;
144     HRESULT decodeHR;
145
146     DWORD outputSize;
147
148 } QTVDecoderImpl;
149
150 static const IBaseFilterVtbl QTVDecoder_Vtbl;
151
152 static void trackingCallback(
153                     void *decompressionTrackingRefCon,
154                     OSStatus result,
155                     ICMDecompressionTrackingFlags decompressionTrackingFlags,
156                     CVPixelBufferRef pixelBuffer,
157                     TimeValue64 displayTime,
158                     TimeValue64 displayDuration,
159                     ICMValidTimeFlags validTimeFlags,
160                     void *reserved,
161                     void *sourceFrameRefCon )
162 {
163     QTVDecoderImpl *This = (QTVDecoderImpl*)decompressionTrackingRefCon;
164     IMediaSample *pSample = (IMediaSample*)sourceFrameRefCon;
165     HRESULT hr  = S_OK;
166     IMediaSample* pOutSample = NULL;
167     LPBYTE pbDstStream;
168     DWORD cbDstStream;
169
170     if (result != noErr)
171     {
172         ERR("Error from Codec, no frame decompressed\n");
173         return;
174     }
175
176     if (!pixelBuffer)
177     {
178         ERR("No pixel buffer, no frame decompressed\n");
179         return;
180     }
181
182     EnterCriticalSection(&This->tf.filter.csFilter);
183     hr = BaseOutputPinImpl_GetDeliveryBuffer((BaseOutputPin*)This->tf.ppPins[1], &pOutSample, NULL, NULL, 0);
184     if (FAILED(hr)) {
185         ERR("Unable to get delivery buffer (%x)\n", hr);
186         goto error;
187     }
188
189     hr = IMediaSample_SetActualDataLength(pOutSample, 0);
190     assert(hr == S_OK);
191
192     hr = IMediaSample_GetPointer(pOutSample, &pbDstStream);
193     if (FAILED(hr)) {
194         ERR("Unable to get pointer to buffer (%x)\n", hr);
195         goto error;
196     }
197
198     cbDstStream = IMediaSample_GetSize(pOutSample);
199     if (cbDstStream < This->outputSize) {
200         ERR("Sample size is too small %d < %d\n", cbDstStream, This->outputSize);
201         hr = E_FAIL;
202         goto error;
203     }
204
205     hr = AccessPixelBufferPixels(pixelBuffer, pbDstStream);
206     if (FAILED(hr))
207         goto error;
208
209     IMediaSample_SetActualDataLength(pOutSample, This->outputSize);
210
211     IMediaSample_SetPreroll(pOutSample, (IMediaSample_IsPreroll(pSample) == S_OK));
212     IMediaSample_SetDiscontinuity(pOutSample, (IMediaSample_IsDiscontinuity(pSample) == S_OK));
213     IMediaSample_SetSyncPoint(pOutSample, (IMediaSample_IsSyncPoint(pSample) == S_OK));
214
215     if (!validTimeFlags)
216         IMediaSample_SetTime(pOutSample, NULL, NULL);
217     else
218     {
219         LONGLONG tStart, tStop;
220
221         if (validTimeFlags & kICMValidTime_DisplayTimeStampIsValid)
222             tStart = displayTime;
223          else
224             tStart = 0;
225         if (validTimeFlags & kICMValidTime_DisplayDurationIsValid)
226             tStop = tStart + displayDuration;
227         else
228             tStop = tStart;
229
230         IMediaSample_SetTime(pOutSample, &tStart, &tStop);
231     }
232
233     LeaveCriticalSection(&This->tf.filter.csFilter);
234     hr = BaseOutputPinImpl_Deliver((BaseOutputPin*)This->tf.ppPins[1], pOutSample);
235     if (hr != S_OK && hr != VFW_E_NOT_CONNECTED)
236         ERR("Error sending sample (%x)\n", hr);
237
238 error:
239     if (pOutSample)
240         IMediaSample_Release(pOutSample);
241
242     This->decodeHR = hr;
243 }
244
245 static HRESULT WINAPI QTVDecoder_StartStreaming(TransformFilter* pTransformFilter)
246 {
247     QTVDecoderImpl* This = (QTVDecoderImpl*)pTransformFilter;
248     OSErr err = noErr;
249     ICMDecompressionSessionOptionsRef sessionOptions = NULL;
250     ICMDecompressionTrackingCallbackRecord trackingCallbackRecord;
251
252     TRACE("(%p)->()\n", This);
253
254     trackingCallbackRecord.decompressionTrackingCallback = trackingCallback;
255     trackingCallbackRecord.decompressionTrackingRefCon = (void*)This;
256
257     err = ICMDecompressionSessionCreate(NULL, This->hImageDescription, sessionOptions, This->outputBufferAttributes, &trackingCallbackRecord, &This->decompressionSession);
258
259     if (err != noErr)
260     {
261         ERR("Error with ICMDecompressionSessionCreate %i\n",err);
262         return E_FAIL;
263     }
264
265     return S_OK;
266 }
267
268 static HRESULT WINAPI QTVDecoder_Receive(TransformFilter *tf, IMediaSample *pSample)
269 {
270     QTVDecoderImpl* This = (QTVDecoderImpl *)tf;
271     HRESULT hr;
272     DWORD cbSrcStream;
273     LPBYTE pbSrcStream;
274
275     ICMFrameTimeRecord frameTime = {{0}};
276     TimeValue time = 1;
277     TimeScale timeScale = 1;
278     OSStatus err = noErr;
279     LONGLONG tStart, tStop;
280
281     EnterCriticalSection(&This->tf.filter.csFilter);
282     hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
283     if (FAILED(hr))
284     {
285         ERR("Cannot get pointer to sample data (%x)\n", hr);
286         goto error;
287     }
288
289     cbSrcStream = IMediaSample_GetActualDataLength(pSample);
290
291     if (IMediaSample_GetTime(pSample, &tStart, &tStop) != S_OK)
292         tStart = tStop = 0;
293
294     time = tStart;
295
296     frameTime.recordSize = sizeof(ICMFrameTimeRecord);
297     *(TimeValue64 *)&frameTime.value = tStart;
298     frameTime.scale = 1;
299     frameTime.rate = fixed1;
300     frameTime.duration = tStop - tStart;
301     frameTime.frameNumber = 0;
302     frameTime.flags = icmFrameTimeIsNonScheduledDisplayTime;
303
304     err = ICMDecompressionSessionDecodeFrame(This->decompressionSession,
305             (UInt8 *)pbSrcStream, cbSrcStream, NULL, &frameTime, pSample);
306
307     if (err != noErr)
308     {
309         ERR("Error with ICMDecompressionSessionDecodeFrame\n");
310         hr = E_FAIL;
311         goto error;
312     }
313
314     ICMDecompressionSessionSetNonScheduledDisplayTime(This->decompressionSession, time, timeScale, 0);
315     ICMDecompressionSessionFlush(This->decompressionSession);
316     hr = This->decodeHR;
317
318 error:
319     LeaveCriticalSection(&This->tf.filter.csFilter);
320     return hr;
321 }
322
323 static HRESULT WINAPI QTVDecoder_StopStreaming(TransformFilter* pTransformFilter)
324 {
325     QTVDecoderImpl* This = (QTVDecoderImpl*)pTransformFilter;
326
327     TRACE("(%p)->()\n", This);
328
329     if (This->decompressionSession)
330         ICMDecompressionSessionRelease(This->decompressionSession);
331     This->decompressionSession = NULL;
332
333     return S_OK;
334 }
335
336 static HRESULT WINAPI QTVDecoder_SetMediaType(TransformFilter *tf, PIN_DIRECTION dir, const AM_MEDIA_TYPE * pmt)
337 {
338     QTVDecoderImpl* This = (QTVDecoderImpl*)tf;
339     HRESULT hr = VFW_E_TYPE_NOT_ACCEPTED;
340     OSErr err = noErr;
341     AM_MEDIA_TYPE *outpmt = &This->tf.pmt;
342     CFNumberRef n = NULL;
343
344     TRACE("(%p)->(%p)\n", This, pmt);
345
346     if (dir != PINDIR_INPUT)
347         return S_OK;
348
349     FreeMediaType(outpmt);
350     CopyMediaType(outpmt, pmt);
351
352     if (This->hImageDescription)
353         DisposeHandle((Handle)This->hImageDescription);
354
355     This->hImageDescription = (ImageDescriptionHandle)
356         NewHandleClear(sizeof(ImageDescription));
357
358     if (This->hImageDescription != NULL)
359     {
360         (**This->hImageDescription).idSize = sizeof(ImageDescription);
361         (**This->hImageDescription).spatialQuality = codecNormalQuality;
362         (**This->hImageDescription).frameCount = 1;
363         (**This->hImageDescription).clutID = -1;
364     }
365     else
366     {
367         ERR("Failed to create ImageDescription\n");
368         goto failed;
369     }
370
371     /* Check root (GUID w/o FOURCC) */
372     if ((IsEqualIID(&pmt->majortype, &MEDIATYPE_Video)) &&
373         (!memcmp(((const char *)&pmt->subtype)+4, ((const char *)&MEDIATYPE_Video)+4, sizeof(GUID)-4)))
374     {
375         VIDEOINFOHEADER *format1 = (VIDEOINFOHEADER *)outpmt->pbFormat;
376         VIDEOINFOHEADER2 *format2 = (VIDEOINFOHEADER2 *)outpmt->pbFormat;
377         BITMAPINFOHEADER *bmi;
378         OSType fourCC;
379         DecompressorComponent dc;
380         OSType format;
381         DWORD outputWidth, outputHeight, outputDepth;
382
383         if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo))
384             bmi = &format1->bmiHeader;
385         else if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo2))
386             bmi = &format2->bmiHeader;
387         else
388             goto failed;
389
390         TRACE("Fourcc: %s\n", debugstr_an((const char *)&pmt->subtype.Data1, 4));
391         fourCC = ((const char *)&pmt->subtype.Data1)[3] |
392                  (((const char *)&pmt->subtype.Data1)[2]<<8) |
393                  (((const char *)&pmt->subtype.Data1)[1]<<16) |
394                  (((const char *)&pmt->subtype.Data1)[0]<<24);
395
396         err = FindCodec(fourCC,NULL,NULL,&dc);
397         if (err != noErr || dc == 0x0)
398         {
399             TRACE("Codec not found\n");
400             goto failed;
401         }
402
403         outputWidth = bmi->biWidth;
404         outputHeight = bmi->biHeight;
405
406         (**This->hImageDescription).cType = fourCC;
407         (**This->hImageDescription).width = outputWidth;
408         (**This->hImageDescription).height = outputHeight;
409         (**This->hImageDescription).depth = bmi->biBitCount;
410         (**This->hImageDescription).hRes = 72<<16;
411         (**This->hImageDescription).vRes = 72<<16;
412
413         if (This->outputBufferAttributes)
414             CFRelease(This->outputBufferAttributes);
415
416         This->outputBufferAttributes = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
417         if (!This->outputBufferAttributes)
418         {
419             ERR("Failed to create outputBufferAttributes\n");
420             goto failed;
421         }
422
423         n = CFNumberCreate(NULL, kCFNumberIntType, &outputWidth);
424         CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferWidthKey, n);
425         CFRelease(n);
426
427         n = CFNumberCreate(NULL, kCFNumberIntType, &outputHeight);
428         CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferHeightKey, n);
429         CFRelease(n);
430
431         /* yes this looks wrong.  but 32ARGB is 24 RGB with an alpha channel */
432         format = k32ARGBPixelFormat;
433         n = CFNumberCreate(NULL, kCFNumberIntType, &format);
434         CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferPixelFormatTypeKey, n);
435         CFRelease(n);
436
437         CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferCGBitmapContextCompatibilityKey, kCFBooleanTrue);
438         CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferCGImageCompatibilityKey, kCFBooleanTrue);
439
440         outputDepth = 3;
441         This->outputSize = outputWidth * outputHeight * outputDepth;
442         bmi->biCompression =  BI_RGB;
443         bmi->biBitCount =  24;
444         outpmt->subtype = MEDIASUBTYPE_RGB24;
445         if (bmi->biHeight > 0)
446             bmi->biHeight = -bmi->biHeight;
447
448         return S_OK;
449     }
450
451 failed:
452     if (This->hImageDescription)
453     {
454         DisposeHandle((Handle)This->hImageDescription);
455         This->hImageDescription =  NULL;
456     }
457     if (This->outputBufferAttributes)
458     {
459         CFRelease(This->outputBufferAttributes);
460         This->outputBufferAttributes = NULL;
461     }
462
463     TRACE("Connection refused\n");
464     return hr;
465 }
466
467 static HRESULT WINAPI QTVDecoder_BreakConnect(TransformFilter *tf, PIN_DIRECTION dir)
468 {
469     QTVDecoderImpl *This = (QTVDecoderImpl *)tf;
470
471     TRACE("(%p)->()\n", This);
472
473     if (This->hImageDescription)
474         DisposeHandle((Handle)This->hImageDescription);
475     if (This->outputBufferAttributes)
476         CFRelease(This->outputBufferAttributes);
477
478     This->hImageDescription = NULL;
479     This->outputBufferAttributes = NULL;
480
481     return S_OK;
482 }
483
484 static HRESULT WINAPI QTVDecoder_DecideBufferSize(TransformFilter *tf, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
485 {
486     QTVDecoderImpl *This = (QTVDecoderImpl*)tf;
487     ALLOCATOR_PROPERTIES actual;
488
489     TRACE("()\n");
490
491     if (!ppropInputRequest->cbAlign)
492         ppropInputRequest->cbAlign = 1;
493
494     if (ppropInputRequest->cbBuffer < This->outputSize)
495             ppropInputRequest->cbBuffer = This->outputSize;
496
497     if (!ppropInputRequest->cBuffers)
498         ppropInputRequest->cBuffers = 1;
499
500     return IMemAllocator_SetProperties(pAlloc, ppropInputRequest, &actual);
501 }
502
503 static const TransformFilterFuncTable QTVDecoder_FuncsTable = {
504     QTVDecoder_DecideBufferSize,
505     QTVDecoder_StartStreaming,
506     QTVDecoder_Receive,
507     QTVDecoder_StopStreaming,
508     NULL,
509     QTVDecoder_SetMediaType,
510     NULL,
511     QTVDecoder_BreakConnect,
512     NULL,
513     NULL,
514     NULL,
515     NULL
516 };
517
518 IUnknown * CALLBACK QTVDecoder_create(IUnknown * pUnkOuter, HRESULT* phr)
519 {
520     HRESULT hr;
521     QTVDecoderImpl * This;
522
523     TRACE("(%p, %p)\n", pUnkOuter, phr);
524
525     *phr = S_OK;
526
527     if (pUnkOuter)
528     {
529         *phr = CLASS_E_NOAGGREGATION;
530         return NULL;
531     }
532
533     hr = TransformFilter_Construct(&QTVDecoder_Vtbl, sizeof(QTVDecoderImpl), &CLSID_QTVDecoder, &QTVDecoder_FuncsTable, (IBaseFilter**)&This);
534
535     if (FAILED(hr))
536     {
537         *phr = hr;
538         return NULL;
539     }
540     else
541     {
542         ISeekingPassThru *passthru;
543         hr = CoCreateInstance(&CLSID_SeekingPassThru, (IUnknown*)This, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void**)&This->seekthru_unk);
544         IUnknown_QueryInterface(This->seekthru_unk, &IID_ISeekingPassThru, (void**)&passthru);
545         ISeekingPassThru_Init(passthru, FALSE, (IPin*)This->tf.ppPins[0]);
546         ISeekingPassThru_Release(passthru);
547     }
548
549     *phr = hr;
550     return (IUnknown*)This;
551 }
552
553 HRESULT WINAPI QTVDecoder_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
554 {
555     HRESULT hr;
556     QTVDecoderImpl *This = (QTVDecoderImpl *)iface;
557     TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
558
559     if (IsEqualIID(riid, &IID_IMediaSeeking))
560         return IUnknown_QueryInterface(This->seekthru_unk, riid, ppv);
561
562     hr = TransformFilterImpl_QueryInterface(iface, riid, ppv);
563
564     return hr;
565 }
566
567 static const IBaseFilterVtbl QTVDecoder_Vtbl =
568 {
569     QTVDecoder_QueryInterface,
570     BaseFilterImpl_AddRef,
571     TransformFilterImpl_Release,
572     BaseFilterImpl_GetClassID,
573     TransformFilterImpl_Stop,
574     TransformFilterImpl_Pause,
575     TransformFilterImpl_Run,
576     BaseFilterImpl_GetState,
577     BaseFilterImpl_SetSyncSource,
578     BaseFilterImpl_GetSyncSource,
579     BaseFilterImpl_EnumPins,
580     TransformFilterImpl_FindPin,
581     BaseFilterImpl_QueryFilterInfo,
582     BaseFilterImpl_JoinFilterGraph,
583     BaseFilterImpl_QueryVendorInfo
584 };