quartz: Use a safe APC to kill the PullPin thread when the PullPin is being released.
[wine] / dlls / quartz / transform.c
1 /*
2  * Transform Filter (Base for decoders, etc...)
3  *
4  * Copyright 2005 Christian Costa
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include "config.h"
22
23 #include "quartz_private.h"
24 #include "control_private.h"
25 #include "pin.h"
26
27 #include "uuids.h"
28 #include "aviriff.h"
29 #include "mmreg.h"
30 #include "vfwmsgs.h"
31 #include "amvideo.h"
32 #include "windef.h"
33 #include "winbase.h"
34 #include "dshow.h"
35 #include "strmif.h"
36 #include "vfwmsgs.h"
37 #include "evcode.h"
38 #include "vfw.h"
39
40 #include <assert.h>
41
42 #include "wine/unicode.h"
43 #include "wine/debug.h"
44
45 #include "transform.h"
46
47 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
48
49 static const WCHAR wcsInputPinName[] = {'i','n','p','u','t',' ','p','i','n',0};
50 static const WCHAR wcsOutputPinName[] = {'o','u','t','p','u','t',' ','p','i','n',0};
51
52 static const IBaseFilterVtbl TransformFilter_Vtbl;
53 static const IPinVtbl TransformFilter_InputPin_Vtbl;
54 static const IMemInputPinVtbl MemInputPin_Vtbl; 
55 static const IPinVtbl TransformFilter_OutputPin_Vtbl;
56
57 static HRESULT TransformFilter_Sample(LPVOID iface, IMediaSample * pSample)
58 {
59     TransformFilterImpl *This = (TransformFilterImpl *)iface;
60     LPBYTE pbSrcStream = NULL;
61     long cbSrcStream = 0;
62     REFERENCE_TIME tStart, tStop;
63     HRESULT hr;
64
65     TRACE("%p %p\n", iface, pSample);
66
67     hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
68     if (FAILED(hr))
69     {
70         ERR("Cannot get pointer to sample data (%x)\n", hr);
71         return hr;
72     }
73
74     hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
75     if (FAILED(hr))
76         ERR("Cannot get sample time (%x)\n", hr);
77
78     cbSrcStream = IMediaSample_GetActualDataLength(pSample);
79
80     TRACE("Sample data ptr = %p, size = %ld\n", pbSrcStream, cbSrcStream);
81
82 #if 0 /* For debugging purpose */
83     {
84         int i;
85         for(i = 0; i < cbSrcStream; i++)
86         {
87             if ((i!=0) && !(i%16))
88                 TRACE("\n");
89             TRACE("%02x ", pbSrcStream[i]);
90         }
91         TRACE("\n");
92     }
93 #endif
94
95     return This->pFuncsTable->pfnProcessSampleData(This, pbSrcStream, cbSrcStream);
96 }
97
98 static HRESULT TransformFilter_Input_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
99 {
100     TransformFilterImpl* This = (TransformFilterImpl*)iface;
101     TRACE("%p\n", iface);
102     dump_AM_MEDIA_TYPE(pmt);
103
104     return This->pFuncsTable->pfnConnectInput(This, pmt);
105 }
106
107
108 static HRESULT TransformFilter_Output_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
109 {
110     TransformFilterImpl* pTransformFilter = (TransformFilterImpl*)iface;
111     AM_MEDIA_TYPE* outpmt = &((OutputPin*)pTransformFilter->ppPins[1])->pin.mtCurrent;
112     TRACE("%p\n", iface);
113
114     if (IsEqualIID(&pmt->majortype, &outpmt->majortype) && IsEqualIID(&pmt->subtype, &outpmt->subtype))
115         return S_OK;
116     return S_FALSE;
117 }
118
119 static HRESULT TransformFilter_InputPin_Construct(const PIN_INFO * pPinInfo, SAMPLEPROC pSampleProc, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
120 {
121     InputPin * pPinImpl;
122
123     *ppPin = NULL;
124
125     if (pPinInfo->dir != PINDIR_INPUT)
126     {
127         ERR("Pin direction(%x) != PINDIR_INPUT\n", pPinInfo->dir);
128         return E_INVALIDARG;
129     }
130
131     pPinImpl = CoTaskMemAlloc(sizeof(*pPinImpl));
132
133     if (!pPinImpl)
134         return E_OUTOFMEMORY;
135
136     if (SUCCEEDED(InputPin_Init(pPinInfo, pSampleProc, pUserData, pQueryAccept, pCritSec, pPinImpl)))
137     {
138         pPinImpl->pin.lpVtbl = &TransformFilter_InputPin_Vtbl;
139         pPinImpl->lpVtblMemInput = &MemInputPin_Vtbl;
140
141         *ppPin = (IPin *)(&pPinImpl->pin.lpVtbl);
142         return S_OK;
143     }
144     return E_FAIL;
145 }
146
147 static HRESULT TransformFilter_OutputPin_Construct(const PIN_INFO * pPinInfo, ALLOCATOR_PROPERTIES *props, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
148 {
149     OutputPin * pPinImpl;
150
151     *ppPin = NULL;
152
153     if (pPinInfo->dir != PINDIR_OUTPUT)
154     {
155         ERR("Pin direction(%x) != PINDIR_OUTPUT\n", pPinInfo->dir);
156         return E_INVALIDARG;
157     }
158
159     pPinImpl = CoTaskMemAlloc(sizeof(*pPinImpl));
160
161     if (!pPinImpl)
162         return E_OUTOFMEMORY;
163
164     if (SUCCEEDED(OutputPin_Init(pPinInfo, props, pUserData, pQueryAccept, pCritSec, pPinImpl)))
165     {
166         pPinImpl->pin.lpVtbl = &TransformFilter_OutputPin_Vtbl;
167
168         *ppPin = (IPin *)(&pPinImpl->pin.lpVtbl);
169         return S_OK;
170     }
171     return E_FAIL;
172 }
173
174 HRESULT TransformFilter_Create(TransformFilterImpl* pTransformFilter, const CLSID* pClsid, const TransformFuncsTable* pFuncsTable)
175 {
176     HRESULT hr;
177     PIN_INFO piInput;
178     PIN_INFO piOutput;
179
180     /* pTransformFilter is already allocated */
181     pTransformFilter->clsid = *pClsid;
182     pTransformFilter->pFuncsTable = pFuncsTable;
183
184     pTransformFilter->lpVtbl = &TransformFilter_Vtbl;
185
186     pTransformFilter->refCount = 1;
187     InitializeCriticalSection(&pTransformFilter->csFilter);
188     pTransformFilter->csFilter.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": TransformFilterImpl.csFilter");
189     pTransformFilter->state = State_Stopped;
190     pTransformFilter->pClock = NULL;
191     ZeroMemory(&pTransformFilter->filterInfo, sizeof(FILTER_INFO));
192
193     pTransformFilter->ppPins = CoTaskMemAlloc(2 * sizeof(IPin *));
194
195     /* construct input pin */
196     piInput.dir = PINDIR_INPUT;
197     piInput.pFilter = (IBaseFilter *)pTransformFilter;
198     lstrcpynW(piInput.achName, wcsInputPinName, sizeof(piInput.achName) / sizeof(piInput.achName[0]));
199     piOutput.dir = PINDIR_OUTPUT;
200     piOutput.pFilter = (IBaseFilter *)pTransformFilter;
201     lstrcpynW(piOutput.achName, wcsOutputPinName, sizeof(piOutput.achName) / sizeof(piOutput.achName[0]));
202
203     hr = TransformFilter_InputPin_Construct(&piInput, TransformFilter_Sample, pTransformFilter, TransformFilter_Input_QueryAccept, &pTransformFilter->csFilter, &pTransformFilter->ppPins[0]);
204
205     if (SUCCEEDED(hr))
206     {
207         ALLOCATOR_PROPERTIES props;
208         props.cbAlign = 1;
209         props.cbPrefix = 0;
210         props.cbBuffer = 0; /* Will be updated at connection time */
211         props.cBuffers = 2;
212
213         hr = TransformFilter_OutputPin_Construct(&piOutput, &props, pTransformFilter, TransformFilter_Output_QueryAccept, &pTransformFilter->csFilter, &pTransformFilter->ppPins[1]);
214
215         if (FAILED(hr))
216             ERR("Cannot create output pin (%x)\n", hr);
217     }
218     else
219     {
220         CoTaskMemFree(pTransformFilter->ppPins);
221         pTransformFilter->csFilter.DebugInfo->Spare[0] = 0;
222         DeleteCriticalSection(&pTransformFilter->csFilter);
223         CoTaskMemFree(pTransformFilter);
224     }
225
226     return hr;
227 }
228
229 static HRESULT WINAPI TransformFilter_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
230 {
231     TransformFilterImpl *This = (TransformFilterImpl *)iface;
232     TRACE("(%p/%p)->(%s, %p)\n", This, iface, qzdebugstr_guid(riid), ppv);
233
234     *ppv = NULL;
235
236     if (IsEqualIID(riid, &IID_IUnknown))
237         *ppv = (LPVOID)This;
238     else if (IsEqualIID(riid, &IID_IPersist))
239         *ppv = (LPVOID)This;
240     else if (IsEqualIID(riid, &IID_IMediaFilter))
241         *ppv = (LPVOID)This;
242     else if (IsEqualIID(riid, &IID_IBaseFilter))
243         *ppv = (LPVOID)This;
244
245     if (*ppv)
246     {
247         IUnknown_AddRef((IUnknown *)(*ppv));
248         return S_OK;
249     }
250
251     FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
252
253     return E_NOINTERFACE;
254 }
255
256 static ULONG WINAPI TransformFilter_AddRef(IBaseFilter * iface)
257 {
258     TransformFilterImpl *This = (TransformFilterImpl *)iface;
259     ULONG refCount = InterlockedIncrement(&This->refCount);
260
261     TRACE("(%p/%p)->() AddRef from %d\n", This, iface, refCount - 1);
262
263     return refCount;
264 }
265
266 static ULONG WINAPI TransformFilter_Release(IBaseFilter * iface)
267 {
268     TransformFilterImpl *This = (TransformFilterImpl *)iface;
269     ULONG refCount = InterlockedDecrement(&This->refCount);
270
271     TRACE("(%p/%p)->() Release from %d\n", This, iface, refCount + 1);
272
273     if (!refCount)
274     {
275         ULONG i;
276
277         if (This->pClock)
278             IReferenceClock_Release(This->pClock);
279
280         for (i = 0; i < 2; i++)
281         {
282             IPin *pConnectedTo;
283
284             if (SUCCEEDED(IPin_ConnectedTo(This->ppPins[i], &pConnectedTo)))
285             {
286                 IPin_Disconnect(pConnectedTo);
287                 IPin_Release(pConnectedTo);
288             }
289             IPin_Disconnect(This->ppPins[i]);
290
291             IPin_Release(This->ppPins[i]);
292         }
293
294         CoTaskMemFree(This->ppPins);
295         This->lpVtbl = NULL;
296
297         This->pFuncsTable->pfnCleanup(This);
298
299         This->csFilter.DebugInfo->Spare[0] = 0;
300         DeleteCriticalSection(&This->csFilter);
301
302         TRACE("Destroying transform filter\n");
303         CoTaskMemFree(This);
304
305         return 0;
306     }
307     else
308         return refCount;
309 }
310
311 /** IPersist methods **/
312
313 static HRESULT WINAPI TransformFilter_GetClassID(IBaseFilter * iface, CLSID * pClsid)
314 {
315     TransformFilterImpl *This = (TransformFilterImpl *)iface;
316
317     TRACE("(%p/%p)->(%p)\n", This, iface, pClsid);
318
319     *pClsid = This->clsid;
320
321     return S_OK;
322 }
323
324 /** IMediaFilter methods **/
325
326 static HRESULT WINAPI TransformFilter_Stop(IBaseFilter * iface)
327 {
328     TransformFilterImpl *This = (TransformFilterImpl *)iface;
329
330     TRACE("(%p/%p)\n", This, iface);
331
332     EnterCriticalSection(&This->csFilter);
333     {
334         This->state = State_Stopped;
335         if (This->pFuncsTable->pfnProcessEnd)
336             This->pFuncsTable->pfnProcessEnd(This);
337     }
338     LeaveCriticalSection(&This->csFilter);
339
340     return S_OK;
341 }
342
343 static HRESULT WINAPI TransformFilter_Pause(IBaseFilter * iface)
344 {
345     TransformFilterImpl *This = (TransformFilterImpl *)iface;
346
347     TRACE("(%p/%p)->()\n", This, iface);
348
349     EnterCriticalSection(&This->csFilter);
350     {
351         This->state = State_Paused;
352     }
353     LeaveCriticalSection(&This->csFilter);
354
355     return S_OK;
356 }
357
358 static HRESULT WINAPI TransformFilter_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
359 {
360     HRESULT hr = S_OK;
361     TransformFilterImpl *This = (TransformFilterImpl *)iface;
362
363     TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(tStart));
364
365     EnterCriticalSection(&This->csFilter);
366     {
367         This->rtStreamStart = tStart;
368         This->state = State_Running;
369         OutputPin_CommitAllocator((OutputPin *)This->ppPins[1]);
370         if (This->pFuncsTable->pfnProcessBegin)
371             This->pFuncsTable->pfnProcessBegin(This);
372     }
373     LeaveCriticalSection(&This->csFilter);
374
375     return hr;
376 }
377
378 static HRESULT WINAPI TransformFilter_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
379 {
380     TransformFilterImpl *This = (TransformFilterImpl *)iface;
381
382     TRACE("(%p/%p)->(%d, %p)\n", This, iface, dwMilliSecsTimeout, pState);
383
384     EnterCriticalSection(&This->csFilter);
385     {
386         *pState = This->state;
387     }
388     LeaveCriticalSection(&This->csFilter);
389
390     return S_OK;
391 }
392
393 static HRESULT WINAPI TransformFilter_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock)
394 {
395     TransformFilterImpl *This = (TransformFilterImpl *)iface;
396
397     TRACE("(%p/%p)->(%p)\n", This, iface, pClock);
398
399     EnterCriticalSection(&This->csFilter);
400     {
401         if (This->pClock)
402             IReferenceClock_Release(This->pClock);
403         This->pClock = pClock;
404         if (This->pClock)
405             IReferenceClock_AddRef(This->pClock);
406     }
407     LeaveCriticalSection(&This->csFilter);
408
409     return S_OK;
410 }
411
412 static HRESULT WINAPI TransformFilter_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock)
413 {
414     TransformFilterImpl *This = (TransformFilterImpl *)iface;
415
416     TRACE("(%p/%p)->(%p)\n", This, iface, ppClock);
417
418     EnterCriticalSection(&This->csFilter);
419     {
420         *ppClock = This->pClock;
421         IReferenceClock_AddRef(This->pClock);
422     }
423     LeaveCriticalSection(&This->csFilter);
424
425     return S_OK;
426 }
427
428 /** IBaseFilter implementation **/
429
430 static HRESULT WINAPI TransformFilter_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum)
431 {
432     ENUMPINDETAILS epd;
433     TransformFilterImpl *This = (TransformFilterImpl *)iface;
434
435     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
436
437     epd.cPins = 2; /* input and output pins */
438     epd.ppPins = This->ppPins;
439     return IEnumPinsImpl_Construct(&epd, ppEnum);
440 }
441
442 static HRESULT WINAPI TransformFilter_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
443 {
444     TransformFilterImpl *This = (TransformFilterImpl *)iface;
445
446     TRACE("(%p/%p)->(%p,%p)\n", This, iface, debugstr_w(Id), ppPin);
447
448     return E_NOTIMPL;
449 }
450
451 static HRESULT WINAPI TransformFilter_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo)
452 {
453     TransformFilterImpl *This = (TransformFilterImpl *)iface;
454
455     TRACE("(%p/%p)->(%p)\n", This, iface, pInfo);
456
457     strcpyW(pInfo->achName, This->filterInfo.achName);
458     pInfo->pGraph = This->filterInfo.pGraph;
459
460     if (pInfo->pGraph)
461         IFilterGraph_AddRef(pInfo->pGraph);
462
463     return S_OK;
464 }
465
466 static HRESULT WINAPI TransformFilter_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName)
467 {
468     HRESULT hr = S_OK;
469     TransformFilterImpl *This = (TransformFilterImpl *)iface;
470
471     TRACE("(%p/%p)->(%p, %s)\n", This, iface, pGraph, debugstr_w(pName));
472
473     EnterCriticalSection(&This->csFilter);
474     {
475         if (pName)
476             strcpyW(This->filterInfo.achName, pName);
477         else
478             *This->filterInfo.achName = '\0';
479         This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */
480     }
481     LeaveCriticalSection(&This->csFilter);
482
483     return hr;
484 }
485
486 static HRESULT WINAPI TransformFilter_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo)
487 {
488     TransformFilterImpl *This = (TransformFilterImpl *)iface;
489     TRACE("(%p/%p)->(%p)\n", This, iface, pVendorInfo);
490     return E_NOTIMPL;
491 }
492
493 static const IBaseFilterVtbl TransformFilter_Vtbl =
494 {
495     TransformFilter_QueryInterface,
496     TransformFilter_AddRef,
497     TransformFilter_Release,
498     TransformFilter_GetClassID,
499     TransformFilter_Stop,
500     TransformFilter_Pause,
501     TransformFilter_Run,
502     TransformFilter_GetState,
503     TransformFilter_SetSyncSource,
504     TransformFilter_GetSyncSource,
505     TransformFilter_EnumPins,
506     TransformFilter_FindPin,
507     TransformFilter_QueryFilterInfo,
508     TransformFilter_JoinFilterGraph,
509     TransformFilter_QueryVendorInfo
510 };
511
512 static HRESULT WINAPI TransformFilter_InputPin_EndOfStream(IPin * iface)
513 {
514     InputPin* This = (InputPin*) iface;
515     TransformFilterImpl* pTransform;
516     IPin* ppin;
517     HRESULT hr;
518     
519     TRACE("(%p)->()\n", iface);
520
521     /* Since we process samples synchronously, just forward notification downstream */
522     pTransform = (TransformFilterImpl*)This->pin.pinInfo.pFilter;
523     if (!pTransform)
524         hr = E_FAIL;
525     else
526         hr = IPin_ConnectedTo(pTransform->ppPins[1], &ppin);
527     if (SUCCEEDED(hr))
528     {
529         hr = IPin_EndOfStream(ppin);
530         IPin_Release(ppin);
531     }
532
533     if (FAILED(hr))
534         ERR("%x\n", hr);
535     return hr;
536 }
537
538 static const IPinVtbl TransformFilter_InputPin_Vtbl = 
539 {
540     InputPin_QueryInterface,
541     IPinImpl_AddRef,
542     InputPin_Release,
543     InputPin_Connect,
544     InputPin_ReceiveConnection,
545     IPinImpl_Disconnect,
546     IPinImpl_ConnectedTo,
547     IPinImpl_ConnectionMediaType,
548     IPinImpl_QueryPinInfo,
549     IPinImpl_QueryDirection,
550     IPinImpl_QueryId,
551     IPinImpl_QueryAccept,
552     IPinImpl_EnumMediaTypes,
553     IPinImpl_QueryInternalConnections,
554     TransformFilter_InputPin_EndOfStream,
555     InputPin_BeginFlush,
556     InputPin_EndFlush,
557     InputPin_NewSegment
558 };
559
560 static HRESULT WINAPI TransformFilter_Output_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
561 {
562     IPinImpl *This = (IPinImpl *)iface;
563     ENUMMEDIADETAILS emd;
564
565     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
566
567     emd.cMediaTypes = 1;
568     emd.pMediaTypes = &This->mtCurrent;
569
570     return IEnumMediaTypesImpl_Construct(&emd, ppEnum);
571 }
572
573 static HRESULT WINAPI TransformFilter_Output_Disconnect(IPin * iface)
574 {
575     OutputPin* This = (OutputPin*)iface;
576     HRESULT hr;
577     TransformFilterImpl* pTransformFilter = (TransformFilterImpl*)This->pin.pinInfo.pFilter;
578
579     TRACE("(%p/%p)->()\n", This, iface);
580
581     hr = OutputPin_Disconnect(iface);
582
583     if (hr == S_OK)
584     {
585         pTransformFilter->pFuncsTable->pfnCleanup(pTransformFilter);
586     }
587
588     return hr;
589 }
590
591 static const IPinVtbl TransformFilter_OutputPin_Vtbl = 
592 {
593     OutputPin_QueryInterface,
594     IPinImpl_AddRef,
595     OutputPin_Release,
596     OutputPin_Connect,
597     OutputPin_ReceiveConnection,
598     TransformFilter_Output_Disconnect,
599     IPinImpl_ConnectedTo,
600     IPinImpl_ConnectionMediaType,
601     IPinImpl_QueryPinInfo,
602     IPinImpl_QueryDirection,
603     IPinImpl_QueryId,
604     IPinImpl_QueryAccept,
605     TransformFilter_Output_EnumMediaTypes,
606     IPinImpl_QueryInternalConnections,
607     OutputPin_EndOfStream,
608     OutputPin_BeginFlush,
609     OutputPin_EndFlush,
610     OutputPin_NewSegment
611 };
612
613 static const IMemInputPinVtbl MemInputPin_Vtbl = 
614 {
615     MemInputPin_QueryInterface,
616     MemInputPin_AddRef,
617     MemInputPin_Release,
618     MemInputPin_GetAllocator,
619     MemInputPin_NotifyAllocator,
620     MemInputPin_GetAllocatorRequirements,
621     MemInputPin_Receive,
622     MemInputPin_ReceiveMultiple,
623     MemInputPin_ReceiveCanBlock
624 };