quartz/avidec: Call IMemInputPin::Receive() directly.
[wine.git] / dlls / qcap / smartteefilter.c
blobfd0dd621e329c84159e12091c8addff72759d3d0
1 /*
2 * Implementation of the SmartTee filter
4 * Copyright 2015 Damjan Jovanovic
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.
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.
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
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wtypes.h"
28 #include "wingdi.h"
29 #include "winuser.h"
30 #include "dshow.h"
32 #include "qcap_main.h"
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(qcap);
38 typedef struct {
39 struct strmbase_filter filter;
40 struct strmbase_sink sink;
41 struct strmbase_source capture, preview;
42 } SmartTeeFilter;
44 static inline SmartTeeFilter *impl_from_strmbase_filter(struct strmbase_filter *filter)
46 return CONTAINING_RECORD(filter, SmartTeeFilter, filter);
49 static inline SmartTeeFilter *impl_from_strmbase_pin(struct strmbase_pin *pin)
51 return impl_from_strmbase_filter(pin->filter);
54 static const IBaseFilterVtbl SmartTeeFilterVtbl = {
55 BaseFilterImpl_QueryInterface,
56 BaseFilterImpl_AddRef,
57 BaseFilterImpl_Release,
58 BaseFilterImpl_GetClassID,
59 BaseFilterImpl_Stop,
60 BaseFilterImpl_Pause,
61 BaseFilterImpl_Run,
62 BaseFilterImpl_GetState,
63 BaseFilterImpl_SetSyncSource,
64 BaseFilterImpl_GetSyncSource,
65 BaseFilterImpl_EnumPins,
66 BaseFilterImpl_FindPin,
67 BaseFilterImpl_QueryFilterInfo,
68 BaseFilterImpl_JoinFilterGraph,
69 BaseFilterImpl_QueryVendorInfo
72 static struct strmbase_pin *smart_tee_get_pin(struct strmbase_filter *iface, unsigned int index)
74 SmartTeeFilter *filter = impl_from_strmbase_filter(iface);
76 if (index == 0)
77 return &filter->sink.pin;
78 else if (index == 1)
79 return &filter->capture.pin;
80 else if (index == 2)
81 return &filter->preview.pin;
82 return NULL;
85 static void smart_tee_destroy(struct strmbase_filter *iface)
87 SmartTeeFilter *filter = impl_from_strmbase_filter(iface);
89 strmbase_sink_cleanup(&filter->sink);
90 strmbase_source_cleanup(&filter->capture);
91 strmbase_source_cleanup(&filter->preview);
92 strmbase_filter_cleanup(&filter->filter);
93 CoTaskMemFree(filter);
96 static const struct strmbase_filter_ops filter_ops =
98 .filter_get_pin = smart_tee_get_pin,
99 .filter_destroy = smart_tee_destroy,
102 static const IPinVtbl SmartTeeFilterInputVtbl = {
103 BasePinImpl_QueryInterface,
104 BasePinImpl_AddRef,
105 BasePinImpl_Release,
106 BaseInputPinImpl_Connect,
107 BaseInputPinImpl_ReceiveConnection,
108 BasePinImpl_Disconnect,
109 BasePinImpl_ConnectedTo,
110 BasePinImpl_ConnectionMediaType,
111 BasePinImpl_QueryPinInfo,
112 BasePinImpl_QueryDirection,
113 BasePinImpl_QueryId,
114 BasePinImpl_QueryAccept,
115 BasePinImpl_EnumMediaTypes,
116 BasePinImpl_QueryInternalConnections,
117 BaseInputPinImpl_EndOfStream,
118 BaseInputPinImpl_BeginFlush,
119 BaseInputPinImpl_EndFlush,
120 BaseInputPinImpl_NewSegment
123 static HRESULT sink_query_accept(struct strmbase_pin *base, const AM_MEDIA_TYPE *pmt)
125 SmartTeeFilter *This = impl_from_strmbase_pin(base);
126 TRACE("(%p, AM_MEDIA_TYPE(%p))\n", This, pmt);
127 if (!pmt)
128 return VFW_E_TYPE_NOT_ACCEPTED;
129 /* We'll take any media type, but the output pins will later
130 * struggle to connect downstream. */
131 return S_OK;
134 static HRESULT sink_get_media_type(struct strmbase_pin *base,
135 unsigned int iPosition, AM_MEDIA_TYPE *amt)
137 SmartTeeFilter *This = impl_from_strmbase_pin(base);
138 HRESULT hr;
139 TRACE("(%p)->(%d, %p)\n", This, iPosition, amt);
140 if (iPosition)
141 return S_FALSE;
142 EnterCriticalSection(&This->filter.csFilter);
143 if (This->sink.pin.peer)
145 CopyMediaType(amt, &This->sink.pin.mt);
146 hr = S_OK;
148 else
149 hr = S_FALSE;
150 LeaveCriticalSection(&This->filter.csFilter);
151 return hr;
154 static HRESULT sink_query_interface(struct strmbase_pin *iface, REFIID iid, void **out)
156 SmartTeeFilter *filter = impl_from_strmbase_pin(iface);
158 if (IsEqualGUID(iid, &IID_IMemInputPin))
159 *out = &filter->sink.IMemInputPin_iface;
160 else
161 return E_NOINTERFACE;
163 IUnknown_AddRef((IUnknown *)*out);
164 return S_OK;
167 static HRESULT copy_sample(IMediaSample *inputSample, IMemAllocator *allocator, IMediaSample **pOutputSample)
169 REFERENCE_TIME startTime, endTime;
170 BOOL haveStartTime = TRUE, haveEndTime = TRUE;
171 IMediaSample *outputSample = NULL;
172 BYTE *ptrIn, *ptrOut;
173 AM_MEDIA_TYPE *mediaType = NULL;
174 HRESULT hr;
176 hr = IMediaSample_GetTime(inputSample, &startTime, &endTime);
177 if (hr == S_OK)
179 else if (hr == VFW_S_NO_STOP_TIME)
180 haveEndTime = FALSE;
181 else if (hr == VFW_E_SAMPLE_TIME_NOT_SET)
182 haveStartTime = haveEndTime = FALSE;
183 else
184 goto end;
186 hr = IMemAllocator_GetBuffer(allocator, &outputSample,
187 haveStartTime ? &startTime : NULL, haveEndTime ? &endTime : NULL, 0);
188 if (FAILED(hr)) goto end;
189 if (IMediaSample_GetSize(outputSample) < IMediaSample_GetActualDataLength(inputSample)) {
190 ERR("insufficient space in sample\n");
191 hr = VFW_E_BUFFER_OVERFLOW;
192 goto end;
195 hr = IMediaSample_SetTime(outputSample, haveStartTime ? &startTime : NULL, haveEndTime ? &endTime : NULL);
196 if (FAILED(hr)) goto end;
198 hr = IMediaSample_GetPointer(inputSample, &ptrIn);
199 if (FAILED(hr)) goto end;
200 hr = IMediaSample_GetPointer(outputSample, &ptrOut);
201 if (FAILED(hr)) goto end;
202 memcpy(ptrOut, ptrIn, IMediaSample_GetActualDataLength(inputSample));
203 IMediaSample_SetActualDataLength(outputSample, IMediaSample_GetActualDataLength(inputSample));
205 hr = IMediaSample_SetDiscontinuity(outputSample, IMediaSample_IsDiscontinuity(inputSample) == S_OK);
206 if (FAILED(hr)) goto end;
208 haveStartTime = haveEndTime = TRUE;
209 hr = IMediaSample_GetMediaTime(inputSample, &startTime, &endTime);
210 if (hr == S_OK)
212 else if (hr == VFW_S_NO_STOP_TIME)
213 haveEndTime = FALSE;
214 else if (hr == VFW_E_MEDIA_TIME_NOT_SET)
215 haveStartTime = haveEndTime = FALSE;
216 else
217 goto end;
218 hr = IMediaSample_SetMediaTime(outputSample, haveStartTime ? &startTime : NULL, haveEndTime ? &endTime : NULL);
219 if (FAILED(hr)) goto end;
221 hr = IMediaSample_GetMediaType(inputSample, &mediaType);
222 if (FAILED(hr)) goto end;
223 if (hr == S_OK) {
224 hr = IMediaSample_SetMediaType(outputSample, mediaType);
225 if (FAILED(hr)) goto end;
228 hr = IMediaSample_SetPreroll(outputSample, IMediaSample_IsPreroll(inputSample) == S_OK);
229 if (FAILED(hr)) goto end;
231 hr = IMediaSample_SetSyncPoint(outputSample, IMediaSample_IsSyncPoint(inputSample) == S_OK);
232 if (FAILED(hr)) goto end;
234 end:
235 if (mediaType)
236 DeleteMediaType(mediaType);
237 if (FAILED(hr) && outputSample) {
238 IMediaSample_Release(outputSample);
239 outputSample = NULL;
241 *pOutputSample = outputSample;
242 return hr;
245 static HRESULT WINAPI SmartTeeFilterInput_Receive(struct strmbase_sink *base, IMediaSample *inputSample)
247 SmartTeeFilter *This = impl_from_strmbase_pin(&base->pin);
248 IMediaSample *captureSample = NULL;
249 IMediaSample *previewSample = NULL;
250 HRESULT hrCapture = VFW_E_NOT_CONNECTED, hrPreview = VFW_E_NOT_CONNECTED;
252 TRACE("(%p)->(%p)\n", This, inputSample);
254 /* Modifying the image coming out of one pin doesn't modify the image
255 * coming out of the other. MSDN claims the filter doesn't copy,
256 * but unless it somehow uses copy-on-write, I just don't see how
257 * that's possible. */
259 /* FIXME: we should ideally do each of these in a separate thread */
260 EnterCriticalSection(&This->filter.csFilter);
261 if (This->capture.pin.peer)
262 hrCapture = copy_sample(inputSample, This->capture.pAllocator, &captureSample);
263 LeaveCriticalSection(&This->filter.csFilter);
264 if (SUCCEEDED(hrCapture))
265 hrCapture = BaseOutputPinImpl_Deliver(&This->capture, captureSample);
266 if (captureSample)
267 IMediaSample_Release(captureSample);
269 EnterCriticalSection(&This->filter.csFilter);
270 if (This->preview.pin.peer)
271 hrPreview = copy_sample(inputSample, This->preview.pAllocator, &previewSample);
272 LeaveCriticalSection(&This->filter.csFilter);
273 /* No timestamps on preview stream: */
274 if (SUCCEEDED(hrPreview))
275 hrPreview = IMediaSample_SetTime(previewSample, NULL, NULL);
276 if (SUCCEEDED(hrPreview))
277 hrPreview = BaseOutputPinImpl_Deliver(&This->preview, previewSample);
278 if (previewSample)
279 IMediaSample_Release(previewSample);
281 /* FIXME: how to merge the HRESULTs from the 2 pins? */
282 if (SUCCEEDED(hrCapture))
283 return hrCapture;
284 else
285 return hrPreview;
288 static const struct strmbase_sink_ops sink_ops =
290 .base.pin_query_accept = sink_query_accept,
291 .base.pin_get_media_type = sink_get_media_type,
292 .base.pin_query_interface = sink_query_interface,
293 .pfnReceive = SmartTeeFilterInput_Receive,
296 static const IPinVtbl SmartTeeFilterCaptureVtbl = {
297 BasePinImpl_QueryInterface,
298 BasePinImpl_AddRef,
299 BasePinImpl_Release,
300 BaseOutputPinImpl_Connect,
301 BaseOutputPinImpl_ReceiveConnection,
302 BaseOutputPinImpl_Disconnect,
303 BasePinImpl_ConnectedTo,
304 BasePinImpl_ConnectionMediaType,
305 BasePinImpl_QueryPinInfo,
306 BasePinImpl_QueryDirection,
307 BasePinImpl_QueryId,
308 BasePinImpl_QueryAccept,
309 BasePinImpl_EnumMediaTypes,
310 BasePinImpl_QueryInternalConnections,
311 BaseOutputPinImpl_EndOfStream,
312 BaseOutputPinImpl_BeginFlush,
313 BaseOutputPinImpl_EndFlush,
314 BasePinImpl_NewSegment
317 static HRESULT capture_query_accept(struct strmbase_pin *base, const AM_MEDIA_TYPE *amt)
319 FIXME("(%p) stub\n", base);
320 return S_OK;
323 static HRESULT source_get_media_type(struct strmbase_pin *iface,
324 unsigned int index, AM_MEDIA_TYPE *mt)
326 SmartTeeFilter *filter = impl_from_strmbase_pin(iface);
327 HRESULT hr = S_OK;
329 EnterCriticalSection(&filter->filter.csFilter);
331 if (!filter->sink.pin.peer)
332 hr = VFW_E_NOT_CONNECTED;
333 else if (!index)
334 CopyMediaType(mt, &filter->sink.pin.mt);
335 else
336 hr = VFW_S_NO_MORE_ITEMS;
338 LeaveCriticalSection(&filter->filter.csFilter);
339 return hr;
342 static HRESULT WINAPI SmartTeeFilterCapture_DecideAllocator(struct strmbase_source *base,
343 IMemInputPin *pPin, IMemAllocator **pAlloc)
345 SmartTeeFilter *This = impl_from_strmbase_pin(&base->pin);
346 TRACE("(%p, %p, %p)\n", This, pPin, pAlloc);
347 *pAlloc = This->sink.pAllocator;
348 IMemAllocator_AddRef(This->sink.pAllocator);
349 return IMemInputPin_NotifyAllocator(pPin, This->sink.pAllocator, TRUE);
352 static const struct strmbase_source_ops capture_ops =
354 .base.pin_query_accept = capture_query_accept,
355 .base.pin_get_media_type = source_get_media_type,
356 .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection,
357 .pfnDecideAllocator = SmartTeeFilterCapture_DecideAllocator,
360 static const IPinVtbl SmartTeeFilterPreviewVtbl = {
361 BasePinImpl_QueryInterface,
362 BasePinImpl_AddRef,
363 BasePinImpl_Release,
364 BaseOutputPinImpl_Connect,
365 BaseOutputPinImpl_ReceiveConnection,
366 BaseOutputPinImpl_Disconnect,
367 BasePinImpl_ConnectedTo,
368 BasePinImpl_ConnectionMediaType,
369 BasePinImpl_QueryPinInfo,
370 BasePinImpl_QueryDirection,
371 BasePinImpl_QueryId,
372 BasePinImpl_QueryAccept,
373 BasePinImpl_EnumMediaTypes,
374 BasePinImpl_QueryInternalConnections,
375 BaseOutputPinImpl_EndOfStream,
376 BaseOutputPinImpl_BeginFlush,
377 BaseOutputPinImpl_EndFlush,
378 BasePinImpl_NewSegment
381 static HRESULT preview_query_accept(struct strmbase_pin *base, const AM_MEDIA_TYPE *amt)
383 FIXME("(%p) stub\n", base);
384 return S_OK;
387 static HRESULT WINAPI SmartTeeFilterPreview_DecideAllocator(struct strmbase_source *base,
388 IMemInputPin *pPin, IMemAllocator **pAlloc)
390 SmartTeeFilter *This = impl_from_strmbase_pin(&base->pin);
391 TRACE("(%p, %p, %p)\n", This, pPin, pAlloc);
392 *pAlloc = This->sink.pAllocator;
393 IMemAllocator_AddRef(This->sink.pAllocator);
394 return IMemInputPin_NotifyAllocator(pPin, This->sink.pAllocator, TRUE);
397 static const struct strmbase_source_ops preview_ops =
399 .base.pin_query_accept = preview_query_accept,
400 .base.pin_get_media_type = source_get_media_type,
401 .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection,
402 .pfnDecideAllocator = SmartTeeFilterPreview_DecideAllocator,
405 IUnknown* WINAPI QCAP_createSmartTeeFilter(IUnknown *outer, HRESULT *phr)
407 static const WCHAR captureW[] = {'C','a','p','t','u','r','e',0};
408 static const WCHAR previewW[] = {'P','r','e','v','i','e','w',0};
409 static const WCHAR inputW[] = {'I','n','p','u','t',0};
410 SmartTeeFilter *object;
411 HRESULT hr;
413 if (!(object = CoTaskMemAlloc(sizeof(*object))))
415 *phr = E_OUTOFMEMORY;
416 return NULL;
418 memset(object, 0, sizeof(*object));
420 strmbase_filter_init(&object->filter, &SmartTeeFilterVtbl, outer, &CLSID_SmartTee, &filter_ops);
421 strmbase_sink_init(&object->sink, &SmartTeeFilterInputVtbl, &object->filter, inputW, &sink_ops, NULL);
422 hr = CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC_SERVER,
423 &IID_IMemAllocator, (void **)&object->sink.pAllocator);
424 if (FAILED(hr))
426 *phr = hr;
427 strmbase_filter_cleanup(&object->filter);
428 return NULL;
431 strmbase_source_init(&object->capture, &SmartTeeFilterCaptureVtbl,
432 &object->filter, captureW, &capture_ops);
433 strmbase_source_init(&object->preview, &SmartTeeFilterPreviewVtbl,
434 &object->filter, previewW, &preview_ops);
436 *phr = S_OK;
437 return &object->filter.IUnknown_inner;