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
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>
68 #undef GetCurrentThread
71 #undef GetCurrentProcess
94 #undef IsWindowVisible
107 #undef STDMETHODCALLTYPE
113 #define NONAMELESSSTRUCT
114 #define NONAMELESSUNION
123 #include "wine/unicode.h"
124 #include "wine/debug.h"
125 #include "wine/strmbase.h"
127 #include "qtprivate.h"
129 WINE_DEFAULT_DEBUG_CHANNEL(qtsplitter);
130 extern CLSID CLSID_QTSplitter;
132 typedef struct QTOutPin {
134 IQualityControl IQualityControl_iface;
140 typedef struct QTInPin {
144 IAsyncReader *pReader;
145 IMemAllocator *pAlloc;
148 typedef struct QTSplitter {
152 QTOutPin *pVideo_Pin;
153 QTOutPin *pAudio_Pin;
155 ALLOCATOR_PROPERTIES props;
158 QTVisualContextRef vContext;
160 MovieAudioExtractionRef aSession;
165 CRITICAL_SECTION csReceive;
167 SourceSeeking sourceSeeking;
168 TimeValue movie_time;
169 TimeValue movie_start;
170 TimeScale movie_scale;
173 HANDLE splitterThread;
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;
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);
184 static HRESULT WINAPI QTSplitter_ChangeStart(IMediaSeeking *iface);
185 static HRESULT WINAPI QTSplitter_ChangeStop(IMediaSeeking *iface);
186 static HRESULT WINAPI QTSplitter_ChangeRate(IMediaSeeking *iface);
188 static inline QTSplitter *impl_from_IMediaSeeking( IMediaSeeking *iface )
190 return CONTAINING_RECORD(iface, QTSplitter, sourceSeeking.IMediaSeeking_iface);
193 static inline QTSplitter *impl_from_BaseFilter( BaseFilter *iface )
195 return CONTAINING_RECORD(iface, QTSplitter, filter);
198 static inline QTSplitter *impl_from_IBaseFilter( IBaseFilter *iface )
200 return CONTAINING_RECORD(iface, QTSplitter, filter.IBaseFilter_iface);
207 static IPin* WINAPI QT_GetPin(BaseFilter *iface, int pos)
209 QTSplitter *This = impl_from_BaseFilter(iface);
210 TRACE("Asking for pos %x\n", pos);
212 if (pos > 2 || pos < 0)
217 IPin_AddRef(&This->pInputPin.pin.IPin_iface);
218 return &This->pInputPin.pin.IPin_iface;
220 if (This->pVideo_Pin)
221 IPin_AddRef(&This->pVideo_Pin->pin.pin.IPin_iface);
222 return &This->pVideo_Pin->pin.pin.IPin_iface;
224 if (This->pAudio_Pin)
225 IPin_AddRef(&This->pAudio_Pin->pin.pin.IPin_iface);
226 return &This->pAudio_Pin->pin.pin.IPin_iface;
232 static LONG WINAPI QT_GetPinCount(BaseFilter *iface)
234 QTSplitter *This = impl_from_BaseFilter(iface);
236 if (This->pAudio_Pin) c++;
237 if (This->pVideo_Pin) c++;
241 static const BaseFilterFuncTable BaseFuncTable = {
246 IUnknown * CALLBACK QTSplitter_create(IUnknown *punkout, HRESULT *phr)
248 IUnknown *obj = NULL;
251 static const WCHAR wcsInputPinName[] = {'I','n','p','u','t',' ','P','i','n',0};
255 RegisterWineDataHandler();
257 This = CoTaskMemAlloc(sizeof(*This));
258 obj = (IUnknown*)This;
261 *phr = E_OUTOFMEMORY;
264 ZeroMemory(This,sizeof(*This));
266 BaseFilter_Init(&This->filter, &QT_Vtbl, &CLSID_QTSplitter, (DWORD_PTR)(__FILE__ ": QTSplitter.csFilter"), &BaseFuncTable);
268 InitializeCriticalSection(&This->csReceive);
269 This->csReceive.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__": QTSplitter.csReceive");
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);
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;
286 SourceSeeking_Init(&This->sourceSeeking, &QT_Seeking_Vtbl, QTSplitter_ChangeStop, QTSplitter_ChangeStart, QTSplitter_ChangeRate, &This->filter.csFilter);
292 static void QT_Destroy(QTSplitter *This)
294 IPin *connected = NULL;
297 TRACE("Destroying\n");
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);
304 IPin_Disconnect(connected);
305 IPin_Release(connected);
307 pinref = IPin_Release(&This->pInputPin.pin.IPin_iface);
310 ERR("pinref should be null, is %u, destroying anyway\n", pinref);
311 assert((LONG)pinref > 0);
314 pinref = IPin_Release(&This->pInputPin.pin.IPin_iface);
319 DisposeMovie(This->pQTMovie);
320 This->pQTMovie = NULL;
323 QTVisualContextRelease(This->vContext);
325 MovieAudioExtractionEnd(This->aSession);
328 LeaveCriticalSection(&This->csReceive);
330 if (This->loaderThread)
332 WaitForSingleObject(This->loaderThread, INFINITE);
333 CloseHandle(This->loaderThread);
335 if (This->splitterThread)
337 SetEvent(This->runEvent);
338 WaitForSingleObject(This->splitterThread, INFINITE);
339 CloseHandle(This->splitterThread);
342 CloseHandle(This->runEvent);
344 This->csReceive.DebugInfo->Spare[0] = 0;
345 DeleteCriticalSection(&This->csReceive);
350 static HRESULT WINAPI QT_QueryInterface(IBaseFilter *iface, REFIID riid, LPVOID *ppv)
352 QTSplitter *This = impl_from_IBaseFilter(iface);
353 TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
357 if (IsEqualIID(riid, &IID_IUnknown))
359 else if (IsEqualIID(riid, &IID_IPersist))
361 else if (IsEqualIID(riid, &IID_IMediaFilter))
363 else if (IsEqualIID(riid, &IID_IBaseFilter))
365 else if (IsEqualIID(riid, &IID_IMediaSeeking))
366 *ppv = &This->sourceSeeking;
370 IUnknown_AddRef((IUnknown *)(*ppv));
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));
378 return E_NOINTERFACE;
381 static ULONG WINAPI QT_Release(IBaseFilter *iface)
383 QTSplitter *This = impl_from_IBaseFilter(iface);
384 ULONG refCount = BaseFilterImpl_Release(iface);
386 TRACE("(%p)->() Release from %d\n", This, refCount + 1);
394 static HRESULT WINAPI QT_Stop(IBaseFilter *iface)
396 QTSplitter *This = impl_from_IBaseFilter(iface);
400 EnterCriticalSection(&This->csReceive);
401 IAsyncReader_BeginFlush(This->pInputPin.pReader);
402 IAsyncReader_EndFlush(This->pInputPin.pReader);
403 LeaveCriticalSection(&This->csReceive);
408 static HRESULT WINAPI QT_Pause(IBaseFilter *iface)
416 static OSErr QT_Create_Extract_Session(QTSplitter *filter)
418 AudioStreamBasicDescription aDesc;
422 pvi = (WAVEFORMATEX*)filter->pAudio_Pin->pmt->pbFormat;
424 err = MovieAudioExtractionBegin(filter->pQTMovie, 0, &filter->aSession);
427 ERR("Failed to begin Extraction session %i\n",err);
431 err = MovieAudioExtractionGetProperty(filter->aSession,
432 kQTPropertyClass_MovieAudioExtraction_Audio,
433 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
434 sizeof(AudioStreamBasicDescription), &aDesc, NULL);
438 MovieAudioExtractionEnd(filter->aSession);
439 filter->aSession = NULL;
440 ERR("Failed to get session description %i\n",err);
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;
454 err = MovieAudioExtractionSetProperty(filter->aSession,
455 kQTPropertyClass_MovieAudioExtraction_Audio,
456 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
457 sizeof(AudioStreamBasicDescription), &aDesc);
459 if (aDesc.mFormatID != kAudioFormatLinearPCM)
461 ERR("Not PCM Wave\n");
464 if (aDesc.mFormatFlags != kLinearPCMFormatFlagIsSignedInteger +
465 kAudioFormatFlagIsPacked)
467 ERR("Unhandled Flags\n");
470 if (aDesc.mFramesPerPacket != 1)
472 ERR("Unhandled Frames per packet %li\n",aDesc.mFramesPerPacket);
475 if (aDesc.mChannelsPerFrame != pvi->nChannels)
477 ERR("Unhandled channel count %li\n",aDesc.mChannelsPerFrame);
480 if (aDesc.mBitsPerChannel != pvi->wBitsPerSample)
482 ERR("Unhandled bits per channel %li\n",aDesc.mBitsPerChannel);
485 if (aDesc.mSampleRate != pvi->nSamplesPerSec)
487 ERR("Unhandled sample rate %f\n",aDesc.mSampleRate);
493 ERR("Failed to create Extraction Session\n");
494 MovieAudioExtractionEnd(filter->aSession);
495 filter->aSession = NULL;
501 static DWORD WINAPI QTSplitter_loading_thread(LPVOID data)
503 QTSplitter *This = (QTSplitter *)data;
505 if (This->pAudio_Pin)
507 /* according to QA1469 a movie has to be fully loaded before we
508 can reliably start the Extraction session.
510 If loaded earlier, then we only get an extraction session for
511 the part of the movie that is loaded at that time.
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.
519 EnterCriticalSection(&This->csReceive);
520 while(This->pQTMovie && GetMovieLoadState(This->pQTMovie) < kMovieLoadStateComplete)
522 MoviesTask(This->pQTMovie, 100);
523 LeaveCriticalSection(&This->csReceive);
525 EnterCriticalSection(&This->csReceive);
527 LeaveCriticalSection(&This->csReceive);
532 static DWORD WINAPI QTSplitter_thread(LPVOID data)
534 QTSplitter *This = (QTSplitter *)data;
537 CVPixelBufferRef pixelBuffer = NULL;
541 WaitForSingleObject(This->runEvent, -1);
543 EnterCriticalSection(&This->csReceive);
546 LeaveCriticalSection(&This->csReceive);
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);
554 GetMovieTime(This->pQTMovie, &tr);
556 if (This->pAudio_Pin)
557 QT_Create_Extract_Session(This);
559 LeaveCriticalSection(&This->csReceive);
563 LONGLONG tStart=0, tStop=0;
564 LONGLONG mStart=0, mStop=0;
567 EnterCriticalSection(&This->csReceive);
570 LeaveCriticalSection(&This->csReceive);
574 GetMovieNextInterestingTime(This->pQTMovie, nextTimeStep, 0, NULL, This->movie_time, 1, &next_time, NULL);
578 TRACE("No next time\n");
579 LeaveCriticalSection(&This->csReceive);
583 tr.value = SInt64ToWide(next_time);
584 SetMovieTime(This->pQTMovie, &tr);
585 MoviesTask(This->pQTMovie,0);
586 QTVisualContextTask(This->vContext);
588 TRACE("In loop at time %ld\n",This->movie_time);
589 TRACE("In Next time %ld\n",next_time);
591 mStart = This->movie_time;
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;
600 if (This->pAudio_Pin && This->pAudio_Pin->pin.pin.pConnectedTo && This->aSession)
604 IMediaSample *sample = NULL;
605 AudioBufferList aData;
611 pvi = (WAVEFORMATEX*)This->pAudio_Pin->pmt->pbFormat;
613 hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->pAudio_Pin->pin, &sample, NULL, NULL, 0);
617 ERR("Audio: Unable to get delivery buffer (%x)\n", hr);
621 hr = IMediaSample_GetPointer(sample, &ptr);
624 ERR("Audio: Unable to get pointer to buffer (%x)\n", hr);
628 duration = (float)next_time / This->movie_scale;
629 time = (float)This->movie_time / This->movie_scale;
631 frames = pvi->nSamplesPerSec * duration;
632 TRACE("Need audio for %f seconds (%li frames)\n",duration,frames);
634 data_size = IMediaSample_GetSize(sample);
635 if (data_size < frames * pvi->nBlockAlign)
636 FIXME("Audio buffer is too small\n");
638 aData.mNumberBuffers = 1;
639 aData.mBuffers[0].mNumberChannels = pvi->nChannels;
640 aData.mBuffers[0].mDataByteSize = data_size;
641 aData.mBuffers[0].mData = ptr;
643 err = MovieAudioExtractionFillBuffer(This->aSession, &frames, &aData, &flags);
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);
655 etr.value = SInt64ToWide(This->movie_time);
656 MovieAudioExtractionSetProperty(This->aSession,
657 kQTPropertyClass_MovieAudioExtraction_Movie,
658 kQTMovieAudioExtractionMoviePropertyID_CurrentTime,
659 sizeof(TimeRecord), &etr );
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;
667 MovieAudioExtractionFillBuffer(This->aSession, &frames, &aData, &flags);
670 TRACE("Got %i frames\n",(int)frames);
672 IMediaSample_SetActualDataLength(sample, frames * pvi->nBlockAlign);
674 IMediaSample_SetMediaTime(sample, &mStart, &mStop);
675 IMediaSample_SetTime(sample, &tStart, &tStop);
677 hr = OutputQueue_Receive(This->pAudio_Pin->queue, sample);
678 TRACE("Audio Delivered (%x)\n",hr);
682 IMediaSample_Release(sample);
685 TRACE("Audio Pin not connected or no Audio\n");
688 if (This->pVideo_Pin && QTVisualContextIsNewImageAvailable(This->vContext,0))
690 err = QTVisualContextCopyImageForTime(This->vContext, NULL, NULL, &pixelBuffer);
695 IMediaSample *sample = NULL;
697 hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->pVideo_Pin->pin, &sample, NULL, NULL, 0);
700 ERR("Video: Unable to get delivery buffer (%x)\n", hr);
704 data_size = IMediaSample_GetSize(sample);
705 if (data_size < This->outputSize)
707 ERR("Sample size is too small %d < %d\n", data_size, This->outputSize)
713 hr = IMediaSample_GetPointer(sample, &ptr);
716 ERR("Video: Unable to get pointer to buffer (%x)\n", hr);
720 hr = AccessPixelBufferPixels( pixelBuffer, ptr);
723 ERR("Failed to access Pixels\n");
727 IMediaSample_SetActualDataLength(sample, This->outputSize);
729 IMediaSample_SetMediaTime(sample, &mStart, &mStop);
730 IMediaSample_SetTime(sample, &tStart, &tStop);
732 hr = OutputQueue_Receive(This->pVideo_Pin->queue, sample);
733 TRACE("Video Delivered (%x)\n",hr);
737 IMediaSample_Release(sample);
739 CVPixelBufferRelease(pixelBuffer);
743 TRACE("No video to deliver\n");
745 This->movie_time = next_time;
746 LeaveCriticalSection(&This->csReceive);
747 } while (hr == S_OK);
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);
758 static HRESULT WINAPI QT_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
761 QTSplitter *This = impl_from_IBaseFilter(iface);
762 HRESULT hr_any = VFW_E_NOT_CONNECTED;
764 TRACE("(%s)\n", wine_dbgstr_longlong(tStart));
766 EnterCriticalSection(&This->csReceive);
767 This->filter.rtStreamStart = tStart;
769 if (This->pVideo_Pin)
770 hr = BaseOutputPinImpl_Active(&This->pVideo_Pin->pin);
773 if (This->pAudio_Pin)
774 hr = BaseOutputPinImpl_Active(&This->pAudio_Pin->pin);
780 SetEvent(This->runEvent);
781 LeaveCriticalSection(&This->csReceive);
786 static HRESULT WINAPI QT_GetState(IBaseFilter *iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
788 QTSplitter *This = impl_from_IBaseFilter(iface);
789 TRACE("(%d, %p)\n", dwMilliSecsTimeout, pState);
791 *pState = This->state;
796 static HRESULT WINAPI QT_FindPin(IBaseFilter *iface, LPCWSTR Id, IPin **ppPin)
798 FIXME("(%p)->(%s,%p) stub\n", iface, debugstr_w(Id), ppPin);
802 static const IBaseFilterVtbl QT_Vtbl = {
804 BaseFilterImpl_AddRef,
806 BaseFilterImpl_GetClassID,
811 BaseFilterImpl_SetSyncSource,
812 BaseFilterImpl_GetSyncSource,
813 BaseFilterImpl_EnumPins,
815 BaseFilterImpl_QueryFilterInfo,
816 BaseFilterImpl_JoinFilterGraph,
817 BaseFilterImpl_QueryVendorInfo
823 static HRESULT QT_RemoveOutputPins(QTSplitter *This)
826 TRACE("(%p)\n", This);
828 if (This->pVideo_Pin)
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;
836 if (This->pAudio_Pin)
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;
845 BaseFilterImpl_IncrementPinVersion(&This->filter);
849 static inline QTInPin *impl_from_IPin( IPin *iface )
851 return CONTAINING_RECORD(iface, QTInPin, pin.IPin_iface);
854 static ULONG WINAPI QTInPin_Release(IPin *iface)
856 QTInPin *This = impl_from_IPin(iface);
857 ULONG refCount = InterlockedDecrement(&This->pin.refCount);
859 TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
862 FreeMediaType(&This->pin.mtCurrent);
864 IMemAllocator_Release(This->pAlloc);
866 This->pin.IPin_iface.lpVtbl = NULL;
873 static HRESULT QT_Process_Video_Track(QTSplitter* filter, Track trk)
876 VIDEOINFOHEADER * pvi;
880 static const WCHAR szwVideoOut[] = {'V','i','d','e','o',0};
881 CFMutableDictionaryRef pixelBufferOptions = NULL;
882 CFMutableDictionaryRef visualContextOptions = NULL;
883 CFNumberRef n = NULL;
885 DWORD outputWidth, outputHeight, outputDepth;
886 Fixed trackWidth, trackHeight;
888 ZeroMemory(&amt, sizeof(amt));
889 amt.formattype = FORMAT_VideoInfo;
890 amt.majortype = MEDIATYPE_Video;
891 amt.subtype = MEDIASUBTYPE_RGB24;
893 GetTrackDimensions(trk, &trackWidth, &trackHeight);
896 outputWidth = Fix2Long(trackWidth);
897 outputHeight = Fix2Long(trackHeight);
898 TRACE("Width %i Height %i\n",outputWidth, outputHeight);
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;
912 filter->outputSize = pvi->bmiHeader.biSizeImage;
915 pixelBufferOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
917 t = k32ARGBPixelFormat;
918 n = CFNumberCreate(NULL, kCFNumberIntType, &t);
919 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferPixelFormatTypeKey, n);
922 n = CFNumberCreate(NULL, kCFNumberIntType, &outputWidth);
923 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferWidthKey, n);
926 n = CFNumberCreate(NULL, kCFNumberIntType, &outputHeight);
927 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferHeightKey, n);
931 n = CFNumberCreate(NULL, kCFNumberIntType, &t);
932 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferBytesPerRowAlignmentKey, n);
935 visualContextOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
937 CFDictionarySetValue(visualContextOptions, kQTVisualContextPixelBufferAttributesKey, pixelBufferOptions);
939 err = QTPixelBufferContextCreate(NULL, visualContextOptions,&filter->vContext);
940 CFRelease(pixelBufferOptions);
941 CFRelease(visualContextOptions);
944 ERR("Failed to create Visual Context\n");
948 err = SetMovieVisualContext(filter->pQTMovie, filter->vContext);
951 ERR("Failed to set Visual Context\n");
955 piOutput.dir = PINDIR_OUTPUT;
956 piOutput.pFilter = &filter->filter.IBaseFilter_iface;
957 lstrcpyW(piOutput.achName,szwVideoOut);
959 hr = QT_AddPin(filter, &piOutput, &amt, TRUE);
961 ERR("Failed to add Video Track\n");
963 TRACE("Video Pin %p\n",filter->pVideo_Pin);
968 static HRESULT QT_Process_Audio_Track(QTSplitter* filter, Track trk)
974 static const WCHAR szwAudioOut[] = {'A','u','d','i','o',0};
977 SoundDescriptionHandle aDesc = (SoundDescriptionHandle) NewHandle(sizeof(SoundDescription));
979 audioMedia = GetTrackMedia(trk);
980 GetMediaSampleDescription(audioMedia, 1, (SampleDescriptionHandle)aDesc);
982 ZeroMemory(&amt, sizeof(amt));
983 amt.formattype = FORMAT_WaveFormatEx;
984 amt.majortype = MEDIATYPE_Audio;
985 amt.subtype = MEDIASUBTYPE_PCM;
986 amt.bTemporalCompression = 0;
988 amt.cbFormat = sizeof(WAVEFORMATEX);
989 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
990 ZeroMemory(amt.pbFormat, amt.cbFormat);
991 pvi = (WAVEFORMATEX*)amt.pbFormat;
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)
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;
1007 DisposeHandle((Handle)aDesc);
1009 piOutput.dir = PINDIR_OUTPUT;
1010 piOutput.pFilter = &filter->filter.IBaseFilter_iface;
1011 lstrcpyW(piOutput.achName,szwAudioOut);
1013 hr = QT_AddPin(filter, &piOutput, &amt, FALSE);
1015 ERR("Failed to add Audio Track\n");
1017 TRACE("Audio Pin %p\n",filter->pAudio_Pin);
1021 static HRESULT QT_Process_Movie(QTSplitter* filter)
1025 WineDataRefRecord ptrDataRefRec;
1026 Handle dataRef = NULL;
1032 TRACE("Trying movie connect\n");
1034 ptrDataRefRec.pReader = filter->pInputPin.pReader;
1035 ptrDataRefRec.streamSubtype = filter->pInputPin.subType;
1036 PtrToHand( &ptrDataRefRec, &dataRef, sizeof(WineDataRefRecord));
1038 err = NewMovieFromDataRef(&filter->pQTMovie, newMovieActive|newMovieDontInteractWithUser|newMovieDontAutoUpdateClock|newMovieDontAskUnresolvedDataRefs|newMovieAsyncOK, &id, dataRef, 'WINE');
1040 DisposeHandle(dataRef);
1044 FIXME("QuickTime cannot handle media type(%i)\n",err);
1045 return VFW_E_TYPE_NOT_ACCEPTED;
1048 PrePrerollMovie(filter->pQTMovie, 0, fixed1, NULL, NULL);
1049 PrerollMovie(filter->pQTMovie, 0, fixed1);
1050 GoToBeginningOfMovie(filter->pQTMovie);
1051 SetMovieActive(filter->pQTMovie,TRUE);
1053 if (GetMovieLoadState(filter->pQTMovie) < kMovieLoadStateLoaded)
1054 MoviesTask(filter->pQTMovie,100);
1056 trk = GetMovieIndTrackType(filter->pQTMovie, 1, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
1057 TRACE("%p is a video track\n",trk);
1059 hr = QT_Process_Video_Track(filter, trk);
1064 trk = GetMovieIndTrackType(filter->pQTMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
1065 TRACE("%p is a audio track\n",trk);
1067 hr = QT_Process_Audio_Track(filter, trk);
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;
1074 TRACE("Movie duration is %s\n",wine_dbgstr_longlong(filter->sourceSeeking.llDuration));
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);
1083 hr = HRESULT_FROM_WIN32(GetLastError());
1088 static HRESULT WINAPI QTInPin_ReceiveConnection(IPin *iface, IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
1091 ALLOCATOR_PROPERTIES props;
1092 QTInPin *This = impl_from_IPin(iface);
1094 TRACE("(%p/%p)->(%p, %p)\n", This, iface, pReceivePin, pmt);
1096 EnterCriticalSection(This->pin.pCritSec);
1097 This->pReader = NULL;
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;
1105 PIN_DIRECTION pindirReceive;
1106 IPin_QueryDirection(pReceivePin, &pindirReceive);
1107 if (pindirReceive != PINDIR_OUTPUT)
1108 hr = VFW_E_INVALID_DIRECTION;
1113 LeaveCriticalSection(This->pin.pCritSec);
1117 hr = IPin_QueryInterface(pReceivePin, &IID_IAsyncReader, (LPVOID *)&This->pReader);
1120 LeaveCriticalSection(This->pin.pCritSec);
1121 TRACE("Input source is not an AsyncReader\n");
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));
1130 LeaveCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1131 TRACE("Unable to process movie\n");
1135 This->pAlloc = NULL;
1138 props.cbBuffer = impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->outputSize + props.cbAlign;
1141 hr = IAsyncReader_RequestAllocator(This->pReader, NULL, &props, &This->pAlloc);
1144 CopyMediaType(&This->pin.mtCurrent, pmt);
1145 This->pin.pConnectedTo = pReceivePin;
1146 IPin_AddRef(pReceivePin);
1147 hr = IMemAllocator_Commit(This->pAlloc);
1151 QT_RemoveOutputPins(impl_from_IBaseFilter(This->pin.pinInfo.pFilter));
1153 IAsyncReader_Release(This->pReader);
1154 This->pReader = NULL;
1156 IMemAllocator_Release(This->pAlloc);
1157 This->pAlloc = NULL;
1159 TRACE("Size: %i\n", props.cbBuffer);
1160 LeaveCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1165 static HRESULT WINAPI QTInPin_Disconnect(IPin *iface)
1168 QTInPin *This = impl_from_IPin(iface);
1172 hr = IBaseFilter_GetState(This->pin.pinInfo.pFilter, INFINITE, &state);
1173 EnterCriticalSection(This->pin.pCritSec);
1174 if (This->pin.pConnectedTo)
1176 QTSplitter *Parser = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
1178 if (SUCCEEDED(hr) && state == State_Stopped)
1180 IMemAllocator_Decommit(This->pAlloc);
1181 IPin_Disconnect(This->pin.pConnectedTo);
1182 This->pin.pConnectedTo = NULL;
1183 hr = QT_RemoveOutputPins(Parser);
1186 hr = VFW_E_NOT_STOPPED;
1190 LeaveCriticalSection(This->pin.pCritSec);
1194 static HRESULT WINAPI QTInPin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *pmt)
1196 QTInPin *This = impl_from_IPin(iface);
1198 TRACE("(%p)->(%p)\n", This, pmt);
1200 if (IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream))
1202 This->subType = pmt->subtype;
1208 static HRESULT WINAPI QTInPin_EndOfStream(IPin *iface)
1210 QTInPin *pin = impl_from_IPin(iface);
1211 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1213 FIXME("Propagate message on %p\n", This);
1217 static HRESULT WINAPI QTInPin_BeginFlush(IPin *iface)
1219 QTInPin *pin = impl_from_IPin(iface);
1220 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1222 FIXME("Propagate message on %p\n", This);
1226 static HRESULT WINAPI QTInPin_EndFlush(IPin *iface)
1228 QTInPin *pin = impl_from_IPin(iface);
1229 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1231 FIXME("Propagate message on %p\n", This);
1235 static HRESULT WINAPI QTInPin_NewSegment(IPin *iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
1237 QTInPin *pin = impl_from_IPin(iface);
1238 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1240 BasePinImpl_NewSegment(iface, tStart, tStop, dRate);
1241 FIXME("Propagate message on %p\n", This);
1245 static HRESULT WINAPI QTInPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
1247 QTInPin *This = impl_from_IPin(iface);
1249 TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
1253 if (IsEqualIID(riid, &IID_IUnknown))
1255 else if (IsEqualIID(riid, &IID_IPin))
1257 else if (IsEqualIID(riid, &IID_IMediaSeeking))
1258 return IBaseFilter_QueryInterface(This->pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
1262 IUnknown_AddRef((IUnknown *)(*ppv));
1266 FIXME("No interface for %s!\n", debugstr_guid(riid));
1268 return E_NOINTERFACE;
1271 static HRESULT WINAPI QTInPin_EnumMediaTypes(IPin *iface, IEnumMediaTypes **ppEnum)
1273 QTInPin *This = impl_from_IPin(iface);
1275 TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
1277 return EnumMediaTypes_Construct(&This->pin, BasePinImpl_GetMediaType, BasePinImpl_GetMediaTypeVersion, ppEnum);
1280 static const IPinVtbl QT_InputPin_Vtbl = {
1281 QTInPin_QueryInterface,
1284 BaseInputPinImpl_Connect,
1285 QTInPin_ReceiveConnection,
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,
1304 static inline QTOutPin *impl_QTOutPin_from_IPin( IPin *iface )
1306 return CONTAINING_RECORD(iface, QTOutPin, pin.pin.IPin_iface);
1309 static inline QTOutPin *impl_QTOutPin_from_BasePin( BasePin *iface )
1311 return CONTAINING_RECORD(iface, QTOutPin, pin.pin);
1314 static inline QTOutPin *impl_QTOutPin_from_BaseOutputPin( BaseOutputPin *iface )
1316 return CONTAINING_RECORD(iface, QTOutPin, pin);
1319 static HRESULT WINAPI QTOutPin_QueryInterface(IPin *iface, REFIID riid, void **ppv)
1321 QTOutPin *This = impl_QTOutPin_from_IPin(iface);
1323 TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
1327 if (IsEqualIID(riid, &IID_IUnknown))
1329 else if (IsEqualIID(riid, &IID_IPin))
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;
1338 IUnknown_AddRef((IUnknown *)(*ppv));
1341 FIXME("No interface for %s!\n", debugstr_guid(riid));
1342 return E_NOINTERFACE;
1345 static ULONG WINAPI QTOutPin_Release(IPin *iface)
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);
1353 DeleteMediaType(This->pmt);
1354 FreeMediaType(&This->pin.pin.mtCurrent);
1355 CoTaskMemFree(This);
1361 static HRESULT WINAPI QTOutPin_GetMediaType(BasePin *iface, int iPosition, AM_MEDIA_TYPE *pmt)
1363 QTOutPin *This = impl_QTOutPin_from_BasePin(iface);
1366 return E_INVALIDARG;
1368 return VFW_S_NO_MORE_ITEMS;
1369 CopyMediaType(pmt, This->pmt);
1373 static HRESULT WINAPI QTOutPin_DecideBufferSize(BaseOutputPin *iface, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
1379 static HRESULT WINAPI QTOutPin_DecideAllocator(BaseOutputPin *iface, IMemInputPin *pPin, IMemAllocator **pAlloc)
1382 QTOutPin *This = impl_QTOutPin_from_BaseOutputPin(iface);
1383 QTSplitter *QTfilter = impl_from_IBaseFilter(This->pin.pin.pinInfo.pFilter);
1386 if (QTfilter->pInputPin.pAlloc)
1387 hr = IMemInputPin_NotifyAllocator(pPin, QTfilter->pInputPin.pAlloc, FALSE);
1389 hr = VFW_E_NO_ALLOCATOR;
1394 static HRESULT WINAPI QTOutPin_BreakConnect(BaseOutputPin *This)
1398 TRACE("(%p)->()\n", This);
1400 EnterCriticalSection(This->pin.pCritSec);
1401 if (!This->pin.pConnectedTo || !This->pMemInputPin)
1402 hr = VFW_E_NOT_CONNECTED;
1405 hr = IPin_Disconnect(This->pin.pConnectedTo);
1406 IPin_Disconnect(&This->pin.IPin_iface);
1408 LeaveCriticalSection(This->pin.pCritSec);
1413 static const IPinVtbl QT_OutputPin_Vtbl = {
1414 QTOutPin_QueryInterface,
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
1434 static inline QTOutPin *impl_from_IQualityControl( IQualityControl *iface )
1436 return CONTAINING_RECORD(iface, QTOutPin, IQualityControl_iface);
1439 HRESULT WINAPI QT_QualityControl_QueryInterface(IQualityControl *iface, REFIID riid, void **ppv)
1441 QTOutPin *This = impl_from_IQualityControl(iface);
1442 return IPin_QueryInterface(&This->pin.pin.IPin_iface, riid, ppv);
1445 ULONG WINAPI QT_QualityControl_AddRef(IQualityControl *iface)
1447 QTOutPin *This = impl_from_IQualityControl(iface);
1448 return IPin_AddRef(&This->pin.pin.IPin_iface);
1451 ULONG WINAPI QT_QualityControl_Release(IQualityControl *iface)
1453 QTOutPin *This = impl_from_IQualityControl(iface);
1454 return IPin_Release(&This->pin.pin.IPin_iface);
1457 static HRESULT WINAPI QT_QualityControl_Notify(IQualityControl *iface, IBaseFilter *sender, Quality qm)
1459 REFERENCE_TIME late = qm.Late;
1460 if (qm.Late < 0 && -qm.Late > qm.TimeStamp)
1461 late = -qm.TimeStamp;
1462 /* TODO: Do Something */
1466 HRESULT WINAPI QT_QualityControl_SetSink(IQualityControl *iface, IQualityControl *tonotify)
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
1480 static const BasePinFuncTable output_BaseFuncTable = {
1482 BaseOutputPinImpl_AttemptConnection,
1483 BasePinImpl_GetMediaTypeVersion,
1484 QTOutPin_GetMediaType
1487 static const BaseOutputPinFuncTable output_BaseOutputFuncTable = {
1488 QTOutPin_DecideBufferSize,
1489 QTOutPin_DecideAllocator,
1490 QTOutPin_BreakConnect
1493 static const OutputQueueFuncTable output_OutputQueueFuncTable = {
1494 OutputQueueImpl_ThreadProc
1497 static HRESULT QT_AddPin(QTSplitter *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt, BOOL video)
1503 target = (IPin**)&This->pVideo_Pin;
1505 target = (IPin**)&This->pAudio_Pin;
1507 if (*target != NULL)
1509 FIXME("We already have a %s pin\n",(video)?"video":"audio");
1513 hr = BaseOutputPin_Construct(&QT_OutputPin_Vtbl, sizeof(QTOutPin), piOutput, &output_BaseFuncTable, &output_BaseOutputFuncTable, &This->filter.csFilter, (IPin**)target);
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;
1522 BaseFilterImpl_IncrementPinVersion(&This->filter);
1524 hr = OutputQueue_Construct(&pin->pin, TRUE, TRUE, 5, FALSE, THREAD_PRIORITY_NORMAL, &output_OutputQueueFuncTable, &pin->queue);
1527 ERR("Failed with error %x\n", hr);
1531 static HRESULT WINAPI QTSplitter_ChangeStart(IMediaSeeking *iface)
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);
1542 static HRESULT WINAPI QTSplitter_ChangeStop(IMediaSeeking *iface)
1544 FIXME("(%p) filter hasn't implemented stop position change!\n", iface);
1548 static HRESULT WINAPI QTSplitter_ChangeRate(IMediaSeeking *iface)
1550 FIXME("(%p) filter hasn't implemented rate change!\n", iface);
1554 static HRESULT WINAPI QT_Seeking_QueryInterface(IMediaSeeking * iface, REFIID riid, LPVOID * ppv)
1556 QTSplitter *This = impl_from_IMediaSeeking(iface);
1558 return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv);
1561 static ULONG WINAPI QT_Seeking_AddRef(IMediaSeeking * iface)
1563 QTSplitter *This = impl_from_IMediaSeeking(iface);
1565 return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
1568 static ULONG WINAPI QT_Seeking_Release(IMediaSeeking * iface)
1570 QTSplitter *This = impl_from_IMediaSeeking(iface);
1572 return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
1575 static const IMediaSeekingVtbl QT_Seeking_Vtbl =
1577 QT_Seeking_QueryInterface,
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