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