comctl32: Store style bits provided with WM_CREATE.
[wine/multimedia.git] / dlls / wineqtdecoder / qtsplitter.c
blob06709c3762760b2e315f1654c8488bd325d2b589
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 <assert.h>
125 #include "wine/unicode.h"
126 #include "wine/debug.h"
127 #include "wine/strmbase.h"
129 #include "qtprivate.h"
131 WINE_DEFAULT_DEBUG_CHANNEL(qtsplitter);
132 extern CLSID CLSID_QTSplitter;
134 typedef struct QTOutPin {
135 BaseOutputPin pin;
137 AM_MEDIA_TYPE * pmt;
138 OutputQueue * queue;
139 } QTOutPin;
141 typedef struct QTInPin {
142 BasePin pin;
143 GUID subType;
145 IAsyncReader *pReader;
146 IMemAllocator *pAlloc;
147 } QTInPin;
149 typedef struct QTSplitter {
150 BaseFilter filter;
152 QTInPin pInputPin;
153 QTOutPin *pVideo_Pin;
154 QTOutPin *pAudio_Pin;
156 ALLOCATOR_PROPERTIES props;
158 Movie pQTMovie;
159 QTVisualContextRef vContext;
161 MovieAudioExtractionRef aSession;
162 HANDLE runEvent;
164 DWORD outputSize;
165 FILTER_STATE state;
166 } QTSplitter;
168 static const IPinVtbl QT_OutputPin_Vtbl;
169 static const IPinVtbl QT_InputPin_Vtbl;
170 static const IBaseFilterVtbl QT_Vtbl;
172 static HRESULT QT_AddPin(QTSplitter *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt, BOOL video);
173 static HRESULT QT_RemoveOutputPins(QTSplitter *This);
176 * Base Filter
179 static IPin* WINAPI QT_GetPin(BaseFilter *iface, int pos)
181 QTSplitter *This = (QTSplitter *)iface;
182 TRACE("Asking for pos %x\n", pos);
184 if (pos > 2 || pos < 0)
185 return NULL;
186 switch (pos)
188 case 0:
189 IPin_AddRef((IPin*)&This->pInputPin);
190 return (IPin*)&This->pInputPin;
191 case 1:
192 if (This->pVideo_Pin)
193 IPin_AddRef((IPin*)This->pVideo_Pin);
194 return (IPin*)This->pVideo_Pin;
195 case 2:
196 if (This->pAudio_Pin)
197 IPin_AddRef((IPin*)This->pAudio_Pin);
198 return (IPin*)This->pAudio_Pin;
199 default:
200 return NULL;
204 static LONG WINAPI QT_GetPinCount(BaseFilter *iface)
206 QTSplitter *This = (QTSplitter *)iface;
207 int c = 1;
208 if (This->pAudio_Pin) c++;
209 if (This->pVideo_Pin) c++;
210 return c;
213 static const BaseFilterFuncTable BaseFuncTable = {
214 QT_GetPin,
215 QT_GetPinCount
218 IUnknown * CALLBACK QTSplitter_create(IUnknown *punkout, HRESULT *phr)
220 IUnknown *obj = NULL;
221 PIN_INFO *piInput;
222 QTSplitter *This;
223 static const WCHAR wcsInputPinName[] = {'I','n','p','u','t',' ','P','i','n',0};
225 EnterMovies();
227 RegisterWineDataHandler();
229 This = CoTaskMemAlloc(sizeof(*This));
230 obj = (IUnknown*)This;
231 if (!This)
233 *phr = E_OUTOFMEMORY;
234 return NULL;
237 BaseFilter_Init(&This->filter, &QT_Vtbl, &CLSID_QTSplitter, (DWORD_PTR)(__FILE__ ": QTSplitter.csFilter"), &BaseFuncTable);
239 This->pVideo_Pin = NULL;
240 This->pAudio_Pin = NULL;
241 This->state = State_Stopped;
242 This->aSession = NULL;
243 This->runEvent = CreateEventW(NULL, 0, 0, NULL);
245 piInput = &This->pInputPin.pin.pinInfo;
246 piInput->dir = PINDIR_INPUT;
247 piInput->pFilter = (IBaseFilter *)This;
248 lstrcpynW(piInput->achName, wcsInputPinName, sizeof(piInput->achName) / sizeof(piInput->achName[0]));
249 This->pInputPin.pin.lpVtbl = &QT_InputPin_Vtbl;
250 This->pInputPin.pin.refCount = 1;
251 This->pInputPin.pin.pConnectedTo = NULL;
252 This->pInputPin.pin.pCritSec = &This->filter.csFilter;
253 ZeroMemory(&This->pInputPin.pin.mtCurrent, sizeof(AM_MEDIA_TYPE));
254 *phr = S_OK;
255 return obj;
258 static void QT_Destroy(QTSplitter *This)
260 IPin *connected = NULL;
261 ULONG pinref;
263 TRACE("Destroying\n");
265 /* Don't need to clean up output pins, disconnecting input pin will do that */
266 IPin_ConnectedTo((IPin *)&This->pInputPin, &connected);
267 if (connected)
269 IPin_Disconnect(connected);
270 IPin_Release(connected);
272 pinref = IPin_Release((IPin *)&This->pInputPin);
273 if (pinref)
275 ERR("pinref should be null, is %u, destroying anyway\n", pinref);
276 assert((LONG)pinref > 0);
278 while (pinref)
279 pinref = IPin_Release((IPin *)&This->pInputPin);
282 if (This->pQTMovie)
283 DisposeMovie(This->pQTMovie);
284 if (This->vContext)
285 QTVisualContextRelease(This->vContext);
286 if (This->aSession)
287 MovieAudioExtractionEnd(This->aSession);
288 CloseHandle(This->runEvent);
290 ExitMovies();
291 CoTaskMemFree(This);
294 static HRESULT WINAPI QT_QueryInterface(IBaseFilter *iface, REFIID riid, LPVOID *ppv)
296 QTSplitter *This = (QTSplitter *)iface;
297 TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
299 *ppv = NULL;
301 if (IsEqualIID(riid, &IID_IUnknown))
302 *ppv = This;
303 else if (IsEqualIID(riid, &IID_IPersist))
304 *ppv = This;
305 else if (IsEqualIID(riid, &IID_IMediaFilter))
306 *ppv = This;
307 else if (IsEqualIID(riid, &IID_IBaseFilter))
308 *ppv = This;
310 if (*ppv)
312 IUnknown_AddRef((IUnknown *)(*ppv));
313 return S_OK;
316 if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow))
317 FIXME("No interface for %s!\n", debugstr_guid(riid));
319 return E_NOINTERFACE;
322 static ULONG WINAPI QT_Release(IBaseFilter *iface)
324 QTSplitter *This = (QTSplitter *)iface;
325 ULONG refCount = BaseFilterImpl_Release(iface);
327 TRACE("(%p)->() Release from %d\n", This, refCount + 1);
329 if (!refCount)
330 QT_Destroy(This);
332 return refCount;
335 static HRESULT WINAPI QT_Stop(IBaseFilter *iface)
337 QTSplitter *This = (QTSplitter *)iface;
339 TRACE("()\n");
341 IAsyncReader_BeginFlush(This->pInputPin.pReader);
342 IAsyncReader_EndFlush(This->pInputPin.pReader);
344 return S_OK;
347 static HRESULT WINAPI QT_Pause(IBaseFilter *iface)
349 HRESULT hr = S_OK;
350 TRACE("()\n");
352 return hr;
355 static OSErr QT_Create_Extract_Session(QTSplitter *filter)
357 AudioStreamBasicDescription aDesc;
358 OSErr err;
359 WAVEFORMATEX* pvi;
361 pvi = (WAVEFORMATEX*)filter->pAudio_Pin->pmt->pbFormat;
363 err = MovieAudioExtractionBegin(filter->pQTMovie, 0, &filter->aSession);
364 if (err != noErr)
366 ERR("Failed to begin Extraction session %i\n",err);
367 return err;
370 err = MovieAudioExtractionGetProperty(filter->aSession,
371 kQTPropertyClass_MovieAudioExtraction_Audio,
372 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
373 sizeof(AudioStreamBasicDescription), &aDesc, NULL);
375 if (err != noErr)
377 MovieAudioExtractionEnd(filter->aSession);
378 filter->aSession = NULL;
379 ERR("Failed to get session description %i\n",err);
380 return err;
383 aDesc.mFormatID = kAudioFormatLinearPCM;
384 aDesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger +
385 kAudioFormatFlagIsPacked;
386 aDesc.mFramesPerPacket = 1;
387 aDesc.mChannelsPerFrame = pvi->nChannels;
388 aDesc.mBitsPerChannel = pvi->wBitsPerSample;
389 aDesc.mSampleRate = pvi->nSamplesPerSec;
390 aDesc.mBytesPerFrame = (aDesc.mBitsPerChannel * aDesc.mChannelsPerFrame) / 8;
391 aDesc.mBytesPerPacket = aDesc.mBytesPerFrame * aDesc.mFramesPerPacket;
393 err = MovieAudioExtractionSetProperty(filter->aSession,
394 kQTPropertyClass_MovieAudioExtraction_Audio,
395 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
396 sizeof(AudioStreamBasicDescription), &aDesc);
398 if (aDesc.mFormatID != kAudioFormatLinearPCM)
400 ERR("Not PCM Wave\n");
401 err = -1;
403 if (aDesc.mFormatFlags != kLinearPCMFormatFlagIsSignedInteger +
404 kAudioFormatFlagIsPacked)
406 ERR("Unhandled Flags\n");
407 err = -1;
409 if (aDesc.mFramesPerPacket != 1)
411 ERR("Unhandled Frames per packet %li\n",aDesc.mFramesPerPacket);
412 err = -1;
414 if (aDesc.mChannelsPerFrame != pvi->nChannels)
416 ERR("Unhandled channel count %li\n",aDesc.mChannelsPerFrame);
417 err = -1;
419 if (aDesc.mBitsPerChannel != pvi->wBitsPerSample)
421 ERR("Unhandled bits per channel %li\n",aDesc.mBitsPerChannel);
422 err = -1;
424 if (aDesc.mSampleRate != pvi->nSamplesPerSec)
426 ERR("Unhandled sample rate %f\n",aDesc.mSampleRate);
427 err = -1;
430 if (err != noErr)
432 ERR("Failed to create Extraction Session\n");
433 MovieAudioExtractionEnd(filter->aSession);
434 filter->aSession = NULL;
437 return err;
440 static DWORD WINAPI QTSplitter_thread(LPVOID data)
442 QTSplitter *This = (QTSplitter *)data;
443 HRESULT hr = S_OK;
444 TimeValue movie_time=0, next_time;
445 CVPixelBufferRef pixelBuffer = NULL;
446 OSStatus err;
447 TimeRecord tr;
449 if (This->pAudio_Pin)
451 /* according to QA1469 a movie has to be fully loaded before we
452 can reliably start the Extraction session */
454 while(GetMovieLoadState(This->pQTMovie) < kMovieLoadStateComplete)
455 MoviesTask(This->pQTMovie,1000);
457 QT_Create_Extract_Session(This);
460 WaitForSingleObject(This->runEvent, -1);
462 This->state = State_Running;
463 /* Prime the pump: Needed for MPEG streams */
464 GetMovieNextInterestingTime(This->pQTMovie, nextTimeEdgeOK | nextTimeStep, 0, NULL, movie_time, 1, &next_time, NULL);
466 GetMovieTime(This->pQTMovie, &tr);
469 LONGLONG tStart=0, tStop=0;
470 float time;
472 GetMovieNextInterestingTime(This->pQTMovie, nextTimeStep, 0, NULL, movie_time, 1, &next_time, NULL);
474 if (next_time == -1)
476 TRACE("No next time\n");
477 break;
480 tr.value = SInt64ToWide(next_time);
481 SetMovieTime(This->pQTMovie, &tr);
482 MoviesTask(This->pQTMovie,0);
483 QTVisualContextTask(This->vContext);
485 TRACE("In loop at time %ld\n",movie_time);
486 TRACE("In Next time %ld\n",next_time);
488 time = (float)movie_time / tr.scale;
489 tStart = time * 10000000;
490 time = (float)next_time / tr.scale;
491 tStop = time * 10000000;
493 /* Deliver Audio */
494 if (This->pAudio_Pin && ((BaseOutputPin*)This->pAudio_Pin)->pin.pConnectedTo && This->aSession)
496 int data_size=0;
497 BYTE* ptr;
498 IMediaSample *sample = NULL;
499 AudioBufferList aData;
500 UInt32 flags;
501 UInt32 frames;
502 WAVEFORMATEX* pvi;
503 float duration;
505 pvi = (WAVEFORMATEX*)This->pAudio_Pin->pmt->pbFormat;
507 hr = BaseOutputPinImpl_GetDeliveryBuffer((BaseOutputPin*)This->pAudio_Pin, &sample, NULL, NULL, 0);
509 if (FAILED(hr))
511 ERR("Audio: Unable to get delivery buffer (%x)\n", hr);
512 goto audio_error;
515 hr = IMediaSample_GetPointer(sample, &ptr);
516 if (FAILED(hr))
518 ERR("Audio: Unable to get pointer to buffer (%x)\n", hr);
519 goto audio_error;
522 duration = (float)next_time / tr.scale;
523 time = (float)movie_time / tr.scale;
524 duration -= time;
525 frames = pvi->nSamplesPerSec * duration;
526 TRACE("Need audio for %f seconds (%li frames)\n",duration,frames);
528 data_size = IMediaSample_GetSize(sample);
529 if (data_size < frames * pvi->nBlockAlign)
530 FIXME("Audio buffer is too small\n");
532 aData.mNumberBuffers = 1;
533 aData.mBuffers[0].mNumberChannels = pvi->nChannels;
534 aData.mBuffers[0].mDataByteSize = data_size;
535 aData.mBuffers[0].mData = ptr;
537 err = MovieAudioExtractionFillBuffer(This->aSession, &frames, &aData, &flags);
538 TRACE("Got %i frames\n",(int)frames);
540 IMediaSample_SetActualDataLength(sample, frames * pvi->nBlockAlign);
542 IMediaSample_SetMediaTime(sample, &tStart, &tStop);
543 if (tStart)
544 IMediaSample_SetTime(sample, &tStart, &tStop);
545 else
546 IMediaSample_SetTime(sample, NULL, NULL);
548 hr = OutputQueue_Receive(This->pAudio_Pin->queue, sample);
549 TRACE("Audio Delivered (%x)\n",hr);
551 audio_error:
552 if (sample)
553 IMediaSample_Release(sample);
555 else
556 TRACE("Audio Pin not connected or no Audio\n");
558 /* Deliver Video */
559 if (This->pVideo_Pin && QTVisualContextIsNewImageAvailable(This->vContext,0))
561 err = QTVisualContextCopyImageForTime(This->vContext, NULL, NULL, &pixelBuffer);
562 if (err == noErr)
564 int data_size=0;
565 BYTE* ptr;
566 IMediaSample *sample = NULL;
568 hr = BaseOutputPinImpl_GetDeliveryBuffer((BaseOutputPin*)This->pVideo_Pin, &sample, NULL, NULL, 0);
569 if (FAILED(hr))
571 ERR("Video: Unable to get delivery buffer (%x)\n", hr);
572 goto video_error;
575 data_size = IMediaSample_GetSize(sample);
576 if (data_size < This->outputSize)
578 ERR("Sample size is too small %d < %d\n", data_size, This->outputSize)
580 hr = E_FAIL;
581 goto video_error;
584 hr = IMediaSample_GetPointer(sample, &ptr);
585 if (FAILED(hr))
587 ERR("Video: Unable to get pointer to buffer (%x)\n", hr);
588 goto video_error;
591 hr = AccessPixelBufferPixels( pixelBuffer, ptr);
592 if (FAILED(hr))
594 ERR("Failed to access Pixels\n");
595 goto video_error;
598 IMediaSample_SetActualDataLength(sample, This->outputSize);
600 IMediaSample_SetMediaTime(sample, &tStart, &tStop);
601 if (tStart)
602 IMediaSample_SetTime(sample, &tStart, &tStop);
603 else
604 IMediaSample_SetTime(sample, NULL, NULL);
606 hr = OutputQueue_Receive(This->pVideo_Pin->queue, sample);
607 TRACE("Video Delivered (%x)\n",hr);
609 video_error:
610 if (sample)
611 IMediaSample_Release(sample);
612 if (pixelBuffer)
613 CVPixelBufferRelease(pixelBuffer);
616 else
617 TRACE("No video to deliver\n");
619 movie_time = next_time;
620 } while (hr == S_OK);
622 This->state = State_Stopped;
623 if (This->pAudio_Pin)
624 OutputQueue_EOS(This->pAudio_Pin->queue);
625 if (This->pVideo_Pin)
626 OutputQueue_EOS(This->pVideo_Pin->queue);
628 return hr;
631 static HRESULT WINAPI QT_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
633 HRESULT hr = S_OK;
634 QTSplitter *This = (QTSplitter *)iface;
635 HRESULT hr_any = VFW_E_NOT_CONNECTED;
637 TRACE("(%s)\n", wine_dbgstr_longlong(tStart));
639 EnterCriticalSection(&This->filter.csFilter);
640 This->filter.rtStreamStart = tStart;
642 if (This->pVideo_Pin)
643 hr = BaseOutputPinImpl_Active((BaseOutputPin *)This->pVideo_Pin);
644 if (SUCCEEDED(hr))
645 hr_any = hr;
646 if (This->pAudio_Pin)
647 hr = BaseOutputPinImpl_Active((BaseOutputPin *)This->pAudio_Pin);
648 if (SUCCEEDED(hr))
649 hr_any = hr;
651 hr = hr_any;
652 LeaveCriticalSection(&This->filter.csFilter);
654 SetEvent(This->runEvent);
656 return hr;
659 static HRESULT WINAPI QT_GetState(IBaseFilter *iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
661 QTSplitter *This = (QTSplitter *)iface;
662 TRACE("(%d, %p)\n", dwMilliSecsTimeout, pState);
664 *pState = This->state;
666 return S_OK;
669 static HRESULT WINAPI QT_FindPin(IBaseFilter *iface, LPCWSTR Id, IPin **ppPin)
671 FIXME("(%p)->(%s,%p) stub\n", iface, debugstr_w(Id), ppPin);
672 return E_NOTIMPL;
675 static const IBaseFilterVtbl QT_Vtbl = {
676 QT_QueryInterface,
677 BaseFilterImpl_AddRef,
678 QT_Release,
679 BaseFilterImpl_GetClassID,
680 QT_Stop,
681 QT_Pause,
682 QT_Run,
683 QT_GetState,
684 BaseFilterImpl_SetSyncSource,
685 BaseFilterImpl_GetSyncSource,
686 BaseFilterImpl_EnumPins,
687 QT_FindPin,
688 BaseFilterImpl_QueryFilterInfo,
689 BaseFilterImpl_JoinFilterGraph,
690 BaseFilterImpl_QueryVendorInfo
694 * Input Pin
696 static HRESULT QT_RemoveOutputPins(QTSplitter *This)
698 HRESULT hr;
699 TRACE("(%p)\n", This);
701 if (This->pVideo_Pin)
703 hr = BaseOutputPinImpl_BreakConnect(&This->pVideo_Pin->pin);
704 TRACE("Disconnect: %08x\n", hr);
705 IPin_Release((IPin*)This->pVideo_Pin);
706 This->pVideo_Pin = NULL;
708 if (This->pAudio_Pin)
710 hr = BaseOutputPinImpl_BreakConnect(&This->pAudio_Pin->pin);
711 TRACE("Disconnect: %08x\n", hr);
712 IPin_Release((IPin*)This->pAudio_Pin);
713 This->pAudio_Pin = NULL;
716 BaseFilterImpl_IncrementPinVersion((BaseFilter*)This);
717 return S_OK;
720 static ULONG WINAPI QTInPin_Release(IPin *iface)
722 QTInPin *This = (QTInPin*)iface;
723 ULONG refCount = InterlockedDecrement(&This->pin.refCount);
725 TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
726 if (!refCount)
728 FreeMediaType(&This->pin.mtCurrent);
729 if (This->pAlloc)
730 IMemAllocator_Release(This->pAlloc);
731 This->pAlloc = NULL;
732 This->pin.lpVtbl = NULL;
733 return 0;
735 else
736 return refCount;
739 static HRESULT QT_Process_Video_Track(QTSplitter* filter, Track trk)
741 AM_MEDIA_TYPE amt;
742 VIDEOINFOHEADER * pvi;
743 PIN_INFO piOutput;
744 HRESULT hr = S_OK;
745 OSErr err;
746 static const WCHAR szwVideoOut[] = {'V','i','d','e','o',0};
747 CFMutableDictionaryRef pixelBufferOptions = NULL;
748 CFMutableDictionaryRef visualContextOptions = NULL;
749 CFNumberRef n = NULL;
750 int t;
751 DWORD outputWidth, outputHeight, outputDepth;
752 Fixed trackWidth, trackHeight;
754 ZeroMemory(&amt, sizeof(amt));
755 amt.formattype = FORMAT_VideoInfo;
756 amt.majortype = MEDIATYPE_Video;
757 amt.subtype = MEDIASUBTYPE_RGB24;
759 GetTrackDimensions(trk, &trackWidth, &trackHeight);
761 outputDepth = 3;
762 outputWidth = Fix2Long(trackWidth);
763 outputHeight = Fix2Long(trackHeight);
764 TRACE("Width %i Height %i\n",outputWidth, outputHeight);
766 amt.cbFormat = sizeof(VIDEOINFOHEADER);
767 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
768 ZeroMemory(amt.pbFormat, amt.cbFormat);
769 pvi = (VIDEOINFOHEADER *)amt.pbFormat;
770 pvi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
771 pvi->bmiHeader.biWidth = outputWidth;
772 pvi->bmiHeader.biHeight = -outputHeight;
773 pvi->bmiHeader.biPlanes = 1;
774 pvi->bmiHeader.biBitCount = 24;
775 pvi->bmiHeader.biCompression = BI_RGB;
777 filter->outputSize = outputWidth * outputHeight * outputDepth;
778 amt.lSampleSize = 0;
780 pixelBufferOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
782 t = k32ARGBPixelFormat;
783 n = CFNumberCreate(NULL, kCFNumberIntType, &t);
784 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferPixelFormatTypeKey, n);
785 CFRelease(n);
787 n = CFNumberCreate(NULL, kCFNumberIntType, &outputWidth);
788 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferWidthKey, n);
789 CFRelease(n);
791 n = CFNumberCreate(NULL, kCFNumberIntType, &outputHeight);
792 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferHeightKey, n);
793 CFRelease(n);
795 t = 16;
796 n = CFNumberCreate(NULL, kCFNumberIntType, &t);
797 CFDictionaryAddValue(pixelBufferOptions, kCVPixelBufferBytesPerRowAlignmentKey, n);
798 CFRelease(n);
800 visualContextOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
802 CFDictionarySetValue(visualContextOptions, kQTVisualContextPixelBufferAttributesKey, pixelBufferOptions);
804 err = QTPixelBufferContextCreate(NULL, visualContextOptions,&filter->vContext);
805 CFRelease(pixelBufferOptions);
806 CFRelease(visualContextOptions);
807 if (err != noErr)
809 ERR("Failed to create Visual Context\n");
810 return E_FAIL;
813 err = SetMovieVisualContext(filter->pQTMovie, filter->vContext);
814 if (err != noErr)
816 ERR("Failed to set Visual Context\n");
817 return E_FAIL;
820 piOutput.dir = PINDIR_OUTPUT;
821 piOutput.pFilter = (IBaseFilter *)filter;
822 lstrcpyW(piOutput.achName,szwVideoOut);
824 hr = QT_AddPin(filter, &piOutput, &amt, TRUE);
825 if (FAILED(hr))
826 ERR("Failed to add Video Track\n");
827 else
828 TRACE("Video Pin %p\n",filter->pVideo_Pin);
830 return hr;
833 static HRESULT QT_Process_Audio_Track(QTSplitter* filter, Track trk)
835 AM_MEDIA_TYPE amt;
836 WAVEFORMATEX* pvi;
837 PIN_INFO piOutput;
838 HRESULT hr = S_OK;
839 static const WCHAR szwAudioOut[] = {'A','u','d','i','o',0};
840 Media audioMedia;
842 SoundDescriptionHandle aDesc = (SoundDescriptionHandle) NewHandle(sizeof(SoundDescription));
844 audioMedia = GetTrackMedia(trk);
845 GetMediaSampleDescription(audioMedia, 1, (SampleDescriptionHandle)aDesc);
847 ZeroMemory(&amt, sizeof(amt));
848 amt.formattype = FORMAT_WaveFormatEx;
849 amt.majortype = MEDIATYPE_Audio;
850 amt.subtype = MEDIASUBTYPE_PCM;
851 amt.bTemporalCompression = 0;
853 amt.cbFormat = sizeof(WAVEFORMATEX);
854 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
855 ZeroMemory(amt.pbFormat, amt.cbFormat);
856 pvi = (WAVEFORMATEX*)amt.pbFormat;
858 pvi->cbSize = sizeof(WAVEFORMATEX);
859 pvi->wFormatTag = WAVE_FORMAT_PCM;
860 pvi->nChannels = ((SoundDescription)**aDesc).numChannels;
861 if (pvi->nChannels < 1 || pvi->nChannels > 2)
862 pvi->nChannels = 2;
863 pvi->nSamplesPerSec = (((SoundDescription)**aDesc).sampleRate/65536);
864 if (pvi->nSamplesPerSec < 8000 || pvi->nChannels > 48000)
865 pvi->nSamplesPerSec = 44100;
866 pvi->wBitsPerSample = ((SoundDescription)**aDesc).sampleSize;
867 if (pvi->wBitsPerSample < 8 || pvi->wBitsPerSample > 32)
868 pvi->wBitsPerSample = 16;
869 pvi->nBlockAlign = (pvi->nChannels * pvi->wBitsPerSample) / 8;
870 pvi->nAvgBytesPerSec = pvi->nSamplesPerSec * pvi->nBlockAlign;
872 DisposeHandle((Handle)aDesc);
874 piOutput.dir = PINDIR_OUTPUT;
875 piOutput.pFilter = (IBaseFilter *)filter;
876 lstrcpyW(piOutput.achName,szwAudioOut);
878 hr = QT_AddPin(filter, &piOutput, &amt, FALSE);
879 if (FAILED(hr))
880 ERR("Failed to add Audio Track\n");
881 else
882 TRACE("Audio Pin %p\n",filter->pAudio_Pin);
883 return hr;
886 static HRESULT QT_Process_Movie(QTSplitter* filter)
888 HRESULT hr = S_OK;
889 OSErr err;
890 WineDataRefRecord ptrDataRefRec;
891 Handle dataRef = NULL;
892 Track trk;
893 short id = 0;
894 DWORD tid;
895 HANDLE thread;
897 TRACE("Trying movie connect\n");
899 ptrDataRefRec.pReader = filter->pInputPin.pReader;
900 ptrDataRefRec.streamSubtype = filter->pInputPin.subType;
901 PtrToHand( &ptrDataRefRec, &dataRef, sizeof(WineDataRefRecord));
903 err = NewMovieFromDataRef(&filter->pQTMovie, newMovieActive|newMovieDontInteractWithUser|newMovieDontAutoUpdateClock|newMovieDontAskUnresolvedDataRefs|newMovieAsyncOK, &id, dataRef, 'WINE');
905 DisposeHandle(dataRef);
907 if (err != noErr)
909 FIXME("QuickTime cannot handle media type(%i)\n",err);
910 return VFW_E_TYPE_NOT_ACCEPTED;
913 PrePrerollMovie(filter->pQTMovie, 0, fixed1, NULL, NULL);
914 PrerollMovie(filter->pQTMovie, 0, fixed1);
915 GoToBeginningOfMovie(filter->pQTMovie);
916 SetMovieActive(filter->pQTMovie,TRUE);
918 if (GetMovieLoadState(filter->pQTMovie) < kMovieLoadStateLoaded)
919 MoviesTask(filter->pQTMovie,100);
921 trk = GetMovieIndTrackType(filter->pQTMovie, 1, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
922 TRACE("%p is a video track\n",trk);
923 if (trk)
924 hr = QT_Process_Video_Track(filter, trk);
926 if (FAILED(hr))
927 return hr;
929 trk = GetMovieIndTrackType(filter->pQTMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
930 TRACE("%p is a audio track\n",trk);
931 if (trk)
932 hr = QT_Process_Audio_Track(filter, trk);
934 thread = CreateThread(NULL, 0, QTSplitter_thread, filter, 0, &tid);
935 if (thread)
937 TRACE("Created thread 0x%08x\n", tid);
938 CloseHandle(thread);
940 else
941 hr = HRESULT_FROM_WIN32(GetLastError());
943 return hr;
946 static HRESULT WINAPI QTInPin_ReceiveConnection(IPin *iface, IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
948 HRESULT hr = S_OK;
949 ALLOCATOR_PROPERTIES props;
950 QTInPin *This = (QTInPin*)iface;
952 TRACE("(%p/%p)->(%p, %p)\n", This, iface, pReceivePin, pmt);
954 EnterCriticalSection(This->pin.pCritSec);
955 This->pReader = NULL;
957 if (This->pin.pConnectedTo)
958 hr = VFW_E_ALREADY_CONNECTED;
959 else if (IPin_QueryAccept(iface, pmt) != S_OK)
960 hr = VFW_E_TYPE_NOT_ACCEPTED;
961 else
963 PIN_DIRECTION pindirReceive;
964 IPin_QueryDirection(pReceivePin, &pindirReceive);
965 if (pindirReceive != PINDIR_OUTPUT)
966 hr = VFW_E_INVALID_DIRECTION;
969 if (FAILED(hr))
971 LeaveCriticalSection(This->pin.pCritSec);
972 return hr;
975 hr = IPin_QueryInterface(pReceivePin, &IID_IAsyncReader, (LPVOID *)&This->pReader);
976 if (FAILED(hr))
978 LeaveCriticalSection(This->pin.pCritSec);
979 TRACE("Input source is not an AsyncReader\n");
980 return hr;
983 LeaveCriticalSection(This->pin.pCritSec);
984 EnterCriticalSection(&((QTSplitter *)This->pin.pinInfo.pFilter)->filter.csFilter);
985 hr = QT_Process_Movie((QTSplitter *)This->pin.pinInfo.pFilter);
986 if (FAILED(hr))
988 LeaveCriticalSection(&((QTSplitter *)This->pin.pinInfo.pFilter)->filter.csFilter);
989 TRACE("Unable to process movie\n");
990 return hr;
993 This->pAlloc = NULL;
994 props.cBuffers = 8;
995 props.cbAlign = 1;
996 props.cbBuffer = ((QTSplitter *)This->pin.pinInfo.pFilter)->outputSize + props.cbAlign;
997 props.cbPrefix = 0;
999 hr = IAsyncReader_RequestAllocator(This->pReader, NULL, &props, &This->pAlloc);
1000 if (SUCCEEDED(hr))
1002 CopyMediaType(&This->pin.mtCurrent, pmt);
1003 This->pin.pConnectedTo = pReceivePin;
1004 IPin_AddRef(pReceivePin);
1005 hr = IMemAllocator_Commit(This->pAlloc);
1007 else
1009 QT_RemoveOutputPins((QTSplitter *)This->pin.pinInfo.pFilter);
1010 if (This->pReader)
1011 IAsyncReader_Release(This->pReader);
1012 This->pReader = NULL;
1013 if (This->pAlloc)
1014 IMemAllocator_Release(This->pAlloc);
1015 This->pAlloc = NULL;
1017 TRACE("Size: %i\n", props.cbBuffer);
1018 LeaveCriticalSection(&((QTSplitter *)This->pin.pinInfo.pFilter)->filter.csFilter);
1020 return hr;
1023 static HRESULT WINAPI QTInPin_Disconnect(IPin *iface)
1025 HRESULT hr;
1026 QTInPin *This = (QTInPin*)iface;
1027 FILTER_STATE state;
1028 TRACE("()\n");
1030 hr = IBaseFilter_GetState(This->pin.pinInfo.pFilter, INFINITE, &state);
1031 EnterCriticalSection(This->pin.pCritSec);
1032 if (This->pin.pConnectedTo)
1034 QTSplitter *Parser = (QTSplitter *)This->pin.pinInfo.pFilter;
1036 if (SUCCEEDED(hr) && state == State_Stopped)
1038 IMemAllocator_Decommit(This->pAlloc);
1039 IPin_Disconnect(This->pin.pConnectedTo);
1040 This->pin.pConnectedTo = NULL;
1041 hr = QT_RemoveOutputPins(Parser);
1043 else
1044 hr = VFW_E_NOT_STOPPED;
1046 else
1047 hr = S_FALSE;
1048 LeaveCriticalSection(This->pin.pCritSec);
1049 return hr;
1052 static HRESULT WINAPI QTInPin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *pmt)
1054 QTInPin *This = (QTInPin*)iface;
1056 TRACE("(%p)->(%p)\n", This, pmt);
1058 if (IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream))
1060 This->subType = pmt->subtype;
1061 return S_OK;
1063 return S_FALSE;
1066 static HRESULT WINAPI QTInPin_EndOfStream(IPin *iface)
1068 QTInPin *pin = (QTInPin*)iface;
1069 QTSplitter *This = (QTSplitter*)pin->pin.pinInfo.pFilter;
1071 FIXME("Propagate message on %p\n", This);
1072 return S_OK;
1075 static HRESULT WINAPI QTInPin_BeginFlush(IPin *iface)
1077 QTInPin *pin = (QTInPin*)iface;
1078 QTSplitter *This = (QTSplitter*)pin->pin.pinInfo.pFilter;
1080 FIXME("Propagate message on %p\n", This);
1081 return S_OK;
1084 static HRESULT WINAPI QTInPin_EndFlush(IPin *iface)
1086 QTInPin *pin = (QTInPin*)iface;
1087 QTSplitter *This = (QTSplitter*)pin->pin.pinInfo.pFilter;
1089 FIXME("Propagate message on %p\n", This);
1090 return S_OK;
1093 static HRESULT WINAPI QTInPin_NewSegment(IPin *iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
1095 QTInPin *pin = (QTInPin*)iface;
1096 QTSplitter *This = (QTSplitter*)pin->pin.pinInfo.pFilter;
1098 BasePinImpl_NewSegment(iface, tStart, tStop, dRate);
1099 FIXME("Propagate message on %p\n", This);
1100 return S_OK;
1103 static HRESULT WINAPI QTInPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
1105 QTInPin *This = (QTInPin*)iface;
1107 TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
1109 *ppv = NULL;
1111 if (IsEqualIID(riid, &IID_IUnknown))
1112 *ppv = iface;
1113 else if (IsEqualIID(riid, &IID_IPin))
1114 *ppv = iface;
1115 else if (IsEqualIID(riid, &IID_IMediaSeeking))
1117 return IBaseFilter_QueryInterface(This->pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
1120 if (*ppv)
1122 IUnknown_AddRef((IUnknown *)(*ppv));
1123 return S_OK;
1126 FIXME("No interface for %s!\n", debugstr_guid(riid));
1128 return E_NOINTERFACE;
1131 static HRESULT WINAPI QTInPin_EnumMediaTypes(IPin *iface, IEnumMediaTypes **ppEnum)
1133 BasePin *This = (BasePin *)iface;
1135 TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
1137 return EnumMediaTypes_Construct(This, BasePinImpl_GetMediaType, BasePinImpl_GetMediaTypeVersion, ppEnum);
1140 static const IPinVtbl QT_InputPin_Vtbl = {
1141 QTInPin_QueryInterface,
1142 BasePinImpl_AddRef,
1143 QTInPin_Release,
1144 BaseInputPinImpl_Connect,
1145 QTInPin_ReceiveConnection,
1146 QTInPin_Disconnect,
1147 BasePinImpl_ConnectedTo,
1148 BasePinImpl_ConnectionMediaType,
1149 BasePinImpl_QueryPinInfo,
1150 BasePinImpl_QueryDirection,
1151 BasePinImpl_QueryId,
1152 QTInPin_QueryAccept,
1153 QTInPin_EnumMediaTypes,
1154 BasePinImpl_QueryInternalConnections,
1155 QTInPin_EndOfStream,
1156 QTInPin_BeginFlush,
1157 QTInPin_EndFlush,
1158 QTInPin_NewSegment
1162 * Output Pin
1165 static HRESULT WINAPI QTOutPin_QueryInterface(IPin *iface, REFIID riid, void **ppv)
1167 QTOutPin *This = (QTOutPin *)iface;
1169 TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
1171 *ppv = NULL;
1173 if (IsEqualIID(riid, &IID_IUnknown))
1174 *ppv = iface;
1175 else if (IsEqualIID(riid, &IID_IPin))
1176 *ppv = iface;
1177 else if (IsEqualIID(riid, &IID_IMediaSeeking))
1178 return IBaseFilter_QueryInterface(This->pin.pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
1180 if (*ppv)
1182 IUnknown_AddRef((IUnknown *)(*ppv));
1183 return S_OK;
1185 FIXME("No interface for %s!\n", debugstr_guid(riid));
1186 return E_NOINTERFACE;
1189 static ULONG WINAPI QTOutPin_Release(IPin *iface)
1191 QTOutPin *This = (QTOutPin *)iface;
1192 ULONG refCount = InterlockedDecrement(&This->pin.pin.refCount);
1193 TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
1195 if (!refCount)
1197 DeleteMediaType(This->pmt);
1198 FreeMediaType(&This->pin.pin.mtCurrent);
1199 OutputQueue_Destroy(This->queue);
1200 CoTaskMemFree(This);
1201 return 0;
1203 return refCount;
1206 static HRESULT WINAPI QTOutPin_GetMediaType(BasePin *iface, int iPosition, AM_MEDIA_TYPE *pmt)
1208 QTOutPin *This = (QTOutPin *)iface;
1210 if (iPosition < 0)
1211 return E_INVALIDARG;
1212 if (iPosition > 0)
1213 return VFW_S_NO_MORE_ITEMS;
1214 CopyMediaType(pmt, This->pmt);
1215 return S_OK;
1218 static HRESULT WINAPI QTOutPin_DecideBufferSize(BaseOutputPin *iface, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
1220 /* Unused */
1221 return S_OK;
1224 static HRESULT WINAPI QTOutPin_DecideAllocator(BaseOutputPin *iface, IMemInputPin *pPin, IMemAllocator **pAlloc)
1226 HRESULT hr;
1227 QTOutPin *This = (QTOutPin *)iface;
1228 QTSplitter *QTfilter = (QTSplitter*)This->pin.pin.pinInfo.pFilter;
1230 *pAlloc = NULL;
1231 if (QTfilter->pInputPin.pAlloc)
1232 hr = IMemInputPin_NotifyAllocator(pPin, QTfilter->pInputPin.pAlloc, FALSE);
1233 else
1234 hr = VFW_E_NO_ALLOCATOR;
1236 return hr;
1239 static HRESULT WINAPI QTOutPin_BreakConnect(BaseOutputPin *This)
1241 HRESULT hr;
1243 TRACE("(%p)->()\n", This);
1245 EnterCriticalSection(This->pin.pCritSec);
1246 if (!This->pin.pConnectedTo || !This->pMemInputPin)
1247 hr = VFW_E_NOT_CONNECTED;
1248 else
1250 hr = IPin_Disconnect(This->pin.pConnectedTo);
1251 IPin_Disconnect((IPin *)This);
1253 LeaveCriticalSection(This->pin.pCritSec);
1255 return hr;
1258 static const IPinVtbl QT_OutputPin_Vtbl = {
1259 QTOutPin_QueryInterface,
1260 BasePinImpl_AddRef,
1261 QTOutPin_Release,
1262 BaseOutputPinImpl_Connect,
1263 BaseOutputPinImpl_ReceiveConnection,
1264 BaseOutputPinImpl_Disconnect,
1265 BasePinImpl_ConnectedTo,
1266 BasePinImpl_ConnectionMediaType,
1267 BasePinImpl_QueryPinInfo,
1268 BasePinImpl_QueryDirection,
1269 BasePinImpl_QueryId,
1270 BasePinImpl_QueryAccept,
1271 BasePinImpl_EnumMediaTypes,
1272 BasePinImpl_QueryInternalConnections,
1273 BaseOutputPinImpl_EndOfStream,
1274 BaseOutputPinImpl_BeginFlush,
1275 BaseOutputPinImpl_EndFlush,
1276 BasePinImpl_NewSegment
1279 static const BasePinFuncTable output_BaseFuncTable = {
1280 NULL,
1281 BaseOutputPinImpl_AttemptConnection,
1282 BasePinImpl_GetMediaTypeVersion,
1283 QTOutPin_GetMediaType
1286 static const BaseOutputPinFuncTable output_BaseOutputFuncTable = {
1287 QTOutPin_DecideBufferSize,
1288 QTOutPin_DecideAllocator,
1289 QTOutPin_BreakConnect
1292 static const OutputQueueFuncTable output_OutputQueueFuncTable = {
1293 OutputQueueImpl_ThreadProc
1296 static HRESULT QT_AddPin(QTSplitter *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt, BOOL video)
1298 HRESULT hr;
1299 IPin **target;
1301 if (video)
1302 target = (IPin**)&This->pVideo_Pin;
1303 else
1304 target = (IPin**)&This->pAudio_Pin;
1306 if (*target != NULL)
1308 FIXME("We already have a %s pin\n",(video)?"video":"audio");
1309 return E_FAIL;
1312 hr = BaseOutputPin_Construct(&QT_OutputPin_Vtbl, sizeof(QTOutPin), piOutput, &output_BaseFuncTable, &output_BaseOutputFuncTable, &This->filter.csFilter, (IPin**)target);
1313 if (SUCCEEDED(hr))
1315 QTOutPin *pin = (QTOutPin*)*target;
1316 pin->pmt = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
1317 CopyMediaType(pin->pmt, amt);
1318 pin->pin.pin.pinInfo.pFilter = (LPVOID)This;
1320 BaseFilterImpl_IncrementPinVersion((BaseFilter*)This);
1322 hr = OutputQueue_Construct((BaseOutputPin*)pin, TRUE, TRUE, 5, FALSE, THREAD_PRIORITY_NORMAL, &output_OutputQueueFuncTable, &pin->queue);
1324 else
1325 ERR("Failed with error %x\n", hr);
1326 return hr;