mstask: Actually run a test and fix the expected behaviour.
[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     IQualityControl IQualityControl_iface;
137
138     AM_MEDIA_TYPE * pmt;
139     OutputQueue * queue;
140 } QTOutPin;
141
142 typedef struct QTInPin {
143     BasePin pin;
144     GUID subType;
145
146     IAsyncReader *pReader;
147     IMemAllocator *pAlloc;
148 } QTInPin;
149
150 typedef struct QTSplitter {
151     BaseFilter filter;
152
153     QTInPin pInputPin;
154     QTOutPin *pVideo_Pin;
155     QTOutPin *pAudio_Pin;
156
157     ALLOCATOR_PROPERTIES props;
158
159     Movie pQTMovie;
160     QTVisualContextRef vContext;
161
162     MovieAudioExtractionRef aSession;
163     HANDLE runEvent;
164
165     DWORD outputSize;
166     FILTER_STATE state;
167     CRITICAL_SECTION csReceive;
168
169     SourceSeeking sourceSeeking;
170     TimeValue movie_time;
171     TimeValue movie_start;
172     TimeScale movie_scale;
173 } QTSplitter;
174
175 static const IPinVtbl QT_OutputPin_Vtbl;
176 static const IPinVtbl QT_InputPin_Vtbl;
177 static const IBaseFilterVtbl QT_Vtbl;
178 static const IMediaSeekingVtbl QT_Seeking_Vtbl;
179
180 static HRESULT QT_AddPin(QTSplitter *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt, BOOL video);
181 static HRESULT QT_RemoveOutputPins(QTSplitter *This);
182
183 static HRESULT WINAPI QTSplitter_ChangeStart(IMediaSeeking *iface);
184 static HRESULT WINAPI QTSplitter_ChangeStop(IMediaSeeking *iface);
185 static HRESULT WINAPI QTSplitter_ChangeRate(IMediaSeeking *iface);
186
187 static inline QTSplitter *impl_from_IMediaSeeking( IMediaSeeking *iface )
188 {
189     return CONTAINING_RECORD(iface, QTSplitter, sourceSeeking.IMediaSeeking_iface);
190 }
191
192 static inline QTSplitter *impl_from_BaseFilter( BaseFilter *iface )
193 {
194     return CONTAINING_RECORD(iface, QTSplitter, filter);
195 }
196
197 static inline QTSplitter *impl_from_IBaseFilter( IBaseFilter *iface )
198 {
199     return CONTAINING_RECORD(iface, QTSplitter, filter.IBaseFilter_iface);
200 }
201
202 /*
203  * Base Filter
204  */
205
206 static IPin* WINAPI QT_GetPin(BaseFilter *iface, int pos)
207 {
208     QTSplitter *This = impl_from_BaseFilter(iface);
209     TRACE("Asking for pos %x\n", pos);
210
211     if (pos > 2 || pos < 0)
212         return NULL;
213     switch (pos)
214     {
215         case 0:
216             IPin_AddRef(&This->pInputPin.pin.IPin_iface);
217             return &This->pInputPin.pin.IPin_iface;
218         case 1:
219             if (This->pVideo_Pin)
220                 IPin_AddRef(&This->pVideo_Pin->pin.pin.IPin_iface);
221             return &This->pVideo_Pin->pin.pin.IPin_iface;
222         case 2:
223             if (This->pAudio_Pin)
224                 IPin_AddRef(&This->pAudio_Pin->pin.pin.IPin_iface);
225             return &This->pAudio_Pin->pin.pin.IPin_iface;
226         default:
227             return NULL;
228     }
229 }
230
231 static LONG WINAPI QT_GetPinCount(BaseFilter *iface)
232 {
233     QTSplitter *This = impl_from_BaseFilter(iface);
234     int c = 1;
235     if (This->pAudio_Pin) c++;
236     if (This->pVideo_Pin) c++;
237     return c;
238 }
239
240 static const BaseFilterFuncTable BaseFuncTable = {
241     QT_GetPin,
242     QT_GetPinCount
243 };
244
245 IUnknown * CALLBACK QTSplitter_create(IUnknown *punkout, HRESULT *phr)
246 {
247     IUnknown *obj = NULL;
248     PIN_INFO *piInput;
249     QTSplitter *This;
250     static const WCHAR wcsInputPinName[] = {'I','n','p','u','t',' ','P','i','n',0};
251
252     EnterMovies();
253
254     RegisterWineDataHandler();
255
256     This = CoTaskMemAlloc(sizeof(*This));
257     obj = (IUnknown*)This;
258     if (!This)
259     {
260         *phr = E_OUTOFMEMORY;
261         return NULL;
262     }
263     ZeroMemory(This,sizeof(*This));
264
265     BaseFilter_Init(&This->filter, &QT_Vtbl, &CLSID_QTSplitter, (DWORD_PTR)(__FILE__ ": QTSplitter.csFilter"), &BaseFuncTable);
266
267     InitializeCriticalSection(&This->csReceive);
268     This->csReceive.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__": QTSplitter.csReceive");
269
270     This->pVideo_Pin = NULL;
271     This->pAudio_Pin = NULL;
272     This->state = State_Stopped;
273     This->aSession = NULL;
274     This->runEvent = CreateEventW(NULL, 0, 0, NULL);
275
276     piInput = &This->pInputPin.pin.pinInfo;
277     piInput->dir = PINDIR_INPUT;
278     piInput->pFilter = &This->filter.IBaseFilter_iface;
279     lstrcpynW(piInput->achName, wcsInputPinName, sizeof(piInput->achName) / sizeof(piInput->achName[0]));
280     This->pInputPin.pin.IPin_iface.lpVtbl = &QT_InputPin_Vtbl;
281     This->pInputPin.pin.refCount = 1;
282     This->pInputPin.pin.pConnectedTo = NULL;
283     This->pInputPin.pin.pCritSec = &This->filter.csFilter;
284
285     SourceSeeking_Init(&This->sourceSeeking, &QT_Seeking_Vtbl, QTSplitter_ChangeStop, QTSplitter_ChangeStart, QTSplitter_ChangeRate,  &This->filter.csFilter);
286
287     *phr = S_OK;
288     return obj;
289 }
290
291 static void QT_Destroy(QTSplitter *This)
292 {
293     IPin *connected = NULL;
294     ULONG pinref;
295
296     TRACE("Destroying\n");
297
298     /* Don't need to clean up output pins, disconnecting input pin will do that */
299     IPin_ConnectedTo(&This->pInputPin.pin.IPin_iface, &connected);
300     if (connected)
301     {
302         IPin_Disconnect(connected);
303         IPin_Release(connected);
304     }
305     pinref = IPin_Release(&This->pInputPin.pin.IPin_iface);
306     if (pinref)
307     {
308         ERR("pinref should be null, is %u, destroying anyway\n", pinref);
309         assert((LONG)pinref > 0);
310
311         while (pinref)
312             pinref = IPin_Release(&This->pInputPin.pin.IPin_iface);
313     }
314
315     if (This->pQTMovie)
316         DisposeMovie(This->pQTMovie);
317     if (This->vContext)
318         QTVisualContextRelease(This->vContext);
319     if (This->aSession)
320         MovieAudioExtractionEnd(This->aSession);
321     CloseHandle(This->runEvent);
322
323     ExitMovies();
324
325     This->csReceive.DebugInfo->Spare[0] = 0;
326     DeleteCriticalSection(&This->csReceive);
327
328     CoTaskMemFree(This);
329 }
330
331 static HRESULT WINAPI QT_QueryInterface(IBaseFilter *iface, REFIID riid, LPVOID *ppv)
332 {
333     QTSplitter *This = impl_from_IBaseFilter(iface);
334     TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
335
336     *ppv = NULL;
337
338     if (IsEqualIID(riid, &IID_IUnknown))
339         *ppv = This;
340     else if (IsEqualIID(riid, &IID_IPersist))
341         *ppv = This;
342     else if (IsEqualIID(riid, &IID_IMediaFilter))
343         *ppv = This;
344     else if (IsEqualIID(riid, &IID_IBaseFilter))
345         *ppv = This;
346     else if (IsEqualIID(riid, &IID_IMediaSeeking))
347         *ppv = &This->sourceSeeking;
348
349     if (*ppv)
350     {
351         IUnknown_AddRef((IUnknown *)(*ppv));
352         return S_OK;
353     }
354
355     if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow) &&
356         !IsEqualIID(riid, &IID_IAMFilterMiscFlags))
357         FIXME("No interface for %s!\n", debugstr_guid(riid));
358
359     return E_NOINTERFACE;
360 }
361
362 static ULONG WINAPI QT_Release(IBaseFilter *iface)
363 {
364     QTSplitter *This = impl_from_IBaseFilter(iface);
365     ULONG refCount = BaseFilterImpl_Release(iface);
366
367     TRACE("(%p)->() Release from %d\n", This, refCount + 1);
368
369     if (!refCount)
370         QT_Destroy(This);
371
372     return refCount;
373 }
374
375 static HRESULT WINAPI QT_Stop(IBaseFilter *iface)
376 {
377     QTSplitter *This = impl_from_IBaseFilter(iface);
378
379     TRACE("()\n");
380
381     EnterCriticalSection(&This->csReceive);
382     IAsyncReader_BeginFlush(This->pInputPin.pReader);
383     IAsyncReader_EndFlush(This->pInputPin.pReader);
384     LeaveCriticalSection(&This->csReceive);
385
386     return S_OK;
387 }
388
389 static HRESULT WINAPI QT_Pause(IBaseFilter *iface)
390 {
391     HRESULT hr = S_OK;
392     TRACE("()\n");
393
394     return hr;
395 }
396
397 static OSErr QT_Create_Extract_Session(QTSplitter *filter)
398 {
399     AudioStreamBasicDescription aDesc;
400     OSErr err;
401     WAVEFORMATEX* pvi;
402
403     pvi = (WAVEFORMATEX*)filter->pAudio_Pin->pmt->pbFormat;
404
405     err = MovieAudioExtractionBegin(filter->pQTMovie, 0, &filter->aSession);
406     if (err != noErr)
407     {
408         ERR("Failed to begin Extraction session %i\n",err);
409         return err;
410     }
411
412     err = MovieAudioExtractionGetProperty(filter->aSession,
413             kQTPropertyClass_MovieAudioExtraction_Audio,
414 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
415             sizeof(AudioStreamBasicDescription), &aDesc, NULL);
416
417     if (err != noErr)
418     {
419         MovieAudioExtractionEnd(filter->aSession);
420         filter->aSession = NULL;
421         ERR("Failed to get session description %i\n",err);
422         return err;
423     }
424
425     aDesc.mFormatID = kAudioFormatLinearPCM;
426     aDesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger +
427         kAudioFormatFlagIsPacked;
428     aDesc.mFramesPerPacket = 1;
429     aDesc.mChannelsPerFrame = pvi->nChannels;
430     aDesc.mBitsPerChannel = pvi->wBitsPerSample;
431     aDesc.mSampleRate = pvi->nSamplesPerSec;
432     aDesc.mBytesPerFrame = (aDesc.mBitsPerChannel * aDesc.mChannelsPerFrame) / 8;
433     aDesc.mBytesPerPacket = aDesc.mBytesPerFrame * aDesc.mFramesPerPacket;
434
435     err = MovieAudioExtractionSetProperty(filter->aSession,
436         kQTPropertyClass_MovieAudioExtraction_Audio,
437 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
438         sizeof(AudioStreamBasicDescription), &aDesc);
439
440     if (aDesc.mFormatID != kAudioFormatLinearPCM)
441     {
442         ERR("Not PCM Wave\n");
443         err = -1;
444     }
445     if (aDesc.mFormatFlags != kLinearPCMFormatFlagIsSignedInteger +
446         kAudioFormatFlagIsPacked)
447     {
448         ERR("Unhandled Flags\n");
449         err = -1;
450     }
451     if (aDesc.mFramesPerPacket != 1)
452     {
453         ERR("Unhandled Frames per packet %li\n",aDesc.mFramesPerPacket);
454         err = -1;
455     }
456     if (aDesc.mChannelsPerFrame != pvi->nChannels)
457     {
458         ERR("Unhandled channel count %li\n",aDesc.mChannelsPerFrame);
459         err = -1;
460     }
461     if (aDesc.mBitsPerChannel != pvi->wBitsPerSample)
462     {
463         ERR("Unhandled bits per channel %li\n",aDesc.mBitsPerChannel);
464         err = -1;
465     }
466     if (aDesc.mSampleRate != pvi->nSamplesPerSec)
467     {
468         ERR("Unhandled sample rate %f\n",aDesc.mSampleRate);
469         err = -1;
470     }
471
472     if (err != noErr)
473     {
474         ERR("Failed to create Extraction Session\n");
475         MovieAudioExtractionEnd(filter->aSession);
476         filter->aSession = NULL;
477     }
478
479     return err;
480 }
481
482 static DWORD WINAPI QTSplitter_thread(LPVOID data)
483 {
484     QTSplitter *This = (QTSplitter *)data;
485     HRESULT hr = S_OK;
486     TimeValue next_time;
487     CVPixelBufferRef pixelBuffer = NULL;
488     OSStatus err;
489     TimeRecord tr;
490
491     if (This->pAudio_Pin)
492     {
493         /* according to QA1469 a movie has to be fully loaded before we
494            can reliably start the Extraction session */
495
496         while(GetMovieLoadState(This->pQTMovie) < kMovieLoadStateComplete)
497             MoviesTask(This->pQTMovie,1000);
498
499         QT_Create_Extract_Session(This);
500     }
501
502     WaitForSingleObject(This->runEvent, -1);
503
504     EnterCriticalSection(&This->csReceive);
505     This->state = State_Running;
506     /* Prime the pump:  Needed for MPEG streams */
507     GetMovieNextInterestingTime(This->pQTMovie, nextTimeEdgeOK | nextTimeStep, 0, NULL, This->movie_time, 1, &next_time, NULL);
508
509     GetMovieTime(This->pQTMovie, &tr);
510     LeaveCriticalSection(&This->csReceive);
511     do
512     {
513         LONGLONG tStart=0, tStop=0;
514         LONGLONG mStart=0, mStop=0;
515         float time;
516
517         EnterCriticalSection(&This->csReceive);
518         GetMovieNextInterestingTime(This->pQTMovie, nextTimeStep, 0, NULL, This->movie_time, 1, &next_time, NULL);
519
520         if (next_time == -1)
521         {
522             TRACE("No next time\n");
523             LeaveCriticalSection(&This->csReceive);
524             break;
525         }
526
527         tr.value = SInt64ToWide(next_time);
528         SetMovieTime(This->pQTMovie, &tr);
529         MoviesTask(This->pQTMovie,0);
530         QTVisualContextTask(This->vContext);
531
532         TRACE("In loop at time %ld\n",This->movie_time);
533         TRACE("In Next time %ld\n",next_time);
534
535         mStart = This->movie_time;
536         mStop = next_time;
537
538         time = (float)(This->movie_time - This->movie_start) / This->movie_scale;
539         tStart = time * 10000000;
540         time = (float)(next_time - This->movie_start) / This->movie_scale;
541         tStop = time * 10000000;
542
543         /* Deliver Audio */
544         if (This->pAudio_Pin && This->pAudio_Pin->pin.pin.pConnectedTo && This->aSession)
545         {
546             int data_size=0;
547             BYTE* ptr;
548             IMediaSample *sample = NULL;
549             AudioBufferList aData;
550             UInt32 flags;
551             UInt32 frames;
552             WAVEFORMATEX* pvi;
553             float duration;
554
555             pvi = (WAVEFORMATEX*)This->pAudio_Pin->pmt->pbFormat;
556
557             hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->pAudio_Pin->pin, &sample, NULL, NULL, 0);
558
559             if (FAILED(hr))
560             {
561                 ERR("Audio: Unable to get delivery buffer (%x)\n", hr);
562                 goto audio_error;
563             }
564
565             hr = IMediaSample_GetPointer(sample, &ptr);
566             if (FAILED(hr))
567             {
568                 ERR("Audio: Unable to get pointer to buffer (%x)\n", hr);
569                 goto audio_error;
570             }
571
572             duration = (float)next_time / This->movie_scale;
573             time = (float)This->movie_time / This->movie_scale;
574             duration -= time;
575             frames = pvi->nSamplesPerSec * duration;
576             TRACE("Need audio for %f seconds (%li frames)\n",duration,frames);
577
578             data_size = IMediaSample_GetSize(sample);
579             if (data_size < frames * pvi->nBlockAlign)
580                 FIXME("Audio buffer is too small\n");
581
582             aData.mNumberBuffers = 1;
583             aData.mBuffers[0].mNumberChannels = pvi->nChannels;
584             aData.mBuffers[0].mDataByteSize = data_size;
585             aData.mBuffers[0].mData = ptr;
586
587             err = MovieAudioExtractionFillBuffer(This->aSession, &frames, &aData, &flags);
588             TRACE("Got %i frames\n",(int)frames);
589
590             IMediaSample_SetActualDataLength(sample, frames * pvi->nBlockAlign);
591
592             IMediaSample_SetMediaTime(sample, &mStart, &mStop);
593             IMediaSample_SetTime(sample, &tStart, &tStop);
594
595             hr = OutputQueue_Receive(This->pAudio_Pin->queue, sample);
596             TRACE("Audio Delivered (%x)\n",hr);
597
598 audio_error:
599             if (sample)
600                 IMediaSample_Release(sample);
601         }
602         else
603             TRACE("Audio Pin not connected or no Audio\n");
604
605         /* Deliver Video */
606         if (This->pVideo_Pin && QTVisualContextIsNewImageAvailable(This->vContext,0))
607         {
608             err = QTVisualContextCopyImageForTime(This->vContext, NULL, NULL, &pixelBuffer);
609             if (err == noErr)
610             {
611                 int data_size=0;
612                 BYTE* ptr;
613                 IMediaSample *sample = NULL;
614
615                 hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->pVideo_Pin->pin, &sample, NULL, NULL, 0);
616                 if (FAILED(hr))
617                 {
618                     ERR("Video: Unable to get delivery buffer (%x)\n", hr);
619                     goto video_error;
620                 }
621
622                 data_size = IMediaSample_GetSize(sample);
623                 if (data_size < This->outputSize)
624                 {
625                     ERR("Sample size is too small %d < %d\n", data_size, This->outputSize)
626     ;
627                     hr = E_FAIL;
628                     goto video_error;
629                 }
630
631                 hr = IMediaSample_GetPointer(sample, &ptr);
632                 if (FAILED(hr))
633                 {
634                     ERR("Video: Unable to get pointer to buffer (%x)\n", hr);
635                     goto video_error;
636                 }
637
638                 hr = AccessPixelBufferPixels( pixelBuffer, ptr);
639                 if (FAILED(hr))
640                 {
641                     ERR("Failed to access Pixels\n");
642                     goto video_error;
643                 }
644
645                 IMediaSample_SetActualDataLength(sample, This->outputSize);
646
647                 IMediaSample_SetMediaTime(sample, &mStart, &mStop);
648                 IMediaSample_SetTime(sample, &tStart, &tStop);
649
650                 hr = OutputQueue_Receive(This->pVideo_Pin->queue, sample);
651                 TRACE("Video Delivered (%x)\n",hr);
652
653     video_error:
654                 if (sample)
655                     IMediaSample_Release(sample);
656                 if (pixelBuffer)
657                     CVPixelBufferRelease(pixelBuffer);
658             }
659         }
660         else
661             TRACE("No video to deliver\n");
662
663         This->movie_time = next_time;
664         LeaveCriticalSection(&This->csReceive);
665     } while (hr == S_OK);
666
667     This->state = State_Stopped;
668     if (This->pAudio_Pin)
669         OutputQueue_EOS(This->pAudio_Pin->queue);
670     if (This->pVideo_Pin)
671         OutputQueue_EOS(This->pVideo_Pin->queue);
672
673     return hr;
674 }
675
676 static HRESULT WINAPI QT_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
677 {
678     HRESULT hr = S_OK;
679     QTSplitter *This = impl_from_IBaseFilter(iface);
680     HRESULT hr_any = VFW_E_NOT_CONNECTED;
681
682     TRACE("(%s)\n", wine_dbgstr_longlong(tStart));
683
684     EnterCriticalSection(&This->csReceive);
685     This->filter.rtStreamStart = tStart;
686
687     if (This->pVideo_Pin)
688         hr = BaseOutputPinImpl_Active(&This->pVideo_Pin->pin);
689     if (SUCCEEDED(hr))
690         hr_any = hr;
691     if (This->pAudio_Pin)
692         hr = BaseOutputPinImpl_Active(&This->pAudio_Pin->pin);
693     if (SUCCEEDED(hr))
694         hr_any = hr;
695
696     hr = hr_any;
697
698     SetEvent(This->runEvent);
699     LeaveCriticalSection(&This->csReceive);
700
701     return hr;
702 }
703
704 static HRESULT WINAPI QT_GetState(IBaseFilter *iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
705 {
706     QTSplitter *This = impl_from_IBaseFilter(iface);
707     TRACE("(%d, %p)\n", dwMilliSecsTimeout, pState);
708
709     *pState = This->state;
710
711     return S_OK;
712 }
713
714 static HRESULT WINAPI QT_FindPin(IBaseFilter *iface, LPCWSTR Id, IPin **ppPin)
715 {
716     FIXME("(%p)->(%s,%p) stub\n", iface, debugstr_w(Id), ppPin);
717     return E_NOTIMPL;
718 }
719
720 static const IBaseFilterVtbl QT_Vtbl = {
721     QT_QueryInterface,
722     BaseFilterImpl_AddRef,
723     QT_Release,
724     BaseFilterImpl_GetClassID,
725     QT_Stop,
726     QT_Pause,
727     QT_Run,
728     QT_GetState,
729     BaseFilterImpl_SetSyncSource,
730     BaseFilterImpl_GetSyncSource,
731     BaseFilterImpl_EnumPins,
732     QT_FindPin,
733     BaseFilterImpl_QueryFilterInfo,
734     BaseFilterImpl_JoinFilterGraph,
735     BaseFilterImpl_QueryVendorInfo
736 };
737
738 /*
739  * Input Pin
740  */
741 static HRESULT QT_RemoveOutputPins(QTSplitter *This)
742 {
743     HRESULT hr;
744     TRACE("(%p)\n", This);
745
746     if (This->pVideo_Pin)
747     {
748         OutputQueue_Destroy(This->pVideo_Pin->queue);
749         hr = BaseOutputPinImpl_BreakConnect(&This->pVideo_Pin->pin);
750         TRACE("Disconnect: %08x\n", hr);
751         IPin_Release(&This->pVideo_Pin->pin.pin.IPin_iface);
752         This->pVideo_Pin = NULL;
753     }
754     if (This->pAudio_Pin)
755     {
756         OutputQueue_Destroy(This->pAudio_Pin->queue);
757         hr = BaseOutputPinImpl_BreakConnect(&This->pAudio_Pin->pin);
758         TRACE("Disconnect: %08x\n", hr);
759         IPin_Release(&This->pAudio_Pin->pin.pin.IPin_iface);
760         This->pAudio_Pin = NULL;
761     }
762
763     BaseFilterImpl_IncrementPinVersion(&This->filter);
764     return S_OK;
765 }
766
767 static inline QTInPin *impl_from_IPin( IPin *iface )
768 {
769     return CONTAINING_RECORD(iface, QTInPin, pin.IPin_iface);
770 }
771
772 static ULONG WINAPI QTInPin_Release(IPin *iface)
773 {
774     QTInPin *This = impl_from_IPin(iface);
775     ULONG refCount = InterlockedDecrement(&This->pin.refCount);
776
777     TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
778     if (!refCount)
779     {
780         FreeMediaType(&This->pin.mtCurrent);
781         if (This->pAlloc)
782             IMemAllocator_Release(This->pAlloc);
783         This->pAlloc = NULL;
784         This->pin.IPin_iface.lpVtbl = NULL;
785         return 0;
786     }
787     else
788         return refCount;
789 }
790
791 static HRESULT QT_Process_Video_Track(QTSplitter* filter, Track trk)
792 {
793     AM_MEDIA_TYPE amt;
794     VIDEOINFOHEADER * pvi;
795     PIN_INFO piOutput;
796     HRESULT hr = S_OK;
797     OSErr err;
798     static const WCHAR szwVideoOut[] = {'V','i','d','e','o',0};
799     CFMutableDictionaryRef  pixelBufferOptions = NULL;
800     CFMutableDictionaryRef  visualContextOptions = NULL;
801     CFNumberRef n = NULL;
802     int t;
803     DWORD outputWidth, outputHeight, outputDepth;
804     Fixed trackWidth, trackHeight;
805
806     ZeroMemory(&amt, sizeof(amt));
807     amt.formattype = FORMAT_VideoInfo;
808     amt.majortype = MEDIATYPE_Video;
809     amt.subtype = MEDIASUBTYPE_RGB24;
810
811     GetTrackDimensions(trk, &trackWidth, &trackHeight);
812
813     outputDepth = 3;
814     outputWidth = Fix2Long(trackWidth);
815     outputHeight = Fix2Long(trackHeight);
816     TRACE("Width %i  Height %i\n",outputWidth, outputHeight);
817
818     amt.cbFormat = sizeof(VIDEOINFOHEADER);
819     amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
820     ZeroMemory(amt.pbFormat, amt.cbFormat);
821     pvi = (VIDEOINFOHEADER *)amt.pbFormat;
822     pvi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
823     pvi->bmiHeader.biWidth = outputWidth;
824     pvi->bmiHeader.biHeight = outputHeight;
825     pvi->bmiHeader.biPlanes = 1;
826     pvi->bmiHeader.biBitCount = 24;
827     pvi->bmiHeader.biCompression = BI_RGB;
828     pvi->bmiHeader.biSizeImage = outputWidth * outputHeight * outputDepth;
829
830     filter->outputSize = pvi->bmiHeader.biSizeImage;
831     amt.lSampleSize = 0;
832
833     pixelBufferOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
834
835     t = k32ARGBPixelFormat;
836     n = CFNumberCreate(NULL, kCFNumberIntType, &t);
837     CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferPixelFormatTypeKey, n);
838     CFRelease(n);
839
840     n = CFNumberCreate(NULL, kCFNumberIntType, &outputWidth);
841     CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferWidthKey, n);
842     CFRelease(n);
843
844     n = CFNumberCreate(NULL, kCFNumberIntType, &outputHeight);
845     CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferHeightKey, n);
846     CFRelease(n);
847
848     t = 16;
849     n = CFNumberCreate(NULL, kCFNumberIntType, &t);
850     CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferBytesPerRowAlignmentKey, n);
851     CFRelease(n);
852
853     visualContextOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
854
855     CFDictionarySetValue(visualContextOptions, kQTVisualContextPixelBufferAttributesKey, pixelBufferOptions);
856
857     err = QTPixelBufferContextCreate(NULL, visualContextOptions,&filter->vContext);
858     CFRelease(pixelBufferOptions);
859     CFRelease(visualContextOptions);
860     if (err != noErr)
861     {
862         ERR("Failed to create Visual Context\n");
863         return E_FAIL;
864     }
865
866     err = SetMovieVisualContext(filter->pQTMovie, filter->vContext);
867     if (err != noErr)
868     {
869         ERR("Failed to set Visual Context\n");
870         return E_FAIL;
871     }
872
873     piOutput.dir = PINDIR_OUTPUT;
874     piOutput.pFilter = &filter->filter.IBaseFilter_iface;
875     lstrcpyW(piOutput.achName,szwVideoOut);
876
877     hr = QT_AddPin(filter, &piOutput, &amt, TRUE);
878     if (FAILED(hr))
879         ERR("Failed to add Video Track\n");
880      else
881         TRACE("Video Pin %p\n",filter->pVideo_Pin);
882
883     return hr;
884 }
885
886 static HRESULT QT_Process_Audio_Track(QTSplitter* filter, Track trk)
887 {
888     AM_MEDIA_TYPE amt;
889     WAVEFORMATEX* pvi;
890     PIN_INFO piOutput;
891     HRESULT hr = S_OK;
892     static const WCHAR szwAudioOut[] = {'A','u','d','i','o',0};
893     Media audioMedia;
894
895     SoundDescriptionHandle  aDesc = (SoundDescriptionHandle) NewHandle(sizeof(SoundDescription));
896
897     audioMedia = GetTrackMedia(trk);
898     GetMediaSampleDescription(audioMedia, 1, (SampleDescriptionHandle)aDesc);
899
900     ZeroMemory(&amt, sizeof(amt));
901     amt.formattype = FORMAT_WaveFormatEx;
902     amt.majortype = MEDIATYPE_Audio;
903     amt.subtype = MEDIASUBTYPE_PCM;
904     amt.bTemporalCompression = 0;
905
906     amt.cbFormat = sizeof(WAVEFORMATEX);
907     amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
908     ZeroMemory(amt.pbFormat, amt.cbFormat);
909     pvi = (WAVEFORMATEX*)amt.pbFormat;
910
911     pvi->cbSize = sizeof(WAVEFORMATEX);
912     pvi->wFormatTag = WAVE_FORMAT_PCM;
913     pvi->nChannels = ((SoundDescription)**aDesc).numChannels;
914     if (pvi->nChannels < 1 || pvi->nChannels > 2)
915         pvi->nChannels = 2;
916     pvi->nSamplesPerSec = (((SoundDescription)**aDesc).sampleRate/65536);
917     if (pvi->nSamplesPerSec < 8000 || pvi->nChannels > 48000)
918         pvi->nSamplesPerSec = 44100;
919     pvi->wBitsPerSample = ((SoundDescription)**aDesc).sampleSize;
920     if (pvi->wBitsPerSample < 8 || pvi->wBitsPerSample > 32)
921         pvi->wBitsPerSample = 16;
922     pvi->nBlockAlign = (pvi->nChannels * pvi->wBitsPerSample) / 8;
923     pvi->nAvgBytesPerSec = pvi->nSamplesPerSec * pvi->nBlockAlign;
924
925     DisposeHandle((Handle)aDesc);
926
927     piOutput.dir = PINDIR_OUTPUT;
928     piOutput.pFilter = &filter->filter.IBaseFilter_iface;
929     lstrcpyW(piOutput.achName,szwAudioOut);
930
931     hr = QT_AddPin(filter, &piOutput, &amt, FALSE);
932     if (FAILED(hr))
933         ERR("Failed to add Audio Track\n");
934     else
935         TRACE("Audio Pin %p\n",filter->pAudio_Pin);
936     return hr;
937 }
938
939 static HRESULT QT_Process_Movie(QTSplitter* filter)
940 {
941     HRESULT hr = S_OK;
942     OSErr err;
943     WineDataRefRecord ptrDataRefRec;
944     Handle dataRef = NULL;
945     Track trk;
946     short id = 0;
947     DWORD tid;
948     HANDLE thread;
949     LONGLONG time;
950
951     TRACE("Trying movie connect\n");
952
953     ptrDataRefRec.pReader = filter->pInputPin.pReader;
954     ptrDataRefRec.streamSubtype = filter->pInputPin.subType;
955     PtrToHand( &ptrDataRefRec, &dataRef, sizeof(WineDataRefRecord));
956
957     err = NewMovieFromDataRef(&filter->pQTMovie, newMovieActive|newMovieDontInteractWithUser|newMovieDontAutoUpdateClock|newMovieDontAskUnresolvedDataRefs|newMovieAsyncOK, &id, dataRef, 'WINE');
958
959     DisposeHandle(dataRef);
960
961     if (err != noErr)
962     {
963         FIXME("QuickTime cannot handle media type(%i)\n",err);
964         return VFW_E_TYPE_NOT_ACCEPTED;
965     }
966
967     PrePrerollMovie(filter->pQTMovie, 0, fixed1, NULL, NULL);
968     PrerollMovie(filter->pQTMovie, 0, fixed1);
969     GoToBeginningOfMovie(filter->pQTMovie);
970     SetMovieActive(filter->pQTMovie,TRUE);
971
972     if (GetMovieLoadState(filter->pQTMovie) < kMovieLoadStateLoaded)
973         MoviesTask(filter->pQTMovie,100);
974
975     trk = GetMovieIndTrackType(filter->pQTMovie, 1, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
976     TRACE("%p is a video track\n",trk);
977     if (trk)
978        hr = QT_Process_Video_Track(filter, trk);
979
980     if (FAILED(hr))
981         return hr;
982
983     trk = GetMovieIndTrackType(filter->pQTMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
984     TRACE("%p is a audio track\n",trk);
985     if (trk)
986         hr = QT_Process_Audio_Track(filter, trk);
987
988     time = GetMovieDuration(filter->pQTMovie);
989     filter->movie_scale = GetMovieTimeScale(filter->pQTMovie);
990     filter->sourceSeeking.llDuration = ((double)time / filter->movie_scale) * 10000000;
991     filter->sourceSeeking.llStop = filter->sourceSeeking.llDuration;
992
993     TRACE("Movie duration is %s\n",wine_dbgstr_longlong(filter->sourceSeeking.llDuration));
994
995     thread = CreateThread(NULL, 0, QTSplitter_thread, filter, 0, &tid);
996     if (thread)
997     {
998         TRACE("Created thread 0x%08x\n", tid);
999         CloseHandle(thread);
1000     }
1001     else
1002         hr = HRESULT_FROM_WIN32(GetLastError());
1003
1004     return hr;
1005 }
1006
1007 static HRESULT WINAPI QTInPin_ReceiveConnection(IPin *iface, IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
1008 {
1009     HRESULT hr = S_OK;
1010     ALLOCATOR_PROPERTIES props;
1011     QTInPin *This = impl_from_IPin(iface);
1012
1013     TRACE("(%p/%p)->(%p, %p)\n", This, iface, pReceivePin, pmt);
1014
1015     EnterCriticalSection(This->pin.pCritSec);
1016     This->pReader = NULL;
1017
1018     if (This->pin.pConnectedTo)
1019         hr = VFW_E_ALREADY_CONNECTED;
1020     else if (IPin_QueryAccept(iface, pmt) != S_OK)
1021         hr = VFW_E_TYPE_NOT_ACCEPTED;
1022     else
1023     {
1024         PIN_DIRECTION pindirReceive;
1025         IPin_QueryDirection(pReceivePin, &pindirReceive);
1026         if (pindirReceive != PINDIR_OUTPUT)
1027             hr = VFW_E_INVALID_DIRECTION;
1028     }
1029
1030     if (FAILED(hr))
1031     {
1032         LeaveCriticalSection(This->pin.pCritSec);
1033         return hr;
1034     }
1035
1036     hr = IPin_QueryInterface(pReceivePin, &IID_IAsyncReader, (LPVOID *)&This->pReader);
1037     if (FAILED(hr))
1038     {
1039         LeaveCriticalSection(This->pin.pCritSec);
1040         TRACE("Input source is not an AsyncReader\n");
1041         return hr;
1042     }
1043
1044     LeaveCriticalSection(This->pin.pCritSec);
1045     EnterCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1046     hr = QT_Process_Movie(impl_from_IBaseFilter(This->pin.pinInfo.pFilter));
1047     if (FAILED(hr))
1048     {
1049         LeaveCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1050         TRACE("Unable to process movie\n");
1051         return hr;
1052     }
1053
1054     This->pAlloc = NULL;
1055     props.cBuffers = 8;
1056     props.cbAlign = 1;
1057     props.cbBuffer = impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->outputSize + props.cbAlign;
1058     props.cbPrefix = 0;
1059
1060     hr = IAsyncReader_RequestAllocator(This->pReader, NULL, &props, &This->pAlloc);
1061     if (SUCCEEDED(hr))
1062     {
1063         CopyMediaType(&This->pin.mtCurrent, pmt);
1064         This->pin.pConnectedTo = pReceivePin;
1065         IPin_AddRef(pReceivePin);
1066         hr = IMemAllocator_Commit(This->pAlloc);
1067     }
1068     else
1069     {
1070         QT_RemoveOutputPins(impl_from_IBaseFilter(This->pin.pinInfo.pFilter));
1071         if (This->pReader)
1072             IAsyncReader_Release(This->pReader);
1073         This->pReader = NULL;
1074         if (This->pAlloc)
1075             IMemAllocator_Release(This->pAlloc);
1076         This->pAlloc = NULL;
1077     }
1078     TRACE("Size: %i\n", props.cbBuffer);
1079     LeaveCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1080
1081     return hr;
1082 }
1083
1084 static HRESULT WINAPI QTInPin_Disconnect(IPin *iface)
1085 {
1086     HRESULT hr;
1087     QTInPin *This = impl_from_IPin(iface);
1088     FILTER_STATE state;
1089     TRACE("()\n");
1090
1091     hr = IBaseFilter_GetState(This->pin.pinInfo.pFilter, INFINITE, &state);
1092     EnterCriticalSection(This->pin.pCritSec);
1093     if (This->pin.pConnectedTo)
1094     {
1095         QTSplitter *Parser = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
1096
1097         if (SUCCEEDED(hr) && state == State_Stopped)
1098         {
1099             IMemAllocator_Decommit(This->pAlloc);
1100             IPin_Disconnect(This->pin.pConnectedTo);
1101             This->pin.pConnectedTo = NULL;
1102             hr = QT_RemoveOutputPins(Parser);
1103         }
1104         else
1105             hr = VFW_E_NOT_STOPPED;
1106     }
1107     else
1108         hr = S_FALSE;
1109     LeaveCriticalSection(This->pin.pCritSec);
1110     return hr;
1111 }
1112
1113 static HRESULT WINAPI QTInPin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *pmt)
1114 {
1115     QTInPin *This = impl_from_IPin(iface);
1116
1117     TRACE("(%p)->(%p)\n", This, pmt);
1118
1119     if (IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream))
1120     {
1121         This->subType = pmt->subtype;
1122         return S_OK;
1123     }
1124     return S_FALSE;
1125 }
1126
1127 static HRESULT WINAPI QTInPin_EndOfStream(IPin *iface)
1128 {
1129     QTInPin *pin = impl_from_IPin(iface);
1130     QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1131
1132     FIXME("Propagate message on %p\n", This);
1133     return S_OK;
1134 }
1135
1136 static HRESULT WINAPI QTInPin_BeginFlush(IPin *iface)
1137 {
1138     QTInPin *pin = impl_from_IPin(iface);
1139     QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1140
1141     FIXME("Propagate message on %p\n", This);
1142     return S_OK;
1143 }
1144
1145 static HRESULT WINAPI QTInPin_EndFlush(IPin *iface)
1146 {
1147     QTInPin *pin = impl_from_IPin(iface);
1148     QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1149
1150     FIXME("Propagate message on %p\n", This);
1151     return S_OK;
1152 }
1153
1154 static HRESULT WINAPI QTInPin_NewSegment(IPin *iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
1155 {
1156     QTInPin *pin = impl_from_IPin(iface);
1157     QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1158
1159     BasePinImpl_NewSegment(iface, tStart, tStop, dRate);
1160     FIXME("Propagate message on %p\n", This);
1161     return S_OK;
1162 }
1163
1164 static HRESULT WINAPI QTInPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
1165 {
1166     QTInPin *This = impl_from_IPin(iface);
1167
1168     TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
1169
1170     *ppv = NULL;
1171
1172     if (IsEqualIID(riid, &IID_IUnknown))
1173         *ppv = iface;
1174     else if (IsEqualIID(riid, &IID_IPin))
1175         *ppv = iface;
1176     else if (IsEqualIID(riid, &IID_IMediaSeeking))
1177         return IBaseFilter_QueryInterface(This->pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
1178
1179     if (*ppv)
1180     {
1181         IUnknown_AddRef((IUnknown *)(*ppv));
1182         return S_OK;
1183     }
1184
1185     FIXME("No interface for %s!\n", debugstr_guid(riid));
1186
1187     return E_NOINTERFACE;
1188 }
1189
1190 static HRESULT WINAPI QTInPin_EnumMediaTypes(IPin *iface, IEnumMediaTypes **ppEnum)
1191 {
1192     QTInPin *This = impl_from_IPin(iface);
1193
1194     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
1195
1196     return EnumMediaTypes_Construct(&This->pin, BasePinImpl_GetMediaType, BasePinImpl_GetMediaTypeVersion, ppEnum);
1197 }
1198
1199 static const IPinVtbl QT_InputPin_Vtbl = {
1200     QTInPin_QueryInterface,
1201     BasePinImpl_AddRef,
1202     QTInPin_Release,
1203     BaseInputPinImpl_Connect,
1204     QTInPin_ReceiveConnection,
1205     QTInPin_Disconnect,
1206     BasePinImpl_ConnectedTo,
1207     BasePinImpl_ConnectionMediaType,
1208     BasePinImpl_QueryPinInfo,
1209     BasePinImpl_QueryDirection,
1210     BasePinImpl_QueryId,
1211     QTInPin_QueryAccept,
1212     QTInPin_EnumMediaTypes,
1213     BasePinImpl_QueryInternalConnections,
1214     QTInPin_EndOfStream,
1215     QTInPin_BeginFlush,
1216     QTInPin_EndFlush,
1217     QTInPin_NewSegment
1218 };
1219
1220 /*
1221  * Output Pin
1222  */
1223 static inline QTOutPin *impl_QTOutPin_from_IPin( IPin *iface )
1224 {
1225     return CONTAINING_RECORD(iface, QTOutPin, pin.pin.IPin_iface);
1226 }
1227
1228 static inline QTOutPin *impl_QTOutPin_from_BasePin( BasePin *iface )
1229 {
1230     return CONTAINING_RECORD(iface, QTOutPin, pin.pin);
1231 }
1232
1233 static inline QTOutPin *impl_QTOutPin_from_BaseOutputPin( BaseOutputPin *iface )
1234 {
1235     return CONTAINING_RECORD(iface, QTOutPin, pin);
1236 }
1237
1238 static HRESULT WINAPI QTOutPin_QueryInterface(IPin *iface, REFIID riid, void **ppv)
1239 {
1240     QTOutPin *This = impl_QTOutPin_from_IPin(iface);
1241
1242     TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
1243
1244     *ppv = NULL;
1245
1246     if (IsEqualIID(riid, &IID_IUnknown))
1247         *ppv = iface;
1248     else if (IsEqualIID(riid, &IID_IPin))
1249         *ppv = iface;
1250     else if (IsEqualIID(riid, &IID_IMediaSeeking))
1251         return IBaseFilter_QueryInterface(This->pin.pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
1252     else if (IsEqualIID(riid, &IID_IQualityControl))
1253         *ppv = &This->IQualityControl_iface;
1254
1255     if (*ppv)
1256     {
1257         IUnknown_AddRef((IUnknown *)(*ppv));
1258         return S_OK;
1259     }
1260     FIXME("No interface for %s!\n", debugstr_guid(riid));
1261     return E_NOINTERFACE;
1262 }
1263
1264 static ULONG WINAPI QTOutPin_Release(IPin *iface)
1265 {
1266     QTOutPin *This = impl_QTOutPin_from_IPin(iface);
1267     ULONG refCount = InterlockedDecrement(&This->pin.pin.refCount);
1268     TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
1269
1270     if (!refCount)
1271     {
1272         DeleteMediaType(This->pmt);
1273         FreeMediaType(&This->pin.pin.mtCurrent);
1274         CoTaskMemFree(This);
1275         return 0;
1276     }
1277     return refCount;
1278 }
1279
1280 static HRESULT WINAPI QTOutPin_GetMediaType(BasePin *iface, int iPosition, AM_MEDIA_TYPE *pmt)
1281 {
1282     QTOutPin *This = impl_QTOutPin_from_BasePin(iface);
1283
1284     if (iPosition < 0)
1285         return E_INVALIDARG;
1286     if (iPosition > 0)
1287         return VFW_S_NO_MORE_ITEMS;
1288     CopyMediaType(pmt, This->pmt);
1289     return S_OK;
1290 }
1291
1292 static HRESULT WINAPI QTOutPin_DecideBufferSize(BaseOutputPin *iface, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
1293 {
1294     /* Unused */
1295     return S_OK;
1296 }
1297
1298 static HRESULT WINAPI QTOutPin_DecideAllocator(BaseOutputPin *iface, IMemInputPin *pPin, IMemAllocator **pAlloc)
1299 {
1300     HRESULT hr;
1301     QTOutPin *This = impl_QTOutPin_from_BaseOutputPin(iface);
1302     QTSplitter *QTfilter = impl_from_IBaseFilter(This->pin.pin.pinInfo.pFilter);
1303
1304     *pAlloc = NULL;
1305     if (QTfilter->pInputPin.pAlloc)
1306         hr = IMemInputPin_NotifyAllocator(pPin, QTfilter->pInputPin.pAlloc, FALSE);
1307     else
1308         hr = VFW_E_NO_ALLOCATOR;
1309
1310     return hr;
1311 }
1312
1313 static HRESULT WINAPI QTOutPin_BreakConnect(BaseOutputPin *This)
1314 {
1315     HRESULT hr;
1316
1317     TRACE("(%p)->()\n", This);
1318
1319     EnterCriticalSection(This->pin.pCritSec);
1320     if (!This->pin.pConnectedTo || !This->pMemInputPin)
1321         hr = VFW_E_NOT_CONNECTED;
1322     else
1323     {
1324         hr = IPin_Disconnect(This->pin.pConnectedTo);
1325         IPin_Disconnect(&This->pin.IPin_iface);
1326     }
1327     LeaveCriticalSection(This->pin.pCritSec);
1328
1329     return hr;
1330 }
1331
1332 static const IPinVtbl QT_OutputPin_Vtbl = {
1333     QTOutPin_QueryInterface,
1334     BasePinImpl_AddRef,
1335     QTOutPin_Release,
1336     BaseOutputPinImpl_Connect,
1337     BaseOutputPinImpl_ReceiveConnection,
1338     BaseOutputPinImpl_Disconnect,
1339     BasePinImpl_ConnectedTo,
1340     BasePinImpl_ConnectionMediaType,
1341     BasePinImpl_QueryPinInfo,
1342     BasePinImpl_QueryDirection,
1343     BasePinImpl_QueryId,
1344     BasePinImpl_QueryAccept,
1345     BasePinImpl_EnumMediaTypes,
1346     BasePinImpl_QueryInternalConnections,
1347     BaseOutputPinImpl_EndOfStream,
1348     BaseOutputPinImpl_BeginFlush,
1349     BaseOutputPinImpl_EndFlush,
1350     BasePinImpl_NewSegment
1351 };
1352
1353 static inline QTOutPin *impl_from_IQualityControl( IQualityControl *iface )
1354 {
1355     return CONTAINING_RECORD(iface, QTOutPin, IQualityControl_iface);
1356 }
1357
1358 HRESULT WINAPI QT_QualityControl_QueryInterface(IQualityControl *iface, REFIID riid, void **ppv)
1359 {
1360     QTOutPin *This = impl_from_IQualityControl(iface);
1361     return IPin_QueryInterface(&This->pin.pin.IPin_iface, riid, ppv);
1362 }
1363
1364 ULONG WINAPI QT_QualityControl_AddRef(IQualityControl *iface)
1365 {
1366     QTOutPin *This = impl_from_IQualityControl(iface);
1367     return IPin_AddRef(&This->pin.pin.IPin_iface);
1368 }
1369
1370 ULONG WINAPI QT_QualityControl_Release(IQualityControl *iface)
1371 {
1372     QTOutPin *This = impl_from_IQualityControl(iface);
1373     return IPin_Release(&This->pin.pin.IPin_iface);
1374 }
1375
1376 static HRESULT WINAPI QT_QualityControl_Notify(IQualityControl *iface, IBaseFilter *sender, Quality qm)
1377 {
1378     REFERENCE_TIME late = qm.Late;
1379     if (qm.Late < 0 && -qm.Late > qm.TimeStamp)
1380         late = -qm.TimeStamp;
1381     /* TODO: Do Something */
1382     return S_OK;
1383 }
1384
1385 HRESULT WINAPI QT_QualityControl_SetSink(IQualityControl *iface, IQualityControl *tonotify)
1386 {
1387     /* Do nothing */
1388     return S_OK;
1389 }
1390
1391 static const IQualityControlVtbl QTOutPin_QualityControl_Vtbl = {
1392     QT_QualityControl_QueryInterface,
1393     QT_QualityControl_AddRef,
1394     QT_QualityControl_Release,
1395     QT_QualityControl_Notify,
1396     QT_QualityControl_SetSink
1397 };
1398
1399 static const BasePinFuncTable output_BaseFuncTable = {
1400     NULL,
1401     BaseOutputPinImpl_AttemptConnection,
1402     BasePinImpl_GetMediaTypeVersion,
1403     QTOutPin_GetMediaType
1404 };
1405
1406 static const BaseOutputPinFuncTable output_BaseOutputFuncTable = {
1407     QTOutPin_DecideBufferSize,
1408     QTOutPin_DecideAllocator,
1409     QTOutPin_BreakConnect
1410 };
1411
1412 static const OutputQueueFuncTable output_OutputQueueFuncTable = {
1413     OutputQueueImpl_ThreadProc
1414 };
1415
1416 static HRESULT QT_AddPin(QTSplitter *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt, BOOL video)
1417 {
1418     HRESULT hr;
1419     IPin **target;
1420
1421     if (video)
1422         target = (IPin**)&This->pVideo_Pin;
1423     else
1424         target = (IPin**)&This->pAudio_Pin;
1425
1426     if (*target != NULL)
1427     {
1428         FIXME("We already have a %s pin\n",(video)?"video":"audio");
1429         return E_FAIL;
1430     }
1431
1432     hr = BaseOutputPin_Construct(&QT_OutputPin_Vtbl, sizeof(QTOutPin), piOutput, &output_BaseFuncTable, &output_BaseOutputFuncTable, &This->filter.csFilter, (IPin**)target);
1433     if (SUCCEEDED(hr))
1434     {
1435         QTOutPin *pin = (QTOutPin*)*target;
1436         pin->pmt = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
1437         CopyMediaType(pin->pmt, amt);
1438         pin->pin.pin.pinInfo.pFilter = (LPVOID)This;
1439         pin->IQualityControl_iface.lpVtbl = &QTOutPin_QualityControl_Vtbl;
1440
1441         BaseFilterImpl_IncrementPinVersion(&This->filter);
1442
1443         hr = OutputQueue_Construct(&pin->pin, TRUE, TRUE, 5, FALSE, THREAD_PRIORITY_NORMAL, &output_OutputQueueFuncTable, &pin->queue);
1444     }
1445     else
1446         ERR("Failed with error %x\n", hr);
1447     return hr;
1448 }
1449
1450 static HRESULT WINAPI QTSplitter_ChangeStart(IMediaSeeking *iface)
1451 {
1452     QTSplitter *This = impl_from_IMediaSeeking(iface);
1453     TRACE("(%p)\n", iface);
1454     EnterCriticalSection(&This->csReceive);
1455     This->movie_time = (This->sourceSeeking.llCurrent * This->movie_scale)/10000000;
1456     This->movie_start = This->movie_time;
1457     LeaveCriticalSection(&This->csReceive);
1458     return S_OK;
1459 }
1460
1461 static HRESULT WINAPI QTSplitter_ChangeStop(IMediaSeeking *iface)
1462 {
1463     FIXME("(%p) filter hasn't implemented stop position change!\n", iface);
1464     return S_OK;
1465 }
1466
1467 static HRESULT WINAPI QTSplitter_ChangeRate(IMediaSeeking *iface)
1468 {
1469     FIXME("(%p) filter hasn't implemented rate change!\n", iface);
1470     return S_OK;
1471 }
1472
1473 static HRESULT WINAPI QT_Seeking_QueryInterface(IMediaSeeking * iface, REFIID riid, LPVOID * ppv)
1474 {
1475     QTSplitter *This = impl_from_IMediaSeeking(iface);
1476
1477     return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv);
1478 }
1479
1480 static ULONG WINAPI QT_Seeking_AddRef(IMediaSeeking * iface)
1481 {
1482     QTSplitter *This = impl_from_IMediaSeeking(iface);
1483
1484     return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
1485 }
1486
1487 static ULONG WINAPI QT_Seeking_Release(IMediaSeeking * iface)
1488 {
1489     QTSplitter *This = impl_from_IMediaSeeking(iface);
1490
1491     return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
1492 }
1493
1494 static const IMediaSeekingVtbl QT_Seeking_Vtbl =
1495 {
1496     QT_Seeking_QueryInterface,
1497     QT_Seeking_AddRef,
1498     QT_Seeking_Release,
1499     SourceSeekingImpl_GetCapabilities,
1500     SourceSeekingImpl_CheckCapabilities,
1501     SourceSeekingImpl_IsFormatSupported,
1502     SourceSeekingImpl_QueryPreferredFormat,
1503     SourceSeekingImpl_GetTimeFormat,
1504     SourceSeekingImpl_IsUsingTimeFormat,
1505     SourceSeekingImpl_SetTimeFormat,
1506     SourceSeekingImpl_GetDuration,
1507     SourceSeekingImpl_GetStopPosition,
1508     SourceSeekingImpl_GetCurrentPosition,
1509     SourceSeekingImpl_ConvertTimeFormat,
1510     SourceSeekingImpl_SetPositions,
1511     SourceSeekingImpl_GetPositions,
1512     SourceSeekingImpl_GetAvailable,
1513     SourceSeekingImpl_SetRate,
1514     SourceSeekingImpl_GetRate,
1515     SourceSeekingImpl_GetPreroll
1516 };