server: Make FD_WINE_NONBLOCKING into a separate field.
[wine.git] / dlls / qcap / smartteefilter.c
blobfc98666196cf62d912a9a23a8a712736e71677dc
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 "qcap_private.h"
23 WINE_DEFAULT_DEBUG_CHANNEL(qcap);
25 typedef struct {
26 struct strmbase_filter filter;
27 struct strmbase_sink sink;
28 struct strmbase_source capture, preview;
29 } SmartTeeFilter;
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);
45 if (index == 0)
46 return &filter->sink.pin;
47 else if (index == 1)
48 return &filter->capture.pin;
49 else if (index == 2)
50 return &filter->preview.pin;
51 return NULL;
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);
62 free(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);
81 if (!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. */
85 return S_OK;
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);
92 HRESULT hr;
93 TRACE("(%p)->(%d, %p)\n", This, iPosition, amt);
94 if (iPosition)
95 return S_FALSE;
96 EnterCriticalSection(&This->filter.filter_cs);
97 if (This->sink.pin.peer)
99 CopyMediaType(amt, &This->sink.pin.mt);
100 hr = S_OK;
102 else
103 hr = S_FALSE;
104 LeaveCriticalSection(&This->filter.filter_cs);
105 return hr;
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;
114 else
115 return E_NOINTERFACE;
117 IUnknown_AddRef((IUnknown *)*out);
118 return S_OK;
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;
128 HRESULT hr;
130 hr = IMediaSample_GetTime(inputSample, &startTime, &endTime);
131 if (hr == S_OK)
133 else if (hr == VFW_S_NO_STOP_TIME)
134 haveEndTime = FALSE;
135 else if (hr == VFW_E_SAMPLE_TIME_NOT_SET)
136 haveStartTime = haveEndTime = FALSE;
137 else
138 goto end;
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;
146 goto end;
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);
164 if (hr == S_OK)
166 else if (hr == VFW_S_NO_STOP_TIME)
167 haveEndTime = FALSE;
168 else if (hr == VFW_E_MEDIA_TIME_NOT_SET)
169 haveStartTime = haveEndTime = FALSE;
170 else
171 goto end;
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;
177 if (hr == S_OK) {
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;
188 end:
189 if (mediaType)
190 DeleteMediaType(mediaType);
191 if (FAILED(hr) && outputSample) {
192 IMediaSample_Release(outputSample);
193 outputSample = NULL;
195 *pOutputSample = outputSample;
196 return hr;
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);
220 if (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);
232 if (previewSample)
233 IMediaSample_Release(previewSample);
235 /* FIXME: how to merge the HRESULTs from the 2 pins? */
236 if (SUCCEEDED(hrCapture))
237 return hrCapture;
238 else
239 return hrPreview;
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);
253 return S_OK;
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);
260 HRESULT hr = S_OK;
262 EnterCriticalSection(&filter->filter.filter_cs);
264 if (!filter->sink.pin.peer)
265 hr = VFW_E_NOT_CONNECTED;
266 else if (!index)
267 CopyMediaType(mt, &filter->sink.pin.mt);
268 else
269 hr = VFW_S_NO_MORE_ITEMS;
271 LeaveCriticalSection(&filter->filter.filter_cs);
272 return hr;
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);
296 return S_OK;
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;
320 HRESULT hr;
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);
329 if (FAILED(hr))
331 strmbase_filter_cleanup(&object->filter);
332 free(object);
333 return hr;
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;
341 return S_OK;