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
32 #include "qcap_main.h"
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(qcap
);
39 struct strmbase_filter filter
;
40 struct strmbase_sink sink
;
41 struct strmbase_source capture
, preview
;
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
,
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
);
77 return &filter
->sink
.pin
;
79 return &filter
->capture
.pin
;
81 return &filter
->preview
.pin
;
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
,
106 BaseInputPinImpl_Connect
,
107 BaseInputPinImpl_ReceiveConnection
,
108 BasePinImpl_Disconnect
,
109 BasePinImpl_ConnectedTo
,
110 BasePinImpl_ConnectionMediaType
,
111 BasePinImpl_QueryPinInfo
,
112 BasePinImpl_QueryDirection
,
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
);
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. */
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
);
139 TRACE("(%p)->(%d, %p)\n", This
, iPosition
, amt
);
142 EnterCriticalSection(&This
->filter
.csFilter
);
143 if (This
->sink
.pin
.peer
)
145 CopyMediaType(amt
, &This
->sink
.pin
.mt
);
150 LeaveCriticalSection(&This
->filter
.csFilter
);
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
;
161 return E_NOINTERFACE
;
163 IUnknown_AddRef((IUnknown
*)*out
);
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
;
176 hr
= IMediaSample_GetTime(inputSample
, &startTime
, &endTime
);
179 else if (hr
== VFW_S_NO_STOP_TIME
)
181 else if (hr
== VFW_E_SAMPLE_TIME_NOT_SET
)
182 haveStartTime
= haveEndTime
= FALSE
;
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
;
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
);
212 else if (hr
== VFW_S_NO_STOP_TIME
)
214 else if (hr
== VFW_E_MEDIA_TIME_NOT_SET
)
215 haveStartTime
= haveEndTime
= FALSE
;
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
;
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
;
236 DeleteMediaType(mediaType
);
237 if (FAILED(hr
) && outputSample
) {
238 IMediaSample_Release(outputSample
);
241 *pOutputSample
= outputSample
;
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
);
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
);
279 IMediaSample_Release(previewSample
);
281 /* FIXME: how to merge the HRESULTs from the 2 pins? */
282 if (SUCCEEDED(hrCapture
))
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
,
300 BaseOutputPinImpl_Connect
,
301 BaseOutputPinImpl_ReceiveConnection
,
302 BaseOutputPinImpl_Disconnect
,
303 BasePinImpl_ConnectedTo
,
304 BasePinImpl_ConnectionMediaType
,
305 BasePinImpl_QueryPinInfo
,
306 BasePinImpl_QueryDirection
,
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
);
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
);
329 EnterCriticalSection(&filter
->filter
.csFilter
);
331 if (!filter
->sink
.pin
.peer
)
332 hr
= VFW_E_NOT_CONNECTED
;
334 CopyMediaType(mt
, &filter
->sink
.pin
.mt
);
336 hr
= VFW_S_NO_MORE_ITEMS
;
338 LeaveCriticalSection(&filter
->filter
.csFilter
);
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
,
364 BaseOutputPinImpl_Connect
,
365 BaseOutputPinImpl_ReceiveConnection
,
366 BaseOutputPinImpl_Disconnect
,
367 BasePinImpl_ConnectedTo
,
368 BasePinImpl_ConnectionMediaType
,
369 BasePinImpl_QueryPinInfo
,
370 BasePinImpl_QueryDirection
,
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
);
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
;
413 if (!(object
= CoTaskMemAlloc(sizeof(*object
))))
415 *phr
= E_OUTOFMEMORY
;
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
);
427 strmbase_filter_cleanup(&object
->filter
);
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
);
437 return &object
->filter
.IUnknown_inner
;