wined3d: Pass a wined3d_device_context to wined3d_cs_emit_blt_sub_resource().
[wine/zf.git] / dlls / wineqtdecoder / qtsplitter.c
blob21fa237674597aa2a4fc37ce17e87a0bc101e968
1 /*
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
21 #include "config.h"
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>
66 #undef LoadResource
67 #undef CompareString
68 #undef GetCurrentThread
69 #undef _CDECL
70 #undef GetCurrentProcess
71 #undef AnimatePalette
72 #undef EqualRgn
73 #undef FillRgn
74 #undef FrameRgn
75 #undef GetPixel
76 #undef InvertRgn
77 #undef LineTo
78 #undef OffsetRgn
79 #undef PaintRgn
80 #undef Polygon
81 #undef ResizePalette
82 #undef SetRectRgn
83 #undef CheckMenuItem
84 #undef DeleteMenu
85 #undef DrawMenuBar
86 #undef EnableMenuItem
87 #undef EqualRect
88 #undef FillRect
89 #undef FrameRect
90 #undef GetCursor
91 #undef GetMenu
92 #undef InvertRect
93 #undef IsWindowVisible
94 #undef MoveWindow
95 #undef OffsetRect
96 #undef PtInRect
97 #undef SetCursor
98 #undef SetRect
99 #undef ShowCursor
100 #undef ShowWindow
101 #undef UnionRect
103 #undef ULONG
104 #undef HRESULT
105 #undef STDMETHODCALLTYPE
107 #include <assert.h>
108 #include <stdio.h>
109 #include <stdarg.h>
111 #define NONAMELESSSTRUCT
112 #define NONAMELESSUNION
113 #define COBJMACROS
115 #include "windef.h"
116 #include "winbase.h"
117 #include "wtypes.h"
118 #include "winuser.h"
119 #include "dshow.h"
121 #include "wine/heap.h"
122 #include "wine/unicode.h"
123 #include "wine/debug.h"
124 #include "wine/strmbase.h"
126 #include "qtprivate.h"
127 #include "wineqtdecoder_classes.h"
129 WINE_DEFAULT_DEBUG_CHANNEL(qtsplitter);
131 typedef struct QTOutPin {
132 struct strmbase_source pin;
133 IQualityControl IQualityControl_iface;
134 SourceSeeking seeking;
136 AM_MEDIA_TYPE * pmt;
137 OutputQueue * queue;
138 } QTOutPin;
140 typedef struct QTInPin {
141 struct strmbase_sink pin;
142 GUID subType;
144 IAsyncReader *pReader;
145 IMemAllocator *pAlloc;
146 } QTInPin;
148 typedef struct QTSplitter {
149 struct strmbase_filter filter;
151 QTInPin pInputPin;
152 QTOutPin *pVideo_Pin;
153 QTOutPin *pAudio_Pin;
155 ALLOCATOR_PROPERTIES props;
157 Movie pQTMovie;
158 QTVisualContextRef vContext;
160 MovieAudioExtractionRef aSession;
161 HANDLE runEvent;
163 DWORD outputSize;
164 CRITICAL_SECTION csReceive;
166 TimeValue movie_time;
167 TimeValue movie_start;
168 TimeScale movie_scale;
170 HANDLE loaderThread;
171 HANDLE splitterThread;
172 } QTSplitter;
174 static const IBaseFilterVtbl QT_Vtbl;
175 static const IMediaSeekingVtbl QT_Seeking_Vtbl;
177 static HRESULT QT_AddPin(QTSplitter *filter, const WCHAR *name, const AM_MEDIA_TYPE *mt, BOOL video);
178 static HRESULT QT_RemoveOutputPins(QTSplitter *This);
180 static HRESULT WINAPI QTSplitter_ChangeStart(IMediaSeeking *iface);
181 static HRESULT WINAPI QTSplitter_ChangeStop(IMediaSeeking *iface);
182 static HRESULT WINAPI QTSplitter_ChangeRate(IMediaSeeking *iface);
184 static inline QTOutPin *impl_from_IMediaSeeking(IMediaSeeking *iface)
186 return CONTAINING_RECORD(iface, QTOutPin, seeking.IMediaSeeking_iface);
189 static inline QTSplitter *impl_from_strmbase_filter(struct strmbase_filter *iface)
191 return CONTAINING_RECORD(iface, QTSplitter, filter);
194 static inline QTSplitter *impl_from_IBaseFilter( IBaseFilter *iface )
196 return CONTAINING_RECORD(iface, QTSplitter, filter.IBaseFilter_iface);
199 static inline QTInPin *impl_from_IPin(IPin *iface)
201 return CONTAINING_RECORD(iface, QTInPin, pin.pin.IPin_iface);
205 * Base Filter
208 static struct strmbase_pin *qt_splitter_get_pin(struct strmbase_filter *base, unsigned int index)
210 QTSplitter *filter = impl_from_strmbase_filter(base);
212 if (index == 0)
213 return &filter->pInputPin.pin.pin;
214 else if (index == 1)
216 if (filter->pVideo_Pin)
217 return &filter->pVideo_Pin->pin.pin;
218 else if (filter->pAudio_Pin)
219 return &filter->pAudio_Pin->pin.pin;
221 else if (index == 2 && filter->pVideo_Pin && filter->pAudio_Pin)
222 return &filter->pAudio_Pin->pin.pin;
224 return NULL;
227 static void qt_splitter_destroy(struct strmbase_filter *iface)
229 QTSplitter *filter = impl_from_strmbase_filter(iface);
231 EnterCriticalSection(&filter->csReceive);
232 /* Don't need to clean up output pins, disconnecting input pin will do that */
233 if (filter->pInputPin.pin.pin.peer)
234 IPin_Disconnect(filter->pInputPin.pin.pin.peer);
236 if (filter->pInputPin.pAlloc)
237 IMemAllocator_Release(filter->pInputPin.pAlloc);
238 filter->pInputPin.pAlloc = NULL;
239 if (filter->pInputPin.pReader)
240 IAsyncReader_Release(filter->pInputPin.pReader);
241 filter->pInputPin.pReader = NULL;
243 if (filter->pQTMovie)
245 DisposeMovie(filter->pQTMovie);
246 filter->pQTMovie = NULL;
248 if (filter->vContext)
249 QTVisualContextRelease(filter->vContext);
250 if (filter->aSession)
251 MovieAudioExtractionEnd(filter->aSession);
253 ExitMoviesOnThread();
254 LeaveCriticalSection(&filter->csReceive);
256 if (filter->loaderThread)
258 WaitForSingleObject(filter->loaderThread, INFINITE);
259 CloseHandle(filter->loaderThread);
261 if (filter->splitterThread)
263 SetEvent(filter->runEvent);
264 WaitForSingleObject(filter->splitterThread, INFINITE);
265 CloseHandle(filter->splitterThread);
268 CloseHandle(filter->runEvent);
270 filter->csReceive.DebugInfo->Spare[0] = 0;
271 DeleteCriticalSection(&filter->csReceive);
272 strmbase_sink_cleanup(&filter->pInputPin.pin);
273 strmbase_filter_cleanup(&filter->filter);
275 CoTaskMemFree(filter);
278 static HRESULT qt_splitter_start_stream(struct strmbase_filter *iface, REFERENCE_TIME time)
280 QTSplitter *filter = impl_from_strmbase_filter(iface);
281 HRESULT hr;
283 EnterCriticalSection(&filter->csReceive);
285 if (filter->pVideo_Pin && filter->pVideo_Pin->pin.pin.peer
286 && FAILED(hr = IMemAllocator_Commit(filter->pVideo_Pin->pin.pAllocator)))
287 ERR("Failed to commit video allocator, hr %#x.\n", hr);
288 if (filter->pAudio_Pin && filter->pAudio_Pin->pin.pin.peer
289 && FAILED(hr = IMemAllocator_Commit(filter->pAudio_Pin->pin.pAllocator)))
290 ERR("Failed to commit audio allocator, hr %#x.\n", hr);
291 SetEvent(filter->runEvent);
293 LeaveCriticalSection(&filter->csReceive);
295 return S_OK;
298 static HRESULT qt_splitter_cleanup_stream(struct strmbase_filter *iface)
300 QTSplitter *filter = impl_from_strmbase_filter(iface);
302 EnterCriticalSection(&filter->csReceive);
303 IAsyncReader_BeginFlush(filter->pInputPin.pReader);
304 IAsyncReader_EndFlush(filter->pInputPin.pReader);
305 LeaveCriticalSection(&filter->csReceive);
307 return S_OK;
310 static const struct strmbase_filter_ops filter_ops =
312 .filter_get_pin = qt_splitter_get_pin,
313 .filter_destroy = qt_splitter_destroy,
314 .filter_start_stream = qt_splitter_start_stream,
315 .filter_cleanup_stream = qt_splitter_cleanup_stream,
318 static HRESULT sink_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *mt)
320 QTInPin *pin = impl_from_IPin(&iface->IPin_iface);
322 if (IsEqualGUID(&mt->majortype, &MEDIATYPE_Stream))
324 pin->subType = mt->subtype;
325 return S_OK;
327 return S_FALSE;
330 static HRESULT QT_Process_Movie(QTSplitter *filter);
332 static HRESULT qt_splitter_sink_connect(struct strmbase_sink *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
334 QTSplitter *filter = impl_from_strmbase_filter(iface->pin.filter);
335 ALLOCATOR_PROPERTIES props;
336 IMemAllocator *allocator;
337 HRESULT hr = S_OK;
339 filter->pInputPin.pReader = NULL;
341 if (FAILED(hr = IPin_QueryInterface(peer, &IID_IAsyncReader, (void **)&filter->pInputPin.pReader)))
342 return hr;
344 if (FAILED(hr = QT_Process_Movie(filter)))
346 IAsyncReader_Release(filter->pInputPin.pReader);
347 filter->pInputPin.pReader = NULL;
348 return hr;
351 filter->pInputPin.pAlloc = NULL;
352 props.cBuffers = 8;
353 props.cbAlign = 1;
354 props.cbBuffer = filter->outputSize + props.cbAlign;
355 props.cbPrefix = 0;
357 /* Some applications depend on IAsyncReader::RequestAllocator() passing a
358 * non-NULL preferred allocator. */
359 hr = CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC,
360 &IID_IMemAllocator, (void **)&allocator);
361 if (FAILED(hr))
362 goto err;
364 hr = IAsyncReader_RequestAllocator(filter->pInputPin.pReader, allocator, &props, &filter->pInputPin.pAlloc);
365 IMemAllocator_Release(allocator);
366 if (FAILED(hr))
368 WARN("Failed to get allocator, hr %#x.\n", hr);
369 goto err;
372 if (FAILED(hr = IMemAllocator_Commit(filter->pInputPin.pAlloc)))
374 WARN("Failed to commit allocator, hr %#x.\n", hr);
375 goto err;
378 return S_OK;
379 err:
380 QT_RemoveOutputPins(filter);
381 IAsyncReader_Release(filter->pInputPin.pReader);
382 return hr;
385 static void qt_splitter_sink_disconnect(struct strmbase_sink *iface)
387 QTSplitter *filter = impl_from_strmbase_filter(iface->pin.filter);
389 IMemAllocator_Decommit(filter->pInputPin.pAlloc);
390 QT_RemoveOutputPins(filter);
393 static const struct strmbase_sink_ops sink_ops =
395 .base.pin_query_accept = sink_query_accept,
396 .sink_connect = qt_splitter_sink_connect,
397 .sink_disconnect = qt_splitter_sink_disconnect,
400 HRESULT qt_splitter_create(IUnknown *outer, IUnknown **out)
402 QTSplitter *This;
403 static const WCHAR wcsInputPinName[] = {'I','n','p','u','t',' ','P','i','n',0};
405 EnterMoviesOnThread(0);
407 RegisterWineDataHandler();
409 if (!(This = CoTaskMemAlloc(sizeof(*This))))
410 return E_OUTOFMEMORY;
411 ZeroMemory(This,sizeof(*This));
413 strmbase_filter_init(&This->filter, outer, &CLSID_QTSplitter, &filter_ops);
414 strmbase_sink_init(&This->pInputPin.pin, &This->filter, wcsInputPinName, &sink_ops, NULL);
416 InitializeCriticalSection(&This->csReceive);
417 This->csReceive.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__": QTSplitter.csReceive");
419 This->pVideo_Pin = NULL;
420 This->pAudio_Pin = NULL;
421 This->aSession = NULL;
422 This->runEvent = CreateEventW(NULL, 0, 0, NULL);
424 TRACE("Created QT splitter %p.\n", This);
425 *out = &This->filter.IUnknown_inner;
426 return S_OK;
429 static OSErr QT_Create_Extract_Session(QTSplitter *filter)
431 AudioStreamBasicDescription aDesc;
432 OSErr err;
433 WAVEFORMATEX* pvi;
435 pvi = (WAVEFORMATEX*)filter->pAudio_Pin->pmt->pbFormat;
437 err = MovieAudioExtractionBegin(filter->pQTMovie, 0, &filter->aSession);
438 if (err != noErr)
440 ERR("Failed to begin Extraction session %i\n",err);
441 return err;
444 err = MovieAudioExtractionGetProperty(filter->aSession,
445 kQTPropertyClass_MovieAudioExtraction_Audio,
446 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
447 sizeof(AudioStreamBasicDescription), &aDesc, NULL);
449 if (err != noErr)
451 MovieAudioExtractionEnd(filter->aSession);
452 filter->aSession = NULL;
453 ERR("Failed to get session description %i\n",err);
454 return err;
457 aDesc.mFormatID = kAudioFormatLinearPCM;
458 aDesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger +
459 kAudioFormatFlagIsPacked;
460 aDesc.mFramesPerPacket = 1;
461 aDesc.mChannelsPerFrame = pvi->nChannels;
462 aDesc.mBitsPerChannel = pvi->wBitsPerSample;
463 aDesc.mSampleRate = pvi->nSamplesPerSec;
464 aDesc.mBytesPerFrame = (aDesc.mBitsPerChannel * aDesc.mChannelsPerFrame) / 8;
465 aDesc.mBytesPerPacket = aDesc.mBytesPerFrame * aDesc.mFramesPerPacket;
467 err = MovieAudioExtractionSetProperty(filter->aSession,
468 kQTPropertyClass_MovieAudioExtraction_Audio,
469 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
470 sizeof(AudioStreamBasicDescription), &aDesc);
472 if (aDesc.mFormatID != kAudioFormatLinearPCM)
474 ERR("Not PCM Wave\n");
475 err = -1;
477 if (aDesc.mFormatFlags != kLinearPCMFormatFlagIsSignedInteger +
478 kAudioFormatFlagIsPacked)
480 ERR("Unhandled Flags\n");
481 err = -1;
483 if (aDesc.mFramesPerPacket != 1)
485 ERR("Unhandled Frames per packet %li\n",aDesc.mFramesPerPacket);
486 err = -1;
488 if (aDesc.mChannelsPerFrame != pvi->nChannels)
490 ERR("Unhandled channel count %li\n",aDesc.mChannelsPerFrame);
491 err = -1;
493 if (aDesc.mBitsPerChannel != pvi->wBitsPerSample)
495 ERR("Unhandled bits per channel %li\n",aDesc.mBitsPerChannel);
496 err = -1;
498 if (aDesc.mSampleRate != pvi->nSamplesPerSec)
500 ERR("Unhandled sample rate %f\n",aDesc.mSampleRate);
501 err = -1;
504 if (err != noErr)
506 ERR("Failed to create Extraction Session\n");
507 MovieAudioExtractionEnd(filter->aSession);
508 filter->aSession = NULL;
511 return err;
514 static DWORD WINAPI QTSplitter_loading_thread(LPVOID data)
516 QTSplitter *This = (QTSplitter *)data;
518 if (This->pAudio_Pin)
520 /* according to QA1469 a movie has to be fully loaded before we
521 can reliably start the Extraction session.
523 If loaded earlier, then we only get an extraction session for
524 the part of the movie that is loaded at that time.
526 We are trying to load as much of the movie as we can before we
527 start extracting. However we can recreate the extraction session
528 again when we run out of loaded extraction frames. But we want
529 to try to minimize that.
532 EnterCriticalSection(&This->csReceive);
533 while(This->pQTMovie && GetMovieLoadState(This->pQTMovie) < kMovieLoadStateComplete)
535 MoviesTask(This->pQTMovie, 100);
536 LeaveCriticalSection(&This->csReceive);
537 Sleep(0);
538 EnterCriticalSection(&This->csReceive);
540 LeaveCriticalSection(&This->csReceive);
542 return 0;
545 static DWORD WINAPI QTSplitter_thread(LPVOID data)
547 QTSplitter *This = (QTSplitter *)data;
548 HRESULT hr = S_OK;
549 TimeValue next_time;
550 CVPixelBufferRef pixelBuffer = NULL;
551 OSStatus err;
552 TimeRecord tr;
554 WaitForSingleObject(This->runEvent, -1);
556 EnterCriticalSection(&This->csReceive);
557 if (!This->pQTMovie)
559 LeaveCriticalSection(&This->csReceive);
560 return 0;
563 /* Prime the pump: Needed for MPEG streams */
564 GetMovieNextInterestingTime(This->pQTMovie, nextTimeEdgeOK | nextTimeStep, 0, NULL, This->movie_time, 1, &next_time, NULL);
566 GetMovieTime(This->pQTMovie, &tr);
568 if (This->pAudio_Pin)
569 QT_Create_Extract_Session(This);
571 LeaveCriticalSection(&This->csReceive);
575 LONGLONG tStart=0, tStop=0;
576 LONGLONG mStart=0, mStop=0;
577 float time;
579 EnterCriticalSection(&This->csReceive);
580 if (!This->pQTMovie)
582 LeaveCriticalSection(&This->csReceive);
583 return 0;
586 GetMovieNextInterestingTime(This->pQTMovie, nextTimeStep, 0, NULL, This->movie_time, 1, &next_time, NULL);
588 if (next_time == -1)
590 TRACE("No next time\n");
591 LeaveCriticalSection(&This->csReceive);
592 break;
595 tr.value = SInt64ToWide(next_time);
596 SetMovieTime(This->pQTMovie, &tr);
597 MoviesTask(This->pQTMovie,0);
598 QTVisualContextTask(This->vContext);
600 TRACE("In loop at time %ld\n",This->movie_time);
601 TRACE("In Next time %ld\n",next_time);
603 mStart = This->movie_time;
604 mStop = next_time;
606 time = (float)(This->movie_time - This->movie_start) / This->movie_scale;
607 tStart = time * 10000000;
608 time = (float)(next_time - This->movie_start) / This->movie_scale;
609 tStop = time * 10000000;
611 /* Deliver Audio */
612 if (This->pAudio_Pin && This->pAudio_Pin->pin.pin.peer && This->aSession)
614 int data_size=0;
615 BYTE* ptr;
616 IMediaSample *sample = NULL;
617 AudioBufferList aData;
618 UInt32 flags;
619 UInt32 frames;
620 WAVEFORMATEX* pvi;
621 float duration;
623 pvi = (WAVEFORMATEX*)This->pAudio_Pin->pmt->pbFormat;
625 hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->pAudio_Pin->pin, &sample, NULL, NULL, 0);
627 if (FAILED(hr))
629 ERR("Audio: Unable to get delivery buffer (%x)\n", hr);
630 goto audio_error;
633 hr = IMediaSample_GetPointer(sample, &ptr);
634 if (FAILED(hr))
636 ERR("Audio: Unable to get pointer to buffer (%x)\n", hr);
637 goto audio_error;
640 duration = (float)next_time / This->movie_scale;
641 time = (float)This->movie_time / This->movie_scale;
642 duration -= time;
643 frames = pvi->nSamplesPerSec * duration;
644 TRACE("Need audio for %f seconds (%li frames)\n",duration,frames);
646 data_size = IMediaSample_GetSize(sample);
647 if (data_size < frames * pvi->nBlockAlign)
648 FIXME("Audio buffer is too small\n");
650 aData.mNumberBuffers = 1;
651 aData.mBuffers[0].mNumberChannels = pvi->nChannels;
652 aData.mBuffers[0].mDataByteSize = data_size;
653 aData.mBuffers[0].mData = ptr;
655 err = MovieAudioExtractionFillBuffer(This->aSession, &frames, &aData, &flags);
656 if (frames == 0)
658 TimeRecord etr;
660 /* Ran out of frames, Restart the extraction session */
661 TRACE("Restarting extraction session\n");
662 MovieAudioExtractionEnd(This->aSession);
663 This->aSession = NULL;
664 QT_Create_Extract_Session(This);
666 etr = tr;
667 etr.value = SInt64ToWide(This->movie_time);
668 MovieAudioExtractionSetProperty(This->aSession,
669 kQTPropertyClass_MovieAudioExtraction_Movie,
670 kQTMovieAudioExtractionMoviePropertyID_CurrentTime,
671 sizeof(TimeRecord), &etr );
673 frames = pvi->nSamplesPerSec * duration;
674 aData.mNumberBuffers = 1;
675 aData.mBuffers[0].mNumberChannels = pvi->nChannels;
676 aData.mBuffers[0].mDataByteSize = data_size;
677 aData.mBuffers[0].mData = ptr;
679 MovieAudioExtractionFillBuffer(This->aSession, &frames, &aData, &flags);
682 TRACE("Got %i frames\n",(int)frames);
684 IMediaSample_SetActualDataLength(sample, frames * pvi->nBlockAlign);
686 IMediaSample_SetMediaTime(sample, &mStart, &mStop);
687 IMediaSample_SetTime(sample, &tStart, &tStop);
689 hr = OutputQueue_Receive(This->pAudio_Pin->queue, sample);
690 TRACE("Audio Delivered (%x)\n",hr);
692 audio_error:
693 if (sample)
694 IMediaSample_Release(sample);
696 else
697 TRACE("Audio Pin not connected or no Audio\n");
699 /* Deliver Video */
700 if (This->pVideo_Pin && QTVisualContextIsNewImageAvailable(This->vContext,0))
702 err = QTVisualContextCopyImageForTime(This->vContext, NULL, NULL, &pixelBuffer);
703 if (err == noErr)
705 int data_size=0;
706 BYTE* ptr;
707 IMediaSample *sample = NULL;
709 hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->pVideo_Pin->pin, &sample, NULL, NULL, 0);
710 if (FAILED(hr))
712 ERR("Video: Unable to get delivery buffer (%x)\n", hr);
713 goto video_error;
716 data_size = IMediaSample_GetSize(sample);
717 if (data_size < This->outputSize)
719 ERR("Sample size is too small %d < %d\n", data_size, This->outputSize)
721 hr = E_FAIL;
722 goto video_error;
725 hr = IMediaSample_GetPointer(sample, &ptr);
726 if (FAILED(hr))
728 ERR("Video: Unable to get pointer to buffer (%x)\n", hr);
729 goto video_error;
732 hr = AccessPixelBufferPixels( pixelBuffer, ptr);
733 if (FAILED(hr))
735 ERR("Failed to access Pixels\n");
736 goto video_error;
739 IMediaSample_SetActualDataLength(sample, This->outputSize);
741 IMediaSample_SetMediaTime(sample, &mStart, &mStop);
742 IMediaSample_SetTime(sample, &tStart, &tStop);
744 hr = OutputQueue_Receive(This->pVideo_Pin->queue, sample);
745 TRACE("Video Delivered (%x)\n",hr);
747 video_error:
748 if (sample)
749 IMediaSample_Release(sample);
750 if (pixelBuffer)
751 CVPixelBufferRelease(pixelBuffer);
754 else
755 TRACE("No video to deliver\n");
757 This->movie_time = next_time;
758 LeaveCriticalSection(&This->csReceive);
759 } while (hr == S_OK);
761 if (This->pAudio_Pin)
762 OutputQueue_EOS(This->pAudio_Pin->queue);
763 if (This->pVideo_Pin)
764 OutputQueue_EOS(This->pVideo_Pin->queue);
766 return hr;
769 static void free_source_pin(QTOutPin *pin)
771 if (pin->pin.pin.peer)
773 if (SUCCEEDED(IMemAllocator_Decommit(pin->pin.pAllocator)))
774 IPin_Disconnect(pin->pin.pin.peer);
775 IPin_Disconnect(&pin->pin.pin.IPin_iface);
778 DeleteMediaType(pin->pmt);
779 strmbase_seeking_cleanup(&pin->seeking);
780 strmbase_source_cleanup(&pin->pin);
781 heap_free(pin);
785 * Input Pin
787 static HRESULT QT_RemoveOutputPins(QTSplitter *This)
789 if (This->pVideo_Pin)
791 OutputQueue_Destroy(This->pVideo_Pin->queue);
792 free_source_pin(This->pVideo_Pin);
793 This->pVideo_Pin = NULL;
795 if (This->pAudio_Pin)
797 OutputQueue_Destroy(This->pAudio_Pin->queue);
798 free_source_pin(This->pAudio_Pin);
799 This->pAudio_Pin = NULL;
802 BaseFilterImpl_IncrementPinVersion(&This->filter);
803 return S_OK;
806 static HRESULT QT_Process_Video_Track(QTSplitter* filter, Track trk)
808 AM_MEDIA_TYPE amt;
809 VIDEOINFOHEADER * pvi;
810 HRESULT hr = S_OK;
811 OSErr err;
812 static const WCHAR szwVideoOut[] = {'V','i','d','e','o',0};
813 CFMutableDictionaryRef pixelBufferOptions = NULL;
814 CFMutableDictionaryRef visualContextOptions = NULL;
815 CFNumberRef n = NULL;
816 int t;
817 DWORD outputWidth, outputHeight, outputDepth;
818 Fixed trackWidth, trackHeight;
819 Media videoMedia;
820 long sampleCount;
821 TimeValue64 duration;
822 TimeScale timeScale;
824 ZeroMemory(&amt, sizeof(amt));
825 amt.formattype = FORMAT_VideoInfo;
826 amt.majortype = MEDIATYPE_Video;
827 amt.subtype = MEDIASUBTYPE_RGB24;
829 GetTrackDimensions(trk, &trackWidth, &trackHeight);
831 outputDepth = 3;
832 outputWidth = Fix2Long(trackWidth);
833 outputHeight = Fix2Long(trackHeight);
834 TRACE("Width %i Height %i\n",outputWidth, outputHeight);
836 amt.cbFormat = sizeof(VIDEOINFOHEADER);
837 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
838 ZeroMemory(amt.pbFormat, amt.cbFormat);
839 pvi = (VIDEOINFOHEADER *)amt.pbFormat;
840 pvi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
841 pvi->bmiHeader.biWidth = outputWidth;
842 pvi->bmiHeader.biHeight = outputHeight;
843 pvi->bmiHeader.biPlanes = 1;
844 pvi->bmiHeader.biBitCount = 24;
845 pvi->bmiHeader.biCompression = BI_RGB;
846 pvi->bmiHeader.biSizeImage = outputWidth * outputHeight * outputDepth;
848 filter->outputSize = pvi->bmiHeader.biSizeImage;
849 amt.lSampleSize = 0;
851 pixelBufferOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
853 t = k32ARGBPixelFormat;
854 n = CFNumberCreate(NULL, kCFNumberIntType, &t);
855 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferPixelFormatTypeKey, n);
856 CFRelease(n);
858 n = CFNumberCreate(NULL, kCFNumberIntType, &outputWidth);
859 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferWidthKey, n);
860 CFRelease(n);
862 n = CFNumberCreate(NULL, kCFNumberIntType, &outputHeight);
863 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferHeightKey, n);
864 CFRelease(n);
866 t = 16;
867 n = CFNumberCreate(NULL, kCFNumberIntType, &t);
868 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferBytesPerRowAlignmentKey, n);
869 CFRelease(n);
871 visualContextOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
873 CFDictionarySetValue(visualContextOptions, kQTVisualContextPixelBufferAttributesKey, pixelBufferOptions);
875 err = QTPixelBufferContextCreate(NULL, visualContextOptions,&filter->vContext);
876 CFRelease(pixelBufferOptions);
877 CFRelease(visualContextOptions);
878 if (err != noErr)
880 ERR("Failed to create Visual Context\n");
881 return E_FAIL;
884 err = SetMovieVisualContext(filter->pQTMovie, filter->vContext);
885 if (err != noErr)
887 ERR("Failed to set Visual Context\n");
888 return E_FAIL;
891 videoMedia = GetTrackMedia(trk);
892 sampleCount = GetMediaSampleCount(videoMedia);
893 timeScale = GetMediaTimeScale(videoMedia);
894 duration = GetMediaDisplayDuration(videoMedia);
895 pvi->AvgTimePerFrame = (100000.0 * sampleCount * timeScale) / duration;
897 hr = QT_AddPin(filter, szwVideoOut, &amt, TRUE);
898 if (FAILED(hr))
899 ERR("Failed to add Video Track\n");
900 else
901 TRACE("Video Pin %p\n",filter->pVideo_Pin);
903 return hr;
906 static HRESULT QT_Process_Audio_Track(QTSplitter* filter, Track trk)
908 AM_MEDIA_TYPE amt;
909 WAVEFORMATEX* pvi;
910 HRESULT hr = S_OK;
911 static const WCHAR szwAudioOut[] = {'A','u','d','i','o',0};
912 Media audioMedia;
914 SoundDescriptionHandle aDesc = (SoundDescriptionHandle) NewHandle(sizeof(SoundDescription));
916 audioMedia = GetTrackMedia(trk);
917 GetMediaSampleDescription(audioMedia, 1, (SampleDescriptionHandle)aDesc);
919 ZeroMemory(&amt, sizeof(amt));
920 amt.formattype = FORMAT_WaveFormatEx;
921 amt.majortype = MEDIATYPE_Audio;
922 amt.subtype = MEDIASUBTYPE_PCM;
923 amt.bTemporalCompression = 0;
925 amt.cbFormat = sizeof(WAVEFORMATEX);
926 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
927 ZeroMemory(amt.pbFormat, amt.cbFormat);
928 pvi = (WAVEFORMATEX*)amt.pbFormat;
930 pvi->cbSize = sizeof(WAVEFORMATEX);
931 pvi->wFormatTag = WAVE_FORMAT_PCM;
932 pvi->nChannels = ((SoundDescription)**aDesc).numChannels;
933 if (pvi->nChannels < 1 || pvi->nChannels > 2)
934 pvi->nChannels = 2;
935 pvi->nSamplesPerSec = (((SoundDescription)**aDesc).sampleRate/65536);
936 if (pvi->nSamplesPerSec < 8000 || pvi->nChannels > 48000)
937 pvi->nSamplesPerSec = 44100;
938 pvi->wBitsPerSample = ((SoundDescription)**aDesc).sampleSize;
939 if (pvi->wBitsPerSample < 8 || pvi->wBitsPerSample > 32)
940 pvi->wBitsPerSample = 16;
941 pvi->nBlockAlign = (pvi->nChannels * pvi->wBitsPerSample) / 8;
942 pvi->nAvgBytesPerSec = pvi->nSamplesPerSec * pvi->nBlockAlign;
944 DisposeHandle((Handle)aDesc);
946 hr = QT_AddPin(filter, szwAudioOut, &amt, FALSE);
947 if (FAILED(hr))
948 ERR("Failed to add Audio Track\n");
949 else
950 TRACE("Audio Pin %p\n",filter->pAudio_Pin);
951 return hr;
954 static HRESULT QT_Process_Movie(QTSplitter* filter)
956 HRESULT hr = S_OK;
957 OSErr err;
958 WineDataRefRecord ptrDataRefRec;
959 Handle dataRef = NULL;
960 Track trk;
961 short id = 0;
962 DWORD tid;
963 LONGLONG time, duration;
965 TRACE("Trying movie connect\n");
967 ptrDataRefRec.pReader = filter->pInputPin.pReader;
968 ptrDataRefRec.streamSubtype = filter->pInputPin.subType;
969 PtrToHand( &ptrDataRefRec, &dataRef, sizeof(WineDataRefRecord));
971 err = NewMovieFromDataRef(&filter->pQTMovie, newMovieActive|newMovieDontInteractWithUser|newMovieDontAutoUpdateClock|newMovieDontAskUnresolvedDataRefs|newMovieAsyncOK, &id, dataRef, 'WINE');
973 DisposeHandle(dataRef);
975 if (err != noErr)
977 FIXME("QuickTime cannot handle media type(%i)\n",err);
978 return VFW_E_TYPE_NOT_ACCEPTED;
981 PrePrerollMovie(filter->pQTMovie, 0, fixed1, NULL, NULL);
982 PrerollMovie(filter->pQTMovie, 0, fixed1);
983 GoToBeginningOfMovie(filter->pQTMovie);
984 SetMovieActive(filter->pQTMovie,TRUE);
986 if (GetMovieLoadState(filter->pQTMovie) < kMovieLoadStateLoaded)
987 MoviesTask(filter->pQTMovie,100);
989 trk = GetMovieIndTrackType(filter->pQTMovie, 1, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
990 TRACE("%p is a video track\n",trk);
991 if (trk)
992 hr = QT_Process_Video_Track(filter, trk);
994 if (FAILED(hr))
995 return hr;
997 trk = GetMovieIndTrackType(filter->pQTMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
998 TRACE("%p is an audio track\n",trk);
999 if (trk)
1000 hr = QT_Process_Audio_Track(filter, trk);
1002 time = GetMovieDuration(filter->pQTMovie);
1003 filter->movie_scale = GetMovieTimeScale(filter->pQTMovie);
1004 duration = ((double)time / filter->movie_scale) * 10000000;
1005 TRACE("Movie duration is %s.\n", wine_dbgstr_longlong(duration));
1006 if (filter->pVideo_Pin)
1007 filter->pVideo_Pin->seeking.llStop = filter->pVideo_Pin->seeking.llDuration = duration;
1008 if (filter->pAudio_Pin)
1009 filter->pAudio_Pin->seeking.llStop = filter->pAudio_Pin->seeking.llDuration = duration;
1011 filter->loaderThread = CreateThread(NULL, 0, QTSplitter_loading_thread, filter, 0, &tid);
1012 if (filter->loaderThread)
1013 TRACE("Created loading thread 0x%08x\n", tid);
1014 filter->splitterThread = CreateThread(NULL, 0, QTSplitter_thread, filter, 0, &tid);
1015 if (filter->splitterThread)
1016 TRACE("Created processing thread 0x%08x\n", tid);
1017 else
1018 hr = HRESULT_FROM_WIN32(GetLastError());
1020 return hr;
1023 static inline QTOutPin *impl_source_from_strmbase_pin(struct strmbase_pin *iface)
1025 return CONTAINING_RECORD(iface, QTOutPin, pin.pin);
1028 static inline QTOutPin *impl_QTOutPin_from_BaseOutputPin(struct strmbase_source *iface)
1030 return CONTAINING_RECORD(iface, QTOutPin, pin);
1033 static HRESULT source_query_interface(struct strmbase_pin *iface, REFIID iid, void **out)
1035 QTOutPin *pin = impl_source_from_strmbase_pin(&iface->IPin_iface);
1037 if (IsEqualGUID(iid, &IID_IMediaSeeking))
1038 *out = &pin->seeking.IMediaSeeking_iface;
1039 else if (IsEqualGUID(iid, &IID_IQualityControl))
1040 *out = &pin->IQualityControl_iface;
1041 else
1042 return E_NOINTERFACE;
1044 IUnknown_AddRef((IUnknown *)*out);
1045 return S_OK;
1048 static HRESULT source_query_accept(struct strmbase_pin *base, const AM_MEDIA_TYPE *amt)
1050 FIXME("(%p) stub\n", base);
1051 return S_OK;
1054 static HRESULT source_get_media_type(struct strmbase_pin *iface, unsigned int iPosition, AM_MEDIA_TYPE *pmt)
1056 QTOutPin *This = impl_source_from_strmbase_pin(iface);
1058 if (iPosition > 0)
1059 return VFW_S_NO_MORE_ITEMS;
1060 CopyMediaType(pmt, This->pmt);
1061 return S_OK;
1064 static HRESULT WINAPI QTOutPin_DecideBufferSize(struct strmbase_source *iface,
1065 IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
1067 /* Unused */
1068 return S_OK;
1071 static HRESULT WINAPI QTOutPin_DecideAllocator(struct strmbase_source *iface,
1072 IMemInputPin *pPin, IMemAllocator **pAlloc)
1074 HRESULT hr;
1075 QTOutPin *This = impl_QTOutPin_from_BaseOutputPin(iface);
1076 QTSplitter *QTfilter = impl_from_strmbase_filter(This->pin.pin.filter);
1078 *pAlloc = NULL;
1079 if (QTfilter->pInputPin.pAlloc)
1081 hr = IMemInputPin_NotifyAllocator(pPin, QTfilter->pInputPin.pAlloc, FALSE);
1082 if (SUCCEEDED(hr))
1084 *pAlloc = QTfilter->pInputPin.pAlloc;
1085 IMemAllocator_AddRef(*pAlloc);
1088 else
1089 hr = VFW_E_NO_ALLOCATOR;
1091 return hr;
1094 static inline QTOutPin *impl_from_IQualityControl( IQualityControl *iface )
1096 return CONTAINING_RECORD(iface, QTOutPin, IQualityControl_iface);
1099 HRESULT WINAPI QT_QualityControl_QueryInterface(IQualityControl *iface, REFIID riid, void **ppv)
1101 QTOutPin *This = impl_from_IQualityControl(iface);
1102 return IPin_QueryInterface(&This->pin.pin.IPin_iface, riid, ppv);
1105 ULONG WINAPI QT_QualityControl_AddRef(IQualityControl *iface)
1107 QTOutPin *This = impl_from_IQualityControl(iface);
1108 return IPin_AddRef(&This->pin.pin.IPin_iface);
1111 ULONG WINAPI QT_QualityControl_Release(IQualityControl *iface)
1113 QTOutPin *This = impl_from_IQualityControl(iface);
1114 return IPin_Release(&This->pin.pin.IPin_iface);
1117 static HRESULT WINAPI QT_QualityControl_Notify(IQualityControl *iface, IBaseFilter *sender, Quality qm)
1119 REFERENCE_TIME late = qm.Late;
1120 if (qm.Late < 0 && -qm.Late > qm.TimeStamp)
1121 late = -qm.TimeStamp;
1122 /* TODO: Do Something */
1123 return S_OK;
1126 HRESULT WINAPI QT_QualityControl_SetSink(IQualityControl *iface, IQualityControl *tonotify)
1128 /* Do nothing */
1129 return S_OK;
1132 static const IQualityControlVtbl QTOutPin_QualityControl_Vtbl = {
1133 QT_QualityControl_QueryInterface,
1134 QT_QualityControl_AddRef,
1135 QT_QualityControl_Release,
1136 QT_QualityControl_Notify,
1137 QT_QualityControl_SetSink
1140 static const struct strmbase_source_ops source_ops =
1142 .base.pin_query_accept = source_query_accept,
1143 .base.pin_get_media_type = source_get_media_type,
1144 .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection,
1145 .pfnDecideBufferSize = QTOutPin_DecideBufferSize,
1146 .pfnDecideAllocator = QTOutPin_DecideAllocator,
1149 static const OutputQueueFuncTable output_OutputQueueFuncTable = {
1150 OutputQueueImpl_ThreadProc
1153 static HRESULT QT_AddPin(QTSplitter *filter, const WCHAR *name,
1154 const AM_MEDIA_TYPE *mt, BOOL video)
1156 QTOutPin *pin;
1158 if (!(pin = heap_alloc_zero(sizeof(*pin))))
1159 return E_OUTOFMEMORY;
1161 if (video)
1162 filter->pVideo_Pin = pin;
1163 else
1164 filter->pAudio_Pin = pin;
1166 strmbase_source_init(&pin->pin, &filter->filter, name, &source_ops);
1167 pin->pmt = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
1168 CopyMediaType(pin->pmt, mt);
1169 pin->IQualityControl_iface.lpVtbl = &QTOutPin_QualityControl_Vtbl;
1170 strmbase_seeking_init(&pin->seeking, &QT_Seeking_Vtbl,
1171 QTSplitter_ChangeStop, QTSplitter_ChangeStart, QTSplitter_ChangeRate);
1172 BaseFilterImpl_IncrementPinVersion(&filter->filter);
1174 return OutputQueue_Construct(&pin->pin, TRUE, TRUE, 5, FALSE,
1175 THREAD_PRIORITY_NORMAL, &output_OutputQueueFuncTable, &pin->queue);
1178 static HRESULT WINAPI QTSplitter_ChangeStart(IMediaSeeking *iface)
1180 QTOutPin *pin = impl_from_IMediaSeeking(iface);
1181 QTSplitter *filter = impl_from_strmbase_filter(pin->pin.pin.filter);
1182 TRACE("(%p)\n", iface);
1183 EnterCriticalSection(&filter->csReceive);
1184 filter->movie_start = filter->movie_time = (pin->seeking.llCurrent * filter->movie_scale)/10000000;
1185 LeaveCriticalSection(&filter->csReceive);
1186 return S_OK;
1189 static HRESULT WINAPI QTSplitter_ChangeStop(IMediaSeeking *iface)
1191 FIXME("(%p) filter hasn't implemented stop position change!\n", iface);
1192 return S_OK;
1195 static HRESULT WINAPI QTSplitter_ChangeRate(IMediaSeeking *iface)
1197 FIXME("(%p) filter hasn't implemented rate change!\n", iface);
1198 return S_OK;
1201 static HRESULT WINAPI QT_Seeking_QueryInterface(IMediaSeeking *iface, REFIID iid, void **out)
1203 QTOutPin *pin = impl_from_IMediaSeeking(iface);
1204 return IPin_QueryInterface(&pin->pin.pin.IPin_iface, iid, out);
1207 static ULONG WINAPI QT_Seeking_AddRef(IMediaSeeking * iface)
1209 QTOutPin *pin = impl_from_IMediaSeeking(iface);
1210 return IPin_AddRef(&pin->pin.pin.IPin_iface);
1213 static ULONG WINAPI QT_Seeking_Release(IMediaSeeking * iface)
1215 QTOutPin *pin = impl_from_IMediaSeeking(iface);
1216 return IPin_Release(&pin->pin.pin.IPin_iface);
1219 static const IMediaSeekingVtbl QT_Seeking_Vtbl =
1221 QT_Seeking_QueryInterface,
1222 QT_Seeking_AddRef,
1223 QT_Seeking_Release,
1224 SourceSeekingImpl_GetCapabilities,
1225 SourceSeekingImpl_CheckCapabilities,
1226 SourceSeekingImpl_IsFormatSupported,
1227 SourceSeekingImpl_QueryPreferredFormat,
1228 SourceSeekingImpl_GetTimeFormat,
1229 SourceSeekingImpl_IsUsingTimeFormat,
1230 SourceSeekingImpl_SetTimeFormat,
1231 SourceSeekingImpl_GetDuration,
1232 SourceSeekingImpl_GetStopPosition,
1233 SourceSeekingImpl_GetCurrentPosition,
1234 SourceSeekingImpl_ConvertTimeFormat,
1235 SourceSeekingImpl_SetPositions,
1236 SourceSeekingImpl_GetPositions,
1237 SourceSeekingImpl_GetAvailable,
1238 SourceSeekingImpl_SetRate,
1239 SourceSeekingImpl_GetRate,
1240 SourceSeekingImpl_GetPreroll