secur32: Avoid some code duplication.
[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 (%lx)\n", hr);
71         return hr;
72     }
73
74     hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
75     if (FAILED(hr))
76         ERR("Cannot get sample time (%lx)\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     This->pFuncsTable->pfnProcessSampleData(This, pbSrcStream, cbSrcStream);
96
97     return S_OK;
98 }
99
100 static HRESULT TransformFilter_Input_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
101 {
102     TransformFilterImpl* This = (TransformFilterImpl*)iface;
103     TRACE("%p\n", iface);
104     dump_AM_MEDIA_TYPE(pmt);
105
106     return This->pFuncsTable->pfnConnectInput(This, pmt);
107 }
108
109
110 static HRESULT TransformFilter_Output_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
111 {
112     TransformFilterImpl* pTransformFilter = (TransformFilterImpl*)iface;
113     AM_MEDIA_TYPE* outpmt = &((OutputPin*)pTransformFilter->ppPins[1])->pin.mtCurrent;
114     TRACE("%p\n", iface);
115
116     if (IsEqualIID(&pmt->majortype, &outpmt->majortype) && IsEqualIID(&pmt->subtype, &outpmt->subtype))
117         return S_OK;
118     return S_FALSE;
119 }
120
121 static HRESULT TransformFilter_InputPin_Construct(const PIN_INFO * pPinInfo, SAMPLEPROC pSampleProc, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
122 {
123     InputPin * pPinImpl;
124
125     *ppPin = NULL;
126
127     if (pPinInfo->dir != PINDIR_INPUT)
128     {
129         ERR("Pin direction(%x) != PINDIR_INPUT\n", pPinInfo->dir);
130         return E_INVALIDARG;
131     }
132
133     pPinImpl = CoTaskMemAlloc(sizeof(*pPinImpl));
134
135     if (!pPinImpl)
136         return E_OUTOFMEMORY;
137
138     if (SUCCEEDED(InputPin_Init(pPinInfo, pSampleProc, pUserData, pQueryAccept, pCritSec, pPinImpl)))
139     {
140         pPinImpl->pin.lpVtbl = &TransformFilter_InputPin_Vtbl;
141         pPinImpl->lpVtblMemInput = &MemInputPin_Vtbl;
142
143         *ppPin = (IPin *)(&pPinImpl->pin.lpVtbl);
144         return S_OK;
145     }
146     return E_FAIL;
147 }
148
149 static HRESULT TransformFilter_OutputPin_Construct(const PIN_INFO * pPinInfo, ALLOCATOR_PROPERTIES *props, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
150 {
151     OutputPin * pPinImpl;
152
153     *ppPin = NULL;
154
155     if (pPinInfo->dir != PINDIR_OUTPUT)
156     {
157         ERR("Pin direction(%x) != PINDIR_OUTPUT\n", pPinInfo->dir);
158         return E_INVALIDARG;
159     }
160
161     pPinImpl = CoTaskMemAlloc(sizeof(*pPinImpl));
162
163     if (!pPinImpl)
164         return E_OUTOFMEMORY;
165
166     if (SUCCEEDED(OutputPin_Init(pPinInfo, props, pUserData, pQueryAccept, pCritSec, pPinImpl)))
167     {
168         pPinImpl->pin.lpVtbl = &TransformFilter_OutputPin_Vtbl;
169
170         *ppPin = (IPin *)(&pPinImpl->pin.lpVtbl);
171         return S_OK;
172     }
173     return E_FAIL;
174 }
175
176 HRESULT TransformFilter_Create(TransformFilterImpl* pTransformFilter, const CLSID* pClsid, TransformFuncsTable* pFuncsTable)
177 {
178     HRESULT hr;
179     PIN_INFO piInput;
180     PIN_INFO piOutput;
181
182     /* pTransformFilter is already allocated */
183     pTransformFilter->clsid = *pClsid;
184     pTransformFilter->pFuncsTable = pFuncsTable;
185
186     pTransformFilter->lpVtbl = &TransformFilter_Vtbl;
187
188     pTransformFilter->refCount = 1;
189     InitializeCriticalSection(&pTransformFilter->csFilter);
190     pTransformFilter->state = State_Stopped;
191     pTransformFilter->pClock = NULL;
192     ZeroMemory(&pTransformFilter->filterInfo, sizeof(FILTER_INFO));
193
194     pTransformFilter->ppPins = CoTaskMemAlloc(2 * sizeof(IPin *));
195
196     /* construct input pin */
197     piInput.dir = PINDIR_INPUT;
198     piInput.pFilter = (IBaseFilter *)pTransformFilter;
199     lstrcpynW(piInput.achName, wcsInputPinName, sizeof(piInput.achName) / sizeof(piInput.achName[0]));
200     piOutput.dir = PINDIR_OUTPUT;
201     piOutput.pFilter = (IBaseFilter *)pTransformFilter;
202     lstrcpynW(piOutput.achName, wcsOutputPinName, sizeof(piOutput.achName) / sizeof(piOutput.achName[0]));
203
204     hr = TransformFilter_InputPin_Construct(&piInput, TransformFilter_Sample, pTransformFilter, TransformFilter_Input_QueryAccept, &pTransformFilter->csFilter, &pTransformFilter->ppPins[0]);
205
206     if (SUCCEEDED(hr))
207     {
208         ALLOCATOR_PROPERTIES props;
209         props.cbAlign = 1;
210         props.cbPrefix = 0;
211         props.cbBuffer = 0; /* Will be updated at connection time */
212         props.cBuffers = 2;
213
214         hr = TransformFilter_OutputPin_Construct(&piOutput, &props, pTransformFilter, TransformFilter_Output_QueryAccept, &pTransformFilter->csFilter, &pTransformFilter->ppPins[1]);
215
216         if (FAILED(hr))
217             ERR("Cannot create output pin (%lx)\n", hr);
218     }
219     else
220     {
221         CoTaskMemFree(pTransformFilter->ppPins);
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 %ld\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 %ld\n", This, iface, refCount + 1);
272
273     if (!refCount)
274     {
275         ULONG i;
276
277         DeleteCriticalSection(&This->csFilter);
278
279         if (This->pClock)
280             IReferenceClock_Release(This->pClock);
281
282         for (i = 0; i < 2; i++)
283             IPin_Release(This->ppPins[i]);
284
285         HeapFree(GetProcessHeap(), 0, This->ppPins);
286         This->lpVtbl = NULL;
287
288         This->pFuncsTable->pfnCleanup(This);
289
290         TRACE("Destroying transform filter\n");
291         CoTaskMemFree(This);
292
293         return 0;
294     }
295     else
296         return refCount;
297 }
298
299 /** IPersist methods **/
300
301 static HRESULT WINAPI TransformFilter_GetClassID(IBaseFilter * iface, CLSID * pClsid)
302 {
303     TransformFilterImpl *This = (TransformFilterImpl *)iface;
304
305     TRACE("(%p/%p)->(%p)\n", This, iface, pClsid);
306
307     *pClsid = This->clsid;
308
309     return S_OK;
310 }
311
312 /** IMediaFilter methods **/
313
314 static HRESULT WINAPI TransformFilter_Stop(IBaseFilter * iface)
315 {
316     TransformFilterImpl *This = (TransformFilterImpl *)iface;
317
318     TRACE("(%p/%p)\n", This, iface);
319
320     EnterCriticalSection(&This->csFilter);
321     {
322         This->state = State_Stopped;
323         if (This->pFuncsTable->pfnProcessEnd)
324             This->pFuncsTable->pfnProcessEnd(This);
325     }
326     LeaveCriticalSection(&This->csFilter);
327
328     return S_OK;
329 }
330
331 static HRESULT WINAPI TransformFilter_Pause(IBaseFilter * iface)
332 {
333     TransformFilterImpl *This = (TransformFilterImpl *)iface;
334
335     TRACE("(%p/%p)->()\n", This, iface);
336
337     EnterCriticalSection(&This->csFilter);
338     {
339         This->state = State_Paused;
340     }
341     LeaveCriticalSection(&This->csFilter);
342
343     return S_OK;
344 }
345
346 static HRESULT WINAPI TransformFilter_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
347 {
348     HRESULT hr = S_OK;
349     TransformFilterImpl *This = (TransformFilterImpl *)iface;
350
351     TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(tStart));
352
353     EnterCriticalSection(&This->csFilter);
354     {
355         This->rtStreamStart = tStart;
356         This->state = State_Running;
357         OutputPin_CommitAllocator((OutputPin *)This->ppPins[1]);
358         if (This->pFuncsTable->pfnProcessBegin)
359             This->pFuncsTable->pfnProcessBegin(This);
360     }
361     LeaveCriticalSection(&This->csFilter);
362
363     return hr;
364 }
365
366 static HRESULT WINAPI TransformFilter_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
367 {
368     TransformFilterImpl *This = (TransformFilterImpl *)iface;
369
370     TRACE("(%p/%p)->(%ld, %p)\n", This, iface, dwMilliSecsTimeout, pState);
371
372     EnterCriticalSection(&This->csFilter);
373     {
374         *pState = This->state;
375     }
376     LeaveCriticalSection(&This->csFilter);
377
378     return S_OK;
379 }
380
381 static HRESULT WINAPI TransformFilter_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock)
382 {
383     TransformFilterImpl *This = (TransformFilterImpl *)iface;
384
385     TRACE("(%p/%p)->(%p)\n", This, iface, pClock);
386
387     EnterCriticalSection(&This->csFilter);
388     {
389         if (This->pClock)
390             IReferenceClock_Release(This->pClock);
391         This->pClock = pClock;
392         if (This->pClock)
393             IReferenceClock_AddRef(This->pClock);
394     }
395     LeaveCriticalSection(&This->csFilter);
396
397     return S_OK;
398 }
399
400 static HRESULT WINAPI TransformFilter_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock)
401 {
402     TransformFilterImpl *This = (TransformFilterImpl *)iface;
403
404     TRACE("(%p/%p)->(%p)\n", This, iface, ppClock);
405
406     EnterCriticalSection(&This->csFilter);
407     {
408         *ppClock = This->pClock;
409         IReferenceClock_AddRef(This->pClock);
410     }
411     LeaveCriticalSection(&This->csFilter);
412
413     return S_OK;
414 }
415
416 /** IBaseFilter implementation **/
417
418 static HRESULT WINAPI TransformFilter_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum)
419 {
420     ENUMPINDETAILS epd;
421     TransformFilterImpl *This = (TransformFilterImpl *)iface;
422
423     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
424
425     epd.cPins = 2; /* input and output pins */
426     epd.ppPins = This->ppPins;
427     return IEnumPinsImpl_Construct(&epd, ppEnum);
428 }
429
430 static HRESULT WINAPI TransformFilter_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
431 {
432     TransformFilterImpl *This = (TransformFilterImpl *)iface;
433
434     TRACE("(%p/%p)->(%p,%p)\n", This, iface, debugstr_w(Id), ppPin);
435
436     return E_NOTIMPL;
437 }
438
439 static HRESULT WINAPI TransformFilter_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo)
440 {
441     TransformFilterImpl *This = (TransformFilterImpl *)iface;
442
443     TRACE("(%p/%p)->(%p)\n", This, iface, pInfo);
444
445     strcpyW(pInfo->achName, This->filterInfo.achName);
446     pInfo->pGraph = This->filterInfo.pGraph;
447
448     if (pInfo->pGraph)
449         IFilterGraph_AddRef(pInfo->pGraph);
450
451     return S_OK;
452 }
453
454 static HRESULT WINAPI TransformFilter_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName)
455 {
456     HRESULT hr = S_OK;
457     TransformFilterImpl *This = (TransformFilterImpl *)iface;
458
459     TRACE("(%p/%p)->(%p, %s)\n", This, iface, pGraph, debugstr_w(pName));
460
461     EnterCriticalSection(&This->csFilter);
462     {
463         if (pName)
464             strcpyW(This->filterInfo.achName, pName);
465         else
466             *This->filterInfo.achName = '\0';
467         This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */
468     }
469     LeaveCriticalSection(&This->csFilter);
470
471     return hr;
472 }
473
474 static HRESULT WINAPI TransformFilter_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo)
475 {
476     TransformFilterImpl *This = (TransformFilterImpl *)iface;
477     TRACE("(%p/%p)->(%p)\n", This, iface, pVendorInfo);
478     return E_NOTIMPL;
479 }
480
481 static const IBaseFilterVtbl TransformFilter_Vtbl =
482 {
483     TransformFilter_QueryInterface,
484     TransformFilter_AddRef,
485     TransformFilter_Release,
486     TransformFilter_GetClassID,
487     TransformFilter_Stop,
488     TransformFilter_Pause,
489     TransformFilter_Run,
490     TransformFilter_GetState,
491     TransformFilter_SetSyncSource,
492     TransformFilter_GetSyncSource,
493     TransformFilter_EnumPins,
494     TransformFilter_FindPin,
495     TransformFilter_QueryFilterInfo,
496     TransformFilter_JoinFilterGraph,
497     TransformFilter_QueryVendorInfo
498 };
499
500 static HRESULT WINAPI TransformFilter_InputPin_EndOfStream(IPin * iface)
501 {
502     InputPin* This = (InputPin*) iface;
503     TransformFilterImpl* pTransform;
504     IPin* ppin;
505     HRESULT hr;
506     
507     TRACE("(%p)->()\n", iface);
508
509     /* Since we process samples synchronously, just forward notification downstream */
510     pTransform = (TransformFilterImpl*)This->pin.pinInfo.pFilter;
511     if (!pTransform)
512         hr = E_FAIL;
513     else
514         hr = IPin_ConnectedTo(pTransform->ppPins[1], &ppin);
515     if (SUCCEEDED(hr))
516     {
517         hr = IPin_EndOfStream(ppin);
518         IPin_Release(ppin);
519     }
520
521     if (FAILED(hr))
522         ERR("%lx\n", hr);
523     return hr;
524 }
525
526 static const IPinVtbl TransformFilter_InputPin_Vtbl = 
527 {
528     InputPin_QueryInterface,
529     IPinImpl_AddRef,
530     InputPin_Release,
531     InputPin_Connect,
532     InputPin_ReceiveConnection,
533     IPinImpl_Disconnect,
534     IPinImpl_ConnectedTo,
535     IPinImpl_ConnectionMediaType,
536     IPinImpl_QueryPinInfo,
537     IPinImpl_QueryDirection,
538     IPinImpl_QueryId,
539     IPinImpl_QueryAccept,
540     IPinImpl_EnumMediaTypes,
541     IPinImpl_QueryInternalConnections,
542     TransformFilter_InputPin_EndOfStream,
543     InputPin_BeginFlush,
544     InputPin_EndFlush,
545     InputPin_NewSegment
546 };
547
548 static HRESULT WINAPI TransformFilter_Output_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
549 {
550     IPinImpl *This = (IPinImpl *)iface;
551     ENUMMEDIADETAILS emd;
552
553     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
554
555     emd.cMediaTypes = 1;
556     emd.pMediaTypes = &This->mtCurrent;
557
558     return IEnumMediaTypesImpl_Construct(&emd, ppEnum);
559 }
560
561 static HRESULT WINAPI TransformFilter_Output_Disconnect(IPin * iface)
562 {
563     OutputPin* This = (OutputPin*)iface;
564     HRESULT hr;
565     TransformFilterImpl* pTransformFilter = (TransformFilterImpl*)This->pin.pinInfo.pFilter;
566
567     TRACE("(%p/%p)->()\n", This, iface);
568
569     hr = OutputPin_Disconnect(iface);
570
571     if (hr == S_OK)
572     {
573         pTransformFilter->pFuncsTable->pfnCleanup(pTransformFilter);
574     }
575
576     return hr;
577 }
578
579 static const IPinVtbl TransformFilter_OutputPin_Vtbl = 
580 {
581     OutputPin_QueryInterface,
582     IPinImpl_AddRef,
583     OutputPin_Release,
584     OutputPin_Connect,
585     OutputPin_ReceiveConnection,
586     TransformFilter_Output_Disconnect,
587     IPinImpl_ConnectedTo,
588     IPinImpl_ConnectionMediaType,
589     IPinImpl_QueryPinInfo,
590     IPinImpl_QueryDirection,
591     IPinImpl_QueryId,
592     IPinImpl_QueryAccept,
593     TransformFilter_Output_EnumMediaTypes,
594     IPinImpl_QueryInternalConnections,
595     OutputPin_EndOfStream,
596     OutputPin_BeginFlush,
597     OutputPin_EndFlush,
598     OutputPin_NewSegment
599 };
600
601 static const IMemInputPinVtbl MemInputPin_Vtbl = 
602 {
603     MemInputPin_QueryInterface,
604     MemInputPin_AddRef,
605     MemInputPin_Release,
606     MemInputPin_GetAllocator,
607     MemInputPin_NotifyAllocator,
608     MemInputPin_GetAllocatorRequirements,
609     MemInputPin_Receive,
610     MemInputPin_ReceiveMultiple,
611     MemInputPin_ReceiveCanBlock
612 };