strmbase: Move TransformFilter implementation to strmbase.
[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
48 static HRESULT WINAPI TransformFilter_Input_CheckMediaType(IPin *iface, const AM_MEDIA_TYPE * pmt)
49 {
50     BaseInputPin* This = (BaseInputPin*) iface;
51     TransformFilter * pTransform;
52
53     TRACE("%p\n", iface);
54     pTransform = (TransformFilter*)This->pin.pinInfo.pFilter;
55
56     if (pTransform->pFuncsTable->pfnCheckInputType)
57         return pTransform->pFuncsTable->pfnCheckInputType(pTransform, pmt);
58     /* Assume OK if there's no query method (the connection will fail if
59        needed) */
60     return S_OK;
61 }
62
63 static HRESULT WINAPI TransformFilter_Input_Receive(IPin *iface, IMediaSample *pInSample)
64 {
65     HRESULT hr = S_FALSE;
66     BaseInputPin* This = (BaseInputPin*) iface;
67     TransformFilter * pTransform;
68     TRACE("%p\n", iface);
69     pTransform = (TransformFilter*)This->pin.pinInfo.pFilter;
70
71     EnterCriticalSection(&pTransform->filter.csFilter);
72     if (pTransform->filter.state == State_Stopped)
73     {
74         LeaveCriticalSection(&pTransform->filter.csFilter);
75         return VFW_E_WRONG_STATE;
76     }
77
78     if (This->end_of_stream || This->flushing)
79     {
80         LeaveCriticalSection(&pTransform->filter.csFilter);
81         return S_FALSE;
82     }
83     LeaveCriticalSection(&pTransform->filter.csFilter);
84
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 IPin* WINAPI TransformFilter_GetPin(IBaseFilter *iface, int pos)
107 {
108     TransformFilter *This = (TransformFilter *)iface;
109
110     if (pos >= This->npins || pos < 0)
111         return NULL;
112
113     IPin_AddRef(This->ppPins[pos]);
114     return This->ppPins[pos];
115 }
116
117 static LONG WINAPI TransformFilter_GetPinCount(IBaseFilter *iface)
118 {
119     TransformFilter *This = (TransformFilter *)iface;
120
121     return (This->npins+1);
122 }
123
124 static HRESULT TransformFilter_Init(const IBaseFilterVtbl *pVtbl, const CLSID* pClsid, const TransformFilterFuncTable* pFuncsTable, TransformFilter* pTransformFilter)
125 {
126     HRESULT hr;
127     PIN_INFO piInput;
128     PIN_INFO piOutput;
129
130     BaseFilter_Init(&pTransformFilter->filter, pVtbl, pClsid, (DWORD_PTR)(__FILE__ ": TransformFilter.csFilter"), TransformFilter_GetPin, TransformFilter_GetPinCount);
131
132     /* pTransformFilter is already allocated */
133     pTransformFilter->pFuncsTable = pFuncsTable;
134     ZeroMemory(&pTransformFilter->pmt, sizeof(pTransformFilter->pmt));
135     pTransformFilter->npins = 2;
136
137     pTransformFilter->ppPins = CoTaskMemAlloc(2 * sizeof(IPin *));
138
139     /* construct input pin */
140     piInput.dir = PINDIR_INPUT;
141     piInput.pFilter = (IBaseFilter *)pTransformFilter;
142     lstrcpynW(piInput.achName, wcsInputPinName, sizeof(piInput.achName) / sizeof(piInput.achName[0]));
143     piOutput.dir = PINDIR_OUTPUT;
144     piOutput.pFilter = (IBaseFilter *)pTransformFilter;
145     lstrcpynW(piOutput.achName, wcsOutputPinName, sizeof(piOutput.achName) / sizeof(piOutput.achName[0]));
146
147     hr = BaseInputPin_Construct(&TransformFilter_InputPin_Vtbl, &piInput, TransformFilter_Input_CheckMediaType, TransformFilter_Input_Receive, &pTransformFilter->filter.csFilter, NULL, &pTransformFilter->ppPins[0]);
148
149     if (SUCCEEDED(hr))
150     {
151         ALLOCATOR_PROPERTIES props;
152         props.cbAlign = 1;
153         props.cbPrefix = 0;
154         props.cbBuffer = 0; /* Will be updated at connection time */
155         props.cBuffers = 1;
156
157         hr = BaseOutputPin_Construct(&TransformFilter_OutputPin_Vtbl, sizeof(BaseOutputPin), &piOutput, &props, NULL, &pTransformFilter->filter.csFilter, &pTransformFilter->ppPins[1]);
158
159         if (FAILED(hr))
160             ERR("Cannot create output pin (%x)\n", hr);
161     }
162     if (FAILED(hr))
163     {
164         CoTaskMemFree(pTransformFilter->ppPins);
165         BaseFilterImpl_Release((IBaseFilter*)pTransformFilter);
166     }
167
168     return hr;
169 }
170
171 HRESULT TransformFilter_Construct(const IBaseFilterVtbl *pVtbl, LONG filter_size, const CLSID* pClsid, const TransformFilterFuncTable* pFuncsTable, IBaseFilter ** ppTransformFilter)
172 {
173     TransformFilter* pTf;
174
175     *ppTransformFilter = NULL;
176
177     assert(filter_size >= sizeof(TransformFilter));
178
179     pTf = CoTaskMemAlloc(filter_size);
180     ZeroMemory(pTf, filter_size);
181
182     if (!pTf)
183         return E_OUTOFMEMORY;
184
185     if (SUCCEEDED(TransformFilter_Init(pVtbl, pClsid, pFuncsTable, pTf)))
186     {
187         *ppTransformFilter = (IBaseFilter*)(&pTf->filter.lpVtbl);
188         return S_OK;
189     }
190
191     CoTaskMemFree(pTf);
192     return E_FAIL;
193 }
194
195 HRESULT WINAPI TransformFilterImpl_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
196 {
197     HRESULT hr;
198     TransformFilter *This = (TransformFilter *)iface;
199     TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
200
201     hr = BaseFilterImpl_QueryInterface(iface, riid, ppv);
202
203     if (FAILED(hr) && (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow)))
204         FIXME("No interface for %s!\n", debugstr_guid(riid));
205
206     return hr;
207 }
208
209 ULONG WINAPI TransformFilterImpl_Release(IBaseFilter * iface)
210 {
211     TransformFilter *This = (TransformFilter *)iface;
212     ULONG refCount = BaseFilterImpl_Release(iface);
213
214     TRACE("(%p/%p)->() Release from %d\n", This, iface, refCount + 1);
215
216     if (!refCount)
217     {
218         ULONG i;
219
220         for (i = 0; i < This->npins; i++)
221         {
222             IPin *pConnectedTo;
223
224             if (SUCCEEDED(IPin_ConnectedTo(This->ppPins[i], &pConnectedTo)))
225             {
226                 IPin_Disconnect(pConnectedTo);
227                 IPin_Release(pConnectedTo);
228             }
229             IPin_Disconnect(This->ppPins[i]);
230
231             IPin_Release(This->ppPins[i]);
232         }
233
234         CoTaskMemFree(This->ppPins);
235
236         TRACE("Destroying transform filter\n");
237         FreeMediaType(&This->pmt);
238         CoTaskMemFree(This);
239
240         return 0;
241     }
242     else
243         return refCount;
244 }
245
246 /** IMediaFilter methods **/
247
248 HRESULT WINAPI TransformFilterImpl_Stop(IBaseFilter * iface)
249 {
250     TransformFilter *This = (TransformFilter *)iface;
251     HRESULT hr = S_OK;
252
253     TRACE("(%p/%p)\n", This, iface);
254
255     EnterCriticalSection(&This->filter.csFilter);
256     {
257         This->filter.state = State_Stopped;
258         if (This->pFuncsTable->pfnStopStreaming)
259             hr = This->pFuncsTable->pfnStopStreaming(This);
260     }
261     LeaveCriticalSection(&This->filter.csFilter);
262
263     return hr;
264 }
265
266 HRESULT WINAPI TransformFilterImpl_Pause(IBaseFilter * iface)
267 {
268     TransformFilter *This = (TransformFilter *)iface;
269     HRESULT hr;
270
271     TRACE("(%p/%p)->()\n", This, iface);
272
273     EnterCriticalSection(&This->filter.csFilter);
274     {
275         if (This->filter.state == State_Stopped)
276             hr = IBaseFilter_Run(iface, -1);
277         else
278             hr = S_OK;
279
280         if (SUCCEEDED(hr))
281             This->filter.state = State_Paused;
282     }
283     LeaveCriticalSection(&This->filter.csFilter);
284
285     return hr;
286 }
287
288 HRESULT WINAPI TransformFilterImpl_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
289 {
290     HRESULT hr = S_OK;
291     TransformFilter *This = (TransformFilter *)iface;
292
293     TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(tStart));
294
295     EnterCriticalSection(&This->filter.csFilter);
296     {
297         if (This->filter.state == State_Stopped)
298         {
299             ((BaseInputPin *)This->ppPins[0])->end_of_stream = 0;
300             if (This->pFuncsTable->pfnStartStreaming)
301                 hr = This->pFuncsTable->pfnStartStreaming(This);
302             if (SUCCEEDED(hr))
303                 hr = BaseOutputPinImpl_Active((BaseOutputPin *)This->ppPins[1]);
304         }
305
306         if (SUCCEEDED(hr))
307         {
308             This->filter.rtStreamStart = tStart;
309             This->filter.state = State_Running;
310         }
311     }
312     LeaveCriticalSection(&This->filter.csFilter);
313
314     return hr;
315 }
316
317 /** IBaseFilter implementation **/
318
319 HRESULT WINAPI TransformFilterImpl_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
320 {
321     TransformFilter *This = (TransformFilter *)iface;
322
323     TRACE("(%p/%p)->(%p,%p)\n", This, iface, debugstr_w(Id), ppPin);
324
325     return E_NOTIMPL;
326 }
327
328 static const IBaseFilterVtbl TransformFilter_Vtbl =
329 {
330     TransformFilterImpl_QueryInterface,
331     BaseFilterImpl_AddRef,
332     TransformFilterImpl_Release,
333     BaseFilterImpl_GetClassID,
334     TransformFilterImpl_Stop,
335     TransformFilterImpl_Pause,
336     TransformFilterImpl_Run,
337     BaseFilterImpl_GetState,
338     BaseFilterImpl_SetSyncSource,
339     BaseFilterImpl_GetSyncSource,
340     BaseFilterImpl_EnumPins,
341     TransformFilterImpl_FindPin,
342     BaseFilterImpl_QueryFilterInfo,
343     BaseFilterImpl_JoinFilterGraph,
344     BaseFilterImpl_QueryVendorInfo
345 };
346
347 static HRESULT WINAPI TransformFilter_InputPin_EndOfStream(IPin * iface)
348 {
349     BaseInputPin* This = (BaseInputPin*) iface;
350     TransformFilter* pTransform;
351     IPin* ppin;
352     HRESULT hr;
353
354     TRACE("(%p)->()\n", iface);
355
356     /* Since we process samples synchronously, just forward notification downstream */
357     pTransform = (TransformFilter*)This->pin.pinInfo.pFilter;
358     if (!pTransform)
359         hr = E_FAIL;
360     else
361         hr = IPin_ConnectedTo(pTransform->ppPins[1], &ppin);
362     if (SUCCEEDED(hr))
363     {
364         hr = IPin_EndOfStream(ppin);
365         IPin_Release(ppin);
366     }
367
368     if (FAILED(hr))
369         ERR("%x\n", hr);
370     return hr;
371 }
372
373 static HRESULT WINAPI TransformFilter_InputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
374 {
375     BaseInputPin* This = (BaseInputPin*) iface;
376     TransformFilter* pTransform;
377     HRESULT hr = S_OK;
378
379     TRACE("(%p)->(%p, %p)\n", iface, pReceivePin, pmt);
380
381     pTransform = (TransformFilter*)This->pin.pinInfo.pFilter;
382
383     if (pTransform->pFuncsTable->pfnSetMediaType)
384         hr = pTransform->pFuncsTable->pfnSetMediaType(pTransform, PINDIR_INPUT, pmt);
385
386     if (SUCCEEDED(hr) && pTransform->pFuncsTable->pfnCompleteConnect)
387         hr = pTransform->pFuncsTable->pfnCompleteConnect(pTransform, PINDIR_INPUT, pReceivePin);
388
389     if (SUCCEEDED(hr))
390     {
391         hr = BaseInputPinImpl_ReceiveConnection(iface, pReceivePin, pmt);
392         if (FAILED(hr) && pTransform->pFuncsTable->pfnBreakConnect)
393             pTransform->pFuncsTable->pfnBreakConnect(pTransform, PINDIR_INPUT);
394     }
395
396     return hr;
397 }
398
399 static HRESULT WINAPI TransformFilter_InputPin_Disconnect(IPin * iface)
400 {
401     BaseInputPin* This = (BaseInputPin*) iface;
402     TransformFilter* pTransform;
403
404     TRACE("(%p)->()\n", iface);
405
406     pTransform = (TransformFilter*)This->pin.pinInfo.pFilter;
407     if (pTransform->pFuncsTable->pfnBreakConnect)
408         pTransform->pFuncsTable->pfnBreakConnect(pTransform, PINDIR_INPUT);
409
410     return BasePinImpl_Disconnect(iface);
411 }
412
413 static HRESULT WINAPI TransformFilter_InputPin_BeginFlush(IPin * iface)
414 {
415     BaseInputPin* This = (BaseInputPin*) iface;
416     TransformFilter* pTransform;
417     HRESULT hr = S_OK;
418
419     TRACE("(%p)->()\n", iface);
420
421     pTransform = (TransformFilter*)This->pin.pinInfo.pFilter;
422     EnterCriticalSection(&pTransform->filter.csFilter);
423     if (pTransform->pFuncsTable->pfnBeginFlush)
424         hr = pTransform->pFuncsTable->pfnBeginFlush(pTransform);
425     if (SUCCEEDED(hr))
426         hr = BaseInputPinImpl_BeginFlush(iface);
427     LeaveCriticalSection(&pTransform->filter.csFilter);
428     return hr;
429 }
430
431 static HRESULT WINAPI TransformFilter_InputPin_EndFlush(IPin * iface)
432 {
433     BaseInputPin* This = (BaseInputPin*) iface;
434     TransformFilter* pTransform;
435     HRESULT hr = S_OK;
436
437     TRACE("(%p)->()\n", iface);
438
439     pTransform = (TransformFilter*)This->pin.pinInfo.pFilter;
440     EnterCriticalSection(&pTransform->filter.csFilter);
441     if (pTransform->pFuncsTable->pfnEndFlush)
442         hr = pTransform->pFuncsTable->pfnEndFlush(pTransform);
443     if (SUCCEEDED(hr))
444         hr = BaseInputPinImpl_EndFlush(iface);
445     LeaveCriticalSection(&pTransform->filter.csFilter);
446     return hr;
447 }
448
449 static HRESULT WINAPI TransformFilter_InputPin_NewSegment(IPin * iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
450 {
451     BaseInputPin* This = (BaseInputPin*) iface;
452     TransformFilter* pTransform;
453     HRESULT hr = S_OK;
454
455     TRACE("(%p)->()\n", iface);
456
457     pTransform = (TransformFilter*)This->pin.pinInfo.pFilter;
458     EnterCriticalSection(&pTransform->filter.csFilter);
459     if (pTransform->pFuncsTable->pfnNewSegment)
460         hr = pTransform->pFuncsTable->pfnNewSegment(pTransform, tStart, tStop, dRate);
461     if (SUCCEEDED(hr))
462         hr = BaseInputPinImpl_NewSegment(iface, tStart, tStop, dRate);
463     LeaveCriticalSection(&pTransform->filter.csFilter);
464     return hr;
465 }
466
467 static const IPinVtbl TransformFilter_InputPin_Vtbl =
468 {
469     BaseInputPinImpl_QueryInterface,
470     BasePinImpl_AddRef,
471     BaseInputPinImpl_Release,
472     BaseInputPinImpl_Connect,
473     TransformFilter_InputPin_ReceiveConnection,
474     TransformFilter_InputPin_Disconnect,
475     BasePinImpl_ConnectedTo,
476     BasePinImpl_ConnectionMediaType,
477     BasePinImpl_QueryPinInfo,
478     BasePinImpl_QueryDirection,
479     BasePinImpl_QueryId,
480     BaseInputPinImpl_QueryAccept,
481     BasePinImpl_EnumMediaTypes,
482     BasePinImpl_QueryInternalConnections,
483     TransformFilter_InputPin_EndOfStream,
484     TransformFilter_InputPin_BeginFlush,
485     TransformFilter_InputPin_EndFlush,
486     TransformFilter_InputPin_NewSegment
487 };
488
489 static HRESULT WINAPI TransformFilter_Output_GetMediaType(IPin *iface, int iPosition, AM_MEDIA_TYPE *pmt)
490 {
491     BasePin *This = (BasePin *)iface;
492     TransformFilter *pTransform = (TransformFilter *)This->pinInfo.pFilter;
493
494     if (iPosition < 0)
495         return E_INVALIDARG;
496     if (iPosition > 0)
497         return VFW_S_NO_MORE_ITEMS;
498     CopyMediaType(pmt, &pTransform->pmt);
499     return S_OK;
500 }
501
502 static HRESULT WINAPI TransformFilter_Output_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
503 {
504     BasePin *This = (BasePin *)iface;
505     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
506
507     return EnumMediaTypes_Construct(iface, TransformFilter_Output_GetMediaType, BasePinImpl_GetMediaTypeVersion, ppEnum);
508 }
509
510 static const IPinVtbl TransformFilter_OutputPin_Vtbl =
511 {
512     BaseOutputPinImpl_QueryInterface,
513     BasePinImpl_AddRef,
514     BaseOutputPinImpl_Release,
515     BaseOutputPinImpl_Connect,
516     BaseOutputPinImpl_ReceiveConnection,
517     BaseOutputPinImpl_Disconnect,
518     BasePinImpl_ConnectedTo,
519     BasePinImpl_ConnectionMediaType,
520     BasePinImpl_QueryPinInfo,
521     BasePinImpl_QueryDirection,
522     BasePinImpl_QueryId,
523     TransformFilter_Output_QueryAccept,
524     TransformFilter_Output_EnumMediaTypes,
525     BasePinImpl_QueryInternalConnections,
526     BaseOutputPinImpl_EndOfStream,
527     BaseOutputPinImpl_BeginFlush,
528     BaseOutputPinImpl_EndFlush,
529     BaseOutputPinImpl_NewSegment
530 };