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