d3drm: Implement IDirect3DRMMeshImpl_GetGroupMaterial.
[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_loading_thread(LPVOID data)
483 {
484     QTSplitter *This = (QTSplitter *)data;
485
486     if (This->pAudio_Pin)
487     {
488         /* according to QA1469 a movie has to be fully loaded before we
489            can reliably start the Extraction session.
490
491            If loaded earlier then we only get an extraction session for
492            the part of the movie that is loaded at that time.
493
494             We are trying to load as much of the movie as we can before we
495             start extracting.  However we can recreate the extraction session
496             again when we run out of loaded extraction frames. But we want
497             to try to minimize that.
498          */
499
500         while(GetMovieLoadState(This->pQTMovie) < kMovieLoadStateComplete)
501         {
502             EnterCriticalSection(&This->csReceive);
503             MoviesTask(This->pQTMovie, 100);
504             LeaveCriticalSection(&This->csReceive);
505             Sleep(0);
506         }
507     }
508     return 0;
509 }
510
511 static DWORD WINAPI QTSplitter_thread(LPVOID data)
512 {
513     QTSplitter *This = (QTSplitter *)data;
514     HRESULT hr = S_OK;
515     TimeValue next_time;
516     CVPixelBufferRef pixelBuffer = NULL;
517     OSStatus err;
518     TimeRecord tr;
519
520     WaitForSingleObject(This->runEvent, -1);
521
522     EnterCriticalSection(&This->csReceive);
523     This->state = State_Running;
524     /* Prime the pump:  Needed for MPEG streams */
525     GetMovieNextInterestingTime(This->pQTMovie, nextTimeEdgeOK | nextTimeStep, 0, NULL, This->movie_time, 1, &next_time, NULL);
526
527     GetMovieTime(This->pQTMovie, &tr);
528
529     if (This->pAudio_Pin)
530         QT_Create_Extract_Session(This);
531
532     LeaveCriticalSection(&This->csReceive);
533
534     do
535     {
536         LONGLONG tStart=0, tStop=0;
537         LONGLONG mStart=0, mStop=0;
538         float time;
539
540         EnterCriticalSection(&This->csReceive);
541         GetMovieNextInterestingTime(This->pQTMovie, nextTimeStep, 0, NULL, This->movie_time, 1, &next_time, NULL);
542
543         if (next_time == -1)
544         {
545             TRACE("No next time\n");
546             LeaveCriticalSection(&This->csReceive);
547             break;
548         }
549
550         tr.value = SInt64ToWide(next_time);
551         SetMovieTime(This->pQTMovie, &tr);
552         MoviesTask(This->pQTMovie,0);
553         QTVisualContextTask(This->vContext);
554
555         TRACE("In loop at time %ld\n",This->movie_time);
556         TRACE("In Next time %ld\n",next_time);
557
558         mStart = This->movie_time;
559         mStop = next_time;
560
561         time = (float)(This->movie_time - This->movie_start) / This->movie_scale;
562         tStart = time * 10000000;
563         time = (float)(next_time - This->movie_start) / This->movie_scale;
564         tStop = time * 10000000;
565
566         /* Deliver Audio */
567         if (This->pAudio_Pin && This->pAudio_Pin->pin.pin.pConnectedTo && This->aSession)
568         {
569             int data_size=0;
570             BYTE* ptr;
571             IMediaSample *sample = NULL;
572             AudioBufferList aData;
573             UInt32 flags;
574             UInt32 frames;
575             WAVEFORMATEX* pvi;
576             float duration;
577
578             pvi = (WAVEFORMATEX*)This->pAudio_Pin->pmt->pbFormat;
579
580             hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->pAudio_Pin->pin, &sample, NULL, NULL, 0);
581
582             if (FAILED(hr))
583             {
584                 ERR("Audio: Unable to get delivery buffer (%x)\n", hr);
585                 goto audio_error;
586             }
587
588             hr = IMediaSample_GetPointer(sample, &ptr);
589             if (FAILED(hr))
590             {
591                 ERR("Audio: Unable to get pointer to buffer (%x)\n", hr);
592                 goto audio_error;
593             }
594
595             duration = (float)next_time / This->movie_scale;
596             time = (float)This->movie_time / This->movie_scale;
597             duration -= time;
598             frames = pvi->nSamplesPerSec * duration;
599             TRACE("Need audio for %f seconds (%li frames)\n",duration,frames);
600
601             data_size = IMediaSample_GetSize(sample);
602             if (data_size < frames * pvi->nBlockAlign)
603                 FIXME("Audio buffer is too small\n");
604
605             aData.mNumberBuffers = 1;
606             aData.mBuffers[0].mNumberChannels = pvi->nChannels;
607             aData.mBuffers[0].mDataByteSize = data_size;
608             aData.mBuffers[0].mData = ptr;
609
610             err = MovieAudioExtractionFillBuffer(This->aSession, &frames, &aData, &flags);
611             if (frames == 0)
612             {
613                 TimeRecord etr;
614
615                 /* Ran out of frames, Restart the extraction session */
616                 TRACE("Restarting extraction session\n");
617                 MovieAudioExtractionEnd(This->aSession);
618                 This->aSession = NULL;
619                 QT_Create_Extract_Session(This);
620
621                 etr = tr;
622                 etr.value = SInt64ToWide(This->movie_time);
623                 MovieAudioExtractionSetProperty(This->aSession,
624                     kQTPropertyClass_MovieAudioExtraction_Movie,
625                     kQTMovieAudioExtractionMoviePropertyID_CurrentTime,
626                     sizeof(TimeRecord), &etr );
627
628                 frames = pvi->nSamplesPerSec * duration;
629                 aData.mNumberBuffers = 1;
630                 aData.mBuffers[0].mNumberChannels = pvi->nChannels;
631                 aData.mBuffers[0].mDataByteSize = data_size;
632                 aData.mBuffers[0].mData = ptr;
633
634                 MovieAudioExtractionFillBuffer(This->aSession, &frames, &aData, &flags);
635             }
636
637             TRACE("Got %i frames\n",(int)frames);
638
639             IMediaSample_SetActualDataLength(sample, frames * pvi->nBlockAlign);
640
641             IMediaSample_SetMediaTime(sample, &mStart, &mStop);
642             IMediaSample_SetTime(sample, &tStart, &tStop);
643
644             hr = OutputQueue_Receive(This->pAudio_Pin->queue, sample);
645             TRACE("Audio Delivered (%x)\n",hr);
646
647 audio_error:
648             if (sample)
649                 IMediaSample_Release(sample);
650         }
651         else
652             TRACE("Audio Pin not connected or no Audio\n");
653
654         /* Deliver Video */
655         if (This->pVideo_Pin && QTVisualContextIsNewImageAvailable(This->vContext,0))
656         {
657             err = QTVisualContextCopyImageForTime(This->vContext, NULL, NULL, &pixelBuffer);
658             if (err == noErr)
659             {
660                 int data_size=0;
661                 BYTE* ptr;
662                 IMediaSample *sample = NULL;
663
664                 hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->pVideo_Pin->pin, &sample, NULL, NULL, 0);
665                 if (FAILED(hr))
666                 {
667                     ERR("Video: Unable to get delivery buffer (%x)\n", hr);
668                     goto video_error;
669                 }
670
671                 data_size = IMediaSample_GetSize(sample);
672                 if (data_size < This->outputSize)
673                 {
674                     ERR("Sample size is too small %d < %d\n", data_size, This->outputSize)
675     ;
676                     hr = E_FAIL;
677                     goto video_error;
678                 }
679
680                 hr = IMediaSample_GetPointer(sample, &ptr);
681                 if (FAILED(hr))
682                 {
683                     ERR("Video: Unable to get pointer to buffer (%x)\n", hr);
684                     goto video_error;
685                 }
686
687                 hr = AccessPixelBufferPixels( pixelBuffer, ptr);
688                 if (FAILED(hr))
689                 {
690                     ERR("Failed to access Pixels\n");
691                     goto video_error;
692                 }
693
694                 IMediaSample_SetActualDataLength(sample, This->outputSize);
695
696                 IMediaSample_SetMediaTime(sample, &mStart, &mStop);
697                 IMediaSample_SetTime(sample, &tStart, &tStop);
698
699                 hr = OutputQueue_Receive(This->pVideo_Pin->queue, sample);
700                 TRACE("Video Delivered (%x)\n",hr);
701
702     video_error:
703                 if (sample)
704                     IMediaSample_Release(sample);
705                 if (pixelBuffer)
706                     CVPixelBufferRelease(pixelBuffer);
707             }
708         }
709         else
710             TRACE("No video to deliver\n");
711
712         This->movie_time = next_time;
713         LeaveCriticalSection(&This->csReceive);
714     } while (hr == S_OK);
715
716     This->state = State_Stopped;
717     if (This->pAudio_Pin)
718         OutputQueue_EOS(This->pAudio_Pin->queue);
719     if (This->pVideo_Pin)
720         OutputQueue_EOS(This->pVideo_Pin->queue);
721
722     return hr;
723 }
724
725 static HRESULT WINAPI QT_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
726 {
727     HRESULT hr = S_OK;
728     QTSplitter *This = impl_from_IBaseFilter(iface);
729     HRESULT hr_any = VFW_E_NOT_CONNECTED;
730
731     TRACE("(%s)\n", wine_dbgstr_longlong(tStart));
732
733     EnterCriticalSection(&This->csReceive);
734     This->filter.rtStreamStart = tStart;
735
736     if (This->pVideo_Pin)
737         hr = BaseOutputPinImpl_Active(&This->pVideo_Pin->pin);
738     if (SUCCEEDED(hr))
739         hr_any = hr;
740     if (This->pAudio_Pin)
741         hr = BaseOutputPinImpl_Active(&This->pAudio_Pin->pin);
742     if (SUCCEEDED(hr))
743         hr_any = hr;
744
745     hr = hr_any;
746
747     SetEvent(This->runEvent);
748     LeaveCriticalSection(&This->csReceive);
749
750     return hr;
751 }
752
753 static HRESULT WINAPI QT_GetState(IBaseFilter *iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
754 {
755     QTSplitter *This = impl_from_IBaseFilter(iface);
756     TRACE("(%d, %p)\n", dwMilliSecsTimeout, pState);
757
758     *pState = This->state;
759
760     return S_OK;
761 }
762
763 static HRESULT WINAPI QT_FindPin(IBaseFilter *iface, LPCWSTR Id, IPin **ppPin)
764 {
765     FIXME("(%p)->(%s,%p) stub\n", iface, debugstr_w(Id), ppPin);
766     return E_NOTIMPL;
767 }
768
769 static const IBaseFilterVtbl QT_Vtbl = {
770     QT_QueryInterface,
771     BaseFilterImpl_AddRef,
772     QT_Release,
773     BaseFilterImpl_GetClassID,
774     QT_Stop,
775     QT_Pause,
776     QT_Run,
777     QT_GetState,
778     BaseFilterImpl_SetSyncSource,
779     BaseFilterImpl_GetSyncSource,
780     BaseFilterImpl_EnumPins,
781     QT_FindPin,
782     BaseFilterImpl_QueryFilterInfo,
783     BaseFilterImpl_JoinFilterGraph,
784     BaseFilterImpl_QueryVendorInfo
785 };
786
787 /*
788  * Input Pin
789  */
790 static HRESULT QT_RemoveOutputPins(QTSplitter *This)
791 {
792     HRESULT hr;
793     TRACE("(%p)\n", This);
794
795     if (This->pVideo_Pin)
796     {
797         OutputQueue_Destroy(This->pVideo_Pin->queue);
798         hr = BaseOutputPinImpl_BreakConnect(&This->pVideo_Pin->pin);
799         TRACE("Disconnect: %08x\n", hr);
800         IPin_Release(&This->pVideo_Pin->pin.pin.IPin_iface);
801         This->pVideo_Pin = NULL;
802     }
803     if (This->pAudio_Pin)
804     {
805         OutputQueue_Destroy(This->pAudio_Pin->queue);
806         hr = BaseOutputPinImpl_BreakConnect(&This->pAudio_Pin->pin);
807         TRACE("Disconnect: %08x\n", hr);
808         IPin_Release(&This->pAudio_Pin->pin.pin.IPin_iface);
809         This->pAudio_Pin = NULL;
810     }
811
812     BaseFilterImpl_IncrementPinVersion(&This->filter);
813     return S_OK;
814 }
815
816 static inline QTInPin *impl_from_IPin( IPin *iface )
817 {
818     return CONTAINING_RECORD(iface, QTInPin, pin.IPin_iface);
819 }
820
821 static ULONG WINAPI QTInPin_Release(IPin *iface)
822 {
823     QTInPin *This = impl_from_IPin(iface);
824     ULONG refCount = InterlockedDecrement(&This->pin.refCount);
825
826     TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
827     if (!refCount)
828     {
829         FreeMediaType(&This->pin.mtCurrent);
830         if (This->pAlloc)
831             IMemAllocator_Release(This->pAlloc);
832         This->pAlloc = NULL;
833         This->pin.IPin_iface.lpVtbl = NULL;
834         return 0;
835     }
836     else
837         return refCount;
838 }
839
840 static HRESULT QT_Process_Video_Track(QTSplitter* filter, Track trk)
841 {
842     AM_MEDIA_TYPE amt;
843     VIDEOINFOHEADER * pvi;
844     PIN_INFO piOutput;
845     HRESULT hr = S_OK;
846     OSErr err;
847     static const WCHAR szwVideoOut[] = {'V','i','d','e','o',0};
848     CFMutableDictionaryRef  pixelBufferOptions = NULL;
849     CFMutableDictionaryRef  visualContextOptions = NULL;
850     CFNumberRef n = NULL;
851     int t;
852     DWORD outputWidth, outputHeight, outputDepth;
853     Fixed trackWidth, trackHeight;
854
855     ZeroMemory(&amt, sizeof(amt));
856     amt.formattype = FORMAT_VideoInfo;
857     amt.majortype = MEDIATYPE_Video;
858     amt.subtype = MEDIASUBTYPE_RGB24;
859
860     GetTrackDimensions(trk, &trackWidth, &trackHeight);
861
862     outputDepth = 3;
863     outputWidth = Fix2Long(trackWidth);
864     outputHeight = Fix2Long(trackHeight);
865     TRACE("Width %i  Height %i\n",outputWidth, outputHeight);
866
867     amt.cbFormat = sizeof(VIDEOINFOHEADER);
868     amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
869     ZeroMemory(amt.pbFormat, amt.cbFormat);
870     pvi = (VIDEOINFOHEADER *)amt.pbFormat;
871     pvi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
872     pvi->bmiHeader.biWidth = outputWidth;
873     pvi->bmiHeader.biHeight = outputHeight;
874     pvi->bmiHeader.biPlanes = 1;
875     pvi->bmiHeader.biBitCount = 24;
876     pvi->bmiHeader.biCompression = BI_RGB;
877     pvi->bmiHeader.biSizeImage = outputWidth * outputHeight * outputDepth;
878
879     filter->outputSize = pvi->bmiHeader.biSizeImage;
880     amt.lSampleSize = 0;
881
882     pixelBufferOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
883
884     t = k32ARGBPixelFormat;
885     n = CFNumberCreate(NULL, kCFNumberIntType, &t);
886     CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferPixelFormatTypeKey, n);
887     CFRelease(n);
888
889     n = CFNumberCreate(NULL, kCFNumberIntType, &outputWidth);
890     CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferWidthKey, n);
891     CFRelease(n);
892
893     n = CFNumberCreate(NULL, kCFNumberIntType, &outputHeight);
894     CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferHeightKey, n);
895     CFRelease(n);
896
897     t = 16;
898     n = CFNumberCreate(NULL, kCFNumberIntType, &t);
899     CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferBytesPerRowAlignmentKey, n);
900     CFRelease(n);
901
902     visualContextOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
903
904     CFDictionarySetValue(visualContextOptions, kQTVisualContextPixelBufferAttributesKey, pixelBufferOptions);
905
906     err = QTPixelBufferContextCreate(NULL, visualContextOptions,&filter->vContext);
907     CFRelease(pixelBufferOptions);
908     CFRelease(visualContextOptions);
909     if (err != noErr)
910     {
911         ERR("Failed to create Visual Context\n");
912         return E_FAIL;
913     }
914
915     err = SetMovieVisualContext(filter->pQTMovie, filter->vContext);
916     if (err != noErr)
917     {
918         ERR("Failed to set Visual Context\n");
919         return E_FAIL;
920     }
921
922     piOutput.dir = PINDIR_OUTPUT;
923     piOutput.pFilter = &filter->filter.IBaseFilter_iface;
924     lstrcpyW(piOutput.achName,szwVideoOut);
925
926     hr = QT_AddPin(filter, &piOutput, &amt, TRUE);
927     if (FAILED(hr))
928         ERR("Failed to add Video Track\n");
929      else
930         TRACE("Video Pin %p\n",filter->pVideo_Pin);
931
932     return hr;
933 }
934
935 static HRESULT QT_Process_Audio_Track(QTSplitter* filter, Track trk)
936 {
937     AM_MEDIA_TYPE amt;
938     WAVEFORMATEX* pvi;
939     PIN_INFO piOutput;
940     HRESULT hr = S_OK;
941     static const WCHAR szwAudioOut[] = {'A','u','d','i','o',0};
942     Media audioMedia;
943
944     SoundDescriptionHandle  aDesc = (SoundDescriptionHandle) NewHandle(sizeof(SoundDescription));
945
946     audioMedia = GetTrackMedia(trk);
947     GetMediaSampleDescription(audioMedia, 1, (SampleDescriptionHandle)aDesc);
948
949     ZeroMemory(&amt, sizeof(amt));
950     amt.formattype = FORMAT_WaveFormatEx;
951     amt.majortype = MEDIATYPE_Audio;
952     amt.subtype = MEDIASUBTYPE_PCM;
953     amt.bTemporalCompression = 0;
954
955     amt.cbFormat = sizeof(WAVEFORMATEX);
956     amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
957     ZeroMemory(amt.pbFormat, amt.cbFormat);
958     pvi = (WAVEFORMATEX*)amt.pbFormat;
959
960     pvi->cbSize = sizeof(WAVEFORMATEX);
961     pvi->wFormatTag = WAVE_FORMAT_PCM;
962     pvi->nChannels = ((SoundDescription)**aDesc).numChannels;
963     if (pvi->nChannels < 1 || pvi->nChannels > 2)
964         pvi->nChannels = 2;
965     pvi->nSamplesPerSec = (((SoundDescription)**aDesc).sampleRate/65536);
966     if (pvi->nSamplesPerSec < 8000 || pvi->nChannels > 48000)
967         pvi->nSamplesPerSec = 44100;
968     pvi->wBitsPerSample = ((SoundDescription)**aDesc).sampleSize;
969     if (pvi->wBitsPerSample < 8 || pvi->wBitsPerSample > 32)
970         pvi->wBitsPerSample = 16;
971     pvi->nBlockAlign = (pvi->nChannels * pvi->wBitsPerSample) / 8;
972     pvi->nAvgBytesPerSec = pvi->nSamplesPerSec * pvi->nBlockAlign;
973
974     DisposeHandle((Handle)aDesc);
975
976     piOutput.dir = PINDIR_OUTPUT;
977     piOutput.pFilter = &filter->filter.IBaseFilter_iface;
978     lstrcpyW(piOutput.achName,szwAudioOut);
979
980     hr = QT_AddPin(filter, &piOutput, &amt, FALSE);
981     if (FAILED(hr))
982         ERR("Failed to add Audio Track\n");
983     else
984         TRACE("Audio Pin %p\n",filter->pAudio_Pin);
985     return hr;
986 }
987
988 static HRESULT QT_Process_Movie(QTSplitter* filter)
989 {
990     HRESULT hr = S_OK;
991     OSErr err;
992     WineDataRefRecord ptrDataRefRec;
993     Handle dataRef = NULL;
994     Track trk;
995     short id = 0;
996     DWORD tid;
997     HANDLE thread;
998     LONGLONG time;
999
1000     TRACE("Trying movie connect\n");
1001
1002     ptrDataRefRec.pReader = filter->pInputPin.pReader;
1003     ptrDataRefRec.streamSubtype = filter->pInputPin.subType;
1004     PtrToHand( &ptrDataRefRec, &dataRef, sizeof(WineDataRefRecord));
1005
1006     err = NewMovieFromDataRef(&filter->pQTMovie, newMovieActive|newMovieDontInteractWithUser|newMovieDontAutoUpdateClock|newMovieDontAskUnresolvedDataRefs|newMovieAsyncOK, &id, dataRef, 'WINE');
1007
1008     DisposeHandle(dataRef);
1009
1010     if (err != noErr)
1011     {
1012         FIXME("QuickTime cannot handle media type(%i)\n",err);
1013         return VFW_E_TYPE_NOT_ACCEPTED;
1014     }
1015
1016     PrePrerollMovie(filter->pQTMovie, 0, fixed1, NULL, NULL);
1017     PrerollMovie(filter->pQTMovie, 0, fixed1);
1018     GoToBeginningOfMovie(filter->pQTMovie);
1019     SetMovieActive(filter->pQTMovie,TRUE);
1020
1021     if (GetMovieLoadState(filter->pQTMovie) < kMovieLoadStateLoaded)
1022         MoviesTask(filter->pQTMovie,100);
1023
1024     trk = GetMovieIndTrackType(filter->pQTMovie, 1, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
1025     TRACE("%p is a video track\n",trk);
1026     if (trk)
1027        hr = QT_Process_Video_Track(filter, trk);
1028
1029     if (FAILED(hr))
1030         return hr;
1031
1032     trk = GetMovieIndTrackType(filter->pQTMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
1033     TRACE("%p is a audio track\n",trk);
1034     if (trk)
1035         hr = QT_Process_Audio_Track(filter, trk);
1036
1037     time = GetMovieDuration(filter->pQTMovie);
1038     filter->movie_scale = GetMovieTimeScale(filter->pQTMovie);
1039     filter->sourceSeeking.llDuration = ((double)time / filter->movie_scale) * 10000000;
1040     filter->sourceSeeking.llStop = filter->sourceSeeking.llDuration;
1041
1042     TRACE("Movie duration is %s\n",wine_dbgstr_longlong(filter->sourceSeeking.llDuration));
1043
1044     thread = CreateThread(NULL, 0, QTSplitter_loading_thread, filter, 0, &tid);
1045     if (thread)
1046     {
1047         TRACE("Created loading thread 0x%08x\n", tid);
1048         CloseHandle(thread);
1049     }
1050     thread = CreateThread(NULL, 0, QTSplitter_thread, filter, 0, &tid);
1051     if (thread)
1052     {
1053         TRACE("Created processing thread 0x%08x\n", tid);
1054         CloseHandle(thread);
1055     }
1056     else
1057         hr = HRESULT_FROM_WIN32(GetLastError());
1058
1059     return hr;
1060 }
1061
1062 static HRESULT WINAPI QTInPin_ReceiveConnection(IPin *iface, IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
1063 {
1064     HRESULT hr = S_OK;
1065     ALLOCATOR_PROPERTIES props;
1066     QTInPin *This = impl_from_IPin(iface);
1067
1068     TRACE("(%p/%p)->(%p, %p)\n", This, iface, pReceivePin, pmt);
1069
1070     EnterCriticalSection(This->pin.pCritSec);
1071     This->pReader = NULL;
1072
1073     if (This->pin.pConnectedTo)
1074         hr = VFW_E_ALREADY_CONNECTED;
1075     else if (IPin_QueryAccept(iface, pmt) != S_OK)
1076         hr = VFW_E_TYPE_NOT_ACCEPTED;
1077     else
1078     {
1079         PIN_DIRECTION pindirReceive;
1080         IPin_QueryDirection(pReceivePin, &pindirReceive);
1081         if (pindirReceive != PINDIR_OUTPUT)
1082             hr = VFW_E_INVALID_DIRECTION;
1083     }
1084
1085     if (FAILED(hr))
1086     {
1087         LeaveCriticalSection(This->pin.pCritSec);
1088         return hr;
1089     }
1090
1091     hr = IPin_QueryInterface(pReceivePin, &IID_IAsyncReader, (LPVOID *)&This->pReader);
1092     if (FAILED(hr))
1093     {
1094         LeaveCriticalSection(This->pin.pCritSec);
1095         TRACE("Input source is not an AsyncReader\n");
1096         return hr;
1097     }
1098
1099     LeaveCriticalSection(This->pin.pCritSec);
1100     EnterCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1101     hr = QT_Process_Movie(impl_from_IBaseFilter(This->pin.pinInfo.pFilter));
1102     if (FAILED(hr))
1103     {
1104         LeaveCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1105         TRACE("Unable to process movie\n");
1106         return hr;
1107     }
1108
1109     This->pAlloc = NULL;
1110     props.cBuffers = 8;
1111     props.cbAlign = 1;
1112     props.cbBuffer = impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->outputSize + props.cbAlign;
1113     props.cbPrefix = 0;
1114
1115     hr = IAsyncReader_RequestAllocator(This->pReader, NULL, &props, &This->pAlloc);
1116     if (SUCCEEDED(hr))
1117     {
1118         CopyMediaType(&This->pin.mtCurrent, pmt);
1119         This->pin.pConnectedTo = pReceivePin;
1120         IPin_AddRef(pReceivePin);
1121         hr = IMemAllocator_Commit(This->pAlloc);
1122     }
1123     else
1124     {
1125         QT_RemoveOutputPins(impl_from_IBaseFilter(This->pin.pinInfo.pFilter));
1126         if (This->pReader)
1127             IAsyncReader_Release(This->pReader);
1128         This->pReader = NULL;
1129         if (This->pAlloc)
1130             IMemAllocator_Release(This->pAlloc);
1131         This->pAlloc = NULL;
1132     }
1133     TRACE("Size: %i\n", props.cbBuffer);
1134     LeaveCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1135
1136     return hr;
1137 }
1138
1139 static HRESULT WINAPI QTInPin_Disconnect(IPin *iface)
1140 {
1141     HRESULT hr;
1142     QTInPin *This = impl_from_IPin(iface);
1143     FILTER_STATE state;
1144     TRACE("()\n");
1145
1146     hr = IBaseFilter_GetState(This->pin.pinInfo.pFilter, INFINITE, &state);
1147     EnterCriticalSection(This->pin.pCritSec);
1148     if (This->pin.pConnectedTo)
1149     {
1150         QTSplitter *Parser = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
1151
1152         if (SUCCEEDED(hr) && state == State_Stopped)
1153         {
1154             IMemAllocator_Decommit(This->pAlloc);
1155             IPin_Disconnect(This->pin.pConnectedTo);
1156             This->pin.pConnectedTo = NULL;
1157             hr = QT_RemoveOutputPins(Parser);
1158         }
1159         else
1160             hr = VFW_E_NOT_STOPPED;
1161     }
1162     else
1163         hr = S_FALSE;
1164     LeaveCriticalSection(This->pin.pCritSec);
1165     return hr;
1166 }
1167
1168 static HRESULT WINAPI QTInPin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *pmt)
1169 {
1170     QTInPin *This = impl_from_IPin(iface);
1171
1172     TRACE("(%p)->(%p)\n", This, pmt);
1173
1174     if (IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream))
1175     {
1176         This->subType = pmt->subtype;
1177         return S_OK;
1178     }
1179     return S_FALSE;
1180 }
1181
1182 static HRESULT WINAPI QTInPin_EndOfStream(IPin *iface)
1183 {
1184     QTInPin *pin = impl_from_IPin(iface);
1185     QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1186
1187     FIXME("Propagate message on %p\n", This);
1188     return S_OK;
1189 }
1190
1191 static HRESULT WINAPI QTInPin_BeginFlush(IPin *iface)
1192 {
1193     QTInPin *pin = impl_from_IPin(iface);
1194     QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1195
1196     FIXME("Propagate message on %p\n", This);
1197     return S_OK;
1198 }
1199
1200 static HRESULT WINAPI QTInPin_EndFlush(IPin *iface)
1201 {
1202     QTInPin *pin = impl_from_IPin(iface);
1203     QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1204
1205     FIXME("Propagate message on %p\n", This);
1206     return S_OK;
1207 }
1208
1209 static HRESULT WINAPI QTInPin_NewSegment(IPin *iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
1210 {
1211     QTInPin *pin = impl_from_IPin(iface);
1212     QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1213
1214     BasePinImpl_NewSegment(iface, tStart, tStop, dRate);
1215     FIXME("Propagate message on %p\n", This);
1216     return S_OK;
1217 }
1218
1219 static HRESULT WINAPI QTInPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
1220 {
1221     QTInPin *This = impl_from_IPin(iface);
1222
1223     TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
1224
1225     *ppv = NULL;
1226
1227     if (IsEqualIID(riid, &IID_IUnknown))
1228         *ppv = iface;
1229     else if (IsEqualIID(riid, &IID_IPin))
1230         *ppv = iface;
1231     else if (IsEqualIID(riid, &IID_IMediaSeeking))
1232         return IBaseFilter_QueryInterface(This->pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
1233
1234     if (*ppv)
1235     {
1236         IUnknown_AddRef((IUnknown *)(*ppv));
1237         return S_OK;
1238     }
1239
1240     FIXME("No interface for %s!\n", debugstr_guid(riid));
1241
1242     return E_NOINTERFACE;
1243 }
1244
1245 static HRESULT WINAPI QTInPin_EnumMediaTypes(IPin *iface, IEnumMediaTypes **ppEnum)
1246 {
1247     QTInPin *This = impl_from_IPin(iface);
1248
1249     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
1250
1251     return EnumMediaTypes_Construct(&This->pin, BasePinImpl_GetMediaType, BasePinImpl_GetMediaTypeVersion, ppEnum);
1252 }
1253
1254 static const IPinVtbl QT_InputPin_Vtbl = {
1255     QTInPin_QueryInterface,
1256     BasePinImpl_AddRef,
1257     QTInPin_Release,
1258     BaseInputPinImpl_Connect,
1259     QTInPin_ReceiveConnection,
1260     QTInPin_Disconnect,
1261     BasePinImpl_ConnectedTo,
1262     BasePinImpl_ConnectionMediaType,
1263     BasePinImpl_QueryPinInfo,
1264     BasePinImpl_QueryDirection,
1265     BasePinImpl_QueryId,
1266     QTInPin_QueryAccept,
1267     QTInPin_EnumMediaTypes,
1268     BasePinImpl_QueryInternalConnections,
1269     QTInPin_EndOfStream,
1270     QTInPin_BeginFlush,
1271     QTInPin_EndFlush,
1272     QTInPin_NewSegment
1273 };
1274
1275 /*
1276  * Output Pin
1277  */
1278 static inline QTOutPin *impl_QTOutPin_from_IPin( IPin *iface )
1279 {
1280     return CONTAINING_RECORD(iface, QTOutPin, pin.pin.IPin_iface);
1281 }
1282
1283 static inline QTOutPin *impl_QTOutPin_from_BasePin( BasePin *iface )
1284 {
1285     return CONTAINING_RECORD(iface, QTOutPin, pin.pin);
1286 }
1287
1288 static inline QTOutPin *impl_QTOutPin_from_BaseOutputPin( BaseOutputPin *iface )
1289 {
1290     return CONTAINING_RECORD(iface, QTOutPin, pin);
1291 }
1292
1293 static HRESULT WINAPI QTOutPin_QueryInterface(IPin *iface, REFIID riid, void **ppv)
1294 {
1295     QTOutPin *This = impl_QTOutPin_from_IPin(iface);
1296
1297     TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
1298
1299     *ppv = NULL;
1300
1301     if (IsEqualIID(riid, &IID_IUnknown))
1302         *ppv = iface;
1303     else if (IsEqualIID(riid, &IID_IPin))
1304         *ppv = iface;
1305     else if (IsEqualIID(riid, &IID_IMediaSeeking))
1306         return IBaseFilter_QueryInterface(This->pin.pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
1307     else if (IsEqualIID(riid, &IID_IQualityControl))
1308         *ppv = &This->IQualityControl_iface;
1309
1310     if (*ppv)
1311     {
1312         IUnknown_AddRef((IUnknown *)(*ppv));
1313         return S_OK;
1314     }
1315     FIXME("No interface for %s!\n", debugstr_guid(riid));
1316     return E_NOINTERFACE;
1317 }
1318
1319 static ULONG WINAPI QTOutPin_Release(IPin *iface)
1320 {
1321     QTOutPin *This = impl_QTOutPin_from_IPin(iface);
1322     ULONG refCount = InterlockedDecrement(&This->pin.pin.refCount);
1323     TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
1324
1325     if (!refCount)
1326     {
1327         DeleteMediaType(This->pmt);
1328         FreeMediaType(&This->pin.pin.mtCurrent);
1329         CoTaskMemFree(This);
1330         return 0;
1331     }
1332     return refCount;
1333 }
1334
1335 static HRESULT WINAPI QTOutPin_GetMediaType(BasePin *iface, int iPosition, AM_MEDIA_TYPE *pmt)
1336 {
1337     QTOutPin *This = impl_QTOutPin_from_BasePin(iface);
1338
1339     if (iPosition < 0)
1340         return E_INVALIDARG;
1341     if (iPosition > 0)
1342         return VFW_S_NO_MORE_ITEMS;
1343     CopyMediaType(pmt, This->pmt);
1344     return S_OK;
1345 }
1346
1347 static HRESULT WINAPI QTOutPin_DecideBufferSize(BaseOutputPin *iface, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
1348 {
1349     /* Unused */
1350     return S_OK;
1351 }
1352
1353 static HRESULT WINAPI QTOutPin_DecideAllocator(BaseOutputPin *iface, IMemInputPin *pPin, IMemAllocator **pAlloc)
1354 {
1355     HRESULT hr;
1356     QTOutPin *This = impl_QTOutPin_from_BaseOutputPin(iface);
1357     QTSplitter *QTfilter = impl_from_IBaseFilter(This->pin.pin.pinInfo.pFilter);
1358
1359     *pAlloc = NULL;
1360     if (QTfilter->pInputPin.pAlloc)
1361         hr = IMemInputPin_NotifyAllocator(pPin, QTfilter->pInputPin.pAlloc, FALSE);
1362     else
1363         hr = VFW_E_NO_ALLOCATOR;
1364
1365     return hr;
1366 }
1367
1368 static HRESULT WINAPI QTOutPin_BreakConnect(BaseOutputPin *This)
1369 {
1370     HRESULT hr;
1371
1372     TRACE("(%p)->()\n", This);
1373
1374     EnterCriticalSection(This->pin.pCritSec);
1375     if (!This->pin.pConnectedTo || !This->pMemInputPin)
1376         hr = VFW_E_NOT_CONNECTED;
1377     else
1378     {
1379         hr = IPin_Disconnect(This->pin.pConnectedTo);
1380         IPin_Disconnect(&This->pin.IPin_iface);
1381     }
1382     LeaveCriticalSection(This->pin.pCritSec);
1383
1384     return hr;
1385 }
1386
1387 static const IPinVtbl QT_OutputPin_Vtbl = {
1388     QTOutPin_QueryInterface,
1389     BasePinImpl_AddRef,
1390     QTOutPin_Release,
1391     BaseOutputPinImpl_Connect,
1392     BaseOutputPinImpl_ReceiveConnection,
1393     BaseOutputPinImpl_Disconnect,
1394     BasePinImpl_ConnectedTo,
1395     BasePinImpl_ConnectionMediaType,
1396     BasePinImpl_QueryPinInfo,
1397     BasePinImpl_QueryDirection,
1398     BasePinImpl_QueryId,
1399     BasePinImpl_QueryAccept,
1400     BasePinImpl_EnumMediaTypes,
1401     BasePinImpl_QueryInternalConnections,
1402     BaseOutputPinImpl_EndOfStream,
1403     BaseOutputPinImpl_BeginFlush,
1404     BaseOutputPinImpl_EndFlush,
1405     BasePinImpl_NewSegment
1406 };
1407
1408 static inline QTOutPin *impl_from_IQualityControl( IQualityControl *iface )
1409 {
1410     return CONTAINING_RECORD(iface, QTOutPin, IQualityControl_iface);
1411 }
1412
1413 HRESULT WINAPI QT_QualityControl_QueryInterface(IQualityControl *iface, REFIID riid, void **ppv)
1414 {
1415     QTOutPin *This = impl_from_IQualityControl(iface);
1416     return IPin_QueryInterface(&This->pin.pin.IPin_iface, riid, ppv);
1417 }
1418
1419 ULONG WINAPI QT_QualityControl_AddRef(IQualityControl *iface)
1420 {
1421     QTOutPin *This = impl_from_IQualityControl(iface);
1422     return IPin_AddRef(&This->pin.pin.IPin_iface);
1423 }
1424
1425 ULONG WINAPI QT_QualityControl_Release(IQualityControl *iface)
1426 {
1427     QTOutPin *This = impl_from_IQualityControl(iface);
1428     return IPin_Release(&This->pin.pin.IPin_iface);
1429 }
1430
1431 static HRESULT WINAPI QT_QualityControl_Notify(IQualityControl *iface, IBaseFilter *sender, Quality qm)
1432 {
1433     REFERENCE_TIME late = qm.Late;
1434     if (qm.Late < 0 && -qm.Late > qm.TimeStamp)
1435         late = -qm.TimeStamp;
1436     /* TODO: Do Something */
1437     return S_OK;
1438 }
1439
1440 HRESULT WINAPI QT_QualityControl_SetSink(IQualityControl *iface, IQualityControl *tonotify)
1441 {
1442     /* Do nothing */
1443     return S_OK;
1444 }
1445
1446 static const IQualityControlVtbl QTOutPin_QualityControl_Vtbl = {
1447     QT_QualityControl_QueryInterface,
1448     QT_QualityControl_AddRef,
1449     QT_QualityControl_Release,
1450     QT_QualityControl_Notify,
1451     QT_QualityControl_SetSink
1452 };
1453
1454 static const BasePinFuncTable output_BaseFuncTable = {
1455     NULL,
1456     BaseOutputPinImpl_AttemptConnection,
1457     BasePinImpl_GetMediaTypeVersion,
1458     QTOutPin_GetMediaType
1459 };
1460
1461 static const BaseOutputPinFuncTable output_BaseOutputFuncTable = {
1462     QTOutPin_DecideBufferSize,
1463     QTOutPin_DecideAllocator,
1464     QTOutPin_BreakConnect
1465 };
1466
1467 static const OutputQueueFuncTable output_OutputQueueFuncTable = {
1468     OutputQueueImpl_ThreadProc
1469 };
1470
1471 static HRESULT QT_AddPin(QTSplitter *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt, BOOL video)
1472 {
1473     HRESULT hr;
1474     IPin **target;
1475
1476     if (video)
1477         target = (IPin**)&This->pVideo_Pin;
1478     else
1479         target = (IPin**)&This->pAudio_Pin;
1480
1481     if (*target != NULL)
1482     {
1483         FIXME("We already have a %s pin\n",(video)?"video":"audio");
1484         return E_FAIL;
1485     }
1486
1487     hr = BaseOutputPin_Construct(&QT_OutputPin_Vtbl, sizeof(QTOutPin), piOutput, &output_BaseFuncTable, &output_BaseOutputFuncTable, &This->filter.csFilter, (IPin**)target);
1488     if (SUCCEEDED(hr))
1489     {
1490         QTOutPin *pin = (QTOutPin*)*target;
1491         pin->pmt = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
1492         CopyMediaType(pin->pmt, amt);
1493         pin->pin.pin.pinInfo.pFilter = (LPVOID)This;
1494         pin->IQualityControl_iface.lpVtbl = &QTOutPin_QualityControl_Vtbl;
1495
1496         BaseFilterImpl_IncrementPinVersion(&This->filter);
1497
1498         hr = OutputQueue_Construct(&pin->pin, TRUE, TRUE, 5, FALSE, THREAD_PRIORITY_NORMAL, &output_OutputQueueFuncTable, &pin->queue);
1499     }
1500     else
1501         ERR("Failed with error %x\n", hr);
1502     return hr;
1503 }
1504
1505 static HRESULT WINAPI QTSplitter_ChangeStart(IMediaSeeking *iface)
1506 {
1507     QTSplitter *This = impl_from_IMediaSeeking(iface);
1508     TRACE("(%p)\n", iface);
1509     EnterCriticalSection(&This->csReceive);
1510     This->movie_time = (This->sourceSeeking.llCurrent * This->movie_scale)/10000000;
1511     This->movie_start = This->movie_time;
1512     LeaveCriticalSection(&This->csReceive);
1513     return S_OK;
1514 }
1515
1516 static HRESULT WINAPI QTSplitter_ChangeStop(IMediaSeeking *iface)
1517 {
1518     FIXME("(%p) filter hasn't implemented stop position change!\n", iface);
1519     return S_OK;
1520 }
1521
1522 static HRESULT WINAPI QTSplitter_ChangeRate(IMediaSeeking *iface)
1523 {
1524     FIXME("(%p) filter hasn't implemented rate change!\n", iface);
1525     return S_OK;
1526 }
1527
1528 static HRESULT WINAPI QT_Seeking_QueryInterface(IMediaSeeking * iface, REFIID riid, LPVOID * ppv)
1529 {
1530     QTSplitter *This = impl_from_IMediaSeeking(iface);
1531
1532     return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv);
1533 }
1534
1535 static ULONG WINAPI QT_Seeking_AddRef(IMediaSeeking * iface)
1536 {
1537     QTSplitter *This = impl_from_IMediaSeeking(iface);
1538
1539     return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
1540 }
1541
1542 static ULONG WINAPI QT_Seeking_Release(IMediaSeeking * iface)
1543 {
1544     QTSplitter *This = impl_from_IMediaSeeking(iface);
1545
1546     return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
1547 }
1548
1549 static const IMediaSeekingVtbl QT_Seeking_Vtbl =
1550 {
1551     QT_Seeking_QueryInterface,
1552     QT_Seeking_AddRef,
1553     QT_Seeking_Release,
1554     SourceSeekingImpl_GetCapabilities,
1555     SourceSeekingImpl_CheckCapabilities,
1556     SourceSeekingImpl_IsFormatSupported,
1557     SourceSeekingImpl_QueryPreferredFormat,
1558     SourceSeekingImpl_GetTimeFormat,
1559     SourceSeekingImpl_IsUsingTimeFormat,
1560     SourceSeekingImpl_SetTimeFormat,
1561     SourceSeekingImpl_GetDuration,
1562     SourceSeekingImpl_GetStopPosition,
1563     SourceSeekingImpl_GetCurrentPosition,
1564     SourceSeekingImpl_ConvertTimeFormat,
1565     SourceSeekingImpl_SetPositions,
1566     SourceSeekingImpl_GetPositions,
1567     SourceSeekingImpl_GetAvailable,
1568     SourceSeekingImpl_SetRate,
1569     SourceSeekingImpl_GetRate,
1570     SourceSeekingImpl_GetPreroll
1571 };