strmbase: Move BasePin implementation to strmbase.
[wine] / dlls / quartz / nullrenderer.c
1 /*
2  * Null Renderer (Promiscuous, not rendering anything at all!)
3  *
4  * Copyright 2004 Christian Costa
5  * Copyright 2008 Maarten Lankhorst
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
22 #include "config.h"
23
24 #define NONAMELESSSTRUCT
25 #define NONAMELESSUNION
26 #include "quartz_private.h"
27 #include "control_private.h"
28 #include "pin.h"
29
30 #include "uuids.h"
31 #include "vfwmsgs.h"
32 #include "amvideo.h"
33 #include "windef.h"
34 #include "winbase.h"
35 #include "dshow.h"
36 #include "evcode.h"
37 #include "strmif.h"
38 #include "ddraw.h"
39
40 #include "wine/unicode.h"
41 #include "wine/debug.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
44
45 static const WCHAR wcsInputPinName[] = {'i','n','p','u','t',' ','p','i','n',0};
46 static const WCHAR wcsAltInputPinName[] = {'I','n',0};
47
48 static const IBaseFilterVtbl NullRenderer_Vtbl;
49 static const IUnknownVtbl IInner_VTable;
50 static const IPinVtbl NullRenderer_InputPin_Vtbl;
51
52 typedef struct NullRendererImpl
53 {
54     const IBaseFilterVtbl * lpVtbl;
55     const IUnknownVtbl * IInner_vtbl;
56     IUnknown *seekthru_unk;
57
58     LONG refCount;
59     CRITICAL_SECTION csFilter;
60     FILTER_STATE state;
61     REFERENCE_TIME rtStreamStart;
62     IReferenceClock * pClock;
63     FILTER_INFO filterInfo;
64
65     InputPin *pInputPin;
66     IUnknown * pUnkOuter;
67     BOOL bUnkOuterValid;
68     BOOL bAggregatable;
69 } NullRendererImpl;
70
71 static HRESULT NullRenderer_Sample(LPVOID iface, IMediaSample * pSample)
72 {
73     NullRendererImpl *This = iface;
74     HRESULT hr = S_OK;
75     REFERENCE_TIME start, stop;
76
77     TRACE("%p %p\n", iface, pSample);
78
79     if (SUCCEEDED(IMediaSample_GetTime(pSample, &start, &stop)))
80         MediaSeekingPassThru_RegisterMediaTime(This->seekthru_unk, start);
81     EnterCriticalSection(&This->csFilter);
82     if (This->pInputPin->flushing || This->pInputPin->end_of_stream)
83         hr = S_FALSE;
84     LeaveCriticalSection(&This->csFilter);
85
86     return hr;
87 }
88
89 static HRESULT NullRenderer_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
90 {
91     TRACE("Not a stub!\n");
92     return S_OK;
93 }
94
95 HRESULT NullRenderer_create(IUnknown * pUnkOuter, LPVOID * ppv)
96 {
97     HRESULT hr;
98     PIN_INFO piInput;
99     NullRendererImpl * pNullRenderer;
100
101     TRACE("(%p, %p)\n", pUnkOuter, ppv);
102
103     *ppv = NULL;
104
105     pNullRenderer = CoTaskMemAlloc(sizeof(NullRendererImpl));
106     pNullRenderer->pUnkOuter = pUnkOuter;
107     pNullRenderer->bUnkOuterValid = FALSE;
108     pNullRenderer->bAggregatable = FALSE;
109     pNullRenderer->IInner_vtbl = &IInner_VTable;
110
111     pNullRenderer->lpVtbl = &NullRenderer_Vtbl;
112     pNullRenderer->refCount = 1;
113     InitializeCriticalSection(&pNullRenderer->csFilter);
114     pNullRenderer->csFilter.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": NullRendererImpl.csFilter");
115     pNullRenderer->state = State_Stopped;
116     pNullRenderer->pClock = NULL;
117     ZeroMemory(&pNullRenderer->filterInfo, sizeof(FILTER_INFO));
118
119     /* construct input pin */
120     piInput.dir = PINDIR_INPUT;
121     piInput.pFilter = (IBaseFilter *)pNullRenderer;
122     lstrcpynW(piInput.achName, wcsInputPinName, sizeof(piInput.achName) / sizeof(piInput.achName[0]));
123
124     hr = InputPin_Construct(&NullRenderer_InputPin_Vtbl, &piInput, NullRenderer_Sample, (LPVOID)pNullRenderer, NullRenderer_QueryAccept, NULL, &pNullRenderer->csFilter, NULL, (IPin **)&pNullRenderer->pInputPin);
125
126     if (SUCCEEDED(hr))
127     {
128         ISeekingPassThru *passthru;
129         hr = CoCreateInstance(&CLSID_SeekingPassThru, pUnkOuter ? pUnkOuter : (IUnknown*)&pNullRenderer->IInner_vtbl, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void**)&pNullRenderer->seekthru_unk);
130         if (FAILED(hr)) {
131             IUnknown_Release((IUnknown*)pNullRenderer);
132             return hr;
133         }
134         IUnknown_QueryInterface(pNullRenderer->seekthru_unk, &IID_ISeekingPassThru, (void**)&passthru);
135         ISeekingPassThru_Init(passthru, TRUE, (IPin*)pNullRenderer->pInputPin);
136         ISeekingPassThru_Release(passthru);
137         *ppv = pNullRenderer;
138     }
139     else
140     {
141         pNullRenderer->csFilter.DebugInfo->Spare[0] = 0;
142         DeleteCriticalSection(&pNullRenderer->csFilter);
143         CoTaskMemFree(pNullRenderer);
144     }
145
146     return hr;
147 }
148
149 static HRESULT WINAPI NullRendererInner_QueryInterface(IUnknown * iface, REFIID riid, LPVOID * ppv)
150 {
151     ICOM_THIS_MULTI(NullRendererImpl, IInner_vtbl, iface);
152     TRACE("(%p/%p)->(%s, %p)\n", This, iface, qzdebugstr_guid(riid), ppv);
153
154     if (This->bAggregatable)
155         This->bUnkOuterValid = TRUE;
156
157     *ppv = NULL;
158
159     if (IsEqualIID(riid, &IID_IUnknown))
160         *ppv = &This->IInner_vtbl;
161     else if (IsEqualIID(riid, &IID_IPersist))
162         *ppv = This;
163     else if (IsEqualIID(riid, &IID_IMediaFilter))
164         *ppv = This;
165     else if (IsEqualIID(riid, &IID_IBaseFilter))
166         *ppv = This;
167     else if (IsEqualIID(riid, &IID_IMediaSeeking))
168         return IUnknown_QueryInterface(This->seekthru_unk, riid, ppv);
169
170     if (*ppv)
171     {
172         IUnknown_AddRef((IUnknown *)(*ppv));
173         return S_OK;
174     }
175
176     if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow))
177         FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
178
179     return E_NOINTERFACE;
180 }
181
182 static ULONG WINAPI NullRendererInner_AddRef(IUnknown * iface)
183 {
184     ICOM_THIS_MULTI(NullRendererImpl, IInner_vtbl, iface);
185     ULONG refCount = InterlockedIncrement(&This->refCount);
186
187     TRACE("(%p/%p)->() AddRef from %d\n", This, iface, refCount - 1);
188
189     return refCount;
190 }
191
192 static ULONG WINAPI NullRendererInner_Release(IUnknown * iface)
193 {
194     ICOM_THIS_MULTI(NullRendererImpl, IInner_vtbl, iface);
195     ULONG refCount = InterlockedDecrement(&This->refCount);
196
197     TRACE("(%p/%p)->() Release from %d\n", This, iface, refCount + 1);
198
199     if (!refCount)
200     {
201         IPin *pConnectedTo;
202
203         if (This->pClock)
204             IReferenceClock_Release(This->pClock);
205
206         if (SUCCEEDED(IPin_ConnectedTo((IPin *)This->pInputPin, &pConnectedTo)))
207         {
208             IPin_Disconnect(pConnectedTo);
209             IPin_Release(pConnectedTo);
210         }
211         IPin_Disconnect((IPin *)This->pInputPin);
212         IPin_Release((IPin *)This->pInputPin);
213
214         This->lpVtbl = NULL;
215         if (This->seekthru_unk)
216             IUnknown_Release(This->seekthru_unk);
217
218         This->csFilter.DebugInfo->Spare[0] = 0;
219         DeleteCriticalSection(&This->csFilter);
220
221         TRACE("Destroying Null Renderer\n");
222         CoTaskMemFree(This);
223         return 0;
224     }
225     else
226         return refCount;
227 }
228
229 static const IUnknownVtbl IInner_VTable =
230 {
231     NullRendererInner_QueryInterface,
232     NullRendererInner_AddRef,
233     NullRendererInner_Release
234 };
235
236 static HRESULT WINAPI NullRenderer_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
237 {
238     NullRendererImpl *This = (NullRendererImpl *)iface;
239
240     if (This->bAggregatable)
241         This->bUnkOuterValid = TRUE;
242
243     if (This->pUnkOuter)
244     {
245         if (This->bAggregatable)
246             return IUnknown_QueryInterface(This->pUnkOuter, riid, ppv);
247
248         if (IsEqualIID(riid, &IID_IUnknown))
249         {
250             HRESULT hr;
251
252             IUnknown_AddRef((IUnknown *)&(This->IInner_vtbl));
253             hr = IUnknown_QueryInterface((IUnknown *)&(This->IInner_vtbl), riid, ppv);
254             IUnknown_Release((IUnknown *)&(This->IInner_vtbl));
255             This->bAggregatable = TRUE;
256             return hr;
257         }
258
259         *ppv = NULL;
260         return E_NOINTERFACE;
261     }
262
263     return IUnknown_QueryInterface((IUnknown *)&(This->IInner_vtbl), riid, ppv);
264 }
265
266 static ULONG WINAPI NullRenderer_AddRef(IBaseFilter * iface)
267 {
268     NullRendererImpl *This = (NullRendererImpl *)iface;
269
270     if (This->pUnkOuter && This->bUnkOuterValid)
271         return IUnknown_AddRef(This->pUnkOuter);
272     return IUnknown_AddRef((IUnknown *)&(This->IInner_vtbl));
273 }
274
275 static ULONG WINAPI NullRenderer_Release(IBaseFilter * iface)
276 {
277     NullRendererImpl *This = (NullRendererImpl *)iface;
278
279     if (This->pUnkOuter && This->bUnkOuterValid)
280         return IUnknown_Release(This->pUnkOuter);
281     return IUnknown_Release((IUnknown *)&(This->IInner_vtbl));
282 }
283
284 /** IPersist methods **/
285
286 static HRESULT WINAPI NullRenderer_GetClassID(IBaseFilter * iface, CLSID * pClsid)
287 {
288     NullRendererImpl *This = (NullRendererImpl *)iface;
289
290     TRACE("(%p/%p)->(%p)\n", This, iface, pClsid);
291
292     *pClsid = CLSID_NullRenderer;
293
294     return S_OK;
295 }
296
297 /** IMediaFilter methods **/
298
299 static HRESULT WINAPI NullRenderer_Stop(IBaseFilter * iface)
300 {
301     NullRendererImpl *This = (NullRendererImpl *)iface;
302
303     TRACE("(%p/%p)->()\n", This, iface);
304
305     EnterCriticalSection(&This->csFilter);
306     {
307         This->state = State_Stopped;
308         MediaSeekingPassThru_ResetMediaTime(This->seekthru_unk);
309     }
310     LeaveCriticalSection(&This->csFilter);
311
312     return S_OK;
313 }
314
315 static HRESULT WINAPI NullRenderer_Pause(IBaseFilter * iface)
316 {
317     NullRendererImpl *This = (NullRendererImpl *)iface;
318
319     TRACE("(%p/%p)->()\n", This, iface);
320
321     EnterCriticalSection(&This->csFilter);
322     {
323         if (This->state == State_Stopped)
324             This->pInputPin->end_of_stream = 0;
325         This->state = State_Paused;
326     }
327     LeaveCriticalSection(&This->csFilter);
328
329     return S_OK;
330 }
331
332 static HRESULT WINAPI NullRenderer_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
333 {
334     NullRendererImpl *This = (NullRendererImpl *)iface;
335
336     TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(tStart));
337
338     EnterCriticalSection(&This->csFilter);
339     {
340         This->rtStreamStart = tStart;
341         This->state = State_Running;
342         This->pInputPin->end_of_stream = 0;
343     }
344     LeaveCriticalSection(&This->csFilter);
345
346     return S_OK;
347 }
348
349 static HRESULT WINAPI NullRenderer_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
350 {
351     NullRendererImpl *This = (NullRendererImpl *)iface;
352
353     TRACE("(%p/%p)->(%d, %p)\n", This, iface, dwMilliSecsTimeout, pState);
354
355     EnterCriticalSection(&This->csFilter);
356     {
357         *pState = This->state;
358     }
359     LeaveCriticalSection(&This->csFilter);
360
361     return S_OK;
362 }
363
364 static HRESULT WINAPI NullRenderer_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock)
365 {
366     NullRendererImpl *This = (NullRendererImpl *)iface;
367
368     TRACE("(%p/%p)->(%p)\n", This, iface, pClock);
369
370     EnterCriticalSection(&This->csFilter);
371     {
372         if (This->pClock)
373             IReferenceClock_Release(This->pClock);
374         This->pClock = pClock;
375         if (This->pClock)
376             IReferenceClock_AddRef(This->pClock);
377     }
378     LeaveCriticalSection(&This->csFilter);
379
380     return S_OK;
381 }
382
383 static HRESULT WINAPI NullRenderer_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock)
384 {
385     NullRendererImpl *This = (NullRendererImpl *)iface;
386
387     TRACE("(%p/%p)->(%p)\n", This, iface, ppClock);
388
389     EnterCriticalSection(&This->csFilter);
390     {
391         *ppClock = This->pClock;
392         if (This->pClock)
393             IReferenceClock_AddRef(This->pClock);
394     }
395     LeaveCriticalSection(&This->csFilter);
396
397     return S_OK;
398 }
399
400 /** IBaseFilter implementation **/
401
402 static IPin* WINAPI NullRenderer_GetPin(IBaseFilter *iface, int pos)
403 {
404     NullRendererImpl *This = (NullRendererImpl *)iface;
405
406     if (pos >= 1 || pos < 0)
407         return NULL;
408
409     IPin_AddRef((IPin *)This->pInputPin);
410     return (IPin *)This->pInputPin;
411 }
412
413 static LONG WINAPI NullRenderer_GetPinCount(IBaseFilter *iface)
414 {
415     return 1;
416 }
417
418 static LONG WINAPI NullRenderer_GetPinVersion(IBaseFilter *iface)
419 {
420     /* Our pins are static, not changing so setting static tick count is ok */
421     return 0;
422 }
423
424 static HRESULT WINAPI NullRenderer_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum)
425 {
426     NullRendererImpl *This = (NullRendererImpl *)iface;
427
428     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
429
430     return EnumPins_Construct(iface, NullRenderer_GetPin, NullRenderer_GetPinCount, NullRenderer_GetPinVersion, ppEnum);
431 }
432
433 static HRESULT WINAPI NullRenderer_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
434 {
435     NullRendererImpl *This = (NullRendererImpl *)iface;
436
437     TRACE("(%p/%p)->(%p,%p)\n", This, iface, debugstr_w(Id), ppPin);
438
439     if (!Id || !ppPin)
440         return E_POINTER;
441
442     if (!lstrcmpiW(Id,wcsInputPinName) || !lstrcmpiW(Id,wcsAltInputPinName))
443     {
444         *ppPin = (IPin *)This->pInputPin;
445         IPin_AddRef(*ppPin);
446         return S_OK;
447     }
448     *ppPin = NULL;
449     return VFW_E_NOT_FOUND;
450 }
451
452 static HRESULT WINAPI NullRenderer_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo)
453 {
454     NullRendererImpl *This = (NullRendererImpl *)iface;
455
456     TRACE("(%p/%p)->(%p)\n", This, iface, pInfo);
457
458     strcpyW(pInfo->achName, This->filterInfo.achName);
459     pInfo->pGraph = This->filterInfo.pGraph;
460
461     if (pInfo->pGraph)
462         IFilterGraph_AddRef(pInfo->pGraph);
463
464     return S_OK;
465 }
466
467 static HRESULT WINAPI NullRenderer_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName)
468 {
469     NullRendererImpl *This = (NullRendererImpl *)iface;
470
471     TRACE("(%p/%p)->(%p, %s)\n", This, iface, pGraph, debugstr_w(pName));
472
473     EnterCriticalSection(&This->csFilter);
474     {
475         if (pName)
476             strcpyW(This->filterInfo.achName, pName);
477         else
478             *This->filterInfo.achName = '\0';
479         This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */
480     }
481     LeaveCriticalSection(&This->csFilter);
482
483     return S_OK;
484 }
485
486 static HRESULT WINAPI NullRenderer_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo)
487 {
488     NullRendererImpl *This = (NullRendererImpl *)iface;
489     TRACE("(%p/%p)->(%p)\n", This, iface, pVendorInfo);
490     return E_NOTIMPL;
491 }
492
493 static const IBaseFilterVtbl NullRenderer_Vtbl =
494 {
495     NullRenderer_QueryInterface,
496     NullRenderer_AddRef,
497     NullRenderer_Release,
498     NullRenderer_GetClassID,
499     NullRenderer_Stop,
500     NullRenderer_Pause,
501     NullRenderer_Run,
502     NullRenderer_GetState,
503     NullRenderer_SetSyncSource,
504     NullRenderer_GetSyncSource,
505     NullRenderer_EnumPins,
506     NullRenderer_FindPin,
507     NullRenderer_QueryFilterInfo,
508     NullRenderer_JoinFilterGraph,
509     NullRenderer_QueryVendorInfo
510 };
511
512 static HRESULT WINAPI NullRenderer_InputPin_EndOfStream(IPin * iface)
513 {
514     InputPin* This = (InputPin*)iface;
515     IMediaEventSink* pEventSink;
516     NullRendererImpl *pNull;
517     IFilterGraph *graph;
518     HRESULT hr = S_OK;
519
520     TRACE("(%p/%p)->()\n", This, iface);
521
522     InputPin_EndOfStream(iface);
523     pNull = (NullRendererImpl*)This->pin.pinInfo.pFilter;
524     graph = pNull->filterInfo.pGraph;
525     if (graph)
526     {
527         hr = IFilterGraph_QueryInterface(((NullRendererImpl*)This->pin.pinInfo.pFilter)->filterInfo.pGraph, &IID_IMediaEventSink, (LPVOID*)&pEventSink);
528         if (SUCCEEDED(hr))
529         {
530             hr = IMediaEventSink_Notify(pEventSink, EC_COMPLETE, S_OK, 0);
531             IMediaEventSink_Release(pEventSink);
532         }
533     }
534     MediaSeekingPassThru_EOS(pNull->seekthru_unk);
535
536     return hr;
537 }
538
539 static HRESULT WINAPI NullRenderer_InputPin_EndFlush(IPin * iface)
540 {
541     InputPin* This = (InputPin*)iface;
542     NullRendererImpl *pNull;
543     HRESULT hr = S_OK;
544
545     TRACE("(%p/%p)->()\n", This, iface);
546
547     hr = InputPin_EndOfStream(iface);
548     pNull = (NullRendererImpl*)This->pin.pinInfo.pFilter;
549     MediaSeekingPassThru_ResetMediaTime(pNull->seekthru_unk);
550     return hr;
551 }
552
553 static const IPinVtbl NullRenderer_InputPin_Vtbl =
554 {
555     InputPin_QueryInterface,
556     BasePinImpl_AddRef,
557     InputPin_Release,
558     InputPin_Connect,
559     InputPin_ReceiveConnection,
560     BasePinImpl_Disconnect,
561     BasePinImpl_ConnectedTo,
562     BasePinImpl_ConnectionMediaType,
563     BasePinImpl_QueryPinInfo,
564     BasePinImpl_QueryDirection,
565     BasePinImpl_QueryId,
566     InputPin_QueryAccept,
567     BasePinImpl_EnumMediaTypes,
568     BasePinImpl_QueryInternalConnections,
569     NullRenderer_InputPin_EndOfStream,
570     InputPin_BeginFlush,
571     NullRenderer_InputPin_EndFlush,
572     InputPin_NewSegment
573 };