windowscodecs: Add support for more types of IFD fields.
[wine/multimedia.git] / dlls / wineqtdecoder / qtsplitter.c
blobb58419839c7d8cd48675563aa85e6a8184b642c0
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 DPRINTF
71 #undef GetCurrentProcess
72 #undef AnimatePalette
73 #undef EqualRgn
74 #undef FillRgn
75 #undef FrameRgn
76 #undef GetPixel
77 #undef InvertRgn
78 #undef LineTo
79 #undef OffsetRgn
80 #undef PaintRgn
81 #undef Polygon
82 #undef ResizePalette
83 #undef SetRectRgn
84 #undef CheckMenuItem
85 #undef DeleteMenu
86 #undef DrawMenuBar
87 #undef EnableMenuItem
88 #undef EqualRect
89 #undef FillRect
90 #undef FrameRect
91 #undef GetCursor
92 #undef GetMenu
93 #undef InvertRect
94 #undef IsWindowVisible
95 #undef MoveWindow
96 #undef OffsetRect
97 #undef PtInRect
98 #undef SetCursor
99 #undef SetRect
100 #undef ShowCursor
101 #undef ShowWindow
102 #undef UnionRect
104 #undef ULONG
105 #undef HRESULT
106 #undef DPRINTF
107 #undef STDMETHODCALLTYPE
109 #include <assert.h>
110 #include <stdio.h>
111 #include <stdarg.h>
113 #define NONAMELESSSTRUCT
114 #define NONAMELESSUNION
115 #define COBJMACROS
117 #include "windef.h"
118 #include "winbase.h"
119 #include "wtypes.h"
120 #include "winuser.h"
121 #include "dshow.h"
123 #include "wine/unicode.h"
124 #include "wine/debug.h"
125 #include "wine/strmbase.h"
127 #include "qtprivate.h"
129 WINE_DEFAULT_DEBUG_CHANNEL(qtsplitter);
130 extern CLSID CLSID_QTSplitter;
132 typedef struct QTOutPin {
133 BaseOutputPin pin;
134 IQualityControl IQualityControl_iface;
136 AM_MEDIA_TYPE * pmt;
137 OutputQueue * queue;
138 } QTOutPin;
140 typedef struct QTInPin {
141 BasePin pin;
142 GUID subType;
144 IAsyncReader *pReader;
145 IMemAllocator *pAlloc;
146 } QTInPin;
148 typedef struct QTSplitter {
149 BaseFilter 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 FILTER_STATE state;
165 CRITICAL_SECTION csReceive;
167 SourceSeeking sourceSeeking;
168 TimeValue movie_time;
169 TimeValue movie_start;
170 TimeScale movie_scale;
171 } QTSplitter;
173 static const IPinVtbl QT_OutputPin_Vtbl;
174 static const IPinVtbl QT_InputPin_Vtbl;
175 static const IBaseFilterVtbl QT_Vtbl;
176 static const IMediaSeekingVtbl QT_Seeking_Vtbl;
178 static HRESULT QT_AddPin(QTSplitter *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt, BOOL video);
179 static HRESULT QT_RemoveOutputPins(QTSplitter *This);
181 static HRESULT WINAPI QTSplitter_ChangeStart(IMediaSeeking *iface);
182 static HRESULT WINAPI QTSplitter_ChangeStop(IMediaSeeking *iface);
183 static HRESULT WINAPI QTSplitter_ChangeRate(IMediaSeeking *iface);
185 static inline QTSplitter *impl_from_IMediaSeeking( IMediaSeeking *iface )
187 return CONTAINING_RECORD(iface, QTSplitter, sourceSeeking.IMediaSeeking_iface);
190 static inline QTSplitter *impl_from_BaseFilter( BaseFilter *iface )
192 return CONTAINING_RECORD(iface, QTSplitter, filter);
195 static inline QTSplitter *impl_from_IBaseFilter( IBaseFilter *iface )
197 return CONTAINING_RECORD(iface, QTSplitter, filter.IBaseFilter_iface);
201 * Base Filter
204 static IPin* WINAPI QT_GetPin(BaseFilter *iface, int pos)
206 QTSplitter *This = impl_from_BaseFilter(iface);
207 TRACE("Asking for pos %x\n", pos);
209 if (pos > 2 || pos < 0)
210 return NULL;
211 switch (pos)
213 case 0:
214 IPin_AddRef(&This->pInputPin.pin.IPin_iface);
215 return &This->pInputPin.pin.IPin_iface;
216 case 1:
217 if (This->pVideo_Pin)
218 IPin_AddRef(&This->pVideo_Pin->pin.pin.IPin_iface);
219 return &This->pVideo_Pin->pin.pin.IPin_iface;
220 case 2:
221 if (This->pAudio_Pin)
222 IPin_AddRef(&This->pAudio_Pin->pin.pin.IPin_iface);
223 return &This->pAudio_Pin->pin.pin.IPin_iface;
224 default:
225 return NULL;
229 static LONG WINAPI QT_GetPinCount(BaseFilter *iface)
231 QTSplitter *This = impl_from_BaseFilter(iface);
232 int c = 1;
233 if (This->pAudio_Pin) c++;
234 if (This->pVideo_Pin) c++;
235 return c;
238 static const BaseFilterFuncTable BaseFuncTable = {
239 QT_GetPin,
240 QT_GetPinCount
243 IUnknown * CALLBACK QTSplitter_create(IUnknown *punkout, HRESULT *phr)
245 IUnknown *obj = NULL;
246 PIN_INFO *piInput;
247 QTSplitter *This;
248 static const WCHAR wcsInputPinName[] = {'I','n','p','u','t',' ','P','i','n',0};
250 EnterMovies();
252 RegisterWineDataHandler();
254 This = CoTaskMemAlloc(sizeof(*This));
255 obj = (IUnknown*)This;
256 if (!This)
258 *phr = E_OUTOFMEMORY;
259 return NULL;
261 ZeroMemory(This,sizeof(*This));
263 BaseFilter_Init(&This->filter, &QT_Vtbl, &CLSID_QTSplitter, (DWORD_PTR)(__FILE__ ": QTSplitter.csFilter"), &BaseFuncTable);
265 InitializeCriticalSection(&This->csReceive);
266 This->csReceive.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__": QTSplitter.csReceive");
268 This->pVideo_Pin = NULL;
269 This->pAudio_Pin = NULL;
270 This->state = State_Stopped;
271 This->aSession = NULL;
272 This->runEvent = CreateEventW(NULL, 0, 0, NULL);
274 piInput = &This->pInputPin.pin.pinInfo;
275 piInput->dir = PINDIR_INPUT;
276 piInput->pFilter = &This->filter.IBaseFilter_iface;
277 lstrcpynW(piInput->achName, wcsInputPinName, sizeof(piInput->achName) / sizeof(piInput->achName[0]));
278 This->pInputPin.pin.IPin_iface.lpVtbl = &QT_InputPin_Vtbl;
279 This->pInputPin.pin.refCount = 1;
280 This->pInputPin.pin.pConnectedTo = NULL;
281 This->pInputPin.pin.pCritSec = &This->filter.csFilter;
283 SourceSeeking_Init(&This->sourceSeeking, &QT_Seeking_Vtbl, QTSplitter_ChangeStop, QTSplitter_ChangeStart, QTSplitter_ChangeRate, &This->filter.csFilter);
285 *phr = S_OK;
286 return obj;
289 static void QT_Destroy(QTSplitter *This)
291 IPin *connected = NULL;
292 ULONG pinref;
294 TRACE("Destroying\n");
296 /* Don't need to clean up output pins, disconnecting input pin will do that */
297 IPin_ConnectedTo(&This->pInputPin.pin.IPin_iface, &connected);
298 if (connected)
300 IPin_Disconnect(connected);
301 IPin_Release(connected);
303 pinref = IPin_Release(&This->pInputPin.pin.IPin_iface);
304 if (pinref)
306 ERR("pinref should be null, is %u, destroying anyway\n", pinref);
307 assert((LONG)pinref > 0);
309 while (pinref)
310 pinref = IPin_Release(&This->pInputPin.pin.IPin_iface);
313 if (This->pQTMovie)
314 DisposeMovie(This->pQTMovie);
315 if (This->vContext)
316 QTVisualContextRelease(This->vContext);
317 if (This->aSession)
318 MovieAudioExtractionEnd(This->aSession);
319 CloseHandle(This->runEvent);
321 ExitMovies();
323 This->csReceive.DebugInfo->Spare[0] = 0;
324 DeleteCriticalSection(&This->csReceive);
326 CoTaskMemFree(This);
329 static HRESULT WINAPI QT_QueryInterface(IBaseFilter *iface, REFIID riid, LPVOID *ppv)
331 QTSplitter *This = impl_from_IBaseFilter(iface);
332 TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
334 *ppv = NULL;
336 if (IsEqualIID(riid, &IID_IUnknown))
337 *ppv = This;
338 else if (IsEqualIID(riid, &IID_IPersist))
339 *ppv = This;
340 else if (IsEqualIID(riid, &IID_IMediaFilter))
341 *ppv = This;
342 else if (IsEqualIID(riid, &IID_IBaseFilter))
343 *ppv = This;
344 else if (IsEqualIID(riid, &IID_IMediaSeeking))
345 *ppv = &This->sourceSeeking;
347 if (*ppv)
349 IUnknown_AddRef((IUnknown *)(*ppv));
350 return S_OK;
353 if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow) &&
354 !IsEqualIID(riid, &IID_IAMFilterMiscFlags))
355 FIXME("No interface for %s!\n", debugstr_guid(riid));
357 return E_NOINTERFACE;
360 static ULONG WINAPI QT_Release(IBaseFilter *iface)
362 QTSplitter *This = impl_from_IBaseFilter(iface);
363 ULONG refCount = BaseFilterImpl_Release(iface);
365 TRACE("(%p)->() Release from %d\n", This, refCount + 1);
367 if (!refCount)
368 QT_Destroy(This);
370 return refCount;
373 static HRESULT WINAPI QT_Stop(IBaseFilter *iface)
375 QTSplitter *This = impl_from_IBaseFilter(iface);
377 TRACE("()\n");
379 EnterCriticalSection(&This->csReceive);
380 IAsyncReader_BeginFlush(This->pInputPin.pReader);
381 IAsyncReader_EndFlush(This->pInputPin.pReader);
382 LeaveCriticalSection(&This->csReceive);
384 return S_OK;
387 static HRESULT WINAPI QT_Pause(IBaseFilter *iface)
389 HRESULT hr = S_OK;
390 TRACE("()\n");
392 return hr;
395 static OSErr QT_Create_Extract_Session(QTSplitter *filter)
397 AudioStreamBasicDescription aDesc;
398 OSErr err;
399 WAVEFORMATEX* pvi;
401 pvi = (WAVEFORMATEX*)filter->pAudio_Pin->pmt->pbFormat;
403 err = MovieAudioExtractionBegin(filter->pQTMovie, 0, &filter->aSession);
404 if (err != noErr)
406 ERR("Failed to begin Extraction session %i\n",err);
407 return err;
410 err = MovieAudioExtractionGetProperty(filter->aSession,
411 kQTPropertyClass_MovieAudioExtraction_Audio,
412 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
413 sizeof(AudioStreamBasicDescription), &aDesc, NULL);
415 if (err != noErr)
417 MovieAudioExtractionEnd(filter->aSession);
418 filter->aSession = NULL;
419 ERR("Failed to get session description %i\n",err);
420 return err;
423 aDesc.mFormatID = kAudioFormatLinearPCM;
424 aDesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger +
425 kAudioFormatFlagIsPacked;
426 aDesc.mFramesPerPacket = 1;
427 aDesc.mChannelsPerFrame = pvi->nChannels;
428 aDesc.mBitsPerChannel = pvi->wBitsPerSample;
429 aDesc.mSampleRate = pvi->nSamplesPerSec;
430 aDesc.mBytesPerFrame = (aDesc.mBitsPerChannel * aDesc.mChannelsPerFrame) / 8;
431 aDesc.mBytesPerPacket = aDesc.mBytesPerFrame * aDesc.mFramesPerPacket;
433 err = MovieAudioExtractionSetProperty(filter->aSession,
434 kQTPropertyClass_MovieAudioExtraction_Audio,
435 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
436 sizeof(AudioStreamBasicDescription), &aDesc);
438 if (aDesc.mFormatID != kAudioFormatLinearPCM)
440 ERR("Not PCM Wave\n");
441 err = -1;
443 if (aDesc.mFormatFlags != kLinearPCMFormatFlagIsSignedInteger +
444 kAudioFormatFlagIsPacked)
446 ERR("Unhandled Flags\n");
447 err = -1;
449 if (aDesc.mFramesPerPacket != 1)
451 ERR("Unhandled Frames per packet %li\n",aDesc.mFramesPerPacket);
452 err = -1;
454 if (aDesc.mChannelsPerFrame != pvi->nChannels)
456 ERR("Unhandled channel count %li\n",aDesc.mChannelsPerFrame);
457 err = -1;
459 if (aDesc.mBitsPerChannel != pvi->wBitsPerSample)
461 ERR("Unhandled bits per channel %li\n",aDesc.mBitsPerChannel);
462 err = -1;
464 if (aDesc.mSampleRate != pvi->nSamplesPerSec)
466 ERR("Unhandled sample rate %f\n",aDesc.mSampleRate);
467 err = -1;
470 if (err != noErr)
472 ERR("Failed to create Extraction Session\n");
473 MovieAudioExtractionEnd(filter->aSession);
474 filter->aSession = NULL;
477 return err;
480 static DWORD WINAPI QTSplitter_loading_thread(LPVOID data)
482 QTSplitter *This = (QTSplitter *)data;
484 if (This->pAudio_Pin)
486 /* according to QA1469 a movie has to be fully loaded before we
487 can reliably start the Extraction session.
489 If loaded earlier, then we only get an extraction session for
490 the part of the movie that is loaded at that time.
492 We are trying to load as much of the movie as we can before we
493 start extracting. However we can recreate the extraction session
494 again when we run out of loaded extraction frames. But we want
495 to try to minimize that.
498 while(GetMovieLoadState(This->pQTMovie) < kMovieLoadStateComplete)
500 EnterCriticalSection(&This->csReceive);
501 MoviesTask(This->pQTMovie, 100);
502 LeaveCriticalSection(&This->csReceive);
503 Sleep(0);
506 return 0;
509 static DWORD WINAPI QTSplitter_thread(LPVOID data)
511 QTSplitter *This = (QTSplitter *)data;
512 HRESULT hr = S_OK;
513 TimeValue next_time;
514 CVPixelBufferRef pixelBuffer = NULL;
515 OSStatus err;
516 TimeRecord tr;
518 WaitForSingleObject(This->runEvent, -1);
520 EnterCriticalSection(&This->csReceive);
521 This->state = State_Running;
522 /* Prime the pump: Needed for MPEG streams */
523 GetMovieNextInterestingTime(This->pQTMovie, nextTimeEdgeOK | nextTimeStep, 0, NULL, This->movie_time, 1, &next_time, NULL);
525 GetMovieTime(This->pQTMovie, &tr);
527 if (This->pAudio_Pin)
528 QT_Create_Extract_Session(This);
530 LeaveCriticalSection(&This->csReceive);
534 LONGLONG tStart=0, tStop=0;
535 LONGLONG mStart=0, mStop=0;
536 float time;
538 EnterCriticalSection(&This->csReceive);
539 GetMovieNextInterestingTime(This->pQTMovie, nextTimeStep, 0, NULL, This->movie_time, 1, &next_time, NULL);
541 if (next_time == -1)
543 TRACE("No next time\n");
544 LeaveCriticalSection(&This->csReceive);
545 break;
548 tr.value = SInt64ToWide(next_time);
549 SetMovieTime(This->pQTMovie, &tr);
550 MoviesTask(This->pQTMovie,0);
551 QTVisualContextTask(This->vContext);
553 TRACE("In loop at time %ld\n",This->movie_time);
554 TRACE("In Next time %ld\n",next_time);
556 mStart = This->movie_time;
557 mStop = next_time;
559 time = (float)(This->movie_time - This->movie_start) / This->movie_scale;
560 tStart = time * 10000000;
561 time = (float)(next_time - This->movie_start) / This->movie_scale;
562 tStop = time * 10000000;
564 /* Deliver Audio */
565 if (This->pAudio_Pin && This->pAudio_Pin->pin.pin.pConnectedTo && This->aSession)
567 int data_size=0;
568 BYTE* ptr;
569 IMediaSample *sample = NULL;
570 AudioBufferList aData;
571 UInt32 flags;
572 UInt32 frames;
573 WAVEFORMATEX* pvi;
574 float duration;
576 pvi = (WAVEFORMATEX*)This->pAudio_Pin->pmt->pbFormat;
578 hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->pAudio_Pin->pin, &sample, NULL, NULL, 0);
580 if (FAILED(hr))
582 ERR("Audio: Unable to get delivery buffer (%x)\n", hr);
583 goto audio_error;
586 hr = IMediaSample_GetPointer(sample, &ptr);
587 if (FAILED(hr))
589 ERR("Audio: Unable to get pointer to buffer (%x)\n", hr);
590 goto audio_error;
593 duration = (float)next_time / This->movie_scale;
594 time = (float)This->movie_time / This->movie_scale;
595 duration -= time;
596 frames = pvi->nSamplesPerSec * duration;
597 TRACE("Need audio for %f seconds (%li frames)\n",duration,frames);
599 data_size = IMediaSample_GetSize(sample);
600 if (data_size < frames * pvi->nBlockAlign)
601 FIXME("Audio buffer is too small\n");
603 aData.mNumberBuffers = 1;
604 aData.mBuffers[0].mNumberChannels = pvi->nChannels;
605 aData.mBuffers[0].mDataByteSize = data_size;
606 aData.mBuffers[0].mData = ptr;
608 err = MovieAudioExtractionFillBuffer(This->aSession, &frames, &aData, &flags);
609 if (frames == 0)
611 TimeRecord etr;
613 /* Ran out of frames, Restart the extraction session */
614 TRACE("Restarting extraction session\n");
615 MovieAudioExtractionEnd(This->aSession);
616 This->aSession = NULL;
617 QT_Create_Extract_Session(This);
619 etr = tr;
620 etr.value = SInt64ToWide(This->movie_time);
621 MovieAudioExtractionSetProperty(This->aSession,
622 kQTPropertyClass_MovieAudioExtraction_Movie,
623 kQTMovieAudioExtractionMoviePropertyID_CurrentTime,
624 sizeof(TimeRecord), &etr );
626 frames = pvi->nSamplesPerSec * duration;
627 aData.mNumberBuffers = 1;
628 aData.mBuffers[0].mNumberChannels = pvi->nChannels;
629 aData.mBuffers[0].mDataByteSize = data_size;
630 aData.mBuffers[0].mData = ptr;
632 MovieAudioExtractionFillBuffer(This->aSession, &frames, &aData, &flags);
635 TRACE("Got %i frames\n",(int)frames);
637 IMediaSample_SetActualDataLength(sample, frames * pvi->nBlockAlign);
639 IMediaSample_SetMediaTime(sample, &mStart, &mStop);
640 IMediaSample_SetTime(sample, &tStart, &tStop);
642 hr = OutputQueue_Receive(This->pAudio_Pin->queue, sample);
643 TRACE("Audio Delivered (%x)\n",hr);
645 audio_error:
646 if (sample)
647 IMediaSample_Release(sample);
649 else
650 TRACE("Audio Pin not connected or no Audio\n");
652 /* Deliver Video */
653 if (This->pVideo_Pin && QTVisualContextIsNewImageAvailable(This->vContext,0))
655 err = QTVisualContextCopyImageForTime(This->vContext, NULL, NULL, &pixelBuffer);
656 if (err == noErr)
658 int data_size=0;
659 BYTE* ptr;
660 IMediaSample *sample = NULL;
662 hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->pVideo_Pin->pin, &sample, NULL, NULL, 0);
663 if (FAILED(hr))
665 ERR("Video: Unable to get delivery buffer (%x)\n", hr);
666 goto video_error;
669 data_size = IMediaSample_GetSize(sample);
670 if (data_size < This->outputSize)
672 ERR("Sample size is too small %d < %d\n", data_size, This->outputSize)
674 hr = E_FAIL;
675 goto video_error;
678 hr = IMediaSample_GetPointer(sample, &ptr);
679 if (FAILED(hr))
681 ERR("Video: Unable to get pointer to buffer (%x)\n", hr);
682 goto video_error;
685 hr = AccessPixelBufferPixels( pixelBuffer, ptr);
686 if (FAILED(hr))
688 ERR("Failed to access Pixels\n");
689 goto video_error;
692 IMediaSample_SetActualDataLength(sample, This->outputSize);
694 IMediaSample_SetMediaTime(sample, &mStart, &mStop);
695 IMediaSample_SetTime(sample, &tStart, &tStop);
697 hr = OutputQueue_Receive(This->pVideo_Pin->queue, sample);
698 TRACE("Video Delivered (%x)\n",hr);
700 video_error:
701 if (sample)
702 IMediaSample_Release(sample);
703 if (pixelBuffer)
704 CVPixelBufferRelease(pixelBuffer);
707 else
708 TRACE("No video to deliver\n");
710 This->movie_time = next_time;
711 LeaveCriticalSection(&This->csReceive);
712 } while (hr == S_OK);
714 This->state = State_Stopped;
715 if (This->pAudio_Pin)
716 OutputQueue_EOS(This->pAudio_Pin->queue);
717 if (This->pVideo_Pin)
718 OutputQueue_EOS(This->pVideo_Pin->queue);
720 return hr;
723 static HRESULT WINAPI QT_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
725 HRESULT hr = S_OK;
726 QTSplitter *This = impl_from_IBaseFilter(iface);
727 HRESULT hr_any = VFW_E_NOT_CONNECTED;
729 TRACE("(%s)\n", wine_dbgstr_longlong(tStart));
731 EnterCriticalSection(&This->csReceive);
732 This->filter.rtStreamStart = tStart;
734 if (This->pVideo_Pin)
735 hr = BaseOutputPinImpl_Active(&This->pVideo_Pin->pin);
736 if (SUCCEEDED(hr))
737 hr_any = hr;
738 if (This->pAudio_Pin)
739 hr = BaseOutputPinImpl_Active(&This->pAudio_Pin->pin);
740 if (SUCCEEDED(hr))
741 hr_any = hr;
743 hr = hr_any;
745 SetEvent(This->runEvent);
746 LeaveCriticalSection(&This->csReceive);
748 return hr;
751 static HRESULT WINAPI QT_GetState(IBaseFilter *iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
753 QTSplitter *This = impl_from_IBaseFilter(iface);
754 TRACE("(%d, %p)\n", dwMilliSecsTimeout, pState);
756 *pState = This->state;
758 return S_OK;
761 static HRESULT WINAPI QT_FindPin(IBaseFilter *iface, LPCWSTR Id, IPin **ppPin)
763 FIXME("(%p)->(%s,%p) stub\n", iface, debugstr_w(Id), ppPin);
764 return E_NOTIMPL;
767 static const IBaseFilterVtbl QT_Vtbl = {
768 QT_QueryInterface,
769 BaseFilterImpl_AddRef,
770 QT_Release,
771 BaseFilterImpl_GetClassID,
772 QT_Stop,
773 QT_Pause,
774 QT_Run,
775 QT_GetState,
776 BaseFilterImpl_SetSyncSource,
777 BaseFilterImpl_GetSyncSource,
778 BaseFilterImpl_EnumPins,
779 QT_FindPin,
780 BaseFilterImpl_QueryFilterInfo,
781 BaseFilterImpl_JoinFilterGraph,
782 BaseFilterImpl_QueryVendorInfo
786 * Input Pin
788 static HRESULT QT_RemoveOutputPins(QTSplitter *This)
790 HRESULT hr;
791 TRACE("(%p)\n", This);
793 if (This->pVideo_Pin)
795 OutputQueue_Destroy(This->pVideo_Pin->queue);
796 hr = BaseOutputPinImpl_BreakConnect(&This->pVideo_Pin->pin);
797 TRACE("Disconnect: %08x\n", hr);
798 IPin_Release(&This->pVideo_Pin->pin.pin.IPin_iface);
799 This->pVideo_Pin = NULL;
801 if (This->pAudio_Pin)
803 OutputQueue_Destroy(This->pAudio_Pin->queue);
804 hr = BaseOutputPinImpl_BreakConnect(&This->pAudio_Pin->pin);
805 TRACE("Disconnect: %08x\n", hr);
806 IPin_Release(&This->pAudio_Pin->pin.pin.IPin_iface);
807 This->pAudio_Pin = NULL;
810 BaseFilterImpl_IncrementPinVersion(&This->filter);
811 return S_OK;
814 static inline QTInPin *impl_from_IPin( IPin *iface )
816 return CONTAINING_RECORD(iface, QTInPin, pin.IPin_iface);
819 static ULONG WINAPI QTInPin_Release(IPin *iface)
821 QTInPin *This = impl_from_IPin(iface);
822 ULONG refCount = InterlockedDecrement(&This->pin.refCount);
824 TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
825 if (!refCount)
827 FreeMediaType(&This->pin.mtCurrent);
828 if (This->pAlloc)
829 IMemAllocator_Release(This->pAlloc);
830 This->pAlloc = NULL;
831 This->pin.IPin_iface.lpVtbl = NULL;
832 return 0;
834 else
835 return refCount;
838 static HRESULT QT_Process_Video_Track(QTSplitter* filter, Track trk)
840 AM_MEDIA_TYPE amt;
841 VIDEOINFOHEADER * pvi;
842 PIN_INFO piOutput;
843 HRESULT hr = S_OK;
844 OSErr err;
845 static const WCHAR szwVideoOut[] = {'V','i','d','e','o',0};
846 CFMutableDictionaryRef pixelBufferOptions = NULL;
847 CFMutableDictionaryRef visualContextOptions = NULL;
848 CFNumberRef n = NULL;
849 int t;
850 DWORD outputWidth, outputHeight, outputDepth;
851 Fixed trackWidth, trackHeight;
853 ZeroMemory(&amt, sizeof(amt));
854 amt.formattype = FORMAT_VideoInfo;
855 amt.majortype = MEDIATYPE_Video;
856 amt.subtype = MEDIASUBTYPE_RGB24;
858 GetTrackDimensions(trk, &trackWidth, &trackHeight);
860 outputDepth = 3;
861 outputWidth = Fix2Long(trackWidth);
862 outputHeight = Fix2Long(trackHeight);
863 TRACE("Width %i Height %i\n",outputWidth, outputHeight);
865 amt.cbFormat = sizeof(VIDEOINFOHEADER);
866 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
867 ZeroMemory(amt.pbFormat, amt.cbFormat);
868 pvi = (VIDEOINFOHEADER *)amt.pbFormat;
869 pvi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
870 pvi->bmiHeader.biWidth = outputWidth;
871 pvi->bmiHeader.biHeight = outputHeight;
872 pvi->bmiHeader.biPlanes = 1;
873 pvi->bmiHeader.biBitCount = 24;
874 pvi->bmiHeader.biCompression = BI_RGB;
875 pvi->bmiHeader.biSizeImage = outputWidth * outputHeight * outputDepth;
877 filter->outputSize = pvi->bmiHeader.biSizeImage;
878 amt.lSampleSize = 0;
880 pixelBufferOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
882 t = k32ARGBPixelFormat;
883 n = CFNumberCreate(NULL, kCFNumberIntType, &t);
884 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferPixelFormatTypeKey, n);
885 CFRelease(n);
887 n = CFNumberCreate(NULL, kCFNumberIntType, &outputWidth);
888 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferWidthKey, n);
889 CFRelease(n);
891 n = CFNumberCreate(NULL, kCFNumberIntType, &outputHeight);
892 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferHeightKey, n);
893 CFRelease(n);
895 t = 16;
896 n = CFNumberCreate(NULL, kCFNumberIntType, &t);
897 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferBytesPerRowAlignmentKey, n);
898 CFRelease(n);
900 visualContextOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
902 CFDictionarySetValue(visualContextOptions, kQTVisualContextPixelBufferAttributesKey, pixelBufferOptions);
904 err = QTPixelBufferContextCreate(NULL, visualContextOptions,&filter->vContext);
905 CFRelease(pixelBufferOptions);
906 CFRelease(visualContextOptions);
907 if (err != noErr)
909 ERR("Failed to create Visual Context\n");
910 return E_FAIL;
913 err = SetMovieVisualContext(filter->pQTMovie, filter->vContext);
914 if (err != noErr)
916 ERR("Failed to set Visual Context\n");
917 return E_FAIL;
920 piOutput.dir = PINDIR_OUTPUT;
921 piOutput.pFilter = &filter->filter.IBaseFilter_iface;
922 lstrcpyW(piOutput.achName,szwVideoOut);
924 hr = QT_AddPin(filter, &piOutput, &amt, TRUE);
925 if (FAILED(hr))
926 ERR("Failed to add Video Track\n");
927 else
928 TRACE("Video Pin %p\n",filter->pVideo_Pin);
930 return hr;
933 static HRESULT QT_Process_Audio_Track(QTSplitter* filter, Track trk)
935 AM_MEDIA_TYPE amt;
936 WAVEFORMATEX* pvi;
937 PIN_INFO piOutput;
938 HRESULT hr = S_OK;
939 static const WCHAR szwAudioOut[] = {'A','u','d','i','o',0};
940 Media audioMedia;
942 SoundDescriptionHandle aDesc = (SoundDescriptionHandle) NewHandle(sizeof(SoundDescription));
944 audioMedia = GetTrackMedia(trk);
945 GetMediaSampleDescription(audioMedia, 1, (SampleDescriptionHandle)aDesc);
947 ZeroMemory(&amt, sizeof(amt));
948 amt.formattype = FORMAT_WaveFormatEx;
949 amt.majortype = MEDIATYPE_Audio;
950 amt.subtype = MEDIASUBTYPE_PCM;
951 amt.bTemporalCompression = 0;
953 amt.cbFormat = sizeof(WAVEFORMATEX);
954 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
955 ZeroMemory(amt.pbFormat, amt.cbFormat);
956 pvi = (WAVEFORMATEX*)amt.pbFormat;
958 pvi->cbSize = sizeof(WAVEFORMATEX);
959 pvi->wFormatTag = WAVE_FORMAT_PCM;
960 pvi->nChannels = ((SoundDescription)**aDesc).numChannels;
961 if (pvi->nChannels < 1 || pvi->nChannels > 2)
962 pvi->nChannels = 2;
963 pvi->nSamplesPerSec = (((SoundDescription)**aDesc).sampleRate/65536);
964 if (pvi->nSamplesPerSec < 8000 || pvi->nChannels > 48000)
965 pvi->nSamplesPerSec = 44100;
966 pvi->wBitsPerSample = ((SoundDescription)**aDesc).sampleSize;
967 if (pvi->wBitsPerSample < 8 || pvi->wBitsPerSample > 32)
968 pvi->wBitsPerSample = 16;
969 pvi->nBlockAlign = (pvi->nChannels * pvi->wBitsPerSample) / 8;
970 pvi->nAvgBytesPerSec = pvi->nSamplesPerSec * pvi->nBlockAlign;
972 DisposeHandle((Handle)aDesc);
974 piOutput.dir = PINDIR_OUTPUT;
975 piOutput.pFilter = &filter->filter.IBaseFilter_iface;
976 lstrcpyW(piOutput.achName,szwAudioOut);
978 hr = QT_AddPin(filter, &piOutput, &amt, FALSE);
979 if (FAILED(hr))
980 ERR("Failed to add Audio Track\n");
981 else
982 TRACE("Audio Pin %p\n",filter->pAudio_Pin);
983 return hr;
986 static HRESULT QT_Process_Movie(QTSplitter* filter)
988 HRESULT hr = S_OK;
989 OSErr err;
990 WineDataRefRecord ptrDataRefRec;
991 Handle dataRef = NULL;
992 Track trk;
993 short id = 0;
994 DWORD tid;
995 HANDLE thread;
996 LONGLONG time;
998 TRACE("Trying movie connect\n");
1000 ptrDataRefRec.pReader = filter->pInputPin.pReader;
1001 ptrDataRefRec.streamSubtype = filter->pInputPin.subType;
1002 PtrToHand( &ptrDataRefRec, &dataRef, sizeof(WineDataRefRecord));
1004 err = NewMovieFromDataRef(&filter->pQTMovie, newMovieActive|newMovieDontInteractWithUser|newMovieDontAutoUpdateClock|newMovieDontAskUnresolvedDataRefs|newMovieAsyncOK, &id, dataRef, 'WINE');
1006 DisposeHandle(dataRef);
1008 if (err != noErr)
1010 FIXME("QuickTime cannot handle media type(%i)\n",err);
1011 return VFW_E_TYPE_NOT_ACCEPTED;
1014 PrePrerollMovie(filter->pQTMovie, 0, fixed1, NULL, NULL);
1015 PrerollMovie(filter->pQTMovie, 0, fixed1);
1016 GoToBeginningOfMovie(filter->pQTMovie);
1017 SetMovieActive(filter->pQTMovie,TRUE);
1019 if (GetMovieLoadState(filter->pQTMovie) < kMovieLoadStateLoaded)
1020 MoviesTask(filter->pQTMovie,100);
1022 trk = GetMovieIndTrackType(filter->pQTMovie, 1, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
1023 TRACE("%p is a video track\n",trk);
1024 if (trk)
1025 hr = QT_Process_Video_Track(filter, trk);
1027 if (FAILED(hr))
1028 return hr;
1030 trk = GetMovieIndTrackType(filter->pQTMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
1031 TRACE("%p is a audio track\n",trk);
1032 if (trk)
1033 hr = QT_Process_Audio_Track(filter, trk);
1035 time = GetMovieDuration(filter->pQTMovie);
1036 filter->movie_scale = GetMovieTimeScale(filter->pQTMovie);
1037 filter->sourceSeeking.llDuration = ((double)time / filter->movie_scale) * 10000000;
1038 filter->sourceSeeking.llStop = filter->sourceSeeking.llDuration;
1040 TRACE("Movie duration is %s\n",wine_dbgstr_longlong(filter->sourceSeeking.llDuration));
1042 thread = CreateThread(NULL, 0, QTSplitter_loading_thread, filter, 0, &tid);
1043 if (thread)
1045 TRACE("Created loading thread 0x%08x\n", tid);
1046 CloseHandle(thread);
1048 thread = CreateThread(NULL, 0, QTSplitter_thread, filter, 0, &tid);
1049 if (thread)
1051 TRACE("Created processing thread 0x%08x\n", tid);
1052 CloseHandle(thread);
1054 else
1055 hr = HRESULT_FROM_WIN32(GetLastError());
1057 return hr;
1060 static HRESULT WINAPI QTInPin_ReceiveConnection(IPin *iface, IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
1062 HRESULT hr = S_OK;
1063 ALLOCATOR_PROPERTIES props;
1064 QTInPin *This = impl_from_IPin(iface);
1066 TRACE("(%p/%p)->(%p, %p)\n", This, iface, pReceivePin, pmt);
1068 EnterCriticalSection(This->pin.pCritSec);
1069 This->pReader = NULL;
1071 if (This->pin.pConnectedTo)
1072 hr = VFW_E_ALREADY_CONNECTED;
1073 else if (IPin_QueryAccept(iface, pmt) != S_OK)
1074 hr = VFW_E_TYPE_NOT_ACCEPTED;
1075 else
1077 PIN_DIRECTION pindirReceive;
1078 IPin_QueryDirection(pReceivePin, &pindirReceive);
1079 if (pindirReceive != PINDIR_OUTPUT)
1080 hr = VFW_E_INVALID_DIRECTION;
1083 if (FAILED(hr))
1085 LeaveCriticalSection(This->pin.pCritSec);
1086 return hr;
1089 hr = IPin_QueryInterface(pReceivePin, &IID_IAsyncReader, (LPVOID *)&This->pReader);
1090 if (FAILED(hr))
1092 LeaveCriticalSection(This->pin.pCritSec);
1093 TRACE("Input source is not an AsyncReader\n");
1094 return hr;
1097 LeaveCriticalSection(This->pin.pCritSec);
1098 EnterCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1099 hr = QT_Process_Movie(impl_from_IBaseFilter(This->pin.pinInfo.pFilter));
1100 if (FAILED(hr))
1102 LeaveCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1103 TRACE("Unable to process movie\n");
1104 return hr;
1107 This->pAlloc = NULL;
1108 props.cBuffers = 8;
1109 props.cbAlign = 1;
1110 props.cbBuffer = impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->outputSize + props.cbAlign;
1111 props.cbPrefix = 0;
1113 hr = IAsyncReader_RequestAllocator(This->pReader, NULL, &props, &This->pAlloc);
1114 if (SUCCEEDED(hr))
1116 CopyMediaType(&This->pin.mtCurrent, pmt);
1117 This->pin.pConnectedTo = pReceivePin;
1118 IPin_AddRef(pReceivePin);
1119 hr = IMemAllocator_Commit(This->pAlloc);
1121 else
1123 QT_RemoveOutputPins(impl_from_IBaseFilter(This->pin.pinInfo.pFilter));
1124 if (This->pReader)
1125 IAsyncReader_Release(This->pReader);
1126 This->pReader = NULL;
1127 if (This->pAlloc)
1128 IMemAllocator_Release(This->pAlloc);
1129 This->pAlloc = NULL;
1131 TRACE("Size: %i\n", props.cbBuffer);
1132 LeaveCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1134 return hr;
1137 static HRESULT WINAPI QTInPin_Disconnect(IPin *iface)
1139 HRESULT hr;
1140 QTInPin *This = impl_from_IPin(iface);
1141 FILTER_STATE state;
1142 TRACE("()\n");
1144 hr = IBaseFilter_GetState(This->pin.pinInfo.pFilter, INFINITE, &state);
1145 EnterCriticalSection(This->pin.pCritSec);
1146 if (This->pin.pConnectedTo)
1148 QTSplitter *Parser = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
1150 if (SUCCEEDED(hr) && state == State_Stopped)
1152 IMemAllocator_Decommit(This->pAlloc);
1153 IPin_Disconnect(This->pin.pConnectedTo);
1154 This->pin.pConnectedTo = NULL;
1155 hr = QT_RemoveOutputPins(Parser);
1157 else
1158 hr = VFW_E_NOT_STOPPED;
1160 else
1161 hr = S_FALSE;
1162 LeaveCriticalSection(This->pin.pCritSec);
1163 return hr;
1166 static HRESULT WINAPI QTInPin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *pmt)
1168 QTInPin *This = impl_from_IPin(iface);
1170 TRACE("(%p)->(%p)\n", This, pmt);
1172 if (IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream))
1174 This->subType = pmt->subtype;
1175 return S_OK;
1177 return S_FALSE;
1180 static HRESULT WINAPI QTInPin_EndOfStream(IPin *iface)
1182 QTInPin *pin = impl_from_IPin(iface);
1183 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1185 FIXME("Propagate message on %p\n", This);
1186 return S_OK;
1189 static HRESULT WINAPI QTInPin_BeginFlush(IPin *iface)
1191 QTInPin *pin = impl_from_IPin(iface);
1192 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1194 FIXME("Propagate message on %p\n", This);
1195 return S_OK;
1198 static HRESULT WINAPI QTInPin_EndFlush(IPin *iface)
1200 QTInPin *pin = impl_from_IPin(iface);
1201 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1203 FIXME("Propagate message on %p\n", This);
1204 return S_OK;
1207 static HRESULT WINAPI QTInPin_NewSegment(IPin *iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
1209 QTInPin *pin = impl_from_IPin(iface);
1210 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1212 BasePinImpl_NewSegment(iface, tStart, tStop, dRate);
1213 FIXME("Propagate message on %p\n", This);
1214 return S_OK;
1217 static HRESULT WINAPI QTInPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
1219 QTInPin *This = impl_from_IPin(iface);
1221 TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
1223 *ppv = NULL;
1225 if (IsEqualIID(riid, &IID_IUnknown))
1226 *ppv = iface;
1227 else if (IsEqualIID(riid, &IID_IPin))
1228 *ppv = iface;
1229 else if (IsEqualIID(riid, &IID_IMediaSeeking))
1230 return IBaseFilter_QueryInterface(This->pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
1232 if (*ppv)
1234 IUnknown_AddRef((IUnknown *)(*ppv));
1235 return S_OK;
1238 FIXME("No interface for %s!\n", debugstr_guid(riid));
1240 return E_NOINTERFACE;
1243 static HRESULT WINAPI QTInPin_EnumMediaTypes(IPin *iface, IEnumMediaTypes **ppEnum)
1245 QTInPin *This = impl_from_IPin(iface);
1247 TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
1249 return EnumMediaTypes_Construct(&This->pin, BasePinImpl_GetMediaType, BasePinImpl_GetMediaTypeVersion, ppEnum);
1252 static const IPinVtbl QT_InputPin_Vtbl = {
1253 QTInPin_QueryInterface,
1254 BasePinImpl_AddRef,
1255 QTInPin_Release,
1256 BaseInputPinImpl_Connect,
1257 QTInPin_ReceiveConnection,
1258 QTInPin_Disconnect,
1259 BasePinImpl_ConnectedTo,
1260 BasePinImpl_ConnectionMediaType,
1261 BasePinImpl_QueryPinInfo,
1262 BasePinImpl_QueryDirection,
1263 BasePinImpl_QueryId,
1264 QTInPin_QueryAccept,
1265 QTInPin_EnumMediaTypes,
1266 BasePinImpl_QueryInternalConnections,
1267 QTInPin_EndOfStream,
1268 QTInPin_BeginFlush,
1269 QTInPin_EndFlush,
1270 QTInPin_NewSegment
1274 * Output Pin
1276 static inline QTOutPin *impl_QTOutPin_from_IPin( IPin *iface )
1278 return CONTAINING_RECORD(iface, QTOutPin, pin.pin.IPin_iface);
1281 static inline QTOutPin *impl_QTOutPin_from_BasePin( BasePin *iface )
1283 return CONTAINING_RECORD(iface, QTOutPin, pin.pin);
1286 static inline QTOutPin *impl_QTOutPin_from_BaseOutputPin( BaseOutputPin *iface )
1288 return CONTAINING_RECORD(iface, QTOutPin, pin);
1291 static HRESULT WINAPI QTOutPin_QueryInterface(IPin *iface, REFIID riid, void **ppv)
1293 QTOutPin *This = impl_QTOutPin_from_IPin(iface);
1295 TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
1297 *ppv = NULL;
1299 if (IsEqualIID(riid, &IID_IUnknown))
1300 *ppv = iface;
1301 else if (IsEqualIID(riid, &IID_IPin))
1302 *ppv = iface;
1303 else if (IsEqualIID(riid, &IID_IMediaSeeking))
1304 return IBaseFilter_QueryInterface(This->pin.pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
1305 else if (IsEqualIID(riid, &IID_IQualityControl))
1306 *ppv = &This->IQualityControl_iface;
1308 if (*ppv)
1310 IUnknown_AddRef((IUnknown *)(*ppv));
1311 return S_OK;
1313 FIXME("No interface for %s!\n", debugstr_guid(riid));
1314 return E_NOINTERFACE;
1317 static ULONG WINAPI QTOutPin_Release(IPin *iface)
1319 QTOutPin *This = impl_QTOutPin_from_IPin(iface);
1320 ULONG refCount = InterlockedDecrement(&This->pin.pin.refCount);
1321 TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
1323 if (!refCount)
1325 DeleteMediaType(This->pmt);
1326 FreeMediaType(&This->pin.pin.mtCurrent);
1327 CoTaskMemFree(This);
1328 return 0;
1330 return refCount;
1333 static HRESULT WINAPI QTOutPin_GetMediaType(BasePin *iface, int iPosition, AM_MEDIA_TYPE *pmt)
1335 QTOutPin *This = impl_QTOutPin_from_BasePin(iface);
1337 if (iPosition < 0)
1338 return E_INVALIDARG;
1339 if (iPosition > 0)
1340 return VFW_S_NO_MORE_ITEMS;
1341 CopyMediaType(pmt, This->pmt);
1342 return S_OK;
1345 static HRESULT WINAPI QTOutPin_DecideBufferSize(BaseOutputPin *iface, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
1347 /* Unused */
1348 return S_OK;
1351 static HRESULT WINAPI QTOutPin_DecideAllocator(BaseOutputPin *iface, IMemInputPin *pPin, IMemAllocator **pAlloc)
1353 HRESULT hr;
1354 QTOutPin *This = impl_QTOutPin_from_BaseOutputPin(iface);
1355 QTSplitter *QTfilter = impl_from_IBaseFilter(This->pin.pin.pinInfo.pFilter);
1357 *pAlloc = NULL;
1358 if (QTfilter->pInputPin.pAlloc)
1359 hr = IMemInputPin_NotifyAllocator(pPin, QTfilter->pInputPin.pAlloc, FALSE);
1360 else
1361 hr = VFW_E_NO_ALLOCATOR;
1363 return hr;
1366 static HRESULT WINAPI QTOutPin_BreakConnect(BaseOutputPin *This)
1368 HRESULT hr;
1370 TRACE("(%p)->()\n", This);
1372 EnterCriticalSection(This->pin.pCritSec);
1373 if (!This->pin.pConnectedTo || !This->pMemInputPin)
1374 hr = VFW_E_NOT_CONNECTED;
1375 else
1377 hr = IPin_Disconnect(This->pin.pConnectedTo);
1378 IPin_Disconnect(&This->pin.IPin_iface);
1380 LeaveCriticalSection(This->pin.pCritSec);
1382 return hr;
1385 static const IPinVtbl QT_OutputPin_Vtbl = {
1386 QTOutPin_QueryInterface,
1387 BasePinImpl_AddRef,
1388 QTOutPin_Release,
1389 BaseOutputPinImpl_Connect,
1390 BaseOutputPinImpl_ReceiveConnection,
1391 BaseOutputPinImpl_Disconnect,
1392 BasePinImpl_ConnectedTo,
1393 BasePinImpl_ConnectionMediaType,
1394 BasePinImpl_QueryPinInfo,
1395 BasePinImpl_QueryDirection,
1396 BasePinImpl_QueryId,
1397 BasePinImpl_QueryAccept,
1398 BasePinImpl_EnumMediaTypes,
1399 BasePinImpl_QueryInternalConnections,
1400 BaseOutputPinImpl_EndOfStream,
1401 BaseOutputPinImpl_BeginFlush,
1402 BaseOutputPinImpl_EndFlush,
1403 BasePinImpl_NewSegment
1406 static inline QTOutPin *impl_from_IQualityControl( IQualityControl *iface )
1408 return CONTAINING_RECORD(iface, QTOutPin, IQualityControl_iface);
1411 HRESULT WINAPI QT_QualityControl_QueryInterface(IQualityControl *iface, REFIID riid, void **ppv)
1413 QTOutPin *This = impl_from_IQualityControl(iface);
1414 return IPin_QueryInterface(&This->pin.pin.IPin_iface, riid, ppv);
1417 ULONG WINAPI QT_QualityControl_AddRef(IQualityControl *iface)
1419 QTOutPin *This = impl_from_IQualityControl(iface);
1420 return IPin_AddRef(&This->pin.pin.IPin_iface);
1423 ULONG WINAPI QT_QualityControl_Release(IQualityControl *iface)
1425 QTOutPin *This = impl_from_IQualityControl(iface);
1426 return IPin_Release(&This->pin.pin.IPin_iface);
1429 static HRESULT WINAPI QT_QualityControl_Notify(IQualityControl *iface, IBaseFilter *sender, Quality qm)
1431 REFERENCE_TIME late = qm.Late;
1432 if (qm.Late < 0 && -qm.Late > qm.TimeStamp)
1433 late = -qm.TimeStamp;
1434 /* TODO: Do Something */
1435 return S_OK;
1438 HRESULT WINAPI QT_QualityControl_SetSink(IQualityControl *iface, IQualityControl *tonotify)
1440 /* Do nothing */
1441 return S_OK;
1444 static const IQualityControlVtbl QTOutPin_QualityControl_Vtbl = {
1445 QT_QualityControl_QueryInterface,
1446 QT_QualityControl_AddRef,
1447 QT_QualityControl_Release,
1448 QT_QualityControl_Notify,
1449 QT_QualityControl_SetSink
1452 static const BasePinFuncTable output_BaseFuncTable = {
1453 NULL,
1454 BaseOutputPinImpl_AttemptConnection,
1455 BasePinImpl_GetMediaTypeVersion,
1456 QTOutPin_GetMediaType
1459 static const BaseOutputPinFuncTable output_BaseOutputFuncTable = {
1460 QTOutPin_DecideBufferSize,
1461 QTOutPin_DecideAllocator,
1462 QTOutPin_BreakConnect
1465 static const OutputQueueFuncTable output_OutputQueueFuncTable = {
1466 OutputQueueImpl_ThreadProc
1469 static HRESULT QT_AddPin(QTSplitter *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt, BOOL video)
1471 HRESULT hr;
1472 IPin **target;
1474 if (video)
1475 target = (IPin**)&This->pVideo_Pin;
1476 else
1477 target = (IPin**)&This->pAudio_Pin;
1479 if (*target != NULL)
1481 FIXME("We already have a %s pin\n",(video)?"video":"audio");
1482 return E_FAIL;
1485 hr = BaseOutputPin_Construct(&QT_OutputPin_Vtbl, sizeof(QTOutPin), piOutput, &output_BaseFuncTable, &output_BaseOutputFuncTable, &This->filter.csFilter, (IPin**)target);
1486 if (SUCCEEDED(hr))
1488 QTOutPin *pin = (QTOutPin*)*target;
1489 pin->pmt = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
1490 CopyMediaType(pin->pmt, amt);
1491 pin->pin.pin.pinInfo.pFilter = (LPVOID)This;
1492 pin->IQualityControl_iface.lpVtbl = &QTOutPin_QualityControl_Vtbl;
1494 BaseFilterImpl_IncrementPinVersion(&This->filter);
1496 hr = OutputQueue_Construct(&pin->pin, TRUE, TRUE, 5, FALSE, THREAD_PRIORITY_NORMAL, &output_OutputQueueFuncTable, &pin->queue);
1498 else
1499 ERR("Failed with error %x\n", hr);
1500 return hr;
1503 static HRESULT WINAPI QTSplitter_ChangeStart(IMediaSeeking *iface)
1505 QTSplitter *This = impl_from_IMediaSeeking(iface);
1506 TRACE("(%p)\n", iface);
1507 EnterCriticalSection(&This->csReceive);
1508 This->movie_time = (This->sourceSeeking.llCurrent * This->movie_scale)/10000000;
1509 This->movie_start = This->movie_time;
1510 LeaveCriticalSection(&This->csReceive);
1511 return S_OK;
1514 static HRESULT WINAPI QTSplitter_ChangeStop(IMediaSeeking *iface)
1516 FIXME("(%p) filter hasn't implemented stop position change!\n", iface);
1517 return S_OK;
1520 static HRESULT WINAPI QTSplitter_ChangeRate(IMediaSeeking *iface)
1522 FIXME("(%p) filter hasn't implemented rate change!\n", iface);
1523 return S_OK;
1526 static HRESULT WINAPI QT_Seeking_QueryInterface(IMediaSeeking * iface, REFIID riid, LPVOID * ppv)
1528 QTSplitter *This = impl_from_IMediaSeeking(iface);
1530 return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv);
1533 static ULONG WINAPI QT_Seeking_AddRef(IMediaSeeking * iface)
1535 QTSplitter *This = impl_from_IMediaSeeking(iface);
1537 return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
1540 static ULONG WINAPI QT_Seeking_Release(IMediaSeeking * iface)
1542 QTSplitter *This = impl_from_IMediaSeeking(iface);
1544 return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
1547 static const IMediaSeekingVtbl QT_Seeking_Vtbl =
1549 QT_Seeking_QueryInterface,
1550 QT_Seeking_AddRef,
1551 QT_Seeking_Release,
1552 SourceSeekingImpl_GetCapabilities,
1553 SourceSeekingImpl_CheckCapabilities,
1554 SourceSeekingImpl_IsFormatSupported,
1555 SourceSeekingImpl_QueryPreferredFormat,
1556 SourceSeekingImpl_GetTimeFormat,
1557 SourceSeekingImpl_IsUsingTimeFormat,
1558 SourceSeekingImpl_SetTimeFormat,
1559 SourceSeekingImpl_GetDuration,
1560 SourceSeekingImpl_GetStopPosition,
1561 SourceSeekingImpl_GetCurrentPosition,
1562 SourceSeekingImpl_ConvertTimeFormat,
1563 SourceSeekingImpl_SetPositions,
1564 SourceSeekingImpl_GetPositions,
1565 SourceSeekingImpl_GetAvailable,
1566 SourceSeekingImpl_SetRate,
1567 SourceSeekingImpl_GetRate,
1568 SourceSeekingImpl_GetPreroll