Add DDCAPS_OVERLAY and DDCAPS_OVERLAYSTRETCH to GetCaps().
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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                 DPRINTF("\n");
89             DPRINTF("%02x ", pbSrcStream[i]);
90         }
91         DPRINTF("\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 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     strncpyW(piInput.achName, wcsInputPinName, sizeof(piInput.achName) / sizeof(piInput.achName[0]));
200     piOutput.dir = PINDIR_OUTPUT;
201     piOutput.pFilter = (IBaseFilter *)pTransformFilter;
202     strncpyW(piOutput.achName, wcsOutputPinName, sizeof(piOutput.achName) / sizeof(piOutput.achName[0]));
203
204     hr = TransformFilter_InputPin_Construct(&piInput, TransformFilter_Sample, (LPVOID)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, NULL, 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         IReferenceClock_Release(This->pClock);
279
280         for (i = 0; i < 2; i++)
281             IPin_Release(This->ppPins[i]);
282
283         HeapFree(GetProcessHeap(), 0, This->ppPins);
284         This->lpVtbl = NULL;
285
286         This->pFuncsTable->pfnCleanup(This);
287
288         TRACE("Destroying transform filter\n");
289         CoTaskMemFree(This);
290
291         return 0;
292     }
293     else
294         return refCount;
295 }
296
297 /** IPersist methods **/
298
299 static HRESULT WINAPI TransformFilter_GetClassID(IBaseFilter * iface, CLSID * pClsid)
300 {
301     TransformFilterImpl *This = (TransformFilterImpl *)iface;
302
303     TRACE("(%p/%p)->(%p)\n", This, iface, pClsid);
304
305     *pClsid = This->clsid;
306
307     return S_OK;
308 }
309
310 /** IMediaFilter methods **/
311
312 static HRESULT WINAPI TransformFilter_Stop(IBaseFilter * iface)
313 {
314     TransformFilterImpl *This = (TransformFilterImpl *)iface;
315
316     TRACE("(%p/%p)\n", This, iface);
317
318     EnterCriticalSection(&This->csFilter);
319     {
320         This->state = State_Stopped;
321         if (This->pFuncsTable->pfnProcessEnd)
322             This->pFuncsTable->pfnProcessEnd(This);
323     }
324     LeaveCriticalSection(&This->csFilter);
325
326     return S_OK;
327 }
328
329 static HRESULT WINAPI TransformFilter_Pause(IBaseFilter * iface)
330 {
331     TransformFilterImpl *This = (TransformFilterImpl *)iface;
332
333     TRACE("(%p/%p)->()\n", This, iface);
334
335     EnterCriticalSection(&This->csFilter);
336     {
337         This->state = State_Paused;
338     }
339     LeaveCriticalSection(&This->csFilter);
340
341     return S_OK;
342 }
343
344 static HRESULT WINAPI TransformFilter_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
345 {
346     HRESULT hr = S_OK;
347     TransformFilterImpl *This = (TransformFilterImpl *)iface;
348
349     TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(tStart));
350
351     EnterCriticalSection(&This->csFilter);
352     {
353         This->rtStreamStart = tStart;
354         This->state = State_Running;
355         OutputPin_CommitAllocator((OutputPin *)This->ppPins[1]);
356         if (This->pFuncsTable->pfnProcessBegin)
357             This->pFuncsTable->pfnProcessBegin(This);
358     }
359     LeaveCriticalSection(&This->csFilter);
360
361     return hr;
362 }
363
364 static HRESULT WINAPI TransformFilter_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
365 {
366     TransformFilterImpl *This = (TransformFilterImpl *)iface;
367
368     TRACE("(%p/%p)->(%ld, %p)\n", This, iface, dwMilliSecsTimeout, pState);
369
370     EnterCriticalSection(&This->csFilter);
371     {
372         *pState = This->state;
373     }
374     LeaveCriticalSection(&This->csFilter);
375
376     return S_OK;
377 }
378
379 static HRESULT WINAPI TransformFilter_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock)
380 {
381     TransformFilterImpl *This = (TransformFilterImpl *)iface;
382
383     TRACE("(%p/%p)->(%p)\n", This, iface, pClock);
384
385     EnterCriticalSection(&This->csFilter);
386     {
387         if (This->pClock)
388             IReferenceClock_Release(This->pClock);
389         This->pClock = pClock;
390         if (This->pClock)
391             IReferenceClock_AddRef(This->pClock);
392     }
393     LeaveCriticalSection(&This->csFilter);
394
395     return S_OK;
396 }
397
398 static HRESULT WINAPI TransformFilter_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock)
399 {
400     TransformFilterImpl *This = (TransformFilterImpl *)iface;
401
402     TRACE("(%p/%p)->(%p)\n", This, iface, ppClock);
403
404     EnterCriticalSection(&This->csFilter);
405     {
406         *ppClock = This->pClock;
407         IReferenceClock_AddRef(This->pClock);
408     }
409     LeaveCriticalSection(&This->csFilter);
410
411     return S_OK;
412 }
413
414 /** IBaseFilter implementation **/
415
416 static HRESULT WINAPI TransformFilter_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum)
417 {
418     ENUMPINDETAILS epd;
419     TransformFilterImpl *This = (TransformFilterImpl *)iface;
420
421     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
422
423     epd.cPins = 2; /* input and output pins */
424     epd.ppPins = This->ppPins;
425     return IEnumPinsImpl_Construct(&epd, ppEnum);
426 }
427
428 static HRESULT WINAPI TransformFilter_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
429 {
430     TransformFilterImpl *This = (TransformFilterImpl *)iface;
431
432     TRACE("(%p/%p)->(%p,%p)\n", This, iface, debugstr_w(Id), ppPin);
433
434     return E_NOTIMPL;
435 }
436
437 static HRESULT WINAPI TransformFilter_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo)
438 {
439     TransformFilterImpl *This = (TransformFilterImpl *)iface;
440
441     TRACE("(%p/%p)->(%p)\n", This, iface, pInfo);
442
443     strcpyW(pInfo->achName, This->filterInfo.achName);
444     pInfo->pGraph = This->filterInfo.pGraph;
445
446     if (pInfo->pGraph)
447         IFilterGraph_AddRef(pInfo->pGraph);
448
449     return S_OK;
450 }
451
452 static HRESULT WINAPI TransformFilter_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName)
453 {
454     HRESULT hr = S_OK;
455     TransformFilterImpl *This = (TransformFilterImpl *)iface;
456
457     TRACE("(%p/%p)->(%p, %s)\n", This, iface, pGraph, debugstr_w(pName));
458
459     EnterCriticalSection(&This->csFilter);
460     {
461         if (pName)
462             strcpyW(This->filterInfo.achName, pName);
463         else
464             *This->filterInfo.achName = '\0';
465         This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */
466     }
467     LeaveCriticalSection(&This->csFilter);
468
469     return hr;
470 }
471
472 static HRESULT WINAPI TransformFilter_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo)
473 {
474     TransformFilterImpl *This = (TransformFilterImpl *)iface;
475     TRACE("(%p/%p)->(%p)\n", This, iface, pVendorInfo);
476     return E_NOTIMPL;
477 }
478
479 static const IBaseFilterVtbl TransformFilter_Vtbl =
480 {
481     TransformFilter_QueryInterface,
482     TransformFilter_AddRef,
483     TransformFilter_Release,
484     TransformFilter_GetClassID,
485     TransformFilter_Stop,
486     TransformFilter_Pause,
487     TransformFilter_Run,
488     TransformFilter_GetState,
489     TransformFilter_SetSyncSource,
490     TransformFilter_GetSyncSource,
491     TransformFilter_EnumPins,
492     TransformFilter_FindPin,
493     TransformFilter_QueryFilterInfo,
494     TransformFilter_JoinFilterGraph,
495     TransformFilter_QueryVendorInfo
496 };
497
498 HRESULT WINAPI TransformFilter_InputPin_EndOfStream(IPin * iface)
499 {
500     InputPin* This = (InputPin*) iface;
501     TransformFilterImpl* pTransform;
502     IPin* ppin;
503     HRESULT hr;
504     
505     TRACE("(%p)->()\n", iface);
506
507     /* Since we process samples synchronously, just forward notification downstream */
508     pTransform = (TransformFilterImpl*)This->pin.pinInfo.pFilter;
509     if (!pTransform)
510         hr = E_FAIL;
511     else
512         hr = IPin_ConnectedTo(pTransform->ppPins[1], &ppin);
513     if (SUCCEEDED(hr))
514     {
515         hr = IPin_EndOfStream(ppin);
516         IPin_Release(ppin);
517     }
518
519     if (FAILED(hr))
520         ERR("%lx\n", hr);
521     return hr;
522 }
523
524 static const IPinVtbl TransformFilter_InputPin_Vtbl = 
525 {
526     InputPin_QueryInterface,
527     IPinImpl_AddRef,
528     InputPin_Release,
529     InputPin_Connect,
530     InputPin_ReceiveConnection,
531     IPinImpl_Disconnect,
532     IPinImpl_ConnectedTo,
533     IPinImpl_ConnectionMediaType,
534     IPinImpl_QueryPinInfo,
535     IPinImpl_QueryDirection,
536     IPinImpl_QueryId,
537     IPinImpl_QueryAccept,
538     IPinImpl_EnumMediaTypes,
539     IPinImpl_QueryInternalConnections,
540     TransformFilter_InputPin_EndOfStream,
541     InputPin_BeginFlush,
542     InputPin_EndFlush,
543     InputPin_NewSegment
544 };
545
546 HRESULT WINAPI TransformFilter_Output_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
547 {
548     IPinImpl *This = (IPinImpl *)iface;
549     ENUMMEDIADETAILS emd;
550
551     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
552
553     emd.cMediaTypes = 1;
554     emd.pMediaTypes = &This->mtCurrent;
555
556     return IEnumMediaTypesImpl_Construct(&emd, ppEnum);
557 }
558
559 HRESULT WINAPI TransformFilter_Output_Disconnect(IPin * iface)
560 {
561     OutputPin* This = (OutputPin*)iface;
562     HRESULT hr;
563     TransformFilterImpl* pTransformFilter = (TransformFilterImpl*)This->pin.pinInfo.pFilter;
564
565     TRACE("(%p/%p)->()\n", This, iface);
566
567     hr = OutputPin_Disconnect(iface);
568
569     if (hr == S_OK)
570     {
571         pTransformFilter->pFuncsTable->pfnCleanup(pTransformFilter);
572     }
573
574     return hr;
575 }
576
577 static const IPinVtbl TransformFilter_OutputPin_Vtbl = 
578 {
579     OutputPin_QueryInterface,
580     IPinImpl_AddRef,
581     OutputPin_Release,
582     OutputPin_Connect,
583     OutputPin_ReceiveConnection,
584     TransformFilter_Output_Disconnect,
585     IPinImpl_ConnectedTo,
586     IPinImpl_ConnectionMediaType,
587     IPinImpl_QueryPinInfo,
588     IPinImpl_QueryDirection,
589     IPinImpl_QueryId,
590     IPinImpl_QueryAccept,
591     TransformFilter_Output_EnumMediaTypes,
592     IPinImpl_QueryInternalConnections,
593     OutputPin_EndOfStream,
594     OutputPin_BeginFlush,
595     OutputPin_EndFlush,
596     OutputPin_NewSegment
597 };
598
599 static const IMemInputPinVtbl MemInputPin_Vtbl = 
600 {
601     MemInputPin_QueryInterface,
602     MemInputPin_AddRef,
603     MemInputPin_Release,
604     MemInputPin_GetAllocator,
605     MemInputPin_NotifyAllocator,
606     MemInputPin_GetAllocatorRequirements,
607     MemInputPin_Receive,
608     MemInputPin_ReceiveMultiple,
609     MemInputPin_ReceiveCanBlock
610 };