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
;
168 static const IPinVtbl QT_OutputPin_Vtbl
;
169 static const IPinVtbl QT_InputPin_Vtbl
;
170 static const IBaseFilterVtbl QT_Vtbl
;
172 static HRESULT
QT_AddPin(QTSplitter
*This
, const PIN_INFO
*piOutput
, const AM_MEDIA_TYPE
*amt
, BOOL video
);
173 static HRESULT
QT_RemoveOutputPins(QTSplitter
*This
);
179 static IPin
* WINAPI
QT_GetPin(BaseFilter
*iface
, int pos
)
181 QTSplitter
*This
= (QTSplitter
*)iface
;
182 TRACE("Asking for pos %x\n", pos
);
184 if (pos
> 2 || pos
< 0)
189 IPin_AddRef((IPin
*)&This
->pInputPin
);
190 return (IPin
*)&This
->pInputPin
;
192 if (This
->pVideo_Pin
)
193 IPin_AddRef((IPin
*)This
->pVideo_Pin
);
194 return (IPin
*)This
->pVideo_Pin
;
196 if (This
->pAudio_Pin
)
197 IPin_AddRef((IPin
*)This
->pAudio_Pin
);
198 return (IPin
*)This
->pAudio_Pin
;
204 static LONG WINAPI
QT_GetPinCount(BaseFilter
*iface
)
206 QTSplitter
*This
= (QTSplitter
*)iface
;
208 if (This
->pAudio_Pin
) c
++;
209 if (This
->pVideo_Pin
) c
++;
213 static const BaseFilterFuncTable BaseFuncTable
= {
218 IUnknown
* CALLBACK
QTSplitter_create(IUnknown
*punkout
, HRESULT
*phr
)
220 IUnknown
*obj
= NULL
;
223 static const WCHAR wcsInputPinName
[] = {'I','n','p','u','t',' ','P','i','n',0};
227 RegisterWineDataHandler();
229 This
= CoTaskMemAlloc(sizeof(*This
));
230 obj
= (IUnknown
*)This
;
233 *phr
= E_OUTOFMEMORY
;
237 BaseFilter_Init(&This
->filter
, &QT_Vtbl
, &CLSID_QTSplitter
, (DWORD_PTR
)(__FILE__
": QTSplitter.csFilter"), &BaseFuncTable
);
239 This
->pVideo_Pin
= NULL
;
240 This
->pAudio_Pin
= NULL
;
241 This
->state
= State_Stopped
;
242 This
->aSession
= NULL
;
243 This
->runEvent
= CreateEventW(NULL
, 0, 0, NULL
);
245 piInput
= &This
->pInputPin
.pin
.pinInfo
;
246 piInput
->dir
= PINDIR_INPUT
;
247 piInput
->pFilter
= (IBaseFilter
*)This
;
248 lstrcpynW(piInput
->achName
, wcsInputPinName
, sizeof(piInput
->achName
) / sizeof(piInput
->achName
[0]));
249 This
->pInputPin
.pin
.lpVtbl
= &QT_InputPin_Vtbl
;
250 This
->pInputPin
.pin
.refCount
= 1;
251 This
->pInputPin
.pin
.pConnectedTo
= NULL
;
252 This
->pInputPin
.pin
.pCritSec
= &This
->filter
.csFilter
;
253 ZeroMemory(&This
->pInputPin
.pin
.mtCurrent
, sizeof(AM_MEDIA_TYPE
));
258 static void QT_Destroy(QTSplitter
*This
)
260 IPin
*connected
= NULL
;
263 TRACE("Destroying\n");
265 /* Don't need to clean up output pins, disconnecting input pin will do that */
266 IPin_ConnectedTo((IPin
*)&This
->pInputPin
, &connected
);
269 IPin_Disconnect(connected
);
270 IPin_Release(connected
);
272 pinref
= IPin_Release((IPin
*)&This
->pInputPin
);
275 ERR("pinref should be null, is %u, destroying anyway\n", pinref
);
276 assert((LONG
)pinref
> 0);
279 pinref
= IPin_Release((IPin
*)&This
->pInputPin
);
283 DisposeMovie(This
->pQTMovie
);
285 QTVisualContextRelease(This
->vContext
);
287 MovieAudioExtractionEnd(This
->aSession
);
288 CloseHandle(This
->runEvent
);
294 static HRESULT WINAPI
QT_QueryInterface(IBaseFilter
*iface
, REFIID riid
, LPVOID
*ppv
)
296 QTSplitter
*This
= (QTSplitter
*)iface
;
297 TRACE("(%s, %p)\n", debugstr_guid(riid
), ppv
);
301 if (IsEqualIID(riid
, &IID_IUnknown
))
303 else if (IsEqualIID(riid
, &IID_IPersist
))
305 else if (IsEqualIID(riid
, &IID_IMediaFilter
))
307 else if (IsEqualIID(riid
, &IID_IBaseFilter
))
312 IUnknown_AddRef((IUnknown
*)(*ppv
));
316 if (!IsEqualIID(riid
, &IID_IPin
) && !IsEqualIID(riid
, &IID_IVideoWindow
))
317 FIXME("No interface for %s!\n", debugstr_guid(riid
));
319 return E_NOINTERFACE
;
322 static ULONG WINAPI
QT_Release(IBaseFilter
*iface
)
324 QTSplitter
*This
= (QTSplitter
*)iface
;
325 ULONG refCount
= BaseFilterImpl_Release(iface
);
327 TRACE("(%p)->() Release from %d\n", This
, refCount
+ 1);
335 static HRESULT WINAPI
QT_Stop(IBaseFilter
*iface
)
337 QTSplitter
*This
= (QTSplitter
*)iface
;
341 IAsyncReader_BeginFlush(This
->pInputPin
.pReader
);
342 IAsyncReader_EndFlush(This
->pInputPin
.pReader
);
347 static HRESULT WINAPI
QT_Pause(IBaseFilter
*iface
)
355 static OSErr
QT_Create_Extract_Session(QTSplitter
*filter
)
357 AudioStreamBasicDescription aDesc
;
361 pvi
= (WAVEFORMATEX
*)filter
->pAudio_Pin
->pmt
->pbFormat
;
363 err
= MovieAudioExtractionBegin(filter
->pQTMovie
, 0, &filter
->aSession
);
366 ERR("Failed to begin Extraction session %i\n",err
);
370 err
= MovieAudioExtractionGetProperty(filter
->aSession
,
371 kQTPropertyClass_MovieAudioExtraction_Audio
,
372 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription
,
373 sizeof(AudioStreamBasicDescription
), &aDesc
, NULL
);
377 MovieAudioExtractionEnd(filter
->aSession
);
378 filter
->aSession
= NULL
;
379 ERR("Failed to get session description %i\n",err
);
383 aDesc
.mFormatID
= kAudioFormatLinearPCM
;
384 aDesc
.mFormatFlags
= kLinearPCMFormatFlagIsSignedInteger
+
385 kAudioFormatFlagIsPacked
;
386 aDesc
.mFramesPerPacket
= 1;
387 aDesc
.mChannelsPerFrame
= pvi
->nChannels
;
388 aDesc
.mBitsPerChannel
= pvi
->wBitsPerSample
;
389 aDesc
.mSampleRate
= pvi
->nSamplesPerSec
;
390 aDesc
.mBytesPerFrame
= (aDesc
.mBitsPerChannel
* aDesc
.mChannelsPerFrame
) / 8;
391 aDesc
.mBytesPerPacket
= aDesc
.mBytesPerFrame
* aDesc
.mFramesPerPacket
;
393 err
= MovieAudioExtractionSetProperty(filter
->aSession
,
394 kQTPropertyClass_MovieAudioExtraction_Audio
,
395 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription
,
396 sizeof(AudioStreamBasicDescription
), &aDesc
);
398 if (aDesc
.mFormatID
!= kAudioFormatLinearPCM
)
400 ERR("Not PCM Wave\n");
403 if (aDesc
.mFormatFlags
!= kLinearPCMFormatFlagIsSignedInteger
+
404 kAudioFormatFlagIsPacked
)
406 ERR("Unhandled Flags\n");
409 if (aDesc
.mFramesPerPacket
!= 1)
411 ERR("Unhandled Frames per packet %li\n",aDesc
.mFramesPerPacket
);
414 if (aDesc
.mChannelsPerFrame
!= pvi
->nChannels
)
416 ERR("Unhandled channel count %li\n",aDesc
.mChannelsPerFrame
);
419 if (aDesc
.mBitsPerChannel
!= pvi
->wBitsPerSample
)
421 ERR("Unhandled bits per channel %li\n",aDesc
.mBitsPerChannel
);
424 if (aDesc
.mSampleRate
!= pvi
->nSamplesPerSec
)
426 ERR("Unhandled sample rate %f\n",aDesc
.mSampleRate
);
432 ERR("Failed to create Extraction Session\n");
433 MovieAudioExtractionEnd(filter
->aSession
);
434 filter
->aSession
= NULL
;
440 static DWORD WINAPI
QTSplitter_thread(LPVOID data
)
442 QTSplitter
*This
= (QTSplitter
*)data
;
444 TimeValue movie_time
=0, next_time
;
445 CVPixelBufferRef pixelBuffer
= NULL
;
449 if (This
->pAudio_Pin
)
451 /* according to QA1469 a movie has to be fully loaded before we
452 can reliably start the Extraction session */
454 while(GetMovieLoadState(This
->pQTMovie
) < kMovieLoadStateComplete
)
455 MoviesTask(This
->pQTMovie
,1000);
457 QT_Create_Extract_Session(This
);
460 WaitForSingleObject(This
->runEvent
, -1);
462 This
->state
= State_Running
;
463 /* Prime the pump: Needed for MPEG streams */
464 GetMovieNextInterestingTime(This
->pQTMovie
, nextTimeEdgeOK
| nextTimeStep
, 0, NULL
, movie_time
, 1, &next_time
, NULL
);
466 GetMovieTime(This
->pQTMovie
, &tr
);
469 LONGLONG tStart
=0, tStop
=0;
472 GetMovieNextInterestingTime(This
->pQTMovie
, nextTimeStep
, 0, NULL
, movie_time
, 1, &next_time
, NULL
);
476 TRACE("No next time\n");
480 tr
.value
= SInt64ToWide(next_time
);
481 SetMovieTime(This
->pQTMovie
, &tr
);
482 MoviesTask(This
->pQTMovie
,0);
483 QTVisualContextTask(This
->vContext
);
485 TRACE("In loop at time %ld\n",movie_time
);
486 TRACE("In Next time %ld\n",next_time
);
488 time
= (float)movie_time
/ tr
.scale
;
489 tStart
= time
* 10000000;
490 time
= (float)next_time
/ tr
.scale
;
491 tStop
= time
* 10000000;
494 if (This
->pAudio_Pin
&& ((BaseOutputPin
*)This
->pAudio_Pin
)->pin
.pConnectedTo
&& This
->aSession
)
498 IMediaSample
*sample
= NULL
;
499 AudioBufferList aData
;
505 pvi
= (WAVEFORMATEX
*)This
->pAudio_Pin
->pmt
->pbFormat
;
507 hr
= BaseOutputPinImpl_GetDeliveryBuffer((BaseOutputPin
*)This
->pAudio_Pin
, &sample
, NULL
, NULL
, 0);
511 ERR("Audio: Unable to get delivery buffer (%x)\n", hr
);
515 hr
= IMediaSample_GetPointer(sample
, &ptr
);
518 ERR("Audio: Unable to get pointer to buffer (%x)\n", hr
);
522 duration
= (float)next_time
/ tr
.scale
;
523 time
= (float)movie_time
/ tr
.scale
;
525 frames
= pvi
->nSamplesPerSec
* duration
;
526 TRACE("Need audio for %f seconds (%li frames)\n",duration
,frames
);
528 data_size
= IMediaSample_GetSize(sample
);
529 if (data_size
< frames
* pvi
->nBlockAlign
)
530 FIXME("Audio buffer is too small\n");
532 aData
.mNumberBuffers
= 1;
533 aData
.mBuffers
[0].mNumberChannels
= pvi
->nChannels
;
534 aData
.mBuffers
[0].mDataByteSize
= data_size
;
535 aData
.mBuffers
[0].mData
= ptr
;
537 err
= MovieAudioExtractionFillBuffer(This
->aSession
, &frames
, &aData
, &flags
);
538 TRACE("Got %i frames\n",(int)frames
);
540 IMediaSample_SetActualDataLength(sample
, frames
* pvi
->nBlockAlign
);
542 IMediaSample_SetMediaTime(sample
, &tStart
, &tStop
);
544 IMediaSample_SetTime(sample
, &tStart
, &tStop
);
546 IMediaSample_SetTime(sample
, NULL
, NULL
);
548 hr
= OutputQueue_Receive(This
->pAudio_Pin
->queue
, sample
);
549 TRACE("Audio Delivered (%x)\n",hr
);
553 IMediaSample_Release(sample
);
556 TRACE("Audio Pin not connected or no Audio\n");
559 if (This
->pVideo_Pin
&& QTVisualContextIsNewImageAvailable(This
->vContext
,0))
561 err
= QTVisualContextCopyImageForTime(This
->vContext
, NULL
, NULL
, &pixelBuffer
);
566 IMediaSample
*sample
= NULL
;
568 hr
= BaseOutputPinImpl_GetDeliveryBuffer((BaseOutputPin
*)This
->pVideo_Pin
, &sample
, NULL
, NULL
, 0);
571 ERR("Video: Unable to get delivery buffer (%x)\n", hr
);
575 data_size
= IMediaSample_GetSize(sample
);
576 if (data_size
< This
->outputSize
)
578 ERR("Sample size is too small %d < %d\n", data_size
, This
->outputSize
)
584 hr
= IMediaSample_GetPointer(sample
, &ptr
);
587 ERR("Video: Unable to get pointer to buffer (%x)\n", hr
);
591 hr
= AccessPixelBufferPixels( pixelBuffer
, ptr
);
594 ERR("Failed to access Pixels\n");
598 IMediaSample_SetActualDataLength(sample
, This
->outputSize
);
600 IMediaSample_SetMediaTime(sample
, &tStart
, &tStop
);
602 IMediaSample_SetTime(sample
, &tStart
, &tStop
);
604 IMediaSample_SetTime(sample
, NULL
, NULL
);
606 hr
= OutputQueue_Receive(This
->pVideo_Pin
->queue
, sample
);
607 TRACE("Video Delivered (%x)\n",hr
);
611 IMediaSample_Release(sample
);
613 CVPixelBufferRelease(pixelBuffer
);
617 TRACE("No video to deliver\n");
619 movie_time
= next_time
;
620 } while (hr
== S_OK
);
622 This
->state
= State_Stopped
;
623 if (This
->pAudio_Pin
)
624 OutputQueue_EOS(This
->pAudio_Pin
->queue
);
625 if (This
->pVideo_Pin
)
626 OutputQueue_EOS(This
->pVideo_Pin
->queue
);
631 static HRESULT WINAPI
QT_Run(IBaseFilter
*iface
, REFERENCE_TIME tStart
)
634 QTSplitter
*This
= (QTSplitter
*)iface
;
635 HRESULT hr_any
= VFW_E_NOT_CONNECTED
;
637 TRACE("(%s)\n", wine_dbgstr_longlong(tStart
));
639 EnterCriticalSection(&This
->filter
.csFilter
);
640 This
->filter
.rtStreamStart
= tStart
;
642 if (This
->pVideo_Pin
)
643 hr
= BaseOutputPinImpl_Active((BaseOutputPin
*)This
->pVideo_Pin
);
646 if (This
->pAudio_Pin
)
647 hr
= BaseOutputPinImpl_Active((BaseOutputPin
*)This
->pAudio_Pin
);
652 LeaveCriticalSection(&This
->filter
.csFilter
);
654 SetEvent(This
->runEvent
);
659 static HRESULT WINAPI
QT_GetState(IBaseFilter
*iface
, DWORD dwMilliSecsTimeout
, FILTER_STATE
*pState
)
661 QTSplitter
*This
= (QTSplitter
*)iface
;
662 TRACE("(%d, %p)\n", dwMilliSecsTimeout
, pState
);
664 *pState
= This
->state
;
669 static HRESULT WINAPI
QT_FindPin(IBaseFilter
*iface
, LPCWSTR Id
, IPin
**ppPin
)
671 FIXME("(%p)->(%s,%p) stub\n", iface
, debugstr_w(Id
), ppPin
);
675 static const IBaseFilterVtbl QT_Vtbl
= {
677 BaseFilterImpl_AddRef
,
679 BaseFilterImpl_GetClassID
,
684 BaseFilterImpl_SetSyncSource
,
685 BaseFilterImpl_GetSyncSource
,
686 BaseFilterImpl_EnumPins
,
688 BaseFilterImpl_QueryFilterInfo
,
689 BaseFilterImpl_JoinFilterGraph
,
690 BaseFilterImpl_QueryVendorInfo
696 static HRESULT
QT_RemoveOutputPins(QTSplitter
*This
)
699 TRACE("(%p)\n", This
);
701 if (This
->pVideo_Pin
)
703 hr
= BaseOutputPinImpl_BreakConnect(&This
->pVideo_Pin
->pin
);
704 TRACE("Disconnect: %08x\n", hr
);
705 IPin_Release((IPin
*)This
->pVideo_Pin
);
706 This
->pVideo_Pin
= NULL
;
708 if (This
->pAudio_Pin
)
710 hr
= BaseOutputPinImpl_BreakConnect(&This
->pAudio_Pin
->pin
);
711 TRACE("Disconnect: %08x\n", hr
);
712 IPin_Release((IPin
*)This
->pAudio_Pin
);
713 This
->pAudio_Pin
= NULL
;
716 BaseFilterImpl_IncrementPinVersion((BaseFilter
*)This
);
720 static ULONG WINAPI
QTInPin_Release(IPin
*iface
)
722 QTInPin
*This
= (QTInPin
*)iface
;
723 ULONG refCount
= InterlockedDecrement(&This
->pin
.refCount
);
725 TRACE("(%p)->() Release from %d\n", iface
, refCount
+ 1);
728 FreeMediaType(&This
->pin
.mtCurrent
);
730 IMemAllocator_Release(This
->pAlloc
);
732 This
->pin
.lpVtbl
= NULL
;
739 static HRESULT
QT_Process_Video_Track(QTSplitter
* filter
, Track trk
)
742 VIDEOINFOHEADER
* pvi
;
746 static const WCHAR szwVideoOut
[] = {'V','i','d','e','o',0};
747 CFMutableDictionaryRef pixelBufferOptions
= NULL
;
748 CFMutableDictionaryRef visualContextOptions
= NULL
;
749 CFNumberRef n
= NULL
;
751 DWORD outputWidth
, outputHeight
, outputDepth
;
752 Fixed trackWidth
, trackHeight
;
754 ZeroMemory(&amt
, sizeof(amt
));
755 amt
.formattype
= FORMAT_VideoInfo
;
756 amt
.majortype
= MEDIATYPE_Video
;
757 amt
.subtype
= MEDIASUBTYPE_RGB24
;
759 GetTrackDimensions(trk
, &trackWidth
, &trackHeight
);
762 outputWidth
= Fix2Long(trackWidth
);
763 outputHeight
= Fix2Long(trackHeight
);
764 TRACE("Width %i Height %i\n",outputWidth
, outputHeight
);
766 amt
.cbFormat
= sizeof(VIDEOINFOHEADER
);
767 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
768 ZeroMemory(amt
.pbFormat
, amt
.cbFormat
);
769 pvi
= (VIDEOINFOHEADER
*)amt
.pbFormat
;
770 pvi
->bmiHeader
.biSize
= sizeof (BITMAPINFOHEADER
);
771 pvi
->bmiHeader
.biWidth
= outputWidth
;
772 pvi
->bmiHeader
.biHeight
= -outputHeight
;
773 pvi
->bmiHeader
.biPlanes
= 1;
774 pvi
->bmiHeader
.biBitCount
= 24;
775 pvi
->bmiHeader
.biCompression
= BI_RGB
;
777 filter
->outputSize
= outputWidth
* outputHeight
* outputDepth
;
780 pixelBufferOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
782 t
= k32ARGBPixelFormat
;
783 n
= CFNumberCreate(NULL
, kCFNumberIntType
, &t
);
784 CFDictionaryAddValue(pixelBufferOptions
, kCVPixelBufferPixelFormatTypeKey
, n
);
787 n
= CFNumberCreate(NULL
, kCFNumberIntType
, &outputWidth
);
788 CFDictionaryAddValue(pixelBufferOptions
, kCVPixelBufferWidthKey
, n
);
791 n
= CFNumberCreate(NULL
, kCFNumberIntType
, &outputHeight
);
792 CFDictionaryAddValue(pixelBufferOptions
, kCVPixelBufferHeightKey
, n
);
796 n
= CFNumberCreate(NULL
, kCFNumberIntType
, &t
);
797 CFDictionaryAddValue(pixelBufferOptions
, kCVPixelBufferBytesPerRowAlignmentKey
, n
);
800 visualContextOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
802 CFDictionarySetValue(visualContextOptions
, kQTVisualContextPixelBufferAttributesKey
, pixelBufferOptions
);
804 err
= QTPixelBufferContextCreate(NULL
, visualContextOptions
,&filter
->vContext
);
805 CFRelease(pixelBufferOptions
);
806 CFRelease(visualContextOptions
);
809 ERR("Failed to create Visual Context\n");
813 err
= SetMovieVisualContext(filter
->pQTMovie
, filter
->vContext
);
816 ERR("Failed to set Visual Context\n");
820 piOutput
.dir
= PINDIR_OUTPUT
;
821 piOutput
.pFilter
= (IBaseFilter
*)filter
;
822 lstrcpyW(piOutput
.achName
,szwVideoOut
);
824 hr
= QT_AddPin(filter
, &piOutput
, &amt
, TRUE
);
826 ERR("Failed to add Video Track\n");
828 TRACE("Video Pin %p\n",filter
->pVideo_Pin
);
833 static HRESULT
QT_Process_Audio_Track(QTSplitter
* filter
, Track trk
)
839 static const WCHAR szwAudioOut
[] = {'A','u','d','i','o',0};
842 SoundDescriptionHandle aDesc
= (SoundDescriptionHandle
) NewHandle(sizeof(SoundDescription
));
844 audioMedia
= GetTrackMedia(trk
);
845 GetMediaSampleDescription(audioMedia
, 1, (SampleDescriptionHandle
)aDesc
);
847 ZeroMemory(&amt
, sizeof(amt
));
848 amt
.formattype
= FORMAT_WaveFormatEx
;
849 amt
.majortype
= MEDIATYPE_Audio
;
850 amt
.subtype
= MEDIASUBTYPE_PCM
;
851 amt
.bTemporalCompression
= 0;
853 amt
.cbFormat
= sizeof(WAVEFORMATEX
);
854 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
855 ZeroMemory(amt
.pbFormat
, amt
.cbFormat
);
856 pvi
= (WAVEFORMATEX
*)amt
.pbFormat
;
858 pvi
->cbSize
= sizeof(WAVEFORMATEX
);
859 pvi
->wFormatTag
= WAVE_FORMAT_PCM
;
860 pvi
->nChannels
= ((SoundDescription
)**aDesc
).numChannels
;
861 if (pvi
->nChannels
< 1 || pvi
->nChannels
> 2)
863 pvi
->nSamplesPerSec
= (((SoundDescription
)**aDesc
).sampleRate
/65536);
864 if (pvi
->nSamplesPerSec
< 8000 || pvi
->nChannels
> 48000)
865 pvi
->nSamplesPerSec
= 44100;
866 pvi
->wBitsPerSample
= ((SoundDescription
)**aDesc
).sampleSize
;
867 if (pvi
->wBitsPerSample
< 8 || pvi
->wBitsPerSample
> 32)
868 pvi
->wBitsPerSample
= 16;
869 pvi
->nBlockAlign
= (pvi
->nChannels
* pvi
->wBitsPerSample
) / 8;
870 pvi
->nAvgBytesPerSec
= pvi
->nSamplesPerSec
* pvi
->nBlockAlign
;
872 DisposeHandle((Handle
)aDesc
);
874 piOutput
.dir
= PINDIR_OUTPUT
;
875 piOutput
.pFilter
= (IBaseFilter
*)filter
;
876 lstrcpyW(piOutput
.achName
,szwAudioOut
);
878 hr
= QT_AddPin(filter
, &piOutput
, &amt
, FALSE
);
880 ERR("Failed to add Audio Track\n");
882 TRACE("Audio Pin %p\n",filter
->pAudio_Pin
);
886 static HRESULT
QT_Process_Movie(QTSplitter
* filter
)
890 WineDataRefRecord ptrDataRefRec
;
891 Handle dataRef
= NULL
;
896 TRACE("Trying movie connect\n");
898 ptrDataRefRec
.pReader
= filter
->pInputPin
.pReader
;
899 ptrDataRefRec
.streamSubtype
= filter
->pInputPin
.subType
;
900 PtrToHand( &ptrDataRefRec
, &dataRef
, sizeof(WineDataRefRecord
));
902 err
= NewMovieFromDataRef(&filter
->pQTMovie
, newMovieActive
|newMovieDontInteractWithUser
|newMovieDontAutoUpdateClock
|newMovieDontAskUnresolvedDataRefs
|newMovieAsyncOK
, &id
, dataRef
, 'WINE');
904 DisposeHandle(dataRef
);
908 FIXME("QuickTime cannot handle media type(%i)\n",err
);
909 return VFW_E_TYPE_NOT_ACCEPTED
;
912 PrePrerollMovie(filter
->pQTMovie
, 0, fixed1
, NULL
, NULL
);
913 PrerollMovie(filter
->pQTMovie
, 0, fixed1
);
914 GoToBeginningOfMovie(filter
->pQTMovie
);
915 SetMovieActive(filter
->pQTMovie
,TRUE
);
917 if (GetMovieLoadState(filter
->pQTMovie
) < kMovieLoadStateLoaded
)
918 MoviesTask(filter
->pQTMovie
,100);
920 trk
= GetMovieIndTrackType(filter
->pQTMovie
, 1, VisualMediaCharacteristic
, movieTrackCharacteristic
| movieTrackEnabledOnly
);
921 TRACE("%p is a video track\n",trk
);
923 hr
= QT_Process_Video_Track(filter
, trk
);
928 trk
= GetMovieIndTrackType(filter
->pQTMovie
, 1, AudioMediaCharacteristic
, movieTrackCharacteristic
| movieTrackEnabledOnly
);
929 TRACE("%p is a audio track\n",trk
);
931 hr
= QT_Process_Audio_Track(filter
, trk
);
933 CreateThread(NULL
, 0, QTSplitter_thread
, filter
, 0, &tid
);
934 TRACE("Created thread 0x%08x\n",tid
);
939 static HRESULT WINAPI
QTInPin_ReceiveConnection(IPin
*iface
, IPin
*pReceivePin
, const AM_MEDIA_TYPE
*pmt
)
942 ALLOCATOR_PROPERTIES props
;
943 QTInPin
*This
= (QTInPin
*)iface
;
945 TRACE("(%p/%p)->(%p, %p)\n", This
, iface
, pReceivePin
, pmt
);
947 EnterCriticalSection(This
->pin
.pCritSec
);
948 This
->pReader
= NULL
;
950 if (This
->pin
.pConnectedTo
)
951 hr
= VFW_E_ALREADY_CONNECTED
;
952 else if (IPin_QueryAccept(iface
, pmt
) != S_OK
)
953 hr
= VFW_E_TYPE_NOT_ACCEPTED
;
956 PIN_DIRECTION pindirReceive
;
957 IPin_QueryDirection(pReceivePin
, &pindirReceive
);
958 if (pindirReceive
!= PINDIR_OUTPUT
)
959 hr
= VFW_E_INVALID_DIRECTION
;
964 LeaveCriticalSection(This
->pin
.pCritSec
);
968 hr
= IPin_QueryInterface(pReceivePin
, &IID_IAsyncReader
, (LPVOID
*)&This
->pReader
);
971 LeaveCriticalSection(This
->pin
.pCritSec
);
972 TRACE("Input source is not an AsyncReader\n");
976 LeaveCriticalSection(This
->pin
.pCritSec
);
977 EnterCriticalSection(&((QTSplitter
*)This
->pin
.pinInfo
.pFilter
)->filter
.csFilter
);
978 hr
= QT_Process_Movie((QTSplitter
*)This
->pin
.pinInfo
.pFilter
);
981 LeaveCriticalSection(&((QTSplitter
*)This
->pin
.pinInfo
.pFilter
)->filter
.csFilter
);
982 TRACE("Unable to process movie\n");
989 props
.cbBuffer
= ((QTSplitter
*)This
->pin
.pinInfo
.pFilter
)->outputSize
+ props
.cbAlign
;
992 hr
= IAsyncReader_RequestAllocator(This
->pReader
, NULL
, &props
, &This
->pAlloc
);
995 CopyMediaType(&This
->pin
.mtCurrent
, pmt
);
996 This
->pin
.pConnectedTo
= pReceivePin
;
997 IPin_AddRef(pReceivePin
);
998 hr
= IMemAllocator_Commit(This
->pAlloc
);
1002 QT_RemoveOutputPins((QTSplitter
*)This
->pin
.pinInfo
.pFilter
);
1004 IAsyncReader_Release(This
->pReader
);
1005 This
->pReader
= NULL
;
1007 IMemAllocator_Release(This
->pAlloc
);
1008 This
->pAlloc
= NULL
;
1010 TRACE("Size: %i\n", props
.cbBuffer
);
1011 LeaveCriticalSection(&((QTSplitter
*)This
->pin
.pinInfo
.pFilter
)->filter
.csFilter
);
1016 static HRESULT WINAPI
QTInPin_Disconnect(IPin
*iface
)
1019 QTInPin
*This
= (QTInPin
*)iface
;
1023 hr
= IBaseFilter_GetState(This
->pin
.pinInfo
.pFilter
, INFINITE
, &state
);
1024 EnterCriticalSection(This
->pin
.pCritSec
);
1025 if (This
->pin
.pConnectedTo
)
1027 QTSplitter
*Parser
= (QTSplitter
*)This
->pin
.pinInfo
.pFilter
;
1029 if (SUCCEEDED(hr
) && state
== State_Stopped
)
1031 IMemAllocator_Decommit(This
->pAlloc
);
1032 IPin_Disconnect(This
->pin
.pConnectedTo
);
1033 This
->pin
.pConnectedTo
= NULL
;
1034 hr
= QT_RemoveOutputPins(Parser
);
1037 hr
= VFW_E_NOT_STOPPED
;
1041 LeaveCriticalSection(This
->pin
.pCritSec
);
1045 static HRESULT WINAPI
QTInPin_QueryAccept(IPin
*iface
, const AM_MEDIA_TYPE
*pmt
)
1047 QTInPin
*This
= (QTInPin
*)iface
;
1049 TRACE("(%p)->(%p)\n", This
, pmt
);
1051 if (IsEqualIID(&pmt
->majortype
, &MEDIATYPE_Stream
))
1053 This
->subType
= pmt
->subtype
;
1059 static HRESULT WINAPI
QTInPin_EndOfStream(IPin
*iface
)
1061 QTInPin
*pin
= (QTInPin
*)iface
;
1062 QTSplitter
*This
= (QTSplitter
*)pin
->pin
.pinInfo
.pFilter
;
1064 FIXME("Propagate message on %p\n", This
);
1068 static HRESULT WINAPI
QTInPin_BeginFlush(IPin
*iface
)
1070 QTInPin
*pin
= (QTInPin
*)iface
;
1071 QTSplitter
*This
= (QTSplitter
*)pin
->pin
.pinInfo
.pFilter
;
1073 FIXME("Propagate message on %p\n", This
);
1077 static HRESULT WINAPI
QTInPin_EndFlush(IPin
*iface
)
1079 QTInPin
*pin
= (QTInPin
*)iface
;
1080 QTSplitter
*This
= (QTSplitter
*)pin
->pin
.pinInfo
.pFilter
;
1082 FIXME("Propagate message on %p\n", This
);
1086 static HRESULT WINAPI
QTInPin_NewSegment(IPin
*iface
, REFERENCE_TIME tStart
, REFERENCE_TIME tStop
, double dRate
)
1088 QTInPin
*pin
= (QTInPin
*)iface
;
1089 QTSplitter
*This
= (QTSplitter
*)pin
->pin
.pinInfo
.pFilter
;
1091 BasePinImpl_NewSegment(iface
, tStart
, tStop
, dRate
);
1092 FIXME("Propagate message on %p\n", This
);
1096 static HRESULT WINAPI
QTInPin_QueryInterface(IPin
* iface
, REFIID riid
, LPVOID
* ppv
)
1098 QTInPin
*This
= (QTInPin
*)iface
;
1100 TRACE("(%p/%p)->(%s, %p)\n", This
, iface
, debugstr_guid(riid
), ppv
);
1104 if (IsEqualIID(riid
, &IID_IUnknown
))
1106 else if (IsEqualIID(riid
, &IID_IPin
))
1108 else if (IsEqualIID(riid
, &IID_IMediaSeeking
))
1110 return IBaseFilter_QueryInterface(This
->pin
.pinInfo
.pFilter
, &IID_IMediaSeeking
, ppv
);
1115 IUnknown_AddRef((IUnknown
*)(*ppv
));
1119 FIXME("No interface for %s!\n", debugstr_guid(riid
));
1121 return E_NOINTERFACE
;
1124 static HRESULT WINAPI
QTInPin_EnumMediaTypes(IPin
*iface
, IEnumMediaTypes
**ppEnum
)
1126 BasePin
*This
= (BasePin
*)iface
;
1128 TRACE("(%p/%p)->(%p)\n", This
, iface
, ppEnum
);
1130 return EnumMediaTypes_Construct(This
, BasePinImpl_GetMediaType
, BasePinImpl_GetMediaTypeVersion
, ppEnum
);
1133 static const IPinVtbl QT_InputPin_Vtbl
= {
1134 QTInPin_QueryInterface
,
1137 BaseInputPinImpl_Connect
,
1138 QTInPin_ReceiveConnection
,
1140 BasePinImpl_ConnectedTo
,
1141 BasePinImpl_ConnectionMediaType
,
1142 BasePinImpl_QueryPinInfo
,
1143 BasePinImpl_QueryDirection
,
1144 BasePinImpl_QueryId
,
1145 QTInPin_QueryAccept
,
1146 QTInPin_EnumMediaTypes
,
1147 BasePinImpl_QueryInternalConnections
,
1148 QTInPin_EndOfStream
,
1158 static HRESULT WINAPI
QTOutPin_QueryInterface(IPin
*iface
, REFIID riid
, void **ppv
)
1160 QTOutPin
*This
= (QTOutPin
*)iface
;
1162 TRACE("(%s, %p)\n", debugstr_guid(riid
), ppv
);
1166 if (IsEqualIID(riid
, &IID_IUnknown
))
1168 else if (IsEqualIID(riid
, &IID_IPin
))
1170 else if (IsEqualIID(riid
, &IID_IMediaSeeking
))
1171 return IBaseFilter_QueryInterface(This
->pin
.pin
.pinInfo
.pFilter
, &IID_IMediaSeeking
, ppv
);
1175 IUnknown_AddRef((IUnknown
*)(*ppv
));
1178 FIXME("No interface for %s!\n", debugstr_guid(riid
));
1179 return E_NOINTERFACE
;
1182 static ULONG WINAPI
QTOutPin_Release(IPin
*iface
)
1184 QTOutPin
*This
= (QTOutPin
*)iface
;
1185 ULONG refCount
= InterlockedDecrement(&This
->pin
.pin
.refCount
);
1186 TRACE("(%p)->() Release from %d\n", iface
, refCount
+ 1);
1190 DeleteMediaType(This
->pmt
);
1191 FreeMediaType(&This
->pin
.pin
.mtCurrent
);
1192 OutputQueue_Destroy(This
->queue
);
1193 CoTaskMemFree(This
);
1199 static HRESULT WINAPI
QTOutPin_GetMediaType(BasePin
*iface
, int iPosition
, AM_MEDIA_TYPE
*pmt
)
1201 QTOutPin
*This
= (QTOutPin
*)iface
;
1204 return E_INVALIDARG
;
1206 return VFW_S_NO_MORE_ITEMS
;
1207 CopyMediaType(pmt
, This
->pmt
);
1211 static HRESULT WINAPI
QTOutPin_DecideBufferSize(BaseOutputPin
*iface
, IMemAllocator
*pAlloc
, ALLOCATOR_PROPERTIES
*ppropInputRequest
)
1217 static HRESULT WINAPI
QTOutPin_DecideAllocator(BaseOutputPin
*iface
, IMemInputPin
*pPin
, IMemAllocator
**pAlloc
)
1220 QTOutPin
*This
= (QTOutPin
*)iface
;
1221 QTSplitter
*QTfilter
= (QTSplitter
*)This
->pin
.pin
.pinInfo
.pFilter
;
1224 if (QTfilter
->pInputPin
.pAlloc
)
1225 hr
= IMemInputPin_NotifyAllocator(pPin
, QTfilter
->pInputPin
.pAlloc
, FALSE
);
1227 hr
= VFW_E_NO_ALLOCATOR
;
1232 static HRESULT WINAPI
QTOutPin_BreakConnect(BaseOutputPin
*This
)
1236 TRACE("(%p)->()\n", This
);
1238 EnterCriticalSection(This
->pin
.pCritSec
);
1239 if (!This
->pin
.pConnectedTo
|| !This
->pMemInputPin
)
1240 hr
= VFW_E_NOT_CONNECTED
;
1243 hr
= IPin_Disconnect(This
->pin
.pConnectedTo
);
1244 IPin_Disconnect((IPin
*)This
);
1246 LeaveCriticalSection(This
->pin
.pCritSec
);
1251 static const IPinVtbl QT_OutputPin_Vtbl
= {
1252 QTOutPin_QueryInterface
,
1255 BaseOutputPinImpl_Connect
,
1256 BaseOutputPinImpl_ReceiveConnection
,
1257 BaseOutputPinImpl_Disconnect
,
1258 BasePinImpl_ConnectedTo
,
1259 BasePinImpl_ConnectionMediaType
,
1260 BasePinImpl_QueryPinInfo
,
1261 BasePinImpl_QueryDirection
,
1262 BasePinImpl_QueryId
,
1263 BasePinImpl_QueryAccept
,
1264 BasePinImpl_EnumMediaTypes
,
1265 BasePinImpl_QueryInternalConnections
,
1266 BaseOutputPinImpl_EndOfStream
,
1267 BaseOutputPinImpl_BeginFlush
,
1268 BaseOutputPinImpl_EndFlush
,
1269 BasePinImpl_NewSegment
1272 static const BasePinFuncTable output_BaseFuncTable
= {
1274 BaseOutputPinImpl_AttemptConnection
,
1275 BasePinImpl_GetMediaTypeVersion
,
1276 QTOutPin_GetMediaType
1279 static const BaseOutputPinFuncTable output_BaseOutputFuncTable
= {
1280 QTOutPin_DecideBufferSize
,
1281 QTOutPin_DecideAllocator
,
1282 QTOutPin_BreakConnect
1285 static const OutputQueueFuncTable output_OutputQueueFuncTable
= {
1286 OutputQueueImpl_ThreadProc
1289 static HRESULT
QT_AddPin(QTSplitter
*This
, const PIN_INFO
*piOutput
, const AM_MEDIA_TYPE
*amt
, BOOL video
)
1295 target
= (IPin
**)&This
->pVideo_Pin
;
1297 target
= (IPin
**)&This
->pAudio_Pin
;
1299 if (*target
!= NULL
)
1301 FIXME("We already have a %s pin\n",(video
)?"video":"audio");
1305 hr
= BaseOutputPin_Construct(&QT_OutputPin_Vtbl
, sizeof(QTOutPin
), piOutput
, &output_BaseFuncTable
, &output_BaseOutputFuncTable
, &This
->filter
.csFilter
, (IPin
**)target
);
1308 QTOutPin
*pin
= (QTOutPin
*)*target
;
1309 pin
->pmt
= CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE
));
1310 CopyMediaType(pin
->pmt
, amt
);
1311 pin
->pin
.pin
.pinInfo
.pFilter
= (LPVOID
)This
;
1313 BaseFilterImpl_IncrementPinVersion((BaseFilter
*)This
);
1315 hr
= OutputQueue_Construct((BaseOutputPin
*)pin
, TRUE
, TRUE
, 5, FALSE
, THREAD_PRIORITY_NORMAL
, &output_OutputQueueFuncTable
, &pin
->queue
);
1318 ERR("Failed with error %x\n", hr
);