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
;
170 static const IPinVtbl QT_OutputPin_Vtbl
;
171 static const IPinVtbl QT_InputPin_Vtbl
;
172 static const IBaseFilterVtbl QT_Vtbl
;
174 static HRESULT
QT_AddPin(QTSplitter
*This
, const PIN_INFO
*piOutput
, const AM_MEDIA_TYPE
*amt
, BOOL video
);
175 static HRESULT
QT_RemoveOutputPins(QTSplitter
*This
);
181 static IPin
* WINAPI
QT_GetPin(BaseFilter
*iface
, int pos
)
183 QTSplitter
*This
= (QTSplitter
*)iface
;
184 TRACE("Asking for pos %x\n", pos
);
186 if (pos
> 2 || pos
< 0)
191 IPin_AddRef((IPin
*)&This
->pInputPin
);
192 return (IPin
*)&This
->pInputPin
;
194 if (This
->pVideo_Pin
)
195 IPin_AddRef((IPin
*)This
->pVideo_Pin
);
196 return (IPin
*)This
->pVideo_Pin
;
198 if (This
->pAudio_Pin
)
199 IPin_AddRef((IPin
*)This
->pAudio_Pin
);
200 return (IPin
*)This
->pAudio_Pin
;
206 static LONG WINAPI
QT_GetPinCount(BaseFilter
*iface
)
208 QTSplitter
*This
= (QTSplitter
*)iface
;
210 if (This
->pAudio_Pin
) c
++;
211 if (This
->pVideo_Pin
) c
++;
215 static const BaseFilterFuncTable BaseFuncTable
= {
220 IUnknown
* CALLBACK
QTSplitter_create(IUnknown
*punkout
, HRESULT
*phr
)
222 IUnknown
*obj
= NULL
;
225 static const WCHAR wcsInputPinName
[] = {'I','n','p','u','t',' ','P','i','n',0};
229 RegisterWineDataHandler();
231 This
= CoTaskMemAlloc(sizeof(*This
));
232 obj
= (IUnknown
*)This
;
235 *phr
= E_OUTOFMEMORY
;
238 ZeroMemory(This
,sizeof(*This
));
240 BaseFilter_Init(&This
->filter
, &QT_Vtbl
, &CLSID_QTSplitter
, (DWORD_PTR
)(__FILE__
": QTSplitter.csFilter"), &BaseFuncTable
);
242 InitializeCriticalSection(&This
->csReceive
);
243 This
->csReceive
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": QTSplitter.csReceive");
245 This
->pVideo_Pin
= NULL
;
246 This
->pAudio_Pin
= NULL
;
247 This
->state
= State_Stopped
;
248 This
->aSession
= NULL
;
249 This
->runEvent
= CreateEventW(NULL
, 0, 0, NULL
);
251 piInput
= &This
->pInputPin
.pin
.pinInfo
;
252 piInput
->dir
= PINDIR_INPUT
;
253 piInput
->pFilter
= (IBaseFilter
*)This
;
254 lstrcpynW(piInput
->achName
, wcsInputPinName
, sizeof(piInput
->achName
) / sizeof(piInput
->achName
[0]));
255 This
->pInputPin
.pin
.lpVtbl
= &QT_InputPin_Vtbl
;
256 This
->pInputPin
.pin
.refCount
= 1;
257 This
->pInputPin
.pin
.pConnectedTo
= NULL
;
258 This
->pInputPin
.pin
.pCritSec
= &This
->filter
.csFilter
;
263 static void QT_Destroy(QTSplitter
*This
)
265 IPin
*connected
= NULL
;
268 TRACE("Destroying\n");
270 /* Don't need to clean up output pins, disconnecting input pin will do that */
271 IPin_ConnectedTo((IPin
*)&This
->pInputPin
, &connected
);
274 IPin_Disconnect(connected
);
275 IPin_Release(connected
);
277 pinref
= IPin_Release((IPin
*)&This
->pInputPin
);
280 ERR("pinref should be null, is %u, destroying anyway\n", pinref
);
281 assert((LONG
)pinref
> 0);
284 pinref
= IPin_Release((IPin
*)&This
->pInputPin
);
288 DisposeMovie(This
->pQTMovie
);
290 QTVisualContextRelease(This
->vContext
);
292 MovieAudioExtractionEnd(This
->aSession
);
293 CloseHandle(This
->runEvent
);
297 This
->csReceive
.DebugInfo
->Spare
[0] = 0;
298 DeleteCriticalSection(&This
->csReceive
);
303 static HRESULT WINAPI
QT_QueryInterface(IBaseFilter
*iface
, REFIID riid
, LPVOID
*ppv
)
305 QTSplitter
*This
= (QTSplitter
*)iface
;
306 TRACE("(%s, %p)\n", debugstr_guid(riid
), ppv
);
310 if (IsEqualIID(riid
, &IID_IUnknown
))
312 else if (IsEqualIID(riid
, &IID_IPersist
))
314 else if (IsEqualIID(riid
, &IID_IMediaFilter
))
316 else if (IsEqualIID(riid
, &IID_IBaseFilter
))
321 IUnknown_AddRef((IUnknown
*)(*ppv
));
325 if (!IsEqualIID(riid
, &IID_IPin
) && !IsEqualIID(riid
, &IID_IVideoWindow
))
326 FIXME("No interface for %s!\n", debugstr_guid(riid
));
328 return E_NOINTERFACE
;
331 static ULONG WINAPI
QT_Release(IBaseFilter
*iface
)
333 QTSplitter
*This
= (QTSplitter
*)iface
;
334 ULONG refCount
= BaseFilterImpl_Release(iface
);
336 TRACE("(%p)->() Release from %d\n", This
, refCount
+ 1);
344 static HRESULT WINAPI
QT_Stop(IBaseFilter
*iface
)
346 QTSplitter
*This
= (QTSplitter
*)iface
;
350 EnterCriticalSection(&This
->csReceive
);
351 IAsyncReader_BeginFlush(This
->pInputPin
.pReader
);
352 IAsyncReader_EndFlush(This
->pInputPin
.pReader
);
353 LeaveCriticalSection(&This
->csReceive
);
358 static HRESULT WINAPI
QT_Pause(IBaseFilter
*iface
)
366 static OSErr
QT_Create_Extract_Session(QTSplitter
*filter
)
368 AudioStreamBasicDescription aDesc
;
372 pvi
= (WAVEFORMATEX
*)filter
->pAudio_Pin
->pmt
->pbFormat
;
374 err
= MovieAudioExtractionBegin(filter
->pQTMovie
, 0, &filter
->aSession
);
377 ERR("Failed to begin Extraction session %i\n",err
);
381 err
= MovieAudioExtractionGetProperty(filter
->aSession
,
382 kQTPropertyClass_MovieAudioExtraction_Audio
,
383 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription
,
384 sizeof(AudioStreamBasicDescription
), &aDesc
, NULL
);
388 MovieAudioExtractionEnd(filter
->aSession
);
389 filter
->aSession
= NULL
;
390 ERR("Failed to get session description %i\n",err
);
394 aDesc
.mFormatID
= kAudioFormatLinearPCM
;
395 aDesc
.mFormatFlags
= kLinearPCMFormatFlagIsSignedInteger
+
396 kAudioFormatFlagIsPacked
;
397 aDesc
.mFramesPerPacket
= 1;
398 aDesc
.mChannelsPerFrame
= pvi
->nChannels
;
399 aDesc
.mBitsPerChannel
= pvi
->wBitsPerSample
;
400 aDesc
.mSampleRate
= pvi
->nSamplesPerSec
;
401 aDesc
.mBytesPerFrame
= (aDesc
.mBitsPerChannel
* aDesc
.mChannelsPerFrame
) / 8;
402 aDesc
.mBytesPerPacket
= aDesc
.mBytesPerFrame
* aDesc
.mFramesPerPacket
;
404 err
= MovieAudioExtractionSetProperty(filter
->aSession
,
405 kQTPropertyClass_MovieAudioExtraction_Audio
,
406 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription
,
407 sizeof(AudioStreamBasicDescription
), &aDesc
);
409 if (aDesc
.mFormatID
!= kAudioFormatLinearPCM
)
411 ERR("Not PCM Wave\n");
414 if (aDesc
.mFormatFlags
!= kLinearPCMFormatFlagIsSignedInteger
+
415 kAudioFormatFlagIsPacked
)
417 ERR("Unhandled Flags\n");
420 if (aDesc
.mFramesPerPacket
!= 1)
422 ERR("Unhandled Frames per packet %li\n",aDesc
.mFramesPerPacket
);
425 if (aDesc
.mChannelsPerFrame
!= pvi
->nChannels
)
427 ERR("Unhandled channel count %li\n",aDesc
.mChannelsPerFrame
);
430 if (aDesc
.mBitsPerChannel
!= pvi
->wBitsPerSample
)
432 ERR("Unhandled bits per channel %li\n",aDesc
.mBitsPerChannel
);
435 if (aDesc
.mSampleRate
!= pvi
->nSamplesPerSec
)
437 ERR("Unhandled sample rate %f\n",aDesc
.mSampleRate
);
443 ERR("Failed to create Extraction Session\n");
444 MovieAudioExtractionEnd(filter
->aSession
);
445 filter
->aSession
= NULL
;
451 static DWORD WINAPI
QTSplitter_thread(LPVOID data
)
453 QTSplitter
*This
= (QTSplitter
*)data
;
455 TimeValue movie_time
=0, next_time
;
456 CVPixelBufferRef pixelBuffer
= NULL
;
460 if (This
->pAudio_Pin
)
462 /* according to QA1469 a movie has to be fully loaded before we
463 can reliably start the Extraction session */
465 while(GetMovieLoadState(This
->pQTMovie
) < kMovieLoadStateComplete
)
466 MoviesTask(This
->pQTMovie
,1000);
468 QT_Create_Extract_Session(This
);
471 WaitForSingleObject(This
->runEvent
, -1);
473 This
->state
= State_Running
;
474 /* Prime the pump: Needed for MPEG streams */
475 GetMovieNextInterestingTime(This
->pQTMovie
, nextTimeEdgeOK
| nextTimeStep
, 0, NULL
, movie_time
, 1, &next_time
, NULL
);
477 GetMovieTime(This
->pQTMovie
, &tr
);
480 LONGLONG tStart
=0, tStop
=0;
481 LONGLONG mStart
=0, mStop
=0;
484 EnterCriticalSection(&This
->csReceive
);
485 GetMovieNextInterestingTime(This
->pQTMovie
, nextTimeStep
, 0, NULL
, movie_time
, 1, &next_time
, NULL
);
489 TRACE("No next time\n");
490 LeaveCriticalSection(&This
->csReceive
);
494 tr
.value
= SInt64ToWide(next_time
);
495 SetMovieTime(This
->pQTMovie
, &tr
);
496 MoviesTask(This
->pQTMovie
,0);
497 QTVisualContextTask(This
->vContext
);
499 TRACE("In loop at time %ld\n",movie_time
);
500 TRACE("In Next time %ld\n",next_time
);
505 time
= (float)movie_time
/ tr
.scale
;
506 tStart
= time
* 10000000;
507 time
= (float)next_time
/ tr
.scale
;
508 tStop
= time
* 10000000;
511 if (This
->pAudio_Pin
&& ((BaseOutputPin
*)This
->pAudio_Pin
)->pin
.pConnectedTo
&& This
->aSession
)
515 IMediaSample
*sample
= NULL
;
516 AudioBufferList aData
;
522 pvi
= (WAVEFORMATEX
*)This
->pAudio_Pin
->pmt
->pbFormat
;
524 hr
= BaseOutputPinImpl_GetDeliveryBuffer((BaseOutputPin
*)This
->pAudio_Pin
, &sample
, NULL
, NULL
, 0);
528 ERR("Audio: Unable to get delivery buffer (%x)\n", hr
);
532 hr
= IMediaSample_GetPointer(sample
, &ptr
);
535 ERR("Audio: Unable to get pointer to buffer (%x)\n", hr
);
539 duration
= (float)next_time
/ tr
.scale
;
540 time
= (float)movie_time
/ tr
.scale
;
542 frames
= pvi
->nSamplesPerSec
* duration
;
543 TRACE("Need audio for %f seconds (%li frames)\n",duration
,frames
);
545 data_size
= IMediaSample_GetSize(sample
);
546 if (data_size
< frames
* pvi
->nBlockAlign
)
547 FIXME("Audio buffer is too small\n");
549 aData
.mNumberBuffers
= 1;
550 aData
.mBuffers
[0].mNumberChannels
= pvi
->nChannels
;
551 aData
.mBuffers
[0].mDataByteSize
= data_size
;
552 aData
.mBuffers
[0].mData
= ptr
;
554 err
= MovieAudioExtractionFillBuffer(This
->aSession
, &frames
, &aData
, &flags
);
555 TRACE("Got %i frames\n",(int)frames
);
557 IMediaSample_SetActualDataLength(sample
, frames
* pvi
->nBlockAlign
);
559 IMediaSample_SetMediaTime(sample
, &mStart
, &mStop
);
560 IMediaSample_SetTime(sample
, &tStart
, &tStop
);
562 hr
= OutputQueue_Receive(This
->pAudio_Pin
->queue
, sample
);
563 TRACE("Audio Delivered (%x)\n",hr
);
567 IMediaSample_Release(sample
);
570 TRACE("Audio Pin not connected or no Audio\n");
573 if (This
->pVideo_Pin
&& QTVisualContextIsNewImageAvailable(This
->vContext
,0))
575 err
= QTVisualContextCopyImageForTime(This
->vContext
, NULL
, NULL
, &pixelBuffer
);
580 IMediaSample
*sample
= NULL
;
582 hr
= BaseOutputPinImpl_GetDeliveryBuffer((BaseOutputPin
*)This
->pVideo_Pin
, &sample
, NULL
, NULL
, 0);
585 ERR("Video: Unable to get delivery buffer (%x)\n", hr
);
589 data_size
= IMediaSample_GetSize(sample
);
590 if (data_size
< This
->outputSize
)
592 ERR("Sample size is too small %d < %d\n", data_size
, This
->outputSize
)
598 hr
= IMediaSample_GetPointer(sample
, &ptr
);
601 ERR("Video: Unable to get pointer to buffer (%x)\n", hr
);
605 hr
= AccessPixelBufferPixels( pixelBuffer
, ptr
);
608 ERR("Failed to access Pixels\n");
612 IMediaSample_SetActualDataLength(sample
, This
->outputSize
);
614 IMediaSample_SetMediaTime(sample
, &mStart
, &mStop
);
615 IMediaSample_SetTime(sample
, &tStart
, &tStop
);
617 hr
= OutputQueue_Receive(This
->pVideo_Pin
->queue
, sample
);
618 TRACE("Video Delivered (%x)\n",hr
);
622 IMediaSample_Release(sample
);
624 CVPixelBufferRelease(pixelBuffer
);
628 TRACE("No video to deliver\n");
630 movie_time
= next_time
;
631 LeaveCriticalSection(&This
->csReceive
);
632 } while (hr
== S_OK
);
634 This
->state
= State_Stopped
;
635 if (This
->pAudio_Pin
)
636 OutputQueue_EOS(This
->pAudio_Pin
->queue
);
637 if (This
->pVideo_Pin
)
638 OutputQueue_EOS(This
->pVideo_Pin
->queue
);
643 static HRESULT WINAPI
QT_Run(IBaseFilter
*iface
, REFERENCE_TIME tStart
)
646 QTSplitter
*This
= (QTSplitter
*)iface
;
647 HRESULT hr_any
= VFW_E_NOT_CONNECTED
;
649 TRACE("(%s)\n", wine_dbgstr_longlong(tStart
));
651 EnterCriticalSection(&This
->csReceive
);
652 This
->filter
.rtStreamStart
= tStart
;
654 if (This
->pVideo_Pin
)
655 hr
= BaseOutputPinImpl_Active((BaseOutputPin
*)This
->pVideo_Pin
);
658 if (This
->pAudio_Pin
)
659 hr
= BaseOutputPinImpl_Active((BaseOutputPin
*)This
->pAudio_Pin
);
665 SetEvent(This
->runEvent
);
666 LeaveCriticalSection(&This
->csReceive
);
671 static HRESULT WINAPI
QT_GetState(IBaseFilter
*iface
, DWORD dwMilliSecsTimeout
, FILTER_STATE
*pState
)
673 QTSplitter
*This
= (QTSplitter
*)iface
;
674 TRACE("(%d, %p)\n", dwMilliSecsTimeout
, pState
);
676 *pState
= This
->state
;
681 static HRESULT WINAPI
QT_FindPin(IBaseFilter
*iface
, LPCWSTR Id
, IPin
**ppPin
)
683 FIXME("(%p)->(%s,%p) stub\n", iface
, debugstr_w(Id
), ppPin
);
687 static const IBaseFilterVtbl QT_Vtbl
= {
689 BaseFilterImpl_AddRef
,
691 BaseFilterImpl_GetClassID
,
696 BaseFilterImpl_SetSyncSource
,
697 BaseFilterImpl_GetSyncSource
,
698 BaseFilterImpl_EnumPins
,
700 BaseFilterImpl_QueryFilterInfo
,
701 BaseFilterImpl_JoinFilterGraph
,
702 BaseFilterImpl_QueryVendorInfo
708 static HRESULT
QT_RemoveOutputPins(QTSplitter
*This
)
711 TRACE("(%p)\n", This
);
713 if (This
->pVideo_Pin
)
715 hr
= BaseOutputPinImpl_BreakConnect(&This
->pVideo_Pin
->pin
);
716 TRACE("Disconnect: %08x\n", hr
);
717 IPin_Release((IPin
*)This
->pVideo_Pin
);
718 This
->pVideo_Pin
= NULL
;
720 if (This
->pAudio_Pin
)
722 hr
= BaseOutputPinImpl_BreakConnect(&This
->pAudio_Pin
->pin
);
723 TRACE("Disconnect: %08x\n", hr
);
724 IPin_Release((IPin
*)This
->pAudio_Pin
);
725 This
->pAudio_Pin
= NULL
;
728 BaseFilterImpl_IncrementPinVersion((BaseFilter
*)This
);
732 static ULONG WINAPI
QTInPin_Release(IPin
*iface
)
734 QTInPin
*This
= (QTInPin
*)iface
;
735 ULONG refCount
= InterlockedDecrement(&This
->pin
.refCount
);
737 TRACE("(%p)->() Release from %d\n", iface
, refCount
+ 1);
740 FreeMediaType(&This
->pin
.mtCurrent
);
742 IMemAllocator_Release(This
->pAlloc
);
744 This
->pin
.lpVtbl
= NULL
;
751 static HRESULT
QT_Process_Video_Track(QTSplitter
* filter
, Track trk
)
754 VIDEOINFOHEADER
* pvi
;
758 static const WCHAR szwVideoOut
[] = {'V','i','d','e','o',0};
759 CFMutableDictionaryRef pixelBufferOptions
= NULL
;
760 CFMutableDictionaryRef visualContextOptions
= NULL
;
761 CFNumberRef n
= NULL
;
763 DWORD outputWidth
, outputHeight
, outputDepth
;
764 Fixed trackWidth
, trackHeight
;
766 ZeroMemory(&amt
, sizeof(amt
));
767 amt
.formattype
= FORMAT_VideoInfo
;
768 amt
.majortype
= MEDIATYPE_Video
;
769 amt
.subtype
= MEDIASUBTYPE_RGB24
;
771 GetTrackDimensions(trk
, &trackWidth
, &trackHeight
);
774 outputWidth
= Fix2Long(trackWidth
);
775 outputHeight
= Fix2Long(trackHeight
);
776 TRACE("Width %i Height %i\n",outputWidth
, outputHeight
);
778 amt
.cbFormat
= sizeof(VIDEOINFOHEADER
);
779 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
780 ZeroMemory(amt
.pbFormat
, amt
.cbFormat
);
781 pvi
= (VIDEOINFOHEADER
*)amt
.pbFormat
;
782 pvi
->bmiHeader
.biSize
= sizeof (BITMAPINFOHEADER
);
783 pvi
->bmiHeader
.biWidth
= outputWidth
;
784 pvi
->bmiHeader
.biHeight
= outputHeight
;
785 pvi
->bmiHeader
.biPlanes
= 1;
786 pvi
->bmiHeader
.biBitCount
= 24;
787 pvi
->bmiHeader
.biCompression
= BI_RGB
;
788 pvi
->bmiHeader
.biSizeImage
= outputWidth
* outputHeight
* outputDepth
;
790 filter
->outputSize
= pvi
->bmiHeader
.biSizeImage
;
793 pixelBufferOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
795 t
= k32ARGBPixelFormat
;
796 n
= CFNumberCreate(NULL
, kCFNumberIntType
, &t
);
797 CFDictionaryAddValue(pixelBufferOptions
, kCVPixelBufferPixelFormatTypeKey
, n
);
800 n
= CFNumberCreate(NULL
, kCFNumberIntType
, &outputWidth
);
801 CFDictionaryAddValue(pixelBufferOptions
, kCVPixelBufferWidthKey
, n
);
804 n
= CFNumberCreate(NULL
, kCFNumberIntType
, &outputHeight
);
805 CFDictionaryAddValue(pixelBufferOptions
, kCVPixelBufferHeightKey
, n
);
809 n
= CFNumberCreate(NULL
, kCFNumberIntType
, &t
);
810 CFDictionaryAddValue(pixelBufferOptions
, kCVPixelBufferBytesPerRowAlignmentKey
, n
);
813 visualContextOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
815 CFDictionarySetValue(visualContextOptions
, kQTVisualContextPixelBufferAttributesKey
, pixelBufferOptions
);
817 err
= QTPixelBufferContextCreate(NULL
, visualContextOptions
,&filter
->vContext
);
818 CFRelease(pixelBufferOptions
);
819 CFRelease(visualContextOptions
);
822 ERR("Failed to create Visual Context\n");
826 err
= SetMovieVisualContext(filter
->pQTMovie
, filter
->vContext
);
829 ERR("Failed to set Visual Context\n");
833 piOutput
.dir
= PINDIR_OUTPUT
;
834 piOutput
.pFilter
= (IBaseFilter
*)filter
;
835 lstrcpyW(piOutput
.achName
,szwVideoOut
);
837 hr
= QT_AddPin(filter
, &piOutput
, &amt
, TRUE
);
839 ERR("Failed to add Video Track\n");
841 TRACE("Video Pin %p\n",filter
->pVideo_Pin
);
846 static HRESULT
QT_Process_Audio_Track(QTSplitter
* filter
, Track trk
)
852 static const WCHAR szwAudioOut
[] = {'A','u','d','i','o',0};
855 SoundDescriptionHandle aDesc
= (SoundDescriptionHandle
) NewHandle(sizeof(SoundDescription
));
857 audioMedia
= GetTrackMedia(trk
);
858 GetMediaSampleDescription(audioMedia
, 1, (SampleDescriptionHandle
)aDesc
);
860 ZeroMemory(&amt
, sizeof(amt
));
861 amt
.formattype
= FORMAT_WaveFormatEx
;
862 amt
.majortype
= MEDIATYPE_Audio
;
863 amt
.subtype
= MEDIASUBTYPE_PCM
;
864 amt
.bTemporalCompression
= 0;
866 amt
.cbFormat
= sizeof(WAVEFORMATEX
);
867 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
868 ZeroMemory(amt
.pbFormat
, amt
.cbFormat
);
869 pvi
= (WAVEFORMATEX
*)amt
.pbFormat
;
871 pvi
->cbSize
= sizeof(WAVEFORMATEX
);
872 pvi
->wFormatTag
= WAVE_FORMAT_PCM
;
873 pvi
->nChannels
= ((SoundDescription
)**aDesc
).numChannels
;
874 if (pvi
->nChannels
< 1 || pvi
->nChannels
> 2)
876 pvi
->nSamplesPerSec
= (((SoundDescription
)**aDesc
).sampleRate
/65536);
877 if (pvi
->nSamplesPerSec
< 8000 || pvi
->nChannels
> 48000)
878 pvi
->nSamplesPerSec
= 44100;
879 pvi
->wBitsPerSample
= ((SoundDescription
)**aDesc
).sampleSize
;
880 if (pvi
->wBitsPerSample
< 8 || pvi
->wBitsPerSample
> 32)
881 pvi
->wBitsPerSample
= 16;
882 pvi
->nBlockAlign
= (pvi
->nChannels
* pvi
->wBitsPerSample
) / 8;
883 pvi
->nAvgBytesPerSec
= pvi
->nSamplesPerSec
* pvi
->nBlockAlign
;
885 DisposeHandle((Handle
)aDesc
);
887 piOutput
.dir
= PINDIR_OUTPUT
;
888 piOutput
.pFilter
= (IBaseFilter
*)filter
;
889 lstrcpyW(piOutput
.achName
,szwAudioOut
);
891 hr
= QT_AddPin(filter
, &piOutput
, &amt
, FALSE
);
893 ERR("Failed to add Audio Track\n");
895 TRACE("Audio Pin %p\n",filter
->pAudio_Pin
);
899 static HRESULT
QT_Process_Movie(QTSplitter
* filter
)
903 WineDataRefRecord ptrDataRefRec
;
904 Handle dataRef
= NULL
;
910 TRACE("Trying movie connect\n");
912 ptrDataRefRec
.pReader
= filter
->pInputPin
.pReader
;
913 ptrDataRefRec
.streamSubtype
= filter
->pInputPin
.subType
;
914 PtrToHand( &ptrDataRefRec
, &dataRef
, sizeof(WineDataRefRecord
));
916 err
= NewMovieFromDataRef(&filter
->pQTMovie
, newMovieActive
|newMovieDontInteractWithUser
|newMovieDontAutoUpdateClock
|newMovieDontAskUnresolvedDataRefs
|newMovieAsyncOK
, &id
, dataRef
, 'WINE');
918 DisposeHandle(dataRef
);
922 FIXME("QuickTime cannot handle media type(%i)\n",err
);
923 return VFW_E_TYPE_NOT_ACCEPTED
;
926 PrePrerollMovie(filter
->pQTMovie
, 0, fixed1
, NULL
, NULL
);
927 PrerollMovie(filter
->pQTMovie
, 0, fixed1
);
928 GoToBeginningOfMovie(filter
->pQTMovie
);
929 SetMovieActive(filter
->pQTMovie
,TRUE
);
931 if (GetMovieLoadState(filter
->pQTMovie
) < kMovieLoadStateLoaded
)
932 MoviesTask(filter
->pQTMovie
,100);
934 trk
= GetMovieIndTrackType(filter
->pQTMovie
, 1, VisualMediaCharacteristic
, movieTrackCharacteristic
| movieTrackEnabledOnly
);
935 TRACE("%p is a video track\n",trk
);
937 hr
= QT_Process_Video_Track(filter
, trk
);
942 trk
= GetMovieIndTrackType(filter
->pQTMovie
, 1, AudioMediaCharacteristic
, movieTrackCharacteristic
| movieTrackEnabledOnly
);
943 TRACE("%p is a audio track\n",trk
);
945 hr
= QT_Process_Audio_Track(filter
, trk
);
947 thread
= CreateThread(NULL
, 0, QTSplitter_thread
, filter
, 0, &tid
);
950 TRACE("Created thread 0x%08x\n", tid
);
954 hr
= HRESULT_FROM_WIN32(GetLastError());
959 static HRESULT WINAPI
QTInPin_ReceiveConnection(IPin
*iface
, IPin
*pReceivePin
, const AM_MEDIA_TYPE
*pmt
)
962 ALLOCATOR_PROPERTIES props
;
963 QTInPin
*This
= (QTInPin
*)iface
;
965 TRACE("(%p/%p)->(%p, %p)\n", This
, iface
, pReceivePin
, pmt
);
967 EnterCriticalSection(This
->pin
.pCritSec
);
968 This
->pReader
= NULL
;
970 if (This
->pin
.pConnectedTo
)
971 hr
= VFW_E_ALREADY_CONNECTED
;
972 else if (IPin_QueryAccept(iface
, pmt
) != S_OK
)
973 hr
= VFW_E_TYPE_NOT_ACCEPTED
;
976 PIN_DIRECTION pindirReceive
;
977 IPin_QueryDirection(pReceivePin
, &pindirReceive
);
978 if (pindirReceive
!= PINDIR_OUTPUT
)
979 hr
= VFW_E_INVALID_DIRECTION
;
984 LeaveCriticalSection(This
->pin
.pCritSec
);
988 hr
= IPin_QueryInterface(pReceivePin
, &IID_IAsyncReader
, (LPVOID
*)&This
->pReader
);
991 LeaveCriticalSection(This
->pin
.pCritSec
);
992 TRACE("Input source is not an AsyncReader\n");
996 LeaveCriticalSection(This
->pin
.pCritSec
);
997 EnterCriticalSection(&((QTSplitter
*)This
->pin
.pinInfo
.pFilter
)->filter
.csFilter
);
998 hr
= QT_Process_Movie((QTSplitter
*)This
->pin
.pinInfo
.pFilter
);
1001 LeaveCriticalSection(&((QTSplitter
*)This
->pin
.pinInfo
.pFilter
)->filter
.csFilter
);
1002 TRACE("Unable to process movie\n");
1006 This
->pAlloc
= NULL
;
1009 props
.cbBuffer
= ((QTSplitter
*)This
->pin
.pinInfo
.pFilter
)->outputSize
+ props
.cbAlign
;
1012 hr
= IAsyncReader_RequestAllocator(This
->pReader
, NULL
, &props
, &This
->pAlloc
);
1015 CopyMediaType(&This
->pin
.mtCurrent
, pmt
);
1016 This
->pin
.pConnectedTo
= pReceivePin
;
1017 IPin_AddRef(pReceivePin
);
1018 hr
= IMemAllocator_Commit(This
->pAlloc
);
1022 QT_RemoveOutputPins((QTSplitter
*)This
->pin
.pinInfo
.pFilter
);
1024 IAsyncReader_Release(This
->pReader
);
1025 This
->pReader
= NULL
;
1027 IMemAllocator_Release(This
->pAlloc
);
1028 This
->pAlloc
= NULL
;
1030 TRACE("Size: %i\n", props
.cbBuffer
);
1031 LeaveCriticalSection(&((QTSplitter
*)This
->pin
.pinInfo
.pFilter
)->filter
.csFilter
);
1036 static HRESULT WINAPI
QTInPin_Disconnect(IPin
*iface
)
1039 QTInPin
*This
= (QTInPin
*)iface
;
1043 hr
= IBaseFilter_GetState(This
->pin
.pinInfo
.pFilter
, INFINITE
, &state
);
1044 EnterCriticalSection(This
->pin
.pCritSec
);
1045 if (This
->pin
.pConnectedTo
)
1047 QTSplitter
*Parser
= (QTSplitter
*)This
->pin
.pinInfo
.pFilter
;
1049 if (SUCCEEDED(hr
) && state
== State_Stopped
)
1051 IMemAllocator_Decommit(This
->pAlloc
);
1052 IPin_Disconnect(This
->pin
.pConnectedTo
);
1053 This
->pin
.pConnectedTo
= NULL
;
1054 hr
= QT_RemoveOutputPins(Parser
);
1057 hr
= VFW_E_NOT_STOPPED
;
1061 LeaveCriticalSection(This
->pin
.pCritSec
);
1065 static HRESULT WINAPI
QTInPin_QueryAccept(IPin
*iface
, const AM_MEDIA_TYPE
*pmt
)
1067 QTInPin
*This
= (QTInPin
*)iface
;
1069 TRACE("(%p)->(%p)\n", This
, pmt
);
1071 if (IsEqualIID(&pmt
->majortype
, &MEDIATYPE_Stream
))
1073 This
->subType
= pmt
->subtype
;
1079 static HRESULT WINAPI
QTInPin_EndOfStream(IPin
*iface
)
1081 QTInPin
*pin
= (QTInPin
*)iface
;
1082 QTSplitter
*This
= (QTSplitter
*)pin
->pin
.pinInfo
.pFilter
;
1084 FIXME("Propagate message on %p\n", This
);
1088 static HRESULT WINAPI
QTInPin_BeginFlush(IPin
*iface
)
1090 QTInPin
*pin
= (QTInPin
*)iface
;
1091 QTSplitter
*This
= (QTSplitter
*)pin
->pin
.pinInfo
.pFilter
;
1093 FIXME("Propagate message on %p\n", This
);
1097 static HRESULT WINAPI
QTInPin_EndFlush(IPin
*iface
)
1099 QTInPin
*pin
= (QTInPin
*)iface
;
1100 QTSplitter
*This
= (QTSplitter
*)pin
->pin
.pinInfo
.pFilter
;
1102 FIXME("Propagate message on %p\n", This
);
1106 static HRESULT WINAPI
QTInPin_NewSegment(IPin
*iface
, REFERENCE_TIME tStart
, REFERENCE_TIME tStop
, double dRate
)
1108 QTInPin
*pin
= (QTInPin
*)iface
;
1109 QTSplitter
*This
= (QTSplitter
*)pin
->pin
.pinInfo
.pFilter
;
1111 BasePinImpl_NewSegment(iface
, tStart
, tStop
, dRate
);
1112 FIXME("Propagate message on %p\n", This
);
1116 static HRESULT WINAPI
QTInPin_QueryInterface(IPin
* iface
, REFIID riid
, LPVOID
* ppv
)
1118 QTInPin
*This
= (QTInPin
*)iface
;
1120 TRACE("(%p/%p)->(%s, %p)\n", This
, iface
, debugstr_guid(riid
), ppv
);
1124 if (IsEqualIID(riid
, &IID_IUnknown
))
1126 else if (IsEqualIID(riid
, &IID_IPin
))
1128 else if (IsEqualIID(riid
, &IID_IMediaSeeking
))
1130 return IBaseFilter_QueryInterface(This
->pin
.pinInfo
.pFilter
, &IID_IMediaSeeking
, ppv
);
1135 IUnknown_AddRef((IUnknown
*)(*ppv
));
1139 FIXME("No interface for %s!\n", debugstr_guid(riid
));
1141 return E_NOINTERFACE
;
1144 static HRESULT WINAPI
QTInPin_EnumMediaTypes(IPin
*iface
, IEnumMediaTypes
**ppEnum
)
1146 BasePin
*This
= (BasePin
*)iface
;
1148 TRACE("(%p/%p)->(%p)\n", This
, iface
, ppEnum
);
1150 return EnumMediaTypes_Construct(This
, BasePinImpl_GetMediaType
, BasePinImpl_GetMediaTypeVersion
, ppEnum
);
1153 static const IPinVtbl QT_InputPin_Vtbl
= {
1154 QTInPin_QueryInterface
,
1157 BaseInputPinImpl_Connect
,
1158 QTInPin_ReceiveConnection
,
1160 BasePinImpl_ConnectedTo
,
1161 BasePinImpl_ConnectionMediaType
,
1162 BasePinImpl_QueryPinInfo
,
1163 BasePinImpl_QueryDirection
,
1164 BasePinImpl_QueryId
,
1165 QTInPin_QueryAccept
,
1166 QTInPin_EnumMediaTypes
,
1167 BasePinImpl_QueryInternalConnections
,
1168 QTInPin_EndOfStream
,
1178 static HRESULT WINAPI
QTOutPin_QueryInterface(IPin
*iface
, REFIID riid
, void **ppv
)
1180 QTOutPin
*This
= (QTOutPin
*)iface
;
1182 TRACE("(%s, %p)\n", debugstr_guid(riid
), ppv
);
1186 if (IsEqualIID(riid
, &IID_IUnknown
))
1188 else if (IsEqualIID(riid
, &IID_IPin
))
1190 else if (IsEqualIID(riid
, &IID_IMediaSeeking
))
1191 return IBaseFilter_QueryInterface(This
->pin
.pin
.pinInfo
.pFilter
, &IID_IMediaSeeking
, ppv
);
1195 IUnknown_AddRef((IUnknown
*)(*ppv
));
1198 FIXME("No interface for %s!\n", debugstr_guid(riid
));
1199 return E_NOINTERFACE
;
1202 static ULONG WINAPI
QTOutPin_Release(IPin
*iface
)
1204 QTOutPin
*This
= (QTOutPin
*)iface
;
1205 ULONG refCount
= InterlockedDecrement(&This
->pin
.pin
.refCount
);
1206 TRACE("(%p)->() Release from %d\n", iface
, refCount
+ 1);
1210 DeleteMediaType(This
->pmt
);
1211 FreeMediaType(&This
->pin
.pin
.mtCurrent
);
1212 OutputQueue_Destroy(This
->queue
);
1213 CoTaskMemFree(This
);
1219 static HRESULT WINAPI
QTOutPin_GetMediaType(BasePin
*iface
, int iPosition
, AM_MEDIA_TYPE
*pmt
)
1221 QTOutPin
*This
= (QTOutPin
*)iface
;
1224 return E_INVALIDARG
;
1226 return VFW_S_NO_MORE_ITEMS
;
1227 CopyMediaType(pmt
, This
->pmt
);
1231 static HRESULT WINAPI
QTOutPin_DecideBufferSize(BaseOutputPin
*iface
, IMemAllocator
*pAlloc
, ALLOCATOR_PROPERTIES
*ppropInputRequest
)
1237 static HRESULT WINAPI
QTOutPin_DecideAllocator(BaseOutputPin
*iface
, IMemInputPin
*pPin
, IMemAllocator
**pAlloc
)
1240 QTOutPin
*This
= (QTOutPin
*)iface
;
1241 QTSplitter
*QTfilter
= (QTSplitter
*)This
->pin
.pin
.pinInfo
.pFilter
;
1244 if (QTfilter
->pInputPin
.pAlloc
)
1245 hr
= IMemInputPin_NotifyAllocator(pPin
, QTfilter
->pInputPin
.pAlloc
, FALSE
);
1247 hr
= VFW_E_NO_ALLOCATOR
;
1252 static HRESULT WINAPI
QTOutPin_BreakConnect(BaseOutputPin
*This
)
1256 TRACE("(%p)->()\n", This
);
1258 EnterCriticalSection(This
->pin
.pCritSec
);
1259 if (!This
->pin
.pConnectedTo
|| !This
->pMemInputPin
)
1260 hr
= VFW_E_NOT_CONNECTED
;
1263 hr
= IPin_Disconnect(This
->pin
.pConnectedTo
);
1264 IPin_Disconnect((IPin
*)This
);
1266 LeaveCriticalSection(This
->pin
.pCritSec
);
1271 static const IPinVtbl QT_OutputPin_Vtbl
= {
1272 QTOutPin_QueryInterface
,
1275 BaseOutputPinImpl_Connect
,
1276 BaseOutputPinImpl_ReceiveConnection
,
1277 BaseOutputPinImpl_Disconnect
,
1278 BasePinImpl_ConnectedTo
,
1279 BasePinImpl_ConnectionMediaType
,
1280 BasePinImpl_QueryPinInfo
,
1281 BasePinImpl_QueryDirection
,
1282 BasePinImpl_QueryId
,
1283 BasePinImpl_QueryAccept
,
1284 BasePinImpl_EnumMediaTypes
,
1285 BasePinImpl_QueryInternalConnections
,
1286 BaseOutputPinImpl_EndOfStream
,
1287 BaseOutputPinImpl_BeginFlush
,
1288 BaseOutputPinImpl_EndFlush
,
1289 BasePinImpl_NewSegment
1292 static const BasePinFuncTable output_BaseFuncTable
= {
1294 BaseOutputPinImpl_AttemptConnection
,
1295 BasePinImpl_GetMediaTypeVersion
,
1296 QTOutPin_GetMediaType
1299 static const BaseOutputPinFuncTable output_BaseOutputFuncTable
= {
1300 QTOutPin_DecideBufferSize
,
1301 QTOutPin_DecideAllocator
,
1302 QTOutPin_BreakConnect
1305 static const OutputQueueFuncTable output_OutputQueueFuncTable
= {
1306 OutputQueueImpl_ThreadProc
1309 static HRESULT
QT_AddPin(QTSplitter
*This
, const PIN_INFO
*piOutput
, const AM_MEDIA_TYPE
*amt
, BOOL video
)
1315 target
= (IPin
**)&This
->pVideo_Pin
;
1317 target
= (IPin
**)&This
->pAudio_Pin
;
1319 if (*target
!= NULL
)
1321 FIXME("We already have a %s pin\n",(video
)?"video":"audio");
1325 hr
= BaseOutputPin_Construct(&QT_OutputPin_Vtbl
, sizeof(QTOutPin
), piOutput
, &output_BaseFuncTable
, &output_BaseOutputFuncTable
, &This
->filter
.csFilter
, (IPin
**)target
);
1328 QTOutPin
*pin
= (QTOutPin
*)*target
;
1329 pin
->pmt
= CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE
));
1330 CopyMediaType(pin
->pmt
, amt
);
1331 pin
->pin
.pin
.pinInfo
.pFilter
= (LPVOID
)This
;
1333 BaseFilterImpl_IncrementPinVersion((BaseFilter
*)This
);
1335 hr
= OutputQueue_Construct((BaseOutputPin
*)pin
, TRUE
, TRUE
, 5, FALSE
, THREAD_PRIORITY_NORMAL
, &output_OutputQueueFuncTable
, &pin
->queue
);
1338 ERR("Failed with error %x\n", hr
);