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};
253 EnterMoviesOnThread(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
, ARRAY_SIZE(piInput
->achName
));
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
);
327 ExitMoviesOnThread();
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
);
346 BaseFilter_Destroy(&This
->filter
);
351 static HRESULT WINAPI
QT_QueryInterface(IBaseFilter
*iface
, REFIID riid
, LPVOID
*ppv
)
353 QTSplitter
*This
= impl_from_IBaseFilter(iface
);
354 TRACE("(%s, %p)\n", debugstr_guid(riid
), ppv
);
358 if (IsEqualIID(riid
, &IID_IUnknown
))
360 else if (IsEqualIID(riid
, &IID_IPersist
))
362 else if (IsEqualIID(riid
, &IID_IMediaFilter
))
364 else if (IsEqualIID(riid
, &IID_IBaseFilter
))
366 else if (IsEqualIID(riid
, &IID_IMediaSeeking
))
367 *ppv
= &This
->sourceSeeking
;
371 IUnknown_AddRef((IUnknown
*)(*ppv
));
375 if (!IsEqualIID(riid
, &IID_IPin
) && !IsEqualIID(riid
, &IID_IVideoWindow
) &&
376 !IsEqualIID(riid
, &IID_IAMFilterMiscFlags
))
377 FIXME("No interface for %s!\n", debugstr_guid(riid
));
379 return E_NOINTERFACE
;
382 static ULONG WINAPI
QT_Release(IBaseFilter
*iface
)
384 QTSplitter
*This
= impl_from_IBaseFilter(iface
);
385 ULONG refCount
= InterlockedDecrement(&This
->filter
.refCount
);
387 TRACE("(%p)->() Release from %d\n", This
, refCount
+ 1);
395 static HRESULT WINAPI
QT_Stop(IBaseFilter
*iface
)
397 QTSplitter
*This
= impl_from_IBaseFilter(iface
);
401 EnterCriticalSection(&This
->csReceive
);
402 IAsyncReader_BeginFlush(This
->pInputPin
.pReader
);
403 IAsyncReader_EndFlush(This
->pInputPin
.pReader
);
404 LeaveCriticalSection(&This
->csReceive
);
409 static HRESULT WINAPI
QT_Pause(IBaseFilter
*iface
)
417 static OSErr
QT_Create_Extract_Session(QTSplitter
*filter
)
419 AudioStreamBasicDescription aDesc
;
423 pvi
= (WAVEFORMATEX
*)filter
->pAudio_Pin
->pmt
->pbFormat
;
425 err
= MovieAudioExtractionBegin(filter
->pQTMovie
, 0, &filter
->aSession
);
428 ERR("Failed to begin Extraction session %i\n",err
);
432 err
= MovieAudioExtractionGetProperty(filter
->aSession
,
433 kQTPropertyClass_MovieAudioExtraction_Audio
,
434 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription
,
435 sizeof(AudioStreamBasicDescription
), &aDesc
, NULL
);
439 MovieAudioExtractionEnd(filter
->aSession
);
440 filter
->aSession
= NULL
;
441 ERR("Failed to get session description %i\n",err
);
445 aDesc
.mFormatID
= kAudioFormatLinearPCM
;
446 aDesc
.mFormatFlags
= kLinearPCMFormatFlagIsSignedInteger
+
447 kAudioFormatFlagIsPacked
;
448 aDesc
.mFramesPerPacket
= 1;
449 aDesc
.mChannelsPerFrame
= pvi
->nChannels
;
450 aDesc
.mBitsPerChannel
= pvi
->wBitsPerSample
;
451 aDesc
.mSampleRate
= pvi
->nSamplesPerSec
;
452 aDesc
.mBytesPerFrame
= (aDesc
.mBitsPerChannel
* aDesc
.mChannelsPerFrame
) / 8;
453 aDesc
.mBytesPerPacket
= aDesc
.mBytesPerFrame
* aDesc
.mFramesPerPacket
;
455 err
= MovieAudioExtractionSetProperty(filter
->aSession
,
456 kQTPropertyClass_MovieAudioExtraction_Audio
,
457 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription
,
458 sizeof(AudioStreamBasicDescription
), &aDesc
);
460 if (aDesc
.mFormatID
!= kAudioFormatLinearPCM
)
462 ERR("Not PCM Wave\n");
465 if (aDesc
.mFormatFlags
!= kLinearPCMFormatFlagIsSignedInteger
+
466 kAudioFormatFlagIsPacked
)
468 ERR("Unhandled Flags\n");
471 if (aDesc
.mFramesPerPacket
!= 1)
473 ERR("Unhandled Frames per packet %li\n",aDesc
.mFramesPerPacket
);
476 if (aDesc
.mChannelsPerFrame
!= pvi
->nChannels
)
478 ERR("Unhandled channel count %li\n",aDesc
.mChannelsPerFrame
);
481 if (aDesc
.mBitsPerChannel
!= pvi
->wBitsPerSample
)
483 ERR("Unhandled bits per channel %li\n",aDesc
.mBitsPerChannel
);
486 if (aDesc
.mSampleRate
!= pvi
->nSamplesPerSec
)
488 ERR("Unhandled sample rate %f\n",aDesc
.mSampleRate
);
494 ERR("Failed to create Extraction Session\n");
495 MovieAudioExtractionEnd(filter
->aSession
);
496 filter
->aSession
= NULL
;
502 static DWORD WINAPI
QTSplitter_loading_thread(LPVOID data
)
504 QTSplitter
*This
= (QTSplitter
*)data
;
506 if (This
->pAudio_Pin
)
508 /* according to QA1469 a movie has to be fully loaded before we
509 can reliably start the Extraction session.
511 If loaded earlier, then we only get an extraction session for
512 the part of the movie that is loaded at that time.
514 We are trying to load as much of the movie as we can before we
515 start extracting. However we can recreate the extraction session
516 again when we run out of loaded extraction frames. But we want
517 to try to minimize that.
520 EnterCriticalSection(&This
->csReceive
);
521 while(This
->pQTMovie
&& GetMovieLoadState(This
->pQTMovie
) < kMovieLoadStateComplete
)
523 MoviesTask(This
->pQTMovie
, 100);
524 LeaveCriticalSection(&This
->csReceive
);
526 EnterCriticalSection(&This
->csReceive
);
528 LeaveCriticalSection(&This
->csReceive
);
533 static DWORD WINAPI
QTSplitter_thread(LPVOID data
)
535 QTSplitter
*This
= (QTSplitter
*)data
;
538 CVPixelBufferRef pixelBuffer
= NULL
;
542 WaitForSingleObject(This
->runEvent
, -1);
544 EnterCriticalSection(&This
->csReceive
);
547 LeaveCriticalSection(&This
->csReceive
);
551 This
->state
= State_Running
;
552 /* Prime the pump: Needed for MPEG streams */
553 GetMovieNextInterestingTime(This
->pQTMovie
, nextTimeEdgeOK
| nextTimeStep
, 0, NULL
, This
->movie_time
, 1, &next_time
, NULL
);
555 GetMovieTime(This
->pQTMovie
, &tr
);
557 if (This
->pAudio_Pin
)
558 QT_Create_Extract_Session(This
);
560 LeaveCriticalSection(&This
->csReceive
);
564 LONGLONG tStart
=0, tStop
=0;
565 LONGLONG mStart
=0, mStop
=0;
568 EnterCriticalSection(&This
->csReceive
);
571 LeaveCriticalSection(&This
->csReceive
);
575 GetMovieNextInterestingTime(This
->pQTMovie
, nextTimeStep
, 0, NULL
, This
->movie_time
, 1, &next_time
, NULL
);
579 TRACE("No next time\n");
580 LeaveCriticalSection(&This
->csReceive
);
584 tr
.value
= SInt64ToWide(next_time
);
585 SetMovieTime(This
->pQTMovie
, &tr
);
586 MoviesTask(This
->pQTMovie
,0);
587 QTVisualContextTask(This
->vContext
);
589 TRACE("In loop at time %ld\n",This
->movie_time
);
590 TRACE("In Next time %ld\n",next_time
);
592 mStart
= This
->movie_time
;
595 time
= (float)(This
->movie_time
- This
->movie_start
) / This
->movie_scale
;
596 tStart
= time
* 10000000;
597 time
= (float)(next_time
- This
->movie_start
) / This
->movie_scale
;
598 tStop
= time
* 10000000;
601 if (This
->pAudio_Pin
&& This
->pAudio_Pin
->pin
.pin
.pConnectedTo
&& This
->aSession
)
605 IMediaSample
*sample
= NULL
;
606 AudioBufferList aData
;
612 pvi
= (WAVEFORMATEX
*)This
->pAudio_Pin
->pmt
->pbFormat
;
614 hr
= BaseOutputPinImpl_GetDeliveryBuffer(&This
->pAudio_Pin
->pin
, &sample
, NULL
, NULL
, 0);
618 ERR("Audio: Unable to get delivery buffer (%x)\n", hr
);
622 hr
= IMediaSample_GetPointer(sample
, &ptr
);
625 ERR("Audio: Unable to get pointer to buffer (%x)\n", hr
);
629 duration
= (float)next_time
/ This
->movie_scale
;
630 time
= (float)This
->movie_time
/ This
->movie_scale
;
632 frames
= pvi
->nSamplesPerSec
* duration
;
633 TRACE("Need audio for %f seconds (%li frames)\n",duration
,frames
);
635 data_size
= IMediaSample_GetSize(sample
);
636 if (data_size
< frames
* pvi
->nBlockAlign
)
637 FIXME("Audio buffer is too small\n");
639 aData
.mNumberBuffers
= 1;
640 aData
.mBuffers
[0].mNumberChannels
= pvi
->nChannels
;
641 aData
.mBuffers
[0].mDataByteSize
= data_size
;
642 aData
.mBuffers
[0].mData
= ptr
;
644 err
= MovieAudioExtractionFillBuffer(This
->aSession
, &frames
, &aData
, &flags
);
649 /* Ran out of frames, Restart the extraction session */
650 TRACE("Restarting extraction session\n");
651 MovieAudioExtractionEnd(This
->aSession
);
652 This
->aSession
= NULL
;
653 QT_Create_Extract_Session(This
);
656 etr
.value
= SInt64ToWide(This
->movie_time
);
657 MovieAudioExtractionSetProperty(This
->aSession
,
658 kQTPropertyClass_MovieAudioExtraction_Movie
,
659 kQTMovieAudioExtractionMoviePropertyID_CurrentTime
,
660 sizeof(TimeRecord
), &etr
);
662 frames
= pvi
->nSamplesPerSec
* duration
;
663 aData
.mNumberBuffers
= 1;
664 aData
.mBuffers
[0].mNumberChannels
= pvi
->nChannels
;
665 aData
.mBuffers
[0].mDataByteSize
= data_size
;
666 aData
.mBuffers
[0].mData
= ptr
;
668 MovieAudioExtractionFillBuffer(This
->aSession
, &frames
, &aData
, &flags
);
671 TRACE("Got %i frames\n",(int)frames
);
673 IMediaSample_SetActualDataLength(sample
, frames
* pvi
->nBlockAlign
);
675 IMediaSample_SetMediaTime(sample
, &mStart
, &mStop
);
676 IMediaSample_SetTime(sample
, &tStart
, &tStop
);
678 hr
= OutputQueue_Receive(This
->pAudio_Pin
->queue
, sample
);
679 TRACE("Audio Delivered (%x)\n",hr
);
683 IMediaSample_Release(sample
);
686 TRACE("Audio Pin not connected or no Audio\n");
689 if (This
->pVideo_Pin
&& QTVisualContextIsNewImageAvailable(This
->vContext
,0))
691 err
= QTVisualContextCopyImageForTime(This
->vContext
, NULL
, NULL
, &pixelBuffer
);
696 IMediaSample
*sample
= NULL
;
698 hr
= BaseOutputPinImpl_GetDeliveryBuffer(&This
->pVideo_Pin
->pin
, &sample
, NULL
, NULL
, 0);
701 ERR("Video: Unable to get delivery buffer (%x)\n", hr
);
705 data_size
= IMediaSample_GetSize(sample
);
706 if (data_size
< This
->outputSize
)
708 ERR("Sample size is too small %d < %d\n", data_size
, This
->outputSize
)
714 hr
= IMediaSample_GetPointer(sample
, &ptr
);
717 ERR("Video: Unable to get pointer to buffer (%x)\n", hr
);
721 hr
= AccessPixelBufferPixels( pixelBuffer
, ptr
);
724 ERR("Failed to access Pixels\n");
728 IMediaSample_SetActualDataLength(sample
, This
->outputSize
);
730 IMediaSample_SetMediaTime(sample
, &mStart
, &mStop
);
731 IMediaSample_SetTime(sample
, &tStart
, &tStop
);
733 hr
= OutputQueue_Receive(This
->pVideo_Pin
->queue
, sample
);
734 TRACE("Video Delivered (%x)\n",hr
);
738 IMediaSample_Release(sample
);
740 CVPixelBufferRelease(pixelBuffer
);
744 TRACE("No video to deliver\n");
746 This
->movie_time
= next_time
;
747 LeaveCriticalSection(&This
->csReceive
);
748 } while (hr
== S_OK
);
750 This
->state
= State_Stopped
;
751 if (This
->pAudio_Pin
)
752 OutputQueue_EOS(This
->pAudio_Pin
->queue
);
753 if (This
->pVideo_Pin
)
754 OutputQueue_EOS(This
->pVideo_Pin
->queue
);
759 static HRESULT WINAPI
QT_Run(IBaseFilter
*iface
, REFERENCE_TIME tStart
)
762 QTSplitter
*This
= impl_from_IBaseFilter(iface
);
763 HRESULT hr_any
= VFW_E_NOT_CONNECTED
;
765 TRACE("(%s)\n", wine_dbgstr_longlong(tStart
));
767 EnterCriticalSection(&This
->csReceive
);
768 This
->filter
.rtStreamStart
= tStart
;
770 if (This
->pVideo_Pin
)
771 hr
= BaseOutputPinImpl_Active(&This
->pVideo_Pin
->pin
);
774 if (This
->pAudio_Pin
)
775 hr
= BaseOutputPinImpl_Active(&This
->pAudio_Pin
->pin
);
781 SetEvent(This
->runEvent
);
782 LeaveCriticalSection(&This
->csReceive
);
787 static HRESULT WINAPI
QT_GetState(IBaseFilter
*iface
, DWORD dwMilliSecsTimeout
, FILTER_STATE
*pState
)
789 QTSplitter
*This
= impl_from_IBaseFilter(iface
);
790 TRACE("(%d, %p)\n", dwMilliSecsTimeout
, pState
);
792 *pState
= This
->state
;
797 static const IBaseFilterVtbl QT_Vtbl
= {
799 BaseFilterImpl_AddRef
,
801 BaseFilterImpl_GetClassID
,
806 BaseFilterImpl_SetSyncSource
,
807 BaseFilterImpl_GetSyncSource
,
808 BaseFilterImpl_EnumPins
,
809 BaseFilterImpl_FindPin
,
810 BaseFilterImpl_QueryFilterInfo
,
811 BaseFilterImpl_JoinFilterGraph
,
812 BaseFilterImpl_QueryVendorInfo
818 static HRESULT
QT_RemoveOutputPins(QTSplitter
*This
)
821 TRACE("(%p)\n", This
);
823 if (This
->pVideo_Pin
)
825 OutputQueue_Destroy(This
->pVideo_Pin
->queue
);
826 hr
= BaseOutputPinImpl_BreakConnect(&This
->pVideo_Pin
->pin
);
827 TRACE("Disconnect: %08x\n", hr
);
828 IPin_Release(&This
->pVideo_Pin
->pin
.pin
.IPin_iface
);
829 This
->pVideo_Pin
= NULL
;
831 if (This
->pAudio_Pin
)
833 OutputQueue_Destroy(This
->pAudio_Pin
->queue
);
834 hr
= BaseOutputPinImpl_BreakConnect(&This
->pAudio_Pin
->pin
);
835 TRACE("Disconnect: %08x\n", hr
);
836 IPin_Release(&This
->pAudio_Pin
->pin
.pin
.IPin_iface
);
837 This
->pAudio_Pin
= NULL
;
840 BaseFilterImpl_IncrementPinVersion(&This
->filter
);
844 static inline QTInPin
*impl_from_IPin( IPin
*iface
)
846 return CONTAINING_RECORD(iface
, QTInPin
, pin
.IPin_iface
);
849 static ULONG WINAPI
QTInPin_Release(IPin
*iface
)
851 QTInPin
*This
= impl_from_IPin(iface
);
852 ULONG refCount
= InterlockedDecrement(&This
->pin
.refCount
);
854 TRACE("(%p)->() Release from %d\n", iface
, refCount
+ 1);
857 FreeMediaType(&This
->pin
.mtCurrent
);
859 IMemAllocator_Release(This
->pAlloc
);
862 IAsyncReader_Release(This
->pReader
);
863 This
->pReader
= NULL
;
864 This
->pin
.IPin_iface
.lpVtbl
= NULL
;
871 static HRESULT
QT_Process_Video_Track(QTSplitter
* filter
, Track trk
)
874 VIDEOINFOHEADER
* pvi
;
878 static const WCHAR szwVideoOut
[] = {'V','i','d','e','o',0};
879 CFMutableDictionaryRef pixelBufferOptions
= NULL
;
880 CFMutableDictionaryRef visualContextOptions
= NULL
;
881 CFNumberRef n
= NULL
;
883 DWORD outputWidth
, outputHeight
, outputDepth
;
884 Fixed trackWidth
, trackHeight
;
887 TimeValue64 duration
;
890 ZeroMemory(&amt
, sizeof(amt
));
891 amt
.formattype
= FORMAT_VideoInfo
;
892 amt
.majortype
= MEDIATYPE_Video
;
893 amt
.subtype
= MEDIASUBTYPE_RGB24
;
895 GetTrackDimensions(trk
, &trackWidth
, &trackHeight
);
898 outputWidth
= Fix2Long(trackWidth
);
899 outputHeight
= Fix2Long(trackHeight
);
900 TRACE("Width %i Height %i\n",outputWidth
, outputHeight
);
902 amt
.cbFormat
= sizeof(VIDEOINFOHEADER
);
903 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
904 ZeroMemory(amt
.pbFormat
, amt
.cbFormat
);
905 pvi
= (VIDEOINFOHEADER
*)amt
.pbFormat
;
906 pvi
->bmiHeader
.biSize
= sizeof (BITMAPINFOHEADER
);
907 pvi
->bmiHeader
.biWidth
= outputWidth
;
908 pvi
->bmiHeader
.biHeight
= outputHeight
;
909 pvi
->bmiHeader
.biPlanes
= 1;
910 pvi
->bmiHeader
.biBitCount
= 24;
911 pvi
->bmiHeader
.biCompression
= BI_RGB
;
912 pvi
->bmiHeader
.biSizeImage
= outputWidth
* outputHeight
* outputDepth
;
914 filter
->outputSize
= pvi
->bmiHeader
.biSizeImage
;
917 pixelBufferOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
919 t
= k32ARGBPixelFormat
;
920 n
= CFNumberCreate(NULL
, kCFNumberIntType
, &t
);
921 CFDictionaryAddValue(pixelBufferOptions
, kCVPixelBufferPixelFormatTypeKey
, n
);
924 n
= CFNumberCreate(NULL
, kCFNumberIntType
, &outputWidth
);
925 CFDictionaryAddValue(pixelBufferOptions
, kCVPixelBufferWidthKey
, n
);
928 n
= CFNumberCreate(NULL
, kCFNumberIntType
, &outputHeight
);
929 CFDictionaryAddValue(pixelBufferOptions
, kCVPixelBufferHeightKey
, n
);
933 n
= CFNumberCreate(NULL
, kCFNumberIntType
, &t
);
934 CFDictionaryAddValue(pixelBufferOptions
, kCVPixelBufferBytesPerRowAlignmentKey
, n
);
937 visualContextOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
939 CFDictionarySetValue(visualContextOptions
, kQTVisualContextPixelBufferAttributesKey
, pixelBufferOptions
);
941 err
= QTPixelBufferContextCreate(NULL
, visualContextOptions
,&filter
->vContext
);
942 CFRelease(pixelBufferOptions
);
943 CFRelease(visualContextOptions
);
946 ERR("Failed to create Visual Context\n");
950 err
= SetMovieVisualContext(filter
->pQTMovie
, filter
->vContext
);
953 ERR("Failed to set Visual Context\n");
957 videoMedia
= GetTrackMedia(trk
);
958 sampleCount
= GetMediaSampleCount(videoMedia
);
959 timeScale
= GetMediaTimeScale(videoMedia
);
960 duration
= GetMediaDisplayDuration(videoMedia
);
961 pvi
->AvgTimePerFrame
= (100000.0 * sampleCount
* timeScale
) / duration
;
963 piOutput
.dir
= PINDIR_OUTPUT
;
964 piOutput
.pFilter
= &filter
->filter
.IBaseFilter_iface
;
965 lstrcpyW(piOutput
.achName
,szwVideoOut
);
967 hr
= QT_AddPin(filter
, &piOutput
, &amt
, TRUE
);
969 ERR("Failed to add Video Track\n");
971 TRACE("Video Pin %p\n",filter
->pVideo_Pin
);
976 static HRESULT
QT_Process_Audio_Track(QTSplitter
* filter
, Track trk
)
982 static const WCHAR szwAudioOut
[] = {'A','u','d','i','o',0};
985 SoundDescriptionHandle aDesc
= (SoundDescriptionHandle
) NewHandle(sizeof(SoundDescription
));
987 audioMedia
= GetTrackMedia(trk
);
988 GetMediaSampleDescription(audioMedia
, 1, (SampleDescriptionHandle
)aDesc
);
990 ZeroMemory(&amt
, sizeof(amt
));
991 amt
.formattype
= FORMAT_WaveFormatEx
;
992 amt
.majortype
= MEDIATYPE_Audio
;
993 amt
.subtype
= MEDIASUBTYPE_PCM
;
994 amt
.bTemporalCompression
= 0;
996 amt
.cbFormat
= sizeof(WAVEFORMATEX
);
997 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
998 ZeroMemory(amt
.pbFormat
, amt
.cbFormat
);
999 pvi
= (WAVEFORMATEX
*)amt
.pbFormat
;
1001 pvi
->cbSize
= sizeof(WAVEFORMATEX
);
1002 pvi
->wFormatTag
= WAVE_FORMAT_PCM
;
1003 pvi
->nChannels
= ((SoundDescription
)**aDesc
).numChannels
;
1004 if (pvi
->nChannels
< 1 || pvi
->nChannels
> 2)
1006 pvi
->nSamplesPerSec
= (((SoundDescription
)**aDesc
).sampleRate
/65536);
1007 if (pvi
->nSamplesPerSec
< 8000 || pvi
->nChannels
> 48000)
1008 pvi
->nSamplesPerSec
= 44100;
1009 pvi
->wBitsPerSample
= ((SoundDescription
)**aDesc
).sampleSize
;
1010 if (pvi
->wBitsPerSample
< 8 || pvi
->wBitsPerSample
> 32)
1011 pvi
->wBitsPerSample
= 16;
1012 pvi
->nBlockAlign
= (pvi
->nChannels
* pvi
->wBitsPerSample
) / 8;
1013 pvi
->nAvgBytesPerSec
= pvi
->nSamplesPerSec
* pvi
->nBlockAlign
;
1015 DisposeHandle((Handle
)aDesc
);
1017 piOutput
.dir
= PINDIR_OUTPUT
;
1018 piOutput
.pFilter
= &filter
->filter
.IBaseFilter_iface
;
1019 lstrcpyW(piOutput
.achName
,szwAudioOut
);
1021 hr
= QT_AddPin(filter
, &piOutput
, &amt
, FALSE
);
1023 ERR("Failed to add Audio Track\n");
1025 TRACE("Audio Pin %p\n",filter
->pAudio_Pin
);
1029 static HRESULT
QT_Process_Movie(QTSplitter
* filter
)
1033 WineDataRefRecord ptrDataRefRec
;
1034 Handle dataRef
= NULL
;
1040 TRACE("Trying movie connect\n");
1042 ptrDataRefRec
.pReader
= filter
->pInputPin
.pReader
;
1043 ptrDataRefRec
.streamSubtype
= filter
->pInputPin
.subType
;
1044 PtrToHand( &ptrDataRefRec
, &dataRef
, sizeof(WineDataRefRecord
));
1046 err
= NewMovieFromDataRef(&filter
->pQTMovie
, newMovieActive
|newMovieDontInteractWithUser
|newMovieDontAutoUpdateClock
|newMovieDontAskUnresolvedDataRefs
|newMovieAsyncOK
, &id
, dataRef
, 'WINE');
1048 DisposeHandle(dataRef
);
1052 FIXME("QuickTime cannot handle media type(%i)\n",err
);
1053 return VFW_E_TYPE_NOT_ACCEPTED
;
1056 PrePrerollMovie(filter
->pQTMovie
, 0, fixed1
, NULL
, NULL
);
1057 PrerollMovie(filter
->pQTMovie
, 0, fixed1
);
1058 GoToBeginningOfMovie(filter
->pQTMovie
);
1059 SetMovieActive(filter
->pQTMovie
,TRUE
);
1061 if (GetMovieLoadState(filter
->pQTMovie
) < kMovieLoadStateLoaded
)
1062 MoviesTask(filter
->pQTMovie
,100);
1064 trk
= GetMovieIndTrackType(filter
->pQTMovie
, 1, VisualMediaCharacteristic
, movieTrackCharacteristic
| movieTrackEnabledOnly
);
1065 TRACE("%p is a video track\n",trk
);
1067 hr
= QT_Process_Video_Track(filter
, trk
);
1072 trk
= GetMovieIndTrackType(filter
->pQTMovie
, 1, AudioMediaCharacteristic
, movieTrackCharacteristic
| movieTrackEnabledOnly
);
1073 TRACE("%p is an audio track\n",trk
);
1075 hr
= QT_Process_Audio_Track(filter
, trk
);
1077 time
= GetMovieDuration(filter
->pQTMovie
);
1078 filter
->movie_scale
= GetMovieTimeScale(filter
->pQTMovie
);
1079 filter
->sourceSeeking
.llDuration
= ((double)time
/ filter
->movie_scale
) * 10000000;
1080 filter
->sourceSeeking
.llStop
= filter
->sourceSeeking
.llDuration
;
1082 TRACE("Movie duration is %s\n",wine_dbgstr_longlong(filter
->sourceSeeking
.llDuration
));
1084 filter
->loaderThread
= CreateThread(NULL
, 0, QTSplitter_loading_thread
, filter
, 0, &tid
);
1085 if (filter
->loaderThread
)
1086 TRACE("Created loading thread 0x%08x\n", tid
);
1087 filter
->splitterThread
= CreateThread(NULL
, 0, QTSplitter_thread
, filter
, 0, &tid
);
1088 if (filter
->splitterThread
)
1089 TRACE("Created processing thread 0x%08x\n", tid
);
1091 hr
= HRESULT_FROM_WIN32(GetLastError());
1096 static HRESULT WINAPI
QTInPin_ReceiveConnection(IPin
*iface
, IPin
*pReceivePin
, const AM_MEDIA_TYPE
*pmt
)
1099 ALLOCATOR_PROPERTIES props
;
1100 QTInPin
*This
= impl_from_IPin(iface
);
1101 IMemAllocator
*pAlloc
;
1103 TRACE("(%p/%p)->(%p, %p)\n", This
, iface
, pReceivePin
, pmt
);
1105 EnterCriticalSection(This
->pin
.pCritSec
);
1106 This
->pReader
= NULL
;
1108 if (This
->pin
.pConnectedTo
)
1109 hr
= VFW_E_ALREADY_CONNECTED
;
1110 else if (IPin_QueryAccept(iface
, pmt
) != S_OK
)
1111 hr
= VFW_E_TYPE_NOT_ACCEPTED
;
1114 PIN_DIRECTION pindirReceive
;
1115 IPin_QueryDirection(pReceivePin
, &pindirReceive
);
1116 if (pindirReceive
!= PINDIR_OUTPUT
)
1117 hr
= VFW_E_INVALID_DIRECTION
;
1122 LeaveCriticalSection(This
->pin
.pCritSec
);
1126 hr
= IPin_QueryInterface(pReceivePin
, &IID_IAsyncReader
, (LPVOID
*)&This
->pReader
);
1129 LeaveCriticalSection(This
->pin
.pCritSec
);
1130 TRACE("Input source is not an AsyncReader\n");
1134 LeaveCriticalSection(This
->pin
.pCritSec
);
1135 EnterCriticalSection(&impl_from_IBaseFilter(This
->pin
.pinInfo
.pFilter
)->filter
.csFilter
);
1136 hr
= QT_Process_Movie(impl_from_IBaseFilter(This
->pin
.pinInfo
.pFilter
));
1139 IAsyncReader_Release(This
->pReader
);
1140 This
->pReader
= NULL
;
1141 LeaveCriticalSection(&impl_from_IBaseFilter(This
->pin
.pinInfo
.pFilter
)->filter
.csFilter
);
1142 TRACE("Unable to process movie\n");
1146 This
->pAlloc
= NULL
;
1149 props
.cbBuffer
= impl_from_IBaseFilter(This
->pin
.pinInfo
.pFilter
)->outputSize
+ props
.cbAlign
;
1151 hr
= CoCreateInstance(&CLSID_MemoryAllocator
, NULL
, CLSCTX_INPROC
,
1152 &IID_IMemAllocator
, (LPVOID
*)&pAlloc
);
1155 /* A certain IAsyncReader::RequestAllocator expects to be passed
1156 non-NULL preferred allocator */
1157 hr
= IAsyncReader_RequestAllocator(This
->pReader
, pAlloc
, &props
, &This
->pAlloc
);
1159 WARN("Can't get an allocator, got %08x\n", hr
);
1160 IMemAllocator_Release(pAlloc
);
1165 CopyMediaType(&This
->pin
.mtCurrent
, pmt
);
1166 This
->pin
.pConnectedTo
= pReceivePin
;
1167 IPin_AddRef(pReceivePin
);
1168 hr
= IMemAllocator_Commit(This
->pAlloc
);
1172 QT_RemoveOutputPins(impl_from_IBaseFilter(This
->pin
.pinInfo
.pFilter
));
1174 IAsyncReader_Release(This
->pReader
);
1175 This
->pReader
= NULL
;
1177 IMemAllocator_Release(This
->pAlloc
);
1178 This
->pAlloc
= NULL
;
1180 TRACE("Size: %i\n", props
.cbBuffer
);
1181 LeaveCriticalSection(&impl_from_IBaseFilter(This
->pin
.pinInfo
.pFilter
)->filter
.csFilter
);
1186 static HRESULT WINAPI
QTInPin_Disconnect(IPin
*iface
)
1189 QTInPin
*This
= impl_from_IPin(iface
);
1193 hr
= IBaseFilter_GetState(This
->pin
.pinInfo
.pFilter
, INFINITE
, &state
);
1194 EnterCriticalSection(This
->pin
.pCritSec
);
1195 if (This
->pin
.pConnectedTo
)
1197 QTSplitter
*Parser
= impl_from_IBaseFilter(This
->pin
.pinInfo
.pFilter
);
1199 if (SUCCEEDED(hr
) && state
== State_Stopped
)
1201 IMemAllocator_Decommit(This
->pAlloc
);
1202 IPin_Disconnect(This
->pin
.pConnectedTo
);
1203 IPin_Release(This
->pin
.pConnectedTo
);
1204 This
->pin
.pConnectedTo
= NULL
;
1205 hr
= QT_RemoveOutputPins(Parser
);
1208 hr
= VFW_E_NOT_STOPPED
;
1212 LeaveCriticalSection(This
->pin
.pCritSec
);
1216 static HRESULT WINAPI
QTInPin_QueryAccept(IPin
*iface
, const AM_MEDIA_TYPE
*pmt
)
1218 QTInPin
*This
= impl_from_IPin(iface
);
1220 TRACE("(%p)->(%p)\n", This
, pmt
);
1222 if (IsEqualIID(&pmt
->majortype
, &MEDIATYPE_Stream
))
1224 This
->subType
= pmt
->subtype
;
1230 static HRESULT WINAPI
QTInPin_EndOfStream(IPin
*iface
)
1232 QTInPin
*pin
= impl_from_IPin(iface
);
1233 QTSplitter
*This
= impl_from_IBaseFilter(pin
->pin
.pinInfo
.pFilter
);
1235 FIXME("Propagate message on %p\n", This
);
1239 static HRESULT WINAPI
QTInPin_BeginFlush(IPin
*iface
)
1241 QTInPin
*pin
= impl_from_IPin(iface
);
1242 QTSplitter
*This
= impl_from_IBaseFilter(pin
->pin
.pinInfo
.pFilter
);
1244 FIXME("Propagate message on %p\n", This
);
1248 static HRESULT WINAPI
QTInPin_EndFlush(IPin
*iface
)
1250 QTInPin
*pin
= impl_from_IPin(iface
);
1251 QTSplitter
*This
= impl_from_IBaseFilter(pin
->pin
.pinInfo
.pFilter
);
1253 FIXME("Propagate message on %p\n", This
);
1257 static HRESULT WINAPI
QTInPin_NewSegment(IPin
*iface
, REFERENCE_TIME tStart
, REFERENCE_TIME tStop
, double dRate
)
1259 QTInPin
*pin
= impl_from_IPin(iface
);
1260 QTSplitter
*This
= impl_from_IBaseFilter(pin
->pin
.pinInfo
.pFilter
);
1262 BasePinImpl_NewSegment(iface
, tStart
, tStop
, dRate
);
1263 FIXME("Propagate message on %p\n", This
);
1267 static HRESULT WINAPI
QTInPin_QueryInterface(IPin
* iface
, REFIID riid
, LPVOID
* ppv
)
1269 QTInPin
*This
= impl_from_IPin(iface
);
1271 TRACE("(%p/%p)->(%s, %p)\n", This
, iface
, debugstr_guid(riid
), ppv
);
1275 if (IsEqualIID(riid
, &IID_IUnknown
))
1277 else if (IsEqualIID(riid
, &IID_IPin
))
1279 else if (IsEqualIID(riid
, &IID_IMediaSeeking
))
1280 return IBaseFilter_QueryInterface(This
->pin
.pinInfo
.pFilter
, &IID_IMediaSeeking
, ppv
);
1284 IUnknown_AddRef((IUnknown
*)(*ppv
));
1288 FIXME("No interface for %s!\n", debugstr_guid(riid
));
1290 return E_NOINTERFACE
;
1293 static HRESULT WINAPI
QTInPin_EnumMediaTypes(IPin
*iface
, IEnumMediaTypes
**ppEnum
)
1295 QTInPin
*This
= impl_from_IPin(iface
);
1297 TRACE("(%p/%p)->(%p)\n", This
, iface
, ppEnum
);
1299 return EnumMediaTypes_Construct(&This
->pin
, BasePinImpl_GetMediaType
, BasePinImpl_GetMediaTypeVersion
, ppEnum
);
1302 static const IPinVtbl QT_InputPin_Vtbl
= {
1303 QTInPin_QueryInterface
,
1306 BaseInputPinImpl_Connect
,
1307 QTInPin_ReceiveConnection
,
1309 BasePinImpl_ConnectedTo
,
1310 BasePinImpl_ConnectionMediaType
,
1311 BasePinImpl_QueryPinInfo
,
1312 BasePinImpl_QueryDirection
,
1313 BasePinImpl_QueryId
,
1314 QTInPin_QueryAccept
,
1315 QTInPin_EnumMediaTypes
,
1316 BasePinImpl_QueryInternalConnections
,
1317 QTInPin_EndOfStream
,
1326 static inline QTOutPin
*impl_QTOutPin_from_IPin( IPin
*iface
)
1328 return CONTAINING_RECORD(iface
, QTOutPin
, pin
.pin
.IPin_iface
);
1331 static inline QTOutPin
*impl_QTOutPin_from_BasePin( BasePin
*iface
)
1333 return CONTAINING_RECORD(iface
, QTOutPin
, pin
.pin
);
1336 static inline QTOutPin
*impl_QTOutPin_from_BaseOutputPin( BaseOutputPin
*iface
)
1338 return CONTAINING_RECORD(iface
, QTOutPin
, pin
);
1341 static HRESULT WINAPI
QTOutPin_QueryInterface(IPin
*iface
, REFIID riid
, void **ppv
)
1343 QTOutPin
*This
= impl_QTOutPin_from_IPin(iface
);
1345 TRACE("(%s, %p)\n", debugstr_guid(riid
), ppv
);
1349 if (IsEqualIID(riid
, &IID_IUnknown
))
1351 else if (IsEqualIID(riid
, &IID_IPin
))
1353 else if (IsEqualIID(riid
, &IID_IMediaSeeking
))
1354 return IBaseFilter_QueryInterface(This
->pin
.pin
.pinInfo
.pFilter
, &IID_IMediaSeeking
, ppv
);
1355 else if (IsEqualIID(riid
, &IID_IQualityControl
))
1356 *ppv
= &This
->IQualityControl_iface
;
1360 IUnknown_AddRef((IUnknown
*)(*ppv
));
1363 FIXME("No interface for %s!\n", debugstr_guid(riid
));
1364 return E_NOINTERFACE
;
1367 static ULONG WINAPI
QTOutPin_Release(IPin
*iface
)
1369 QTOutPin
*This
= impl_QTOutPin_from_IPin(iface
);
1370 ULONG refCount
= InterlockedDecrement(&This
->pin
.pin
.refCount
);
1371 TRACE("(%p)->() Release from %d\n", iface
, refCount
+ 1);
1375 DeleteMediaType(This
->pmt
);
1376 FreeMediaType(&This
->pin
.pin
.mtCurrent
);
1377 if (This
->pin
.pAllocator
)
1378 IMemAllocator_Release(This
->pin
.pAllocator
);
1379 CoTaskMemFree(This
);
1385 static HRESULT WINAPI
QTOutPin_CheckMediaType(BasePin
*base
, const AM_MEDIA_TYPE
*amt
)
1387 FIXME("(%p) stub\n", base
);
1391 static HRESULT WINAPI
QTOutPin_GetMediaType(BasePin
*iface
, int iPosition
, AM_MEDIA_TYPE
*pmt
)
1393 QTOutPin
*This
= impl_QTOutPin_from_BasePin(iface
);
1396 return E_INVALIDARG
;
1398 return VFW_S_NO_MORE_ITEMS
;
1399 CopyMediaType(pmt
, This
->pmt
);
1403 static HRESULT WINAPI
QTOutPin_DecideBufferSize(BaseOutputPin
*iface
, IMemAllocator
*pAlloc
, ALLOCATOR_PROPERTIES
*ppropInputRequest
)
1409 static HRESULT WINAPI
QTOutPin_DecideAllocator(BaseOutputPin
*iface
, IMemInputPin
*pPin
, IMemAllocator
**pAlloc
)
1412 QTOutPin
*This
= impl_QTOutPin_from_BaseOutputPin(iface
);
1413 QTSplitter
*QTfilter
= impl_from_IBaseFilter(This
->pin
.pin
.pinInfo
.pFilter
);
1416 if (QTfilter
->pInputPin
.pAlloc
)
1418 hr
= IMemInputPin_NotifyAllocator(pPin
, QTfilter
->pInputPin
.pAlloc
, FALSE
);
1421 *pAlloc
= QTfilter
->pInputPin
.pAlloc
;
1422 IMemAllocator_AddRef(*pAlloc
);
1426 hr
= VFW_E_NO_ALLOCATOR
;
1431 static HRESULT WINAPI
QTOutPin_BreakConnect(BaseOutputPin
*This
)
1435 TRACE("(%p)->()\n", This
);
1437 EnterCriticalSection(This
->pin
.pCritSec
);
1438 if (!This
->pin
.pConnectedTo
|| !This
->pMemInputPin
)
1439 hr
= VFW_E_NOT_CONNECTED
;
1442 hr
= IPin_Disconnect(This
->pin
.pConnectedTo
);
1443 IPin_Disconnect(&This
->pin
.IPin_iface
);
1445 LeaveCriticalSection(This
->pin
.pCritSec
);
1450 static const IPinVtbl QT_OutputPin_Vtbl
= {
1451 QTOutPin_QueryInterface
,
1454 BaseOutputPinImpl_Connect
,
1455 BaseOutputPinImpl_ReceiveConnection
,
1456 BaseOutputPinImpl_Disconnect
,
1457 BasePinImpl_ConnectedTo
,
1458 BasePinImpl_ConnectionMediaType
,
1459 BasePinImpl_QueryPinInfo
,
1460 BasePinImpl_QueryDirection
,
1461 BasePinImpl_QueryId
,
1462 BasePinImpl_QueryAccept
,
1463 BasePinImpl_EnumMediaTypes
,
1464 BasePinImpl_QueryInternalConnections
,
1465 BaseOutputPinImpl_EndOfStream
,
1466 BaseOutputPinImpl_BeginFlush
,
1467 BaseOutputPinImpl_EndFlush
,
1468 BasePinImpl_NewSegment
1471 static inline QTOutPin
*impl_from_IQualityControl( IQualityControl
*iface
)
1473 return CONTAINING_RECORD(iface
, QTOutPin
, IQualityControl_iface
);
1476 HRESULT WINAPI
QT_QualityControl_QueryInterface(IQualityControl
*iface
, REFIID riid
, void **ppv
)
1478 QTOutPin
*This
= impl_from_IQualityControl(iface
);
1479 return IPin_QueryInterface(&This
->pin
.pin
.IPin_iface
, riid
, ppv
);
1482 ULONG WINAPI
QT_QualityControl_AddRef(IQualityControl
*iface
)
1484 QTOutPin
*This
= impl_from_IQualityControl(iface
);
1485 return IPin_AddRef(&This
->pin
.pin
.IPin_iface
);
1488 ULONG WINAPI
QT_QualityControl_Release(IQualityControl
*iface
)
1490 QTOutPin
*This
= impl_from_IQualityControl(iface
);
1491 return IPin_Release(&This
->pin
.pin
.IPin_iface
);
1494 static HRESULT WINAPI
QT_QualityControl_Notify(IQualityControl
*iface
, IBaseFilter
*sender
, Quality qm
)
1496 REFERENCE_TIME late
= qm
.Late
;
1497 if (qm
.Late
< 0 && -qm
.Late
> qm
.TimeStamp
)
1498 late
= -qm
.TimeStamp
;
1499 /* TODO: Do Something */
1503 HRESULT WINAPI
QT_QualityControl_SetSink(IQualityControl
*iface
, IQualityControl
*tonotify
)
1509 static const IQualityControlVtbl QTOutPin_QualityControl_Vtbl
= {
1510 QT_QualityControl_QueryInterface
,
1511 QT_QualityControl_AddRef
,
1512 QT_QualityControl_Release
,
1513 QT_QualityControl_Notify
,
1514 QT_QualityControl_SetSink
1517 static const BaseOutputPinFuncTable output_BaseOutputFuncTable
= {
1519 QTOutPin_CheckMediaType
,
1520 BaseOutputPinImpl_AttemptConnection
,
1521 BasePinImpl_GetMediaTypeVersion
,
1522 QTOutPin_GetMediaType
1524 QTOutPin_DecideBufferSize
,
1525 QTOutPin_DecideAllocator
,
1526 QTOutPin_BreakConnect
1529 static const OutputQueueFuncTable output_OutputQueueFuncTable
= {
1530 OutputQueueImpl_ThreadProc
1533 static HRESULT
QT_AddPin(QTSplitter
*This
, const PIN_INFO
*piOutput
, const AM_MEDIA_TYPE
*amt
, BOOL video
)
1539 target
= (IPin
**)&This
->pVideo_Pin
;
1541 target
= (IPin
**)&This
->pAudio_Pin
;
1543 if (*target
!= NULL
)
1545 FIXME("We already have a %s pin\n",(video
)?"video":"audio");
1549 hr
= BaseOutputPin_Construct(&QT_OutputPin_Vtbl
, sizeof(QTOutPin
), piOutput
, &output_BaseOutputFuncTable
, &This
->filter
.csFilter
, (IPin
**)target
);
1552 QTOutPin
*pin
= (QTOutPin
*)*target
;
1553 pin
->pmt
= CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE
));
1554 CopyMediaType(pin
->pmt
, amt
);
1555 pin
->pin
.pin
.pinInfo
.pFilter
= (LPVOID
)This
;
1556 pin
->IQualityControl_iface
.lpVtbl
= &QTOutPin_QualityControl_Vtbl
;
1558 BaseFilterImpl_IncrementPinVersion(&This
->filter
);
1560 hr
= OutputQueue_Construct(&pin
->pin
, TRUE
, TRUE
, 5, FALSE
, THREAD_PRIORITY_NORMAL
, &output_OutputQueueFuncTable
, &pin
->queue
);
1563 ERR("Failed with error %x\n", hr
);
1567 static HRESULT WINAPI
QTSplitter_ChangeStart(IMediaSeeking
*iface
)
1569 QTSplitter
*This
= impl_from_IMediaSeeking(iface
);
1570 TRACE("(%p)\n", iface
);
1571 EnterCriticalSection(&This
->csReceive
);
1572 This
->movie_time
= (This
->sourceSeeking
.llCurrent
* This
->movie_scale
)/10000000;
1573 This
->movie_start
= This
->movie_time
;
1574 LeaveCriticalSection(&This
->csReceive
);
1578 static HRESULT WINAPI
QTSplitter_ChangeStop(IMediaSeeking
*iface
)
1580 FIXME("(%p) filter hasn't implemented stop position change!\n", iface
);
1584 static HRESULT WINAPI
QTSplitter_ChangeRate(IMediaSeeking
*iface
)
1586 FIXME("(%p) filter hasn't implemented rate change!\n", iface
);
1590 static HRESULT WINAPI
QT_Seeking_QueryInterface(IMediaSeeking
* iface
, REFIID riid
, LPVOID
* ppv
)
1592 QTSplitter
*This
= impl_from_IMediaSeeking(iface
);
1594 return IBaseFilter_QueryInterface(&This
->filter
.IBaseFilter_iface
, riid
, ppv
);
1597 static ULONG WINAPI
QT_Seeking_AddRef(IMediaSeeking
* iface
)
1599 QTSplitter
*This
= impl_from_IMediaSeeking(iface
);
1601 return IBaseFilter_AddRef(&This
->filter
.IBaseFilter_iface
);
1604 static ULONG WINAPI
QT_Seeking_Release(IMediaSeeking
* iface
)
1606 QTSplitter
*This
= impl_from_IMediaSeeking(iface
);
1608 return IBaseFilter_Release(&This
->filter
.IBaseFilter_iface
);
1611 static const IMediaSeekingVtbl QT_Seeking_Vtbl
=
1613 QT_Seeking_QueryInterface
,
1616 SourceSeekingImpl_GetCapabilities
,
1617 SourceSeekingImpl_CheckCapabilities
,
1618 SourceSeekingImpl_IsFormatSupported
,
1619 SourceSeekingImpl_QueryPreferredFormat
,
1620 SourceSeekingImpl_GetTimeFormat
,
1621 SourceSeekingImpl_IsUsingTimeFormat
,
1622 SourceSeekingImpl_SetTimeFormat
,
1623 SourceSeekingImpl_GetDuration
,
1624 SourceSeekingImpl_GetStopPosition
,
1625 SourceSeekingImpl_GetCurrentPosition
,
1626 SourceSeekingImpl_ConvertTimeFormat
,
1627 SourceSeekingImpl_SetPositions
,
1628 SourceSeekingImpl_GetPositions
,
1629 SourceSeekingImpl_GetAvailable
,
1630 SourceSeekingImpl_SetRate
,
1631 SourceSeekingImpl_GetRate
,
1632 SourceSeekingImpl_GetPreroll