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
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 {
136 IQualityControl IQualityControl_iface;
142 typedef struct QTInPin {
146 IAsyncReader *pReader;
147 IMemAllocator *pAlloc;
150 typedef struct QTSplitter {
154 QTOutPin *pVideo_Pin;
155 QTOutPin *pAudio_Pin;
157 ALLOCATOR_PROPERTIES props;
160 QTVisualContextRef vContext;
162 MovieAudioExtractionRef aSession;
167 CRITICAL_SECTION csReceive;
169 SourceSeeking sourceSeeking;
170 TimeValue movie_time;
171 TimeValue movie_start;
172 TimeScale movie_scale;
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);
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)
216 IPin_AddRef(&This->pInputPin.pin.IPin_iface);
217 return &This->pInputPin.pin.IPin_iface;
219 if (This->pVideo_Pin)
220 IPin_AddRef(&This->pVideo_Pin->pin.pin.IPin_iface);
221 return &This->pVideo_Pin->pin.pin.IPin_iface;
223 if (This->pAudio_Pin)
224 IPin_AddRef(&This->pAudio_Pin->pin.pin.IPin_iface);
225 return &This->pAudio_Pin->pin.pin.IPin_iface;
231 static LONG WINAPI QT_GetPinCount(BaseFilter *iface)
233 QTSplitter *This = impl_from_BaseFilter(iface);
235 if (This->pAudio_Pin) c++;
236 if (This->pVideo_Pin) c++;
240 static const BaseFilterFuncTable BaseFuncTable = {
245 IUnknown * CALLBACK QTSplitter_create(IUnknown *punkout, HRESULT *phr)
247 IUnknown *obj = NULL;
250 static const WCHAR wcsInputPinName[] = {'I','n','p','u','t',' ','P','i','n',0};
254 RegisterWineDataHandler();
256 This = CoTaskMemAlloc(sizeof(*This));
257 obj = (IUnknown*)This;
260 *phr = E_OUTOFMEMORY;
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);
291 static void QT_Destroy(QTSplitter *This)
293 IPin *connected = NULL;
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);
302 IPin_Disconnect(connected);
303 IPin_Release(connected);
305 pinref = IPin_Release(&This->pInputPin.pin.IPin_iface);
308 ERR("pinref should be null, is %u, destroying anyway\n", pinref);
309 assert((LONG)pinref > 0);
312 pinref = IPin_Release(&This->pInputPin.pin.IPin_iface);
316 DisposeMovie(This->pQTMovie);
318 QTVisualContextRelease(This->vContext);
320 MovieAudioExtractionEnd(This->aSession);
321 CloseHandle(This->runEvent);
325 This->csReceive.DebugInfo->Spare[0] = 0;
326 DeleteCriticalSection(&This->csReceive);
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);
338 if (IsEqualIID(riid, &IID_IUnknown))
340 else if (IsEqualIID(riid, &IID_IPersist))
342 else if (IsEqualIID(riid, &IID_IMediaFilter))
344 else if (IsEqualIID(riid, &IID_IBaseFilter))
346 else if (IsEqualIID(riid, &IID_IMediaSeeking))
347 *ppv = &This->sourceSeeking;
351 IUnknown_AddRef((IUnknown *)(*ppv));
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);
375 static HRESULT WINAPI QT_Stop(IBaseFilter *iface)
377 QTSplitter *This = impl_from_IBaseFilter(iface);
381 EnterCriticalSection(&This->csReceive);
382 IAsyncReader_BeginFlush(This->pInputPin.pReader);
383 IAsyncReader_EndFlush(This->pInputPin.pReader);
384 LeaveCriticalSection(&This->csReceive);
389 static HRESULT WINAPI QT_Pause(IBaseFilter *iface)
397 static OSErr QT_Create_Extract_Session(QTSplitter *filter)
399 AudioStreamBasicDescription aDesc;
403 pvi = (WAVEFORMATEX*)filter->pAudio_Pin->pmt->pbFormat;
405 err = MovieAudioExtractionBegin(filter->pQTMovie, 0, &filter->aSession);
408 ERR("Failed to begin Extraction session %i\n",err);
412 err = MovieAudioExtractionGetProperty(filter->aSession,
413 kQTPropertyClass_MovieAudioExtraction_Audio,
414 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
415 sizeof(AudioStreamBasicDescription), &aDesc, NULL);
419 MovieAudioExtractionEnd(filter->aSession);
420 filter->aSession = NULL;
421 ERR("Failed to get session description %i\n",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");
445 if (aDesc.mFormatFlags != kLinearPCMFormatFlagIsSignedInteger +
446 kAudioFormatFlagIsPacked)
448 ERR("Unhandled Flags\n");
451 if (aDesc.mFramesPerPacket != 1)
453 ERR("Unhandled Frames per packet %li\n",aDesc.mFramesPerPacket);
456 if (aDesc.mChannelsPerFrame != pvi->nChannels)
458 ERR("Unhandled channel count %li\n",aDesc.mChannelsPerFrame);
461 if (aDesc.mBitsPerChannel != pvi->wBitsPerSample)
463 ERR("Unhandled bits per channel %li\n",aDesc.mBitsPerChannel);
466 if (aDesc.mSampleRate != pvi->nSamplesPerSec)
468 ERR("Unhandled sample rate %f\n",aDesc.mSampleRate);
474 ERR("Failed to create Extraction Session\n");
475 MovieAudioExtractionEnd(filter->aSession);
476 filter->aSession = NULL;
482 static DWORD WINAPI QTSplitter_loading_thread(LPVOID data)
484 QTSplitter *This = (QTSplitter *)data;
486 if (This->pAudio_Pin)
488 /* according to QA1469 a movie has to be fully loaded before we
489 can reliably start the Extraction session.
491 If loaded earlier then we only get an extraction session for
492 the part of the movie that is loaded at that time.
494 We are trying to load as much of the movie as we can before we
495 start extracting. However we can recreate the extraction session
496 again when we run out of loaded extraction frames. But we want
497 to try to minimize that.
500 while(GetMovieLoadState(This->pQTMovie) < kMovieLoadStateComplete)
502 EnterCriticalSection(&This->csReceive);
503 MoviesTask(This->pQTMovie, 100);
504 LeaveCriticalSection(&This->csReceive);
511 static DWORD WINAPI QTSplitter_thread(LPVOID data)
513 QTSplitter *This = (QTSplitter *)data;
516 CVPixelBufferRef pixelBuffer = NULL;
520 WaitForSingleObject(This->runEvent, -1);
522 EnterCriticalSection(&This->csReceive);
523 This->state = State_Running;
524 /* Prime the pump: Needed for MPEG streams */
525 GetMovieNextInterestingTime(This->pQTMovie, nextTimeEdgeOK | nextTimeStep, 0, NULL, This->movie_time, 1, &next_time, NULL);
527 GetMovieTime(This->pQTMovie, &tr);
529 if (This->pAudio_Pin)
530 QT_Create_Extract_Session(This);
532 LeaveCriticalSection(&This->csReceive);
536 LONGLONG tStart=0, tStop=0;
537 LONGLONG mStart=0, mStop=0;
540 EnterCriticalSection(&This->csReceive);
541 GetMovieNextInterestingTime(This->pQTMovie, nextTimeStep, 0, NULL, This->movie_time, 1, &next_time, NULL);
545 TRACE("No next time\n");
546 LeaveCriticalSection(&This->csReceive);
550 tr.value = SInt64ToWide(next_time);
551 SetMovieTime(This->pQTMovie, &tr);
552 MoviesTask(This->pQTMovie,0);
553 QTVisualContextTask(This->vContext);
555 TRACE("In loop at time %ld\n",This->movie_time);
556 TRACE("In Next time %ld\n",next_time);
558 mStart = This->movie_time;
561 time = (float)(This->movie_time - This->movie_start) / This->movie_scale;
562 tStart = time * 10000000;
563 time = (float)(next_time - This->movie_start) / This->movie_scale;
564 tStop = time * 10000000;
567 if (This->pAudio_Pin && This->pAudio_Pin->pin.pin.pConnectedTo && This->aSession)
571 IMediaSample *sample = NULL;
572 AudioBufferList aData;
578 pvi = (WAVEFORMATEX*)This->pAudio_Pin->pmt->pbFormat;
580 hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->pAudio_Pin->pin, &sample, NULL, NULL, 0);
584 ERR("Audio: Unable to get delivery buffer (%x)\n", hr);
588 hr = IMediaSample_GetPointer(sample, &ptr);
591 ERR("Audio: Unable to get pointer to buffer (%x)\n", hr);
595 duration = (float)next_time / This->movie_scale;
596 time = (float)This->movie_time / This->movie_scale;
598 frames = pvi->nSamplesPerSec * duration;
599 TRACE("Need audio for %f seconds (%li frames)\n",duration,frames);
601 data_size = IMediaSample_GetSize(sample);
602 if (data_size < frames * pvi->nBlockAlign)
603 FIXME("Audio buffer is too small\n");
605 aData.mNumberBuffers = 1;
606 aData.mBuffers[0].mNumberChannels = pvi->nChannels;
607 aData.mBuffers[0].mDataByteSize = data_size;
608 aData.mBuffers[0].mData = ptr;
610 err = MovieAudioExtractionFillBuffer(This->aSession, &frames, &aData, &flags);
615 /* Ran out of frames, Restart the extraction session */
616 TRACE("Restarting extraction session\n");
617 MovieAudioExtractionEnd(This->aSession);
618 This->aSession = NULL;
619 QT_Create_Extract_Session(This);
622 etr.value = SInt64ToWide(This->movie_time);
623 MovieAudioExtractionSetProperty(This->aSession,
624 kQTPropertyClass_MovieAudioExtraction_Movie,
625 kQTMovieAudioExtractionMoviePropertyID_CurrentTime,
626 sizeof(TimeRecord), &etr );
628 frames = pvi->nSamplesPerSec * duration;
629 aData.mNumberBuffers = 1;
630 aData.mBuffers[0].mNumberChannels = pvi->nChannels;
631 aData.mBuffers[0].mDataByteSize = data_size;
632 aData.mBuffers[0].mData = ptr;
634 MovieAudioExtractionFillBuffer(This->aSession, &frames, &aData, &flags);
637 TRACE("Got %i frames\n",(int)frames);
639 IMediaSample_SetActualDataLength(sample, frames * pvi->nBlockAlign);
641 IMediaSample_SetMediaTime(sample, &mStart, &mStop);
642 IMediaSample_SetTime(sample, &tStart, &tStop);
644 hr = OutputQueue_Receive(This->pAudio_Pin->queue, sample);
645 TRACE("Audio Delivered (%x)\n",hr);
649 IMediaSample_Release(sample);
652 TRACE("Audio Pin not connected or no Audio\n");
655 if (This->pVideo_Pin && QTVisualContextIsNewImageAvailable(This->vContext,0))
657 err = QTVisualContextCopyImageForTime(This->vContext, NULL, NULL, &pixelBuffer);
662 IMediaSample *sample = NULL;
664 hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->pVideo_Pin->pin, &sample, NULL, NULL, 0);
667 ERR("Video: Unable to get delivery buffer (%x)\n", hr);
671 data_size = IMediaSample_GetSize(sample);
672 if (data_size < This->outputSize)
674 ERR("Sample size is too small %d < %d\n", data_size, This->outputSize)
680 hr = IMediaSample_GetPointer(sample, &ptr);
683 ERR("Video: Unable to get pointer to buffer (%x)\n", hr);
687 hr = AccessPixelBufferPixels( pixelBuffer, ptr);
690 ERR("Failed to access Pixels\n");
694 IMediaSample_SetActualDataLength(sample, This->outputSize);
696 IMediaSample_SetMediaTime(sample, &mStart, &mStop);
697 IMediaSample_SetTime(sample, &tStart, &tStop);
699 hr = OutputQueue_Receive(This->pVideo_Pin->queue, sample);
700 TRACE("Video Delivered (%x)\n",hr);
704 IMediaSample_Release(sample);
706 CVPixelBufferRelease(pixelBuffer);
710 TRACE("No video to deliver\n");
712 This->movie_time = next_time;
713 LeaveCriticalSection(&This->csReceive);
714 } while (hr == S_OK);
716 This->state = State_Stopped;
717 if (This->pAudio_Pin)
718 OutputQueue_EOS(This->pAudio_Pin->queue);
719 if (This->pVideo_Pin)
720 OutputQueue_EOS(This->pVideo_Pin->queue);
725 static HRESULT WINAPI QT_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
728 QTSplitter *This = impl_from_IBaseFilter(iface);
729 HRESULT hr_any = VFW_E_NOT_CONNECTED;
731 TRACE("(%s)\n", wine_dbgstr_longlong(tStart));
733 EnterCriticalSection(&This->csReceive);
734 This->filter.rtStreamStart = tStart;
736 if (This->pVideo_Pin)
737 hr = BaseOutputPinImpl_Active(&This->pVideo_Pin->pin);
740 if (This->pAudio_Pin)
741 hr = BaseOutputPinImpl_Active(&This->pAudio_Pin->pin);
747 SetEvent(This->runEvent);
748 LeaveCriticalSection(&This->csReceive);
753 static HRESULT WINAPI QT_GetState(IBaseFilter *iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
755 QTSplitter *This = impl_from_IBaseFilter(iface);
756 TRACE("(%d, %p)\n", dwMilliSecsTimeout, pState);
758 *pState = This->state;
763 static HRESULT WINAPI QT_FindPin(IBaseFilter *iface, LPCWSTR Id, IPin **ppPin)
765 FIXME("(%p)->(%s,%p) stub\n", iface, debugstr_w(Id), ppPin);
769 static const IBaseFilterVtbl QT_Vtbl = {
771 BaseFilterImpl_AddRef,
773 BaseFilterImpl_GetClassID,
778 BaseFilterImpl_SetSyncSource,
779 BaseFilterImpl_GetSyncSource,
780 BaseFilterImpl_EnumPins,
782 BaseFilterImpl_QueryFilterInfo,
783 BaseFilterImpl_JoinFilterGraph,
784 BaseFilterImpl_QueryVendorInfo
790 static HRESULT QT_RemoveOutputPins(QTSplitter *This)
793 TRACE("(%p)\n", This);
795 if (This->pVideo_Pin)
797 OutputQueue_Destroy(This->pVideo_Pin->queue);
798 hr = BaseOutputPinImpl_BreakConnect(&This->pVideo_Pin->pin);
799 TRACE("Disconnect: %08x\n", hr);
800 IPin_Release(&This->pVideo_Pin->pin.pin.IPin_iface);
801 This->pVideo_Pin = NULL;
803 if (This->pAudio_Pin)
805 OutputQueue_Destroy(This->pAudio_Pin->queue);
806 hr = BaseOutputPinImpl_BreakConnect(&This->pAudio_Pin->pin);
807 TRACE("Disconnect: %08x\n", hr);
808 IPin_Release(&This->pAudio_Pin->pin.pin.IPin_iface);
809 This->pAudio_Pin = NULL;
812 BaseFilterImpl_IncrementPinVersion(&This->filter);
816 static inline QTInPin *impl_from_IPin( IPin *iface )
818 return CONTAINING_RECORD(iface, QTInPin, pin.IPin_iface);
821 static ULONG WINAPI QTInPin_Release(IPin *iface)
823 QTInPin *This = impl_from_IPin(iface);
824 ULONG refCount = InterlockedDecrement(&This->pin.refCount);
826 TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
829 FreeMediaType(&This->pin.mtCurrent);
831 IMemAllocator_Release(This->pAlloc);
833 This->pin.IPin_iface.lpVtbl = NULL;
840 static HRESULT QT_Process_Video_Track(QTSplitter* filter, Track trk)
843 VIDEOINFOHEADER * pvi;
847 static const WCHAR szwVideoOut[] = {'V','i','d','e','o',0};
848 CFMutableDictionaryRef pixelBufferOptions = NULL;
849 CFMutableDictionaryRef visualContextOptions = NULL;
850 CFNumberRef n = NULL;
852 DWORD outputWidth, outputHeight, outputDepth;
853 Fixed trackWidth, trackHeight;
855 ZeroMemory(&amt, sizeof(amt));
856 amt.formattype = FORMAT_VideoInfo;
857 amt.majortype = MEDIATYPE_Video;
858 amt.subtype = MEDIASUBTYPE_RGB24;
860 GetTrackDimensions(trk, &trackWidth, &trackHeight);
863 outputWidth = Fix2Long(trackWidth);
864 outputHeight = Fix2Long(trackHeight);
865 TRACE("Width %i Height %i\n",outputWidth, outputHeight);
867 amt.cbFormat = sizeof(VIDEOINFOHEADER);
868 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
869 ZeroMemory(amt.pbFormat, amt.cbFormat);
870 pvi = (VIDEOINFOHEADER *)amt.pbFormat;
871 pvi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
872 pvi->bmiHeader.biWidth = outputWidth;
873 pvi->bmiHeader.biHeight = outputHeight;
874 pvi->bmiHeader.biPlanes = 1;
875 pvi->bmiHeader.biBitCount = 24;
876 pvi->bmiHeader.biCompression = BI_RGB;
877 pvi->bmiHeader.biSizeImage = outputWidth * outputHeight * outputDepth;
879 filter->outputSize = pvi->bmiHeader.biSizeImage;
882 pixelBufferOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
884 t = k32ARGBPixelFormat;
885 n = CFNumberCreate(NULL, kCFNumberIntType, &t);
886 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferPixelFormatTypeKey, n);
889 n = CFNumberCreate(NULL, kCFNumberIntType, &outputWidth);
890 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferWidthKey, n);
893 n = CFNumberCreate(NULL, kCFNumberIntType, &outputHeight);
894 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferHeightKey, n);
898 n = CFNumberCreate(NULL, kCFNumberIntType, &t);
899 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferBytesPerRowAlignmentKey, n);
902 visualContextOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
904 CFDictionarySetValue(visualContextOptions, kQTVisualContextPixelBufferAttributesKey, pixelBufferOptions);
906 err = QTPixelBufferContextCreate(NULL, visualContextOptions,&filter->vContext);
907 CFRelease(pixelBufferOptions);
908 CFRelease(visualContextOptions);
911 ERR("Failed to create Visual Context\n");
915 err = SetMovieVisualContext(filter->pQTMovie, filter->vContext);
918 ERR("Failed to set Visual Context\n");
922 piOutput.dir = PINDIR_OUTPUT;
923 piOutput.pFilter = &filter->filter.IBaseFilter_iface;
924 lstrcpyW(piOutput.achName,szwVideoOut);
926 hr = QT_AddPin(filter, &piOutput, &amt, TRUE);
928 ERR("Failed to add Video Track\n");
930 TRACE("Video Pin %p\n",filter->pVideo_Pin);
935 static HRESULT QT_Process_Audio_Track(QTSplitter* filter, Track trk)
941 static const WCHAR szwAudioOut[] = {'A','u','d','i','o',0};
944 SoundDescriptionHandle aDesc = (SoundDescriptionHandle) NewHandle(sizeof(SoundDescription));
946 audioMedia = GetTrackMedia(trk);
947 GetMediaSampleDescription(audioMedia, 1, (SampleDescriptionHandle)aDesc);
949 ZeroMemory(&amt, sizeof(amt));
950 amt.formattype = FORMAT_WaveFormatEx;
951 amt.majortype = MEDIATYPE_Audio;
952 amt.subtype = MEDIASUBTYPE_PCM;
953 amt.bTemporalCompression = 0;
955 amt.cbFormat = sizeof(WAVEFORMATEX);
956 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
957 ZeroMemory(amt.pbFormat, amt.cbFormat);
958 pvi = (WAVEFORMATEX*)amt.pbFormat;
960 pvi->cbSize = sizeof(WAVEFORMATEX);
961 pvi->wFormatTag = WAVE_FORMAT_PCM;
962 pvi->nChannels = ((SoundDescription)**aDesc).numChannels;
963 if (pvi->nChannels < 1 || pvi->nChannels > 2)
965 pvi->nSamplesPerSec = (((SoundDescription)**aDesc).sampleRate/65536);
966 if (pvi->nSamplesPerSec < 8000 || pvi->nChannels > 48000)
967 pvi->nSamplesPerSec = 44100;
968 pvi->wBitsPerSample = ((SoundDescription)**aDesc).sampleSize;
969 if (pvi->wBitsPerSample < 8 || pvi->wBitsPerSample > 32)
970 pvi->wBitsPerSample = 16;
971 pvi->nBlockAlign = (pvi->nChannels * pvi->wBitsPerSample) / 8;
972 pvi->nAvgBytesPerSec = pvi->nSamplesPerSec * pvi->nBlockAlign;
974 DisposeHandle((Handle)aDesc);
976 piOutput.dir = PINDIR_OUTPUT;
977 piOutput.pFilter = &filter->filter.IBaseFilter_iface;
978 lstrcpyW(piOutput.achName,szwAudioOut);
980 hr = QT_AddPin(filter, &piOutput, &amt, FALSE);
982 ERR("Failed to add Audio Track\n");
984 TRACE("Audio Pin %p\n",filter->pAudio_Pin);
988 static HRESULT QT_Process_Movie(QTSplitter* filter)
992 WineDataRefRecord ptrDataRefRec;
993 Handle dataRef = NULL;
1000 TRACE("Trying movie connect\n");
1002 ptrDataRefRec.pReader = filter->pInputPin.pReader;
1003 ptrDataRefRec.streamSubtype = filter->pInputPin.subType;
1004 PtrToHand( &ptrDataRefRec, &dataRef, sizeof(WineDataRefRecord));
1006 err = NewMovieFromDataRef(&filter->pQTMovie, newMovieActive|newMovieDontInteractWithUser|newMovieDontAutoUpdateClock|newMovieDontAskUnresolvedDataRefs|newMovieAsyncOK, &id, dataRef, 'WINE');
1008 DisposeHandle(dataRef);
1012 FIXME("QuickTime cannot handle media type(%i)\n",err);
1013 return VFW_E_TYPE_NOT_ACCEPTED;
1016 PrePrerollMovie(filter->pQTMovie, 0, fixed1, NULL, NULL);
1017 PrerollMovie(filter->pQTMovie, 0, fixed1);
1018 GoToBeginningOfMovie(filter->pQTMovie);
1019 SetMovieActive(filter->pQTMovie,TRUE);
1021 if (GetMovieLoadState(filter->pQTMovie) < kMovieLoadStateLoaded)
1022 MoviesTask(filter->pQTMovie,100);
1024 trk = GetMovieIndTrackType(filter->pQTMovie, 1, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
1025 TRACE("%p is a video track\n",trk);
1027 hr = QT_Process_Video_Track(filter, trk);
1032 trk = GetMovieIndTrackType(filter->pQTMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
1033 TRACE("%p is a audio track\n",trk);
1035 hr = QT_Process_Audio_Track(filter, trk);
1037 time = GetMovieDuration(filter->pQTMovie);
1038 filter->movie_scale = GetMovieTimeScale(filter->pQTMovie);
1039 filter->sourceSeeking.llDuration = ((double)time / filter->movie_scale) * 10000000;
1040 filter->sourceSeeking.llStop = filter->sourceSeeking.llDuration;
1042 TRACE("Movie duration is %s\n",wine_dbgstr_longlong(filter->sourceSeeking.llDuration));
1044 thread = CreateThread(NULL, 0, QTSplitter_loading_thread, filter, 0, &tid);
1047 TRACE("Created loading thread 0x%08x\n", tid);
1048 CloseHandle(thread);
1050 thread = CreateThread(NULL, 0, QTSplitter_thread, filter, 0, &tid);
1053 TRACE("Created processing thread 0x%08x\n", tid);
1054 CloseHandle(thread);
1057 hr = HRESULT_FROM_WIN32(GetLastError());
1062 static HRESULT WINAPI QTInPin_ReceiveConnection(IPin *iface, IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
1065 ALLOCATOR_PROPERTIES props;
1066 QTInPin *This = impl_from_IPin(iface);
1068 TRACE("(%p/%p)->(%p, %p)\n", This, iface, pReceivePin, pmt);
1070 EnterCriticalSection(This->pin.pCritSec);
1071 This->pReader = NULL;
1073 if (This->pin.pConnectedTo)
1074 hr = VFW_E_ALREADY_CONNECTED;
1075 else if (IPin_QueryAccept(iface, pmt) != S_OK)
1076 hr = VFW_E_TYPE_NOT_ACCEPTED;
1079 PIN_DIRECTION pindirReceive;
1080 IPin_QueryDirection(pReceivePin, &pindirReceive);
1081 if (pindirReceive != PINDIR_OUTPUT)
1082 hr = VFW_E_INVALID_DIRECTION;
1087 LeaveCriticalSection(This->pin.pCritSec);
1091 hr = IPin_QueryInterface(pReceivePin, &IID_IAsyncReader, (LPVOID *)&This->pReader);
1094 LeaveCriticalSection(This->pin.pCritSec);
1095 TRACE("Input source is not an AsyncReader\n");
1099 LeaveCriticalSection(This->pin.pCritSec);
1100 EnterCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1101 hr = QT_Process_Movie(impl_from_IBaseFilter(This->pin.pinInfo.pFilter));
1104 LeaveCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1105 TRACE("Unable to process movie\n");
1109 This->pAlloc = NULL;
1112 props.cbBuffer = impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->outputSize + props.cbAlign;
1115 hr = IAsyncReader_RequestAllocator(This->pReader, NULL, &props, &This->pAlloc);
1118 CopyMediaType(&This->pin.mtCurrent, pmt);
1119 This->pin.pConnectedTo = pReceivePin;
1120 IPin_AddRef(pReceivePin);
1121 hr = IMemAllocator_Commit(This->pAlloc);
1125 QT_RemoveOutputPins(impl_from_IBaseFilter(This->pin.pinInfo.pFilter));
1127 IAsyncReader_Release(This->pReader);
1128 This->pReader = NULL;
1130 IMemAllocator_Release(This->pAlloc);
1131 This->pAlloc = NULL;
1133 TRACE("Size: %i\n", props.cbBuffer);
1134 LeaveCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1139 static HRESULT WINAPI QTInPin_Disconnect(IPin *iface)
1142 QTInPin *This = impl_from_IPin(iface);
1146 hr = IBaseFilter_GetState(This->pin.pinInfo.pFilter, INFINITE, &state);
1147 EnterCriticalSection(This->pin.pCritSec);
1148 if (This->pin.pConnectedTo)
1150 QTSplitter *Parser = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
1152 if (SUCCEEDED(hr) && state == State_Stopped)
1154 IMemAllocator_Decommit(This->pAlloc);
1155 IPin_Disconnect(This->pin.pConnectedTo);
1156 This->pin.pConnectedTo = NULL;
1157 hr = QT_RemoveOutputPins(Parser);
1160 hr = VFW_E_NOT_STOPPED;
1164 LeaveCriticalSection(This->pin.pCritSec);
1168 static HRESULT WINAPI QTInPin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *pmt)
1170 QTInPin *This = impl_from_IPin(iface);
1172 TRACE("(%p)->(%p)\n", This, pmt);
1174 if (IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream))
1176 This->subType = pmt->subtype;
1182 static HRESULT WINAPI QTInPin_EndOfStream(IPin *iface)
1184 QTInPin *pin = impl_from_IPin(iface);
1185 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1187 FIXME("Propagate message on %p\n", This);
1191 static HRESULT WINAPI QTInPin_BeginFlush(IPin *iface)
1193 QTInPin *pin = impl_from_IPin(iface);
1194 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1196 FIXME("Propagate message on %p\n", This);
1200 static HRESULT WINAPI QTInPin_EndFlush(IPin *iface)
1202 QTInPin *pin = impl_from_IPin(iface);
1203 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1205 FIXME("Propagate message on %p\n", This);
1209 static HRESULT WINAPI QTInPin_NewSegment(IPin *iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
1211 QTInPin *pin = impl_from_IPin(iface);
1212 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1214 BasePinImpl_NewSegment(iface, tStart, tStop, dRate);
1215 FIXME("Propagate message on %p\n", This);
1219 static HRESULT WINAPI QTInPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
1221 QTInPin *This = impl_from_IPin(iface);
1223 TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
1227 if (IsEqualIID(riid, &IID_IUnknown))
1229 else if (IsEqualIID(riid, &IID_IPin))
1231 else if (IsEqualIID(riid, &IID_IMediaSeeking))
1232 return IBaseFilter_QueryInterface(This->pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
1236 IUnknown_AddRef((IUnknown *)(*ppv));
1240 FIXME("No interface for %s!\n", debugstr_guid(riid));
1242 return E_NOINTERFACE;
1245 static HRESULT WINAPI QTInPin_EnumMediaTypes(IPin *iface, IEnumMediaTypes **ppEnum)
1247 QTInPin *This = impl_from_IPin(iface);
1249 TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
1251 return EnumMediaTypes_Construct(&This->pin, BasePinImpl_GetMediaType, BasePinImpl_GetMediaTypeVersion, ppEnum);
1254 static const IPinVtbl QT_InputPin_Vtbl = {
1255 QTInPin_QueryInterface,
1258 BaseInputPinImpl_Connect,
1259 QTInPin_ReceiveConnection,
1261 BasePinImpl_ConnectedTo,
1262 BasePinImpl_ConnectionMediaType,
1263 BasePinImpl_QueryPinInfo,
1264 BasePinImpl_QueryDirection,
1265 BasePinImpl_QueryId,
1266 QTInPin_QueryAccept,
1267 QTInPin_EnumMediaTypes,
1268 BasePinImpl_QueryInternalConnections,
1269 QTInPin_EndOfStream,
1278 static inline QTOutPin *impl_QTOutPin_from_IPin( IPin *iface )
1280 return CONTAINING_RECORD(iface, QTOutPin, pin.pin.IPin_iface);
1283 static inline QTOutPin *impl_QTOutPin_from_BasePin( BasePin *iface )
1285 return CONTAINING_RECORD(iface, QTOutPin, pin.pin);
1288 static inline QTOutPin *impl_QTOutPin_from_BaseOutputPin( BaseOutputPin *iface )
1290 return CONTAINING_RECORD(iface, QTOutPin, pin);
1293 static HRESULT WINAPI QTOutPin_QueryInterface(IPin *iface, REFIID riid, void **ppv)
1295 QTOutPin *This = impl_QTOutPin_from_IPin(iface);
1297 TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
1301 if (IsEqualIID(riid, &IID_IUnknown))
1303 else if (IsEqualIID(riid, &IID_IPin))
1305 else if (IsEqualIID(riid, &IID_IMediaSeeking))
1306 return IBaseFilter_QueryInterface(This->pin.pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
1307 else if (IsEqualIID(riid, &IID_IQualityControl))
1308 *ppv = &This->IQualityControl_iface;
1312 IUnknown_AddRef((IUnknown *)(*ppv));
1315 FIXME("No interface for %s!\n", debugstr_guid(riid));
1316 return E_NOINTERFACE;
1319 static ULONG WINAPI QTOutPin_Release(IPin *iface)
1321 QTOutPin *This = impl_QTOutPin_from_IPin(iface);
1322 ULONG refCount = InterlockedDecrement(&This->pin.pin.refCount);
1323 TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
1327 DeleteMediaType(This->pmt);
1328 FreeMediaType(&This->pin.pin.mtCurrent);
1329 CoTaskMemFree(This);
1335 static HRESULT WINAPI QTOutPin_GetMediaType(BasePin *iface, int iPosition, AM_MEDIA_TYPE *pmt)
1337 QTOutPin *This = impl_QTOutPin_from_BasePin(iface);
1340 return E_INVALIDARG;
1342 return VFW_S_NO_MORE_ITEMS;
1343 CopyMediaType(pmt, This->pmt);
1347 static HRESULT WINAPI QTOutPin_DecideBufferSize(BaseOutputPin *iface, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
1353 static HRESULT WINAPI QTOutPin_DecideAllocator(BaseOutputPin *iface, IMemInputPin *pPin, IMemAllocator **pAlloc)
1356 QTOutPin *This = impl_QTOutPin_from_BaseOutputPin(iface);
1357 QTSplitter *QTfilter = impl_from_IBaseFilter(This->pin.pin.pinInfo.pFilter);
1360 if (QTfilter->pInputPin.pAlloc)
1361 hr = IMemInputPin_NotifyAllocator(pPin, QTfilter->pInputPin.pAlloc, FALSE);
1363 hr = VFW_E_NO_ALLOCATOR;
1368 static HRESULT WINAPI QTOutPin_BreakConnect(BaseOutputPin *This)
1372 TRACE("(%p)->()\n", This);
1374 EnterCriticalSection(This->pin.pCritSec);
1375 if (!This->pin.pConnectedTo || !This->pMemInputPin)
1376 hr = VFW_E_NOT_CONNECTED;
1379 hr = IPin_Disconnect(This->pin.pConnectedTo);
1380 IPin_Disconnect(&This->pin.IPin_iface);
1382 LeaveCriticalSection(This->pin.pCritSec);
1387 static const IPinVtbl QT_OutputPin_Vtbl = {
1388 QTOutPin_QueryInterface,
1391 BaseOutputPinImpl_Connect,
1392 BaseOutputPinImpl_ReceiveConnection,
1393 BaseOutputPinImpl_Disconnect,
1394 BasePinImpl_ConnectedTo,
1395 BasePinImpl_ConnectionMediaType,
1396 BasePinImpl_QueryPinInfo,
1397 BasePinImpl_QueryDirection,
1398 BasePinImpl_QueryId,
1399 BasePinImpl_QueryAccept,
1400 BasePinImpl_EnumMediaTypes,
1401 BasePinImpl_QueryInternalConnections,
1402 BaseOutputPinImpl_EndOfStream,
1403 BaseOutputPinImpl_BeginFlush,
1404 BaseOutputPinImpl_EndFlush,
1405 BasePinImpl_NewSegment
1408 static inline QTOutPin *impl_from_IQualityControl( IQualityControl *iface )
1410 return CONTAINING_RECORD(iface, QTOutPin, IQualityControl_iface);
1413 HRESULT WINAPI QT_QualityControl_QueryInterface(IQualityControl *iface, REFIID riid, void **ppv)
1415 QTOutPin *This = impl_from_IQualityControl(iface);
1416 return IPin_QueryInterface(&This->pin.pin.IPin_iface, riid, ppv);
1419 ULONG WINAPI QT_QualityControl_AddRef(IQualityControl *iface)
1421 QTOutPin *This = impl_from_IQualityControl(iface);
1422 return IPin_AddRef(&This->pin.pin.IPin_iface);
1425 ULONG WINAPI QT_QualityControl_Release(IQualityControl *iface)
1427 QTOutPin *This = impl_from_IQualityControl(iface);
1428 return IPin_Release(&This->pin.pin.IPin_iface);
1431 static HRESULT WINAPI QT_QualityControl_Notify(IQualityControl *iface, IBaseFilter *sender, Quality qm)
1433 REFERENCE_TIME late = qm.Late;
1434 if (qm.Late < 0 && -qm.Late > qm.TimeStamp)
1435 late = -qm.TimeStamp;
1436 /* TODO: Do Something */
1440 HRESULT WINAPI QT_QualityControl_SetSink(IQualityControl *iface, IQualityControl *tonotify)
1446 static const IQualityControlVtbl QTOutPin_QualityControl_Vtbl = {
1447 QT_QualityControl_QueryInterface,
1448 QT_QualityControl_AddRef,
1449 QT_QualityControl_Release,
1450 QT_QualityControl_Notify,
1451 QT_QualityControl_SetSink
1454 static const BasePinFuncTable output_BaseFuncTable = {
1456 BaseOutputPinImpl_AttemptConnection,
1457 BasePinImpl_GetMediaTypeVersion,
1458 QTOutPin_GetMediaType
1461 static const BaseOutputPinFuncTable output_BaseOutputFuncTable = {
1462 QTOutPin_DecideBufferSize,
1463 QTOutPin_DecideAllocator,
1464 QTOutPin_BreakConnect
1467 static const OutputQueueFuncTable output_OutputQueueFuncTable = {
1468 OutputQueueImpl_ThreadProc
1471 static HRESULT QT_AddPin(QTSplitter *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt, BOOL video)
1477 target = (IPin**)&This->pVideo_Pin;
1479 target = (IPin**)&This->pAudio_Pin;
1481 if (*target != NULL)
1483 FIXME("We already have a %s pin\n",(video)?"video":"audio");
1487 hr = BaseOutputPin_Construct(&QT_OutputPin_Vtbl, sizeof(QTOutPin), piOutput, &output_BaseFuncTable, &output_BaseOutputFuncTable, &This->filter.csFilter, (IPin**)target);
1490 QTOutPin *pin = (QTOutPin*)*target;
1491 pin->pmt = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
1492 CopyMediaType(pin->pmt, amt);
1493 pin->pin.pin.pinInfo.pFilter = (LPVOID)This;
1494 pin->IQualityControl_iface.lpVtbl = &QTOutPin_QualityControl_Vtbl;
1496 BaseFilterImpl_IncrementPinVersion(&This->filter);
1498 hr = OutputQueue_Construct(&pin->pin, TRUE, TRUE, 5, FALSE, THREAD_PRIORITY_NORMAL, &output_OutputQueueFuncTable, &pin->queue);
1501 ERR("Failed with error %x\n", hr);
1505 static HRESULT WINAPI QTSplitter_ChangeStart(IMediaSeeking *iface)
1507 QTSplitter *This = impl_from_IMediaSeeking(iface);
1508 TRACE("(%p)\n", iface);
1509 EnterCriticalSection(&This->csReceive);
1510 This->movie_time = (This->sourceSeeking.llCurrent * This->movie_scale)/10000000;
1511 This->movie_start = This->movie_time;
1512 LeaveCriticalSection(&This->csReceive);
1516 static HRESULT WINAPI QTSplitter_ChangeStop(IMediaSeeking *iface)
1518 FIXME("(%p) filter hasn't implemented stop position change!\n", iface);
1522 static HRESULT WINAPI QTSplitter_ChangeRate(IMediaSeeking *iface)
1524 FIXME("(%p) filter hasn't implemented rate change!\n", iface);
1528 static HRESULT WINAPI QT_Seeking_QueryInterface(IMediaSeeking * iface, REFIID riid, LPVOID * ppv)
1530 QTSplitter *This = impl_from_IMediaSeeking(iface);
1532 return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv);
1535 static ULONG WINAPI QT_Seeking_AddRef(IMediaSeeking * iface)
1537 QTSplitter *This = impl_from_IMediaSeeking(iface);
1539 return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
1542 static ULONG WINAPI QT_Seeking_Release(IMediaSeeking * iface)
1544 QTSplitter *This = impl_from_IMediaSeeking(iface);
1546 return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
1549 static const IMediaSeekingVtbl QT_Seeking_Vtbl =
1551 QT_Seeking_QueryInterface,
1554 SourceSeekingImpl_GetCapabilities,
1555 SourceSeekingImpl_CheckCapabilities,
1556 SourceSeekingImpl_IsFormatSupported,
1557 SourceSeekingImpl_QueryPreferredFormat,
1558 SourceSeekingImpl_GetTimeFormat,
1559 SourceSeekingImpl_IsUsingTimeFormat,
1560 SourceSeekingImpl_SetTimeFormat,
1561 SourceSeekingImpl_GetDuration,
1562 SourceSeekingImpl_GetStopPosition,
1563 SourceSeekingImpl_GetCurrentPosition,
1564 SourceSeekingImpl_ConvertTimeFormat,
1565 SourceSeekingImpl_SetPositions,
1566 SourceSeekingImpl_GetPositions,
1567 SourceSeekingImpl_GetAvailable,
1568 SourceSeekingImpl_SetRate,
1569 SourceSeekingImpl_GetRate,
1570 SourceSeekingImpl_GetPreroll