urlmon: Recognize <body> tag in FindMimeFromData function.
[wine/multimedia.git] / dlls / wineqtdecoder / qtsplitter.c
blob35dc8071a89f2197dcf30fb93b00026b058b80b6
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 EnterMovies();
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 ExitMovies();
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);
347 CoTaskMemFree(This);
350 static HRESULT WINAPI QT_QueryInterface(IBaseFilter *iface, REFIID riid, LPVOID *ppv)
352 QTSplitter *This = impl_from_IBaseFilter(iface);
353 TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
355 *ppv = NULL;
357 if (IsEqualIID(riid, &IID_IUnknown))
358 *ppv = This;
359 else if (IsEqualIID(riid, &IID_IPersist))
360 *ppv = This;
361 else if (IsEqualIID(riid, &IID_IMediaFilter))
362 *ppv = This;
363 else if (IsEqualIID(riid, &IID_IBaseFilter))
364 *ppv = This;
365 else if (IsEqualIID(riid, &IID_IMediaSeeking))
366 *ppv = &This->sourceSeeking;
368 if (*ppv)
370 IUnknown_AddRef((IUnknown *)(*ppv));
371 return S_OK;
374 if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow) &&
375 !IsEqualIID(riid, &IID_IAMFilterMiscFlags))
376 FIXME("No interface for %s!\n", debugstr_guid(riid));
378 return E_NOINTERFACE;
381 static ULONG WINAPI QT_Release(IBaseFilter *iface)
383 QTSplitter *This = impl_from_IBaseFilter(iface);
384 ULONG refCount = BaseFilterImpl_Release(iface);
386 TRACE("(%p)->() Release from %d\n", This, refCount + 1);
388 if (!refCount)
389 QT_Destroy(This);
391 return refCount;
394 static HRESULT WINAPI QT_Stop(IBaseFilter *iface)
396 QTSplitter *This = impl_from_IBaseFilter(iface);
398 TRACE("()\n");
400 EnterCriticalSection(&This->csReceive);
401 IAsyncReader_BeginFlush(This->pInputPin.pReader);
402 IAsyncReader_EndFlush(This->pInputPin.pReader);
403 LeaveCriticalSection(&This->csReceive);
405 return S_OK;
408 static HRESULT WINAPI QT_Pause(IBaseFilter *iface)
410 HRESULT hr = S_OK;
411 TRACE("()\n");
413 return hr;
416 static OSErr QT_Create_Extract_Session(QTSplitter *filter)
418 AudioStreamBasicDescription aDesc;
419 OSErr err;
420 WAVEFORMATEX* pvi;
422 pvi = (WAVEFORMATEX*)filter->pAudio_Pin->pmt->pbFormat;
424 err = MovieAudioExtractionBegin(filter->pQTMovie, 0, &filter->aSession);
425 if (err != noErr)
427 ERR("Failed to begin Extraction session %i\n",err);
428 return err;
431 err = MovieAudioExtractionGetProperty(filter->aSession,
432 kQTPropertyClass_MovieAudioExtraction_Audio,
433 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
434 sizeof(AudioStreamBasicDescription), &aDesc, NULL);
436 if (err != noErr)
438 MovieAudioExtractionEnd(filter->aSession);
439 filter->aSession = NULL;
440 ERR("Failed to get session description %i\n",err);
441 return err;
444 aDesc.mFormatID = kAudioFormatLinearPCM;
445 aDesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger +
446 kAudioFormatFlagIsPacked;
447 aDesc.mFramesPerPacket = 1;
448 aDesc.mChannelsPerFrame = pvi->nChannels;
449 aDesc.mBitsPerChannel = pvi->wBitsPerSample;
450 aDesc.mSampleRate = pvi->nSamplesPerSec;
451 aDesc.mBytesPerFrame = (aDesc.mBitsPerChannel * aDesc.mChannelsPerFrame) / 8;
452 aDesc.mBytesPerPacket = aDesc.mBytesPerFrame * aDesc.mFramesPerPacket;
454 err = MovieAudioExtractionSetProperty(filter->aSession,
455 kQTPropertyClass_MovieAudioExtraction_Audio,
456 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
457 sizeof(AudioStreamBasicDescription), &aDesc);
459 if (aDesc.mFormatID != kAudioFormatLinearPCM)
461 ERR("Not PCM Wave\n");
462 err = -1;
464 if (aDesc.mFormatFlags != kLinearPCMFormatFlagIsSignedInteger +
465 kAudioFormatFlagIsPacked)
467 ERR("Unhandled Flags\n");
468 err = -1;
470 if (aDesc.mFramesPerPacket != 1)
472 ERR("Unhandled Frames per packet %li\n",aDesc.mFramesPerPacket);
473 err = -1;
475 if (aDesc.mChannelsPerFrame != pvi->nChannels)
477 ERR("Unhandled channel count %li\n",aDesc.mChannelsPerFrame);
478 err = -1;
480 if (aDesc.mBitsPerChannel != pvi->wBitsPerSample)
482 ERR("Unhandled bits per channel %li\n",aDesc.mBitsPerChannel);
483 err = -1;
485 if (aDesc.mSampleRate != pvi->nSamplesPerSec)
487 ERR("Unhandled sample rate %f\n",aDesc.mSampleRate);
488 err = -1;
491 if (err != noErr)
493 ERR("Failed to create Extraction Session\n");
494 MovieAudioExtractionEnd(filter->aSession);
495 filter->aSession = NULL;
498 return err;
501 static DWORD WINAPI QTSplitter_loading_thread(LPVOID data)
503 QTSplitter *This = (QTSplitter *)data;
505 if (This->pAudio_Pin)
507 /* according to QA1469 a movie has to be fully loaded before we
508 can reliably start the Extraction session.
510 If loaded earlier, then we only get an extraction session for
511 the part of the movie that is loaded at that time.
513 We are trying to load as much of the movie as we can before we
514 start extracting. However we can recreate the extraction session
515 again when we run out of loaded extraction frames. But we want
516 to try to minimize that.
519 EnterCriticalSection(&This->csReceive);
520 while(This->pQTMovie && GetMovieLoadState(This->pQTMovie) < kMovieLoadStateComplete)
522 MoviesTask(This->pQTMovie, 100);
523 LeaveCriticalSection(&This->csReceive);
524 Sleep(0);
525 EnterCriticalSection(&This->csReceive);
527 LeaveCriticalSection(&This->csReceive);
529 return 0;
532 static DWORD WINAPI QTSplitter_thread(LPVOID data)
534 QTSplitter *This = (QTSplitter *)data;
535 HRESULT hr = S_OK;
536 TimeValue next_time;
537 CVPixelBufferRef pixelBuffer = NULL;
538 OSStatus err;
539 TimeRecord tr;
541 WaitForSingleObject(This->runEvent, -1);
543 EnterCriticalSection(&This->csReceive);
544 if (!This->pQTMovie)
546 LeaveCriticalSection(&This->csReceive);
547 return 0;
550 This->state = State_Running;
551 /* Prime the pump: Needed for MPEG streams */
552 GetMovieNextInterestingTime(This->pQTMovie, nextTimeEdgeOK | nextTimeStep, 0, NULL, This->movie_time, 1, &next_time, NULL);
554 GetMovieTime(This->pQTMovie, &tr);
556 if (This->pAudio_Pin)
557 QT_Create_Extract_Session(This);
559 LeaveCriticalSection(&This->csReceive);
563 LONGLONG tStart=0, tStop=0;
564 LONGLONG mStart=0, mStop=0;
565 float time;
567 EnterCriticalSection(&This->csReceive);
568 if (!This->pQTMovie)
570 LeaveCriticalSection(&This->csReceive);
571 return 0;
574 GetMovieNextInterestingTime(This->pQTMovie, nextTimeStep, 0, NULL, This->movie_time, 1, &next_time, NULL);
576 if (next_time == -1)
578 TRACE("No next time\n");
579 LeaveCriticalSection(&This->csReceive);
580 break;
583 tr.value = SInt64ToWide(next_time);
584 SetMovieTime(This->pQTMovie, &tr);
585 MoviesTask(This->pQTMovie,0);
586 QTVisualContextTask(This->vContext);
588 TRACE("In loop at time %ld\n",This->movie_time);
589 TRACE("In Next time %ld\n",next_time);
591 mStart = This->movie_time;
592 mStop = next_time;
594 time = (float)(This->movie_time - This->movie_start) / This->movie_scale;
595 tStart = time * 10000000;
596 time = (float)(next_time - This->movie_start) / This->movie_scale;
597 tStop = time * 10000000;
599 /* Deliver Audio */
600 if (This->pAudio_Pin && This->pAudio_Pin->pin.pin.pConnectedTo && This->aSession)
602 int data_size=0;
603 BYTE* ptr;
604 IMediaSample *sample = NULL;
605 AudioBufferList aData;
606 UInt32 flags;
607 UInt32 frames;
608 WAVEFORMATEX* pvi;
609 float duration;
611 pvi = (WAVEFORMATEX*)This->pAudio_Pin->pmt->pbFormat;
613 hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->pAudio_Pin->pin, &sample, NULL, NULL, 0);
615 if (FAILED(hr))
617 ERR("Audio: Unable to get delivery buffer (%x)\n", hr);
618 goto audio_error;
621 hr = IMediaSample_GetPointer(sample, &ptr);
622 if (FAILED(hr))
624 ERR("Audio: Unable to get pointer to buffer (%x)\n", hr);
625 goto audio_error;
628 duration = (float)next_time / This->movie_scale;
629 time = (float)This->movie_time / This->movie_scale;
630 duration -= time;
631 frames = pvi->nSamplesPerSec * duration;
632 TRACE("Need audio for %f seconds (%li frames)\n",duration,frames);
634 data_size = IMediaSample_GetSize(sample);
635 if (data_size < frames * pvi->nBlockAlign)
636 FIXME("Audio buffer is too small\n");
638 aData.mNumberBuffers = 1;
639 aData.mBuffers[0].mNumberChannels = pvi->nChannels;
640 aData.mBuffers[0].mDataByteSize = data_size;
641 aData.mBuffers[0].mData = ptr;
643 err = MovieAudioExtractionFillBuffer(This->aSession, &frames, &aData, &flags);
644 if (frames == 0)
646 TimeRecord etr;
648 /* Ran out of frames, Restart the extraction session */
649 TRACE("Restarting extraction session\n");
650 MovieAudioExtractionEnd(This->aSession);
651 This->aSession = NULL;
652 QT_Create_Extract_Session(This);
654 etr = tr;
655 etr.value = SInt64ToWide(This->movie_time);
656 MovieAudioExtractionSetProperty(This->aSession,
657 kQTPropertyClass_MovieAudioExtraction_Movie,
658 kQTMovieAudioExtractionMoviePropertyID_CurrentTime,
659 sizeof(TimeRecord), &etr );
661 frames = pvi->nSamplesPerSec * duration;
662 aData.mNumberBuffers = 1;
663 aData.mBuffers[0].mNumberChannels = pvi->nChannels;
664 aData.mBuffers[0].mDataByteSize = data_size;
665 aData.mBuffers[0].mData = ptr;
667 MovieAudioExtractionFillBuffer(This->aSession, &frames, &aData, &flags);
670 TRACE("Got %i frames\n",(int)frames);
672 IMediaSample_SetActualDataLength(sample, frames * pvi->nBlockAlign);
674 IMediaSample_SetMediaTime(sample, &mStart, &mStop);
675 IMediaSample_SetTime(sample, &tStart, &tStop);
677 hr = OutputQueue_Receive(This->pAudio_Pin->queue, sample);
678 TRACE("Audio Delivered (%x)\n",hr);
680 audio_error:
681 if (sample)
682 IMediaSample_Release(sample);
684 else
685 TRACE("Audio Pin not connected or no Audio\n");
687 /* Deliver Video */
688 if (This->pVideo_Pin && QTVisualContextIsNewImageAvailable(This->vContext,0))
690 err = QTVisualContextCopyImageForTime(This->vContext, NULL, NULL, &pixelBuffer);
691 if (err == noErr)
693 int data_size=0;
694 BYTE* ptr;
695 IMediaSample *sample = NULL;
697 hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->pVideo_Pin->pin, &sample, NULL, NULL, 0);
698 if (FAILED(hr))
700 ERR("Video: Unable to get delivery buffer (%x)\n", hr);
701 goto video_error;
704 data_size = IMediaSample_GetSize(sample);
705 if (data_size < This->outputSize)
707 ERR("Sample size is too small %d < %d\n", data_size, This->outputSize)
709 hr = E_FAIL;
710 goto video_error;
713 hr = IMediaSample_GetPointer(sample, &ptr);
714 if (FAILED(hr))
716 ERR("Video: Unable to get pointer to buffer (%x)\n", hr);
717 goto video_error;
720 hr = AccessPixelBufferPixels( pixelBuffer, ptr);
721 if (FAILED(hr))
723 ERR("Failed to access Pixels\n");
724 goto video_error;
727 IMediaSample_SetActualDataLength(sample, This->outputSize);
729 IMediaSample_SetMediaTime(sample, &mStart, &mStop);
730 IMediaSample_SetTime(sample, &tStart, &tStop);
732 hr = OutputQueue_Receive(This->pVideo_Pin->queue, sample);
733 TRACE("Video Delivered (%x)\n",hr);
735 video_error:
736 if (sample)
737 IMediaSample_Release(sample);
738 if (pixelBuffer)
739 CVPixelBufferRelease(pixelBuffer);
742 else
743 TRACE("No video to deliver\n");
745 This->movie_time = next_time;
746 LeaveCriticalSection(&This->csReceive);
747 } while (hr == S_OK);
749 This->state = State_Stopped;
750 if (This->pAudio_Pin)
751 OutputQueue_EOS(This->pAudio_Pin->queue);
752 if (This->pVideo_Pin)
753 OutputQueue_EOS(This->pVideo_Pin->queue);
755 return hr;
758 static HRESULT WINAPI QT_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
760 HRESULT hr = S_OK;
761 QTSplitter *This = impl_from_IBaseFilter(iface);
762 HRESULT hr_any = VFW_E_NOT_CONNECTED;
764 TRACE("(%s)\n", wine_dbgstr_longlong(tStart));
766 EnterCriticalSection(&This->csReceive);
767 This->filter.rtStreamStart = tStart;
769 if (This->pVideo_Pin)
770 hr = BaseOutputPinImpl_Active(&This->pVideo_Pin->pin);
771 if (SUCCEEDED(hr))
772 hr_any = hr;
773 if (This->pAudio_Pin)
774 hr = BaseOutputPinImpl_Active(&This->pAudio_Pin->pin);
775 if (SUCCEEDED(hr))
776 hr_any = hr;
778 hr = hr_any;
780 SetEvent(This->runEvent);
781 LeaveCriticalSection(&This->csReceive);
783 return hr;
786 static HRESULT WINAPI QT_GetState(IBaseFilter *iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
788 QTSplitter *This = impl_from_IBaseFilter(iface);
789 TRACE("(%d, %p)\n", dwMilliSecsTimeout, pState);
791 *pState = This->state;
793 return S_OK;
796 static HRESULT WINAPI QT_FindPin(IBaseFilter *iface, LPCWSTR Id, IPin **ppPin)
798 FIXME("(%p)->(%s,%p) stub\n", iface, debugstr_w(Id), ppPin);
799 return E_NOTIMPL;
802 static const IBaseFilterVtbl QT_Vtbl = {
803 QT_QueryInterface,
804 BaseFilterImpl_AddRef,
805 QT_Release,
806 BaseFilterImpl_GetClassID,
807 QT_Stop,
808 QT_Pause,
809 QT_Run,
810 QT_GetState,
811 BaseFilterImpl_SetSyncSource,
812 BaseFilterImpl_GetSyncSource,
813 BaseFilterImpl_EnumPins,
814 QT_FindPin,
815 BaseFilterImpl_QueryFilterInfo,
816 BaseFilterImpl_JoinFilterGraph,
817 BaseFilterImpl_QueryVendorInfo
821 * Input Pin
823 static HRESULT QT_RemoveOutputPins(QTSplitter *This)
825 HRESULT hr;
826 TRACE("(%p)\n", This);
828 if (This->pVideo_Pin)
830 OutputQueue_Destroy(This->pVideo_Pin->queue);
831 hr = BaseOutputPinImpl_BreakConnect(&This->pVideo_Pin->pin);
832 TRACE("Disconnect: %08x\n", hr);
833 IPin_Release(&This->pVideo_Pin->pin.pin.IPin_iface);
834 This->pVideo_Pin = NULL;
836 if (This->pAudio_Pin)
838 OutputQueue_Destroy(This->pAudio_Pin->queue);
839 hr = BaseOutputPinImpl_BreakConnect(&This->pAudio_Pin->pin);
840 TRACE("Disconnect: %08x\n", hr);
841 IPin_Release(&This->pAudio_Pin->pin.pin.IPin_iface);
842 This->pAudio_Pin = NULL;
845 BaseFilterImpl_IncrementPinVersion(&This->filter);
846 return S_OK;
849 static inline QTInPin *impl_from_IPin( IPin *iface )
851 return CONTAINING_RECORD(iface, QTInPin, pin.IPin_iface);
854 static ULONG WINAPI QTInPin_Release(IPin *iface)
856 QTInPin *This = impl_from_IPin(iface);
857 ULONG refCount = InterlockedDecrement(&This->pin.refCount);
859 TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
860 if (!refCount)
862 FreeMediaType(&This->pin.mtCurrent);
863 if (This->pAlloc)
864 IMemAllocator_Release(This->pAlloc);
865 This->pAlloc = NULL;
866 This->pin.IPin_iface.lpVtbl = NULL;
867 return 0;
869 else
870 return refCount;
873 static HRESULT QT_Process_Video_Track(QTSplitter* filter, Track trk)
875 AM_MEDIA_TYPE amt;
876 VIDEOINFOHEADER * pvi;
877 PIN_INFO piOutput;
878 HRESULT hr = S_OK;
879 OSErr err;
880 static const WCHAR szwVideoOut[] = {'V','i','d','e','o',0};
881 CFMutableDictionaryRef pixelBufferOptions = NULL;
882 CFMutableDictionaryRef visualContextOptions = NULL;
883 CFNumberRef n = NULL;
884 int t;
885 DWORD outputWidth, outputHeight, outputDepth;
886 Fixed trackWidth, trackHeight;
887 Media videoMedia;
888 long sampleCount;
889 TimeValue64 duration;
890 TimeScale timeScale;
892 ZeroMemory(&amt, sizeof(amt));
893 amt.formattype = FORMAT_VideoInfo;
894 amt.majortype = MEDIATYPE_Video;
895 amt.subtype = MEDIASUBTYPE_RGB24;
897 GetTrackDimensions(trk, &trackWidth, &trackHeight);
899 outputDepth = 3;
900 outputWidth = Fix2Long(trackWidth);
901 outputHeight = Fix2Long(trackHeight);
902 TRACE("Width %i Height %i\n",outputWidth, outputHeight);
904 amt.cbFormat = sizeof(VIDEOINFOHEADER);
905 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
906 ZeroMemory(amt.pbFormat, amt.cbFormat);
907 pvi = (VIDEOINFOHEADER *)amt.pbFormat;
908 pvi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
909 pvi->bmiHeader.biWidth = outputWidth;
910 pvi->bmiHeader.biHeight = outputHeight;
911 pvi->bmiHeader.biPlanes = 1;
912 pvi->bmiHeader.biBitCount = 24;
913 pvi->bmiHeader.biCompression = BI_RGB;
914 pvi->bmiHeader.biSizeImage = outputWidth * outputHeight * outputDepth;
916 filter->outputSize = pvi->bmiHeader.biSizeImage;
917 amt.lSampleSize = 0;
919 pixelBufferOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
921 t = k32ARGBPixelFormat;
922 n = CFNumberCreate(NULL, kCFNumberIntType, &t);
923 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferPixelFormatTypeKey, n);
924 CFRelease(n);
926 n = CFNumberCreate(NULL, kCFNumberIntType, &outputWidth);
927 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferWidthKey, n);
928 CFRelease(n);
930 n = CFNumberCreate(NULL, kCFNumberIntType, &outputHeight);
931 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferHeightKey, n);
932 CFRelease(n);
934 t = 16;
935 n = CFNumberCreate(NULL, kCFNumberIntType, &t);
936 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferBytesPerRowAlignmentKey, n);
937 CFRelease(n);
939 visualContextOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
941 CFDictionarySetValue(visualContextOptions, kQTVisualContextPixelBufferAttributesKey, pixelBufferOptions);
943 err = QTPixelBufferContextCreate(NULL, visualContextOptions,&filter->vContext);
944 CFRelease(pixelBufferOptions);
945 CFRelease(visualContextOptions);
946 if (err != noErr)
948 ERR("Failed to create Visual Context\n");
949 return E_FAIL;
952 err = SetMovieVisualContext(filter->pQTMovie, filter->vContext);
953 if (err != noErr)
955 ERR("Failed to set Visual Context\n");
956 return E_FAIL;
959 videoMedia = GetTrackMedia(trk);
960 sampleCount = GetMediaSampleCount(videoMedia);
961 timeScale = GetMediaTimeScale(videoMedia);
962 duration = GetMediaDisplayDuration(videoMedia);
963 pvi->AvgTimePerFrame = (100000.0 * sampleCount * timeScale) / duration;
965 piOutput.dir = PINDIR_OUTPUT;
966 piOutput.pFilter = &filter->filter.IBaseFilter_iface;
967 lstrcpyW(piOutput.achName,szwVideoOut);
969 hr = QT_AddPin(filter, &piOutput, &amt, TRUE);
970 if (FAILED(hr))
971 ERR("Failed to add Video Track\n");
972 else
973 TRACE("Video Pin %p\n",filter->pVideo_Pin);
975 return hr;
978 static HRESULT QT_Process_Audio_Track(QTSplitter* filter, Track trk)
980 AM_MEDIA_TYPE amt;
981 WAVEFORMATEX* pvi;
982 PIN_INFO piOutput;
983 HRESULT hr = S_OK;
984 static const WCHAR szwAudioOut[] = {'A','u','d','i','o',0};
985 Media audioMedia;
987 SoundDescriptionHandle aDesc = (SoundDescriptionHandle) NewHandle(sizeof(SoundDescription));
989 audioMedia = GetTrackMedia(trk);
990 GetMediaSampleDescription(audioMedia, 1, (SampleDescriptionHandle)aDesc);
992 ZeroMemory(&amt, sizeof(amt));
993 amt.formattype = FORMAT_WaveFormatEx;
994 amt.majortype = MEDIATYPE_Audio;
995 amt.subtype = MEDIASUBTYPE_PCM;
996 amt.bTemporalCompression = 0;
998 amt.cbFormat = sizeof(WAVEFORMATEX);
999 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
1000 ZeroMemory(amt.pbFormat, amt.cbFormat);
1001 pvi = (WAVEFORMATEX*)amt.pbFormat;
1003 pvi->cbSize = sizeof(WAVEFORMATEX);
1004 pvi->wFormatTag = WAVE_FORMAT_PCM;
1005 pvi->nChannels = ((SoundDescription)**aDesc).numChannels;
1006 if (pvi->nChannels < 1 || pvi->nChannels > 2)
1007 pvi->nChannels = 2;
1008 pvi->nSamplesPerSec = (((SoundDescription)**aDesc).sampleRate/65536);
1009 if (pvi->nSamplesPerSec < 8000 || pvi->nChannels > 48000)
1010 pvi->nSamplesPerSec = 44100;
1011 pvi->wBitsPerSample = ((SoundDescription)**aDesc).sampleSize;
1012 if (pvi->wBitsPerSample < 8 || pvi->wBitsPerSample > 32)
1013 pvi->wBitsPerSample = 16;
1014 pvi->nBlockAlign = (pvi->nChannels * pvi->wBitsPerSample) / 8;
1015 pvi->nAvgBytesPerSec = pvi->nSamplesPerSec * pvi->nBlockAlign;
1017 DisposeHandle((Handle)aDesc);
1019 piOutput.dir = PINDIR_OUTPUT;
1020 piOutput.pFilter = &filter->filter.IBaseFilter_iface;
1021 lstrcpyW(piOutput.achName,szwAudioOut);
1023 hr = QT_AddPin(filter, &piOutput, &amt, FALSE);
1024 if (FAILED(hr))
1025 ERR("Failed to add Audio Track\n");
1026 else
1027 TRACE("Audio Pin %p\n",filter->pAudio_Pin);
1028 return hr;
1031 static HRESULT QT_Process_Movie(QTSplitter* filter)
1033 HRESULT hr = S_OK;
1034 OSErr err;
1035 WineDataRefRecord ptrDataRefRec;
1036 Handle dataRef = NULL;
1037 Track trk;
1038 short id = 0;
1039 DWORD tid;
1040 LONGLONG time;
1042 TRACE("Trying movie connect\n");
1044 ptrDataRefRec.pReader = filter->pInputPin.pReader;
1045 ptrDataRefRec.streamSubtype = filter->pInputPin.subType;
1046 PtrToHand( &ptrDataRefRec, &dataRef, sizeof(WineDataRefRecord));
1048 err = NewMovieFromDataRef(&filter->pQTMovie, newMovieActive|newMovieDontInteractWithUser|newMovieDontAutoUpdateClock|newMovieDontAskUnresolvedDataRefs|newMovieAsyncOK, &id, dataRef, 'WINE');
1050 DisposeHandle(dataRef);
1052 if (err != noErr)
1054 FIXME("QuickTime cannot handle media type(%i)\n",err);
1055 return VFW_E_TYPE_NOT_ACCEPTED;
1058 PrePrerollMovie(filter->pQTMovie, 0, fixed1, NULL, NULL);
1059 PrerollMovie(filter->pQTMovie, 0, fixed1);
1060 GoToBeginningOfMovie(filter->pQTMovie);
1061 SetMovieActive(filter->pQTMovie,TRUE);
1063 if (GetMovieLoadState(filter->pQTMovie) < kMovieLoadStateLoaded)
1064 MoviesTask(filter->pQTMovie,100);
1066 trk = GetMovieIndTrackType(filter->pQTMovie, 1, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
1067 TRACE("%p is a video track\n",trk);
1068 if (trk)
1069 hr = QT_Process_Video_Track(filter, trk);
1071 if (FAILED(hr))
1072 return hr;
1074 trk = GetMovieIndTrackType(filter->pQTMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
1075 TRACE("%p is an audio track\n",trk);
1076 if (trk)
1077 hr = QT_Process_Audio_Track(filter, trk);
1079 time = GetMovieDuration(filter->pQTMovie);
1080 filter->movie_scale = GetMovieTimeScale(filter->pQTMovie);
1081 filter->sourceSeeking.llDuration = ((double)time / filter->movie_scale) * 10000000;
1082 filter->sourceSeeking.llStop = filter->sourceSeeking.llDuration;
1084 TRACE("Movie duration is %s\n",wine_dbgstr_longlong(filter->sourceSeeking.llDuration));
1086 filter->loaderThread = CreateThread(NULL, 0, QTSplitter_loading_thread, filter, 0, &tid);
1087 if (filter->loaderThread)
1088 TRACE("Created loading thread 0x%08x\n", tid);
1089 filter->splitterThread = CreateThread(NULL, 0, QTSplitter_thread, filter, 0, &tid);
1090 if (filter->splitterThread)
1091 TRACE("Created processing thread 0x%08x\n", tid);
1092 else
1093 hr = HRESULT_FROM_WIN32(GetLastError());
1095 return hr;
1098 static HRESULT WINAPI QTInPin_ReceiveConnection(IPin *iface, IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
1100 HRESULT hr = S_OK;
1101 ALLOCATOR_PROPERTIES props;
1102 QTInPin *This = impl_from_IPin(iface);
1104 TRACE("(%p/%p)->(%p, %p)\n", This, iface, pReceivePin, pmt);
1106 EnterCriticalSection(This->pin.pCritSec);
1107 This->pReader = NULL;
1109 if (This->pin.pConnectedTo)
1110 hr = VFW_E_ALREADY_CONNECTED;
1111 else if (IPin_QueryAccept(iface, pmt) != S_OK)
1112 hr = VFW_E_TYPE_NOT_ACCEPTED;
1113 else
1115 PIN_DIRECTION pindirReceive;
1116 IPin_QueryDirection(pReceivePin, &pindirReceive);
1117 if (pindirReceive != PINDIR_OUTPUT)
1118 hr = VFW_E_INVALID_DIRECTION;
1121 if (FAILED(hr))
1123 LeaveCriticalSection(This->pin.pCritSec);
1124 return hr;
1127 hr = IPin_QueryInterface(pReceivePin, &IID_IAsyncReader, (LPVOID *)&This->pReader);
1128 if (FAILED(hr))
1130 LeaveCriticalSection(This->pin.pCritSec);
1131 TRACE("Input source is not an AsyncReader\n");
1132 return hr;
1135 LeaveCriticalSection(This->pin.pCritSec);
1136 EnterCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1137 hr = QT_Process_Movie(impl_from_IBaseFilter(This->pin.pinInfo.pFilter));
1138 if (FAILED(hr))
1140 LeaveCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1141 TRACE("Unable to process movie\n");
1142 return hr;
1145 This->pAlloc = NULL;
1146 props.cBuffers = 8;
1147 props.cbAlign = 1;
1148 props.cbBuffer = impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->outputSize + props.cbAlign;
1149 props.cbPrefix = 0;
1151 hr = IAsyncReader_RequestAllocator(This->pReader, NULL, &props, &This->pAlloc);
1152 if (SUCCEEDED(hr))
1154 CopyMediaType(&This->pin.mtCurrent, pmt);
1155 This->pin.pConnectedTo = pReceivePin;
1156 IPin_AddRef(pReceivePin);
1157 hr = IMemAllocator_Commit(This->pAlloc);
1159 else
1161 QT_RemoveOutputPins(impl_from_IBaseFilter(This->pin.pinInfo.pFilter));
1162 if (This->pReader)
1163 IAsyncReader_Release(This->pReader);
1164 This->pReader = NULL;
1165 if (This->pAlloc)
1166 IMemAllocator_Release(This->pAlloc);
1167 This->pAlloc = NULL;
1169 TRACE("Size: %i\n", props.cbBuffer);
1170 LeaveCriticalSection(&impl_from_IBaseFilter(This->pin.pinInfo.pFilter)->filter.csFilter);
1172 return hr;
1175 static HRESULT WINAPI QTInPin_Disconnect(IPin *iface)
1177 HRESULT hr;
1178 QTInPin *This = impl_from_IPin(iface);
1179 FILTER_STATE state;
1180 TRACE("()\n");
1182 hr = IBaseFilter_GetState(This->pin.pinInfo.pFilter, INFINITE, &state);
1183 EnterCriticalSection(This->pin.pCritSec);
1184 if (This->pin.pConnectedTo)
1186 QTSplitter *Parser = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
1188 if (SUCCEEDED(hr) && state == State_Stopped)
1190 IMemAllocator_Decommit(This->pAlloc);
1191 IPin_Disconnect(This->pin.pConnectedTo);
1192 This->pin.pConnectedTo = NULL;
1193 hr = QT_RemoveOutputPins(Parser);
1195 else
1196 hr = VFW_E_NOT_STOPPED;
1198 else
1199 hr = S_FALSE;
1200 LeaveCriticalSection(This->pin.pCritSec);
1201 return hr;
1204 static HRESULT WINAPI QTInPin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *pmt)
1206 QTInPin *This = impl_from_IPin(iface);
1208 TRACE("(%p)->(%p)\n", This, pmt);
1210 if (IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream))
1212 This->subType = pmt->subtype;
1213 return S_OK;
1215 return S_FALSE;
1218 static HRESULT WINAPI QTInPin_EndOfStream(IPin *iface)
1220 QTInPin *pin = impl_from_IPin(iface);
1221 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1223 FIXME("Propagate message on %p\n", This);
1224 return S_OK;
1227 static HRESULT WINAPI QTInPin_BeginFlush(IPin *iface)
1229 QTInPin *pin = impl_from_IPin(iface);
1230 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1232 FIXME("Propagate message on %p\n", This);
1233 return S_OK;
1236 static HRESULT WINAPI QTInPin_EndFlush(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_NewSegment(IPin *iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
1247 QTInPin *pin = impl_from_IPin(iface);
1248 QTSplitter *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
1250 BasePinImpl_NewSegment(iface, tStart, tStop, dRate);
1251 FIXME("Propagate message on %p\n", This);
1252 return S_OK;
1255 static HRESULT WINAPI QTInPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
1257 QTInPin *This = impl_from_IPin(iface);
1259 TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
1261 *ppv = NULL;
1263 if (IsEqualIID(riid, &IID_IUnknown))
1264 *ppv = iface;
1265 else if (IsEqualIID(riid, &IID_IPin))
1266 *ppv = iface;
1267 else if (IsEqualIID(riid, &IID_IMediaSeeking))
1268 return IBaseFilter_QueryInterface(This->pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
1270 if (*ppv)
1272 IUnknown_AddRef((IUnknown *)(*ppv));
1273 return S_OK;
1276 FIXME("No interface for %s!\n", debugstr_guid(riid));
1278 return E_NOINTERFACE;
1281 static HRESULT WINAPI QTInPin_EnumMediaTypes(IPin *iface, IEnumMediaTypes **ppEnum)
1283 QTInPin *This = impl_from_IPin(iface);
1285 TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
1287 return EnumMediaTypes_Construct(&This->pin, BasePinImpl_GetMediaType, BasePinImpl_GetMediaTypeVersion, ppEnum);
1290 static const IPinVtbl QT_InputPin_Vtbl = {
1291 QTInPin_QueryInterface,
1292 BasePinImpl_AddRef,
1293 QTInPin_Release,
1294 BaseInputPinImpl_Connect,
1295 QTInPin_ReceiveConnection,
1296 QTInPin_Disconnect,
1297 BasePinImpl_ConnectedTo,
1298 BasePinImpl_ConnectionMediaType,
1299 BasePinImpl_QueryPinInfo,
1300 BasePinImpl_QueryDirection,
1301 BasePinImpl_QueryId,
1302 QTInPin_QueryAccept,
1303 QTInPin_EnumMediaTypes,
1304 BasePinImpl_QueryInternalConnections,
1305 QTInPin_EndOfStream,
1306 QTInPin_BeginFlush,
1307 QTInPin_EndFlush,
1308 QTInPin_NewSegment
1312 * Output Pin
1314 static inline QTOutPin *impl_QTOutPin_from_IPin( IPin *iface )
1316 return CONTAINING_RECORD(iface, QTOutPin, pin.pin.IPin_iface);
1319 static inline QTOutPin *impl_QTOutPin_from_BasePin( BasePin *iface )
1321 return CONTAINING_RECORD(iface, QTOutPin, pin.pin);
1324 static inline QTOutPin *impl_QTOutPin_from_BaseOutputPin( BaseOutputPin *iface )
1326 return CONTAINING_RECORD(iface, QTOutPin, pin);
1329 static HRESULT WINAPI QTOutPin_QueryInterface(IPin *iface, REFIID riid, void **ppv)
1331 QTOutPin *This = impl_QTOutPin_from_IPin(iface);
1333 TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
1335 *ppv = NULL;
1337 if (IsEqualIID(riid, &IID_IUnknown))
1338 *ppv = iface;
1339 else if (IsEqualIID(riid, &IID_IPin))
1340 *ppv = iface;
1341 else if (IsEqualIID(riid, &IID_IMediaSeeking))
1342 return IBaseFilter_QueryInterface(This->pin.pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
1343 else if (IsEqualIID(riid, &IID_IQualityControl))
1344 *ppv = &This->IQualityControl_iface;
1346 if (*ppv)
1348 IUnknown_AddRef((IUnknown *)(*ppv));
1349 return S_OK;
1351 FIXME("No interface for %s!\n", debugstr_guid(riid));
1352 return E_NOINTERFACE;
1355 static ULONG WINAPI QTOutPin_Release(IPin *iface)
1357 QTOutPin *This = impl_QTOutPin_from_IPin(iface);
1358 ULONG refCount = InterlockedDecrement(&This->pin.pin.refCount);
1359 TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
1361 if (!refCount)
1363 DeleteMediaType(This->pmt);
1364 FreeMediaType(&This->pin.pin.mtCurrent);
1365 if (This->pin.pAllocator)
1366 IMemAllocator_Release(This->pin.pAllocator);
1367 CoTaskMemFree(This);
1368 return 0;
1370 return refCount;
1373 static HRESULT WINAPI QTOutPin_GetMediaType(BasePin *iface, int iPosition, AM_MEDIA_TYPE *pmt)
1375 QTOutPin *This = impl_QTOutPin_from_BasePin(iface);
1377 if (iPosition < 0)
1378 return E_INVALIDARG;
1379 if (iPosition > 0)
1380 return VFW_S_NO_MORE_ITEMS;
1381 CopyMediaType(pmt, This->pmt);
1382 return S_OK;
1385 static HRESULT WINAPI QTOutPin_DecideBufferSize(BaseOutputPin *iface, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
1387 /* Unused */
1388 return S_OK;
1391 static HRESULT WINAPI QTOutPin_DecideAllocator(BaseOutputPin *iface, IMemInputPin *pPin, IMemAllocator **pAlloc)
1393 HRESULT hr;
1394 QTOutPin *This = impl_QTOutPin_from_BaseOutputPin(iface);
1395 QTSplitter *QTfilter = impl_from_IBaseFilter(This->pin.pin.pinInfo.pFilter);
1397 *pAlloc = NULL;
1398 if (QTfilter->pInputPin.pAlloc)
1400 hr = IMemInputPin_NotifyAllocator(pPin, QTfilter->pInputPin.pAlloc, FALSE);
1401 if (SUCCEEDED(hr))
1403 *pAlloc = QTfilter->pInputPin.pAlloc;
1404 IMemAllocator_AddRef(*pAlloc);
1407 else
1408 hr = VFW_E_NO_ALLOCATOR;
1410 return hr;
1413 static HRESULT WINAPI QTOutPin_BreakConnect(BaseOutputPin *This)
1415 HRESULT hr;
1417 TRACE("(%p)->()\n", This);
1419 EnterCriticalSection(This->pin.pCritSec);
1420 if (!This->pin.pConnectedTo || !This->pMemInputPin)
1421 hr = VFW_E_NOT_CONNECTED;
1422 else
1424 hr = IPin_Disconnect(This->pin.pConnectedTo);
1425 IPin_Disconnect(&This->pin.IPin_iface);
1427 LeaveCriticalSection(This->pin.pCritSec);
1429 return hr;
1432 static const IPinVtbl QT_OutputPin_Vtbl = {
1433 QTOutPin_QueryInterface,
1434 BasePinImpl_AddRef,
1435 QTOutPin_Release,
1436 BaseOutputPinImpl_Connect,
1437 BaseOutputPinImpl_ReceiveConnection,
1438 BaseOutputPinImpl_Disconnect,
1439 BasePinImpl_ConnectedTo,
1440 BasePinImpl_ConnectionMediaType,
1441 BasePinImpl_QueryPinInfo,
1442 BasePinImpl_QueryDirection,
1443 BasePinImpl_QueryId,
1444 BasePinImpl_QueryAccept,
1445 BasePinImpl_EnumMediaTypes,
1446 BasePinImpl_QueryInternalConnections,
1447 BaseOutputPinImpl_EndOfStream,
1448 BaseOutputPinImpl_BeginFlush,
1449 BaseOutputPinImpl_EndFlush,
1450 BasePinImpl_NewSegment
1453 static inline QTOutPin *impl_from_IQualityControl( IQualityControl *iface )
1455 return CONTAINING_RECORD(iface, QTOutPin, IQualityControl_iface);
1458 HRESULT WINAPI QT_QualityControl_QueryInterface(IQualityControl *iface, REFIID riid, void **ppv)
1460 QTOutPin *This = impl_from_IQualityControl(iface);
1461 return IPin_QueryInterface(&This->pin.pin.IPin_iface, riid, ppv);
1464 ULONG WINAPI QT_QualityControl_AddRef(IQualityControl *iface)
1466 QTOutPin *This = impl_from_IQualityControl(iface);
1467 return IPin_AddRef(&This->pin.pin.IPin_iface);
1470 ULONG WINAPI QT_QualityControl_Release(IQualityControl *iface)
1472 QTOutPin *This = impl_from_IQualityControl(iface);
1473 return IPin_Release(&This->pin.pin.IPin_iface);
1476 static HRESULT WINAPI QT_QualityControl_Notify(IQualityControl *iface, IBaseFilter *sender, Quality qm)
1478 REFERENCE_TIME late = qm.Late;
1479 if (qm.Late < 0 && -qm.Late > qm.TimeStamp)
1480 late = -qm.TimeStamp;
1481 /* TODO: Do Something */
1482 return S_OK;
1485 HRESULT WINAPI QT_QualityControl_SetSink(IQualityControl *iface, IQualityControl *tonotify)
1487 /* Do nothing */
1488 return S_OK;
1491 static const IQualityControlVtbl QTOutPin_QualityControl_Vtbl = {
1492 QT_QualityControl_QueryInterface,
1493 QT_QualityControl_AddRef,
1494 QT_QualityControl_Release,
1495 QT_QualityControl_Notify,
1496 QT_QualityControl_SetSink
1499 static const BaseOutputPinFuncTable output_BaseOutputFuncTable = {
1501 NULL,
1502 BaseOutputPinImpl_AttemptConnection,
1503 BasePinImpl_GetMediaTypeVersion,
1504 QTOutPin_GetMediaType
1506 QTOutPin_DecideBufferSize,
1507 QTOutPin_DecideAllocator,
1508 QTOutPin_BreakConnect
1511 static const OutputQueueFuncTable output_OutputQueueFuncTable = {
1512 OutputQueueImpl_ThreadProc
1515 static HRESULT QT_AddPin(QTSplitter *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt, BOOL video)
1517 HRESULT hr;
1518 IPin **target;
1520 if (video)
1521 target = (IPin**)&This->pVideo_Pin;
1522 else
1523 target = (IPin**)&This->pAudio_Pin;
1525 if (*target != NULL)
1527 FIXME("We already have a %s pin\n",(video)?"video":"audio");
1528 return E_FAIL;
1531 hr = BaseOutputPin_Construct(&QT_OutputPin_Vtbl, sizeof(QTOutPin), piOutput, &output_BaseOutputFuncTable, &This->filter.csFilter, (IPin**)target);
1532 if (SUCCEEDED(hr))
1534 QTOutPin *pin = (QTOutPin*)*target;
1535 pin->pmt = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
1536 CopyMediaType(pin->pmt, amt);
1537 pin->pin.pin.pinInfo.pFilter = (LPVOID)This;
1538 pin->IQualityControl_iface.lpVtbl = &QTOutPin_QualityControl_Vtbl;
1540 BaseFilterImpl_IncrementPinVersion(&This->filter);
1542 hr = OutputQueue_Construct(&pin->pin, TRUE, TRUE, 5, FALSE, THREAD_PRIORITY_NORMAL, &output_OutputQueueFuncTable, &pin->queue);
1544 else
1545 ERR("Failed with error %x\n", hr);
1546 return hr;
1549 static HRESULT WINAPI QTSplitter_ChangeStart(IMediaSeeking *iface)
1551 QTSplitter *This = impl_from_IMediaSeeking(iface);
1552 TRACE("(%p)\n", iface);
1553 EnterCriticalSection(&This->csReceive);
1554 This->movie_time = (This->sourceSeeking.llCurrent * This->movie_scale)/10000000;
1555 This->movie_start = This->movie_time;
1556 LeaveCriticalSection(&This->csReceive);
1557 return S_OK;
1560 static HRESULT WINAPI QTSplitter_ChangeStop(IMediaSeeking *iface)
1562 FIXME("(%p) filter hasn't implemented stop position change!\n", iface);
1563 return S_OK;
1566 static HRESULT WINAPI QTSplitter_ChangeRate(IMediaSeeking *iface)
1568 FIXME("(%p) filter hasn't implemented rate change!\n", iface);
1569 return S_OK;
1572 static HRESULT WINAPI QT_Seeking_QueryInterface(IMediaSeeking * iface, REFIID riid, LPVOID * ppv)
1574 QTSplitter *This = impl_from_IMediaSeeking(iface);
1576 return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv);
1579 static ULONG WINAPI QT_Seeking_AddRef(IMediaSeeking * iface)
1581 QTSplitter *This = impl_from_IMediaSeeking(iface);
1583 return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
1586 static ULONG WINAPI QT_Seeking_Release(IMediaSeeking * iface)
1588 QTSplitter *This = impl_from_IMediaSeeking(iface);
1590 return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
1593 static const IMediaSeekingVtbl QT_Seeking_Vtbl =
1595 QT_Seeking_QueryInterface,
1596 QT_Seeking_AddRef,
1597 QT_Seeking_Release,
1598 SourceSeekingImpl_GetCapabilities,
1599 SourceSeekingImpl_CheckCapabilities,
1600 SourceSeekingImpl_IsFormatSupported,
1601 SourceSeekingImpl_QueryPreferredFormat,
1602 SourceSeekingImpl_GetTimeFormat,
1603 SourceSeekingImpl_IsUsingTimeFormat,
1604 SourceSeekingImpl_SetTimeFormat,
1605 SourceSeekingImpl_GetDuration,
1606 SourceSeekingImpl_GetStopPosition,
1607 SourceSeekingImpl_GetCurrentPosition,
1608 SourceSeekingImpl_ConvertTimeFormat,
1609 SourceSeekingImpl_SetPositions,
1610 SourceSeekingImpl_GetPositions,
1611 SourceSeekingImpl_GetAvailable,
1612 SourceSeekingImpl_SetRate,
1613 SourceSeekingImpl_GetRate,
1614 SourceSeekingImpl_GetPreroll