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