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