wineqtdecoder: Work to add a QuickTime video Splitter.
[wine] / dlls / wineqtdecoder / qtsplitter.c
1 /*
2  * QuickTime splitter + decoder
3  *
4  * Copyright 2011 Aric Stewart for 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/Movies.h>
64 #include <QuickTime/QuickTimeComponents.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 #include <assert.h>
110 #include <stdio.h>
111 #include <stdarg.h>
112
113 #define NONAMELESSSTRUCT
114 #define NONAMELESSUNION
115 #define COBJMACROS
116
117 #include "windef.h"
118 #include "winbase.h"
119 #include "wtypes.h"
120 #include "winuser.h"
121 #include "dshow.h"
122
123 #include <assert.h>
124
125 #include "wine/unicode.h"
126 #include "wine/debug.h"
127 #include "wine/strmbase.h"
128
129 #include "qtprivate.h"
130
131 WINE_DEFAULT_DEBUG_CHANNEL(qtsplitter);
132 extern CLSID CLSID_QTSplitter;
133
134 typedef struct QTOutPin {
135     BaseOutputPin pin;
136
137     AM_MEDIA_TYPE * pmt;
138     OutputQueue * queue;
139 } QTOutPin;
140
141 typedef struct QTInPin {
142     BasePin pin;
143     GUID subType;
144
145     IAsyncReader *pReader;
146     IMemAllocator *pAlloc;
147 } QTInPin;
148
149 typedef struct QTSplitter {
150     BaseFilter filter;
151
152     QTInPin pInputPin;
153     QTOutPin *pVideo_Pin;
154
155     ALLOCATOR_PROPERTIES props;
156
157     Movie pQTMovie;
158     QTVisualContextRef vContext;
159
160     DWORD outputSize;
161     FILTER_STATE state;
162 } QTSplitter;
163
164 static const IPinVtbl QT_OutputPin_Vtbl;
165 static const IPinVtbl QT_InputPin_Vtbl;
166 static const IBaseFilterVtbl QT_Vtbl;
167
168 static HRESULT QT_AddPin(QTSplitter *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt);
169 static HRESULT QT_RemoveOutputPins(QTSplitter *This);
170
171 /*
172  * Base Filter
173  */
174
175 static IPin* WINAPI QT_GetPin(BaseFilter *iface, int pos)
176 {
177     QTSplitter *This = (QTSplitter *)iface;
178     TRACE("Asking for pos %x\n", pos);
179
180     if (pos > 2 || pos < 0)
181         return NULL;
182     switch (pos)
183     {
184         case 0:
185             IPin_AddRef((IPin*)&This->pInputPin);
186             return (IPin*)&This->pInputPin;
187         case 1:
188             if (This->pVideo_Pin)
189                 IPin_AddRef((IPin*)This->pVideo_Pin);
190             return (IPin*)This->pVideo_Pin;
191         case 2: /* TODO: Audio */
192             return NULL;
193         default:
194             return NULL;
195     }
196 }
197
198 static LONG WINAPI QT_GetPinCount(BaseFilter *iface)
199 {
200     QTSplitter *This = (QTSplitter *)iface;
201     int c = 1;
202     if (This->pVideo_Pin) c++;
203     return c;
204 }
205
206 static const BaseFilterFuncTable BaseFuncTable = {
207     QT_GetPin,
208     QT_GetPinCount
209 };
210
211 IUnknown * CALLBACK QTSplitter_create(IUnknown *punkout, HRESULT *phr)
212 {
213     IUnknown *obj = NULL;
214     PIN_INFO *piInput;
215     QTSplitter *This;
216     static const WCHAR wcsInputPinName[] = {'I','n','p','u','t',' ','P','i','n',0};
217
218     EnterMovies();
219
220     RegisterWineDataHandler();
221
222     This = CoTaskMemAlloc(sizeof(*This));
223     obj = (IUnknown*)This;
224     if (!This)
225     {
226         *phr = E_OUTOFMEMORY;
227         return NULL;
228     }
229
230     BaseFilter_Init(&This->filter, &QT_Vtbl, &CLSID_QTSplitter, (DWORD_PTR)(__FILE__ ": QTSplitter.csFilter"), &BaseFuncTable);
231
232     This->pVideo_Pin = NULL;
233     This->state = State_Stopped;
234
235     piInput = &This->pInputPin.pin.pinInfo;
236     piInput->dir = PINDIR_INPUT;
237     piInput->pFilter = (IBaseFilter *)This;
238     lstrcpynW(piInput->achName, wcsInputPinName, sizeof(piInput->achName) / sizeof(piInput->achName[0]));
239     This->pInputPin.pin.lpVtbl = &QT_InputPin_Vtbl;
240     This->pInputPin.pin.refCount = 1;
241     This->pInputPin.pin.pConnectedTo = NULL;
242     This->pInputPin.pin.pCritSec = &This->filter.csFilter;
243     ZeroMemory(&This->pInputPin.pin.mtCurrent, sizeof(AM_MEDIA_TYPE));
244     *phr = S_OK;
245     return obj;
246 }
247
248 static void QT_Destroy(QTSplitter *This)
249 {
250     IPin *connected = NULL;
251     ULONG pinref;
252
253     TRACE("Destroying\n");
254
255     /* Don't need to clean up output pins, disconnecting input pin will do that */
256     IPin_ConnectedTo((IPin *)&This->pInputPin, &connected);
257     if (connected)
258     {
259         IPin_Disconnect(connected);
260         IPin_Release(connected);
261     }
262     pinref = IPin_Release((IPin *)&This->pInputPin);
263     if (pinref)
264     {
265         ERR("pinref should be null, is %u, destroying anyway\n", pinref);
266         assert((LONG)pinref > 0);
267
268         while (pinref)
269             pinref = IPin_Release((IPin *)&This->pInputPin);
270     }
271
272     if (This->pQTMovie)
273         DisposeMovie(This->pQTMovie);
274     if (This->vContext)
275         QTVisualContextRelease(This->vContext);
276
277     ExitMovies();
278     CoTaskMemFree(This);
279 }
280
281 static HRESULT WINAPI QT_QueryInterface(IBaseFilter *iface, REFIID riid, LPVOID *ppv)
282 {
283     QTSplitter *This = (QTSplitter *)iface;
284     TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
285
286     *ppv = NULL;
287
288     if (IsEqualIID(riid, &IID_IUnknown))
289         *ppv = This;
290     else if (IsEqualIID(riid, &IID_IPersist))
291         *ppv = This;
292     else if (IsEqualIID(riid, &IID_IMediaFilter))
293         *ppv = This;
294     else if (IsEqualIID(riid, &IID_IBaseFilter))
295         *ppv = This;
296
297     if (*ppv)
298     {
299         IUnknown_AddRef((IUnknown *)(*ppv));
300         return S_OK;
301     }
302
303     if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow))
304         FIXME("No interface for %s!\n", debugstr_guid(riid));
305
306     return E_NOINTERFACE;
307 }
308
309 static ULONG WINAPI QT_Release(IBaseFilter *iface)
310 {
311     QTSplitter *This = (QTSplitter *)iface;
312     ULONG refCount = BaseFilterImpl_Release(iface);
313
314     TRACE("(%p)->() Release from %d\n", This, refCount + 1);
315
316     if (!refCount)
317         QT_Destroy(This);
318
319     return refCount;
320 }
321
322 static HRESULT WINAPI QT_Stop(IBaseFilter *iface)
323 {
324     QTSplitter *This = (QTSplitter *)iface;
325
326     TRACE("()\n");
327
328     IAsyncReader_BeginFlush(This->pInputPin.pReader);
329     IAsyncReader_EndFlush(This->pInputPin.pReader);
330
331     return S_OK;
332 }
333
334 static HRESULT WINAPI QT_Pause(IBaseFilter *iface)
335 {
336     HRESULT hr = S_OK;
337     TRACE("()\n");
338
339     return hr;
340 }
341
342 static DWORD WINAPI QTSplitter_thread_reader(LPVOID data)
343 {
344     QTSplitter *This = (QTSplitter *)data;
345     HRESULT hr = S_OK;
346     TimeValue movie_time=0, next_time;
347     CVPixelBufferRef pixelBuffer = NULL;
348     OSStatus err;
349     TimeRecord tr;
350
351     This->state = State_Running;
352     GetMovieTime(This->pQTMovie, &tr);
353
354     do
355     {
356         LONGLONG tStart=0, tStop=0;
357         float time;
358
359         MoviesTask(This->pQTMovie,0);
360         QTVisualContextTask(This->vContext);
361         GetMovieNextInterestingTime(This->pQTMovie, nextTimeStep, 0, NULL, movie_time, 1, &next_time, NULL);
362
363         if (next_time == -1)
364         {
365             TRACE("No next time\n");
366             break;
367         }
368
369         tr.value = SInt64ToWide(next_time);
370         SetMovieTime(This->pQTMovie, &tr);
371         MoviesTask(This->pQTMovie,0);
372         QTVisualContextTask(This->vContext);
373
374         TRACE("In loop at time %ld\n",movie_time);
375         TRACE("In Next time %ld\n",next_time);
376
377         time = (float)movie_time / tr.scale;
378         tStart = time * 10000000;
379         time = (float)next_time / tr.scale;
380         tStop = time * 10000000;
381
382         /* Deliver Video */
383         if (QTVisualContextIsNewImageAvailable(This->vContext,0))
384         {
385             err = QTVisualContextCopyImageForTime(This->vContext, NULL, NULL, &pixelBuffer);
386             if (err == noErr)
387             {
388                 int data_size=0;
389                 BYTE* ptr;
390                 IMediaSample *sample = NULL;
391
392                 hr = BaseOutputPinImpl_GetDeliveryBuffer((BaseOutputPin*)This->pVideo_Pin, &sample, NULL, NULL, 0);
393                 if (FAILED(hr))
394                 {
395                     ERR("Video: Unable to get delivery buffer (%x)\n", hr);
396                     goto video_error;
397                 }
398
399                 data_size = IMediaSample_GetSize(sample);
400                 if (data_size < This->outputSize)
401                 {
402                     ERR("Sample size is too small %d < %d\n", data_size, This->outputSize)
403     ;
404                     hr = E_FAIL;
405                     goto video_error;
406                 }
407
408                 hr = IMediaSample_GetPointer(sample, &ptr);
409                 if (FAILED(hr))
410                 {
411                     ERR("Video: Unable to get pointer to buffer (%x)\n", hr);
412                     goto video_error;
413                 }
414
415                 hr = AccessPixelBufferPixels( pixelBuffer, ptr);
416                 if (FAILED(hr))
417                 {
418                     ERR("Failed to access Pixels\n");
419                     goto video_error;
420                 }
421
422                 IMediaSample_SetActualDataLength(sample, This->outputSize);
423
424                 IMediaSample_SetMediaTime(sample, &tStart, &tStop);
425                 if (tStart)
426                     IMediaSample_SetTime(sample, &tStart, &tStop);
427                 else
428                     IMediaSample_SetTime(sample, NULL, NULL);
429
430                 hr = OutputQueue_Receive(This->pVideo_Pin->queue, sample);
431                 TRACE("Video Delivered (%x)\n",hr);
432
433     video_error:
434                 if (sample)
435                     IMediaSample_Release(sample);
436                 if (pixelBuffer)
437                     CVPixelBufferRelease(pixelBuffer);
438             }
439         }
440
441         movie_time = next_time;
442     } while (hr == S_OK);
443
444     This->state = State_Stopped;
445     OutputQueue_EOS(This->pVideo_Pin->queue);
446
447     return hr;
448 }
449
450 static HRESULT WINAPI QT_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
451 {
452     HRESULT hr = S_OK;
453     QTSplitter *This = (QTSplitter *)iface;
454     HRESULT hr_any = VFW_E_NOT_CONNECTED;
455     DWORD tid;
456
457     TRACE("(%s)\n", wine_dbgstr_longlong(tStart));
458
459     EnterCriticalSection(&This->filter.csFilter);
460     This->filter.rtStreamStart = tStart;
461
462     if (This->pVideo_Pin)
463         hr = BaseOutputPinImpl_Active((BaseOutputPin *)This->pVideo_Pin);
464     if (SUCCEEDED(hr))
465         hr_any = hr;
466
467     hr = hr_any;
468     LeaveCriticalSection(&This->filter.csFilter);
469
470     CreateThread(NULL, 0, QTSplitter_thread_reader, This, 0, &tid);
471     TRACE("Created thread 0x%08x\n",tid);
472
473     return hr;
474 }
475
476 static HRESULT WINAPI QT_GetState(IBaseFilter *iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
477 {
478     QTSplitter *This = (QTSplitter *)iface;
479     TRACE("(%d, %p)\n", dwMilliSecsTimeout, pState);
480
481     *pState = This->state;
482
483     return S_OK;
484 }
485
486 static HRESULT WINAPI QT_FindPin(IBaseFilter *iface, LPCWSTR Id, IPin **ppPin)
487 {
488     FIXME("(%p)->(%s,%p) stub\n", iface, debugstr_w(Id), ppPin);
489     return E_NOTIMPL;
490 }
491
492 static const IBaseFilterVtbl QT_Vtbl = {
493     QT_QueryInterface,
494     BaseFilterImpl_AddRef,
495     QT_Release,
496     BaseFilterImpl_GetClassID,
497     QT_Stop,
498     QT_Pause,
499     QT_Run,
500     QT_GetState,
501     BaseFilterImpl_SetSyncSource,
502     BaseFilterImpl_GetSyncSource,
503     BaseFilterImpl_EnumPins,
504     QT_FindPin,
505     BaseFilterImpl_QueryFilterInfo,
506     BaseFilterImpl_JoinFilterGraph,
507     BaseFilterImpl_QueryVendorInfo
508 };
509
510 /*
511  * Input Pin
512  */
513 static HRESULT QT_RemoveOutputPins(QTSplitter *This)
514 {
515     HRESULT hr;
516     TRACE("(%p)\n", This);
517
518     if (This->pVideo_Pin)
519     {
520         hr = BaseOutputPinImpl_BreakConnect(&This->pVideo_Pin->pin);
521         TRACE("Disconnect: %08x\n", hr);
522         IPin_Release((IPin*)This->pVideo_Pin);
523         This->pVideo_Pin = NULL;
524     }
525
526     BaseFilterImpl_IncrementPinVersion((BaseFilter*)This);
527     return S_OK;
528 }
529
530 static ULONG WINAPI QTInPin_Release(IPin *iface)
531 {
532     QTInPin *This = (QTInPin*)iface;
533     ULONG refCount = InterlockedDecrement(&This->pin.refCount);
534
535     TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
536     if (!refCount)
537     {
538         FreeMediaType(&This->pin.mtCurrent);
539         if (This->pAlloc)
540             IMemAllocator_Release(This->pAlloc);
541         This->pAlloc = NULL;
542         This->pin.lpVtbl = NULL;
543         return 0;
544     }
545     else
546         return refCount;
547 }
548
549 static HRESULT QT_Process_Video_Track(QTSplitter* filter, Track trk)
550 {
551     AM_MEDIA_TYPE amt;
552     VIDEOINFOHEADER * pvi;
553     PIN_INFO piOutput;
554     HRESULT hr = S_OK;
555     OSErr err;
556     static const WCHAR szwVideoOut[] = {'V','i','d','e','o',0};
557     CFMutableDictionaryRef  pixelBufferOptions = NULL;
558     CFMutableDictionaryRef  visualContextOptions = NULL;
559     CFNumberRef n = NULL;
560     int t;
561     DWORD outputWidth, outputHeight, outputDepth;
562     Fixed trackWidth, trackHeight;
563
564     ZeroMemory(&amt, sizeof(amt));
565     amt.formattype = FORMAT_VideoInfo;
566     amt.majortype = MEDIATYPE_Video;
567     amt.subtype = MEDIASUBTYPE_RGB24;
568
569     GetTrackDimensions(trk, &trackWidth, &trackHeight);
570
571     outputDepth = 3;
572     outputWidth = Fix2Long(trackWidth);
573     outputHeight = Fix2Long(trackHeight);
574     TRACE("Width %i  Height %i\n",outputWidth, outputHeight);
575
576     amt.cbFormat = sizeof(VIDEOINFOHEADER);
577     amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
578     ZeroMemory(amt.pbFormat, amt.cbFormat);
579     pvi = (VIDEOINFOHEADER *)amt.pbFormat;
580     pvi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
581     pvi->bmiHeader.biWidth = outputWidth;
582     pvi->bmiHeader.biHeight = -outputHeight;
583     pvi->bmiHeader.biPlanes = 1;
584     pvi->bmiHeader.biBitCount = 24;
585     pvi->bmiHeader.biCompression = BI_RGB;
586
587     filter->outputSize = outputWidth * outputHeight * outputDepth;
588     amt.lSampleSize = 0;
589
590     pixelBufferOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
591
592     t = k32ARGBPixelFormat;
593     n = CFNumberCreate(NULL, kCFNumberIntType, &t);
594     CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferPixelFormatTypeKey, n);
595     CFRelease(n);
596
597     n = CFNumberCreate(NULL, kCFNumberIntType, &outputWidth);
598     CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferWidthKey, n);
599     CFRelease(n);
600
601     n = CFNumberCreate(NULL, kCFNumberIntType, &outputHeight);
602     CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferHeightKey, n);
603     CFRelease(n);
604
605     t = 16;
606     n = CFNumberCreate(NULL, kCFNumberIntType, &t);
607     CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferBytesPerRowAlignmentKey, n);
608     CFRelease(n);
609
610     visualContextOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
611
612     CFDictionarySetValue(visualContextOptions, kQTVisualContextPixelBufferAttributesKey, pixelBufferOptions);
613
614     err = QTPixelBufferContextCreate(NULL, visualContextOptions,&filter->vContext);
615     CFRelease(pixelBufferOptions);
616     CFRelease(visualContextOptions);
617     if (err != noErr)
618     {
619         ERR("Failed to create Visual Context\n");
620         return E_FAIL;
621     }
622
623     err = SetMovieVisualContext(filter->pQTMovie, filter->vContext);
624     if (err != noErr)
625     {
626         ERR("Failed to set Visual Context\n");
627         return E_FAIL;
628     }
629
630     piOutput.dir = PINDIR_OUTPUT;
631     piOutput.pFilter = (IBaseFilter *)filter;
632     lstrcpyW(piOutput.achName,szwVideoOut);
633
634     hr = QT_AddPin(filter, &piOutput, &amt);
635     if (FAILED(hr))
636         ERR("Failed to add Video Track\n");
637      else
638         TRACE("Video Pin %p\n",filter->pVideo_Pin);
639
640     return hr;
641 }
642
643 static HRESULT QT_Process_Movie(QTSplitter* filter)
644 {
645     HRESULT hr = S_OK;
646     OSErr err;
647     WineDataRefRecord ptrDataRefRec;
648     Handle dataRef = NULL;
649     Track trk;
650     short id = 0;
651
652     TRACE("Trying movie connect\n");
653
654     ptrDataRefRec.pReader = filter->pInputPin.pReader;
655     ptrDataRefRec.streamSubtype = filter->pInputPin.subType;
656     PtrToHand( &ptrDataRefRec, &dataRef, sizeof(WineDataRefRecord));
657
658     err = NewMovieFromDataRef(&filter->pQTMovie, newMovieActive|newMovieDontInteractWithUser|newMovieDontAutoUpdateClock|newMovieDontAskUnresolvedDataRefs|newMovieIdleImportOK, &id, dataRef, 'WINE');
659
660     if (err != noErr)
661     {
662         FIXME("Quicktime cannot handle media type(%i)\n",err);
663         return VFW_E_TYPE_NOT_ACCEPTED;
664     }
665
666     PrePrerollMovie(filter->pQTMovie, 0, fixed1, NULL, NULL);
667     PrerollMovie(filter->pQTMovie, 0, fixed1);
668     GoToBeginningOfMovie(filter->pQTMovie);
669     SetMovieActive(filter->pQTMovie,TRUE);
670
671     if (GetMovieLoadState(filter->pQTMovie) < kMovieLoadStateLoaded)
672         MoviesTask(filter->pQTMovie,100);
673
674     trk = GetMovieIndTrackType(filter->pQTMovie, 1, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
675     TRACE("%p is a video track\n",trk);
676     if (trk)
677        hr = QT_Process_Video_Track(filter, trk);
678
679     if (!SUCCEEDED(hr))
680         return hr;
681
682     return hr;
683 }
684
685 static HRESULT WINAPI QTInPin_ReceiveConnection(IPin *iface, IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
686 {
687     HRESULT hr = S_OK;
688     ALLOCATOR_PROPERTIES props;
689     QTInPin *This = (QTInPin*)iface;
690
691     TRACE("(%p/%p)->(%p, %p)\n", This, iface, pReceivePin, pmt);
692
693     EnterCriticalSection(This->pin.pCritSec);
694     This->pReader = NULL;
695
696     if (This->pin.pConnectedTo)
697         hr = VFW_E_ALREADY_CONNECTED;
698     else if (IPin_QueryAccept(iface, pmt) != S_OK)
699         hr = VFW_E_TYPE_NOT_ACCEPTED;
700     else
701     {
702         PIN_DIRECTION pindirReceive;
703         IPin_QueryDirection(pReceivePin, &pindirReceive);
704         if (pindirReceive != PINDIR_OUTPUT)
705             hr = VFW_E_INVALID_DIRECTION;
706     }
707
708     if (!SUCCEEDED(hr))
709     {
710         LeaveCriticalSection(This->pin.pCritSec);
711         return hr;
712     }
713
714     hr = IPin_QueryInterface(pReceivePin, &IID_IAsyncReader, (LPVOID *)&This->pReader);
715     if (!SUCCEEDED(hr))
716     {
717         LeaveCriticalSection(This->pin.pCritSec);
718         TRACE("Input source is not an AsyncReader\n");
719         return hr;
720     }
721
722     LeaveCriticalSection(This->pin.pCritSec);
723     EnterCriticalSection(&((QTSplitter *)This->pin.pinInfo.pFilter)->filter.csFilter);
724     hr = QT_Process_Movie((QTSplitter *)This->pin.pinInfo.pFilter);
725     if (!SUCCEEDED(hr))
726     {
727         LeaveCriticalSection(&((QTSplitter *)This->pin.pinInfo.pFilter)->filter.csFilter);
728         TRACE("Unable to process movie\n");
729         return hr;
730     }
731
732     This->pAlloc = NULL;
733     props.cBuffers = 8;
734     props.cbAlign = 1;
735     props.cbBuffer = ((QTSplitter *)This->pin.pinInfo.pFilter)->outputSize + props.cbAlign;
736     props.cbPrefix = 0;
737
738     hr = IAsyncReader_RequestAllocator(This->pReader, NULL, &props, &This->pAlloc);
739     if (SUCCEEDED(hr))
740     {
741         CopyMediaType(&This->pin.mtCurrent, pmt);
742         This->pin.pConnectedTo = pReceivePin;
743         IPin_AddRef(pReceivePin);
744         hr = IMemAllocator_Commit(This->pAlloc);
745     }
746     else
747     {
748         QT_RemoveOutputPins((QTSplitter *)This->pin.pinInfo.pFilter);
749         if (This->pReader)
750             IAsyncReader_Release(This->pReader);
751         This->pReader = NULL;
752         if (This->pAlloc)
753             IMemAllocator_Release(This->pAlloc);
754         This->pAlloc = NULL;
755     }
756     TRACE("Size: %i\n", props.cbBuffer);
757     LeaveCriticalSection(&((QTSplitter *)This->pin.pinInfo.pFilter)->filter.csFilter);
758
759     return hr;
760 }
761
762 static HRESULT WINAPI QTInPin_Disconnect(IPin *iface)
763 {
764     HRESULT hr;
765     QTInPin *This = (QTInPin*)iface;
766     FILTER_STATE state;
767     TRACE("()\n");
768
769     hr = IBaseFilter_GetState(This->pin.pinInfo.pFilter, INFINITE, &state);
770     EnterCriticalSection(This->pin.pCritSec);
771     if (This->pin.pConnectedTo)
772     {
773         QTSplitter *Parser = (QTSplitter *)This->pin.pinInfo.pFilter;
774
775         if (SUCCEEDED(hr) && state == State_Stopped)
776         {
777             IMemAllocator_Decommit(This->pAlloc);
778             IPin_Disconnect(This->pin.pConnectedTo);
779             This->pin.pConnectedTo = NULL;
780             hr = QT_RemoveOutputPins(Parser);
781         }
782         else
783             hr = VFW_E_NOT_STOPPED;
784     }
785     else
786         hr = S_FALSE;
787     LeaveCriticalSection(This->pin.pCritSec);
788     return hr;
789 }
790
791 static HRESULT WINAPI QTInPin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *pmt)
792 {
793     QTInPin *This = (QTInPin*)iface;
794
795     TRACE("(%p)->(%p)\n", This, pmt);
796
797     if (IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream))
798     {
799         This->subType = pmt->subtype;
800         return S_OK;
801     }
802     return S_FALSE;
803 }
804
805 static HRESULT WINAPI QTInPin_EndOfStream(IPin *iface)
806 {
807     QTInPin *pin = (QTInPin*)iface;
808     QTSplitter *This = (QTSplitter*)pin->pin.pinInfo.pFilter;
809
810     FIXME("Propagate message on %p\n", This);
811     return S_OK;
812 }
813
814 static HRESULT WINAPI QTInPin_BeginFlush(IPin *iface)
815 {
816     QTInPin *pin = (QTInPin*)iface;
817     QTSplitter *This = (QTSplitter*)pin->pin.pinInfo.pFilter;
818
819     FIXME("Propagate message on %p\n", This);
820     return S_OK;
821 }
822
823 static HRESULT WINAPI QTInPin_EndFlush(IPin *iface)
824 {
825     QTInPin *pin = (QTInPin*)iface;
826     QTSplitter *This = (QTSplitter*)pin->pin.pinInfo.pFilter;
827
828     FIXME("Propagate message on %p\n", This);
829     return S_OK;
830 }
831
832 static HRESULT WINAPI QTInPin_NewSegment(IPin *iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
833 {
834     QTInPin *pin = (QTInPin*)iface;
835     QTSplitter *This = (QTSplitter*)pin->pin.pinInfo.pFilter;
836
837     BasePinImpl_NewSegment(iface, tStart, tStop, dRate);
838     FIXME("Propagate message on %p\n", This);
839     return S_OK;
840 }
841
842 static HRESULT WINAPI QTInPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
843 {
844     QTInPin *This = (QTInPin*)iface;
845
846     TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
847
848     *ppv = NULL;
849
850     if (IsEqualIID(riid, &IID_IUnknown))
851         *ppv = iface;
852     else if (IsEqualIID(riid, &IID_IPin))
853         *ppv = iface;
854     else if (IsEqualIID(riid, &IID_IMediaSeeking))
855     {
856         return IBaseFilter_QueryInterface(This->pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
857     }
858
859     if (*ppv)
860     {
861         IUnknown_AddRef((IUnknown *)(*ppv));
862         return S_OK;
863     }
864
865     FIXME("No interface for %s!\n", debugstr_guid(riid));
866
867     return E_NOINTERFACE;
868 }
869
870 static HRESULT WINAPI QTInPin_EnumMediaTypes(IPin *iface, IEnumMediaTypes **ppEnum)
871 {
872     BasePin *This = (BasePin *)iface;
873
874     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
875
876     return EnumMediaTypes_Construct(This, BasePinImpl_GetMediaType, BasePinImpl_GetMediaTypeVersion, ppEnum);
877 }
878
879 static const IPinVtbl QT_InputPin_Vtbl = {
880     QTInPin_QueryInterface,
881     BasePinImpl_AddRef,
882     QTInPin_Release,
883     BaseInputPinImpl_Connect,
884     QTInPin_ReceiveConnection,
885     QTInPin_Disconnect,
886     BasePinImpl_ConnectedTo,
887     BasePinImpl_ConnectionMediaType,
888     BasePinImpl_QueryPinInfo,
889     BasePinImpl_QueryDirection,
890     BasePinImpl_QueryId,
891     QTInPin_QueryAccept,
892     QTInPin_EnumMediaTypes,
893     BasePinImpl_QueryInternalConnections,
894     QTInPin_EndOfStream,
895     QTInPin_BeginFlush,
896     QTInPin_EndFlush,
897     QTInPin_NewSegment
898 };
899
900 /*
901  * Output Pin
902  */
903
904 static HRESULT WINAPI QTOutPin_QueryInterface(IPin *iface, REFIID riid, void **ppv)
905 {
906     QTOutPin *This = (QTOutPin *)iface;
907
908     TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
909
910     *ppv = NULL;
911
912     if (IsEqualIID(riid, &IID_IUnknown))
913         *ppv = iface;
914     else if (IsEqualIID(riid, &IID_IPin))
915         *ppv = iface;
916     else if (IsEqualIID(riid, &IID_IMediaSeeking))
917         return IBaseFilter_QueryInterface(This->pin.pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
918
919     if (*ppv)
920     {
921         IUnknown_AddRef((IUnknown *)(*ppv));
922         return S_OK;
923     }
924     FIXME("No interface for %s!\n", debugstr_guid(riid));
925     return E_NOINTERFACE;
926 }
927
928 static ULONG WINAPI QTOutPin_Release(IPin *iface)
929 {
930     QTOutPin *This = (QTOutPin *)iface;
931     ULONG refCount = InterlockedDecrement(&This->pin.pin.refCount);
932     TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
933
934     if (!refCount)
935     {
936         DeleteMediaType(This->pmt);
937         FreeMediaType(&This->pin.pin.mtCurrent);
938         OutputQueue_Destroy(This->queue);
939         CoTaskMemFree(This);
940         return 0;
941     }
942     return refCount;
943 }
944
945 static HRESULT WINAPI QTOutPin_GetMediaType(BasePin *iface, int iPosition, AM_MEDIA_TYPE *pmt)
946 {
947     QTOutPin *This = (QTOutPin *)iface;
948
949     if (iPosition < 0)
950         return E_INVALIDARG;
951     if (iPosition > 0)
952         return VFW_S_NO_MORE_ITEMS;
953     CopyMediaType(pmt, This->pmt);
954     return S_OK;
955 }
956
957 static HRESULT WINAPI QTOutPin_DecideBufferSize(BaseOutputPin *iface, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
958 {
959     /* Unused */
960     return S_OK;
961 }
962
963 static HRESULT WINAPI QTOutPin_DecideAllocator(BaseOutputPin *iface, IMemInputPin *pPin, IMemAllocator **pAlloc)
964 {
965     HRESULT hr;
966     QTOutPin *This = (QTOutPin *)iface;
967     QTSplitter *QTfilter = (QTSplitter*)This->pin.pin.pinInfo.pFilter;
968
969     pAlloc = NULL;
970     if (QTfilter->pInputPin.pAlloc)
971         hr = IMemInputPin_NotifyAllocator(pPin, QTfilter->pInputPin.pAlloc, FALSE);
972     else
973         hr = VFW_E_NO_ALLOCATOR;
974
975     return hr;
976 }
977
978 static HRESULT WINAPI QTOutPin_BreakConnect(BaseOutputPin *This)
979 {
980     HRESULT hr;
981
982     TRACE("(%p)->()\n", This);
983
984     EnterCriticalSection(This->pin.pCritSec);
985     if (!This->pin.pConnectedTo || !This->pMemInputPin)
986         hr = VFW_E_NOT_CONNECTED;
987     else
988     {
989         hr = IPin_Disconnect(This->pin.pConnectedTo);
990         IPin_Disconnect((IPin *)This);
991     }
992     LeaveCriticalSection(This->pin.pCritSec);
993
994     return hr;
995 }
996
997 static const IPinVtbl QT_OutputPin_Vtbl = {
998     QTOutPin_QueryInterface,
999     BasePinImpl_AddRef,
1000     QTOutPin_Release,
1001     BaseOutputPinImpl_Connect,
1002     BaseOutputPinImpl_ReceiveConnection,
1003     BaseOutputPinImpl_Disconnect,
1004     BasePinImpl_ConnectedTo,
1005     BasePinImpl_ConnectionMediaType,
1006     BasePinImpl_QueryPinInfo,
1007     BasePinImpl_QueryDirection,
1008     BasePinImpl_QueryId,
1009     BasePinImpl_QueryAccept,
1010     BasePinImpl_EnumMediaTypes,
1011     BasePinImpl_QueryInternalConnections,
1012     BaseOutputPinImpl_EndOfStream,
1013     BaseOutputPinImpl_BeginFlush,
1014     BaseOutputPinImpl_EndFlush,
1015     BasePinImpl_NewSegment
1016 };
1017
1018 static const BasePinFuncTable output_BaseFuncTable = {
1019     NULL,
1020     BaseOutputPinImpl_AttemptConnection,
1021     BasePinImpl_GetMediaTypeVersion,
1022     QTOutPin_GetMediaType
1023 };
1024
1025 static const BaseOutputPinFuncTable output_BaseOutputFuncTable = {
1026     QTOutPin_DecideBufferSize,
1027     QTOutPin_DecideAllocator,
1028     QTOutPin_BreakConnect
1029 };
1030
1031 static const OutputQueueFuncTable output_OutputQueueFuncTable = {
1032     OutputQueueImpl_ThreadProc
1033 };
1034
1035 static HRESULT QT_AddPin(QTSplitter *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt)
1036 {
1037     HRESULT hr;
1038     IPin **target;
1039
1040     target = (IPin**)&This->pVideo_Pin;
1041
1042     if (*target != NULL)
1043     {
1044         ERR("We we already have a video pin\n");
1045         return E_FAIL;
1046     }
1047
1048     hr = BaseOutputPin_Construct(&QT_OutputPin_Vtbl, sizeof(QTOutPin), piOutput, &output_BaseFuncTable, &output_BaseOutputFuncTable, &This->filter.csFilter, (IPin**)target);
1049     if (SUCCEEDED(hr))
1050     {
1051         QTOutPin *pin = (QTOutPin*)*target;
1052         pin->pmt = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
1053         CopyMediaType(pin->pmt, amt);
1054         pin->pin.pin.pinInfo.pFilter = (LPVOID)This;
1055
1056         BaseFilterImpl_IncrementPinVersion((BaseFilter*)This);
1057
1058         hr = OutputQueue_Construct((BaseOutputPin*)pin, TRUE, TRUE, 5, FALSE, THREAD_PRIORITY_NORMAL, &output_OutputQueueFuncTable, &pin->queue);
1059     }
1060     else
1061         ERR("Failed with error %x\n", hr);
1062     return hr;
1063 }