advpack: sign-compare fix.
[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     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, const 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->csFilter.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": TransformFilterImpl.csFilter");
191     pTransformFilter->state = State_Stopped;
192     pTransformFilter->pClock = NULL;
193     ZeroMemory(&pTransformFilter->filterInfo, sizeof(FILTER_INFO));
194
195     pTransformFilter->ppPins = CoTaskMemAlloc(2 * sizeof(IPin *));
196
197     /* construct input pin */
198     piInput.dir = PINDIR_INPUT;
199     piInput.pFilter = (IBaseFilter *)pTransformFilter;
200     lstrcpynW(piInput.achName, wcsInputPinName, sizeof(piInput.achName) / sizeof(piInput.achName[0]));
201     piOutput.dir = PINDIR_OUTPUT;
202     piOutput.pFilter = (IBaseFilter *)pTransformFilter;
203     lstrcpynW(piOutput.achName, wcsOutputPinName, sizeof(piOutput.achName) / sizeof(piOutput.achName[0]));
204
205     hr = TransformFilter_InputPin_Construct(&piInput, TransformFilter_Sample, pTransformFilter, TransformFilter_Input_QueryAccept, &pTransformFilter->csFilter, &pTransformFilter->ppPins[0]);
206
207     if (SUCCEEDED(hr))
208     {
209         ALLOCATOR_PROPERTIES props;
210         props.cbAlign = 1;
211         props.cbPrefix = 0;
212         props.cbBuffer = 0; /* Will be updated at connection time */
213         props.cBuffers = 2;
214
215         hr = TransformFilter_OutputPin_Construct(&piOutput, &props, pTransformFilter, TransformFilter_Output_QueryAccept, &pTransformFilter->csFilter, &pTransformFilter->ppPins[1]);
216
217         if (FAILED(hr))
218             ERR("Cannot create output pin (%x)\n", hr);
219     }
220     else
221     {
222         CoTaskMemFree(pTransformFilter->ppPins);
223         pTransformFilter->csFilter.DebugInfo->Spare[0] = 0;
224         DeleteCriticalSection(&pTransformFilter->csFilter);
225         CoTaskMemFree(pTransformFilter);
226     }
227
228     return hr;
229 }
230
231 static HRESULT WINAPI TransformFilter_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
232 {
233     TransformFilterImpl *This = (TransformFilterImpl *)iface;
234     TRACE("(%p/%p)->(%s, %p)\n", This, iface, qzdebugstr_guid(riid), ppv);
235
236     *ppv = NULL;
237
238     if (IsEqualIID(riid, &IID_IUnknown))
239         *ppv = (LPVOID)This;
240     else if (IsEqualIID(riid, &IID_IPersist))
241         *ppv = (LPVOID)This;
242     else if (IsEqualIID(riid, &IID_IMediaFilter))
243         *ppv = (LPVOID)This;
244     else if (IsEqualIID(riid, &IID_IBaseFilter))
245         *ppv = (LPVOID)This;
246
247     if (*ppv)
248     {
249         IUnknown_AddRef((IUnknown *)(*ppv));
250         return S_OK;
251     }
252
253     FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
254
255     return E_NOINTERFACE;
256 }
257
258 static ULONG WINAPI TransformFilter_AddRef(IBaseFilter * iface)
259 {
260     TransformFilterImpl *This = (TransformFilterImpl *)iface;
261     ULONG refCount = InterlockedIncrement(&This->refCount);
262
263     TRACE("(%p/%p)->() AddRef from %d\n", This, iface, refCount - 1);
264
265     return refCount;
266 }
267
268 static ULONG WINAPI TransformFilter_Release(IBaseFilter * iface)
269 {
270     TransformFilterImpl *This = (TransformFilterImpl *)iface;
271     ULONG refCount = InterlockedDecrement(&This->refCount);
272
273     TRACE("(%p/%p)->() Release from %d\n", This, iface, refCount + 1);
274
275     if (!refCount)
276     {
277         ULONG i;
278
279         This->csFilter.DebugInfo->Spare[0] = 0;
280         DeleteCriticalSection(&This->csFilter);
281
282         if (This->pClock)
283             IReferenceClock_Release(This->pClock);
284
285         for (i = 0; i < 2; i++)
286         {
287             IPin *pConnectedTo;
288
289             if (SUCCEEDED(IPin_ConnectedTo(This->ppPins[i], &pConnectedTo)))
290             {
291                 IPin_Disconnect(pConnectedTo);
292                 IPin_Release(pConnectedTo);
293             }
294             IPin_Disconnect(This->ppPins[i]);
295
296             IPin_Release(This->ppPins[i]);
297         }
298
299         CoTaskMemFree(This->ppPins);
300         This->lpVtbl = NULL;
301
302         This->pFuncsTable->pfnCleanup(This);
303
304         TRACE("Destroying transform filter\n");
305         CoTaskMemFree(This);
306
307         return 0;
308     }
309     else
310         return refCount;
311 }
312
313 /** IPersist methods **/
314
315 static HRESULT WINAPI TransformFilter_GetClassID(IBaseFilter * iface, CLSID * pClsid)
316 {
317     TransformFilterImpl *This = (TransformFilterImpl *)iface;
318
319     TRACE("(%p/%p)->(%p)\n", This, iface, pClsid);
320
321     *pClsid = This->clsid;
322
323     return S_OK;
324 }
325
326 /** IMediaFilter methods **/
327
328 static HRESULT WINAPI TransformFilter_Stop(IBaseFilter * iface)
329 {
330     TransformFilterImpl *This = (TransformFilterImpl *)iface;
331
332     TRACE("(%p/%p)\n", This, iface);
333
334     EnterCriticalSection(&This->csFilter);
335     {
336         This->state = State_Stopped;
337         if (This->pFuncsTable->pfnProcessEnd)
338             This->pFuncsTable->pfnProcessEnd(This);
339     }
340     LeaveCriticalSection(&This->csFilter);
341
342     return S_OK;
343 }
344
345 static HRESULT WINAPI TransformFilter_Pause(IBaseFilter * iface)
346 {
347     TransformFilterImpl *This = (TransformFilterImpl *)iface;
348
349     TRACE("(%p/%p)->()\n", This, iface);
350
351     EnterCriticalSection(&This->csFilter);
352     {
353         This->state = State_Paused;
354     }
355     LeaveCriticalSection(&This->csFilter);
356
357     return S_OK;
358 }
359
360 static HRESULT WINAPI TransformFilter_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
361 {
362     HRESULT hr = S_OK;
363     TransformFilterImpl *This = (TransformFilterImpl *)iface;
364
365     TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(tStart));
366
367     EnterCriticalSection(&This->csFilter);
368     {
369         This->rtStreamStart = tStart;
370         This->state = State_Running;
371         OutputPin_CommitAllocator((OutputPin *)This->ppPins[1]);
372         if (This->pFuncsTable->pfnProcessBegin)
373             This->pFuncsTable->pfnProcessBegin(This);
374     }
375     LeaveCriticalSection(&This->csFilter);
376
377     return hr;
378 }
379
380 static HRESULT WINAPI TransformFilter_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
381 {
382     TransformFilterImpl *This = (TransformFilterImpl *)iface;
383
384     TRACE("(%p/%p)->(%d, %p)\n", This, iface, dwMilliSecsTimeout, pState);
385
386     EnterCriticalSection(&This->csFilter);
387     {
388         *pState = This->state;
389     }
390     LeaveCriticalSection(&This->csFilter);
391
392     return S_OK;
393 }
394
395 static HRESULT WINAPI TransformFilter_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock)
396 {
397     TransformFilterImpl *This = (TransformFilterImpl *)iface;
398
399     TRACE("(%p/%p)->(%p)\n", This, iface, pClock);
400
401     EnterCriticalSection(&This->csFilter);
402     {
403         if (This->pClock)
404             IReferenceClock_Release(This->pClock);
405         This->pClock = pClock;
406         if (This->pClock)
407             IReferenceClock_AddRef(This->pClock);
408     }
409     LeaveCriticalSection(&This->csFilter);
410
411     return S_OK;
412 }
413
414 static HRESULT WINAPI TransformFilter_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock)
415 {
416     TransformFilterImpl *This = (TransformFilterImpl *)iface;
417
418     TRACE("(%p/%p)->(%p)\n", This, iface, ppClock);
419
420     EnterCriticalSection(&This->csFilter);
421     {
422         *ppClock = This->pClock;
423         IReferenceClock_AddRef(This->pClock);
424     }
425     LeaveCriticalSection(&This->csFilter);
426
427     return S_OK;
428 }
429
430 /** IBaseFilter implementation **/
431
432 static HRESULT WINAPI TransformFilter_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum)
433 {
434     ENUMPINDETAILS epd;
435     TransformFilterImpl *This = (TransformFilterImpl *)iface;
436
437     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
438
439     epd.cPins = 2; /* input and output pins */
440     epd.ppPins = This->ppPins;
441     return IEnumPinsImpl_Construct(&epd, ppEnum);
442 }
443
444 static HRESULT WINAPI TransformFilter_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
445 {
446     TransformFilterImpl *This = (TransformFilterImpl *)iface;
447
448     TRACE("(%p/%p)->(%p,%p)\n", This, iface, debugstr_w(Id), ppPin);
449
450     return E_NOTIMPL;
451 }
452
453 static HRESULT WINAPI TransformFilter_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo)
454 {
455     TransformFilterImpl *This = (TransformFilterImpl *)iface;
456
457     TRACE("(%p/%p)->(%p)\n", This, iface, pInfo);
458
459     strcpyW(pInfo->achName, This->filterInfo.achName);
460     pInfo->pGraph = This->filterInfo.pGraph;
461
462     if (pInfo->pGraph)
463         IFilterGraph_AddRef(pInfo->pGraph);
464
465     return S_OK;
466 }
467
468 static HRESULT WINAPI TransformFilter_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName)
469 {
470     HRESULT hr = S_OK;
471     TransformFilterImpl *This = (TransformFilterImpl *)iface;
472
473     TRACE("(%p/%p)->(%p, %s)\n", This, iface, pGraph, debugstr_w(pName));
474
475     EnterCriticalSection(&This->csFilter);
476     {
477         if (pName)
478             strcpyW(This->filterInfo.achName, pName);
479         else
480             *This->filterInfo.achName = '\0';
481         This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */
482     }
483     LeaveCriticalSection(&This->csFilter);
484
485     return hr;
486 }
487
488 static HRESULT WINAPI TransformFilter_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo)
489 {
490     TransformFilterImpl *This = (TransformFilterImpl *)iface;
491     TRACE("(%p/%p)->(%p)\n", This, iface, pVendorInfo);
492     return E_NOTIMPL;
493 }
494
495 static const IBaseFilterVtbl TransformFilter_Vtbl =
496 {
497     TransformFilter_QueryInterface,
498     TransformFilter_AddRef,
499     TransformFilter_Release,
500     TransformFilter_GetClassID,
501     TransformFilter_Stop,
502     TransformFilter_Pause,
503     TransformFilter_Run,
504     TransformFilter_GetState,
505     TransformFilter_SetSyncSource,
506     TransformFilter_GetSyncSource,
507     TransformFilter_EnumPins,
508     TransformFilter_FindPin,
509     TransformFilter_QueryFilterInfo,
510     TransformFilter_JoinFilterGraph,
511     TransformFilter_QueryVendorInfo
512 };
513
514 static HRESULT WINAPI TransformFilter_InputPin_EndOfStream(IPin * iface)
515 {
516     InputPin* This = (InputPin*) iface;
517     TransformFilterImpl* pTransform;
518     IPin* ppin;
519     HRESULT hr;
520     
521     TRACE("(%p)->()\n", iface);
522
523     /* Since we process samples synchronously, just forward notification downstream */
524     pTransform = (TransformFilterImpl*)This->pin.pinInfo.pFilter;
525     if (!pTransform)
526         hr = E_FAIL;
527     else
528         hr = IPin_ConnectedTo(pTransform->ppPins[1], &ppin);
529     if (SUCCEEDED(hr))
530     {
531         hr = IPin_EndOfStream(ppin);
532         IPin_Release(ppin);
533     }
534
535     if (FAILED(hr))
536         ERR("%x\n", hr);
537     return hr;
538 }
539
540 static const IPinVtbl TransformFilter_InputPin_Vtbl = 
541 {
542     InputPin_QueryInterface,
543     IPinImpl_AddRef,
544     InputPin_Release,
545     InputPin_Connect,
546     InputPin_ReceiveConnection,
547     IPinImpl_Disconnect,
548     IPinImpl_ConnectedTo,
549     IPinImpl_ConnectionMediaType,
550     IPinImpl_QueryPinInfo,
551     IPinImpl_QueryDirection,
552     IPinImpl_QueryId,
553     IPinImpl_QueryAccept,
554     IPinImpl_EnumMediaTypes,
555     IPinImpl_QueryInternalConnections,
556     TransformFilter_InputPin_EndOfStream,
557     InputPin_BeginFlush,
558     InputPin_EndFlush,
559     InputPin_NewSegment
560 };
561
562 static HRESULT WINAPI TransformFilter_Output_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
563 {
564     IPinImpl *This = (IPinImpl *)iface;
565     ENUMMEDIADETAILS emd;
566
567     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
568
569     emd.cMediaTypes = 1;
570     emd.pMediaTypes = &This->mtCurrent;
571
572     return IEnumMediaTypesImpl_Construct(&emd, ppEnum);
573 }
574
575 static HRESULT WINAPI TransformFilter_Output_Disconnect(IPin * iface)
576 {
577     OutputPin* This = (OutputPin*)iface;
578     HRESULT hr;
579     TransformFilterImpl* pTransformFilter = (TransformFilterImpl*)This->pin.pinInfo.pFilter;
580
581     TRACE("(%p/%p)->()\n", This, iface);
582
583     hr = OutputPin_Disconnect(iface);
584
585     if (hr == S_OK)
586     {
587         pTransformFilter->pFuncsTable->pfnCleanup(pTransformFilter);
588     }
589
590     return hr;
591 }
592
593 static const IPinVtbl TransformFilter_OutputPin_Vtbl = 
594 {
595     OutputPin_QueryInterface,
596     IPinImpl_AddRef,
597     OutputPin_Release,
598     OutputPin_Connect,
599     OutputPin_ReceiveConnection,
600     TransformFilter_Output_Disconnect,
601     IPinImpl_ConnectedTo,
602     IPinImpl_ConnectionMediaType,
603     IPinImpl_QueryPinInfo,
604     IPinImpl_QueryDirection,
605     IPinImpl_QueryId,
606     IPinImpl_QueryAccept,
607     TransformFilter_Output_EnumMediaTypes,
608     IPinImpl_QueryInternalConnections,
609     OutputPin_EndOfStream,
610     OutputPin_BeginFlush,
611     OutputPin_EndFlush,
612     OutputPin_NewSegment
613 };
614
615 static const IMemInputPinVtbl MemInputPin_Vtbl = 
616 {
617     MemInputPin_QueryInterface,
618     MemInputPin_AddRef,
619     MemInputPin_Release,
620     MemInputPin_GetAllocator,
621     MemInputPin_NotifyAllocator,
622     MemInputPin_GetAllocatorRequirements,
623     MemInputPin_Receive,
624     MemInputPin_ReceiveMultiple,
625     MemInputPin_ReceiveCanBlock
626 };