gdiplus: Copy only the area requested when (un)locking bitmaps.
[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 extern CLSID CLSID_QTVDecoder;
131
132 WINE_DEFAULT_DEBUG_CHANNEL(qtdecoder);
133
134 typedef struct QTVDecoderImpl
135 {
136     TransformFilter tf;
137     IUnknown *seekthru_unk;
138
139     ImageDescriptionHandle hImageDescription;
140     CFMutableDictionaryRef outputBufferAttributes;
141     ICMDecompressionSessionRef decompressionSession;
142     HRESULT decodeHR;
143
144     DWORD outputSize;
145     DWORD outputWidth, outputHeight, outputDepth;
146
147 } QTVDecoderImpl;
148
149 typedef struct {
150       UInt8 a; /* Alpha Channel */
151       UInt8 r; /* red component */
152       UInt8 g; /* green component */
153       UInt8 b; /* blue component */
154 } ARGBPixelRecord, *ARGBPixelPtr, **ARGBPixelHdl;
155
156 static const IBaseFilterVtbl QTVDecoder_Vtbl;
157
158 static void trackingCallback(
159                     void *decompressionTrackingRefCon,
160                     OSStatus result,
161                     ICMDecompressionTrackingFlags decompressionTrackingFlags,
162                     CVPixelBufferRef pixelBuffer,
163                     TimeValue64 displayTime,
164                     TimeValue64 displayDuration,
165                     ICMValidTimeFlags validTimeFlags,
166                     void *reserved,
167                     void *sourceFrameRefCon )
168 {
169     QTVDecoderImpl *This = (QTVDecoderImpl*)decompressionTrackingRefCon;
170     IMediaSample *pSample = (IMediaSample*)sourceFrameRefCon;
171     HRESULT hr  = S_OK;
172     IMediaSample* pOutSample = NULL;
173     LPBYTE pbDstStream;
174     DWORD cbDstStream;
175     LPBYTE pPixels = NULL;
176     LPBYTE out = NULL;
177     size_t bytesPerRow = 0;
178
179     int i;
180
181     if (result != noErr)
182     {
183         ERR("Error from Codec, no frame decompressed\n");
184         return;
185     }
186
187     if (!pixelBuffer)
188     {
189         ERR("No pixel buffer, no frame decompressed\n");
190         return;
191     }
192
193     EnterCriticalSection(&This->tf.filter.csFilter);
194     hr = BaseOutputPinImpl_GetDeliveryBuffer((BaseOutputPin*)This->tf.ppPins[1], &pOutSample, NULL, NULL, 0);
195     if (FAILED(hr)) {
196         ERR("Unable to get delivery buffer (%x)\n", hr);
197         goto error;
198     }
199
200     hr = IMediaSample_SetActualDataLength(pOutSample, 0);
201     assert(hr == S_OK);
202
203     hr = IMediaSample_GetPointer(pOutSample, &pbDstStream);
204     if (FAILED(hr)) {
205         ERR("Unable to get pointer to buffer (%x)\n", hr);
206         goto error;
207     }
208
209     cbDstStream = IMediaSample_GetSize(pOutSample);
210     if (cbDstStream < This->outputSize) {
211         ERR("Sample size is too small %d < %d\n", cbDstStream, This->outputSize);
212         hr = E_FAIL;
213         goto error;
214     }
215
216     /* ACCESS THE PIXELS */
217     CVPixelBufferLockBaseAddress(pixelBuffer,0);
218     pPixels = (LPBYTE)CVPixelBufferGetBaseAddress(pixelBuffer);
219     bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer);
220
221     for (out = pbDstStream, i = 0; i < This->outputHeight; i++)
222     {
223         int j;
224         for (j = 0; j < This->outputWidth; j++)
225         {
226             *((DWORD*)out) = (((ARGBPixelPtr)pPixels)[j].r) << 16
227                           | (((ARGBPixelPtr)pPixels)[j].g) << 8
228                           | (((ARGBPixelPtr)pPixels)[j].b);
229             out+=This->outputDepth;
230         }
231         pPixels += bytesPerRow;
232     }
233     CVPixelBufferUnlockBaseAddress(pixelBuffer,0);
234
235     IMediaSample_SetActualDataLength(pOutSample, This->outputSize);
236
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));
240
241     if (!validTimeFlags)
242         IMediaSample_SetTime(pOutSample, NULL, NULL);
243     else
244     {
245         LONGLONG tStart, tStop;
246
247         if (validTimeFlags & kICMValidTime_DisplayTimeStampIsValid)
248             tStart = displayTime;
249          else
250             tStart = 0;
251         if (validTimeFlags & kICMValidTime_DisplayDurationIsValid)
252             tStop = tStart + displayDuration;
253         else
254             tStop = tStart;
255
256         IMediaSample_SetTime(pOutSample, &tStart, &tStop);
257     }
258
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);
263
264 error:
265     if (pOutSample)
266         IMediaSample_Release(pOutSample);
267
268     This->decodeHR = hr;
269 }
270
271 static HRESULT WINAPI QTVDecoder_StartStreaming(TransformFilter* pTransformFilter)
272 {
273     QTVDecoderImpl* This = (QTVDecoderImpl*)pTransformFilter;
274     OSErr err = noErr;
275     ICMDecompressionSessionOptionsRef sessionOptions = NULL;
276     ICMDecompressionTrackingCallbackRecord trackingCallbackRecord;
277
278     TRACE("(%p)->()\n", This);
279
280     trackingCallbackRecord.decompressionTrackingCallback = trackingCallback;
281     trackingCallbackRecord.decompressionTrackingRefCon = (void*)This;
282
283     err = ICMDecompressionSessionCreate(NULL, This->hImageDescription, sessionOptions, This->outputBufferAttributes, &trackingCallbackRecord, &This->decompressionSession);
284
285     if (err != noErr)
286     {
287         ERR("Error with ICMDecompressionSessionCreate %i\n",err);
288         return E_FAIL;
289     }
290
291     return S_OK;
292 }
293
294 static HRESULT WINAPI QTVDecoder_Receive(TransformFilter *tf, IMediaSample *pSample)
295 {
296     QTVDecoderImpl* This = (QTVDecoderImpl *)tf;
297     HRESULT hr;
298     DWORD cbSrcStream;
299     LPBYTE pbSrcStream;
300
301     ICMFrameTimeRecord frameTime = {{0}};
302     TimeValue time = 1;
303     TimeScale timeScale = 1;
304     OSStatus err = noErr;
305     LONGLONG tStart, tStop;
306
307     EnterCriticalSection(&This->tf.filter.csFilter);
308     hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
309     if (FAILED(hr))
310     {
311         ERR("Cannot get pointer to sample data (%x)\n", hr);
312         goto error;
313     }
314
315     cbSrcStream = IMediaSample_GetActualDataLength(pSample);
316
317     if (IMediaSample_GetTime(pSample, &tStart, &tStop) != S_OK)
318         tStart = tStop = 0;
319
320     time = tStart;
321
322     frameTime.recordSize = sizeof(ICMFrameTimeRecord);
323     *(TimeValue64 *)&frameTime.value = tStart;
324     frameTime.scale = 1;
325     frameTime.rate = fixed1;
326     frameTime.duration = tStop - tStart;
327     frameTime.frameNumber = 0;
328     frameTime.flags = icmFrameTimeIsNonScheduledDisplayTime;
329
330     err = ICMDecompressionSessionDecodeFrame(This->decompressionSession,
331             (UInt8 *)pbSrcStream, cbSrcStream, NULL, &frameTime, pSample);
332
333     if (err != noErr)
334     {
335         ERR("Error with ICMDecompressionSessionDecodeFrame\n");
336         hr = E_FAIL;
337         goto error;
338     }
339
340     ICMDecompressionSessionSetNonScheduledDisplayTime(This->decompressionSession, time, timeScale, 0);
341     ICMDecompressionSessionFlush(This->decompressionSession);
342     hr = This->decodeHR;
343
344 error:
345     LeaveCriticalSection(&This->tf.filter.csFilter);
346     return hr;
347 }
348
349 static HRESULT WINAPI QTVDecoder_StopStreaming(TransformFilter* pTransformFilter)
350 {
351     QTVDecoderImpl* This = (QTVDecoderImpl*)pTransformFilter;
352
353     TRACE("(%p)->()\n", This);
354
355     if (This->decompressionSession)
356         ICMDecompressionSessionRelease(This->decompressionSession);
357     This->decompressionSession = NULL;
358
359     return S_OK;
360 }
361
362 static HRESULT WINAPI QTVDecoder_SetMediaType(TransformFilter *tf, PIN_DIRECTION dir, const AM_MEDIA_TYPE * pmt)
363 {
364     QTVDecoderImpl* This = (QTVDecoderImpl*)tf;
365     HRESULT hr = VFW_E_TYPE_NOT_ACCEPTED;
366     OSErr err = noErr;
367     AM_MEDIA_TYPE *outpmt = &This->tf.pmt;
368     CFNumberRef n = NULL;
369
370     TRACE("(%p)->(%p)\n", This, pmt);
371
372     if (dir != PINDIR_INPUT)
373         return S_OK;
374
375     FreeMediaType(outpmt);
376     CopyMediaType(outpmt, pmt);
377
378     if (This->hImageDescription)
379         DisposeHandle((Handle)This->hImageDescription);
380
381     This->hImageDescription = (ImageDescriptionHandle)
382         NewHandleClear(sizeof(ImageDescription));
383
384     if (This->hImageDescription != NULL)
385     {
386         (**This->hImageDescription).idSize = sizeof(ImageDescription);
387         (**This->hImageDescription).spatialQuality = codecNormalQuality;
388         (**This->hImageDescription).frameCount = 1;
389         (**This->hImageDescription).clutID = -1;
390     }
391     else
392     {
393         ERR("Failed to create ImageDescription\n");
394         goto failed;
395     }
396
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)))
400     {
401         VIDEOINFOHEADER *format1 = (VIDEOINFOHEADER *)outpmt->pbFormat;
402         VIDEOINFOHEADER2 *format2 = (VIDEOINFOHEADER2 *)outpmt->pbFormat;
403         BITMAPINFOHEADER *bmi;
404         OSType fourCC;
405         DecompressorComponent dc;
406         OSType format;
407
408         if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo))
409             bmi = &format1->bmiHeader;
410         else if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo2))
411             bmi = &format2->bmiHeader;
412         else
413             goto failed;
414
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);
420
421         err = FindCodec(fourCC,NULL,NULL,&dc);
422         if (err != noErr || dc == 0x0)
423         {
424             TRACE("Codec not found\n");
425             goto failed;
426         }
427
428         This->outputWidth = bmi->biWidth;
429         This->outputHeight = bmi->biHeight;
430
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;
437
438         if (This->outputBufferAttributes)
439             CFRelease(This->outputBufferAttributes);
440
441         This->outputBufferAttributes = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
442         if (!This->outputBufferAttributes)
443         {
444             ERR("Failed to create outputBufferAttributes\n");
445             goto failed;
446         }
447
448         n = CFNumberCreate(NULL, kCFNumberIntType, &This->outputWidth);
449         CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferWidthKey, n);
450         CFRelease(n);
451
452         n = CFNumberCreate(NULL, kCFNumberIntType, &This->outputHeight);
453         CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferHeightKey, n);
454         CFRelease(n);
455
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);
460         CFRelease(n);
461
462         CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferCGBitmapContextCompatibilityKey, kCFBooleanTrue);
463         CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferCGImageCompatibilityKey, kCFBooleanTrue);
464
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;
472
473         return S_OK;
474     }
475
476 failed:
477     if (This->hImageDescription)
478     {
479         DisposeHandle((Handle)This->hImageDescription);
480         This->hImageDescription =  NULL;
481     }
482     if (This->outputBufferAttributes)
483     {
484         CFRelease(This->outputBufferAttributes);
485         This->outputBufferAttributes = NULL;
486     }
487
488     TRACE("Connection refused\n");
489     return hr;
490 }
491
492 static HRESULT WINAPI QTVDecoder_BreakConnect(TransformFilter *tf, PIN_DIRECTION dir)
493 {
494     QTVDecoderImpl *This = (QTVDecoderImpl *)tf;
495
496     TRACE("(%p)->()\n", This);
497
498     if (This->hImageDescription)
499         DisposeHandle((Handle)This->hImageDescription);
500     if (This->outputBufferAttributes)
501         CFRelease(This->outputBufferAttributes);
502
503     This->hImageDescription = NULL;
504     This->outputBufferAttributes = NULL;
505
506     return S_OK;
507 }
508
509 static HRESULT WINAPI QTVDecoder_DecideBufferSize(TransformFilter *tf, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
510 {
511     QTVDecoderImpl *This = (QTVDecoderImpl*)tf;
512     ALLOCATOR_PROPERTIES actual;
513
514     TRACE("()\n");
515
516     if (!ppropInputRequest->cbAlign)
517         ppropInputRequest->cbAlign = 1;
518
519     if (ppropInputRequest->cbBuffer < This->outputSize)
520             ppropInputRequest->cbBuffer = This->outputSize;
521
522     if (!ppropInputRequest->cBuffers)
523         ppropInputRequest->cBuffers = 1;
524
525     return IMemAllocator_SetProperties(pAlloc, ppropInputRequest, &actual);
526 }
527
528 static const TransformFilterFuncTable QTVDecoder_FuncsTable = {
529     QTVDecoder_DecideBufferSize,
530     QTVDecoder_StartStreaming,
531     QTVDecoder_Receive,
532     QTVDecoder_StopStreaming,
533     NULL,
534     QTVDecoder_SetMediaType,
535     NULL,
536     QTVDecoder_BreakConnect,
537     NULL,
538     NULL,
539     NULL,
540     NULL
541 };
542
543 IUnknown * CALLBACK QTVDecoder_create(IUnknown * pUnkOuter, HRESULT* phr)
544 {
545     HRESULT hr;
546     QTVDecoderImpl * This;
547
548     TRACE("(%p, %p)\n", pUnkOuter, phr);
549
550     *phr = S_OK;
551
552     if (pUnkOuter)
553     {
554         *phr = CLASS_E_NOAGGREGATION;
555         return NULL;
556     }
557
558     hr = TransformFilter_Construct(&QTVDecoder_Vtbl, sizeof(QTVDecoderImpl), &CLSID_QTVDecoder, &QTVDecoder_FuncsTable, (IBaseFilter**)&This);
559
560     if (FAILED(hr))
561     {
562         *phr = hr;
563         return NULL;
564     }
565     else
566     {
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);
572     }
573
574     *phr = hr;
575     return (IUnknown*)This;
576 }
577
578 HRESULT WINAPI QTVDecoder_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
579 {
580     HRESULT hr;
581     QTVDecoderImpl *This = (QTVDecoderImpl *)iface;
582     TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
583
584     if (IsEqualIID(riid, &IID_IMediaSeeking))
585         return IUnknown_QueryInterface(This->seekthru_unk, riid, ppv);
586
587     hr = TransformFilterImpl_QueryInterface(iface, riid, ppv);
588
589     return hr;
590 }
591
592 static const IBaseFilterVtbl QTVDecoder_Vtbl =
593 {
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
609 };