2 * QuickTime splitter + decoder
4 * Copyright 2011 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #define ULONG CoreFoundation_ULONG
24 #define HRESULT CoreFoundation_HRESULT
26 #define LoadResource __carbon_LoadResource
27 #define CompareString __carbon_CompareString
28 #define GetCurrentThread __carbon_GetCurrentThread
29 #define GetCurrentProcess __carbon_GetCurrentProcess
30 #define AnimatePalette __carbon_AnimatePalette
31 #define EqualRgn __carbon_EqualRgn
32 #define FillRgn __carbon_FillRgn
33 #define FrameRgn __carbon_FrameRgn
34 #define GetPixel __carbon_GetPixel
35 #define InvertRgn __carbon_InvertRgn
36 #define LineTo __carbon_LineTo
37 #define OffsetRgn __carbon_OffsetRgn
38 #define PaintRgn __carbon_PaintRgn
39 #define Polygon __carbon_Polygon
40 #define ResizePalette __carbon_ResizePalette
41 #define SetRectRgn __carbon_SetRectRgn
43 #define CheckMenuItem __carbon_CheckMenuItem
44 #define DeleteMenu __carbon_DeleteMenu
45 #define DrawMenuBar __carbon_DrawMenuBar
46 #define EnableMenuItem __carbon_EnableMenuItem
47 #define EqualRect __carbon_EqualRect
48 #define FillRect __carbon_FillRect
49 #define FrameRect __carbon_FrameRect
50 #define GetCursor __carbon_GetCursor
51 #define GetMenu __carbon_GetMenu
52 #define InvertRect __carbon_InvertRect
53 #define IsWindowVisible __carbon_IsWindowVisible
54 #define MoveWindow __carbon_MoveWindow
55 #define OffsetRect __carbon_OffsetRect
56 #define PtInRect __carbon_PtInRect
57 #define SetCursor __carbon_SetCursor
58 #define SetRect __carbon_SetRect
59 #define ShowCursor __carbon_ShowCursor
60 #define ShowWindow __carbon_ShowWindow
61 #define UnionRect __carbon_UnionRect
63 #include <QuickTime/Movies.h>
64 #include <QuickTime/QuickTimeComponents.h>
68 #undef GetCurrentThread
71 #undef GetCurrentProcess
94 #undef IsWindowVisible
107 #undef STDMETHODCALLTYPE
113 #define NONAMELESSSTRUCT
114 #define NONAMELESSUNION
125 #include "wine/unicode.h"
126 #include "wine/debug.h"
127 #include "wine/strmbase.h"
129 #include "qtprivate.h"
131 WINE_DEFAULT_DEBUG_CHANNEL(qtsplitter);
132 extern CLSID CLSID_QTSplitter;
134 typedef struct QTOutPin {
141 typedef struct QTInPin {
145 IAsyncReader *pReader;
146 IMemAllocator *pAlloc;
149 typedef struct QTSplitter {
153 QTOutPin *pVideo_Pin;
154 QTOutPin *pAudio_Pin;
156 ALLOCATOR_PROPERTIES props;
159 QTVisualContextRef vContext;
161 MovieAudioExtractionRef aSession;
166 CRITICAL_SECTION csReceive;
168 SourceSeeking sourceSeeking;
169 TimeValue movie_time;
170 TimeValue movie_start;
171 TimeScale movie_scale;
174 static const IPinVtbl QT_OutputPin_Vtbl;
175 static const IPinVtbl QT_InputPin_Vtbl;
176 static const IBaseFilterVtbl QT_Vtbl;
177 static const IMediaSeekingVtbl QT_Seeking_Vtbl;
179 static HRESULT QT_AddPin(QTSplitter *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt, BOOL video);
180 static HRESULT QT_RemoveOutputPins(QTSplitter *This);
182 static HRESULT WINAPI QTSplitter_ChangeStart(IMediaSeeking *iface);
183 static HRESULT WINAPI QTSplitter_ChangeStop(IMediaSeeking *iface);
184 static HRESULT WINAPI QTSplitter_ChangeRate(IMediaSeeking *iface);
186 static inline QTSplitter *impl_from_IMediaSeeking( IMediaSeeking *iface )
188 return CONTAINING_RECORD(iface, QTSplitter, sourceSeeking.IMediaSeeking_iface);
191 static inline QTSplitter *impl_from_BaseFilter( BaseFilter *iface )
193 return CONTAINING_RECORD(iface, QTSplitter, filter);
196 static inline QTSplitter *impl_from_IBaseFilter( IBaseFilter *iface )
198 return CONTAINING_RECORD(iface, QTSplitter, filter.IBaseFilter_iface);
205 static IPin* WINAPI QT_GetPin(BaseFilter *iface, int pos)
207 QTSplitter *This = impl_from_BaseFilter(iface);
208 TRACE("Asking for pos %x\n", pos);
210 if (pos > 2 || pos < 0)
215 IPin_AddRef(&This->pInputPin.pin.IPin_iface);
216 return &This->pInputPin.pin.IPin_iface;
218 if (This->pVideo_Pin)
219 IPin_AddRef(&This->pVideo_Pin->pin.pin.IPin_iface);
220 return &This->pVideo_Pin->pin.pin.IPin_iface;
222 if (This->pAudio_Pin)
223 IPin_AddRef(&This->pAudio_Pin->pin.pin.IPin_iface);
224 return &This->pAudio_Pin->pin.pin.IPin_iface;
230 static LONG WINAPI QT_GetPinCount(BaseFilter *iface)
232 QTSplitter *This = impl_from_BaseFilter(iface);
234 if (This->pAudio_Pin) c++;
235 if (This->pVideo_Pin) c++;
239 static const BaseFilterFuncTable BaseFuncTable = {
244 IUnknown * CALLBACK QTSplitter_create(IUnknown *punkout, HRESULT *phr)
246 IUnknown *obj = NULL;
249 static const WCHAR wcsInputPinName[] = {'I','n','p','u','t',' ','P','i','n',0};
253 RegisterWineDataHandler();
255 This = CoTaskMemAlloc(sizeof(*This));
256 obj = (IUnknown*)This;
259 *phr = E_OUTOFMEMORY;
262 ZeroMemory(This,sizeof(*This));
264 BaseFilter_Init(&This->filter, &QT_Vtbl, &CLSID_QTSplitter, (DWORD_PTR)(__FILE__ ": QTSplitter.csFilter"), &BaseFuncTable);
266 InitializeCriticalSection(&This->csReceive);
267 This->csReceive.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__": QTSplitter.csReceive");
269 This->pVideo_Pin = NULL;
270 This->pAudio_Pin = NULL;
271 This->state = State_Stopped;
272 This->aSession = NULL;
273 This->runEvent = CreateEventW(NULL, 0, 0, NULL);
275 piInput = &This->pInputPin.pin.pinInfo;
276 piInput->dir = PINDIR_INPUT;
277 piInput->pFilter = &This->filter.IBaseFilter_iface;
278 lstrcpynW(piInput->achName, wcsInputPinName, sizeof(piInput->achName) / sizeof(piInput->achName[0]));
279 This->pInputPin.pin.IPin_iface.lpVtbl = &QT_InputPin_Vtbl;
280 This->pInputPin.pin.refCount = 1;
281 This->pInputPin.pin.pConnectedTo = NULL;
282 This->pInputPin.pin.pCritSec = &This->filter.csFilter;
284 SourceSeeking_Init(&This->sourceSeeking, &QT_Seeking_Vtbl, QTSplitter_ChangeStop, QTSplitter_ChangeStart, QTSplitter_ChangeRate, &This->filter.csFilter);
290 static void QT_Destroy(QTSplitter *This)
292 IPin *connected = NULL;
295 TRACE("Destroying\n");
297 /* Don't need to clean up output pins, disconnecting input pin will do that */
298 IPin_ConnectedTo(&This->pInputPin.pin.IPin_iface, &connected);
301 IPin_Disconnect(connected);
302 IPin_Release(connected);
304 pinref = IPin_Release(&This->pInputPin.pin.IPin_iface);
307 ERR("pinref should be null, is %u, destroying anyway\n", pinref);
308 assert((LONG)pinref > 0);
311 pinref = IPin_Release(&This->pInputPin.pin.IPin_iface);
315 DisposeMovie(This->pQTMovie);
317 QTVisualContextRelease(This->vContext);
319 MovieAudioExtractionEnd(This->aSession);
320 CloseHandle(This->runEvent);
324 This->csReceive.DebugInfo->Spare[0] = 0;
325 DeleteCriticalSection(&This->csReceive);
330 static HRESULT WINAPI QT_QueryInterface(IBaseFilter *iface, REFIID riid, LPVOID *ppv)
332 QTSplitter *This = impl_from_IBaseFilter(iface);
333 TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
337 if (IsEqualIID(riid, &IID_IUnknown))
339 else if (IsEqualIID(riid, &IID_IPersist))
341 else if (IsEqualIID(riid, &IID_IMediaFilter))
343 else if (IsEqualIID(riid, &IID_IBaseFilter))
345 else if (IsEqualIID(riid, &IID_IMediaSeeking))
346 *ppv = &This->sourceSeeking;
350 IUnknown_AddRef((IUnknown *)(*ppv));
354 if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow) &&
355 !IsEqualIID(riid, &IID_IAMFilterMiscFlags))
356 FIXME("No interface for %s!\n", debugstr_guid(riid));
358 return E_NOINTERFACE;
361 static ULONG WINAPI QT_Release(IBaseFilter *iface)
363 QTSplitter *This = impl_from_IBaseFilter(iface);
364 ULONG refCount = BaseFilterImpl_Release(iface);
366 TRACE("(%p)->() Release from %d\n", This, refCount + 1);
374 static HRESULT WINAPI QT_Stop(IBaseFilter *iface)
376 QTSplitter *This = impl_from_IBaseFilter(iface);
380 EnterCriticalSection(&This->csReceive);
381 IAsyncReader_BeginFlush(This->pInputPin.pReader);
382 IAsyncReader_EndFlush(This->pInputPin.pReader);
383 LeaveCriticalSection(&This->csReceive);
388 static HRESULT WINAPI QT_Pause(IBaseFilter *iface)
396 static OSErr QT_Create_Extract_Session(QTSplitter *filter)
398 AudioStreamBasicDescription aDesc;
402 pvi = (WAVEFORMATEX*)filter->pAudio_Pin->pmt->pbFormat;
404 err = MovieAudioExtractionBegin(filter->pQTMovie, 0, &filter->aSession);
407 ERR("Failed to begin Extraction session %i\n",err);
411 err = MovieAudioExtractionGetProperty(filter->aSession,
412 kQTPropertyClass_MovieAudioExtraction_Audio,
413 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
414 sizeof(AudioStreamBasicDescription), &aDesc, NULL);
418 MovieAudioExtractionEnd(filter->aSession);
419 filter->aSession = NULL;
420 ERR("Failed to get session description %i\n",err);
424 aDesc.mFormatID = kAudioFormatLinearPCM;
425 aDesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger +
426 kAudioFormatFlagIsPacked;
427 aDesc.mFramesPerPacket = 1;
428 aDesc.mChannelsPerFrame = pvi->nChannels;
429 aDesc.mBitsPerChannel = pvi->wBitsPerSample;
430 aDesc.mSampleRate = pvi->nSamplesPerSec;
431 aDesc.mBytesPerFrame = (aDesc.mBitsPerChannel * aDesc.mChannelsPerFrame) / 8;
432 aDesc.mBytesPerPacket = aDesc.mBytesPerFrame * aDesc.mFramesPerPacket;
434 err = MovieAudioExtractionSetProperty(filter->aSession,
435 kQTPropertyClass_MovieAudioExtraction_Audio,
436 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
437 sizeof(AudioStreamBasicDescription), &aDesc);
439 if (aDesc.mFormatID != kAudioFormatLinearPCM)
441 ERR("Not PCM Wave\n");
444 if (aDesc.mFormatFlags != kLinearPCMFormatFlagIsSignedInteger +
445 kAudioFormatFlagIsPacked)
447 ERR("Unhandled Flags\n");
450 if (aDesc.mFramesPerPacket != 1)
452 ERR("Unhandled Frames per packet %li\n",aDesc.mFramesPerPacket);
455 if (aDesc.mChannelsPerFrame != pvi->nChannels)
457 ERR("Unhandled channel count %li\n",aDesc.mChannelsPerFrame);
460 if (aDesc.mBitsPerChannel != pvi->wBitsPerSample)
462 ERR("Unhandled bits per channel %li\n",aDesc.mBitsPerChannel);
465 if (aDesc.mSampleRate != pvi->nSamplesPerSec)
467 ERR("Unhandled sample rate %f\n",aDesc.mSampleRate);
473 ERR("Failed to create Extraction Session\n");
474 MovieAudioExtractionEnd(filter->aSession);
475 filter->aSession = NULL;
481 static DWORD WINAPI QTSplitter_thread(LPVOID data)
483 QTSplitter *This = (QTSplitter *)data;
486 CVPixelBufferRef pixelBuffer = NULL;
490 if (This->pAudio_Pin)
492 /* according to QA1469 a movie has to be fully loaded before we
493 can reliably start the Extraction session */
495 while(GetMovieLoadState(This->pQTMovie) < kMovieLoadStateComplete)
496 MoviesTask(This->pQTMovie,1000);
498 QT_Create_Extract_Session(This);
501 WaitForSingleObject(This->runEvent, -1);
503 EnterCriticalSection(&This->csReceive);
504 This->state = State_Running;
505 /* Prime the pump: Needed for MPEG streams */
506 GetMovieNextInterestingTime(This->pQTMovie, nextTimeEdgeOK | nextTimeStep, 0, NULL, This->movie_time, 1, &next_time, NULL);
508 GetMovieTime(This->pQTMovie, &tr);
509 LeaveCriticalSection(&This->csReceive);
512 LONGLONG tStart=0, tStop=0;
513 LONGLONG mStart=0, mStop=0;
516 EnterCriticalSection(&This->csReceive);
517 GetMovieNextInterestingTime(This->pQTMovie, nextTimeStep, 0, NULL, This->movie_time, 1, &next_time, NULL);
521 TRACE("No next time\n");
522 LeaveCriticalSection(&This->csReceive);
526 tr.value = SInt64ToWide(next_time);
527 SetMovieTime(This->pQTMovie, &tr);
528 MoviesTask(This->pQTMovie,0);
529 QTVisualContextTask(This->vContext);
531 TRACE("In loop at time %ld\n",This->movie_time);
532 TRACE("In Next time %ld\n",next_time);
534 mStart = This->movie_time;
537 time = (float)(This->movie_time - This->movie_start) / This->movie_scale;
538 tStart = time * 10000000;
539 time = (float)(next_time - This->movie_start) / This->movie_scale;
540 tStop = time * 10000000;
543 if (This->pAudio_Pin && This->pAudio_Pin->pin.pin.pConnectedTo && This->aSession)
547 IMediaSample *sample = NULL;
548 AudioBufferList aData;
554 pvi = (WAVEFORMATEX*)This->pAudio_Pin->pmt->pbFormat;
556 hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->pAudio_Pin->pin, &sample, NULL, NULL, 0);
560 ERR("Audio: Unable to get delivery buffer (%x)\n", hr);
564 hr = IMediaSample_GetPointer(sample, &ptr);
567 ERR("Audio: Unable to get pointer to buffer (%x)\n", hr);
571 duration = (float)next_time / This->movie_scale;
572 time = (float)This->movie_time / This->movie_scale;
574 frames = pvi->nSamplesPerSec * duration;
575 TRACE("Need audio for %f seconds (%li frames)\n",duration,frames);
577 data_size = IMediaSample_GetSize(sample);
578 if (data_size < frames * pvi->nBlockAlign)
579 FIXME("Audio buffer is too small\n");
581 aData.mNumberBuffers = 1;
582 aData.mBuffers[0].mNumberChannels = pvi->nChannels;
583 aData.mBuffers[0].mDataByteSize = data_size;
584 aData.mBuffers[0].mData = ptr;
586 err = MovieAudioExtractionFillBuffer(This->aSession, &frames, &aData, &flags);
587 TRACE("Got %i frames\n",(int)frames);
589 IMediaSample_SetActualDataLength(sample, frames * pvi->nBlockAlign);
591 IMediaSample_SetMediaTime(sample, &mStart, &mStop);
592 IMediaSample_SetTime(sample, &tStart, &tStop);
594 hr = OutputQueue_Receive(This->pAudio_Pin->queue, sample);
595 TRACE("Audio Delivered (%x)\n",hr);
599 IMediaSample_Release(sample);
602 TRACE("Audio Pin not connected or no Audio\n");
605 if (This->pVideo_Pin && QTVisualContextIsNewImageAvailable(This->vContext,0))
607 err = QTVisualContextCopyImageForTime(This->vContext, NULL, NULL, &pixelBuffer);
612 IMediaSample *sample = NULL;
614 hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->pVideo_Pin->pin, &sample, NULL, NULL, 0);
617 ERR("Video: Unable to get delivery buffer (%x)\n", hr);
621 data_size = IMediaSample_GetSize(sample);
622 if (data_size < This->outputSize)
624 ERR("Sample size is too small %d < %d\n", data_size, This->outputSize)
630 hr = IMediaSample_GetPointer(sample, &ptr);
633 ERR("Video: Unable to get pointer to buffer (%x)\n", hr);
637 hr = AccessPixelBufferPixels( pixelBuffer, ptr);
640 ERR("Failed to access Pixels\n");
644 IMediaSample_SetActualDataLength(sample, This->outputSize);
646 IMediaSample_SetMediaTime(sample, &mStart, &mStop);
647 IMediaSample_SetTime(sample, &tStart, &tStop);
649 hr = OutputQueue_Receive(This->pVideo_Pin->queue, sample);
650 TRACE("Video Delivered (%x)\n",hr);
654 IMediaSample_Release(sample);
656 CVPixelBufferRelease(pixelBuffer);
660 TRACE("No video to deliver\n");
662 This->movie_time = next_time;
663 LeaveCriticalSection(&This->csReceive);
664 } while (hr == S_OK);
666 This->state = State_Stopped;
667 if (This->pAudio_Pin)
668 OutputQueue_EOS(This->pAudio_Pin->queue);
669 if (This->pVideo_Pin)
670 OutputQueue_EOS(This->pVideo_Pin->queue);
675 static HRESULT WINAPI QT_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
678 QTSplitter *This = impl_from_IBaseFilter(iface);
679 HRESULT hr_any = VFW_E_NOT_CONNECTED;
681 TRACE("(%s)\n", wine_dbgstr_longlong(tStart));
683 EnterCriticalSection(&This->csReceive);
684 This->filter.rtStreamStart = tStart;
686 if (This->pVideo_Pin)
687 hr = BaseOutputPinImpl_Active(&This->pVideo_Pin->pin);
690 if (This->pAudio_Pin)
691 hr = BaseOutputPinImpl_Active(&This->pAudio_Pin->pin);
697 SetEvent(This->runEvent);
698 LeaveCriticalSection(&This->csReceive);
703 static HRESULT WINAPI QT_GetState(IBaseFilter *iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
705 QTSplitter *This = impl_from_IBaseFilter(iface);
706 TRACE("(%d, %p)\n", dwMilliSecsTimeout, pState);
708 *pState = This->state;
713 static HRESULT WINAPI QT_FindPin(IBaseFilter *iface, LPCWSTR Id, IPin **ppPin)
715 FIXME("(%p)->(%s,%p) stub\n", iface, debugstr_w(Id), ppPin);
719 static const IBaseFilterVtbl QT_Vtbl = {
721 BaseFilterImpl_AddRef,
723 BaseFilterImpl_GetClassID,
728 BaseFilterImpl_SetSyncSource,
729 BaseFilterImpl_GetSyncSource,
730 BaseFilterImpl_EnumPins,
732 BaseFilterImpl_QueryFilterInfo,
733 BaseFilterImpl_JoinFilterGraph,
734 BaseFilterImpl_QueryVendorInfo
740 static HRESULT QT_RemoveOutputPins(QTSplitter *This)
743 TRACE("(%p)\n", This);
745 if (This->pVideo_Pin)
747 hr = BaseOutputPinImpl_BreakConnect(&This->pVideo_Pin->pin);
748 TRACE("Disconnect: %08x\n", hr);
749 IPin_Release(&This->pVideo_Pin->pin.pin.IPin_iface);
750 This->pVideo_Pin = NULL;
752 if (This->pAudio_Pin)
754 hr = BaseOutputPinImpl_BreakConnect(&This->pAudio_Pin->pin);
755 TRACE("Disconnect: %08x\n", hr);
756 IPin_Release(&This->pAudio_Pin->pin.pin.IPin_iface);
757 This->pAudio_Pin = NULL;
760 BaseFilterImpl_IncrementPinVersion(&This->filter);
764 static inline QTInPin *impl_from_IPin( IPin *iface )
766 return CONTAINING_RECORD(iface, QTInPin, pin.IPin_iface);
769 static ULONG WINAPI QTInPin_Release(IPin *iface)
771 QTInPin *This = impl_from_IPin(iface);
772 ULONG refCount = InterlockedDecrement(&This->pin.refCount);
774 TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
777 FreeMediaType(&This->pin.mtCurrent);
779 IMemAllocator_Release(This->pAlloc);
781 This->pin.IPin_iface.lpVtbl = NULL;
788 static HRESULT QT_Process_Video_Track(QTSplitter* filter, Track trk)
791 VIDEOINFOHEADER * pvi;
795 static const WCHAR szwVideoOut[] = {'V','i','d','e','o',0};
796 CFMutableDictionaryRef pixelBufferOptions = NULL;
797 CFMutableDictionaryRef visualContextOptions = NULL;
798 CFNumberRef n = NULL;
800 DWORD outputWidth, outputHeight, outputDepth;
801 Fixed trackWidth, trackHeight;
803 ZeroMemory(&amt, sizeof(amt));
804 amt.formattype = FORMAT_VideoInfo;
805 amt.majortype = MEDIATYPE_Video;
806 amt.subtype = MEDIASUBTYPE_RGB24;
808 GetTrackDimensions(trk, &trackWidth, &trackHeight);
811 outputWidth = Fix2Long(trackWidth);
812 outputHeight = Fix2Long(trackHeight);
813 TRACE("Width %i Height %i\n",outputWidth, outputHeight);
815 amt.cbFormat = sizeof(VIDEOINFOHEADER);
816 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
817 ZeroMemory(amt.pbFormat, amt.cbFormat);
818 pvi = (VIDEOINFOHEADER *)amt.pbFormat;
819 pvi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
820 pvi->bmiHeader.biWidth = outputWidth;
821 pvi->bmiHeader.biHeight = outputHeight;
822 pvi->bmiHeader.biPlanes = 1;
823 pvi->bmiHeader.biBitCount = 24;
824 pvi->bmiHeader.biCompression = BI_RGB;
825 pvi->bmiHeader.biSizeImage = outputWidth * outputHeight * outputDepth;
827 filter->outputSize = pvi->bmiHeader.biSizeImage;
830 pixelBufferOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
832 t = k32ARGBPixelFormat;
833 n = CFNumberCreate(NULL, kCFNumberIntType, &t);
834 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferPixelFormatTypeKey, n);
837 n = CFNumberCreate(NULL, kCFNumberIntType, &outputWidth);
838 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferWidthKey, n);
841 n = CFNumberCreate(NULL, kCFNumberIntType, &outputHeight);
842 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferHeightKey, n);
846 n = CFNumberCreate(NULL, kCFNumberIntType, &t);
847 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferBytesPerRowAlignmentKey, n);
850 visualContextOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
852 CFDictionarySetValue(visualContextOptions, kQTVisualContextPixelBufferAttributesKey, pixelBufferOptions);
854 err = QTPixelBufferContextCreate(NULL, visualContextOptions,&filter->vContext);
855 CFRelease(pixelBufferOptions);
856 CFRelease(visualContextOptions);
859 ERR("Failed to create Visual Context\n");
863 err = SetMovieVisualContext(filter->pQTMovie, filter->vContext);
866 ERR("Failed to set Visual Context\n");
870 piOutput.dir = PINDIR_OUTPUT;
871 piOutput.pFilter = &filter->filter.IBaseFilter_iface;
872 lstrcpyW(piOutput.achName,szwVideoOut);
874 hr = QT_AddPin(filter, &piOutput, &amt, TRUE);
876 ERR("Failed to add Video Track\n");
878 TRACE("Video Pin %p\n",filter->pVideo_Pin);
883 static HRESULT QT_Process_Audio_Track(QTSplitter* filter, Track trk)
889 static const WCHAR szwAudioOut[] = {'A','u','d','i','o',0};
892 SoundDescriptionHandle aDesc = (SoundDescriptionHandle) NewHandle(sizeof(SoundDescription));
894 audioMedia = GetTrackMedia(trk);
895 GetMediaSampleDescription(audioMedia, 1, (SampleDescriptionHandle)aDesc);
897 ZeroMemory(&amt, sizeof(amt));
898 amt.formattype = FORMAT_WaveFormatEx;
899 amt.majortype = MEDIATYPE_Audio;
900 amt.subtype = MEDIASUBTYPE_PCM;
901 amt.bTemporalCompression = 0;
903 amt.cbFormat = sizeof(WAVEFORMATEX);
904 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
905 ZeroMemory(amt.pbFormat, amt.cbFormat);
906 pvi = (WAVEFORMATEX*)amt.pbFormat;
908 pvi->cbSize = sizeof(WAVEFORMATEX);
909 pvi->wFormatTag = WAVE_FORMAT_PCM;
910 pvi->nChannels = ((SoundDescription)**aDesc).numChannels;
911 if (pvi->nChannels < 1 || pvi->nChannels > 2)
913 pvi->nSamplesPerSec = (((SoundDescription)**aDesc).sampleRate/65536);
914 if (pvi->nSamplesPerSec < 8000 || pvi->nChannels > 48000)
915 pvi->nSamplesPerSec = 44100;
916 pvi->wBitsPerSample = ((SoundDescription)**aDesc).sampleSize;
917 if (pvi->wBitsPerSample < 8 || pvi->wBitsPerSample > 32)
918 pvi->wBitsPerSample = 16;
919 pvi->nBlockAlign = (pvi->nChannels * pvi->wBitsPerSample) / 8;
920 pvi->nAvgBytesPerSec = pvi->nSamplesPerSec * pvi->nBlockAlign;
922 DisposeHandle((Handle)aDesc);
924 piOutput.dir = PINDIR_OUTPUT;
925 piOutput.pFilter = &filter->filter.IBaseFilter_iface;
926 lstrcpyW(piOutput.achName,szwAudioOut);
928 hr = QT_AddPin(filter, &piOutput, &amt, FALSE);
930 ERR("Failed to add Audio Track\n");
932 TRACE("Audio Pin %p\n",filter->pAudio_Pin);
936 static HRESULT QT_Process_Movie(QTSplitter* filter)
940 WineDataRefRecord ptrDataRefRec;
941 Handle dataRef = NULL;
948 TRACE("Trying movie connect\n");
950 ptrDataRefRec.pReader = filter->pInputPin.pReader;
951 ptrDataRefRec.streamSubtype = filter->pInputPin.subType;
952 PtrToHand( &ptrDataRefRec, &dataRef, sizeof(WineDataRefRecord));
954 err = NewMovieFromDataRef(&filter->pQTMovie, newMovieActive|newMovieDontInteractWithUser|newMovieDontAutoUpdateClock|newMovieDontAskUnresolvedDataRefs|newMovieAsyncOK, &id, dataRef, 'WINE');
956 DisposeHandle(dataRef);
960 FIXME("QuickTime cannot handle media type(%i)\n",err);
961 return VFW_E_TYPE_NOT_ACCEPTED;
964 PrePrerollMovie(filter->pQTMovie, 0, fixed1, NULL, NULL);
965 PrerollMovie(filter->pQTMovie, 0, fixed1);
966 GoToBeginningOfMovie(filter->pQTMovie);
967 SetMovieActive(filter->pQTMovie,TRUE);
969 if (GetMovieLoadState(filter->pQTMovie) < kMovieLoadStateLoaded)
970 MoviesTask(filter->pQTMovie,100);
972 trk = GetMovieIndTrackType(filter->pQTMovie, 1, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
973 TRACE("%p is a video track\n",trk);
975 hr = QT_Process_Video_Track(filter, trk);
980 trk = GetMovieIndTrackType(filter->pQTMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
981 TRACE("%p is a audio track\n",trk);
983 hr = QT_Process_Audio_Track(filter, trk);
985 time = GetMovieDuration(filter->pQTMovie);
986 filter->movie_scale = GetMovieTimeScale(filter->pQTMovie);
987 filter->sourceSeeking.llDuration = ((double)time / filter->movie_scale) * 10000000;
988 filter->sourceSeeking.llStop = filter->sourceSeeking.llDuration;
990 TRACE("Movie duration is %s\n",wine_dbgstr_longlong(filter->sourceSeeking.llDuration));
992 thread = CreateThread(NULL, 0, QTSplitter_thread, filter, 0, &tid);
995 TRACE("Created thread 0x%08x\n", tid);
999 hr = HRESULT_FROM_WIN32(GetLastError());
1004 static HRESULT WINAPI QTInPin_ReceiveConnection(IPin *iface, IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
1007 ALLOCATOR_PROPERTIES props;
1008 QTInPin *This = impl_from_IPin(iface);
1010 TRACE("(%p/%p)->(%p, %p)\n", This, iface, pReceivePin, pmt);
1012 EnterCriticalSection(This->pin.pCritSec);
1013 This->pReader = NULL;
1015 if (This->pin.pConnectedTo)
1016 hr = VFW_E_ALREADY_CONNECTED;
1017 else if (IPin_QueryAccept(iface, pmt) != S_OK)
1018 hr = VFW_E_TYPE_NOT_ACCEPTED;
1021 PIN_DIRECTION pindirReceive;
1022 IPin_QueryDirection(pReceivePin, &pindirReceive);
1023 if (pindirReceive != PINDIR_OUTPUT)
1024 hr = VFW_E_INVALID_DIRECTION;
1029 LeaveCriticalSection(This->pin.pCritSec);
1033 hr = IPin_QueryInterface(pReceivePin, &IID_IAsyncReader, (LPVOID *)&This->pReader);
1036 LeaveCriticalSection(This->pin.pCritSec);
1037 TRACE("Input source is not an AsyncReader\n");
1041 LeaveCriticalSection(This->pin.pCritSec);
1042 EnterCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1043 hr = QT_Process_Movie(impl_from_IBaseFilter(This->pin.pinInfo.pFilter));
1046 LeaveCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1047 TRACE("Unable to process movie\n");
1051 This->pAlloc = NULL;
1054 props.cbBuffer = impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->outputSize + props.cbAlign;
1057 hr = IAsyncReader_RequestAllocator(This->pReader, NULL, &props, &This->pAlloc);
1060 CopyMediaType(&This->pin.mtCurrent, pmt);
1061 This->pin.pConnectedTo = pReceivePin;
1062 IPin_AddRef(pReceivePin);
1063 hr = IMemAllocator_Commit(This->pAlloc);
1067 QT_RemoveOutputPins(impl_from_IBaseFilter(This->pin.pinInfo.pFilter));
1069 IAsyncReader_Release(This->pReader);
1070 This->pReader = NULL;
1072 IMemAllocator_Release(This->pAlloc);
1073 This->pAlloc = NULL;
1075 TRACE("Size: %i\n", props.cbBuffer);
1076 LeaveCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1081 static HRESULT WINAPI QTInPin_Disconnect(IPin *iface)
1084 QTInPin *This = impl_from_IPin(iface);
1088 hr = IBaseFilter_GetState(This->pin.pinInfo.pFilter, INFINITE, &state);
1089 EnterCriticalSection(This->pin.pCritSec);
1090 if (This->pin.pConnectedTo)
1092 QTSplitter *Parser = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
1094 if (SUCCEEDED(hr) && state == State_Stopped)
1096 IMemAllocator_Decommit(This->pAlloc);
1097 IPin_Disconnect(This->pin.pConnectedTo);
1098 This->pin.pConnectedTo = NULL;
1099 hr = QT_RemoveOutputPins(Parser);
1102 hr = VFW_E_NOT_STOPPED;
1106 LeaveCriticalSection(This->pin.pCritSec);
1110 static HRESULT WINAPI QTInPin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *pmt)
1112 QTInPin *This = impl_from_IPin(iface);
1114 TRACE("(%p)->(%p)\n", This, pmt);
1116 if (IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream))
1118 This->subType = pmt->subtype;
1124 static HRESULT WINAPI QTInPin_EndOfStream(IPin *iface)
1126 QTInPin *pin = impl_from_IPin(iface);
1127 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1129 FIXME("Propagate message on %p\n", This);
1133 static HRESULT WINAPI QTInPin_BeginFlush(IPin *iface)
1135 QTInPin *pin = impl_from_IPin(iface);
1136 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1138 FIXME("Propagate message on %p\n", This);
1142 static HRESULT WINAPI QTInPin_EndFlush(IPin *iface)
1144 QTInPin *pin = impl_from_IPin(iface);
1145 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1147 FIXME("Propagate message on %p\n", This);
1151 static HRESULT WINAPI QTInPin_NewSegment(IPin *iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
1153 QTInPin *pin = impl_from_IPin(iface);
1154 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1156 BasePinImpl_NewSegment(iface, tStart, tStop, dRate);
1157 FIXME("Propagate message on %p\n", This);
1161 static HRESULT WINAPI QTInPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
1163 QTInPin *This = impl_from_IPin(iface);
1165 TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
1169 if (IsEqualIID(riid, &IID_IUnknown))
1171 else if (IsEqualIID(riid, &IID_IPin))
1173 else if (IsEqualIID(riid, &IID_IMediaSeeking))
1174 return IBaseFilter_QueryInterface(This->pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
1178 IUnknown_AddRef((IUnknown *)(*ppv));
1182 FIXME("No interface for %s!\n", debugstr_guid(riid));
1184 return E_NOINTERFACE;
1187 static HRESULT WINAPI QTInPin_EnumMediaTypes(IPin *iface, IEnumMediaTypes **ppEnum)
1189 QTInPin *This = impl_from_IPin(iface);
1191 TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
1193 return EnumMediaTypes_Construct(&This->pin, BasePinImpl_GetMediaType, BasePinImpl_GetMediaTypeVersion, ppEnum);
1196 static const IPinVtbl QT_InputPin_Vtbl = {
1197 QTInPin_QueryInterface,
1200 BaseInputPinImpl_Connect,
1201 QTInPin_ReceiveConnection,
1203 BasePinImpl_ConnectedTo,
1204 BasePinImpl_ConnectionMediaType,
1205 BasePinImpl_QueryPinInfo,
1206 BasePinImpl_QueryDirection,
1207 BasePinImpl_QueryId,
1208 QTInPin_QueryAccept,
1209 QTInPin_EnumMediaTypes,
1210 BasePinImpl_QueryInternalConnections,
1211 QTInPin_EndOfStream,
1220 static inline QTOutPin *impl_QTOutPin_from_IPin( IPin *iface )
1222 return CONTAINING_RECORD(iface, QTOutPin, pin.pin.IPin_iface);
1225 static inline QTOutPin *impl_QTOutPin_from_BasePin( BasePin *iface )
1227 return CONTAINING_RECORD(iface, QTOutPin, pin.pin);
1230 static inline QTOutPin *impl_QTOutPin_from_BaseOutputPin( BaseOutputPin *iface )
1232 return CONTAINING_RECORD(iface, QTOutPin, pin);
1235 static HRESULT WINAPI QTOutPin_QueryInterface(IPin *iface, REFIID riid, void **ppv)
1237 QTOutPin *This = impl_QTOutPin_from_IPin(iface);
1239 TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
1243 if (IsEqualIID(riid, &IID_IUnknown))
1245 else if (IsEqualIID(riid, &IID_IPin))
1247 else if (IsEqualIID(riid, &IID_IMediaSeeking))
1248 return IBaseFilter_QueryInterface(This->pin.pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
1252 IUnknown_AddRef((IUnknown *)(*ppv));
1255 FIXME("No interface for %s!\n", debugstr_guid(riid));
1256 return E_NOINTERFACE;
1259 static ULONG WINAPI QTOutPin_Release(IPin *iface)
1261 QTOutPin *This = impl_QTOutPin_from_IPin(iface);
1262 ULONG refCount = InterlockedDecrement(&This->pin.pin.refCount);
1263 TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
1267 DeleteMediaType(This->pmt);
1268 FreeMediaType(&This->pin.pin.mtCurrent);
1269 OutputQueue_Destroy(This->queue);
1270 CoTaskMemFree(This);
1276 static HRESULT WINAPI QTOutPin_GetMediaType(BasePin *iface, int iPosition, AM_MEDIA_TYPE *pmt)
1278 QTOutPin *This = impl_QTOutPin_from_BasePin(iface);
1281 return E_INVALIDARG;
1283 return VFW_S_NO_MORE_ITEMS;
1284 CopyMediaType(pmt, This->pmt);
1288 static HRESULT WINAPI QTOutPin_DecideBufferSize(BaseOutputPin *iface, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
1294 static HRESULT WINAPI QTOutPin_DecideAllocator(BaseOutputPin *iface, IMemInputPin *pPin, IMemAllocator **pAlloc)
1297 QTOutPin *This = impl_QTOutPin_from_BaseOutputPin(iface);
1298 QTSplitter *QTfilter = impl_from_IBaseFilter(This->pin.pin.pinInfo.pFilter);
1301 if (QTfilter->pInputPin.pAlloc)
1302 hr = IMemInputPin_NotifyAllocator(pPin, QTfilter->pInputPin.pAlloc, FALSE);
1304 hr = VFW_E_NO_ALLOCATOR;
1309 static HRESULT WINAPI QTOutPin_BreakConnect(BaseOutputPin *This)
1313 TRACE("(%p)->()\n", This);
1315 EnterCriticalSection(This->pin.pCritSec);
1316 if (!This->pin.pConnectedTo || !This->pMemInputPin)
1317 hr = VFW_E_NOT_CONNECTED;
1320 hr = IPin_Disconnect(This->pin.pConnectedTo);
1321 IPin_Disconnect(&This->pin.IPin_iface);
1323 LeaveCriticalSection(This->pin.pCritSec);
1328 static const IPinVtbl QT_OutputPin_Vtbl = {
1329 QTOutPin_QueryInterface,
1332 BaseOutputPinImpl_Connect,
1333 BaseOutputPinImpl_ReceiveConnection,
1334 BaseOutputPinImpl_Disconnect,
1335 BasePinImpl_ConnectedTo,
1336 BasePinImpl_ConnectionMediaType,
1337 BasePinImpl_QueryPinInfo,
1338 BasePinImpl_QueryDirection,
1339 BasePinImpl_QueryId,
1340 BasePinImpl_QueryAccept,
1341 BasePinImpl_EnumMediaTypes,
1342 BasePinImpl_QueryInternalConnections,
1343 BaseOutputPinImpl_EndOfStream,
1344 BaseOutputPinImpl_BeginFlush,
1345 BaseOutputPinImpl_EndFlush,
1346 BasePinImpl_NewSegment
1349 static const BasePinFuncTable output_BaseFuncTable = {
1351 BaseOutputPinImpl_AttemptConnection,
1352 BasePinImpl_GetMediaTypeVersion,
1353 QTOutPin_GetMediaType
1356 static const BaseOutputPinFuncTable output_BaseOutputFuncTable = {
1357 QTOutPin_DecideBufferSize,
1358 QTOutPin_DecideAllocator,
1359 QTOutPin_BreakConnect
1362 static const OutputQueueFuncTable output_OutputQueueFuncTable = {
1363 OutputQueueImpl_ThreadProc
1366 static HRESULT QT_AddPin(QTSplitter *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt, BOOL video)
1372 target = (IPin**)&This->pVideo_Pin;
1374 target = (IPin**)&This->pAudio_Pin;
1376 if (*target != NULL)
1378 FIXME("We already have a %s pin\n",(video)?"video":"audio");
1382 hr = BaseOutputPin_Construct(&QT_OutputPin_Vtbl, sizeof(QTOutPin), piOutput, &output_BaseFuncTable, &output_BaseOutputFuncTable, &This->filter.csFilter, (IPin**)target);
1385 QTOutPin *pin = (QTOutPin*)*target;
1386 pin->pmt = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
1387 CopyMediaType(pin->pmt, amt);
1388 pin->pin.pin.pinInfo.pFilter = (LPVOID)This;
1390 BaseFilterImpl_IncrementPinVersion(&This->filter);
1392 hr = OutputQueue_Construct(&pin->pin, TRUE, TRUE, 5, FALSE, THREAD_PRIORITY_NORMAL, &output_OutputQueueFuncTable, &pin->queue);
1395 ERR("Failed with error %x\n", hr);
1399 static HRESULT WINAPI QTSplitter_ChangeStart(IMediaSeeking *iface)
1401 QTSplitter *This = impl_from_IMediaSeeking(iface);
1402 TRACE("(%p)\n", iface);
1403 EnterCriticalSection(&This->csReceive);
1404 This->movie_time = (This->sourceSeeking.llCurrent * This->movie_scale)/10000000;
1405 This->movie_start = This->movie_time;
1406 LeaveCriticalSection(&This->csReceive);
1410 static HRESULT WINAPI QTSplitter_ChangeStop(IMediaSeeking *iface)
1412 FIXME("(%p) filter hasn't implemented stop position change!\n", iface);
1416 static HRESULT WINAPI QTSplitter_ChangeRate(IMediaSeeking *iface)
1418 FIXME("(%p) filter hasn't implemented rate change!\n", iface);
1422 static HRESULT WINAPI QT_Seeking_QueryInterface(IMediaSeeking * iface, REFIID riid, LPVOID * ppv)
1424 QTSplitter *This = impl_from_IMediaSeeking(iface);
1426 return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv);
1429 static ULONG WINAPI QT_Seeking_AddRef(IMediaSeeking * iface)
1431 QTSplitter *This = impl_from_IMediaSeeking(iface);
1433 return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
1436 static ULONG WINAPI QT_Seeking_Release(IMediaSeeking * iface)
1438 QTSplitter *This = impl_from_IMediaSeeking(iface);
1440 return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
1443 static const IMediaSeekingVtbl QT_Seeking_Vtbl =
1445 QT_Seeking_QueryInterface,
1448 SourceSeekingImpl_GetCapabilities,
1449 SourceSeekingImpl_CheckCapabilities,
1450 SourceSeekingImpl_IsFormatSupported,
1451 SourceSeekingImpl_QueryPreferredFormat,
1452 SourceSeekingImpl_GetTimeFormat,
1453 SourceSeekingImpl_IsUsingTimeFormat,
1454 SourceSeekingImpl_SetTimeFormat,
1455 SourceSeekingImpl_GetDuration,
1456 SourceSeekingImpl_GetStopPosition,
1457 SourceSeekingImpl_GetCurrentPosition,
1458 SourceSeekingImpl_ConvertTimeFormat,
1459 SourceSeekingImpl_SetPositions,
1460 SourceSeekingImpl_GetPositions,
1461 SourceSeekingImpl_GetAvailable,
1462 SourceSeekingImpl_SetRate,
1463 SourceSeekingImpl_GetRate,
1464 SourceSeekingImpl_GetPreroll