mfmediaengine: Remove unnecessary import library.
[wine.git] / dlls / quartz / acmwrapper.c
bloba07bdfef0ca9d6c8ad27e4282f7d38ead8a918d3
1 /*
2 * ACM Wrapper
4 * Copyright 2005 Christian Costa
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 "quartz_private.h"
23 #include "uuids.h"
24 #include "mmreg.h"
25 #include "windef.h"
26 #include "winbase.h"
27 #include "dshow.h"
28 #include "strmif.h"
29 #include "vfwmsgs.h"
30 #include "msacm.h"
32 #include <assert.h>
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
38 struct acm_wrapper
40 struct strmbase_filter filter;
42 struct strmbase_source source;
43 IQualityControl source_IQualityControl_iface;
44 IQualityControl *source_qc_sink;
45 struct strmbase_passthrough passthrough;
47 struct strmbase_sink sink;
49 AM_MEDIA_TYPE mt;
50 HACMSTREAM has;
51 LPWAVEFORMATEX pWfOut;
53 LONGLONG lasttime_real;
54 LONGLONG lasttime_sent;
57 static struct acm_wrapper *impl_from_strmbase_filter(struct strmbase_filter *iface)
59 return CONTAINING_RECORD(iface, struct acm_wrapper, filter);
62 static HRESULT acm_wrapper_sink_query_interface(struct strmbase_pin *iface, REFIID iid, void **out)
64 struct acm_wrapper *filter = impl_from_strmbase_filter(iface->filter);
66 if (IsEqualGUID(iid, &IID_IMemInputPin))
67 *out = &filter->sink.IMemInputPin_iface;
68 else
69 return E_NOINTERFACE;
71 IUnknown_AddRef((IUnknown *)*out);
72 return S_OK;
75 static HRESULT acm_wrapper_sink_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *mt)
77 return S_OK;
80 static HRESULT WINAPI acm_wrapper_sink_Receive(struct strmbase_sink *iface, IMediaSample *pSample)
82 struct acm_wrapper *This = impl_from_strmbase_filter(iface->pin.filter);
83 IMediaSample* pOutSample = NULL;
84 DWORD cbDstStream, cbSrcStream;
85 LPBYTE pbDstStream;
86 LPBYTE pbSrcStream = NULL;
87 ACMSTREAMHEADER ash;
88 BOOL unprepare_header = FALSE, preroll;
89 MMRESULT res;
90 HRESULT hr;
91 LONGLONG tStart = -1, tStop = -1, tMed;
92 LONGLONG mtStart = -1, mtStop = -1, mtMed;
94 /* We do not expect pin connection state to change while the filter is
95 * running. This guarantee is necessary, since otherwise we would have to
96 * take the filter lock, and we can't take the filter lock from a streaming
97 * thread. */
98 if (!This->source.pMemInputPin)
100 WARN("Source is not connected, returning VFW_E_NOT_CONNECTED.\n");
101 return VFW_E_NOT_CONNECTED;
104 if (This->filter.state == State_Stopped)
105 return VFW_E_WRONG_STATE;
107 if (This->sink.flushing)
108 return S_FALSE;
110 hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
111 if (FAILED(hr))
113 ERR("Cannot get pointer to sample data (%x)\n", hr);
114 return hr;
117 preroll = (IMediaSample_IsPreroll(pSample) == S_OK);
119 IMediaSample_GetTime(pSample, &tStart, &tStop);
120 if (IMediaSample_GetMediaTime(pSample, &mtStart, &mtStop) != S_OK)
121 mtStart = mtStop = -1;
122 cbSrcStream = IMediaSample_GetActualDataLength(pSample);
124 /* Prevent discontinuities when codecs 'absorb' data but not give anything back in return */
125 if (IMediaSample_IsDiscontinuity(pSample) == S_OK)
127 This->lasttime_real = tStart;
128 This->lasttime_sent = tStart;
130 else if (This->lasttime_real == tStart)
131 tStart = This->lasttime_sent;
132 else
133 WARN("Discontinuity\n");
135 tMed = tStart;
136 mtMed = mtStart;
138 TRACE("Sample data ptr = %p, size = %d\n", pbSrcStream, cbSrcStream);
140 ash.pbSrc = pbSrcStream;
141 ash.cbSrcLength = cbSrcStream;
143 while(hr == S_OK && ash.cbSrcLength)
145 hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->source, &pOutSample, NULL, NULL, 0);
146 if (FAILED(hr))
148 ERR("Unable to get delivery buffer (%x)\n", hr);
149 return hr;
151 IMediaSample_SetPreroll(pOutSample, preroll);
153 hr = IMediaSample_SetActualDataLength(pOutSample, 0);
154 assert(hr == S_OK);
156 hr = IMediaSample_GetPointer(pOutSample, &pbDstStream);
157 if (FAILED(hr)) {
158 ERR("Unable to get pointer to buffer (%x)\n", hr);
159 goto error;
161 cbDstStream = IMediaSample_GetSize(pOutSample);
163 ash.cbStruct = sizeof(ash);
164 ash.fdwStatus = 0;
165 ash.dwUser = 0;
166 ash.pbDst = pbDstStream;
167 ash.cbDstLength = cbDstStream;
169 if ((res = acmStreamPrepareHeader(This->has, &ash, 0))) {
170 ERR("Cannot prepare header %d\n", res);
171 goto error;
173 unprepare_header = TRUE;
175 if (IMediaSample_IsDiscontinuity(pSample) == S_OK)
177 res = acmStreamConvert(This->has, &ash, ACM_STREAMCONVERTF_START);
178 IMediaSample_SetDiscontinuity(pOutSample, TRUE);
179 /* One sample could be converted to multiple packets */
180 IMediaSample_SetDiscontinuity(pSample, FALSE);
182 else
184 res = acmStreamConvert(This->has, &ash, 0);
185 IMediaSample_SetDiscontinuity(pOutSample, FALSE);
188 if (res)
190 if(res != MMSYSERR_MOREDATA)
191 ERR("Cannot convert data header %d\n", res);
192 goto error;
195 TRACE("used in %u/%u, used out %u/%u\n", ash.cbSrcLengthUsed, ash.cbSrcLength, ash.cbDstLengthUsed, ash.cbDstLength);
197 hr = IMediaSample_SetActualDataLength(pOutSample, ash.cbDstLengthUsed);
198 assert(hr == S_OK);
200 /* Bug in acm codecs? It apparently uses the input, but doesn't necessarily output immediately */
201 if (!ash.cbSrcLengthUsed)
203 WARN("Sample was skipped? Outputted: %u\n", ash.cbDstLengthUsed);
204 ash.cbSrcLength = 0;
205 goto error;
208 TRACE("Sample start time: %s.\n", debugstr_time(tStart));
209 if (ash.cbSrcLengthUsed == cbSrcStream)
211 IMediaSample_SetTime(pOutSample, &tStart, &tStop);
212 tStart = tMed = tStop;
214 else if (tStop != tStart)
216 tMed = tStop - tStart;
217 tMed = tStart + tMed * ash.cbSrcLengthUsed / cbSrcStream;
218 IMediaSample_SetTime(pOutSample, &tStart, &tMed);
219 tStart = tMed;
221 else
223 ERR("No valid timestamp found\n");
224 IMediaSample_SetTime(pOutSample, NULL, NULL);
227 if (mtStart < 0) {
228 IMediaSample_SetMediaTime(pOutSample, NULL, NULL);
229 } else if (ash.cbSrcLengthUsed == cbSrcStream) {
230 IMediaSample_SetMediaTime(pOutSample, &mtStart, &mtStop);
231 mtStart = mtMed = mtStop;
232 } else if (mtStop >= mtStart) {
233 mtMed = mtStop - mtStart;
234 mtMed = mtStart + mtMed * ash.cbSrcLengthUsed / cbSrcStream;
235 IMediaSample_SetMediaTime(pOutSample, &mtStart, &mtMed);
236 mtStart = mtMed;
237 } else {
238 IMediaSample_SetMediaTime(pOutSample, NULL, NULL);
241 TRACE("Sample stop time: %s\n", debugstr_time(tStart));
243 hr = IMemInputPin_Receive(This->source.pMemInputPin, pOutSample);
244 if (hr != S_OK && hr != VFW_E_NOT_CONNECTED) {
245 if (FAILED(hr))
246 ERR("Error sending sample (%x)\n", hr);
247 goto error;
250 error:
251 if (unprepare_header && (res = acmStreamUnprepareHeader(This->has, &ash, 0)))
252 ERR("Cannot unprepare header %d\n", res);
253 unprepare_header = FALSE;
254 ash.pbSrc += ash.cbSrcLengthUsed;
255 ash.cbSrcLength -= ash.cbSrcLengthUsed;
257 IMediaSample_Release(pOutSample);
258 pOutSample = NULL;
262 This->lasttime_real = tStop;
263 This->lasttime_sent = tMed;
265 return hr;
268 static BOOL is_audio_subtype(const GUID *guid)
270 return !memcmp(&guid->Data2, &MEDIATYPE_Audio.Data2, sizeof(GUID) - sizeof(int));
273 static HRESULT acm_wrapper_sink_connect(struct strmbase_sink *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
275 struct acm_wrapper *filter = impl_from_strmbase_filter(iface->pin.filter);
276 const WAVEFORMATEX *wfx = (WAVEFORMATEX *)mt->pbFormat;
277 HACMSTREAM drv;
278 MMRESULT res;
280 if (!IsEqualGUID(&mt->majortype, &MEDIATYPE_Audio) || !is_audio_subtype(&mt->subtype)
281 || !IsEqualGUID(&mt->formattype, &FORMAT_WaveFormatEx) || !wfx
282 || wfx->wFormatTag == WAVE_FORMAT_PCM || wfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
283 return VFW_E_TYPE_NOT_ACCEPTED;
285 CopyMediaType(&filter->mt, mt);
286 filter->mt.subtype.Data1 = WAVE_FORMAT_PCM;
287 filter->pWfOut = (WAVEFORMATEX *)filter->mt.pbFormat;
288 filter->pWfOut->wFormatTag = WAVE_FORMAT_PCM;
289 filter->pWfOut->wBitsPerSample = 16;
290 filter->pWfOut->nBlockAlign = filter->pWfOut->wBitsPerSample * filter->pWfOut->nChannels / 8;
291 filter->pWfOut->cbSize = 0;
292 filter->pWfOut->nAvgBytesPerSec = filter->pWfOut->nChannels * filter->pWfOut->nSamplesPerSec
293 * (filter->pWfOut->wBitsPerSample / 8);
295 if ((res = acmStreamOpen(&drv, NULL, (WAVEFORMATEX *)wfx, filter->pWfOut, NULL, 0, 0, 0)))
297 ERR("Failed to open stream, error %u.\n", res);
298 FreeMediaType(&filter->mt);
299 return VFW_E_TYPE_NOT_ACCEPTED;
302 filter->has = drv;
304 return S_OK;
307 static void acm_wrapper_sink_disconnect(struct strmbase_sink *iface)
309 struct acm_wrapper *filter = impl_from_strmbase_filter(iface->pin.filter);
311 if (filter->has)
312 acmStreamClose(filter->has, 0);
313 filter->has = 0;
314 filter->lasttime_real = filter->lasttime_sent = -1;
317 static const struct strmbase_sink_ops sink_ops =
319 .base.pin_query_interface = acm_wrapper_sink_query_interface,
320 .base.pin_query_accept = acm_wrapper_sink_query_accept,
321 .pfnReceive = acm_wrapper_sink_Receive,
322 .sink_connect = acm_wrapper_sink_connect,
323 .sink_disconnect = acm_wrapper_sink_disconnect,
326 static HRESULT acm_wrapper_source_query_interface(struct strmbase_pin *iface, REFIID iid, void **out)
328 struct acm_wrapper *filter = impl_from_strmbase_filter(iface->filter);
330 if (IsEqualGUID(iid, &IID_IQualityControl))
331 *out = &filter->source_IQualityControl_iface;
332 else if (IsEqualGUID(iid, &IID_IMediaSeeking))
333 *out = &filter->passthrough.IMediaSeeking_iface;
334 else
335 return E_NOINTERFACE;
337 IUnknown_AddRef((IUnknown *)*out);
338 return S_OK;
341 static HRESULT acm_wrapper_source_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *mt)
343 struct acm_wrapper *filter = impl_from_strmbase_filter(iface->filter);
345 if (IsEqualGUID(&mt->majortype, &filter->mt.majortype)
346 && (IsEqualGUID(&mt->subtype, &filter->mt.subtype)
347 || IsEqualGUID(&filter->mt.subtype, &GUID_NULL)))
348 return S_OK;
349 return S_FALSE;
352 static HRESULT acm_wrapper_source_get_media_type(struct strmbase_pin *iface,
353 unsigned int index, AM_MEDIA_TYPE *mt)
355 struct acm_wrapper *filter = impl_from_strmbase_filter(iface->filter);
357 if (index)
358 return VFW_S_NO_MORE_ITEMS;
359 CopyMediaType(mt, &filter->mt);
360 return S_OK;
363 static HRESULT WINAPI acm_wrapper_source_DecideBufferSize(struct strmbase_source *iface,
364 IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
366 struct acm_wrapper *filter = impl_from_strmbase_filter(iface->pin.filter);
367 ALLOCATOR_PROPERTIES actual;
369 if (!ppropInputRequest->cbAlign)
370 ppropInputRequest->cbAlign = 1;
372 if (ppropInputRequest->cbBuffer < filter->pWfOut->nAvgBytesPerSec / 2)
373 ppropInputRequest->cbBuffer = filter->pWfOut->nAvgBytesPerSec / 2;
375 if (!ppropInputRequest->cBuffers)
376 ppropInputRequest->cBuffers = 1;
378 return IMemAllocator_SetProperties(pAlloc, ppropInputRequest, &actual);
381 static const struct strmbase_source_ops source_ops =
383 .base.pin_query_interface = acm_wrapper_source_query_interface,
384 .base.pin_query_accept = acm_wrapper_source_query_accept,
385 .base.pin_get_media_type = acm_wrapper_source_get_media_type,
386 .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection,
387 .pfnDecideAllocator = BaseOutputPinImpl_DecideAllocator,
388 .pfnDecideBufferSize = acm_wrapper_source_DecideBufferSize,
391 static struct acm_wrapper *impl_from_source_IQualityControl(IQualityControl *iface)
393 return CONTAINING_RECORD(iface, struct acm_wrapper, source_IQualityControl_iface);
396 static HRESULT WINAPI acm_wrapper_source_qc_QueryInterface(IQualityControl *iface,
397 REFIID iid, void **out)
399 struct acm_wrapper *filter = impl_from_source_IQualityControl(iface);
400 return IPin_QueryInterface(&filter->source.pin.IPin_iface, iid, out);
403 static ULONG WINAPI acm_wrapper_source_qc_AddRef(IQualityControl *iface)
405 struct acm_wrapper *filter = impl_from_source_IQualityControl(iface);
406 return IPin_AddRef(&filter->source.pin.IPin_iface);
409 static ULONG WINAPI acm_wrapper_source_qc_Release(IQualityControl *iface)
411 struct acm_wrapper *filter = impl_from_source_IQualityControl(iface);
412 return IPin_Release(&filter->source.pin.IPin_iface);
415 static HRESULT WINAPI acm_wrapper_source_qc_Notify(IQualityControl *iface,
416 IBaseFilter *sender, Quality q)
418 struct acm_wrapper *filter = impl_from_source_IQualityControl(iface);
419 IQualityControl *peer;
420 HRESULT hr = S_OK;
422 TRACE("filter %p, sender %p, type %#x, proportion %u, late %s, timestamp %s.\n",
423 filter, sender, q.Type, q.Proportion, debugstr_time(q.Late), debugstr_time(q.TimeStamp));
425 if (filter->source_qc_sink)
426 return IQualityControl_Notify(filter->source_qc_sink, &filter->filter.IBaseFilter_iface, q);
428 if (filter->sink.pin.peer
429 && SUCCEEDED(IPin_QueryInterface(filter->sink.pin.peer, &IID_IQualityControl, (void **)&peer)))
431 hr = IQualityControl_Notify(peer, &filter->filter.IBaseFilter_iface, q);
432 IQualityControl_Release(peer);
434 return hr;
437 static HRESULT WINAPI acm_wrapper_source_qc_SetSink(IQualityControl *iface, IQualityControl *sink)
439 struct acm_wrapper *filter = impl_from_source_IQualityControl(iface);
441 TRACE("filter %p, sink %p.\n", filter, sink);
443 filter->source_qc_sink = sink;
445 return S_OK;
448 static const IQualityControlVtbl source_qc_vtbl =
450 acm_wrapper_source_qc_QueryInterface,
451 acm_wrapper_source_qc_AddRef,
452 acm_wrapper_source_qc_Release,
453 acm_wrapper_source_qc_Notify,
454 acm_wrapper_source_qc_SetSink,
457 static struct strmbase_pin *acm_wrapper_get_pin(struct strmbase_filter *iface, unsigned int index)
459 struct acm_wrapper *filter = impl_from_strmbase_filter(iface);
461 if (index == 0)
462 return &filter->sink.pin;
463 else if (index == 1)
464 return &filter->source.pin;
465 return NULL;
468 static void acm_wrapper_destroy(struct strmbase_filter *iface)
470 struct acm_wrapper *filter = impl_from_strmbase_filter(iface);
472 if (filter->sink.pin.peer)
473 IPin_Disconnect(filter->sink.pin.peer);
474 IPin_Disconnect(&filter->sink.pin.IPin_iface);
476 if (filter->source.pin.peer)
477 IPin_Disconnect(filter->source.pin.peer);
478 IPin_Disconnect(&filter->source.pin.IPin_iface);
480 strmbase_sink_cleanup(&filter->sink);
481 strmbase_source_cleanup(&filter->source);
482 strmbase_passthrough_cleanup(&filter->passthrough);
484 FreeMediaType(&filter->mt);
485 strmbase_filter_cleanup(&filter->filter);
486 free(filter);
489 static HRESULT acm_wrapper_init_stream(struct strmbase_filter *iface)
491 struct acm_wrapper *filter = impl_from_strmbase_filter(iface);
492 HRESULT hr;
494 if (filter->source.pin.peer && FAILED(hr = IMemAllocator_Commit(filter->source.pAllocator)))
495 ERR("Failed to commit allocator, hr %#x.\n", hr);
496 return S_OK;
499 static HRESULT acm_wrapper_cleanup_stream(struct strmbase_filter *iface)
501 struct acm_wrapper *filter = impl_from_strmbase_filter(iface);
503 if (filter->source.pin.peer)
504 IMemAllocator_Decommit(filter->source.pAllocator);
505 return S_OK;
508 static const struct strmbase_filter_ops filter_ops =
510 .filter_get_pin = acm_wrapper_get_pin,
511 .filter_destroy = acm_wrapper_destroy,
512 .filter_init_stream = acm_wrapper_init_stream,
513 .filter_cleanup_stream = acm_wrapper_cleanup_stream,
516 HRESULT acm_wrapper_create(IUnknown *outer, IUnknown **out)
518 struct acm_wrapper *object;
520 if (!(object = calloc(1, sizeof(*object))))
521 return E_OUTOFMEMORY;
523 strmbase_filter_init(&object->filter, outer, &CLSID_ACMWrapper, &filter_ops);
525 strmbase_sink_init(&object->sink, &object->filter, L"In", &sink_ops, NULL);
527 strmbase_source_init(&object->source, &object->filter, L"Out", &source_ops);
528 object->source_IQualityControl_iface.lpVtbl = &source_qc_vtbl;
529 strmbase_passthrough_init(&object->passthrough, (IUnknown *)&object->source.pin.IPin_iface);
530 ISeekingPassThru_Init(&object->passthrough.ISeekingPassThru_iface, FALSE,
531 &object->sink.pin.IPin_iface);
533 object->lasttime_real = object->lasttime_sent = -1;
535 TRACE("Created ACM wrapper %p.\n", object);
536 *out = &object->filter.IUnknown_inner;
538 return S_OK;