spoolss: Mark internal symbols with hidden visibility.
[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
896     TRACE("Trying movie connect\n");
897
898     ptrDataRefRec.pReader = filter->pInputPin.pReader;
899     ptrDataRefRec.streamSubtype = filter->pInputPin.subType;
900     PtrToHand( &ptrDataRefRec, &dataRef, sizeof(WineDataRefRecord));
901
902     err = NewMovieFromDataRef(&filter->pQTMovie, newMovieActive|newMovieDontInteractWithUser|newMovieDontAutoUpdateClock|newMovieDontAskUnresolvedDataRefs|newMovieAsyncOK, &id, dataRef, 'WINE');
903
904     DisposeHandle(dataRef);
905
906     if (err != noErr)
907     {
908         FIXME("QuickTime cannot handle media type(%i)\n",err);
909         return VFW_E_TYPE_NOT_ACCEPTED;
910     }
911
912     PrePrerollMovie(filter->pQTMovie, 0, fixed1, NULL, NULL);
913     PrerollMovie(filter->pQTMovie, 0, fixed1);
914     GoToBeginningOfMovie(filter->pQTMovie);
915     SetMovieActive(filter->pQTMovie,TRUE);
916
917     if (GetMovieLoadState(filter->pQTMovie) < kMovieLoadStateLoaded)
918         MoviesTask(filter->pQTMovie,100);
919
920     trk = GetMovieIndTrackType(filter->pQTMovie, 1, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
921     TRACE("%p is a video track\n",trk);
922     if (trk)
923        hr = QT_Process_Video_Track(filter, trk);
924
925     if (!SUCCEEDED(hr))
926         return hr;
927
928     trk = GetMovieIndTrackType(filter->pQTMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
929     TRACE("%p is a audio track\n",trk);
930     if (trk)
931         hr = QT_Process_Audio_Track(filter, trk);
932
933     CreateThread(NULL, 0, QTSplitter_thread, filter, 0, &tid);
934     TRACE("Created thread 0x%08x\n",tid);
935
936     return hr;
937 }
938
939 static HRESULT WINAPI QTInPin_ReceiveConnection(IPin *iface, IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
940 {
941     HRESULT hr = S_OK;
942     ALLOCATOR_PROPERTIES props;
943     QTInPin *This = (QTInPin*)iface;
944
945     TRACE("(%p/%p)->(%p, %p)\n", This, iface, pReceivePin, pmt);
946
947     EnterCriticalSection(This->pin.pCritSec);
948     This->pReader = NULL;
949
950     if (This->pin.pConnectedTo)
951         hr = VFW_E_ALREADY_CONNECTED;
952     else if (IPin_QueryAccept(iface, pmt) != S_OK)
953         hr = VFW_E_TYPE_NOT_ACCEPTED;
954     else
955     {
956         PIN_DIRECTION pindirReceive;
957         IPin_QueryDirection(pReceivePin, &pindirReceive);
958         if (pindirReceive != PINDIR_OUTPUT)
959             hr = VFW_E_INVALID_DIRECTION;
960     }
961
962     if (!SUCCEEDED(hr))
963     {
964         LeaveCriticalSection(This->pin.pCritSec);
965         return hr;
966     }
967
968     hr = IPin_QueryInterface(pReceivePin, &IID_IAsyncReader, (LPVOID *)&This->pReader);
969     if (!SUCCEEDED(hr))
970     {
971         LeaveCriticalSection(This->pin.pCritSec);
972         TRACE("Input source is not an AsyncReader\n");
973         return hr;
974     }
975
976     LeaveCriticalSection(This->pin.pCritSec);
977     EnterCriticalSection(&((QTSplitter *)This->pin.pinInfo.pFilter)->filter.csFilter);
978     hr = QT_Process_Movie((QTSplitter *)This->pin.pinInfo.pFilter);
979     if (!SUCCEEDED(hr))
980     {
981         LeaveCriticalSection(&((QTSplitter *)This->pin.pinInfo.pFilter)->filter.csFilter);
982         TRACE("Unable to process movie\n");
983         return hr;
984     }
985
986     This->pAlloc = NULL;
987     props.cBuffers = 8;
988     props.cbAlign = 1;
989     props.cbBuffer = ((QTSplitter *)This->pin.pinInfo.pFilter)->outputSize + props.cbAlign;
990     props.cbPrefix = 0;
991
992     hr = IAsyncReader_RequestAllocator(This->pReader, NULL, &props, &This->pAlloc);
993     if (SUCCEEDED(hr))
994     {
995         CopyMediaType(&This->pin.mtCurrent, pmt);
996         This->pin.pConnectedTo = pReceivePin;
997         IPin_AddRef(pReceivePin);
998         hr = IMemAllocator_Commit(This->pAlloc);
999     }
1000     else
1001     {
1002         QT_RemoveOutputPins((QTSplitter *)This->pin.pinInfo.pFilter);
1003         if (This->pReader)
1004             IAsyncReader_Release(This->pReader);
1005         This->pReader = NULL;
1006         if (This->pAlloc)
1007             IMemAllocator_Release(This->pAlloc);
1008         This->pAlloc = NULL;
1009     }
1010     TRACE("Size: %i\n", props.cbBuffer);
1011     LeaveCriticalSection(&((QTSplitter *)This->pin.pinInfo.pFilter)->filter.csFilter);
1012
1013     return hr;
1014 }
1015
1016 static HRESULT WINAPI QTInPin_Disconnect(IPin *iface)
1017 {
1018     HRESULT hr;
1019     QTInPin *This = (QTInPin*)iface;
1020     FILTER_STATE state;
1021     TRACE("()\n");
1022
1023     hr = IBaseFilter_GetState(This->pin.pinInfo.pFilter, INFINITE, &state);
1024     EnterCriticalSection(This->pin.pCritSec);
1025     if (This->pin.pConnectedTo)
1026     {
1027         QTSplitter *Parser = (QTSplitter *)This->pin.pinInfo.pFilter;
1028
1029         if (SUCCEEDED(hr) && state == State_Stopped)
1030         {
1031             IMemAllocator_Decommit(This->pAlloc);
1032             IPin_Disconnect(This->pin.pConnectedTo);
1033             This->pin.pConnectedTo = NULL;
1034             hr = QT_RemoveOutputPins(Parser);
1035         }
1036         else
1037             hr = VFW_E_NOT_STOPPED;
1038     }
1039     else
1040         hr = S_FALSE;
1041     LeaveCriticalSection(This->pin.pCritSec);
1042     return hr;
1043 }
1044
1045 static HRESULT WINAPI QTInPin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *pmt)
1046 {
1047     QTInPin *This = (QTInPin*)iface;
1048
1049     TRACE("(%p)->(%p)\n", This, pmt);
1050
1051     if (IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream))
1052     {
1053         This->subType = pmt->subtype;
1054         return S_OK;
1055     }
1056     return S_FALSE;
1057 }
1058
1059 static HRESULT WINAPI QTInPin_EndOfStream(IPin *iface)
1060 {
1061     QTInPin *pin = (QTInPin*)iface;
1062     QTSplitter *This = (QTSplitter*)pin->pin.pinInfo.pFilter;
1063
1064     FIXME("Propagate message on %p\n", This);
1065     return S_OK;
1066 }
1067
1068 static HRESULT WINAPI QTInPin_BeginFlush(IPin *iface)
1069 {
1070     QTInPin *pin = (QTInPin*)iface;
1071     QTSplitter *This = (QTSplitter*)pin->pin.pinInfo.pFilter;
1072
1073     FIXME("Propagate message on %p\n", This);
1074     return S_OK;
1075 }
1076
1077 static HRESULT WINAPI QTInPin_EndFlush(IPin *iface)
1078 {
1079     QTInPin *pin = (QTInPin*)iface;
1080     QTSplitter *This = (QTSplitter*)pin->pin.pinInfo.pFilter;
1081
1082     FIXME("Propagate message on %p\n", This);
1083     return S_OK;
1084 }
1085
1086 static HRESULT WINAPI QTInPin_NewSegment(IPin *iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
1087 {
1088     QTInPin *pin = (QTInPin*)iface;
1089     QTSplitter *This = (QTSplitter*)pin->pin.pinInfo.pFilter;
1090
1091     BasePinImpl_NewSegment(iface, tStart, tStop, dRate);
1092     FIXME("Propagate message on %p\n", This);
1093     return S_OK;
1094 }
1095
1096 static HRESULT WINAPI QTInPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
1097 {
1098     QTInPin *This = (QTInPin*)iface;
1099
1100     TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
1101
1102     *ppv = NULL;
1103
1104     if (IsEqualIID(riid, &IID_IUnknown))
1105         *ppv = iface;
1106     else if (IsEqualIID(riid, &IID_IPin))
1107         *ppv = iface;
1108     else if (IsEqualIID(riid, &IID_IMediaSeeking))
1109     {
1110         return IBaseFilter_QueryInterface(This->pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
1111     }
1112
1113     if (*ppv)
1114     {
1115         IUnknown_AddRef((IUnknown *)(*ppv));
1116         return S_OK;
1117     }
1118
1119     FIXME("No interface for %s!\n", debugstr_guid(riid));
1120
1121     return E_NOINTERFACE;
1122 }
1123
1124 static HRESULT WINAPI QTInPin_EnumMediaTypes(IPin *iface, IEnumMediaTypes **ppEnum)
1125 {
1126     BasePin *This = (BasePin *)iface;
1127
1128     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
1129
1130     return EnumMediaTypes_Construct(This, BasePinImpl_GetMediaType, BasePinImpl_GetMediaTypeVersion, ppEnum);
1131 }
1132
1133 static const IPinVtbl QT_InputPin_Vtbl = {
1134     QTInPin_QueryInterface,
1135     BasePinImpl_AddRef,
1136     QTInPin_Release,
1137     BaseInputPinImpl_Connect,
1138     QTInPin_ReceiveConnection,
1139     QTInPin_Disconnect,
1140     BasePinImpl_ConnectedTo,
1141     BasePinImpl_ConnectionMediaType,
1142     BasePinImpl_QueryPinInfo,
1143     BasePinImpl_QueryDirection,
1144     BasePinImpl_QueryId,
1145     QTInPin_QueryAccept,
1146     QTInPin_EnumMediaTypes,
1147     BasePinImpl_QueryInternalConnections,
1148     QTInPin_EndOfStream,
1149     QTInPin_BeginFlush,
1150     QTInPin_EndFlush,
1151     QTInPin_NewSegment
1152 };
1153
1154 /*
1155  * Output Pin
1156  */
1157
1158 static HRESULT WINAPI QTOutPin_QueryInterface(IPin *iface, REFIID riid, void **ppv)
1159 {
1160     QTOutPin *This = (QTOutPin *)iface;
1161
1162     TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
1163
1164     *ppv = NULL;
1165
1166     if (IsEqualIID(riid, &IID_IUnknown))
1167         *ppv = iface;
1168     else if (IsEqualIID(riid, &IID_IPin))
1169         *ppv = iface;
1170     else if (IsEqualIID(riid, &IID_IMediaSeeking))
1171         return IBaseFilter_QueryInterface(This->pin.pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
1172
1173     if (*ppv)
1174     {
1175         IUnknown_AddRef((IUnknown *)(*ppv));
1176         return S_OK;
1177     }
1178     FIXME("No interface for %s!\n", debugstr_guid(riid));
1179     return E_NOINTERFACE;
1180 }
1181
1182 static ULONG WINAPI QTOutPin_Release(IPin *iface)
1183 {
1184     QTOutPin *This = (QTOutPin *)iface;
1185     ULONG refCount = InterlockedDecrement(&This->pin.pin.refCount);
1186     TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
1187
1188     if (!refCount)
1189     {
1190         DeleteMediaType(This->pmt);
1191         FreeMediaType(&This->pin.pin.mtCurrent);
1192         OutputQueue_Destroy(This->queue);
1193         CoTaskMemFree(This);
1194         return 0;
1195     }
1196     return refCount;
1197 }
1198
1199 static HRESULT WINAPI QTOutPin_GetMediaType(BasePin *iface, int iPosition, AM_MEDIA_TYPE *pmt)
1200 {
1201     QTOutPin *This = (QTOutPin *)iface;
1202
1203     if (iPosition < 0)
1204         return E_INVALIDARG;
1205     if (iPosition > 0)
1206         return VFW_S_NO_MORE_ITEMS;
1207     CopyMediaType(pmt, This->pmt);
1208     return S_OK;
1209 }
1210
1211 static HRESULT WINAPI QTOutPin_DecideBufferSize(BaseOutputPin *iface, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
1212 {
1213     /* Unused */
1214     return S_OK;
1215 }
1216
1217 static HRESULT WINAPI QTOutPin_DecideAllocator(BaseOutputPin *iface, IMemInputPin *pPin, IMemAllocator **pAlloc)
1218 {
1219     HRESULT hr;
1220     QTOutPin *This = (QTOutPin *)iface;
1221     QTSplitter *QTfilter = (QTSplitter*)This->pin.pin.pinInfo.pFilter;
1222
1223     pAlloc = NULL;
1224     if (QTfilter->pInputPin.pAlloc)
1225         hr = IMemInputPin_NotifyAllocator(pPin, QTfilter->pInputPin.pAlloc, FALSE);
1226     else
1227         hr = VFW_E_NO_ALLOCATOR;
1228
1229     return hr;
1230 }
1231
1232 static HRESULT WINAPI QTOutPin_BreakConnect(BaseOutputPin *This)
1233 {
1234     HRESULT hr;
1235
1236     TRACE("(%p)->()\n", This);
1237
1238     EnterCriticalSection(This->pin.pCritSec);
1239     if (!This->pin.pConnectedTo || !This->pMemInputPin)
1240         hr = VFW_E_NOT_CONNECTED;
1241     else
1242     {
1243         hr = IPin_Disconnect(This->pin.pConnectedTo);
1244         IPin_Disconnect((IPin *)This);
1245     }
1246     LeaveCriticalSection(This->pin.pCritSec);
1247
1248     return hr;
1249 }
1250
1251 static const IPinVtbl QT_OutputPin_Vtbl = {
1252     QTOutPin_QueryInterface,
1253     BasePinImpl_AddRef,
1254     QTOutPin_Release,
1255     BaseOutputPinImpl_Connect,
1256     BaseOutputPinImpl_ReceiveConnection,
1257     BaseOutputPinImpl_Disconnect,
1258     BasePinImpl_ConnectedTo,
1259     BasePinImpl_ConnectionMediaType,
1260     BasePinImpl_QueryPinInfo,
1261     BasePinImpl_QueryDirection,
1262     BasePinImpl_QueryId,
1263     BasePinImpl_QueryAccept,
1264     BasePinImpl_EnumMediaTypes,
1265     BasePinImpl_QueryInternalConnections,
1266     BaseOutputPinImpl_EndOfStream,
1267     BaseOutputPinImpl_BeginFlush,
1268     BaseOutputPinImpl_EndFlush,
1269     BasePinImpl_NewSegment
1270 };
1271
1272 static const BasePinFuncTable output_BaseFuncTable = {
1273     NULL,
1274     BaseOutputPinImpl_AttemptConnection,
1275     BasePinImpl_GetMediaTypeVersion,
1276     QTOutPin_GetMediaType
1277 };
1278
1279 static const BaseOutputPinFuncTable output_BaseOutputFuncTable = {
1280     QTOutPin_DecideBufferSize,
1281     QTOutPin_DecideAllocator,
1282     QTOutPin_BreakConnect
1283 };
1284
1285 static const OutputQueueFuncTable output_OutputQueueFuncTable = {
1286     OutputQueueImpl_ThreadProc
1287 };
1288
1289 static HRESULT QT_AddPin(QTSplitter *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt, BOOL video)
1290 {
1291     HRESULT hr;
1292     IPin **target;
1293
1294     if (video)
1295         target = (IPin**)&This->pVideo_Pin;
1296     else
1297         target = (IPin**)&This->pAudio_Pin;
1298
1299     if (*target != NULL)
1300     {
1301         FIXME("We already have a %s pin\n",(video)?"video":"audio");
1302         return E_FAIL;
1303     }
1304
1305     hr = BaseOutputPin_Construct(&QT_OutputPin_Vtbl, sizeof(QTOutPin), piOutput, &output_BaseFuncTable, &output_BaseOutputFuncTable, &This->filter.csFilter, (IPin**)target);
1306     if (SUCCEEDED(hr))
1307     {
1308         QTOutPin *pin = (QTOutPin*)*target;
1309         pin->pmt = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
1310         CopyMediaType(pin->pmt, amt);
1311         pin->pin.pin.pinInfo.pFilter = (LPVOID)This;
1312
1313         BaseFilterImpl_IncrementPinVersion((BaseFilter*)This);
1314
1315         hr = OutputQueue_Construct((BaseOutputPin*)pin, TRUE, TRUE, 5, FALSE, THREAD_PRIORITY_NORMAL, &output_OutputQueueFuncTable, &pin->queue);
1316     }
1317     else
1318         ERR("Failed with error %x\n", hr);
1319     return hr;
1320 }