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