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
{
141 typedef struct QTInPin
{
145 IAsyncReader
*pReader
;
146 IMemAllocator
*pAlloc
;
149 typedef struct QTSplitter
{
153 QTOutPin
*pVideo_Pin
;
154 QTOutPin
*pAudio_Pin
;
156 ALLOCATOR_PROPERTIES props
;
159 QTVisualContextRef vContext
;
161 MovieAudioExtractionRef aSession
;
166 CRITICAL_SECTION csReceive
;
168 SourceSeeking sourceSeeking
;
169 TimeValue movie_time
;
170 TimeValue movie_start
;
171 TimeScale movie_scale
;
174 static const IPinVtbl QT_OutputPin_Vtbl
;
175 static const IPinVtbl QT_InputPin_Vtbl
;
176 static const IBaseFilterVtbl QT_Vtbl
;
177 static const IMediaSeekingVtbl QT_Seeking_Vtbl
;
179 static HRESULT
QT_AddPin(QTSplitter
*This
, const PIN_INFO
*piOutput
, const AM_MEDIA_TYPE
*amt
, BOOL video
);
180 static HRESULT
QT_RemoveOutputPins(QTSplitter
*This
);
182 static HRESULT WINAPI
QTSplitter_ChangeStart(IMediaSeeking
*iface
);
183 static HRESULT WINAPI
QTSplitter_ChangeStop(IMediaSeeking
*iface
);
184 static HRESULT WINAPI
QTSplitter_ChangeRate(IMediaSeeking
*iface
);
186 static inline QTSplitter
*impl_from_IMediaSeeking( IMediaSeeking
*iface
)
188 return (QTSplitter
*)((char*)iface
- FIELD_OFFSET(QTSplitter
, sourceSeeking
.lpVtbl
));
195 static IPin
* WINAPI
QT_GetPin(BaseFilter
*iface
, int pos
)
197 QTSplitter
*This
= (QTSplitter
*)iface
;
198 TRACE("Asking for pos %x\n", pos
);
200 if (pos
> 2 || pos
< 0)
205 IPin_AddRef((IPin
*)&This
->pInputPin
);
206 return (IPin
*)&This
->pInputPin
;
208 if (This
->pVideo_Pin
)
209 IPin_AddRef((IPin
*)This
->pVideo_Pin
);
210 return (IPin
*)This
->pVideo_Pin
;
212 if (This
->pAudio_Pin
)
213 IPin_AddRef((IPin
*)This
->pAudio_Pin
);
214 return (IPin
*)This
->pAudio_Pin
;
220 static LONG WINAPI
QT_GetPinCount(BaseFilter
*iface
)
222 QTSplitter
*This
= (QTSplitter
*)iface
;
224 if (This
->pAudio_Pin
) c
++;
225 if (This
->pVideo_Pin
) c
++;
229 static const BaseFilterFuncTable BaseFuncTable
= {
234 IUnknown
* CALLBACK
QTSplitter_create(IUnknown
*punkout
, HRESULT
*phr
)
236 IUnknown
*obj
= NULL
;
239 static const WCHAR wcsInputPinName
[] = {'I','n','p','u','t',' ','P','i','n',0};
243 RegisterWineDataHandler();
245 This
= CoTaskMemAlloc(sizeof(*This
));
246 obj
= (IUnknown
*)This
;
249 *phr
= E_OUTOFMEMORY
;
252 ZeroMemory(This
,sizeof(*This
));
254 BaseFilter_Init(&This
->filter
, &QT_Vtbl
, &CLSID_QTSplitter
, (DWORD_PTR
)(__FILE__
": QTSplitter.csFilter"), &BaseFuncTable
);
256 InitializeCriticalSection(&This
->csReceive
);
257 This
->csReceive
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": QTSplitter.csReceive");
259 This
->pVideo_Pin
= NULL
;
260 This
->pAudio_Pin
= NULL
;
261 This
->state
= State_Stopped
;
262 This
->aSession
= NULL
;
263 This
->runEvent
= CreateEventW(NULL
, 0, 0, NULL
);
265 piInput
= &This
->pInputPin
.pin
.pinInfo
;
266 piInput
->dir
= PINDIR_INPUT
;
267 piInput
->pFilter
= (IBaseFilter
*)This
;
268 lstrcpynW(piInput
->achName
, wcsInputPinName
, sizeof(piInput
->achName
) / sizeof(piInput
->achName
[0]));
269 This
->pInputPin
.pin
.lpVtbl
= &QT_InputPin_Vtbl
;
270 This
->pInputPin
.pin
.refCount
= 1;
271 This
->pInputPin
.pin
.pConnectedTo
= NULL
;
272 This
->pInputPin
.pin
.pCritSec
= &This
->filter
.csFilter
;
274 SourceSeeking_Init(&This
->sourceSeeking
, &QT_Seeking_Vtbl
, QTSplitter_ChangeStop
, QTSplitter_ChangeStart
, QTSplitter_ChangeRate
, &This
->filter
.csFilter
);
280 static void QT_Destroy(QTSplitter
*This
)
282 IPin
*connected
= NULL
;
285 TRACE("Destroying\n");
287 /* Don't need to clean up output pins, disconnecting input pin will do that */
288 IPin_ConnectedTo((IPin
*)&This
->pInputPin
, &connected
);
291 IPin_Disconnect(connected
);
292 IPin_Release(connected
);
294 pinref
= IPin_Release((IPin
*)&This
->pInputPin
);
297 ERR("pinref should be null, is %u, destroying anyway\n", pinref
);
298 assert((LONG
)pinref
> 0);
301 pinref
= IPin_Release((IPin
*)&This
->pInputPin
);
305 DisposeMovie(This
->pQTMovie
);
307 QTVisualContextRelease(This
->vContext
);
309 MovieAudioExtractionEnd(This
->aSession
);
310 CloseHandle(This
->runEvent
);
314 This
->csReceive
.DebugInfo
->Spare
[0] = 0;
315 DeleteCriticalSection(&This
->csReceive
);
320 static HRESULT WINAPI
QT_QueryInterface(IBaseFilter
*iface
, REFIID riid
, LPVOID
*ppv
)
322 QTSplitter
*This
= (QTSplitter
*)iface
;
323 TRACE("(%s, %p)\n", debugstr_guid(riid
), ppv
);
327 if (IsEqualIID(riid
, &IID_IUnknown
))
329 else if (IsEqualIID(riid
, &IID_IPersist
))
331 else if (IsEqualIID(riid
, &IID_IMediaFilter
))
333 else if (IsEqualIID(riid
, &IID_IBaseFilter
))
335 else if (IsEqualIID(riid
, &IID_IMediaSeeking
))
336 *ppv
= &This
->sourceSeeking
;
340 IUnknown_AddRef((IUnknown
*)(*ppv
));
344 if (!IsEqualIID(riid
, &IID_IPin
) && !IsEqualIID(riid
, &IID_IVideoWindow
))
345 FIXME("No interface for %s!\n", debugstr_guid(riid
));
347 return E_NOINTERFACE
;
350 static ULONG WINAPI
QT_Release(IBaseFilter
*iface
)
352 QTSplitter
*This
= (QTSplitter
*)iface
;
353 ULONG refCount
= BaseFilterImpl_Release(iface
);
355 TRACE("(%p)->() Release from %d\n", This
, refCount
+ 1);
363 static HRESULT WINAPI
QT_Stop(IBaseFilter
*iface
)
365 QTSplitter
*This
= (QTSplitter
*)iface
;
369 EnterCriticalSection(&This
->csReceive
);
370 IAsyncReader_BeginFlush(This
->pInputPin
.pReader
);
371 IAsyncReader_EndFlush(This
->pInputPin
.pReader
);
372 LeaveCriticalSection(&This
->csReceive
);
377 static HRESULT WINAPI
QT_Pause(IBaseFilter
*iface
)
385 static OSErr
QT_Create_Extract_Session(QTSplitter
*filter
)
387 AudioStreamBasicDescription aDesc
;
391 pvi
= (WAVEFORMATEX
*)filter
->pAudio_Pin
->pmt
->pbFormat
;
393 err
= MovieAudioExtractionBegin(filter
->pQTMovie
, 0, &filter
->aSession
);
396 ERR("Failed to begin Extraction session %i\n",err
);
400 err
= MovieAudioExtractionGetProperty(filter
->aSession
,
401 kQTPropertyClass_MovieAudioExtraction_Audio
,
402 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription
,
403 sizeof(AudioStreamBasicDescription
), &aDesc
, NULL
);
407 MovieAudioExtractionEnd(filter
->aSession
);
408 filter
->aSession
= NULL
;
409 ERR("Failed to get session description %i\n",err
);
413 aDesc
.mFormatID
= kAudioFormatLinearPCM
;
414 aDesc
.mFormatFlags
= kLinearPCMFormatFlagIsSignedInteger
+
415 kAudioFormatFlagIsPacked
;
416 aDesc
.mFramesPerPacket
= 1;
417 aDesc
.mChannelsPerFrame
= pvi
->nChannels
;
418 aDesc
.mBitsPerChannel
= pvi
->wBitsPerSample
;
419 aDesc
.mSampleRate
= pvi
->nSamplesPerSec
;
420 aDesc
.mBytesPerFrame
= (aDesc
.mBitsPerChannel
* aDesc
.mChannelsPerFrame
) / 8;
421 aDesc
.mBytesPerPacket
= aDesc
.mBytesPerFrame
* aDesc
.mFramesPerPacket
;
423 err
= MovieAudioExtractionSetProperty(filter
->aSession
,
424 kQTPropertyClass_MovieAudioExtraction_Audio
,
425 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription
,
426 sizeof(AudioStreamBasicDescription
), &aDesc
);
428 if (aDesc
.mFormatID
!= kAudioFormatLinearPCM
)
430 ERR("Not PCM Wave\n");
433 if (aDesc
.mFormatFlags
!= kLinearPCMFormatFlagIsSignedInteger
+
434 kAudioFormatFlagIsPacked
)
436 ERR("Unhandled Flags\n");
439 if (aDesc
.mFramesPerPacket
!= 1)
441 ERR("Unhandled Frames per packet %li\n",aDesc
.mFramesPerPacket
);
444 if (aDesc
.mChannelsPerFrame
!= pvi
->nChannels
)
446 ERR("Unhandled channel count %li\n",aDesc
.mChannelsPerFrame
);
449 if (aDesc
.mBitsPerChannel
!= pvi
->wBitsPerSample
)
451 ERR("Unhandled bits per channel %li\n",aDesc
.mBitsPerChannel
);
454 if (aDesc
.mSampleRate
!= pvi
->nSamplesPerSec
)
456 ERR("Unhandled sample rate %f\n",aDesc
.mSampleRate
);
462 ERR("Failed to create Extraction Session\n");
463 MovieAudioExtractionEnd(filter
->aSession
);
464 filter
->aSession
= NULL
;
470 static DWORD WINAPI
QTSplitter_thread(LPVOID data
)
472 QTSplitter
*This
= (QTSplitter
*)data
;
475 CVPixelBufferRef pixelBuffer
= NULL
;
479 if (This
->pAudio_Pin
)
481 /* according to QA1469 a movie has to be fully loaded before we
482 can reliably start the Extraction session */
484 while(GetMovieLoadState(This
->pQTMovie
) < kMovieLoadStateComplete
)
485 MoviesTask(This
->pQTMovie
,1000);
487 QT_Create_Extract_Session(This
);
490 WaitForSingleObject(This
->runEvent
, -1);
492 EnterCriticalSection(&This
->csReceive
);
493 This
->state
= State_Running
;
494 /* Prime the pump: Needed for MPEG streams */
495 GetMovieNextInterestingTime(This
->pQTMovie
, nextTimeEdgeOK
| nextTimeStep
, 0, NULL
, This
->movie_time
, 1, &next_time
, NULL
);
497 GetMovieTime(This
->pQTMovie
, &tr
);
498 LeaveCriticalSection(&This
->csReceive
);
501 LONGLONG tStart
=0, tStop
=0;
502 LONGLONG mStart
=0, mStop
=0;
505 EnterCriticalSection(&This
->csReceive
);
506 GetMovieNextInterestingTime(This
->pQTMovie
, nextTimeStep
, 0, NULL
, This
->movie_time
, 1, &next_time
, NULL
);
510 TRACE("No next time\n");
511 LeaveCriticalSection(&This
->csReceive
);
515 tr
.value
= SInt64ToWide(next_time
);
516 SetMovieTime(This
->pQTMovie
, &tr
);
517 MoviesTask(This
->pQTMovie
,0);
518 QTVisualContextTask(This
->vContext
);
520 TRACE("In loop at time %ld\n",This
->movie_time
);
521 TRACE("In Next time %ld\n",next_time
);
523 mStart
= This
->movie_time
;
526 time
= (float)(This
->movie_time
- This
->movie_start
) / This
->movie_scale
;
527 tStart
= time
* 10000000;
528 time
= (float)(next_time
- This
->movie_start
) / This
->movie_scale
;
529 tStop
= time
* 10000000;
532 if (This
->pAudio_Pin
&& ((BaseOutputPin
*)This
->pAudio_Pin
)->pin
.pConnectedTo
&& This
->aSession
)
536 IMediaSample
*sample
= NULL
;
537 AudioBufferList aData
;
543 pvi
= (WAVEFORMATEX
*)This
->pAudio_Pin
->pmt
->pbFormat
;
545 hr
= BaseOutputPinImpl_GetDeliveryBuffer((BaseOutputPin
*)This
->pAudio_Pin
, &sample
, NULL
, NULL
, 0);
549 ERR("Audio: Unable to get delivery buffer (%x)\n", hr
);
553 hr
= IMediaSample_GetPointer(sample
, &ptr
);
556 ERR("Audio: Unable to get pointer to buffer (%x)\n", hr
);
560 duration
= (float)next_time
/ This
->movie_scale
;
561 time
= (float)This
->movie_time
/ This
->movie_scale
;
563 frames
= pvi
->nSamplesPerSec
* duration
;
564 TRACE("Need audio for %f seconds (%li frames)\n",duration
,frames
);
566 data_size
= IMediaSample_GetSize(sample
);
567 if (data_size
< frames
* pvi
->nBlockAlign
)
568 FIXME("Audio buffer is too small\n");
570 aData
.mNumberBuffers
= 1;
571 aData
.mBuffers
[0].mNumberChannels
= pvi
->nChannels
;
572 aData
.mBuffers
[0].mDataByteSize
= data_size
;
573 aData
.mBuffers
[0].mData
= ptr
;
575 err
= MovieAudioExtractionFillBuffer(This
->aSession
, &frames
, &aData
, &flags
);
576 TRACE("Got %i frames\n",(int)frames
);
578 IMediaSample_SetActualDataLength(sample
, frames
* pvi
->nBlockAlign
);
580 IMediaSample_SetMediaTime(sample
, &mStart
, &mStop
);
581 IMediaSample_SetTime(sample
, &tStart
, &tStop
);
583 hr
= OutputQueue_Receive(This
->pAudio_Pin
->queue
, sample
);
584 TRACE("Audio Delivered (%x)\n",hr
);
588 IMediaSample_Release(sample
);
591 TRACE("Audio Pin not connected or no Audio\n");
594 if (This
->pVideo_Pin
&& QTVisualContextIsNewImageAvailable(This
->vContext
,0))
596 err
= QTVisualContextCopyImageForTime(This
->vContext
, NULL
, NULL
, &pixelBuffer
);
601 IMediaSample
*sample
= NULL
;
603 hr
= BaseOutputPinImpl_GetDeliveryBuffer((BaseOutputPin
*)This
->pVideo_Pin
, &sample
, NULL
, NULL
, 0);
606 ERR("Video: Unable to get delivery buffer (%x)\n", hr
);
610 data_size
= IMediaSample_GetSize(sample
);
611 if (data_size
< This
->outputSize
)
613 ERR("Sample size is too small %d < %d\n", data_size
, This
->outputSize
)
619 hr
= IMediaSample_GetPointer(sample
, &ptr
);
622 ERR("Video: Unable to get pointer to buffer (%x)\n", hr
);
626 hr
= AccessPixelBufferPixels( pixelBuffer
, ptr
);
629 ERR("Failed to access Pixels\n");
633 IMediaSample_SetActualDataLength(sample
, This
->outputSize
);
635 IMediaSample_SetMediaTime(sample
, &mStart
, &mStop
);
636 IMediaSample_SetTime(sample
, &tStart
, &tStop
);
638 hr
= OutputQueue_Receive(This
->pVideo_Pin
->queue
, sample
);
639 TRACE("Video Delivered (%x)\n",hr
);
643 IMediaSample_Release(sample
);
645 CVPixelBufferRelease(pixelBuffer
);
649 TRACE("No video to deliver\n");
651 This
->movie_time
= next_time
;
652 LeaveCriticalSection(&This
->csReceive
);
653 } while (hr
== S_OK
);
655 This
->state
= State_Stopped
;
656 if (This
->pAudio_Pin
)
657 OutputQueue_EOS(This
->pAudio_Pin
->queue
);
658 if (This
->pVideo_Pin
)
659 OutputQueue_EOS(This
->pVideo_Pin
->queue
);
664 static HRESULT WINAPI
QT_Run(IBaseFilter
*iface
, REFERENCE_TIME tStart
)
667 QTSplitter
*This
= (QTSplitter
*)iface
;
668 HRESULT hr_any
= VFW_E_NOT_CONNECTED
;
670 TRACE("(%s)\n", wine_dbgstr_longlong(tStart
));
672 EnterCriticalSection(&This
->csReceive
);
673 This
->filter
.rtStreamStart
= tStart
;
675 if (This
->pVideo_Pin
)
676 hr
= BaseOutputPinImpl_Active((BaseOutputPin
*)This
->pVideo_Pin
);
679 if (This
->pAudio_Pin
)
680 hr
= BaseOutputPinImpl_Active((BaseOutputPin
*)This
->pAudio_Pin
);
686 SetEvent(This
->runEvent
);
687 LeaveCriticalSection(&This
->csReceive
);
692 static HRESULT WINAPI
QT_GetState(IBaseFilter
*iface
, DWORD dwMilliSecsTimeout
, FILTER_STATE
*pState
)
694 QTSplitter
*This
= (QTSplitter
*)iface
;
695 TRACE("(%d, %p)\n", dwMilliSecsTimeout
, pState
);
697 *pState
= This
->state
;
702 static HRESULT WINAPI
QT_FindPin(IBaseFilter
*iface
, LPCWSTR Id
, IPin
**ppPin
)
704 FIXME("(%p)->(%s,%p) stub\n", iface
, debugstr_w(Id
), ppPin
);
708 static const IBaseFilterVtbl QT_Vtbl
= {
710 BaseFilterImpl_AddRef
,
712 BaseFilterImpl_GetClassID
,
717 BaseFilterImpl_SetSyncSource
,
718 BaseFilterImpl_GetSyncSource
,
719 BaseFilterImpl_EnumPins
,
721 BaseFilterImpl_QueryFilterInfo
,
722 BaseFilterImpl_JoinFilterGraph
,
723 BaseFilterImpl_QueryVendorInfo
729 static HRESULT
QT_RemoveOutputPins(QTSplitter
*This
)
732 TRACE("(%p)\n", This
);
734 if (This
->pVideo_Pin
)
736 hr
= BaseOutputPinImpl_BreakConnect(&This
->pVideo_Pin
->pin
);
737 TRACE("Disconnect: %08x\n", hr
);
738 IPin_Release((IPin
*)This
->pVideo_Pin
);
739 This
->pVideo_Pin
= NULL
;
741 if (This
->pAudio_Pin
)
743 hr
= BaseOutputPinImpl_BreakConnect(&This
->pAudio_Pin
->pin
);
744 TRACE("Disconnect: %08x\n", hr
);
745 IPin_Release((IPin
*)This
->pAudio_Pin
);
746 This
->pAudio_Pin
= NULL
;
749 BaseFilterImpl_IncrementPinVersion((BaseFilter
*)This
);
753 static ULONG WINAPI
QTInPin_Release(IPin
*iface
)
755 QTInPin
*This
= (QTInPin
*)iface
;
756 ULONG refCount
= InterlockedDecrement(&This
->pin
.refCount
);
758 TRACE("(%p)->() Release from %d\n", iface
, refCount
+ 1);
761 FreeMediaType(&This
->pin
.mtCurrent
);
763 IMemAllocator_Release(This
->pAlloc
);
765 This
->pin
.lpVtbl
= NULL
;
772 static HRESULT
QT_Process_Video_Track(QTSplitter
* filter
, Track trk
)
775 VIDEOINFOHEADER
* pvi
;
779 static const WCHAR szwVideoOut
[] = {'V','i','d','e','o',0};
780 CFMutableDictionaryRef pixelBufferOptions
= NULL
;
781 CFMutableDictionaryRef visualContextOptions
= NULL
;
782 CFNumberRef n
= NULL
;
784 DWORD outputWidth
, outputHeight
, outputDepth
;
785 Fixed trackWidth
, trackHeight
;
787 ZeroMemory(&amt
, sizeof(amt
));
788 amt
.formattype
= FORMAT_VideoInfo
;
789 amt
.majortype
= MEDIATYPE_Video
;
790 amt
.subtype
= MEDIASUBTYPE_RGB24
;
792 GetTrackDimensions(trk
, &trackWidth
, &trackHeight
);
795 outputWidth
= Fix2Long(trackWidth
);
796 outputHeight
= Fix2Long(trackHeight
);
797 TRACE("Width %i Height %i\n",outputWidth
, outputHeight
);
799 amt
.cbFormat
= sizeof(VIDEOINFOHEADER
);
800 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
801 ZeroMemory(amt
.pbFormat
, amt
.cbFormat
);
802 pvi
= (VIDEOINFOHEADER
*)amt
.pbFormat
;
803 pvi
->bmiHeader
.biSize
= sizeof (BITMAPINFOHEADER
);
804 pvi
->bmiHeader
.biWidth
= outputWidth
;
805 pvi
->bmiHeader
.biHeight
= outputHeight
;
806 pvi
->bmiHeader
.biPlanes
= 1;
807 pvi
->bmiHeader
.biBitCount
= 24;
808 pvi
->bmiHeader
.biCompression
= BI_RGB
;
809 pvi
->bmiHeader
.biSizeImage
= outputWidth
* outputHeight
* outputDepth
;
811 filter
->outputSize
= pvi
->bmiHeader
.biSizeImage
;
814 pixelBufferOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
816 t
= k32ARGBPixelFormat
;
817 n
= CFNumberCreate(NULL
, kCFNumberIntType
, &t
);
818 CFDictionaryAddValue(pixelBufferOptions
, kCVPixelBufferPixelFormatTypeKey
, n
);
821 n
= CFNumberCreate(NULL
, kCFNumberIntType
, &outputWidth
);
822 CFDictionaryAddValue(pixelBufferOptions
, kCVPixelBufferWidthKey
, n
);
825 n
= CFNumberCreate(NULL
, kCFNumberIntType
, &outputHeight
);
826 CFDictionaryAddValue(pixelBufferOptions
, kCVPixelBufferHeightKey
, n
);
830 n
= CFNumberCreate(NULL
, kCFNumberIntType
, &t
);
831 CFDictionaryAddValue(pixelBufferOptions
, kCVPixelBufferBytesPerRowAlignmentKey
, n
);
834 visualContextOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
836 CFDictionarySetValue(visualContextOptions
, kQTVisualContextPixelBufferAttributesKey
, pixelBufferOptions
);
838 err
= QTPixelBufferContextCreate(NULL
, visualContextOptions
,&filter
->vContext
);
839 CFRelease(pixelBufferOptions
);
840 CFRelease(visualContextOptions
);
843 ERR("Failed to create Visual Context\n");
847 err
= SetMovieVisualContext(filter
->pQTMovie
, filter
->vContext
);
850 ERR("Failed to set Visual Context\n");
854 piOutput
.dir
= PINDIR_OUTPUT
;
855 piOutput
.pFilter
= (IBaseFilter
*)filter
;
856 lstrcpyW(piOutput
.achName
,szwVideoOut
);
858 hr
= QT_AddPin(filter
, &piOutput
, &amt
, TRUE
);
860 ERR("Failed to add Video Track\n");
862 TRACE("Video Pin %p\n",filter
->pVideo_Pin
);
867 static HRESULT
QT_Process_Audio_Track(QTSplitter
* filter
, Track trk
)
873 static const WCHAR szwAudioOut
[] = {'A','u','d','i','o',0};
876 SoundDescriptionHandle aDesc
= (SoundDescriptionHandle
) NewHandle(sizeof(SoundDescription
));
878 audioMedia
= GetTrackMedia(trk
);
879 GetMediaSampleDescription(audioMedia
, 1, (SampleDescriptionHandle
)aDesc
);
881 ZeroMemory(&amt
, sizeof(amt
));
882 amt
.formattype
= FORMAT_WaveFormatEx
;
883 amt
.majortype
= MEDIATYPE_Audio
;
884 amt
.subtype
= MEDIASUBTYPE_PCM
;
885 amt
.bTemporalCompression
= 0;
887 amt
.cbFormat
= sizeof(WAVEFORMATEX
);
888 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
889 ZeroMemory(amt
.pbFormat
, amt
.cbFormat
);
890 pvi
= (WAVEFORMATEX
*)amt
.pbFormat
;
892 pvi
->cbSize
= sizeof(WAVEFORMATEX
);
893 pvi
->wFormatTag
= WAVE_FORMAT_PCM
;
894 pvi
->nChannels
= ((SoundDescription
)**aDesc
).numChannels
;
895 if (pvi
->nChannels
< 1 || pvi
->nChannels
> 2)
897 pvi
->nSamplesPerSec
= (((SoundDescription
)**aDesc
).sampleRate
/65536);
898 if (pvi
->nSamplesPerSec
< 8000 || pvi
->nChannels
> 48000)
899 pvi
->nSamplesPerSec
= 44100;
900 pvi
->wBitsPerSample
= ((SoundDescription
)**aDesc
).sampleSize
;
901 if (pvi
->wBitsPerSample
< 8 || pvi
->wBitsPerSample
> 32)
902 pvi
->wBitsPerSample
= 16;
903 pvi
->nBlockAlign
= (pvi
->nChannels
* pvi
->wBitsPerSample
) / 8;
904 pvi
->nAvgBytesPerSec
= pvi
->nSamplesPerSec
* pvi
->nBlockAlign
;
906 DisposeHandle((Handle
)aDesc
);
908 piOutput
.dir
= PINDIR_OUTPUT
;
909 piOutput
.pFilter
= (IBaseFilter
*)filter
;
910 lstrcpyW(piOutput
.achName
,szwAudioOut
);
912 hr
= QT_AddPin(filter
, &piOutput
, &amt
, FALSE
);
914 ERR("Failed to add Audio Track\n");
916 TRACE("Audio Pin %p\n",filter
->pAudio_Pin
);
920 static HRESULT
QT_Process_Movie(QTSplitter
* filter
)
924 WineDataRefRecord ptrDataRefRec
;
925 Handle dataRef
= NULL
;
932 TRACE("Trying movie connect\n");
934 ptrDataRefRec
.pReader
= filter
->pInputPin
.pReader
;
935 ptrDataRefRec
.streamSubtype
= filter
->pInputPin
.subType
;
936 PtrToHand( &ptrDataRefRec
, &dataRef
, sizeof(WineDataRefRecord
));
938 err
= NewMovieFromDataRef(&filter
->pQTMovie
, newMovieActive
|newMovieDontInteractWithUser
|newMovieDontAutoUpdateClock
|newMovieDontAskUnresolvedDataRefs
|newMovieAsyncOK
, &id
, dataRef
, 'WINE');
940 DisposeHandle(dataRef
);
944 FIXME("QuickTime cannot handle media type(%i)\n",err
);
945 return VFW_E_TYPE_NOT_ACCEPTED
;
948 PrePrerollMovie(filter
->pQTMovie
, 0, fixed1
, NULL
, NULL
);
949 PrerollMovie(filter
->pQTMovie
, 0, fixed1
);
950 GoToBeginningOfMovie(filter
->pQTMovie
);
951 SetMovieActive(filter
->pQTMovie
,TRUE
);
953 if (GetMovieLoadState(filter
->pQTMovie
) < kMovieLoadStateLoaded
)
954 MoviesTask(filter
->pQTMovie
,100);
956 trk
= GetMovieIndTrackType(filter
->pQTMovie
, 1, VisualMediaCharacteristic
, movieTrackCharacteristic
| movieTrackEnabledOnly
);
957 TRACE("%p is a video track\n",trk
);
959 hr
= QT_Process_Video_Track(filter
, trk
);
964 trk
= GetMovieIndTrackType(filter
->pQTMovie
, 1, AudioMediaCharacteristic
, movieTrackCharacteristic
| movieTrackEnabledOnly
);
965 TRACE("%p is a audio track\n",trk
);
967 hr
= QT_Process_Audio_Track(filter
, trk
);
969 time
= GetMovieDuration(filter
->pQTMovie
);
970 filter
->movie_scale
= GetMovieTimeScale(filter
->pQTMovie
);
971 filter
->sourceSeeking
.llDuration
= ((double)time
/ filter
->movie_scale
) * 10000000;
972 filter
->sourceSeeking
.llStop
= filter
->sourceSeeking
.llDuration
;
974 TRACE("Movie duration is %s\n",wine_dbgstr_longlong(filter
->sourceSeeking
.llDuration
));
976 thread
= CreateThread(NULL
, 0, QTSplitter_thread
, filter
, 0, &tid
);
979 TRACE("Created thread 0x%08x\n", tid
);
983 hr
= HRESULT_FROM_WIN32(GetLastError());
988 static HRESULT WINAPI
QTInPin_ReceiveConnection(IPin
*iface
, IPin
*pReceivePin
, const AM_MEDIA_TYPE
*pmt
)
991 ALLOCATOR_PROPERTIES props
;
992 QTInPin
*This
= (QTInPin
*)iface
;
994 TRACE("(%p/%p)->(%p, %p)\n", This
, iface
, pReceivePin
, pmt
);
996 EnterCriticalSection(This
->pin
.pCritSec
);
997 This
->pReader
= NULL
;
999 if (This
->pin
.pConnectedTo
)
1000 hr
= VFW_E_ALREADY_CONNECTED
;
1001 else if (IPin_QueryAccept(iface
, pmt
) != S_OK
)
1002 hr
= VFW_E_TYPE_NOT_ACCEPTED
;
1005 PIN_DIRECTION pindirReceive
;
1006 IPin_QueryDirection(pReceivePin
, &pindirReceive
);
1007 if (pindirReceive
!= PINDIR_OUTPUT
)
1008 hr
= VFW_E_INVALID_DIRECTION
;
1013 LeaveCriticalSection(This
->pin
.pCritSec
);
1017 hr
= IPin_QueryInterface(pReceivePin
, &IID_IAsyncReader
, (LPVOID
*)&This
->pReader
);
1020 LeaveCriticalSection(This
->pin
.pCritSec
);
1021 TRACE("Input source is not an AsyncReader\n");
1025 LeaveCriticalSection(This
->pin
.pCritSec
);
1026 EnterCriticalSection(&((QTSplitter
*)This
->pin
.pinInfo
.pFilter
)->filter
.csFilter
);
1027 hr
= QT_Process_Movie((QTSplitter
*)This
->pin
.pinInfo
.pFilter
);
1030 LeaveCriticalSection(&((QTSplitter
*)This
->pin
.pinInfo
.pFilter
)->filter
.csFilter
);
1031 TRACE("Unable to process movie\n");
1035 This
->pAlloc
= NULL
;
1038 props
.cbBuffer
= ((QTSplitter
*)This
->pin
.pinInfo
.pFilter
)->outputSize
+ props
.cbAlign
;
1041 hr
= IAsyncReader_RequestAllocator(This
->pReader
, NULL
, &props
, &This
->pAlloc
);
1044 CopyMediaType(&This
->pin
.mtCurrent
, pmt
);
1045 This
->pin
.pConnectedTo
= pReceivePin
;
1046 IPin_AddRef(pReceivePin
);
1047 hr
= IMemAllocator_Commit(This
->pAlloc
);
1051 QT_RemoveOutputPins((QTSplitter
*)This
->pin
.pinInfo
.pFilter
);
1053 IAsyncReader_Release(This
->pReader
);
1054 This
->pReader
= NULL
;
1056 IMemAllocator_Release(This
->pAlloc
);
1057 This
->pAlloc
= NULL
;
1059 TRACE("Size: %i\n", props
.cbBuffer
);
1060 LeaveCriticalSection(&((QTSplitter
*)This
->pin
.pinInfo
.pFilter
)->filter
.csFilter
);
1065 static HRESULT WINAPI
QTInPin_Disconnect(IPin
*iface
)
1068 QTInPin
*This
= (QTInPin
*)iface
;
1072 hr
= IBaseFilter_GetState(This
->pin
.pinInfo
.pFilter
, INFINITE
, &state
);
1073 EnterCriticalSection(This
->pin
.pCritSec
);
1074 if (This
->pin
.pConnectedTo
)
1076 QTSplitter
*Parser
= (QTSplitter
*)This
->pin
.pinInfo
.pFilter
;
1078 if (SUCCEEDED(hr
) && state
== State_Stopped
)
1080 IMemAllocator_Decommit(This
->pAlloc
);
1081 IPin_Disconnect(This
->pin
.pConnectedTo
);
1082 This
->pin
.pConnectedTo
= NULL
;
1083 hr
= QT_RemoveOutputPins(Parser
);
1086 hr
= VFW_E_NOT_STOPPED
;
1090 LeaveCriticalSection(This
->pin
.pCritSec
);
1094 static HRESULT WINAPI
QTInPin_QueryAccept(IPin
*iface
, const AM_MEDIA_TYPE
*pmt
)
1096 QTInPin
*This
= (QTInPin
*)iface
;
1098 TRACE("(%p)->(%p)\n", This
, pmt
);
1100 if (IsEqualIID(&pmt
->majortype
, &MEDIATYPE_Stream
))
1102 This
->subType
= pmt
->subtype
;
1108 static HRESULT WINAPI
QTInPin_EndOfStream(IPin
*iface
)
1110 QTInPin
*pin
= (QTInPin
*)iface
;
1111 QTSplitter
*This
= (QTSplitter
*)pin
->pin
.pinInfo
.pFilter
;
1113 FIXME("Propagate message on %p\n", This
);
1117 static HRESULT WINAPI
QTInPin_BeginFlush(IPin
*iface
)
1119 QTInPin
*pin
= (QTInPin
*)iface
;
1120 QTSplitter
*This
= (QTSplitter
*)pin
->pin
.pinInfo
.pFilter
;
1122 FIXME("Propagate message on %p\n", This
);
1126 static HRESULT WINAPI
QTInPin_EndFlush(IPin
*iface
)
1128 QTInPin
*pin
= (QTInPin
*)iface
;
1129 QTSplitter
*This
= (QTSplitter
*)pin
->pin
.pinInfo
.pFilter
;
1131 FIXME("Propagate message on %p\n", This
);
1135 static HRESULT WINAPI
QTInPin_NewSegment(IPin
*iface
, REFERENCE_TIME tStart
, REFERENCE_TIME tStop
, double dRate
)
1137 QTInPin
*pin
= (QTInPin
*)iface
;
1138 QTSplitter
*This
= (QTSplitter
*)pin
->pin
.pinInfo
.pFilter
;
1140 BasePinImpl_NewSegment(iface
, tStart
, tStop
, dRate
);
1141 FIXME("Propagate message on %p\n", This
);
1145 static HRESULT WINAPI
QTInPin_QueryInterface(IPin
* iface
, REFIID riid
, LPVOID
* ppv
)
1147 QTInPin
*This
= (QTInPin
*)iface
;
1149 TRACE("(%p/%p)->(%s, %p)\n", This
, iface
, debugstr_guid(riid
), ppv
);
1153 if (IsEqualIID(riid
, &IID_IUnknown
))
1155 else if (IsEqualIID(riid
, &IID_IPin
))
1157 else if (IsEqualIID(riid
, &IID_IMediaSeeking
))
1158 return IBaseFilter_QueryInterface(This
->pin
.pinInfo
.pFilter
, &IID_IMediaSeeking
, ppv
);
1162 IUnknown_AddRef((IUnknown
*)(*ppv
));
1166 FIXME("No interface for %s!\n", debugstr_guid(riid
));
1168 return E_NOINTERFACE
;
1171 static HRESULT WINAPI
QTInPin_EnumMediaTypes(IPin
*iface
, IEnumMediaTypes
**ppEnum
)
1173 BasePin
*This
= (BasePin
*)iface
;
1175 TRACE("(%p/%p)->(%p)\n", This
, iface
, ppEnum
);
1177 return EnumMediaTypes_Construct(This
, BasePinImpl_GetMediaType
, BasePinImpl_GetMediaTypeVersion
, ppEnum
);
1180 static const IPinVtbl QT_InputPin_Vtbl
= {
1181 QTInPin_QueryInterface
,
1184 BaseInputPinImpl_Connect
,
1185 QTInPin_ReceiveConnection
,
1187 BasePinImpl_ConnectedTo
,
1188 BasePinImpl_ConnectionMediaType
,
1189 BasePinImpl_QueryPinInfo
,
1190 BasePinImpl_QueryDirection
,
1191 BasePinImpl_QueryId
,
1192 QTInPin_QueryAccept
,
1193 QTInPin_EnumMediaTypes
,
1194 BasePinImpl_QueryInternalConnections
,
1195 QTInPin_EndOfStream
,
1205 static HRESULT WINAPI
QTOutPin_QueryInterface(IPin
*iface
, REFIID riid
, void **ppv
)
1207 QTOutPin
*This
= (QTOutPin
*)iface
;
1209 TRACE("(%s, %p)\n", debugstr_guid(riid
), ppv
);
1213 if (IsEqualIID(riid
, &IID_IUnknown
))
1215 else if (IsEqualIID(riid
, &IID_IPin
))
1217 else if (IsEqualIID(riid
, &IID_IMediaSeeking
))
1218 return IBaseFilter_QueryInterface(This
->pin
.pin
.pinInfo
.pFilter
, &IID_IMediaSeeking
, ppv
);
1222 IUnknown_AddRef((IUnknown
*)(*ppv
));
1225 FIXME("No interface for %s!\n", debugstr_guid(riid
));
1226 return E_NOINTERFACE
;
1229 static ULONG WINAPI
QTOutPin_Release(IPin
*iface
)
1231 QTOutPin
*This
= (QTOutPin
*)iface
;
1232 ULONG refCount
= InterlockedDecrement(&This
->pin
.pin
.refCount
);
1233 TRACE("(%p)->() Release from %d\n", iface
, refCount
+ 1);
1237 DeleteMediaType(This
->pmt
);
1238 FreeMediaType(&This
->pin
.pin
.mtCurrent
);
1239 OutputQueue_Destroy(This
->queue
);
1240 CoTaskMemFree(This
);
1246 static HRESULT WINAPI
QTOutPin_GetMediaType(BasePin
*iface
, int iPosition
, AM_MEDIA_TYPE
*pmt
)
1248 QTOutPin
*This
= (QTOutPin
*)iface
;
1251 return E_INVALIDARG
;
1253 return VFW_S_NO_MORE_ITEMS
;
1254 CopyMediaType(pmt
, This
->pmt
);
1258 static HRESULT WINAPI
QTOutPin_DecideBufferSize(BaseOutputPin
*iface
, IMemAllocator
*pAlloc
, ALLOCATOR_PROPERTIES
*ppropInputRequest
)
1264 static HRESULT WINAPI
QTOutPin_DecideAllocator(BaseOutputPin
*iface
, IMemInputPin
*pPin
, IMemAllocator
**pAlloc
)
1267 QTOutPin
*This
= (QTOutPin
*)iface
;
1268 QTSplitter
*QTfilter
= (QTSplitter
*)This
->pin
.pin
.pinInfo
.pFilter
;
1271 if (QTfilter
->pInputPin
.pAlloc
)
1272 hr
= IMemInputPin_NotifyAllocator(pPin
, QTfilter
->pInputPin
.pAlloc
, FALSE
);
1274 hr
= VFW_E_NO_ALLOCATOR
;
1279 static HRESULT WINAPI
QTOutPin_BreakConnect(BaseOutputPin
*This
)
1283 TRACE("(%p)->()\n", This
);
1285 EnterCriticalSection(This
->pin
.pCritSec
);
1286 if (!This
->pin
.pConnectedTo
|| !This
->pMemInputPin
)
1287 hr
= VFW_E_NOT_CONNECTED
;
1290 hr
= IPin_Disconnect(This
->pin
.pConnectedTo
);
1291 IPin_Disconnect((IPin
*)This
);
1293 LeaveCriticalSection(This
->pin
.pCritSec
);
1298 static const IPinVtbl QT_OutputPin_Vtbl
= {
1299 QTOutPin_QueryInterface
,
1302 BaseOutputPinImpl_Connect
,
1303 BaseOutputPinImpl_ReceiveConnection
,
1304 BaseOutputPinImpl_Disconnect
,
1305 BasePinImpl_ConnectedTo
,
1306 BasePinImpl_ConnectionMediaType
,
1307 BasePinImpl_QueryPinInfo
,
1308 BasePinImpl_QueryDirection
,
1309 BasePinImpl_QueryId
,
1310 BasePinImpl_QueryAccept
,
1311 BasePinImpl_EnumMediaTypes
,
1312 BasePinImpl_QueryInternalConnections
,
1313 BaseOutputPinImpl_EndOfStream
,
1314 BaseOutputPinImpl_BeginFlush
,
1315 BaseOutputPinImpl_EndFlush
,
1316 BasePinImpl_NewSegment
1319 static const BasePinFuncTable output_BaseFuncTable
= {
1321 BaseOutputPinImpl_AttemptConnection
,
1322 BasePinImpl_GetMediaTypeVersion
,
1323 QTOutPin_GetMediaType
1326 static const BaseOutputPinFuncTable output_BaseOutputFuncTable
= {
1327 QTOutPin_DecideBufferSize
,
1328 QTOutPin_DecideAllocator
,
1329 QTOutPin_BreakConnect
1332 static const OutputQueueFuncTable output_OutputQueueFuncTable
= {
1333 OutputQueueImpl_ThreadProc
1336 static HRESULT
QT_AddPin(QTSplitter
*This
, const PIN_INFO
*piOutput
, const AM_MEDIA_TYPE
*amt
, BOOL video
)
1342 target
= (IPin
**)&This
->pVideo_Pin
;
1344 target
= (IPin
**)&This
->pAudio_Pin
;
1346 if (*target
!= NULL
)
1348 FIXME("We already have a %s pin\n",(video
)?"video":"audio");
1352 hr
= BaseOutputPin_Construct(&QT_OutputPin_Vtbl
, sizeof(QTOutPin
), piOutput
, &output_BaseFuncTable
, &output_BaseOutputFuncTable
, &This
->filter
.csFilter
, (IPin
**)target
);
1355 QTOutPin
*pin
= (QTOutPin
*)*target
;
1356 pin
->pmt
= CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE
));
1357 CopyMediaType(pin
->pmt
, amt
);
1358 pin
->pin
.pin
.pinInfo
.pFilter
= (LPVOID
)This
;
1360 BaseFilterImpl_IncrementPinVersion((BaseFilter
*)This
);
1362 hr
= OutputQueue_Construct((BaseOutputPin
*)pin
, TRUE
, TRUE
, 5, FALSE
, THREAD_PRIORITY_NORMAL
, &output_OutputQueueFuncTable
, &pin
->queue
);
1365 ERR("Failed with error %x\n", hr
);
1369 static HRESULT WINAPI
QTSplitter_ChangeStart(IMediaSeeking
*iface
)
1371 QTSplitter
*This
= impl_from_IMediaSeeking(iface
);
1372 TRACE("(%p)\n", iface
);
1373 EnterCriticalSection(&This
->csReceive
);
1374 This
->movie_time
= (This
->sourceSeeking
.llCurrent
* This
->movie_scale
)/10000000;
1375 This
->movie_start
= This
->movie_time
;
1376 LeaveCriticalSection(&This
->csReceive
);
1380 static HRESULT WINAPI
QTSplitter_ChangeStop(IMediaSeeking
*iface
)
1382 FIXME("(%p) filter hasn't implemented stop position change!\n", iface
);
1386 static HRESULT WINAPI
QTSplitter_ChangeRate(IMediaSeeking
*iface
)
1388 FIXME("(%p) filter hasn't implemented rate change!\n", iface
);
1392 static HRESULT WINAPI
QT_Seeking_QueryInterface(IMediaSeeking
* iface
, REFIID riid
, LPVOID
* ppv
)
1394 QTSplitter
*This
= impl_from_IMediaSeeking(iface
);
1396 return IUnknown_QueryInterface((IUnknown
*)This
, riid
, ppv
);
1399 static ULONG WINAPI
QT_Seeking_AddRef(IMediaSeeking
* iface
)
1401 QTSplitter
*This
= impl_from_IMediaSeeking(iface
);
1403 return IUnknown_AddRef((IUnknown
*)This
);
1406 static ULONG WINAPI
QT_Seeking_Release(IMediaSeeking
* iface
)
1408 QTSplitter
*This
= impl_from_IMediaSeeking(iface
);
1410 return IUnknown_Release((IUnknown
*)This
);
1413 static const IMediaSeekingVtbl QT_Seeking_Vtbl
=
1415 QT_Seeking_QueryInterface
,
1418 SourceSeekingImpl_GetCapabilities
,
1419 SourceSeekingImpl_CheckCapabilities
,
1420 SourceSeekingImpl_IsFormatSupported
,
1421 SourceSeekingImpl_QueryPreferredFormat
,
1422 SourceSeekingImpl_GetTimeFormat
,
1423 SourceSeekingImpl_IsUsingTimeFormat
,
1424 SourceSeekingImpl_SetTimeFormat
,
1425 SourceSeekingImpl_GetDuration
,
1426 SourceSeekingImpl_GetStopPosition
,
1427 SourceSeekingImpl_GetCurrentPosition
,
1428 SourceSeekingImpl_ConvertTimeFormat
,
1429 SourceSeekingImpl_SetPositions
,
1430 SourceSeekingImpl_GetPositions
,
1431 SourceSeekingImpl_GetAvailable
,
1432 SourceSeekingImpl_SetRate
,
1433 SourceSeekingImpl_GetRate
,
1434 SourceSeekingImpl_GetPreroll