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