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 "qcap_private.h"
23 WINE_DEFAULT_DEBUG_CHANNEL(qcap
);
26 struct strmbase_filter filter
;
27 struct strmbase_sink sink
;
28 struct strmbase_source capture
, preview
;
31 static inline SmartTeeFilter
*impl_from_strmbase_filter(struct strmbase_filter
*filter
)
33 return CONTAINING_RECORD(filter
, SmartTeeFilter
, filter
);
36 static inline SmartTeeFilter
*impl_from_strmbase_pin(struct strmbase_pin
*pin
)
38 return impl_from_strmbase_filter(pin
->filter
);
41 static struct strmbase_pin
*smart_tee_get_pin(struct strmbase_filter
*iface
, unsigned int index
)
43 SmartTeeFilter
*filter
= impl_from_strmbase_filter(iface
);
46 return &filter
->sink
.pin
;
48 return &filter
->capture
.pin
;
50 return &filter
->preview
.pin
;
54 static void smart_tee_destroy(struct strmbase_filter
*iface
)
56 SmartTeeFilter
*filter
= impl_from_strmbase_filter(iface
);
58 strmbase_sink_cleanup(&filter
->sink
);
59 strmbase_source_cleanup(&filter
->capture
);
60 strmbase_source_cleanup(&filter
->preview
);
61 strmbase_filter_cleanup(&filter
->filter
);
65 static HRESULT
smart_tee_wait_state(struct strmbase_filter
*iface
, DWORD timeout
)
67 return iface
->state
== State_Paused
? VFW_S_CANT_CUE
: S_OK
;
70 static const struct strmbase_filter_ops filter_ops
=
72 .filter_get_pin
= smart_tee_get_pin
,
73 .filter_destroy
= smart_tee_destroy
,
74 .filter_wait_state
= smart_tee_wait_state
,
77 static HRESULT
sink_query_accept(struct strmbase_pin
*base
, const AM_MEDIA_TYPE
*pmt
)
79 SmartTeeFilter
*This
= impl_from_strmbase_pin(base
);
80 TRACE("(%p, AM_MEDIA_TYPE(%p))\n", This
, pmt
);
82 return VFW_E_TYPE_NOT_ACCEPTED
;
83 /* We'll take any media type, but the output pins will later
84 * struggle to connect downstream. */
88 static HRESULT
sink_get_media_type(struct strmbase_pin
*base
,
89 unsigned int iPosition
, AM_MEDIA_TYPE
*amt
)
91 SmartTeeFilter
*This
= impl_from_strmbase_pin(base
);
93 TRACE("(%p)->(%d, %p)\n", This
, iPosition
, amt
);
96 EnterCriticalSection(&This
->filter
.filter_cs
);
97 if (This
->sink
.pin
.peer
)
99 CopyMediaType(amt
, &This
->sink
.pin
.mt
);
104 LeaveCriticalSection(&This
->filter
.filter_cs
);
108 static HRESULT
sink_query_interface(struct strmbase_pin
*iface
, REFIID iid
, void **out
)
110 SmartTeeFilter
*filter
= impl_from_strmbase_pin(iface
);
112 if (IsEqualGUID(iid
, &IID_IMemInputPin
))
113 *out
= &filter
->sink
.IMemInputPin_iface
;
115 return E_NOINTERFACE
;
117 IUnknown_AddRef((IUnknown
*)*out
);
121 static HRESULT
copy_sample(IMediaSample
*inputSample
, IMemAllocator
*allocator
, IMediaSample
**pOutputSample
)
123 REFERENCE_TIME startTime
, endTime
;
124 BOOL haveStartTime
= TRUE
, haveEndTime
= TRUE
;
125 IMediaSample
*outputSample
= NULL
;
126 BYTE
*ptrIn
, *ptrOut
;
127 AM_MEDIA_TYPE
*mediaType
= NULL
;
130 hr
= IMediaSample_GetTime(inputSample
, &startTime
, &endTime
);
133 else if (hr
== VFW_S_NO_STOP_TIME
)
135 else if (hr
== VFW_E_SAMPLE_TIME_NOT_SET
)
136 haveStartTime
= haveEndTime
= FALSE
;
140 hr
= IMemAllocator_GetBuffer(allocator
, &outputSample
,
141 haveStartTime
? &startTime
: NULL
, haveEndTime
? &endTime
: NULL
, 0);
142 if (FAILED(hr
)) goto end
;
143 if (IMediaSample_GetSize(outputSample
) < IMediaSample_GetActualDataLength(inputSample
)) {
144 ERR("insufficient space in sample\n");
145 hr
= VFW_E_BUFFER_OVERFLOW
;
149 hr
= IMediaSample_SetTime(outputSample
, haveStartTime
? &startTime
: NULL
, haveEndTime
? &endTime
: NULL
);
150 if (FAILED(hr
)) goto end
;
152 hr
= IMediaSample_GetPointer(inputSample
, &ptrIn
);
153 if (FAILED(hr
)) goto end
;
154 hr
= IMediaSample_GetPointer(outputSample
, &ptrOut
);
155 if (FAILED(hr
)) goto end
;
156 memcpy(ptrOut
, ptrIn
, IMediaSample_GetActualDataLength(inputSample
));
157 IMediaSample_SetActualDataLength(outputSample
, IMediaSample_GetActualDataLength(inputSample
));
159 hr
= IMediaSample_SetDiscontinuity(outputSample
, IMediaSample_IsDiscontinuity(inputSample
) == S_OK
);
160 if (FAILED(hr
)) goto end
;
162 haveStartTime
= haveEndTime
= TRUE
;
163 hr
= IMediaSample_GetMediaTime(inputSample
, &startTime
, &endTime
);
166 else if (hr
== VFW_S_NO_STOP_TIME
)
168 else if (hr
== VFW_E_MEDIA_TIME_NOT_SET
)
169 haveStartTime
= haveEndTime
= FALSE
;
172 hr
= IMediaSample_SetMediaTime(outputSample
, haveStartTime
? &startTime
: NULL
, haveEndTime
? &endTime
: NULL
);
173 if (FAILED(hr
)) goto end
;
175 hr
= IMediaSample_GetMediaType(inputSample
, &mediaType
);
176 if (FAILED(hr
)) goto end
;
178 hr
= IMediaSample_SetMediaType(outputSample
, mediaType
);
179 if (FAILED(hr
)) goto end
;
182 hr
= IMediaSample_SetPreroll(outputSample
, IMediaSample_IsPreroll(inputSample
) == S_OK
);
183 if (FAILED(hr
)) goto end
;
185 hr
= IMediaSample_SetSyncPoint(outputSample
, IMediaSample_IsSyncPoint(inputSample
) == S_OK
);
186 if (FAILED(hr
)) goto end
;
190 DeleteMediaType(mediaType
);
191 if (FAILED(hr
) && outputSample
) {
192 IMediaSample_Release(outputSample
);
195 *pOutputSample
= outputSample
;
199 static HRESULT WINAPI
SmartTeeFilterInput_Receive(struct strmbase_sink
*base
, IMediaSample
*inputSample
)
201 SmartTeeFilter
*This
= impl_from_strmbase_pin(&base
->pin
);
202 IMediaSample
*captureSample
= NULL
;
203 IMediaSample
*previewSample
= NULL
;
204 HRESULT hrCapture
= VFW_E_NOT_CONNECTED
, hrPreview
= VFW_E_NOT_CONNECTED
;
206 TRACE("(%p)->(%p)\n", This
, inputSample
);
208 /* Modifying the image coming out of one pin doesn't modify the image
209 * coming out of the other. MSDN claims the filter doesn't copy,
210 * but unless it somehow uses copy-on-write, I just don't see how
211 * that's possible. */
213 /* FIXME: we should ideally do each of these in a separate thread */
214 EnterCriticalSection(&This
->filter
.filter_cs
);
215 if (This
->capture
.pin
.peer
)
216 hrCapture
= copy_sample(inputSample
, This
->capture
.pAllocator
, &captureSample
);
217 LeaveCriticalSection(&This
->filter
.filter_cs
);
218 if (SUCCEEDED(hrCapture
) && This
->capture
.pMemInputPin
)
219 hrCapture
= IMemInputPin_Receive(This
->capture
.pMemInputPin
, captureSample
);
221 IMediaSample_Release(captureSample
);
223 EnterCriticalSection(&This
->filter
.filter_cs
);
224 if (This
->preview
.pin
.peer
)
225 hrPreview
= copy_sample(inputSample
, This
->preview
.pAllocator
, &previewSample
);
226 LeaveCriticalSection(&This
->filter
.filter_cs
);
227 /* No timestamps on preview stream: */
228 if (SUCCEEDED(hrPreview
))
229 hrPreview
= IMediaSample_SetTime(previewSample
, NULL
, NULL
);
230 if (SUCCEEDED(hrPreview
) && This
->preview
.pMemInputPin
)
231 hrPreview
= IMemInputPin_Receive(This
->preview
.pMemInputPin
, previewSample
);
233 IMediaSample_Release(previewSample
);
235 /* FIXME: how to merge the HRESULTs from the 2 pins? */
236 if (SUCCEEDED(hrCapture
))
242 static const struct strmbase_sink_ops sink_ops
=
244 .base
.pin_query_accept
= sink_query_accept
,
245 .base
.pin_get_media_type
= sink_get_media_type
,
246 .base
.pin_query_interface
= sink_query_interface
,
247 .pfnReceive
= SmartTeeFilterInput_Receive
,
250 static HRESULT
capture_query_accept(struct strmbase_pin
*base
, const AM_MEDIA_TYPE
*amt
)
252 FIXME("(%p) stub\n", base
);
256 static HRESULT
source_get_media_type(struct strmbase_pin
*iface
,
257 unsigned int index
, AM_MEDIA_TYPE
*mt
)
259 SmartTeeFilter
*filter
= impl_from_strmbase_pin(iface
);
262 EnterCriticalSection(&filter
->filter
.filter_cs
);
264 if (!filter
->sink
.pin
.peer
)
265 hr
= VFW_E_NOT_CONNECTED
;
267 CopyMediaType(mt
, &filter
->sink
.pin
.mt
);
269 hr
= VFW_S_NO_MORE_ITEMS
;
271 LeaveCriticalSection(&filter
->filter
.filter_cs
);
275 static HRESULT WINAPI
SmartTeeFilterCapture_DecideAllocator(struct strmbase_source
*base
,
276 IMemInputPin
*pPin
, IMemAllocator
**pAlloc
)
278 SmartTeeFilter
*This
= impl_from_strmbase_pin(&base
->pin
);
279 TRACE("(%p, %p, %p)\n", This
, pPin
, pAlloc
);
280 *pAlloc
= This
->sink
.pAllocator
;
281 IMemAllocator_AddRef(This
->sink
.pAllocator
);
282 return IMemInputPin_NotifyAllocator(pPin
, This
->sink
.pAllocator
, TRUE
);
285 static const struct strmbase_source_ops capture_ops
=
287 .base
.pin_query_accept
= capture_query_accept
,
288 .base
.pin_get_media_type
= source_get_media_type
,
289 .pfnAttemptConnection
= BaseOutputPinImpl_AttemptConnection
,
290 .pfnDecideAllocator
= SmartTeeFilterCapture_DecideAllocator
,
293 static HRESULT
preview_query_accept(struct strmbase_pin
*base
, const AM_MEDIA_TYPE
*amt
)
295 FIXME("(%p) stub\n", base
);
299 static HRESULT WINAPI
SmartTeeFilterPreview_DecideAllocator(struct strmbase_source
*base
,
300 IMemInputPin
*pPin
, IMemAllocator
**pAlloc
)
302 SmartTeeFilter
*This
= impl_from_strmbase_pin(&base
->pin
);
303 TRACE("(%p, %p, %p)\n", This
, pPin
, pAlloc
);
304 *pAlloc
= This
->sink
.pAllocator
;
305 IMemAllocator_AddRef(This
->sink
.pAllocator
);
306 return IMemInputPin_NotifyAllocator(pPin
, This
->sink
.pAllocator
, TRUE
);
309 static const struct strmbase_source_ops preview_ops
=
311 .base
.pin_query_accept
= preview_query_accept
,
312 .base
.pin_get_media_type
= source_get_media_type
,
313 .pfnAttemptConnection
= BaseOutputPinImpl_AttemptConnection
,
314 .pfnDecideAllocator
= SmartTeeFilterPreview_DecideAllocator
,
317 HRESULT
smart_tee_create(IUnknown
*outer
, IUnknown
**out
)
319 SmartTeeFilter
*object
;
322 if (!(object
= calloc(1, sizeof(*object
))))
323 return E_OUTOFMEMORY
;
325 strmbase_filter_init(&object
->filter
, outer
, &CLSID_SmartTee
, &filter_ops
);
326 strmbase_sink_init(&object
->sink
, &object
->filter
, L
"Input", &sink_ops
, NULL
);
327 hr
= CoCreateInstance(&CLSID_MemoryAllocator
, NULL
, CLSCTX_INPROC_SERVER
,
328 &IID_IMemAllocator
, (void **)&object
->sink
.pAllocator
);
331 strmbase_filter_cleanup(&object
->filter
);
336 strmbase_source_init(&object
->capture
, &object
->filter
, L
"Capture", &capture_ops
);
337 strmbase_source_init(&object
->preview
, &object
->filter
, L
"Preview", &preview_ops
);
339 TRACE("Created smart tee %p.\n", object
);
340 *out
= &object
->filter
.IUnknown_inner
;