bcrypt: Add support for AES encryption on macOS.
[wine.git] / dlls / wineqtdecoder / qtsplitter.c
blob7db7a28f973013db55db63b6bbaebc66af6a0a4b
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;
172 HANDLE loaderThread;
173 HANDLE splitterThread;
174 } QTSplitter;
176 static const IPinVtbl QT_OutputPin_Vtbl;
177 static const IPinVtbl QT_InputPin_Vtbl;
178 static const IBaseFilterVtbl QT_Vtbl;
179 static const IMediaSeekingVtbl QT_Seeking_Vtbl;
181 static HRESULT QT_AddPin(QTSplitter *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt, BOOL video);
182 static HRESULT QT_RemoveOutputPins(QTSplitter *This);
184 static HRESULT WINAPI QTSplitter_ChangeStart(IMediaSeeking *iface);
185 static HRESULT WINAPI QTSplitter_ChangeStop(IMediaSeeking *iface);
186 static HRESULT WINAPI QTSplitter_ChangeRate(IMediaSeeking *iface);
188 static inline QTSplitter *impl_from_IMediaSeeking( IMediaSeeking *iface )
190 return CONTAINING_RECORD(iface, QTSplitter, sourceSeeking.IMediaSeeking_iface);
193 static inline QTSplitter *impl_from_BaseFilter( BaseFilter *iface )
195 return CONTAINING_RECORD(iface, QTSplitter, filter);
198 static inline QTSplitter *impl_from_IBaseFilter( IBaseFilter *iface )
200 return CONTAINING_RECORD(iface, QTSplitter, filter.IBaseFilter_iface);
204 * Base Filter
207 static IPin* WINAPI QT_GetPin(BaseFilter *iface, int pos)
209 QTSplitter *This = impl_from_BaseFilter(iface);
210 TRACE("Asking for pos %x\n", pos);
212 if (pos > 2 || pos < 0)
213 return NULL;
214 switch (pos)
216 case 0:
217 IPin_AddRef(&This->pInputPin.pin.IPin_iface);
218 return &This->pInputPin.pin.IPin_iface;
219 case 1:
220 if (This->pVideo_Pin)
221 IPin_AddRef(&This->pVideo_Pin->pin.pin.IPin_iface);
222 return &This->pVideo_Pin->pin.pin.IPin_iface;
223 case 2:
224 if (This->pAudio_Pin)
225 IPin_AddRef(&This->pAudio_Pin->pin.pin.IPin_iface);
226 return &This->pAudio_Pin->pin.pin.IPin_iface;
227 default:
228 return NULL;
232 static LONG WINAPI QT_GetPinCount(BaseFilter *iface)
234 QTSplitter *This = impl_from_BaseFilter(iface);
235 int c = 1;
236 if (This->pAudio_Pin) c++;
237 if (This->pVideo_Pin) c++;
238 return c;
241 static const BaseFilterFuncTable BaseFuncTable = {
242 QT_GetPin,
243 QT_GetPinCount
246 IUnknown * CALLBACK QTSplitter_create(IUnknown *punkout, HRESULT *phr)
248 IUnknown *obj = NULL;
249 PIN_INFO *piInput;
250 QTSplitter *This;
251 static const WCHAR wcsInputPinName[] = {'I','n','p','u','t',' ','P','i','n',0};
253 EnterMoviesOnThread(0);
255 RegisterWineDataHandler();
257 This = CoTaskMemAlloc(sizeof(*This));
258 obj = (IUnknown*)This;
259 if (!This)
261 *phr = E_OUTOFMEMORY;
262 return NULL;
264 ZeroMemory(This,sizeof(*This));
266 BaseFilter_Init(&This->filter, &QT_Vtbl, &CLSID_QTSplitter, (DWORD_PTR)(__FILE__ ": QTSplitter.csFilter"), &BaseFuncTable);
268 InitializeCriticalSection(&This->csReceive);
269 This->csReceive.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__": QTSplitter.csReceive");
271 This->pVideo_Pin = NULL;
272 This->pAudio_Pin = NULL;
273 This->state = State_Stopped;
274 This->aSession = NULL;
275 This->runEvent = CreateEventW(NULL, 0, 0, NULL);
277 piInput = &This->pInputPin.pin.pinInfo;
278 piInput->dir = PINDIR_INPUT;
279 piInput->pFilter = &This->filter.IBaseFilter_iface;
280 lstrcpynW(piInput->achName, wcsInputPinName, sizeof(piInput->achName) / sizeof(piInput->achName[0]));
281 This->pInputPin.pin.IPin_iface.lpVtbl = &QT_InputPin_Vtbl;
282 This->pInputPin.pin.refCount = 1;
283 This->pInputPin.pin.pConnectedTo = NULL;
284 This->pInputPin.pin.pCritSec = &This->filter.csFilter;
286 SourceSeeking_Init(&This->sourceSeeking, &QT_Seeking_Vtbl, QTSplitter_ChangeStop, QTSplitter_ChangeStart, QTSplitter_ChangeRate, &This->filter.csFilter);
288 *phr = S_OK;
289 return obj;
292 static void QT_Destroy(QTSplitter *This)
294 IPin *connected = NULL;
295 ULONG pinref;
297 TRACE("Destroying\n");
299 EnterCriticalSection(&This->csReceive);
300 /* Don't need to clean up output pins, disconnecting input pin will do that */
301 IPin_ConnectedTo(&This->pInputPin.pin.IPin_iface, &connected);
302 if (connected)
304 IPin_Disconnect(connected);
305 IPin_Release(connected);
307 pinref = IPin_Release(&This->pInputPin.pin.IPin_iface);
308 if (pinref)
310 ERR("pinref should be null, is %u, destroying anyway\n", pinref);
311 assert((LONG)pinref > 0);
313 while (pinref)
314 pinref = IPin_Release(&This->pInputPin.pin.IPin_iface);
317 if (This->pQTMovie)
319 DisposeMovie(This->pQTMovie);
320 This->pQTMovie = NULL;
322 if (This->vContext)
323 QTVisualContextRelease(This->vContext);
324 if (This->aSession)
325 MovieAudioExtractionEnd(This->aSession);
327 ExitMoviesOnThread();
328 LeaveCriticalSection(&This->csReceive);
330 if (This->loaderThread)
332 WaitForSingleObject(This->loaderThread, INFINITE);
333 CloseHandle(This->loaderThread);
335 if (This->splitterThread)
337 SetEvent(This->runEvent);
338 WaitForSingleObject(This->splitterThread, INFINITE);
339 CloseHandle(This->splitterThread);
342 CloseHandle(This->runEvent);
344 This->csReceive.DebugInfo->Spare[0] = 0;
345 DeleteCriticalSection(&This->csReceive);
346 BaseFilter_Destroy(&This->filter);
348 CoTaskMemFree(This);
351 static HRESULT WINAPI QT_QueryInterface(IBaseFilter *iface, REFIID riid, LPVOID *ppv)
353 QTSplitter *This = impl_from_IBaseFilter(iface);
354 TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
356 *ppv = NULL;
358 if (IsEqualIID(riid, &IID_IUnknown))
359 *ppv = This;
360 else if (IsEqualIID(riid, &IID_IPersist))
361 *ppv = This;
362 else if (IsEqualIID(riid, &IID_IMediaFilter))
363 *ppv = This;
364 else if (IsEqualIID(riid, &IID_IBaseFilter))
365 *ppv = This;
366 else if (IsEqualIID(riid, &IID_IMediaSeeking))
367 *ppv = &This->sourceSeeking;
369 if (*ppv)
371 IUnknown_AddRef((IUnknown *)(*ppv));
372 return S_OK;
375 if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow) &&
376 !IsEqualIID(riid, &IID_IAMFilterMiscFlags))
377 FIXME("No interface for %s!\n", debugstr_guid(riid));
379 return E_NOINTERFACE;
382 static ULONG WINAPI QT_Release(IBaseFilter *iface)
384 QTSplitter *This = impl_from_IBaseFilter(iface);
385 ULONG refCount = InterlockedDecrement(&This->filter.refCount);
387 TRACE("(%p)->() Release from %d\n", This, refCount + 1);
389 if (!refCount)
390 QT_Destroy(This);
392 return refCount;
395 static HRESULT WINAPI QT_Stop(IBaseFilter *iface)
397 QTSplitter *This = impl_from_IBaseFilter(iface);
399 TRACE("()\n");
401 EnterCriticalSection(&This->csReceive);
402 IAsyncReader_BeginFlush(This->pInputPin.pReader);
403 IAsyncReader_EndFlush(This->pInputPin.pReader);
404 LeaveCriticalSection(&This->csReceive);
406 return S_OK;
409 static HRESULT WINAPI QT_Pause(IBaseFilter *iface)
411 HRESULT hr = S_OK;
412 TRACE("()\n");
414 return hr;
417 static OSErr QT_Create_Extract_Session(QTSplitter *filter)
419 AudioStreamBasicDescription aDesc;
420 OSErr err;
421 WAVEFORMATEX* pvi;
423 pvi = (WAVEFORMATEX*)filter->pAudio_Pin->pmt->pbFormat;
425 err = MovieAudioExtractionBegin(filter->pQTMovie, 0, &filter->aSession);
426 if (err != noErr)
428 ERR("Failed to begin Extraction session %i\n",err);
429 return err;
432 err = MovieAudioExtractionGetProperty(filter->aSession,
433 kQTPropertyClass_MovieAudioExtraction_Audio,
434 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
435 sizeof(AudioStreamBasicDescription), &aDesc, NULL);
437 if (err != noErr)
439 MovieAudioExtractionEnd(filter->aSession);
440 filter->aSession = NULL;
441 ERR("Failed to get session description %i\n",err);
442 return err;
445 aDesc.mFormatID = kAudioFormatLinearPCM;
446 aDesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger +
447 kAudioFormatFlagIsPacked;
448 aDesc.mFramesPerPacket = 1;
449 aDesc.mChannelsPerFrame = pvi->nChannels;
450 aDesc.mBitsPerChannel = pvi->wBitsPerSample;
451 aDesc.mSampleRate = pvi->nSamplesPerSec;
452 aDesc.mBytesPerFrame = (aDesc.mBitsPerChannel * aDesc.mChannelsPerFrame) / 8;
453 aDesc.mBytesPerPacket = aDesc.mBytesPerFrame * aDesc.mFramesPerPacket;
455 err = MovieAudioExtractionSetProperty(filter->aSession,
456 kQTPropertyClass_MovieAudioExtraction_Audio,
457 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
458 sizeof(AudioStreamBasicDescription), &aDesc);
460 if (aDesc.mFormatID != kAudioFormatLinearPCM)
462 ERR("Not PCM Wave\n");
463 err = -1;
465 if (aDesc.mFormatFlags != kLinearPCMFormatFlagIsSignedInteger +
466 kAudioFormatFlagIsPacked)
468 ERR("Unhandled Flags\n");
469 err = -1;
471 if (aDesc.mFramesPerPacket != 1)
473 ERR("Unhandled Frames per packet %li\n",aDesc.mFramesPerPacket);
474 err = -1;
476 if (aDesc.mChannelsPerFrame != pvi->nChannels)
478 ERR("Unhandled channel count %li\n",aDesc.mChannelsPerFrame);
479 err = -1;
481 if (aDesc.mBitsPerChannel != pvi->wBitsPerSample)
483 ERR("Unhandled bits per channel %li\n",aDesc.mBitsPerChannel);
484 err = -1;
486 if (aDesc.mSampleRate != pvi->nSamplesPerSec)
488 ERR("Unhandled sample rate %f\n",aDesc.mSampleRate);
489 err = -1;
492 if (err != noErr)
494 ERR("Failed to create Extraction Session\n");
495 MovieAudioExtractionEnd(filter->aSession);
496 filter->aSession = NULL;
499 return err;
502 static DWORD WINAPI QTSplitter_loading_thread(LPVOID data)
504 QTSplitter *This = (QTSplitter *)data;
506 if (This->pAudio_Pin)
508 /* according to QA1469 a movie has to be fully loaded before we
509 can reliably start the Extraction session.
511 If loaded earlier, then we only get an extraction session for
512 the part of the movie that is loaded at that time.
514 We are trying to load as much of the movie as we can before we
515 start extracting. However we can recreate the extraction session
516 again when we run out of loaded extraction frames. But we want
517 to try to minimize that.
520 EnterCriticalSection(&This->csReceive);
521 while(This->pQTMovie && GetMovieLoadState(This->pQTMovie) < kMovieLoadStateComplete)
523 MoviesTask(This->pQTMovie, 100);
524 LeaveCriticalSection(&This->csReceive);
525 Sleep(0);
526 EnterCriticalSection(&This->csReceive);
528 LeaveCriticalSection(&This->csReceive);
530 return 0;
533 static DWORD WINAPI QTSplitter_thread(LPVOID data)
535 QTSplitter *This = (QTSplitter *)data;
536 HRESULT hr = S_OK;
537 TimeValue next_time;
538 CVPixelBufferRef pixelBuffer = NULL;
539 OSStatus err;
540 TimeRecord tr;
542 WaitForSingleObject(This->runEvent, -1);
544 EnterCriticalSection(&This->csReceive);
545 if (!This->pQTMovie)
547 LeaveCriticalSection(&This->csReceive);
548 return 0;
551 This->state = State_Running;
552 /* Prime the pump: Needed for MPEG streams */
553 GetMovieNextInterestingTime(This->pQTMovie, nextTimeEdgeOK | nextTimeStep, 0, NULL, This->movie_time, 1, &next_time, NULL);
555 GetMovieTime(This->pQTMovie, &tr);
557 if (This->pAudio_Pin)
558 QT_Create_Extract_Session(This);
560 LeaveCriticalSection(&This->csReceive);
564 LONGLONG tStart=0, tStop=0;
565 LONGLONG mStart=0, mStop=0;
566 float time;
568 EnterCriticalSection(&This->csReceive);
569 if (!This->pQTMovie)
571 LeaveCriticalSection(&This->csReceive);
572 return 0;
575 GetMovieNextInterestingTime(This->pQTMovie, nextTimeStep, 0, NULL, This->movie_time, 1, &next_time, NULL);
577 if (next_time == -1)
579 TRACE("No next time\n");
580 LeaveCriticalSection(&This->csReceive);
581 break;
584 tr.value = SInt64ToWide(next_time);
585 SetMovieTime(This->pQTMovie, &tr);
586 MoviesTask(This->pQTMovie,0);
587 QTVisualContextTask(This->vContext);
589 TRACE("In loop at time %ld\n",This->movie_time);
590 TRACE("In Next time %ld\n",next_time);
592 mStart = This->movie_time;
593 mStop = next_time;
595 time = (float)(This->movie_time - This->movie_start) / This->movie_scale;
596 tStart = time * 10000000;
597 time = (float)(next_time - This->movie_start) / This->movie_scale;
598 tStop = time * 10000000;
600 /* Deliver Audio */
601 if (This->pAudio_Pin && This->pAudio_Pin->pin.pin.pConnectedTo && This->aSession)
603 int data_size=0;
604 BYTE* ptr;
605 IMediaSample *sample = NULL;
606 AudioBufferList aData;
607 UInt32 flags;
608 UInt32 frames;
609 WAVEFORMATEX* pvi;
610 float duration;
612 pvi = (WAVEFORMATEX*)This->pAudio_Pin->pmt->pbFormat;
614 hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->pAudio_Pin->pin, &sample, NULL, NULL, 0);
616 if (FAILED(hr))
618 ERR("Audio: Unable to get delivery buffer (%x)\n", hr);
619 goto audio_error;
622 hr = IMediaSample_GetPointer(sample, &ptr);
623 if (FAILED(hr))
625 ERR("Audio: Unable to get pointer to buffer (%x)\n", hr);
626 goto audio_error;
629 duration = (float)next_time / This->movie_scale;
630 time = (float)This->movie_time / This->movie_scale;
631 duration -= time;
632 frames = pvi->nSamplesPerSec * duration;
633 TRACE("Need audio for %f seconds (%li frames)\n",duration,frames);
635 data_size = IMediaSample_GetSize(sample);
636 if (data_size < frames * pvi->nBlockAlign)
637 FIXME("Audio buffer is too small\n");
639 aData.mNumberBuffers = 1;
640 aData.mBuffers[0].mNumberChannels = pvi->nChannels;
641 aData.mBuffers[0].mDataByteSize = data_size;
642 aData.mBuffers[0].mData = ptr;
644 err = MovieAudioExtractionFillBuffer(This->aSession, &frames, &aData, &flags);
645 if (frames == 0)
647 TimeRecord etr;
649 /* Ran out of frames, Restart the extraction session */
650 TRACE("Restarting extraction session\n");
651 MovieAudioExtractionEnd(This->aSession);
652 This->aSession = NULL;
653 QT_Create_Extract_Session(This);
655 etr = tr;
656 etr.value = SInt64ToWide(This->movie_time);
657 MovieAudioExtractionSetProperty(This->aSession,
658 kQTPropertyClass_MovieAudioExtraction_Movie,
659 kQTMovieAudioExtractionMoviePropertyID_CurrentTime,
660 sizeof(TimeRecord), &etr );
662 frames = pvi->nSamplesPerSec * duration;
663 aData.mNumberBuffers = 1;
664 aData.mBuffers[0].mNumberChannels = pvi->nChannels;
665 aData.mBuffers[0].mDataByteSize = data_size;
666 aData.mBuffers[0].mData = ptr;
668 MovieAudioExtractionFillBuffer(This->aSession, &frames, &aData, &flags);
671 TRACE("Got %i frames\n",(int)frames);
673 IMediaSample_SetActualDataLength(sample, frames * pvi->nBlockAlign);
675 IMediaSample_SetMediaTime(sample, &mStart, &mStop);
676 IMediaSample_SetTime(sample, &tStart, &tStop);
678 hr = OutputQueue_Receive(This->pAudio_Pin->queue, sample);
679 TRACE("Audio Delivered (%x)\n",hr);
681 audio_error:
682 if (sample)
683 IMediaSample_Release(sample);
685 else
686 TRACE("Audio Pin not connected or no Audio\n");
688 /* Deliver Video */
689 if (This->pVideo_Pin && QTVisualContextIsNewImageAvailable(This->vContext,0))
691 err = QTVisualContextCopyImageForTime(This->vContext, NULL, NULL, &pixelBuffer);
692 if (err == noErr)
694 int data_size=0;
695 BYTE* ptr;
696 IMediaSample *sample = NULL;
698 hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->pVideo_Pin->pin, &sample, NULL, NULL, 0);
699 if (FAILED(hr))
701 ERR("Video: Unable to get delivery buffer (%x)\n", hr);
702 goto video_error;
705 data_size = IMediaSample_GetSize(sample);
706 if (data_size < This->outputSize)
708 ERR("Sample size is too small %d < %d\n", data_size, This->outputSize)
710 hr = E_FAIL;
711 goto video_error;
714 hr = IMediaSample_GetPointer(sample, &ptr);
715 if (FAILED(hr))
717 ERR("Video: Unable to get pointer to buffer (%x)\n", hr);
718 goto video_error;
721 hr = AccessPixelBufferPixels( pixelBuffer, ptr);
722 if (FAILED(hr))
724 ERR("Failed to access Pixels\n");
725 goto video_error;
728 IMediaSample_SetActualDataLength(sample, This->outputSize);
730 IMediaSample_SetMediaTime(sample, &mStart, &mStop);
731 IMediaSample_SetTime(sample, &tStart, &tStop);
733 hr = OutputQueue_Receive(This->pVideo_Pin->queue, sample);
734 TRACE("Video Delivered (%x)\n",hr);
736 video_error:
737 if (sample)
738 IMediaSample_Release(sample);
739 if (pixelBuffer)
740 CVPixelBufferRelease(pixelBuffer);
743 else
744 TRACE("No video to deliver\n");
746 This->movie_time = next_time;
747 LeaveCriticalSection(&This->csReceive);
748 } while (hr == S_OK);
750 This->state = State_Stopped;
751 if (This->pAudio_Pin)
752 OutputQueue_EOS(This->pAudio_Pin->queue);
753 if (This->pVideo_Pin)
754 OutputQueue_EOS(This->pVideo_Pin->queue);
756 return hr;
759 static HRESULT WINAPI QT_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
761 HRESULT hr = S_OK;
762 QTSplitter *This = impl_from_IBaseFilter(iface);
763 HRESULT hr_any = VFW_E_NOT_CONNECTED;
765 TRACE("(%s)\n", wine_dbgstr_longlong(tStart));
767 EnterCriticalSection(&This->csReceive);
768 This->filter.rtStreamStart = tStart;
770 if (This->pVideo_Pin)
771 hr = BaseOutputPinImpl_Active(&This->pVideo_Pin->pin);
772 if (SUCCEEDED(hr))
773 hr_any = hr;
774 if (This->pAudio_Pin)
775 hr = BaseOutputPinImpl_Active(&This->pAudio_Pin->pin);
776 if (SUCCEEDED(hr))
777 hr_any = hr;
779 hr = hr_any;
781 SetEvent(This->runEvent);
782 LeaveCriticalSection(&This->csReceive);
784 return hr;
787 static HRESULT WINAPI QT_GetState(IBaseFilter *iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
789 QTSplitter *This = impl_from_IBaseFilter(iface);
790 TRACE("(%d, %p)\n", dwMilliSecsTimeout, pState);
792 *pState = This->state;
794 return S_OK;
797 static HRESULT WINAPI QT_FindPin(IBaseFilter *iface, LPCWSTR Id, IPin **ppPin)
799 FIXME("(%p)->(%s,%p) stub\n", iface, debugstr_w(Id), ppPin);
800 return E_NOTIMPL;
803 static const IBaseFilterVtbl QT_Vtbl = {
804 QT_QueryInterface,
805 BaseFilterImpl_AddRef,
806 QT_Release,
807 BaseFilterImpl_GetClassID,
808 QT_Stop,
809 QT_Pause,
810 QT_Run,
811 QT_GetState,
812 BaseFilterImpl_SetSyncSource,
813 BaseFilterImpl_GetSyncSource,
814 BaseFilterImpl_EnumPins,
815 QT_FindPin,
816 BaseFilterImpl_QueryFilterInfo,
817 BaseFilterImpl_JoinFilterGraph,
818 BaseFilterImpl_QueryVendorInfo
822 * Input Pin
824 static HRESULT QT_RemoveOutputPins(QTSplitter *This)
826 HRESULT hr;
827 TRACE("(%p)\n", This);
829 if (This->pVideo_Pin)
831 OutputQueue_Destroy(This->pVideo_Pin->queue);
832 hr = BaseOutputPinImpl_BreakConnect(&This->pVideo_Pin->pin);
833 TRACE("Disconnect: %08x\n", hr);
834 IPin_Release(&This->pVideo_Pin->pin.pin.IPin_iface);
835 This->pVideo_Pin = NULL;
837 if (This->pAudio_Pin)
839 OutputQueue_Destroy(This->pAudio_Pin->queue);
840 hr = BaseOutputPinImpl_BreakConnect(&This->pAudio_Pin->pin);
841 TRACE("Disconnect: %08x\n", hr);
842 IPin_Release(&This->pAudio_Pin->pin.pin.IPin_iface);
843 This->pAudio_Pin = NULL;
846 BaseFilterImpl_IncrementPinVersion(&This->filter);
847 return S_OK;
850 static inline QTInPin *impl_from_IPin( IPin *iface )
852 return CONTAINING_RECORD(iface, QTInPin, pin.IPin_iface);
855 static ULONG WINAPI QTInPin_Release(IPin *iface)
857 QTInPin *This = impl_from_IPin(iface);
858 ULONG refCount = InterlockedDecrement(&This->pin.refCount);
860 TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
861 if (!refCount)
863 FreeMediaType(&This->pin.mtCurrent);
864 if (This->pAlloc)
865 IMemAllocator_Release(This->pAlloc);
866 This->pAlloc = NULL;
867 if (This->pReader)
868 IAsyncReader_Release(This->pReader);
869 This->pReader = NULL;
870 This->pin.IPin_iface.lpVtbl = NULL;
871 return 0;
873 else
874 return refCount;
877 static HRESULT QT_Process_Video_Track(QTSplitter* filter, Track trk)
879 AM_MEDIA_TYPE amt;
880 VIDEOINFOHEADER * pvi;
881 PIN_INFO piOutput;
882 HRESULT hr = S_OK;
883 OSErr err;
884 static const WCHAR szwVideoOut[] = {'V','i','d','e','o',0};
885 CFMutableDictionaryRef pixelBufferOptions = NULL;
886 CFMutableDictionaryRef visualContextOptions = NULL;
887 CFNumberRef n = NULL;
888 int t;
889 DWORD outputWidth, outputHeight, outputDepth;
890 Fixed trackWidth, trackHeight;
891 Media videoMedia;
892 long sampleCount;
893 TimeValue64 duration;
894 TimeScale timeScale;
896 ZeroMemory(&amt, sizeof(amt));
897 amt.formattype = FORMAT_VideoInfo;
898 amt.majortype = MEDIATYPE_Video;
899 amt.subtype = MEDIASUBTYPE_RGB24;
901 GetTrackDimensions(trk, &trackWidth, &trackHeight);
903 outputDepth = 3;
904 outputWidth = Fix2Long(trackWidth);
905 outputHeight = Fix2Long(trackHeight);
906 TRACE("Width %i Height %i\n",outputWidth, outputHeight);
908 amt.cbFormat = sizeof(VIDEOINFOHEADER);
909 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
910 ZeroMemory(amt.pbFormat, amt.cbFormat);
911 pvi = (VIDEOINFOHEADER *)amt.pbFormat;
912 pvi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
913 pvi->bmiHeader.biWidth = outputWidth;
914 pvi->bmiHeader.biHeight = outputHeight;
915 pvi->bmiHeader.biPlanes = 1;
916 pvi->bmiHeader.biBitCount = 24;
917 pvi->bmiHeader.biCompression = BI_RGB;
918 pvi->bmiHeader.biSizeImage = outputWidth * outputHeight * outputDepth;
920 filter->outputSize = pvi->bmiHeader.biSizeImage;
921 amt.lSampleSize = 0;
923 pixelBufferOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
925 t = k32ARGBPixelFormat;
926 n = CFNumberCreate(NULL, kCFNumberIntType, &t);
927 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferPixelFormatTypeKey, n);
928 CFRelease(n);
930 n = CFNumberCreate(NULL, kCFNumberIntType, &outputWidth);
931 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferWidthKey, n);
932 CFRelease(n);
934 n = CFNumberCreate(NULL, kCFNumberIntType, &outputHeight);
935 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferHeightKey, n);
936 CFRelease(n);
938 t = 16;
939 n = CFNumberCreate(NULL, kCFNumberIntType, &t);
940 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferBytesPerRowAlignmentKey, n);
941 CFRelease(n);
943 visualContextOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
945 CFDictionarySetValue(visualContextOptions, kQTVisualContextPixelBufferAttributesKey, pixelBufferOptions);
947 err = QTPixelBufferContextCreate(NULL, visualContextOptions,&filter->vContext);
948 CFRelease(pixelBufferOptions);
949 CFRelease(visualContextOptions);
950 if (err != noErr)
952 ERR("Failed to create Visual Context\n");
953 return E_FAIL;
956 err = SetMovieVisualContext(filter->pQTMovie, filter->vContext);
957 if (err != noErr)
959 ERR("Failed to set Visual Context\n");
960 return E_FAIL;
963 videoMedia = GetTrackMedia(trk);
964 sampleCount = GetMediaSampleCount(videoMedia);
965 timeScale = GetMediaTimeScale(videoMedia);
966 duration = GetMediaDisplayDuration(videoMedia);
967 pvi->AvgTimePerFrame = (100000.0 * sampleCount * timeScale) / duration;
969 piOutput.dir = PINDIR_OUTPUT;
970 piOutput.pFilter = &filter->filter.IBaseFilter_iface;
971 lstrcpyW(piOutput.achName,szwVideoOut);
973 hr = QT_AddPin(filter, &piOutput, &amt, TRUE);
974 if (FAILED(hr))
975 ERR("Failed to add Video Track\n");
976 else
977 TRACE("Video Pin %p\n",filter->pVideo_Pin);
979 return hr;
982 static HRESULT QT_Process_Audio_Track(QTSplitter* filter, Track trk)
984 AM_MEDIA_TYPE amt;
985 WAVEFORMATEX* pvi;
986 PIN_INFO piOutput;
987 HRESULT hr = S_OK;
988 static const WCHAR szwAudioOut[] = {'A','u','d','i','o',0};
989 Media audioMedia;
991 SoundDescriptionHandle aDesc = (SoundDescriptionHandle) NewHandle(sizeof(SoundDescription));
993 audioMedia = GetTrackMedia(trk);
994 GetMediaSampleDescription(audioMedia, 1, (SampleDescriptionHandle)aDesc);
996 ZeroMemory(&amt, sizeof(amt));
997 amt.formattype = FORMAT_WaveFormatEx;
998 amt.majortype = MEDIATYPE_Audio;
999 amt.subtype = MEDIASUBTYPE_PCM;
1000 amt.bTemporalCompression = 0;
1002 amt.cbFormat = sizeof(WAVEFORMATEX);
1003 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
1004 ZeroMemory(amt.pbFormat, amt.cbFormat);
1005 pvi = (WAVEFORMATEX*)amt.pbFormat;
1007 pvi->cbSize = sizeof(WAVEFORMATEX);
1008 pvi->wFormatTag = WAVE_FORMAT_PCM;
1009 pvi->nChannels = ((SoundDescription)**aDesc).numChannels;
1010 if (pvi->nChannels < 1 || pvi->nChannels > 2)
1011 pvi->nChannels = 2;
1012 pvi->nSamplesPerSec = (((SoundDescription)**aDesc).sampleRate/65536);
1013 if (pvi->nSamplesPerSec < 8000 || pvi->nChannels > 48000)
1014 pvi->nSamplesPerSec = 44100;
1015 pvi->wBitsPerSample = ((SoundDescription)**aDesc).sampleSize;
1016 if (pvi->wBitsPerSample < 8 || pvi->wBitsPerSample > 32)
1017 pvi->wBitsPerSample = 16;
1018 pvi->nBlockAlign = (pvi->nChannels * pvi->wBitsPerSample) / 8;
1019 pvi->nAvgBytesPerSec = pvi->nSamplesPerSec * pvi->nBlockAlign;
1021 DisposeHandle((Handle)aDesc);
1023 piOutput.dir = PINDIR_OUTPUT;
1024 piOutput.pFilter = &filter->filter.IBaseFilter_iface;
1025 lstrcpyW(piOutput.achName,szwAudioOut);
1027 hr = QT_AddPin(filter, &piOutput, &amt, FALSE);
1028 if (FAILED(hr))
1029 ERR("Failed to add Audio Track\n");
1030 else
1031 TRACE("Audio Pin %p\n",filter->pAudio_Pin);
1032 return hr;
1035 static HRESULT QT_Process_Movie(QTSplitter* filter)
1037 HRESULT hr = S_OK;
1038 OSErr err;
1039 WineDataRefRecord ptrDataRefRec;
1040 Handle dataRef = NULL;
1041 Track trk;
1042 short id = 0;
1043 DWORD tid;
1044 LONGLONG time;
1046 TRACE("Trying movie connect\n");
1048 ptrDataRefRec.pReader = filter->pInputPin.pReader;
1049 ptrDataRefRec.streamSubtype = filter->pInputPin.subType;
1050 PtrToHand( &ptrDataRefRec, &dataRef, sizeof(WineDataRefRecord));
1052 err = NewMovieFromDataRef(&filter->pQTMovie, newMovieActive|newMovieDontInteractWithUser|newMovieDontAutoUpdateClock|newMovieDontAskUnresolvedDataRefs|newMovieAsyncOK, &id, dataRef, 'WINE');
1054 DisposeHandle(dataRef);
1056 if (err != noErr)
1058 FIXME("QuickTime cannot handle media type(%i)\n",err);
1059 return VFW_E_TYPE_NOT_ACCEPTED;
1062 PrePrerollMovie(filter->pQTMovie, 0, fixed1, NULL, NULL);
1063 PrerollMovie(filter->pQTMovie, 0, fixed1);
1064 GoToBeginningOfMovie(filter->pQTMovie);
1065 SetMovieActive(filter->pQTMovie,TRUE);
1067 if (GetMovieLoadState(filter->pQTMovie) < kMovieLoadStateLoaded)
1068 MoviesTask(filter->pQTMovie,100);
1070 trk = GetMovieIndTrackType(filter->pQTMovie, 1, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
1071 TRACE("%p is a video track\n",trk);
1072 if (trk)
1073 hr = QT_Process_Video_Track(filter, trk);
1075 if (FAILED(hr))
1076 return hr;
1078 trk = GetMovieIndTrackType(filter->pQTMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
1079 TRACE("%p is an audio track\n",trk);
1080 if (trk)
1081 hr = QT_Process_Audio_Track(filter, trk);
1083 time = GetMovieDuration(filter->pQTMovie);
1084 filter->movie_scale = GetMovieTimeScale(filter->pQTMovie);
1085 filter->sourceSeeking.llDuration = ((double)time / filter->movie_scale) * 10000000;
1086 filter->sourceSeeking.llStop = filter->sourceSeeking.llDuration;
1088 TRACE("Movie duration is %s\n",wine_dbgstr_longlong(filter->sourceSeeking.llDuration));
1090 filter->loaderThread = CreateThread(NULL, 0, QTSplitter_loading_thread, filter, 0, &tid);
1091 if (filter->loaderThread)
1092 TRACE("Created loading thread 0x%08x\n", tid);
1093 filter->splitterThread = CreateThread(NULL, 0, QTSplitter_thread, filter, 0, &tid);
1094 if (filter->splitterThread)
1095 TRACE("Created processing thread 0x%08x\n", tid);
1096 else
1097 hr = HRESULT_FROM_WIN32(GetLastError());
1099 return hr;
1102 static HRESULT WINAPI QTInPin_ReceiveConnection(IPin *iface, IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
1104 HRESULT hr = S_OK;
1105 ALLOCATOR_PROPERTIES props;
1106 QTInPin *This = impl_from_IPin(iface);
1107 IMemAllocator *pAlloc;
1109 TRACE("(%p/%p)->(%p, %p)\n", This, iface, pReceivePin, pmt);
1111 EnterCriticalSection(This->pin.pCritSec);
1112 This->pReader = NULL;
1114 if (This->pin.pConnectedTo)
1115 hr = VFW_E_ALREADY_CONNECTED;
1116 else if (IPin_QueryAccept(iface, pmt) != S_OK)
1117 hr = VFW_E_TYPE_NOT_ACCEPTED;
1118 else
1120 PIN_DIRECTION pindirReceive;
1121 IPin_QueryDirection(pReceivePin, &pindirReceive);
1122 if (pindirReceive != PINDIR_OUTPUT)
1123 hr = VFW_E_INVALID_DIRECTION;
1126 if (FAILED(hr))
1128 LeaveCriticalSection(This->pin.pCritSec);
1129 return hr;
1132 hr = IPin_QueryInterface(pReceivePin, &IID_IAsyncReader, (LPVOID *)&This->pReader);
1133 if (FAILED(hr))
1135 LeaveCriticalSection(This->pin.pCritSec);
1136 TRACE("Input source is not an AsyncReader\n");
1137 return hr;
1140 LeaveCriticalSection(This->pin.pCritSec);
1141 EnterCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1142 hr = QT_Process_Movie(impl_from_IBaseFilter(This->pin.pinInfo.pFilter));
1143 if (FAILED(hr))
1145 IAsyncReader_Release(This->pReader);
1146 This->pReader = NULL;
1147 LeaveCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1148 TRACE("Unable to process movie\n");
1149 return hr;
1152 This->pAlloc = NULL;
1153 props.cBuffers = 8;
1154 props.cbAlign = 1;
1155 props.cbBuffer = impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->outputSize + props.cbAlign;
1156 props.cbPrefix = 0;
1157 hr = CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC,
1158 &IID_IMemAllocator, (LPVOID *)&pAlloc);
1159 if (SUCCEEDED(hr))
1161 /* A certain IAsyncReader::RequestAllocator expects to be passed
1162 non-NULL preferred allocator */
1163 hr = IAsyncReader_RequestAllocator(This->pReader, pAlloc, &props, &This->pAlloc);
1164 if (FAILED(hr))
1165 WARN("Can't get an allocator, got %08x\n", hr);
1166 IMemAllocator_Release(pAlloc);
1169 if (SUCCEEDED(hr))
1171 CopyMediaType(&This->pin.mtCurrent, pmt);
1172 This->pin.pConnectedTo = pReceivePin;
1173 IPin_AddRef(pReceivePin);
1174 hr = IMemAllocator_Commit(This->pAlloc);
1176 else
1178 QT_RemoveOutputPins(impl_from_IBaseFilter(This->pin.pinInfo.pFilter));
1179 if (This->pReader)
1180 IAsyncReader_Release(This->pReader);
1181 This->pReader = NULL;
1182 if (This->pAlloc)
1183 IMemAllocator_Release(This->pAlloc);
1184 This->pAlloc = NULL;
1186 TRACE("Size: %i\n", props.cbBuffer);
1187 LeaveCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1189 return hr;
1192 static HRESULT WINAPI QTInPin_Disconnect(IPin *iface)
1194 HRESULT hr;
1195 QTInPin *This = impl_from_IPin(iface);
1196 FILTER_STATE state;
1197 TRACE("()\n");
1199 hr = IBaseFilter_GetState(This->pin.pinInfo.pFilter, INFINITE, &state);
1200 EnterCriticalSection(This->pin.pCritSec);
1201 if (This->pin.pConnectedTo)
1203 QTSplitter *Parser = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
1205 if (SUCCEEDED(hr) && state == State_Stopped)
1207 IMemAllocator_Decommit(This->pAlloc);
1208 IPin_Disconnect(This->pin.pConnectedTo);
1209 IPin_Release(This->pin.pConnectedTo);
1210 This->pin.pConnectedTo = NULL;
1211 hr = QT_RemoveOutputPins(Parser);
1213 else
1214 hr = VFW_E_NOT_STOPPED;
1216 else
1217 hr = S_FALSE;
1218 LeaveCriticalSection(This->pin.pCritSec);
1219 return hr;
1222 static HRESULT WINAPI QTInPin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *pmt)
1224 QTInPin *This = impl_from_IPin(iface);
1226 TRACE("(%p)->(%p)\n", This, pmt);
1228 if (IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream))
1230 This->subType = pmt->subtype;
1231 return S_OK;
1233 return S_FALSE;
1236 static HRESULT WINAPI QTInPin_EndOfStream(IPin *iface)
1238 QTInPin *pin = impl_from_IPin(iface);
1239 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1241 FIXME("Propagate message on %p\n", This);
1242 return S_OK;
1245 static HRESULT WINAPI QTInPin_BeginFlush(IPin *iface)
1247 QTInPin *pin = impl_from_IPin(iface);
1248 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1250 FIXME("Propagate message on %p\n", This);
1251 return S_OK;
1254 static HRESULT WINAPI QTInPin_EndFlush(IPin *iface)
1256 QTInPin *pin = impl_from_IPin(iface);
1257 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1259 FIXME("Propagate message on %p\n", This);
1260 return S_OK;
1263 static HRESULT WINAPI QTInPin_NewSegment(IPin *iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
1265 QTInPin *pin = impl_from_IPin(iface);
1266 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1268 BasePinImpl_NewSegment(iface, tStart, tStop, dRate);
1269 FIXME("Propagate message on %p\n", This);
1270 return S_OK;
1273 static HRESULT WINAPI QTInPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
1275 QTInPin *This = impl_from_IPin(iface);
1277 TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
1279 *ppv = NULL;
1281 if (IsEqualIID(riid, &IID_IUnknown))
1282 *ppv = iface;
1283 else if (IsEqualIID(riid, &IID_IPin))
1284 *ppv = iface;
1285 else if (IsEqualIID(riid, &IID_IMediaSeeking))
1286 return IBaseFilter_QueryInterface(This->pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
1288 if (*ppv)
1290 IUnknown_AddRef((IUnknown *)(*ppv));
1291 return S_OK;
1294 FIXME("No interface for %s!\n", debugstr_guid(riid));
1296 return E_NOINTERFACE;
1299 static HRESULT WINAPI QTInPin_EnumMediaTypes(IPin *iface, IEnumMediaTypes **ppEnum)
1301 QTInPin *This = impl_from_IPin(iface);
1303 TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
1305 return EnumMediaTypes_Construct(&This->pin, BasePinImpl_GetMediaType, BasePinImpl_GetMediaTypeVersion, ppEnum);
1308 static const IPinVtbl QT_InputPin_Vtbl = {
1309 QTInPin_QueryInterface,
1310 BasePinImpl_AddRef,
1311 QTInPin_Release,
1312 BaseInputPinImpl_Connect,
1313 QTInPin_ReceiveConnection,
1314 QTInPin_Disconnect,
1315 BasePinImpl_ConnectedTo,
1316 BasePinImpl_ConnectionMediaType,
1317 BasePinImpl_QueryPinInfo,
1318 BasePinImpl_QueryDirection,
1319 BasePinImpl_QueryId,
1320 QTInPin_QueryAccept,
1321 QTInPin_EnumMediaTypes,
1322 BasePinImpl_QueryInternalConnections,
1323 QTInPin_EndOfStream,
1324 QTInPin_BeginFlush,
1325 QTInPin_EndFlush,
1326 QTInPin_NewSegment
1330 * Output Pin
1332 static inline QTOutPin *impl_QTOutPin_from_IPin( IPin *iface )
1334 return CONTAINING_RECORD(iface, QTOutPin, pin.pin.IPin_iface);
1337 static inline QTOutPin *impl_QTOutPin_from_BasePin( BasePin *iface )
1339 return CONTAINING_RECORD(iface, QTOutPin, pin.pin);
1342 static inline QTOutPin *impl_QTOutPin_from_BaseOutputPin( BaseOutputPin *iface )
1344 return CONTAINING_RECORD(iface, QTOutPin, pin);
1347 static HRESULT WINAPI QTOutPin_QueryInterface(IPin *iface, REFIID riid, void **ppv)
1349 QTOutPin *This = impl_QTOutPin_from_IPin(iface);
1351 TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
1353 *ppv = NULL;
1355 if (IsEqualIID(riid, &IID_IUnknown))
1356 *ppv = iface;
1357 else if (IsEqualIID(riid, &IID_IPin))
1358 *ppv = iface;
1359 else if (IsEqualIID(riid, &IID_IMediaSeeking))
1360 return IBaseFilter_QueryInterface(This->pin.pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
1361 else if (IsEqualIID(riid, &IID_IQualityControl))
1362 *ppv = &This->IQualityControl_iface;
1364 if (*ppv)
1366 IUnknown_AddRef((IUnknown *)(*ppv));
1367 return S_OK;
1369 FIXME("No interface for %s!\n", debugstr_guid(riid));
1370 return E_NOINTERFACE;
1373 static ULONG WINAPI QTOutPin_Release(IPin *iface)
1375 QTOutPin *This = impl_QTOutPin_from_IPin(iface);
1376 ULONG refCount = InterlockedDecrement(&This->pin.pin.refCount);
1377 TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
1379 if (!refCount)
1381 DeleteMediaType(This->pmt);
1382 FreeMediaType(&This->pin.pin.mtCurrent);
1383 if (This->pin.pAllocator)
1384 IMemAllocator_Release(This->pin.pAllocator);
1385 CoTaskMemFree(This);
1386 return 0;
1388 return refCount;
1391 static HRESULT WINAPI QTOutPin_GetMediaType(BasePin *iface, int iPosition, AM_MEDIA_TYPE *pmt)
1393 QTOutPin *This = impl_QTOutPin_from_BasePin(iface);
1395 if (iPosition < 0)
1396 return E_INVALIDARG;
1397 if (iPosition > 0)
1398 return VFW_S_NO_MORE_ITEMS;
1399 CopyMediaType(pmt, This->pmt);
1400 return S_OK;
1403 static HRESULT WINAPI QTOutPin_DecideBufferSize(BaseOutputPin *iface, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
1405 /* Unused */
1406 return S_OK;
1409 static HRESULT WINAPI QTOutPin_DecideAllocator(BaseOutputPin *iface, IMemInputPin *pPin, IMemAllocator **pAlloc)
1411 HRESULT hr;
1412 QTOutPin *This = impl_QTOutPin_from_BaseOutputPin(iface);
1413 QTSplitter *QTfilter = impl_from_IBaseFilter(This->pin.pin.pinInfo.pFilter);
1415 *pAlloc = NULL;
1416 if (QTfilter->pInputPin.pAlloc)
1418 hr = IMemInputPin_NotifyAllocator(pPin, QTfilter->pInputPin.pAlloc, FALSE);
1419 if (SUCCEEDED(hr))
1421 *pAlloc = QTfilter->pInputPin.pAlloc;
1422 IMemAllocator_AddRef(*pAlloc);
1425 else
1426 hr = VFW_E_NO_ALLOCATOR;
1428 return hr;
1431 static HRESULT WINAPI QTOutPin_BreakConnect(BaseOutputPin *This)
1433 HRESULT hr;
1435 TRACE("(%p)->()\n", This);
1437 EnterCriticalSection(This->pin.pCritSec);
1438 if (!This->pin.pConnectedTo || !This->pMemInputPin)
1439 hr = VFW_E_NOT_CONNECTED;
1440 else
1442 hr = IPin_Disconnect(This->pin.pConnectedTo);
1443 IPin_Disconnect(&This->pin.IPin_iface);
1445 LeaveCriticalSection(This->pin.pCritSec);
1447 return hr;
1450 static const IPinVtbl QT_OutputPin_Vtbl = {
1451 QTOutPin_QueryInterface,
1452 BasePinImpl_AddRef,
1453 QTOutPin_Release,
1454 BaseOutputPinImpl_Connect,
1455 BaseOutputPinImpl_ReceiveConnection,
1456 BaseOutputPinImpl_Disconnect,
1457 BasePinImpl_ConnectedTo,
1458 BasePinImpl_ConnectionMediaType,
1459 BasePinImpl_QueryPinInfo,
1460 BasePinImpl_QueryDirection,
1461 BasePinImpl_QueryId,
1462 BasePinImpl_QueryAccept,
1463 BasePinImpl_EnumMediaTypes,
1464 BasePinImpl_QueryInternalConnections,
1465 BaseOutputPinImpl_EndOfStream,
1466 BaseOutputPinImpl_BeginFlush,
1467 BaseOutputPinImpl_EndFlush,
1468 BasePinImpl_NewSegment
1471 static inline QTOutPin *impl_from_IQualityControl( IQualityControl *iface )
1473 return CONTAINING_RECORD(iface, QTOutPin, IQualityControl_iface);
1476 HRESULT WINAPI QT_QualityControl_QueryInterface(IQualityControl *iface, REFIID riid, void **ppv)
1478 QTOutPin *This = impl_from_IQualityControl(iface);
1479 return IPin_QueryInterface(&This->pin.pin.IPin_iface, riid, ppv);
1482 ULONG WINAPI QT_QualityControl_AddRef(IQualityControl *iface)
1484 QTOutPin *This = impl_from_IQualityControl(iface);
1485 return IPin_AddRef(&This->pin.pin.IPin_iface);
1488 ULONG WINAPI QT_QualityControl_Release(IQualityControl *iface)
1490 QTOutPin *This = impl_from_IQualityControl(iface);
1491 return IPin_Release(&This->pin.pin.IPin_iface);
1494 static HRESULT WINAPI QT_QualityControl_Notify(IQualityControl *iface, IBaseFilter *sender, Quality qm)
1496 REFERENCE_TIME late = qm.Late;
1497 if (qm.Late < 0 && -qm.Late > qm.TimeStamp)
1498 late = -qm.TimeStamp;
1499 /* TODO: Do Something */
1500 return S_OK;
1503 HRESULT WINAPI QT_QualityControl_SetSink(IQualityControl *iface, IQualityControl *tonotify)
1505 /* Do nothing */
1506 return S_OK;
1509 static const IQualityControlVtbl QTOutPin_QualityControl_Vtbl = {
1510 QT_QualityControl_QueryInterface,
1511 QT_QualityControl_AddRef,
1512 QT_QualityControl_Release,
1513 QT_QualityControl_Notify,
1514 QT_QualityControl_SetSink
1517 static const BaseOutputPinFuncTable output_BaseOutputFuncTable = {
1519 NULL,
1520 BaseOutputPinImpl_AttemptConnection,
1521 BasePinImpl_GetMediaTypeVersion,
1522 QTOutPin_GetMediaType
1524 QTOutPin_DecideBufferSize,
1525 QTOutPin_DecideAllocator,
1526 QTOutPin_BreakConnect
1529 static const OutputQueueFuncTable output_OutputQueueFuncTable = {
1530 OutputQueueImpl_ThreadProc
1533 static HRESULT QT_AddPin(QTSplitter *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt, BOOL video)
1535 HRESULT hr;
1536 IPin **target;
1538 if (video)
1539 target = (IPin**)&This->pVideo_Pin;
1540 else
1541 target = (IPin**)&This->pAudio_Pin;
1543 if (*target != NULL)
1545 FIXME("We already have a %s pin\n",(video)?"video":"audio");
1546 return E_FAIL;
1549 hr = BaseOutputPin_Construct(&QT_OutputPin_Vtbl, sizeof(QTOutPin), piOutput, &output_BaseOutputFuncTable, &This->filter.csFilter, (IPin**)target);
1550 if (SUCCEEDED(hr))
1552 QTOutPin *pin = (QTOutPin*)*target;
1553 pin->pmt = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
1554 CopyMediaType(pin->pmt, amt);
1555 pin->pin.pin.pinInfo.pFilter = (LPVOID)This;
1556 pin->IQualityControl_iface.lpVtbl = &QTOutPin_QualityControl_Vtbl;
1558 BaseFilterImpl_IncrementPinVersion(&This->filter);
1560 hr = OutputQueue_Construct(&pin->pin, TRUE, TRUE, 5, FALSE, THREAD_PRIORITY_NORMAL, &output_OutputQueueFuncTable, &pin->queue);
1562 else
1563 ERR("Failed with error %x\n", hr);
1564 return hr;
1567 static HRESULT WINAPI QTSplitter_ChangeStart(IMediaSeeking *iface)
1569 QTSplitter *This = impl_from_IMediaSeeking(iface);
1570 TRACE("(%p)\n", iface);
1571 EnterCriticalSection(&This->csReceive);
1572 This->movie_time = (This->sourceSeeking.llCurrent * This->movie_scale)/10000000;
1573 This->movie_start = This->movie_time;
1574 LeaveCriticalSection(&This->csReceive);
1575 return S_OK;
1578 static HRESULT WINAPI QTSplitter_ChangeStop(IMediaSeeking *iface)
1580 FIXME("(%p) filter hasn't implemented stop position change!\n", iface);
1581 return S_OK;
1584 static HRESULT WINAPI QTSplitter_ChangeRate(IMediaSeeking *iface)
1586 FIXME("(%p) filter hasn't implemented rate change!\n", iface);
1587 return S_OK;
1590 static HRESULT WINAPI QT_Seeking_QueryInterface(IMediaSeeking * iface, REFIID riid, LPVOID * ppv)
1592 QTSplitter *This = impl_from_IMediaSeeking(iface);
1594 return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv);
1597 static ULONG WINAPI QT_Seeking_AddRef(IMediaSeeking * iface)
1599 QTSplitter *This = impl_from_IMediaSeeking(iface);
1601 return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
1604 static ULONG WINAPI QT_Seeking_Release(IMediaSeeking * iface)
1606 QTSplitter *This = impl_from_IMediaSeeking(iface);
1608 return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
1611 static const IMediaSeekingVtbl QT_Seeking_Vtbl =
1613 QT_Seeking_QueryInterface,
1614 QT_Seeking_AddRef,
1615 QT_Seeking_Release,
1616 SourceSeekingImpl_GetCapabilities,
1617 SourceSeekingImpl_CheckCapabilities,
1618 SourceSeekingImpl_IsFormatSupported,
1619 SourceSeekingImpl_QueryPreferredFormat,
1620 SourceSeekingImpl_GetTimeFormat,
1621 SourceSeekingImpl_IsUsingTimeFormat,
1622 SourceSeekingImpl_SetTimeFormat,
1623 SourceSeekingImpl_GetDuration,
1624 SourceSeekingImpl_GetStopPosition,
1625 SourceSeekingImpl_GetCurrentPosition,
1626 SourceSeekingImpl_ConvertTimeFormat,
1627 SourceSeekingImpl_SetPositions,
1628 SourceSeekingImpl_GetPositions,
1629 SourceSeekingImpl_GetAvailable,
1630 SourceSeekingImpl_SetRate,
1631 SourceSeekingImpl_GetRate,
1632 SourceSeekingImpl_GetPreroll