quartz: DSoundRender shouldn't delete buffer when already connected.
[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
47 static const IBaseFilterVtbl NullRenderer_Vtbl;
48 static const IUnknownVtbl IInner_VTable;
49 static const IPinVtbl NullRenderer_InputPin_Vtbl;
50
51 typedef struct NullRendererImpl
52 {
53     const IBaseFilterVtbl * lpVtbl;
54     const IUnknownVtbl * IInner_vtbl;
55
56     LONG refCount;
57     CRITICAL_SECTION csFilter;
58     FILTER_STATE state;
59     REFERENCE_TIME rtStreamStart;
60     IReferenceClock * pClock;
61     FILTER_INFO filterInfo;
62
63     InputPin *pInputPin;
64     IUnknown * pUnkOuter;
65     BOOL bUnkOuterValid;
66     BOOL bAggregatable;
67     MediaSeekingImpl mediaSeeking;
68 } NullRendererImpl;
69
70 static const IMemInputPinVtbl MemInputPin_Vtbl =
71 {
72     MemInputPin_QueryInterface,
73     MemInputPin_AddRef,
74     MemInputPin_Release,
75     MemInputPin_GetAllocator,
76     MemInputPin_NotifyAllocator,
77     MemInputPin_GetAllocatorRequirements,
78     MemInputPin_Receive,
79     MemInputPin_ReceiveMultiple,
80     MemInputPin_ReceiveCanBlock
81 };
82
83 static HRESULT NullRenderer_Sample(LPVOID iface, IMediaSample * pSample)
84 {
85     LPBYTE pbSrcStream = NULL;
86     long cbSrcStream = 0;
87     REFERENCE_TIME tStart, tStop;
88     HRESULT hr;
89
90     TRACE("%p %p\n", iface, pSample);
91
92     hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
93     if (FAILED(hr))
94     {
95         ERR("Cannot get pointer to sample data (%x)\n", hr);
96         return hr;
97     }
98
99     hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
100     if (FAILED(hr))
101         ERR("Cannot get sample time (%x)\n", hr);
102
103     cbSrcStream = IMediaSample_GetActualDataLength(pSample);
104
105     TRACE("val %p %ld\n", pbSrcStream, cbSrcStream);
106
107     return S_OK;
108 }
109
110 static HRESULT NullRenderer_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
111 {
112     TRACE("Not a stub!\n");
113     return S_OK;
114 }
115
116 static inline NullRendererImpl *impl_from_IMediaSeeking( IMediaSeeking *iface )
117 {
118     return (NullRendererImpl *)((char*)iface - FIELD_OFFSET(NullRendererImpl, mediaSeeking.lpVtbl));
119 }
120
121 static HRESULT WINAPI NullRendererImpl_Seeking_QueryInterface(IMediaSeeking * iface, REFIID riid, LPVOID * ppv)
122 {
123     NullRendererImpl *This = impl_from_IMediaSeeking(iface);
124
125     return IUnknown_QueryInterface((IUnknown *)This, riid, ppv);
126 }
127
128 static ULONG WINAPI NullRendererImpl_Seeking_AddRef(IMediaSeeking * iface)
129 {
130     NullRendererImpl *This = impl_from_IMediaSeeking(iface);
131
132     return IUnknown_AddRef((IUnknown *)This);
133 }
134
135 static ULONG WINAPI NullRendererImpl_Seeking_Release(IMediaSeeking * iface)
136 {
137     NullRendererImpl *This = impl_from_IMediaSeeking(iface);
138
139     return IUnknown_Release((IUnknown *)This);
140 }
141
142 static const IMediaSeekingVtbl TransformFilter_Seeking_Vtbl =
143 {
144     NullRendererImpl_Seeking_QueryInterface,
145     NullRendererImpl_Seeking_AddRef,
146     NullRendererImpl_Seeking_Release,
147     MediaSeekingImpl_GetCapabilities,
148     MediaSeekingImpl_CheckCapabilities,
149     MediaSeekingImpl_IsFormatSupported,
150     MediaSeekingImpl_QueryPreferredFormat,
151     MediaSeekingImpl_GetTimeFormat,
152     MediaSeekingImpl_IsUsingTimeFormat,
153     MediaSeekingImpl_SetTimeFormat,
154     MediaSeekingImpl_GetDuration,
155     MediaSeekingImpl_GetStopPosition,
156     MediaSeekingImpl_GetCurrentPosition,
157     MediaSeekingImpl_ConvertTimeFormat,
158     MediaSeekingImpl_SetPositions,
159     MediaSeekingImpl_GetPositions,
160     MediaSeekingImpl_GetAvailable,
161     MediaSeekingImpl_SetRate,
162     MediaSeekingImpl_GetRate,
163     MediaSeekingImpl_GetPreroll
164 };
165
166 static HRESULT NullRendererImpl_Change(IBaseFilter *iface)
167 {
168     TRACE("(%p)\n", iface);
169     return S_OK;
170 }
171
172 HRESULT NullRenderer_create(IUnknown * pUnkOuter, LPVOID * ppv)
173 {
174     HRESULT hr;
175     PIN_INFO piInput;
176     NullRendererImpl * pNullRenderer;
177
178     TRACE("(%p, %p)\n", pUnkOuter, ppv);
179
180     *ppv = NULL;
181
182     pNullRenderer = CoTaskMemAlloc(sizeof(NullRendererImpl));
183     pNullRenderer->pUnkOuter = pUnkOuter;
184     pNullRenderer->bUnkOuterValid = FALSE;
185     pNullRenderer->bAggregatable = FALSE;
186     pNullRenderer->IInner_vtbl = &IInner_VTable;
187
188     pNullRenderer->lpVtbl = &NullRenderer_Vtbl;
189     pNullRenderer->refCount = 1;
190     InitializeCriticalSection(&pNullRenderer->csFilter);
191     pNullRenderer->csFilter.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": NullRendererImpl.csFilter");
192     pNullRenderer->state = State_Stopped;
193     pNullRenderer->pClock = NULL;
194     ZeroMemory(&pNullRenderer->filterInfo, sizeof(FILTER_INFO));
195
196     /* construct input pin */
197     piInput.dir = PINDIR_INPUT;
198     piInput.pFilter = (IBaseFilter *)pNullRenderer;
199     lstrcpynW(piInput.achName, wcsInputPinName, sizeof(piInput.achName) / sizeof(piInput.achName[0]));
200
201     hr = InputPin_Construct(&NullRenderer_InputPin_Vtbl, &piInput, NullRenderer_Sample, (LPVOID)pNullRenderer, NullRenderer_QueryAccept, NULL, &pNullRenderer->csFilter, NULL, (IPin **)&pNullRenderer->pInputPin);
202
203     if (SUCCEEDED(hr))
204     {
205         MediaSeekingImpl_Init((IBaseFilter*)pNullRenderer, NullRendererImpl_Change, NullRendererImpl_Change, NullRendererImpl_Change, &pNullRenderer->mediaSeeking, &pNullRenderer->csFilter);
206         pNullRenderer->mediaSeeking.lpVtbl = &TransformFilter_Seeking_Vtbl;
207
208         *ppv = (LPVOID)pNullRenderer;
209     }
210     else
211     {
212         pNullRenderer->csFilter.DebugInfo->Spare[0] = 0;
213         DeleteCriticalSection(&pNullRenderer->csFilter);
214         CoTaskMemFree(pNullRenderer);
215     }
216
217     return hr;
218 }
219
220 static HRESULT WINAPI NullRendererInner_QueryInterface(IUnknown * iface, REFIID riid, LPVOID * ppv)
221 {
222     ICOM_THIS_MULTI(NullRendererImpl, IInner_vtbl, iface);
223     TRACE("(%p/%p)->(%s, %p)\n", This, iface, qzdebugstr_guid(riid), ppv);
224
225     if (This->bAggregatable)
226         This->bUnkOuterValid = TRUE;
227
228     *ppv = NULL;
229
230     if (IsEqualIID(riid, &IID_IUnknown))
231         *ppv = (LPVOID)&(This->IInner_vtbl);
232     else if (IsEqualIID(riid, &IID_IPersist))
233         *ppv = (LPVOID)This;
234     else if (IsEqualIID(riid, &IID_IMediaFilter))
235         *ppv = (LPVOID)This;
236     else if (IsEqualIID(riid, &IID_IBaseFilter))
237         *ppv = (LPVOID)This;
238     else if (IsEqualIID(riid, &IID_IMediaSeeking))
239         *ppv = &This->mediaSeeking;
240
241     if (*ppv)
242     {
243         IUnknown_AddRef((IUnknown *)(*ppv));
244         return S_OK;
245     }
246
247     if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow))
248         FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
249
250     return E_NOINTERFACE;
251 }
252
253 static ULONG WINAPI NullRendererInner_AddRef(IUnknown * iface)
254 {
255     ICOM_THIS_MULTI(NullRendererImpl, IInner_vtbl, iface);
256     ULONG refCount = InterlockedIncrement(&This->refCount);
257
258     TRACE("(%p/%p)->() AddRef from %d\n", This, iface, refCount - 1);
259
260     return refCount;
261 }
262
263 static ULONG WINAPI NullRendererInner_Release(IUnknown * iface)
264 {
265     ICOM_THIS_MULTI(NullRendererImpl, IInner_vtbl, iface);
266     ULONG refCount = InterlockedDecrement(&This->refCount);
267
268     TRACE("(%p/%p)->() Release from %d\n", This, iface, refCount + 1);
269
270     if (!refCount)
271     {
272         IPin *pConnectedTo;
273
274         if (This->pClock)
275             IReferenceClock_Release(This->pClock);
276
277         if (SUCCEEDED(IPin_ConnectedTo((IPin *)This->pInputPin, &pConnectedTo)))
278         {
279             IPin_Disconnect(pConnectedTo);
280             IPin_Release(pConnectedTo);
281         }
282         IPin_Disconnect((IPin *)This->pInputPin);
283         IPin_Release((IPin *)This->pInputPin);
284
285         This->lpVtbl = NULL;
286
287         This->csFilter.DebugInfo->Spare[0] = 0;
288         DeleteCriticalSection(&This->csFilter);
289
290         TRACE("Destroying Null Renderer\n");
291         CoTaskMemFree(This);
292         return 0;
293     }
294     else
295         return refCount;
296 }
297
298 static const IUnknownVtbl IInner_VTable =
299 {
300     NullRendererInner_QueryInterface,
301     NullRendererInner_AddRef,
302     NullRendererInner_Release
303 };
304
305 static HRESULT WINAPI NullRenderer_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
306 {
307     NullRendererImpl *This = (NullRendererImpl *)iface;
308
309     if (This->bAggregatable)
310         This->bUnkOuterValid = TRUE;
311
312     if (This->pUnkOuter)
313     {
314         if (This->bAggregatable)
315             return IUnknown_QueryInterface(This->pUnkOuter, riid, ppv);
316
317         if (IsEqualIID(riid, &IID_IUnknown))
318         {
319             HRESULT hr;
320
321             IUnknown_AddRef((IUnknown *)&(This->IInner_vtbl));
322             hr = IUnknown_QueryInterface((IUnknown *)&(This->IInner_vtbl), riid, ppv);
323             IUnknown_Release((IUnknown *)&(This->IInner_vtbl));
324             This->bAggregatable = TRUE;
325             return hr;
326         }
327
328         *ppv = NULL;
329         return E_NOINTERFACE;
330     }
331
332     return IUnknown_QueryInterface((IUnknown *)&(This->IInner_vtbl), riid, ppv);
333 }
334
335 static ULONG WINAPI NullRenderer_AddRef(IBaseFilter * iface)
336 {
337     NullRendererImpl *This = (NullRendererImpl *)iface;
338
339     if (This->pUnkOuter && This->bUnkOuterValid)
340         return IUnknown_AddRef(This->pUnkOuter);
341     return IUnknown_AddRef((IUnknown *)&(This->IInner_vtbl));
342 }
343
344 static ULONG WINAPI NullRenderer_Release(IBaseFilter * iface)
345 {
346     NullRendererImpl *This = (NullRendererImpl *)iface;
347
348     if (This->pUnkOuter && This->bUnkOuterValid)
349         return IUnknown_Release(This->pUnkOuter);
350     return IUnknown_Release((IUnknown *)&(This->IInner_vtbl));
351 }
352
353 /** IPersist methods **/
354
355 static HRESULT WINAPI NullRenderer_GetClassID(IBaseFilter * iface, CLSID * pClsid)
356 {
357     NullRendererImpl *This = (NullRendererImpl *)iface;
358
359     TRACE("(%p/%p)->(%p)\n", This, iface, pClsid);
360
361     *pClsid = CLSID_NullRenderer;
362
363     return S_OK;
364 }
365
366 /** IMediaFilter methods **/
367
368 static HRESULT WINAPI NullRenderer_Stop(IBaseFilter * iface)
369 {
370     NullRendererImpl *This = (NullRendererImpl *)iface;
371
372     TRACE("(%p/%p)->()\n", This, iface);
373
374     EnterCriticalSection(&This->csFilter);
375     {
376         This->state = State_Stopped;
377     }
378     LeaveCriticalSection(&This->csFilter);
379
380     return S_OK;
381 }
382
383 static HRESULT WINAPI NullRenderer_Pause(IBaseFilter * iface)
384 {
385     NullRendererImpl *This = (NullRendererImpl *)iface;
386
387     TRACE("(%p/%p)->()\n", This, iface);
388
389     EnterCriticalSection(&This->csFilter);
390     {
391         if (This->state == State_Stopped)
392             This->pInputPin->end_of_stream = 0;
393         This->state = State_Paused;
394     }
395     LeaveCriticalSection(&This->csFilter);
396
397     return S_OK;
398 }
399
400 static HRESULT WINAPI NullRenderer_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
401 {
402     NullRendererImpl *This = (NullRendererImpl *)iface;
403
404     TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(tStart));
405
406     EnterCriticalSection(&This->csFilter);
407     {
408         This->rtStreamStart = tStart;
409         This->state = State_Running;
410         This->pInputPin->end_of_stream = 0;
411     }
412     LeaveCriticalSection(&This->csFilter);
413
414     return S_OK;
415 }
416
417 static HRESULT WINAPI NullRenderer_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
418 {
419     NullRendererImpl *This = (NullRendererImpl *)iface;
420
421     TRACE("(%p/%p)->(%d, %p)\n", This, iface, dwMilliSecsTimeout, pState);
422
423     EnterCriticalSection(&This->csFilter);
424     {
425         *pState = This->state;
426     }
427     LeaveCriticalSection(&This->csFilter);
428
429     return S_OK;
430 }
431
432 static HRESULT WINAPI NullRenderer_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock)
433 {
434     NullRendererImpl *This = (NullRendererImpl *)iface;
435
436     TRACE("(%p/%p)->(%p)\n", This, iface, pClock);
437
438     EnterCriticalSection(&This->csFilter);
439     {
440         if (This->pClock)
441             IReferenceClock_Release(This->pClock);
442         This->pClock = pClock;
443         if (This->pClock)
444             IReferenceClock_AddRef(This->pClock);
445     }
446     LeaveCriticalSection(&This->csFilter);
447
448     return S_OK;
449 }
450
451 static HRESULT WINAPI NullRenderer_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock)
452 {
453     NullRendererImpl *This = (NullRendererImpl *)iface;
454
455     TRACE("(%p/%p)->(%p)\n", This, iface, ppClock);
456
457     EnterCriticalSection(&This->csFilter);
458     {
459         *ppClock = This->pClock;
460         if (This->pClock)
461             IReferenceClock_AddRef(This->pClock);
462     }
463     LeaveCriticalSection(&This->csFilter);
464
465     return S_OK;
466 }
467
468 /** IBaseFilter implementation **/
469
470 static HRESULT NullRenderer_GetPin(IBaseFilter *iface, ULONG pos, IPin **pin, DWORD *lastsynctick)
471 {
472     NullRendererImpl *This = (NullRendererImpl *)iface;
473
474     /* Our pins are static, not changing so setting static tick count is ok */
475     *lastsynctick = 0;
476
477     if (pos >= 1)
478         return S_FALSE;
479
480     *pin = (IPin *)This->pInputPin;
481     IPin_AddRef(*pin);
482     return S_OK;
483 }
484
485 static HRESULT WINAPI NullRenderer_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum)
486 {
487     NullRendererImpl *This = (NullRendererImpl *)iface;
488
489     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
490
491     return IEnumPinsImpl_Construct(ppEnum, NullRenderer_GetPin, iface);
492 }
493
494 static HRESULT WINAPI NullRenderer_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
495 {
496     NullRendererImpl *This = (NullRendererImpl *)iface;
497
498     TRACE("(%p/%p)->(%p,%p)\n", This, iface, debugstr_w(Id), ppPin);
499
500     FIXME("NullRenderer::FindPin(...)\n");
501
502     /* FIXME: critical section */
503
504     return E_NOTIMPL;
505 }
506
507 static HRESULT WINAPI NullRenderer_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo)
508 {
509     NullRendererImpl *This = (NullRendererImpl *)iface;
510
511     TRACE("(%p/%p)->(%p)\n", This, iface, pInfo);
512
513     strcpyW(pInfo->achName, This->filterInfo.achName);
514     pInfo->pGraph = This->filterInfo.pGraph;
515
516     if (pInfo->pGraph)
517         IFilterGraph_AddRef(pInfo->pGraph);
518
519     return S_OK;
520 }
521
522 static HRESULT WINAPI NullRenderer_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName)
523 {
524     NullRendererImpl *This = (NullRendererImpl *)iface;
525
526     TRACE("(%p/%p)->(%p, %s)\n", This, iface, pGraph, debugstr_w(pName));
527
528     EnterCriticalSection(&This->csFilter);
529     {
530         if (pName)
531             strcpyW(This->filterInfo.achName, pName);
532         else
533             *This->filterInfo.achName = '\0';
534         This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */
535     }
536     LeaveCriticalSection(&This->csFilter);
537
538     return S_OK;
539 }
540
541 static HRESULT WINAPI NullRenderer_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo)
542 {
543     NullRendererImpl *This = (NullRendererImpl *)iface;
544     TRACE("(%p/%p)->(%p)\n", This, iface, pVendorInfo);
545     return E_NOTIMPL;
546 }
547
548 static const IBaseFilterVtbl NullRenderer_Vtbl =
549 {
550     NullRenderer_QueryInterface,
551     NullRenderer_AddRef,
552     NullRenderer_Release,
553     NullRenderer_GetClassID,
554     NullRenderer_Stop,
555     NullRenderer_Pause,
556     NullRenderer_Run,
557     NullRenderer_GetState,
558     NullRenderer_SetSyncSource,
559     NullRenderer_GetSyncSource,
560     NullRenderer_EnumPins,
561     NullRenderer_FindPin,
562     NullRenderer_QueryFilterInfo,
563     NullRenderer_JoinFilterGraph,
564     NullRenderer_QueryVendorInfo
565 };
566
567 static HRESULT WINAPI NullRenderer_InputPin_EndOfStream(IPin * iface)
568 {
569     InputPin* This = (InputPin*)iface;
570     IMediaEventSink* pEventSink;
571     IFilterGraph *graph;
572     HRESULT hr = S_OK;
573
574     TRACE("(%p/%p)->()\n", This, iface);
575
576     InputPin_EndOfStream(iface);
577     graph = ((NullRendererImpl*)This->pin.pinInfo.pFilter)->filterInfo.pGraph;
578     if (graph)
579     {
580         hr = IFilterGraph_QueryInterface(((NullRendererImpl*)This->pin.pinInfo.pFilter)->filterInfo.pGraph, &IID_IMediaEventSink, (LPVOID*)&pEventSink);
581         if (SUCCEEDED(hr))
582         {
583             hr = IMediaEventSink_Notify(pEventSink, EC_COMPLETE, S_OK, 0);
584             IMediaEventSink_Release(pEventSink);
585         }
586     }
587
588     return hr;
589 }
590
591 static const IPinVtbl NullRenderer_InputPin_Vtbl =
592 {
593     InputPin_QueryInterface,
594     IPinImpl_AddRef,
595     InputPin_Release,
596     InputPin_Connect,
597     InputPin_ReceiveConnection,
598     IPinImpl_Disconnect,
599     IPinImpl_ConnectedTo,
600     IPinImpl_ConnectionMediaType,
601     IPinImpl_QueryPinInfo,
602     IPinImpl_QueryDirection,
603     IPinImpl_QueryId,
604     IPinImpl_QueryAccept,
605     IPinImpl_EnumMediaTypes,
606     IPinImpl_QueryInternalConnections,
607     NullRenderer_InputPin_EndOfStream,
608     InputPin_BeginFlush,
609     InputPin_EndFlush,
610     InputPin_NewSegment
611 };