wineqtdecoder: Call OutputQueue_Destroy during pin disconnect.
[wine/multimedia.git] / dlls / wineqtdecoder / qtsplitter.c
blobb21f198de0e5bb9e55ef21af24a131bece516126
1 /*
2 * QuickTime splitter + decoder
4 * Copyright 2011 Aric Stewart for CodeWeavers
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.
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.
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
21 #include "config.h"
23 #define ULONG CoreFoundation_ULONG
24 #define HRESULT CoreFoundation_HRESULT
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
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
63 #include <QuickTime/Movies.h>
64 #include <QuickTime/QuickTimeComponents.h>
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
104 #undef ULONG
105 #undef HRESULT
106 #undef DPRINTF
107 #undef STDMETHODCALLTYPE
109 #include <assert.h>
110 #include <stdio.h>
111 #include <stdarg.h>
113 #define NONAMELESSSTRUCT
114 #define NONAMELESSUNION
115 #define COBJMACROS
117 #include "windef.h"
118 #include "winbase.h"
119 #include "wtypes.h"
120 #include "winuser.h"
121 #include "dshow.h"
123 #include <assert.h>
125 #include "wine/unicode.h"
126 #include "wine/debug.h"
127 #include "wine/strmbase.h"
129 #include "qtprivate.h"
131 WINE_DEFAULT_DEBUG_CHANNEL(qtsplitter);
132 extern CLSID CLSID_QTSplitter;
134 typedef struct QTOutPin {
135 BaseOutputPin pin;
136 IQualityControl IQualityControl_iface;
138 AM_MEDIA_TYPE * pmt;
139 OutputQueue * queue;
140 } QTOutPin;
142 typedef struct QTInPin {
143 BasePin pin;
144 GUID subType;
146 IAsyncReader *pReader;
147 IMemAllocator *pAlloc;
148 } QTInPin;
150 typedef struct QTSplitter {
151 BaseFilter filter;
153 QTInPin pInputPin;
154 QTOutPin *pVideo_Pin;
155 QTOutPin *pAudio_Pin;
157 ALLOCATOR_PROPERTIES props;
159 Movie pQTMovie;
160 QTVisualContextRef vContext;
162 MovieAudioExtractionRef aSession;
163 HANDLE runEvent;
165 DWORD outputSize;
166 FILTER_STATE state;
167 CRITICAL_SECTION csReceive;
169 SourceSeeking sourceSeeking;
170 TimeValue movie_time;
171 TimeValue movie_start;
172 TimeScale movie_scale;
173 } QTSplitter;
175 static const IPinVtbl QT_OutputPin_Vtbl;
176 static const IPinVtbl QT_InputPin_Vtbl;
177 static const IBaseFilterVtbl QT_Vtbl;
178 static const IMediaSeekingVtbl QT_Seeking_Vtbl;
180 static HRESULT QT_AddPin(QTSplitter *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt, BOOL video);
181 static HRESULT QT_RemoveOutputPins(QTSplitter *This);
183 static HRESULT WINAPI QTSplitter_ChangeStart(IMediaSeeking *iface);
184 static HRESULT WINAPI QTSplitter_ChangeStop(IMediaSeeking *iface);
185 static HRESULT WINAPI QTSplitter_ChangeRate(IMediaSeeking *iface);
187 static inline QTSplitter *impl_from_IMediaSeeking( IMediaSeeking *iface )
189 return CONTAINING_RECORD(iface, QTSplitter, sourceSeeking.IMediaSeeking_iface);
192 static inline QTSplitter *impl_from_BaseFilter( BaseFilter *iface )
194 return CONTAINING_RECORD(iface, QTSplitter, filter);
197 static inline QTSplitter *impl_from_IBaseFilter( IBaseFilter *iface )
199 return CONTAINING_RECORD(iface, QTSplitter, filter.IBaseFilter_iface);
203 * Base Filter
206 static IPin* WINAPI QT_GetPin(BaseFilter *iface, int pos)
208 QTSplitter *This = impl_from_BaseFilter(iface);
209 TRACE("Asking for pos %x\n", pos);
211 if (pos > 2 || pos < 0)
212 return NULL;
213 switch (pos)
215 case 0:
216 IPin_AddRef(&This->pInputPin.pin.IPin_iface);
217 return &This->pInputPin.pin.IPin_iface;
218 case 1:
219 if (This->pVideo_Pin)
220 IPin_AddRef(&This->pVideo_Pin->pin.pin.IPin_iface);
221 return &This->pVideo_Pin->pin.pin.IPin_iface;
222 case 2:
223 if (This->pAudio_Pin)
224 IPin_AddRef(&This->pAudio_Pin->pin.pin.IPin_iface);
225 return &This->pAudio_Pin->pin.pin.IPin_iface;
226 default:
227 return NULL;
231 static LONG WINAPI QT_GetPinCount(BaseFilter *iface)
233 QTSplitter *This = impl_from_BaseFilter(iface);
234 int c = 1;
235 if (This->pAudio_Pin) c++;
236 if (This->pVideo_Pin) c++;
237 return c;
240 static const BaseFilterFuncTable BaseFuncTable = {
241 QT_GetPin,
242 QT_GetPinCount
245 IUnknown * CALLBACK QTSplitter_create(IUnknown *punkout, HRESULT *phr)
247 IUnknown *obj = NULL;
248 PIN_INFO *piInput;
249 QTSplitter *This;
250 static const WCHAR wcsInputPinName[] = {'I','n','p','u','t',' ','P','i','n',0};
252 EnterMovies();
254 RegisterWineDataHandler();
256 This = CoTaskMemAlloc(sizeof(*This));
257 obj = (IUnknown*)This;
258 if (!This)
260 *phr = E_OUTOFMEMORY;
261 return NULL;
263 ZeroMemory(This,sizeof(*This));
265 BaseFilter_Init(&This->filter, &QT_Vtbl, &CLSID_QTSplitter, (DWORD_PTR)(__FILE__ ": QTSplitter.csFilter"), &BaseFuncTable);
267 InitializeCriticalSection(&This->csReceive);
268 This->csReceive.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__": QTSplitter.csReceive");
270 This->pVideo_Pin = NULL;
271 This->pAudio_Pin = NULL;
272 This->state = State_Stopped;
273 This->aSession = NULL;
274 This->runEvent = CreateEventW(NULL, 0, 0, NULL);
276 piInput = &This->pInputPin.pin.pinInfo;
277 piInput->dir = PINDIR_INPUT;
278 piInput->pFilter = &This->filter.IBaseFilter_iface;
279 lstrcpynW(piInput->achName, wcsInputPinName, sizeof(piInput->achName) / sizeof(piInput->achName[0]));
280 This->pInputPin.pin.IPin_iface.lpVtbl = &QT_InputPin_Vtbl;
281 This->pInputPin.pin.refCount = 1;
282 This->pInputPin.pin.pConnectedTo = NULL;
283 This->pInputPin.pin.pCritSec = &This->filter.csFilter;
285 SourceSeeking_Init(&This->sourceSeeking, &QT_Seeking_Vtbl, QTSplitter_ChangeStop, QTSplitter_ChangeStart, QTSplitter_ChangeRate, &This->filter.csFilter);
287 *phr = S_OK;
288 return obj;
291 static void QT_Destroy(QTSplitter *This)
293 IPin *connected = NULL;
294 ULONG pinref;
296 TRACE("Destroying\n");
298 /* Don't need to clean up output pins, disconnecting input pin will do that */
299 IPin_ConnectedTo(&This->pInputPin.pin.IPin_iface, &connected);
300 if (connected)
302 IPin_Disconnect(connected);
303 IPin_Release(connected);
305 pinref = IPin_Release(&This->pInputPin.pin.IPin_iface);
306 if (pinref)
308 ERR("pinref should be null, is %u, destroying anyway\n", pinref);
309 assert((LONG)pinref > 0);
311 while (pinref)
312 pinref = IPin_Release(&This->pInputPin.pin.IPin_iface);
315 if (This->pQTMovie)
316 DisposeMovie(This->pQTMovie);
317 if (This->vContext)
318 QTVisualContextRelease(This->vContext);
319 if (This->aSession)
320 MovieAudioExtractionEnd(This->aSession);
321 CloseHandle(This->runEvent);
323 ExitMovies();
325 This->csReceive.DebugInfo->Spare[0] = 0;
326 DeleteCriticalSection(&This->csReceive);
328 CoTaskMemFree(This);
331 static HRESULT WINAPI QT_QueryInterface(IBaseFilter *iface, REFIID riid, LPVOID *ppv)
333 QTSplitter *This = impl_from_IBaseFilter(iface);
334 TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
336 *ppv = NULL;
338 if (IsEqualIID(riid, &IID_IUnknown))
339 *ppv = This;
340 else if (IsEqualIID(riid, &IID_IPersist))
341 *ppv = This;
342 else if (IsEqualIID(riid, &IID_IMediaFilter))
343 *ppv = This;
344 else if (IsEqualIID(riid, &IID_IBaseFilter))
345 *ppv = This;
346 else if (IsEqualIID(riid, &IID_IMediaSeeking))
347 *ppv = &This->sourceSeeking;
349 if (*ppv)
351 IUnknown_AddRef((IUnknown *)(*ppv));
352 return S_OK;
355 if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow) &&
356 !IsEqualIID(riid, &IID_IAMFilterMiscFlags))
357 FIXME("No interface for %s!\n", debugstr_guid(riid));
359 return E_NOINTERFACE;
362 static ULONG WINAPI QT_Release(IBaseFilter *iface)
364 QTSplitter *This = impl_from_IBaseFilter(iface);
365 ULONG refCount = BaseFilterImpl_Release(iface);
367 TRACE("(%p)->() Release from %d\n", This, refCount + 1);
369 if (!refCount)
370 QT_Destroy(This);
372 return refCount;
375 static HRESULT WINAPI QT_Stop(IBaseFilter *iface)
377 QTSplitter *This = impl_from_IBaseFilter(iface);
379 TRACE("()\n");
381 EnterCriticalSection(&This->csReceive);
382 IAsyncReader_BeginFlush(This->pInputPin.pReader);
383 IAsyncReader_EndFlush(This->pInputPin.pReader);
384 LeaveCriticalSection(&This->csReceive);
386 return S_OK;
389 static HRESULT WINAPI QT_Pause(IBaseFilter *iface)
391 HRESULT hr = S_OK;
392 TRACE("()\n");
394 return hr;
397 static OSErr QT_Create_Extract_Session(QTSplitter *filter)
399 AudioStreamBasicDescription aDesc;
400 OSErr err;
401 WAVEFORMATEX* pvi;
403 pvi = (WAVEFORMATEX*)filter->pAudio_Pin->pmt->pbFormat;
405 err = MovieAudioExtractionBegin(filter->pQTMovie, 0, &filter->aSession);
406 if (err != noErr)
408 ERR("Failed to begin Extraction session %i\n",err);
409 return err;
412 err = MovieAudioExtractionGetProperty(filter->aSession,
413 kQTPropertyClass_MovieAudioExtraction_Audio,
414 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
415 sizeof(AudioStreamBasicDescription), &aDesc, NULL);
417 if (err != noErr)
419 MovieAudioExtractionEnd(filter->aSession);
420 filter->aSession = NULL;
421 ERR("Failed to get session description %i\n",err);
422 return err;
425 aDesc.mFormatID = kAudioFormatLinearPCM;
426 aDesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger +
427 kAudioFormatFlagIsPacked;
428 aDesc.mFramesPerPacket = 1;
429 aDesc.mChannelsPerFrame = pvi->nChannels;
430 aDesc.mBitsPerChannel = pvi->wBitsPerSample;
431 aDesc.mSampleRate = pvi->nSamplesPerSec;
432 aDesc.mBytesPerFrame = (aDesc.mBitsPerChannel * aDesc.mChannelsPerFrame) / 8;
433 aDesc.mBytesPerPacket = aDesc.mBytesPerFrame * aDesc.mFramesPerPacket;
435 err = MovieAudioExtractionSetProperty(filter->aSession,
436 kQTPropertyClass_MovieAudioExtraction_Audio,
437 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
438 sizeof(AudioStreamBasicDescription), &aDesc);
440 if (aDesc.mFormatID != kAudioFormatLinearPCM)
442 ERR("Not PCM Wave\n");
443 err = -1;
445 if (aDesc.mFormatFlags != kLinearPCMFormatFlagIsSignedInteger +
446 kAudioFormatFlagIsPacked)
448 ERR("Unhandled Flags\n");
449 err = -1;
451 if (aDesc.mFramesPerPacket != 1)
453 ERR("Unhandled Frames per packet %li\n",aDesc.mFramesPerPacket);
454 err = -1;
456 if (aDesc.mChannelsPerFrame != pvi->nChannels)
458 ERR("Unhandled channel count %li\n",aDesc.mChannelsPerFrame);
459 err = -1;
461 if (aDesc.mBitsPerChannel != pvi->wBitsPerSample)
463 ERR("Unhandled bits per channel %li\n",aDesc.mBitsPerChannel);
464 err = -1;
466 if (aDesc.mSampleRate != pvi->nSamplesPerSec)
468 ERR("Unhandled sample rate %f\n",aDesc.mSampleRate);
469 err = -1;
472 if (err != noErr)
474 ERR("Failed to create Extraction Session\n");
475 MovieAudioExtractionEnd(filter->aSession);
476 filter->aSession = NULL;
479 return err;
482 static DWORD WINAPI QTSplitter_thread(LPVOID data)
484 QTSplitter *This = (QTSplitter *)data;
485 HRESULT hr = S_OK;
486 TimeValue next_time;
487 CVPixelBufferRef pixelBuffer = NULL;
488 OSStatus err;
489 TimeRecord tr;
491 if (This->pAudio_Pin)
493 /* according to QA1469 a movie has to be fully loaded before we
494 can reliably start the Extraction session */
496 while(GetMovieLoadState(This->pQTMovie) < kMovieLoadStateComplete)
497 MoviesTask(This->pQTMovie,1000);
499 QT_Create_Extract_Session(This);
502 WaitForSingleObject(This->runEvent, -1);
504 EnterCriticalSection(&This->csReceive);
505 This->state = State_Running;
506 /* Prime the pump: Needed for MPEG streams */
507 GetMovieNextInterestingTime(This->pQTMovie, nextTimeEdgeOK | nextTimeStep, 0, NULL, This->movie_time, 1, &next_time, NULL);
509 GetMovieTime(This->pQTMovie, &tr);
510 LeaveCriticalSection(&This->csReceive);
513 LONGLONG tStart=0, tStop=0;
514 LONGLONG mStart=0, mStop=0;
515 float time;
517 EnterCriticalSection(&This->csReceive);
518 GetMovieNextInterestingTime(This->pQTMovie, nextTimeStep, 0, NULL, This->movie_time, 1, &next_time, NULL);
520 if (next_time == -1)
522 TRACE("No next time\n");
523 LeaveCriticalSection(&This->csReceive);
524 break;
527 tr.value = SInt64ToWide(next_time);
528 SetMovieTime(This->pQTMovie, &tr);
529 MoviesTask(This->pQTMovie,0);
530 QTVisualContextTask(This->vContext);
532 TRACE("In loop at time %ld\n",This->movie_time);
533 TRACE("In Next time %ld\n",next_time);
535 mStart = This->movie_time;
536 mStop = next_time;
538 time = (float)(This->movie_time - This->movie_start) / This->movie_scale;
539 tStart = time * 10000000;
540 time = (float)(next_time - This->movie_start) / This->movie_scale;
541 tStop = time * 10000000;
543 /* Deliver Audio */
544 if (This->pAudio_Pin && This->pAudio_Pin->pin.pin.pConnectedTo && This->aSession)
546 int data_size=0;
547 BYTE* ptr;
548 IMediaSample *sample = NULL;
549 AudioBufferList aData;
550 UInt32 flags;
551 UInt32 frames;
552 WAVEFORMATEX* pvi;
553 float duration;
555 pvi = (WAVEFORMATEX*)This->pAudio_Pin->pmt->pbFormat;
557 hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->pAudio_Pin->pin, &sample, NULL, NULL, 0);
559 if (FAILED(hr))
561 ERR("Audio: Unable to get delivery buffer (%x)\n", hr);
562 goto audio_error;
565 hr = IMediaSample_GetPointer(sample, &ptr);
566 if (FAILED(hr))
568 ERR("Audio: Unable to get pointer to buffer (%x)\n", hr);
569 goto audio_error;
572 duration = (float)next_time / This->movie_scale;
573 time = (float)This->movie_time / This->movie_scale;
574 duration -= time;
575 frames = pvi->nSamplesPerSec * duration;
576 TRACE("Need audio for %f seconds (%li frames)\n",duration,frames);
578 data_size = IMediaSample_GetSize(sample);
579 if (data_size < frames * pvi->nBlockAlign)
580 FIXME("Audio buffer is too small\n");
582 aData.mNumberBuffers = 1;
583 aData.mBuffers[0].mNumberChannels = pvi->nChannels;
584 aData.mBuffers[0].mDataByteSize = data_size;
585 aData.mBuffers[0].mData = ptr;
587 err = MovieAudioExtractionFillBuffer(This->aSession, &frames, &aData, &flags);
588 TRACE("Got %i frames\n",(int)frames);
590 IMediaSample_SetActualDataLength(sample, frames * pvi->nBlockAlign);
592 IMediaSample_SetMediaTime(sample, &mStart, &mStop);
593 IMediaSample_SetTime(sample, &tStart, &tStop);
595 hr = OutputQueue_Receive(This->pAudio_Pin->queue, sample);
596 TRACE("Audio Delivered (%x)\n",hr);
598 audio_error:
599 if (sample)
600 IMediaSample_Release(sample);
602 else
603 TRACE("Audio Pin not connected or no Audio\n");
605 /* Deliver Video */
606 if (This->pVideo_Pin && QTVisualContextIsNewImageAvailable(This->vContext,0))
608 err = QTVisualContextCopyImageForTime(This->vContext, NULL, NULL, &pixelBuffer);
609 if (err == noErr)
611 int data_size=0;
612 BYTE* ptr;
613 IMediaSample *sample = NULL;
615 hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->pVideo_Pin->pin, &sample, NULL, NULL, 0);
616 if (FAILED(hr))
618 ERR("Video: Unable to get delivery buffer (%x)\n", hr);
619 goto video_error;
622 data_size = IMediaSample_GetSize(sample);
623 if (data_size < This->outputSize)
625 ERR("Sample size is too small %d < %d\n", data_size, This->outputSize)
627 hr = E_FAIL;
628 goto video_error;
631 hr = IMediaSample_GetPointer(sample, &ptr);
632 if (FAILED(hr))
634 ERR("Video: Unable to get pointer to buffer (%x)\n", hr);
635 goto video_error;
638 hr = AccessPixelBufferPixels( pixelBuffer, ptr);
639 if (FAILED(hr))
641 ERR("Failed to access Pixels\n");
642 goto video_error;
645 IMediaSample_SetActualDataLength(sample, This->outputSize);
647 IMediaSample_SetMediaTime(sample, &mStart, &mStop);
648 IMediaSample_SetTime(sample, &tStart, &tStop);
650 hr = OutputQueue_Receive(This->pVideo_Pin->queue, sample);
651 TRACE("Video Delivered (%x)\n",hr);
653 video_error:
654 if (sample)
655 IMediaSample_Release(sample);
656 if (pixelBuffer)
657 CVPixelBufferRelease(pixelBuffer);
660 else
661 TRACE("No video to deliver\n");
663 This->movie_time = next_time;
664 LeaveCriticalSection(&This->csReceive);
665 } while (hr == S_OK);
667 This->state = State_Stopped;
668 if (This->pAudio_Pin)
669 OutputQueue_EOS(This->pAudio_Pin->queue);
670 if (This->pVideo_Pin)
671 OutputQueue_EOS(This->pVideo_Pin->queue);
673 return hr;
676 static HRESULT WINAPI QT_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
678 HRESULT hr = S_OK;
679 QTSplitter *This = impl_from_IBaseFilter(iface);
680 HRESULT hr_any = VFW_E_NOT_CONNECTED;
682 TRACE("(%s)\n", wine_dbgstr_longlong(tStart));
684 EnterCriticalSection(&This->csReceive);
685 This->filter.rtStreamStart = tStart;
687 if (This->pVideo_Pin)
688 hr = BaseOutputPinImpl_Active(&This->pVideo_Pin->pin);
689 if (SUCCEEDED(hr))
690 hr_any = hr;
691 if (This->pAudio_Pin)
692 hr = BaseOutputPinImpl_Active(&This->pAudio_Pin->pin);
693 if (SUCCEEDED(hr))
694 hr_any = hr;
696 hr = hr_any;
698 SetEvent(This->runEvent);
699 LeaveCriticalSection(&This->csReceive);
701 return hr;
704 static HRESULT WINAPI QT_GetState(IBaseFilter *iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
706 QTSplitter *This = impl_from_IBaseFilter(iface);
707 TRACE("(%d, %p)\n", dwMilliSecsTimeout, pState);
709 *pState = This->state;
711 return S_OK;
714 static HRESULT WINAPI QT_FindPin(IBaseFilter *iface, LPCWSTR Id, IPin **ppPin)
716 FIXME("(%p)->(%s,%p) stub\n", iface, debugstr_w(Id), ppPin);
717 return E_NOTIMPL;
720 static const IBaseFilterVtbl QT_Vtbl = {
721 QT_QueryInterface,
722 BaseFilterImpl_AddRef,
723 QT_Release,
724 BaseFilterImpl_GetClassID,
725 QT_Stop,
726 QT_Pause,
727 QT_Run,
728 QT_GetState,
729 BaseFilterImpl_SetSyncSource,
730 BaseFilterImpl_GetSyncSource,
731 BaseFilterImpl_EnumPins,
732 QT_FindPin,
733 BaseFilterImpl_QueryFilterInfo,
734 BaseFilterImpl_JoinFilterGraph,
735 BaseFilterImpl_QueryVendorInfo
739 * Input Pin
741 static HRESULT QT_RemoveOutputPins(QTSplitter *This)
743 HRESULT hr;
744 TRACE("(%p)\n", This);
746 if (This->pVideo_Pin)
748 OutputQueue_Destroy(This->pVideo_Pin->queue);
749 hr = BaseOutputPinImpl_BreakConnect(&This->pVideo_Pin->pin);
750 TRACE("Disconnect: %08x\n", hr);
751 IPin_Release(&This->pVideo_Pin->pin.pin.IPin_iface);
752 This->pVideo_Pin = NULL;
754 if (This->pAudio_Pin)
756 OutputQueue_Destroy(This->pAudio_Pin->queue);
757 hr = BaseOutputPinImpl_BreakConnect(&This->pAudio_Pin->pin);
758 TRACE("Disconnect: %08x\n", hr);
759 IPin_Release(&This->pAudio_Pin->pin.pin.IPin_iface);
760 This->pAudio_Pin = NULL;
763 BaseFilterImpl_IncrementPinVersion(&This->filter);
764 return S_OK;
767 static inline QTInPin *impl_from_IPin( IPin *iface )
769 return CONTAINING_RECORD(iface, QTInPin, pin.IPin_iface);
772 static ULONG WINAPI QTInPin_Release(IPin *iface)
774 QTInPin *This = impl_from_IPin(iface);
775 ULONG refCount = InterlockedDecrement(&This->pin.refCount);
777 TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
778 if (!refCount)
780 FreeMediaType(&This->pin.mtCurrent);
781 if (This->pAlloc)
782 IMemAllocator_Release(This->pAlloc);
783 This->pAlloc = NULL;
784 This->pin.IPin_iface.lpVtbl = NULL;
785 return 0;
787 else
788 return refCount;
791 static HRESULT QT_Process_Video_Track(QTSplitter* filter, Track trk)
793 AM_MEDIA_TYPE amt;
794 VIDEOINFOHEADER * pvi;
795 PIN_INFO piOutput;
796 HRESULT hr = S_OK;
797 OSErr err;
798 static const WCHAR szwVideoOut[] = {'V','i','d','e','o',0};
799 CFMutableDictionaryRef pixelBufferOptions = NULL;
800 CFMutableDictionaryRef visualContextOptions = NULL;
801 CFNumberRef n = NULL;
802 int t;
803 DWORD outputWidth, outputHeight, outputDepth;
804 Fixed trackWidth, trackHeight;
806 ZeroMemory(&amt, sizeof(amt));
807 amt.formattype = FORMAT_VideoInfo;
808 amt.majortype = MEDIATYPE_Video;
809 amt.subtype = MEDIASUBTYPE_RGB24;
811 GetTrackDimensions(trk, &trackWidth, &trackHeight);
813 outputDepth = 3;
814 outputWidth = Fix2Long(trackWidth);
815 outputHeight = Fix2Long(trackHeight);
816 TRACE("Width %i Height %i\n",outputWidth, outputHeight);
818 amt.cbFormat = sizeof(VIDEOINFOHEADER);
819 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
820 ZeroMemory(amt.pbFormat, amt.cbFormat);
821 pvi = (VIDEOINFOHEADER *)amt.pbFormat;
822 pvi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
823 pvi->bmiHeader.biWidth = outputWidth;
824 pvi->bmiHeader.biHeight = outputHeight;
825 pvi->bmiHeader.biPlanes = 1;
826 pvi->bmiHeader.biBitCount = 24;
827 pvi->bmiHeader.biCompression = BI_RGB;
828 pvi->bmiHeader.biSizeImage = outputWidth * outputHeight * outputDepth;
830 filter->outputSize = pvi->bmiHeader.biSizeImage;
831 amt.lSampleSize = 0;
833 pixelBufferOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
835 t = k32ARGBPixelFormat;
836 n = CFNumberCreate(NULL, kCFNumberIntType, &t);
837 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferPixelFormatTypeKey, n);
838 CFRelease(n);
840 n = CFNumberCreate(NULL, kCFNumberIntType, &outputWidth);
841 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferWidthKey, n);
842 CFRelease(n);
844 n = CFNumberCreate(NULL, kCFNumberIntType, &outputHeight);
845 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferHeightKey, n);
846 CFRelease(n);
848 t = 16;
849 n = CFNumberCreate(NULL, kCFNumberIntType, &t);
850 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferBytesPerRowAlignmentKey, n);
851 CFRelease(n);
853 visualContextOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
855 CFDictionarySetValue(visualContextOptions, kQTVisualContextPixelBufferAttributesKey, pixelBufferOptions);
857 err = QTPixelBufferContextCreate(NULL, visualContextOptions,&filter->vContext);
858 CFRelease(pixelBufferOptions);
859 CFRelease(visualContextOptions);
860 if (err != noErr)
862 ERR("Failed to create Visual Context\n");
863 return E_FAIL;
866 err = SetMovieVisualContext(filter->pQTMovie, filter->vContext);
867 if (err != noErr)
869 ERR("Failed to set Visual Context\n");
870 return E_FAIL;
873 piOutput.dir = PINDIR_OUTPUT;
874 piOutput.pFilter = &filter->filter.IBaseFilter_iface;
875 lstrcpyW(piOutput.achName,szwVideoOut);
877 hr = QT_AddPin(filter, &piOutput, &amt, TRUE);
878 if (FAILED(hr))
879 ERR("Failed to add Video Track\n");
880 else
881 TRACE("Video Pin %p\n",filter->pVideo_Pin);
883 return hr;
886 static HRESULT QT_Process_Audio_Track(QTSplitter* filter, Track trk)
888 AM_MEDIA_TYPE amt;
889 WAVEFORMATEX* pvi;
890 PIN_INFO piOutput;
891 HRESULT hr = S_OK;
892 static const WCHAR szwAudioOut[] = {'A','u','d','i','o',0};
893 Media audioMedia;
895 SoundDescriptionHandle aDesc = (SoundDescriptionHandle) NewHandle(sizeof(SoundDescription));
897 audioMedia = GetTrackMedia(trk);
898 GetMediaSampleDescription(audioMedia, 1, (SampleDescriptionHandle)aDesc);
900 ZeroMemory(&amt, sizeof(amt));
901 amt.formattype = FORMAT_WaveFormatEx;
902 amt.majortype = MEDIATYPE_Audio;
903 amt.subtype = MEDIASUBTYPE_PCM;
904 amt.bTemporalCompression = 0;
906 amt.cbFormat = sizeof(WAVEFORMATEX);
907 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
908 ZeroMemory(amt.pbFormat, amt.cbFormat);
909 pvi = (WAVEFORMATEX*)amt.pbFormat;
911 pvi->cbSize = sizeof(WAVEFORMATEX);
912 pvi->wFormatTag = WAVE_FORMAT_PCM;
913 pvi->nChannels = ((SoundDescription)**aDesc).numChannels;
914 if (pvi->nChannels < 1 || pvi->nChannels > 2)
915 pvi->nChannels = 2;
916 pvi->nSamplesPerSec = (((SoundDescription)**aDesc).sampleRate/65536);
917 if (pvi->nSamplesPerSec < 8000 || pvi->nChannels > 48000)
918 pvi->nSamplesPerSec = 44100;
919 pvi->wBitsPerSample = ((SoundDescription)**aDesc).sampleSize;
920 if (pvi->wBitsPerSample < 8 || pvi->wBitsPerSample > 32)
921 pvi->wBitsPerSample = 16;
922 pvi->nBlockAlign = (pvi->nChannels * pvi->wBitsPerSample) / 8;
923 pvi->nAvgBytesPerSec = pvi->nSamplesPerSec * pvi->nBlockAlign;
925 DisposeHandle((Handle)aDesc);
927 piOutput.dir = PINDIR_OUTPUT;
928 piOutput.pFilter = &filter->filter.IBaseFilter_iface;
929 lstrcpyW(piOutput.achName,szwAudioOut);
931 hr = QT_AddPin(filter, &piOutput, &amt, FALSE);
932 if (FAILED(hr))
933 ERR("Failed to add Audio Track\n");
934 else
935 TRACE("Audio Pin %p\n",filter->pAudio_Pin);
936 return hr;
939 static HRESULT QT_Process_Movie(QTSplitter* filter)
941 HRESULT hr = S_OK;
942 OSErr err;
943 WineDataRefRecord ptrDataRefRec;
944 Handle dataRef = NULL;
945 Track trk;
946 short id = 0;
947 DWORD tid;
948 HANDLE thread;
949 LONGLONG time;
951 TRACE("Trying movie connect\n");
953 ptrDataRefRec.pReader = filter->pInputPin.pReader;
954 ptrDataRefRec.streamSubtype = filter->pInputPin.subType;
955 PtrToHand( &ptrDataRefRec, &dataRef, sizeof(WineDataRefRecord));
957 err = NewMovieFromDataRef(&filter->pQTMovie, newMovieActive|newMovieDontInteractWithUser|newMovieDontAutoUpdateClock|newMovieDontAskUnresolvedDataRefs|newMovieAsyncOK, &id, dataRef, 'WINE');
959 DisposeHandle(dataRef);
961 if (err != noErr)
963 FIXME("QuickTime cannot handle media type(%i)\n",err);
964 return VFW_E_TYPE_NOT_ACCEPTED;
967 PrePrerollMovie(filter->pQTMovie, 0, fixed1, NULL, NULL);
968 PrerollMovie(filter->pQTMovie, 0, fixed1);
969 GoToBeginningOfMovie(filter->pQTMovie);
970 SetMovieActive(filter->pQTMovie,TRUE);
972 if (GetMovieLoadState(filter->pQTMovie) < kMovieLoadStateLoaded)
973 MoviesTask(filter->pQTMovie,100);
975 trk = GetMovieIndTrackType(filter->pQTMovie, 1, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
976 TRACE("%p is a video track\n",trk);
977 if (trk)
978 hr = QT_Process_Video_Track(filter, trk);
980 if (FAILED(hr))
981 return hr;
983 trk = GetMovieIndTrackType(filter->pQTMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
984 TRACE("%p is a audio track\n",trk);
985 if (trk)
986 hr = QT_Process_Audio_Track(filter, trk);
988 time = GetMovieDuration(filter->pQTMovie);
989 filter->movie_scale = GetMovieTimeScale(filter->pQTMovie);
990 filter->sourceSeeking.llDuration = ((double)time / filter->movie_scale) * 10000000;
991 filter->sourceSeeking.llStop = filter->sourceSeeking.llDuration;
993 TRACE("Movie duration is %s\n",wine_dbgstr_longlong(filter->sourceSeeking.llDuration));
995 thread = CreateThread(NULL, 0, QTSplitter_thread, filter, 0, &tid);
996 if (thread)
998 TRACE("Created thread 0x%08x\n", tid);
999 CloseHandle(thread);
1001 else
1002 hr = HRESULT_FROM_WIN32(GetLastError());
1004 return hr;
1007 static HRESULT WINAPI QTInPin_ReceiveConnection(IPin *iface, IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
1009 HRESULT hr = S_OK;
1010 ALLOCATOR_PROPERTIES props;
1011 QTInPin *This = impl_from_IPin(iface);
1013 TRACE("(%p/%p)->(%p, %p)\n", This, iface, pReceivePin, pmt);
1015 EnterCriticalSection(This->pin.pCritSec);
1016 This->pReader = NULL;
1018 if (This->pin.pConnectedTo)
1019 hr = VFW_E_ALREADY_CONNECTED;
1020 else if (IPin_QueryAccept(iface, pmt) != S_OK)
1021 hr = VFW_E_TYPE_NOT_ACCEPTED;
1022 else
1024 PIN_DIRECTION pindirReceive;
1025 IPin_QueryDirection(pReceivePin, &pindirReceive);
1026 if (pindirReceive != PINDIR_OUTPUT)
1027 hr = VFW_E_INVALID_DIRECTION;
1030 if (FAILED(hr))
1032 LeaveCriticalSection(This->pin.pCritSec);
1033 return hr;
1036 hr = IPin_QueryInterface(pReceivePin, &IID_IAsyncReader, (LPVOID *)&This->pReader);
1037 if (FAILED(hr))
1039 LeaveCriticalSection(This->pin.pCritSec);
1040 TRACE("Input source is not an AsyncReader\n");
1041 return hr;
1044 LeaveCriticalSection(This->pin.pCritSec);
1045 EnterCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1046 hr = QT_Process_Movie(impl_from_IBaseFilter(This->pin.pinInfo.pFilter));
1047 if (FAILED(hr))
1049 LeaveCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1050 TRACE("Unable to process movie\n");
1051 return hr;
1054 This->pAlloc = NULL;
1055 props.cBuffers = 8;
1056 props.cbAlign = 1;
1057 props.cbBuffer = impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->outputSize + props.cbAlign;
1058 props.cbPrefix = 0;
1060 hr = IAsyncReader_RequestAllocator(This->pReader, NULL, &props, &This->pAlloc);
1061 if (SUCCEEDED(hr))
1063 CopyMediaType(&This->pin.mtCurrent, pmt);
1064 This->pin.pConnectedTo = pReceivePin;
1065 IPin_AddRef(pReceivePin);
1066 hr = IMemAllocator_Commit(This->pAlloc);
1068 else
1070 QT_RemoveOutputPins(impl_from_IBaseFilter(This->pin.pinInfo.pFilter));
1071 if (This->pReader)
1072 IAsyncReader_Release(This->pReader);
1073 This->pReader = NULL;
1074 if (This->pAlloc)
1075 IMemAllocator_Release(This->pAlloc);
1076 This->pAlloc = NULL;
1078 TRACE("Size: %i\n", props.cbBuffer);
1079 LeaveCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1081 return hr;
1084 static HRESULT WINAPI QTInPin_Disconnect(IPin *iface)
1086 HRESULT hr;
1087 QTInPin *This = impl_from_IPin(iface);
1088 FILTER_STATE state;
1089 TRACE("()\n");
1091 hr = IBaseFilter_GetState(This->pin.pinInfo.pFilter, INFINITE, &state);
1092 EnterCriticalSection(This->pin.pCritSec);
1093 if (This->pin.pConnectedTo)
1095 QTSplitter *Parser = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
1097 if (SUCCEEDED(hr) && state == State_Stopped)
1099 IMemAllocator_Decommit(This->pAlloc);
1100 IPin_Disconnect(This->pin.pConnectedTo);
1101 This->pin.pConnectedTo = NULL;
1102 hr = QT_RemoveOutputPins(Parser);
1104 else
1105 hr = VFW_E_NOT_STOPPED;
1107 else
1108 hr = S_FALSE;
1109 LeaveCriticalSection(This->pin.pCritSec);
1110 return hr;
1113 static HRESULT WINAPI QTInPin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *pmt)
1115 QTInPin *This = impl_from_IPin(iface);
1117 TRACE("(%p)->(%p)\n", This, pmt);
1119 if (IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream))
1121 This->subType = pmt->subtype;
1122 return S_OK;
1124 return S_FALSE;
1127 static HRESULT WINAPI QTInPin_EndOfStream(IPin *iface)
1129 QTInPin *pin = impl_from_IPin(iface);
1130 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1132 FIXME("Propagate message on %p\n", This);
1133 return S_OK;
1136 static HRESULT WINAPI QTInPin_BeginFlush(IPin *iface)
1138 QTInPin *pin = impl_from_IPin(iface);
1139 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1141 FIXME("Propagate message on %p\n", This);
1142 return S_OK;
1145 static HRESULT WINAPI QTInPin_EndFlush(IPin *iface)
1147 QTInPin *pin = impl_from_IPin(iface);
1148 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1150 FIXME("Propagate message on %p\n", This);
1151 return S_OK;
1154 static HRESULT WINAPI QTInPin_NewSegment(IPin *iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
1156 QTInPin *pin = impl_from_IPin(iface);
1157 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1159 BasePinImpl_NewSegment(iface, tStart, tStop, dRate);
1160 FIXME("Propagate message on %p\n", This);
1161 return S_OK;
1164 static HRESULT WINAPI QTInPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
1166 QTInPin *This = impl_from_IPin(iface);
1168 TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
1170 *ppv = NULL;
1172 if (IsEqualIID(riid, &IID_IUnknown))
1173 *ppv = iface;
1174 else if (IsEqualIID(riid, &IID_IPin))
1175 *ppv = iface;
1176 else if (IsEqualIID(riid, &IID_IMediaSeeking))
1177 return IBaseFilter_QueryInterface(This->pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
1179 if (*ppv)
1181 IUnknown_AddRef((IUnknown *)(*ppv));
1182 return S_OK;
1185 FIXME("No interface for %s!\n", debugstr_guid(riid));
1187 return E_NOINTERFACE;
1190 static HRESULT WINAPI QTInPin_EnumMediaTypes(IPin *iface, IEnumMediaTypes **ppEnum)
1192 QTInPin *This = impl_from_IPin(iface);
1194 TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
1196 return EnumMediaTypes_Construct(&This->pin, BasePinImpl_GetMediaType, BasePinImpl_GetMediaTypeVersion, ppEnum);
1199 static const IPinVtbl QT_InputPin_Vtbl = {
1200 QTInPin_QueryInterface,
1201 BasePinImpl_AddRef,
1202 QTInPin_Release,
1203 BaseInputPinImpl_Connect,
1204 QTInPin_ReceiveConnection,
1205 QTInPin_Disconnect,
1206 BasePinImpl_ConnectedTo,
1207 BasePinImpl_ConnectionMediaType,
1208 BasePinImpl_QueryPinInfo,
1209 BasePinImpl_QueryDirection,
1210 BasePinImpl_QueryId,
1211 QTInPin_QueryAccept,
1212 QTInPin_EnumMediaTypes,
1213 BasePinImpl_QueryInternalConnections,
1214 QTInPin_EndOfStream,
1215 QTInPin_BeginFlush,
1216 QTInPin_EndFlush,
1217 QTInPin_NewSegment
1221 * Output Pin
1223 static inline QTOutPin *impl_QTOutPin_from_IPin( IPin *iface )
1225 return CONTAINING_RECORD(iface, QTOutPin, pin.pin.IPin_iface);
1228 static inline QTOutPin *impl_QTOutPin_from_BasePin( BasePin *iface )
1230 return CONTAINING_RECORD(iface, QTOutPin, pin.pin);
1233 static inline QTOutPin *impl_QTOutPin_from_BaseOutputPin( BaseOutputPin *iface )
1235 return CONTAINING_RECORD(iface, QTOutPin, pin);
1238 static HRESULT WINAPI QTOutPin_QueryInterface(IPin *iface, REFIID riid, void **ppv)
1240 QTOutPin *This = impl_QTOutPin_from_IPin(iface);
1242 TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
1244 *ppv = NULL;
1246 if (IsEqualIID(riid, &IID_IUnknown))
1247 *ppv = iface;
1248 else if (IsEqualIID(riid, &IID_IPin))
1249 *ppv = iface;
1250 else if (IsEqualIID(riid, &IID_IMediaSeeking))
1251 return IBaseFilter_QueryInterface(This->pin.pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
1252 else if (IsEqualIID(riid, &IID_IQualityControl))
1253 *ppv = &This->IQualityControl_iface;
1255 if (*ppv)
1257 IUnknown_AddRef((IUnknown *)(*ppv));
1258 return S_OK;
1260 FIXME("No interface for %s!\n", debugstr_guid(riid));
1261 return E_NOINTERFACE;
1264 static ULONG WINAPI QTOutPin_Release(IPin *iface)
1266 QTOutPin *This = impl_QTOutPin_from_IPin(iface);
1267 ULONG refCount = InterlockedDecrement(&This->pin.pin.refCount);
1268 TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
1270 if (!refCount)
1272 DeleteMediaType(This->pmt);
1273 FreeMediaType(&This->pin.pin.mtCurrent);
1274 CoTaskMemFree(This);
1275 return 0;
1277 return refCount;
1280 static HRESULT WINAPI QTOutPin_GetMediaType(BasePin *iface, int iPosition, AM_MEDIA_TYPE *pmt)
1282 QTOutPin *This = impl_QTOutPin_from_BasePin(iface);
1284 if (iPosition < 0)
1285 return E_INVALIDARG;
1286 if (iPosition > 0)
1287 return VFW_S_NO_MORE_ITEMS;
1288 CopyMediaType(pmt, This->pmt);
1289 return S_OK;
1292 static HRESULT WINAPI QTOutPin_DecideBufferSize(BaseOutputPin *iface, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
1294 /* Unused */
1295 return S_OK;
1298 static HRESULT WINAPI QTOutPin_DecideAllocator(BaseOutputPin *iface, IMemInputPin *pPin, IMemAllocator **pAlloc)
1300 HRESULT hr;
1301 QTOutPin *This = impl_QTOutPin_from_BaseOutputPin(iface);
1302 QTSplitter *QTfilter = impl_from_IBaseFilter(This->pin.pin.pinInfo.pFilter);
1304 *pAlloc = NULL;
1305 if (QTfilter->pInputPin.pAlloc)
1306 hr = IMemInputPin_NotifyAllocator(pPin, QTfilter->pInputPin.pAlloc, FALSE);
1307 else
1308 hr = VFW_E_NO_ALLOCATOR;
1310 return hr;
1313 static HRESULT WINAPI QTOutPin_BreakConnect(BaseOutputPin *This)
1315 HRESULT hr;
1317 TRACE("(%p)->()\n", This);
1319 EnterCriticalSection(This->pin.pCritSec);
1320 if (!This->pin.pConnectedTo || !This->pMemInputPin)
1321 hr = VFW_E_NOT_CONNECTED;
1322 else
1324 hr = IPin_Disconnect(This->pin.pConnectedTo);
1325 IPin_Disconnect(&This->pin.IPin_iface);
1327 LeaveCriticalSection(This->pin.pCritSec);
1329 return hr;
1332 static const IPinVtbl QT_OutputPin_Vtbl = {
1333 QTOutPin_QueryInterface,
1334 BasePinImpl_AddRef,
1335 QTOutPin_Release,
1336 BaseOutputPinImpl_Connect,
1337 BaseOutputPinImpl_ReceiveConnection,
1338 BaseOutputPinImpl_Disconnect,
1339 BasePinImpl_ConnectedTo,
1340 BasePinImpl_ConnectionMediaType,
1341 BasePinImpl_QueryPinInfo,
1342 BasePinImpl_QueryDirection,
1343 BasePinImpl_QueryId,
1344 BasePinImpl_QueryAccept,
1345 BasePinImpl_EnumMediaTypes,
1346 BasePinImpl_QueryInternalConnections,
1347 BaseOutputPinImpl_EndOfStream,
1348 BaseOutputPinImpl_BeginFlush,
1349 BaseOutputPinImpl_EndFlush,
1350 BasePinImpl_NewSegment
1353 static inline QTOutPin *impl_from_IQualityControl( IQualityControl *iface )
1355 return CONTAINING_RECORD(iface, QTOutPin, IQualityControl_iface);
1358 HRESULT WINAPI QT_QualityControl_QueryInterface(IQualityControl *iface, REFIID riid, void **ppv)
1360 QTOutPin *This = impl_from_IQualityControl(iface);
1361 return IPin_QueryInterface(&This->pin.pin.IPin_iface, riid, ppv);
1364 ULONG WINAPI QT_QualityControl_AddRef(IQualityControl *iface)
1366 QTOutPin *This = impl_from_IQualityControl(iface);
1367 return IPin_AddRef(&This->pin.pin.IPin_iface);
1370 ULONG WINAPI QT_QualityControl_Release(IQualityControl *iface)
1372 QTOutPin *This = impl_from_IQualityControl(iface);
1373 return IPin_Release(&This->pin.pin.IPin_iface);
1376 static HRESULT WINAPI QT_QualityControl_Notify(IQualityControl *iface, IBaseFilter *sender, Quality qm)
1378 REFERENCE_TIME late = qm.Late;
1379 if (qm.Late < 0 && -qm.Late > qm.TimeStamp)
1380 late = -qm.TimeStamp;
1381 /* TODO: Do Something */
1382 return S_OK;
1385 HRESULT WINAPI QT_QualityControl_SetSink(IQualityControl *iface, IQualityControl *tonotify)
1387 /* Do nothing */
1388 return S_OK;
1391 static const IQualityControlVtbl QTOutPin_QualityControl_Vtbl = {
1392 QT_QualityControl_QueryInterface,
1393 QT_QualityControl_AddRef,
1394 QT_QualityControl_Release,
1395 QT_QualityControl_Notify,
1396 QT_QualityControl_SetSink
1399 static const BasePinFuncTable output_BaseFuncTable = {
1400 NULL,
1401 BaseOutputPinImpl_AttemptConnection,
1402 BasePinImpl_GetMediaTypeVersion,
1403 QTOutPin_GetMediaType
1406 static const BaseOutputPinFuncTable output_BaseOutputFuncTable = {
1407 QTOutPin_DecideBufferSize,
1408 QTOutPin_DecideAllocator,
1409 QTOutPin_BreakConnect
1412 static const OutputQueueFuncTable output_OutputQueueFuncTable = {
1413 OutputQueueImpl_ThreadProc
1416 static HRESULT QT_AddPin(QTSplitter *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt, BOOL video)
1418 HRESULT hr;
1419 IPin **target;
1421 if (video)
1422 target = (IPin**)&This->pVideo_Pin;
1423 else
1424 target = (IPin**)&This->pAudio_Pin;
1426 if (*target != NULL)
1428 FIXME("We already have a %s pin\n",(video)?"video":"audio");
1429 return E_FAIL;
1432 hr = BaseOutputPin_Construct(&QT_OutputPin_Vtbl, sizeof(QTOutPin), piOutput, &output_BaseFuncTable, &output_BaseOutputFuncTable, &This->filter.csFilter, (IPin**)target);
1433 if (SUCCEEDED(hr))
1435 QTOutPin *pin = (QTOutPin*)*target;
1436 pin->pmt = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
1437 CopyMediaType(pin->pmt, amt);
1438 pin->pin.pin.pinInfo.pFilter = (LPVOID)This;
1439 pin->IQualityControl_iface.lpVtbl = &QTOutPin_QualityControl_Vtbl;
1441 BaseFilterImpl_IncrementPinVersion(&This->filter);
1443 hr = OutputQueue_Construct(&pin->pin, TRUE, TRUE, 5, FALSE, THREAD_PRIORITY_NORMAL, &output_OutputQueueFuncTable, &pin->queue);
1445 else
1446 ERR("Failed with error %x\n", hr);
1447 return hr;
1450 static HRESULT WINAPI QTSplitter_ChangeStart(IMediaSeeking *iface)
1452 QTSplitter *This = impl_from_IMediaSeeking(iface);
1453 TRACE("(%p)\n", iface);
1454 EnterCriticalSection(&This->csReceive);
1455 This->movie_time = (This->sourceSeeking.llCurrent * This->movie_scale)/10000000;
1456 This->movie_start = This->movie_time;
1457 LeaveCriticalSection(&This->csReceive);
1458 return S_OK;
1461 static HRESULT WINAPI QTSplitter_ChangeStop(IMediaSeeking *iface)
1463 FIXME("(%p) filter hasn't implemented stop position change!\n", iface);
1464 return S_OK;
1467 static HRESULT WINAPI QTSplitter_ChangeRate(IMediaSeeking *iface)
1469 FIXME("(%p) filter hasn't implemented rate change!\n", iface);
1470 return S_OK;
1473 static HRESULT WINAPI QT_Seeking_QueryInterface(IMediaSeeking * iface, REFIID riid, LPVOID * ppv)
1475 QTSplitter *This = impl_from_IMediaSeeking(iface);
1477 return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv);
1480 static ULONG WINAPI QT_Seeking_AddRef(IMediaSeeking * iface)
1482 QTSplitter *This = impl_from_IMediaSeeking(iface);
1484 return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
1487 static ULONG WINAPI QT_Seeking_Release(IMediaSeeking * iface)
1489 QTSplitter *This = impl_from_IMediaSeeking(iface);
1491 return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
1494 static const IMediaSeekingVtbl QT_Seeking_Vtbl =
1496 QT_Seeking_QueryInterface,
1497 QT_Seeking_AddRef,
1498 QT_Seeking_Release,
1499 SourceSeekingImpl_GetCapabilities,
1500 SourceSeekingImpl_CheckCapabilities,
1501 SourceSeekingImpl_IsFormatSupported,
1502 SourceSeekingImpl_QueryPreferredFormat,
1503 SourceSeekingImpl_GetTimeFormat,
1504 SourceSeekingImpl_IsUsingTimeFormat,
1505 SourceSeekingImpl_SetTimeFormat,
1506 SourceSeekingImpl_GetDuration,
1507 SourceSeekingImpl_GetStopPosition,
1508 SourceSeekingImpl_GetCurrentPosition,
1509 SourceSeekingImpl_ConvertTimeFormat,
1510 SourceSeekingImpl_SetPositions,
1511 SourceSeekingImpl_GetPositions,
1512 SourceSeekingImpl_GetAvailable,
1513 SourceSeekingImpl_SetRate,
1514 SourceSeekingImpl_GetRate,
1515 SourceSeekingImpl_GetPreroll