strmbase: Do not hold the csReceive lock when calling the transform's Receive function.
[wine] / dlls / strmbase / transform.c
1 /*
2  * Transform Filter (Base for decoders, etc...)
3  *
4  * Copyright 2005 Christian Costa
5  * Copyright 2010 Aric Stewart, CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 #include "config.h"
22 #include <stdarg.h>
23
24 #define COBJMACROS
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "dshow.h"
29 #include "amvideo.h"
30 #include "strmif.h"
31 #include "vfw.h"
32
33 #include <assert.h>
34
35 #include "wine/unicode.h"
36 #include "wine/debug.h"
37 #include "wine/strmbase.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(strmbase);
40
41 static const WCHAR wcsInputPinName[] = {'i','n','p','u','t',' ','p','i','n',0};
42 static const WCHAR wcsOutputPinName[] = {'o','u','t','p','u','t',' ','p','i','n',0};
43
44 static const IBaseFilterVtbl TransformFilter_Vtbl;
45 static const IPinVtbl TransformFilter_InputPin_Vtbl;
46 static const IPinVtbl TransformFilter_OutputPin_Vtbl;
47 static const IQualityControlVtbl TransformFilter_QualityControl_Vtbl;
48
49 static HRESULT WINAPI TransformFilter_Input_CheckMediaType(BasePin *iface, const AM_MEDIA_TYPE * pmt)
50 {
51     BaseInputPin* This = (BaseInputPin*) iface;
52     TransformFilter * pTransform;
53
54     TRACE("%p\n", iface);
55     pTransform = (TransformFilter*)This->pin.pinInfo.pFilter;
56
57     if (pTransform->pFuncsTable->pfnCheckInputType)
58         return pTransform->pFuncsTable->pfnCheckInputType(pTransform, pmt);
59     /* Assume OK if there's no query method (the connection will fail if
60        needed) */
61     return S_OK;
62 }
63
64 static HRESULT WINAPI TransformFilter_Input_Receive(BaseInputPin *This, IMediaSample *pInSample)
65 {
66     HRESULT hr = S_FALSE;
67     TransformFilter * pTransform;
68     TRACE("%p\n", This);
69     pTransform = (TransformFilter*)This->pin.pinInfo.pFilter;
70
71     EnterCriticalSection(&pTransform->csReceive);
72     if (pTransform->filter.state == State_Stopped)
73     {
74         LeaveCriticalSection(&pTransform->csReceive);
75         return VFW_E_WRONG_STATE;
76     }
77
78     if (This->end_of_stream || This->flushing)
79     {
80         LeaveCriticalSection(&pTransform->csReceive);
81         return S_FALSE;
82     }
83
84     LeaveCriticalSection(&pTransform->csReceive);
85     if (pTransform->pFuncsTable->pfnReceive)
86         hr = pTransform->pFuncsTable->pfnReceive(pTransform, pInSample);
87     else
88         hr = S_FALSE;
89
90     return hr;
91 }
92
93 static HRESULT WINAPI TransformFilter_Output_QueryAccept(IPin *iface, const AM_MEDIA_TYPE * pmt)
94 {
95     BasePin *This = (BasePin *)iface;
96     TransformFilter *pTransformFilter = (TransformFilter *)This->pinInfo.pFilter;
97     AM_MEDIA_TYPE* outpmt = &pTransformFilter->pmt;
98     TRACE("%p\n", iface);
99
100     if (IsEqualIID(&pmt->majortype, &outpmt->majortype)
101         && (IsEqualIID(&pmt->subtype, &outpmt->subtype) || IsEqualIID(&outpmt->subtype, &GUID_NULL)))
102         return S_OK;
103     return S_FALSE;
104 }
105
106 static HRESULT WINAPI TransformFilter_Output_DecideBufferSize(BaseOutputPin *This, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
107 {
108     TransformFilter *pTransformFilter = (TransformFilter *)This->pin.pinInfo.pFilter;
109     return pTransformFilter->pFuncsTable->pfnDecideBufferSize(pTransformFilter, pAlloc, ppropInputRequest);
110 }
111
112 static HRESULT WINAPI TransformFilter_Output_GetMediaType(BasePin *This, int iPosition, AM_MEDIA_TYPE *pmt)
113 {
114     TransformFilter *pTransform = (TransformFilter *)This->pinInfo.pFilter;
115
116     if (iPosition < 0)
117         return E_INVALIDARG;
118     if (iPosition > 0)
119         return VFW_S_NO_MORE_ITEMS;
120     CopyMediaType(pmt, &pTransform->pmt);
121     return S_OK;
122 }
123
124 static IPin* WINAPI TransformFilter_GetPin(BaseFilter *iface, int pos)
125 {
126     TransformFilter *This = (TransformFilter *)iface;
127
128     if (pos >= This->npins || pos < 0)
129         return NULL;
130
131     IPin_AddRef(This->ppPins[pos]);
132     return This->ppPins[pos];
133 }
134
135 static LONG WINAPI TransformFilter_GetPinCount(BaseFilter *iface)
136 {
137     TransformFilter *This = (TransformFilter *)iface;
138
139     return (This->npins+1);
140 }
141
142 static const BaseFilterFuncTable tfBaseFuncTable = {
143     TransformFilter_GetPin,
144     TransformFilter_GetPinCount
145 };
146
147 static const  BasePinFuncTable tf_input_BaseFuncTable = {
148     TransformFilter_Input_CheckMediaType,
149     NULL,
150     BasePinImpl_GetMediaTypeVersion,
151     BasePinImpl_GetMediaType
152 };
153
154 static const BaseInputPinFuncTable tf_input_BaseInputFuncTable = {
155     TransformFilter_Input_Receive
156 };
157
158 static const  BasePinFuncTable tf_output_BaseFuncTable = {
159     NULL,
160     BaseOutputPinImpl_AttemptConnection,
161     BasePinImpl_GetMediaTypeVersion,
162     TransformFilter_Output_GetMediaType
163 };
164
165 static const BaseOutputPinFuncTable tf_output_BaseOutputFuncTable = {
166     TransformFilter_Output_DecideBufferSize,
167     BaseOutputPinImpl_DecideAllocator,
168     BaseOutputPinImpl_BreakConnect
169 };
170
171 static HRESULT TransformFilter_Init(const IBaseFilterVtbl *pVtbl, const CLSID* pClsid, const TransformFilterFuncTable* pFuncsTable, TransformFilter* pTransformFilter)
172 {
173     HRESULT hr;
174     PIN_INFO piInput;
175     PIN_INFO piOutput;
176
177     BaseFilter_Init(&pTransformFilter->filter, pVtbl, pClsid, (DWORD_PTR)(__FILE__ ": TransformFilter.csFilter"), &tfBaseFuncTable);
178
179     InitializeCriticalSection(&pTransformFilter->csReceive);
180     pTransformFilter->csReceive.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__": TransformFilter.csReceive");
181
182     /* pTransformFilter is already allocated */
183     pTransformFilter->pFuncsTable = pFuncsTable;
184     ZeroMemory(&pTransformFilter->pmt, sizeof(pTransformFilter->pmt));
185     pTransformFilter->npins = 2;
186
187     pTransformFilter->ppPins = CoTaskMemAlloc(2 * sizeof(IPin *));
188
189     /* construct input pin */
190     piInput.dir = PINDIR_INPUT;
191     piInput.pFilter = (IBaseFilter *)pTransformFilter;
192     lstrcpynW(piInput.achName, wcsInputPinName, sizeof(piInput.achName) / sizeof(piInput.achName[0]));
193     piOutput.dir = PINDIR_OUTPUT;
194     piOutput.pFilter = (IBaseFilter *)pTransformFilter;
195     lstrcpynW(piOutput.achName, wcsOutputPinName, sizeof(piOutput.achName) / sizeof(piOutput.achName[0]));
196
197     hr = BaseInputPin_Construct(&TransformFilter_InputPin_Vtbl, &piInput, &tf_input_BaseFuncTable, &tf_input_BaseInputFuncTable, &pTransformFilter->filter.csFilter, NULL, &pTransformFilter->ppPins[0]);
198
199     if (SUCCEEDED(hr))
200     {
201         hr = BaseOutputPin_Construct(&TransformFilter_OutputPin_Vtbl, sizeof(BaseOutputPin), &piOutput, &tf_output_BaseFuncTable, &tf_output_BaseOutputFuncTable, &pTransformFilter->filter.csFilter, &pTransformFilter->ppPins[1]);
202
203         if (FAILED(hr))
204             ERR("Cannot create output pin (%x)\n", hr);
205         else {
206             QualityControlImpl_init(&pTransformFilter->qcimpl, (IPin*)pTransformFilter->ppPins[0], (IBaseFilter*)pTransformFilter);
207             pTransformFilter->qcimpl.lpVtbl = &TransformFilter_QualityControl_Vtbl;
208         }
209     }
210     if (FAILED(hr))
211     {
212         CoTaskMemFree(pTransformFilter->ppPins);
213         BaseFilterImpl_Release((IBaseFilter*)pTransformFilter);
214     }
215
216     return hr;
217 }
218
219 HRESULT TransformFilter_Construct(const IBaseFilterVtbl *pVtbl, LONG filter_size, const CLSID* pClsid, const TransformFilterFuncTable* pFuncsTable, IBaseFilter ** ppTransformFilter)
220 {
221     TransformFilter* pTf;
222
223     *ppTransformFilter = NULL;
224
225     assert(filter_size >= sizeof(TransformFilter));
226
227     pTf = CoTaskMemAlloc(filter_size);
228
229     if (!pTf)
230         return E_OUTOFMEMORY;
231
232     ZeroMemory(pTf, filter_size);
233
234     if (SUCCEEDED(TransformFilter_Init(pVtbl, pClsid, pFuncsTable, pTf)))
235     {
236         *ppTransformFilter = (IBaseFilter*)(&pTf->filter.lpVtbl);
237         return S_OK;
238     }
239
240     CoTaskMemFree(pTf);
241     return E_FAIL;
242 }
243
244 HRESULT WINAPI TransformFilterImpl_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
245 {
246     HRESULT hr;
247     TransformFilter *This = (TransformFilter *)iface;
248     TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
249
250     if (IsEqualIID(riid, &IID_IQualityControl))  {
251         *ppv = (IQualityControl*)&This->qcimpl;
252         IUnknown_AddRef((IUnknown*)*ppv);
253         return S_OK;
254     }
255     hr = BaseFilterImpl_QueryInterface(iface, riid, ppv);
256
257     if (FAILED(hr) && !IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow) &&
258         !IsEqualIID(riid, &IID_IAMFilterMiscFlags))
259         FIXME("No interface for %s!\n", debugstr_guid(riid));
260
261     return hr;
262 }
263
264 ULONG WINAPI TransformFilterImpl_Release(IBaseFilter * iface)
265 {
266     TransformFilter *This = (TransformFilter *)iface;
267     ULONG refCount = BaseFilterImpl_Release(iface);
268
269     TRACE("(%p/%p)->() Release from %d\n", This, iface, refCount + 1);
270
271     if (!refCount)
272     {
273         ULONG i;
274
275         for (i = 0; i < This->npins; i++)
276         {
277             IPin *pConnectedTo;
278
279             if (SUCCEEDED(IPin_ConnectedTo(This->ppPins[i], &pConnectedTo)))
280             {
281                 IPin_Disconnect(pConnectedTo);
282                 IPin_Release(pConnectedTo);
283             }
284             IPin_Disconnect(This->ppPins[i]);
285
286             IPin_Release(This->ppPins[i]);
287         }
288
289         CoTaskMemFree(This->ppPins);
290
291         TRACE("Destroying transform filter\n");
292         This->csReceive.DebugInfo->Spare[0] = 0;
293         DeleteCriticalSection(&This->csReceive);
294         FreeMediaType(&This->pmt);
295         CoTaskMemFree(This);
296
297         return 0;
298     }
299     else
300         return refCount;
301 }
302
303 /** IMediaFilter methods **/
304
305 HRESULT WINAPI TransformFilterImpl_Stop(IBaseFilter * iface)
306 {
307     TransformFilter *This = (TransformFilter *)iface;
308     HRESULT hr = S_OK;
309
310     TRACE("(%p/%p)\n", This, iface);
311
312     EnterCriticalSection(&This->csReceive);
313     {
314         This->filter.state = State_Stopped;
315         if (This->pFuncsTable->pfnStopStreaming)
316             hr = This->pFuncsTable->pfnStopStreaming(This);
317     }
318     LeaveCriticalSection(&This->csReceive);
319
320     return hr;
321 }
322
323 HRESULT WINAPI TransformFilterImpl_Pause(IBaseFilter * iface)
324 {
325     TransformFilter *This = (TransformFilter *)iface;
326     HRESULT hr;
327
328     TRACE("(%p/%p)->()\n", This, iface);
329
330     EnterCriticalSection(&This->csReceive);
331     {
332         if (This->filter.state == State_Stopped)
333             hr = IBaseFilter_Run(iface, -1);
334         else
335             hr = S_OK;
336
337         if (SUCCEEDED(hr))
338             This->filter.state = State_Paused;
339     }
340     LeaveCriticalSection(&This->csReceive);
341
342     return hr;
343 }
344
345 HRESULT WINAPI TransformFilterImpl_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
346 {
347     HRESULT hr = S_OK;
348     TransformFilter *This = (TransformFilter *)iface;
349
350     TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(tStart));
351
352     EnterCriticalSection(&This->csReceive);
353     {
354         if (This->filter.state == State_Stopped)
355         {
356             ((BaseInputPin *)This->ppPins[0])->end_of_stream = 0;
357             if (This->pFuncsTable->pfnStartStreaming)
358                 hr = This->pFuncsTable->pfnStartStreaming(This);
359             if (SUCCEEDED(hr))
360                 hr = BaseOutputPinImpl_Active((BaseOutputPin *)This->ppPins[1]);
361         }
362
363         if (SUCCEEDED(hr))
364         {
365             This->filter.rtStreamStart = tStart;
366             This->filter.state = State_Running;
367         }
368     }
369     LeaveCriticalSection(&This->csReceive);
370
371     return hr;
372 }
373
374 /** IBaseFilter implementation **/
375
376 HRESULT WINAPI TransformFilterImpl_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
377 {
378     TransformFilter *This = (TransformFilter *)iface;
379
380     TRACE("(%p/%p)->(%p,%p)\n", This, iface, debugstr_w(Id), ppPin);
381
382     return E_NOTIMPL;
383 }
384
385 static const IBaseFilterVtbl TransformFilter_Vtbl =
386 {
387     TransformFilterImpl_QueryInterface,
388     BaseFilterImpl_AddRef,
389     TransformFilterImpl_Release,
390     BaseFilterImpl_GetClassID,
391     TransformFilterImpl_Stop,
392     TransformFilterImpl_Pause,
393     TransformFilterImpl_Run,
394     BaseFilterImpl_GetState,
395     BaseFilterImpl_SetSyncSource,
396     BaseFilterImpl_GetSyncSource,
397     BaseFilterImpl_EnumPins,
398     TransformFilterImpl_FindPin,
399     BaseFilterImpl_QueryFilterInfo,
400     BaseFilterImpl_JoinFilterGraph,
401     BaseFilterImpl_QueryVendorInfo
402 };
403
404 static HRESULT WINAPI TransformFilter_InputPin_EndOfStream(IPin * iface)
405 {
406     BaseInputPin* This = (BaseInputPin*) iface;
407     TransformFilter* pTransform;
408     IPin* ppin;
409     HRESULT hr;
410
411     TRACE("(%p)->()\n", iface);
412
413     /* Since we process samples synchronously, just forward notification downstream */
414     pTransform = (TransformFilter*)This->pin.pinInfo.pFilter;
415     if (!pTransform)
416         hr = E_FAIL;
417     else
418         hr = IPin_ConnectedTo(pTransform->ppPins[1], &ppin);
419     if (SUCCEEDED(hr))
420     {
421         hr = IPin_EndOfStream(ppin);
422         IPin_Release(ppin);
423     }
424
425     if (FAILED(hr))
426         ERR("%x\n", hr);
427     return hr;
428 }
429
430 static HRESULT WINAPI TransformFilter_InputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
431 {
432     BaseInputPin* This = (BaseInputPin*) iface;
433     TransformFilter* pTransform;
434     HRESULT hr = S_OK;
435
436     TRACE("(%p)->(%p, %p)\n", iface, pReceivePin, pmt);
437
438     pTransform = (TransformFilter*)This->pin.pinInfo.pFilter;
439
440     if (pTransform->pFuncsTable->pfnSetMediaType)
441         hr = pTransform->pFuncsTable->pfnSetMediaType(pTransform, PINDIR_INPUT, pmt);
442
443     if (SUCCEEDED(hr) && pTransform->pFuncsTable->pfnCompleteConnect)
444         hr = pTransform->pFuncsTable->pfnCompleteConnect(pTransform, PINDIR_INPUT, pReceivePin);
445
446     if (SUCCEEDED(hr))
447     {
448         hr = BaseInputPinImpl_ReceiveConnection(iface, pReceivePin, pmt);
449         if (FAILED(hr) && pTransform->pFuncsTable->pfnBreakConnect)
450             pTransform->pFuncsTable->pfnBreakConnect(pTransform, PINDIR_INPUT);
451     }
452
453     return hr;
454 }
455
456 static HRESULT WINAPI TransformFilter_InputPin_Disconnect(IPin * iface)
457 {
458     BaseInputPin* This = (BaseInputPin*) iface;
459     TransformFilter* pTransform;
460
461     TRACE("(%p)->()\n", iface);
462
463     pTransform = (TransformFilter*)This->pin.pinInfo.pFilter;
464     if (pTransform->pFuncsTable->pfnBreakConnect)
465         pTransform->pFuncsTable->pfnBreakConnect(pTransform, PINDIR_INPUT);
466
467     return BasePinImpl_Disconnect(iface);
468 }
469
470 static HRESULT WINAPI TransformFilter_InputPin_BeginFlush(IPin * iface)
471 {
472     BaseInputPin* This = (BaseInputPin*) iface;
473     TransformFilter* pTransform;
474     HRESULT hr = S_OK;
475
476     TRACE("(%p)->()\n", iface);
477
478     pTransform = (TransformFilter*)This->pin.pinInfo.pFilter;
479     EnterCriticalSection(&pTransform->filter.csFilter);
480     if (pTransform->pFuncsTable->pfnBeginFlush)
481         hr = pTransform->pFuncsTable->pfnBeginFlush(pTransform);
482     if (SUCCEEDED(hr))
483         hr = BaseInputPinImpl_BeginFlush(iface);
484     LeaveCriticalSection(&pTransform->filter.csFilter);
485     return hr;
486 }
487
488 static HRESULT WINAPI TransformFilter_InputPin_EndFlush(IPin * iface)
489 {
490     BaseInputPin* This = (BaseInputPin*) iface;
491     TransformFilter* pTransform;
492     HRESULT hr = S_OK;
493
494     TRACE("(%p)->()\n", iface);
495
496     pTransform = (TransformFilter*)This->pin.pinInfo.pFilter;
497     EnterCriticalSection(&pTransform->filter.csFilter);
498     if (pTransform->pFuncsTable->pfnEndFlush)
499         hr = pTransform->pFuncsTable->pfnEndFlush(pTransform);
500     if (SUCCEEDED(hr))
501         hr = BaseInputPinImpl_EndFlush(iface);
502     LeaveCriticalSection(&pTransform->filter.csFilter);
503     return hr;
504 }
505
506 static HRESULT WINAPI TransformFilter_InputPin_NewSegment(IPin * iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
507 {
508     BaseInputPin* This = (BaseInputPin*) iface;
509     TransformFilter* pTransform;
510     HRESULT hr = S_OK;
511
512     TRACE("(%p)->()\n", iface);
513
514     pTransform = (TransformFilter*)This->pin.pinInfo.pFilter;
515     EnterCriticalSection(&pTransform->filter.csFilter);
516     if (pTransform->pFuncsTable->pfnNewSegment)
517         hr = pTransform->pFuncsTable->pfnNewSegment(pTransform, tStart, tStop, dRate);
518     if (SUCCEEDED(hr))
519         hr = BaseInputPinImpl_NewSegment(iface, tStart, tStop, dRate);
520     LeaveCriticalSection(&pTransform->filter.csFilter);
521     return hr;
522 }
523
524 static const IPinVtbl TransformFilter_InputPin_Vtbl =
525 {
526     BaseInputPinImpl_QueryInterface,
527     BasePinImpl_AddRef,
528     BaseInputPinImpl_Release,
529     BaseInputPinImpl_Connect,
530     TransformFilter_InputPin_ReceiveConnection,
531     TransformFilter_InputPin_Disconnect,
532     BasePinImpl_ConnectedTo,
533     BasePinImpl_ConnectionMediaType,
534     BasePinImpl_QueryPinInfo,
535     BasePinImpl_QueryDirection,
536     BasePinImpl_QueryId,
537     BaseInputPinImpl_QueryAccept,
538     BasePinImpl_EnumMediaTypes,
539     BasePinImpl_QueryInternalConnections,
540     TransformFilter_InputPin_EndOfStream,
541     TransformFilter_InputPin_BeginFlush,
542     TransformFilter_InputPin_EndFlush,
543     TransformFilter_InputPin_NewSegment
544 };
545
546 static const IPinVtbl TransformFilter_OutputPin_Vtbl =
547 {
548     BaseOutputPinImpl_QueryInterface,
549     BasePinImpl_AddRef,
550     BaseOutputPinImpl_Release,
551     BaseOutputPinImpl_Connect,
552     BaseOutputPinImpl_ReceiveConnection,
553     BaseOutputPinImpl_Disconnect,
554     BasePinImpl_ConnectedTo,
555     BasePinImpl_ConnectionMediaType,
556     BasePinImpl_QueryPinInfo,
557     BasePinImpl_QueryDirection,
558     BasePinImpl_QueryId,
559     TransformFilter_Output_QueryAccept,
560     BasePinImpl_EnumMediaTypes,
561     BasePinImpl_QueryInternalConnections,
562     BaseOutputPinImpl_EndOfStream,
563     BaseOutputPinImpl_BeginFlush,
564     BaseOutputPinImpl_EndFlush,
565     BasePinImpl_NewSegment
566 };
567
568 static HRESULT WINAPI TransformFilter_QualityControlImpl_Notify(IQualityControl *iface, IBaseFilter *sender, Quality qm) {
569     QualityControlImpl *qc = (QualityControlImpl*)iface;
570     TransformFilter *This = (TransformFilter *)qc->self;
571
572     if (This->pFuncsTable->pfnNotify)
573         return This->pFuncsTable->pfnNotify(This, sender, qm);
574     else
575         return QualityControlImpl_Notify(iface, sender, qm);
576 }
577
578 static const IQualityControlVtbl TransformFilter_QualityControl_Vtbl = {
579     QualityControlImpl_QueryInterface,
580     QualityControlImpl_AddRef,
581     QualityControlImpl_Release,
582     TransformFilter_QualityControlImpl_Notify,
583     QualityControlImpl_SetSink
584 };