qasf: Implement ASF Reader filter init_stream and cleanup_stream.
[wine.git] / dlls / qasf / asfreader.c
blob935619bea4fb1dee516276c35951f697b034dbf3
1 /*
2 * WM ASF reader
4 * Copyright 2020 Jactry Zeng for CodeWeavers
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 "qasf_private.h"
23 #include "mediaobj.h"
24 #include "propsys.h"
25 #include "initguid.h"
26 #include "wmsdkidl.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
30 struct asf_stream
32 struct strmbase_source source;
33 DWORD index;
36 struct asf_reader
38 struct strmbase_filter filter;
39 IFileSourceFilter IFileSourceFilter_iface;
41 AM_MEDIA_TYPE media_type;
42 WCHAR *file_name;
44 HRESULT result;
45 WMT_STATUS status;
46 CRITICAL_SECTION status_cs;
47 CONDITION_VARIABLE status_cv;
49 IWMReaderCallback *callback;
50 IWMReader *reader;
52 UINT stream_count;
53 struct asf_stream streams[16];
56 static inline struct asf_stream *impl_from_strmbase_pin(struct strmbase_pin *iface)
58 return CONTAINING_RECORD(iface, struct asf_stream, source.pin);
61 static inline struct asf_reader *asf_reader_from_asf_stream(struct asf_stream *stream)
63 return CONTAINING_RECORD(stream, struct asf_reader, streams[stream->index]);
66 static HRESULT asf_stream_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *media_type)
68 struct asf_stream *stream = impl_from_strmbase_pin(iface);
69 struct asf_reader *filter = asf_reader_from_asf_stream(stream);
70 IWMOutputMediaProps *props;
71 WM_MEDIA_TYPE *mt;
72 DWORD size, i = 0;
73 HRESULT hr;
75 TRACE("iface %p, media_type %p.\n", iface, media_type);
77 if (FAILED(hr = IWMReader_GetOutputFormat(filter->reader, stream->index, i, &props)))
78 return hr;
79 if (FAILED(hr = IWMOutputMediaProps_GetMediaType(props, NULL, &size)))
81 IWMOutputMediaProps_Release(props);
82 return hr;
84 if (!(mt = malloc(size)))
86 IWMOutputMediaProps_Release(props);
87 return E_OUTOFMEMORY;
92 if (SUCCEEDED(hr = IWMOutputMediaProps_GetMediaType(props, mt, &size))
93 && IsEqualGUID(&mt->majortype, &media_type->majortype)
94 && IsEqualGUID(&mt->subtype, &media_type->subtype))
96 IWMOutputMediaProps_Release(props);
97 break;
100 IWMOutputMediaProps_Release(props);
101 } while (SUCCEEDED(hr = IWMReader_GetOutputFormat(filter->reader, stream->index, ++i, &props)));
103 free(mt);
104 return hr;
107 static HRESULT asf_stream_get_media_type(struct strmbase_pin *iface, unsigned int index, AM_MEDIA_TYPE *media_type)
109 struct asf_stream *stream = impl_from_strmbase_pin(iface);
110 struct asf_reader *filter = asf_reader_from_asf_stream(stream);
111 IWMOutputMediaProps *props;
112 WM_MEDIA_TYPE *mt;
113 DWORD size;
114 HRESULT hr;
116 TRACE("iface %p, index %u, media_type %p.\n", iface, index, media_type);
118 if (FAILED(IWMReader_GetOutputFormat(filter->reader, stream->index, index, &props)))
119 return VFW_S_NO_MORE_ITEMS;
120 if (FAILED(hr = IWMOutputMediaProps_GetMediaType(props, NULL, &size)))
122 IWMOutputMediaProps_Release(props);
123 return hr;
125 if (!(mt = malloc(size)))
127 IWMOutputMediaProps_Release(props);
128 return E_OUTOFMEMORY;
131 hr = IWMOutputMediaProps_GetMediaType(props, mt, &size);
132 if (SUCCEEDED(hr))
133 hr = CopyMediaType(media_type, (AM_MEDIA_TYPE *)mt);
135 free(mt);
136 IWMOutputMediaProps_Release(props);
137 return hr;
140 static inline struct asf_reader *impl_from_strmbase_filter(struct strmbase_filter *iface)
142 return CONTAINING_RECORD(iface, struct asf_reader, filter);
145 static struct strmbase_pin *asf_reader_get_pin(struct strmbase_filter *iface, unsigned int index)
147 struct asf_reader *filter = impl_from_strmbase_filter(iface);
148 struct strmbase_pin *pin = NULL;
150 TRACE("iface %p, index %u.\n", iface, index);
152 EnterCriticalSection(&filter->filter.filter_cs);
153 if (index < filter->stream_count)
154 pin = &filter->streams[index].source.pin;
155 LeaveCriticalSection(&filter->filter.filter_cs);
157 return pin;
160 static void asf_reader_destroy(struct strmbase_filter *iface)
162 struct asf_reader *filter = impl_from_strmbase_filter(iface);
163 struct strmbase_source *source;
165 while (filter->stream_count--)
167 source = &filter->streams[filter->stream_count].source;
168 if (source->pin.peer) IPin_Disconnect(source->pin.peer);
169 IPin_Disconnect(&source->pin.IPin_iface);
170 strmbase_source_cleanup(source);
173 free(filter->file_name);
174 FreeMediaType(&filter->media_type);
175 IWMReaderCallback_Release(filter->callback);
176 IWMReader_Release(filter->reader);
178 strmbase_filter_cleanup(&filter->filter);
180 filter->status_cs.DebugInfo->Spare[0] = 0;
181 DeleteCriticalSection(&filter->status_cs);
183 free(filter);
186 static HRESULT asf_reader_query_interface(struct strmbase_filter *iface, REFIID iid, void **out)
188 struct asf_reader *filter = impl_from_strmbase_filter(iface);
190 if (IsEqualGUID(iid, &IID_IFileSourceFilter))
192 *out = &filter->IFileSourceFilter_iface;
193 IUnknown_AddRef((IUnknown *)*out);
194 return S_OK;
197 return E_NOINTERFACE;
200 static HRESULT asf_reader_init_stream(struct strmbase_filter *iface)
202 struct asf_reader *filter = impl_from_strmbase_filter(iface);
203 HRESULT hr = S_OK;
204 int i;
206 TRACE("iface %p\n", iface);
208 for (i = 0; i < filter->stream_count; ++i)
210 struct asf_stream *stream = filter->streams + i;
212 if (!stream->source.pin.peer)
213 continue;
215 if (FAILED(hr = IMemAllocator_Commit(stream->source.pAllocator)))
217 WARN("Failed to commit stream %u allocator, hr %#lx\n", i, hr);
218 break;
222 return hr;
225 static HRESULT asf_reader_cleanup_stream(struct strmbase_filter *iface)
227 struct asf_reader *filter = impl_from_strmbase_filter(iface);
228 HRESULT hr = S_OK;
229 int i;
231 TRACE("iface %p\n", iface);
233 for (i = 0; i < filter->stream_count; ++i)
235 struct asf_stream *stream = filter->streams + i;
237 if (!stream->source.pin.peer)
238 continue;
240 if (FAILED(hr = IMemAllocator_Decommit(stream->source.pAllocator)))
242 WARN("Failed to decommit stream %u allocator, hr %#lx\n", i, hr);
243 break;
247 return hr;
250 static const struct strmbase_filter_ops filter_ops =
252 .filter_get_pin = asf_reader_get_pin,
253 .filter_destroy = asf_reader_destroy,
254 .filter_query_interface = asf_reader_query_interface,
255 .filter_init_stream = asf_reader_init_stream,
256 .filter_cleanup_stream = asf_reader_cleanup_stream,
259 static HRESULT WINAPI asf_reader_DecideBufferSize(struct strmbase_source *iface,
260 IMemAllocator *allocator, ALLOCATOR_PROPERTIES *req_props)
262 struct asf_stream *stream = impl_from_strmbase_pin(&iface->pin);
263 unsigned int buffer_size = 16384;
264 ALLOCATOR_PROPERTIES ret_props;
266 TRACE("iface %p, allocator %p, req_props %p.\n", iface, allocator, req_props);
268 if (IsEqualGUID(&stream->source.pin.mt.formattype, &FORMAT_VideoInfo))
270 VIDEOINFOHEADER *format = (VIDEOINFOHEADER *)stream->source.pin.mt.pbFormat;
271 buffer_size = format->bmiHeader.biSizeImage;
273 else if (IsEqualGUID(&stream->source.pin.mt.formattype, &FORMAT_WaveFormatEx)
274 && (IsEqualGUID(&stream->source.pin.mt.subtype, &MEDIASUBTYPE_PCM)
275 || IsEqualGUID(&stream->source.pin.mt.subtype, &MEDIASUBTYPE_IEEE_FLOAT)))
277 WAVEFORMATEX *format = (WAVEFORMATEX *)stream->source.pin.mt.pbFormat;
278 buffer_size = format->nAvgBytesPerSec;
281 req_props->cBuffers = max(req_props->cBuffers, 1);
282 req_props->cbBuffer = max(req_props->cbBuffer, buffer_size);
283 req_props->cbAlign = max(req_props->cbAlign, 1);
284 return IMemAllocator_SetProperties(allocator, req_props, &ret_props);
287 static const struct strmbase_source_ops source_ops =
289 .base.pin_query_accept = asf_stream_query_accept,
290 .base.pin_get_media_type = asf_stream_get_media_type,
291 .pfnDecideAllocator = BaseOutputPinImpl_DecideAllocator,
292 .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection,
293 .pfnDecideBufferSize = asf_reader_DecideBufferSize,
296 static inline struct asf_reader *impl_from_IFileSourceFilter(IFileSourceFilter *iface)
298 return CONTAINING_RECORD(iface, struct asf_reader, IFileSourceFilter_iface);
301 static HRESULT WINAPI file_source_QueryInterface(IFileSourceFilter *iface, REFIID iid, void **out)
303 struct asf_reader *filter = impl_from_IFileSourceFilter(iface);
305 return IBaseFilter_QueryInterface(&filter->filter.IBaseFilter_iface, iid, out);
308 static ULONG WINAPI file_source_AddRef(IFileSourceFilter *iface)
310 struct asf_reader *filter = impl_from_IFileSourceFilter(iface);
312 return IBaseFilter_AddRef(&filter->filter.IBaseFilter_iface);
315 static ULONG WINAPI file_source_Release(IFileSourceFilter *iface)
317 struct asf_reader *filter = impl_from_IFileSourceFilter(iface);
319 return IBaseFilter_Release(&filter->filter.IBaseFilter_iface);
322 static HRESULT WINAPI file_source_Load(IFileSourceFilter *iface, LPCOLESTR file_name, const AM_MEDIA_TYPE *media_type)
324 struct asf_reader *filter = impl_from_IFileSourceFilter(iface);
325 HRESULT hr;
327 TRACE("filter %p, file_name %s, media_type %p.\n", filter, debugstr_w(file_name), media_type);
328 strmbase_dump_media_type(media_type);
330 if (!file_name)
331 return E_POINTER;
333 EnterCriticalSection(&filter->filter.filter_cs);
335 if (filter->file_name || !(filter->file_name = wcsdup(file_name)))
337 LeaveCriticalSection(&filter->filter.filter_cs);
338 return E_FAIL;
341 if (media_type && FAILED(hr = CopyMediaType(&filter->media_type, media_type)))
343 LeaveCriticalSection(&filter->filter.filter_cs);
344 return hr;
347 EnterCriticalSection(&filter->status_cs);
348 if (SUCCEEDED(hr = IWMReader_Open(filter->reader, filter->file_name, filter->callback, NULL)))
350 filter->status = -1;
351 while (filter->status != WMT_OPENED)
352 SleepConditionVariableCS(&filter->status_cv, &filter->status_cs, INFINITE);
353 hr = filter->result;
355 LeaveCriticalSection(&filter->status_cs);
357 if (FAILED(hr))
358 WARN("Failed to open WM reader, hr %#lx.\n", hr);
360 LeaveCriticalSection(&filter->filter.filter_cs);
362 return S_OK;
365 static HRESULT WINAPI file_source_GetCurFile(IFileSourceFilter *iface, LPOLESTR *file_name, AM_MEDIA_TYPE *media_type)
367 struct asf_reader *filter = impl_from_IFileSourceFilter(iface);
369 TRACE("filter %p, file_name %p, media_type %p.\n", filter, file_name, media_type);
371 if (!file_name)
372 return E_POINTER;
373 *file_name = NULL;
375 if (media_type)
377 media_type->majortype = filter->media_type.majortype;
378 media_type->subtype = filter->media_type.subtype;
379 media_type->lSampleSize = filter->media_type.lSampleSize;
380 media_type->pUnk = filter->media_type.pUnk;
381 media_type->cbFormat = filter->media_type.cbFormat;
384 if (filter->file_name)
386 *file_name = CoTaskMemAlloc((wcslen(filter->file_name) + 1) * sizeof(WCHAR));
387 wcscpy(*file_name, filter->file_name);
390 return S_OK;
393 static const IFileSourceFilterVtbl file_source_vtbl =
395 file_source_QueryInterface,
396 file_source_AddRef,
397 file_source_Release,
398 file_source_Load,
399 file_source_GetCurFile,
402 struct asf_callback
404 IWMReaderCallback IWMReaderCallback_iface;
405 LONG ref;
407 struct asf_reader *filter;
410 static inline struct asf_callback *impl_from_IWMReaderCallback(IWMReaderCallback *iface)
412 return CONTAINING_RECORD(iface, struct asf_callback, IWMReaderCallback_iface);
415 static HRESULT WINAPI reader_callback_QueryInterface(IWMReaderCallback *iface, const IID *iid, void **out)
417 struct asf_callback *callback = impl_from_IWMReaderCallback(iface);
419 TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out);
421 if (IsEqualGUID(iid, &IID_IUnknown)
422 || IsEqualGUID(iid, &IID_IWMStatusCallback)
423 || IsEqualGUID(iid, &IID_IWMReaderCallback))
424 *out = &callback->IWMReaderCallback_iface;
425 else
427 *out = NULL;
428 WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid));
429 return E_NOINTERFACE;
432 IUnknown_AddRef((IUnknown *)*out);
433 return S_OK;
436 static ULONG WINAPI reader_callback_AddRef(IWMReaderCallback *iface)
438 struct asf_callback *callback = impl_from_IWMReaderCallback(iface);
439 ULONG ref = InterlockedIncrement(&callback->ref);
441 TRACE("%p increasing ref to %lu.\n", callback, ref);
443 return ref;
446 static ULONG WINAPI reader_callback_Release(IWMReaderCallback *iface)
448 struct asf_callback *callback = impl_from_IWMReaderCallback(iface);
449 ULONG ref = InterlockedDecrement(&callback->ref);
451 TRACE("%p decreasing ref to %lu.\n", callback, ref);
453 if (!ref)
454 free(callback);
456 return ref;
459 static HRESULT WINAPI reader_callback_OnStatus(IWMReaderCallback *iface, WMT_STATUS status, HRESULT result,
460 WMT_ATTR_DATATYPE type, BYTE *value, void *context)
462 struct asf_reader *filter = impl_from_IWMReaderCallback(iface)->filter;
463 AM_MEDIA_TYPE stream_media_type = {{0}};
464 DWORD i, stream_count;
465 WCHAR name[MAX_PATH];
466 HRESULT hr;
468 TRACE("iface %p, status %d, result %#lx, type %d, value %p, context %p.\n",
469 iface, status, result, type, value, context);
471 switch (status)
473 case WMT_OPENED:
474 if (FAILED(hr = IWMReader_GetOutputCount(filter->reader, &stream_count)))
476 ERR("Failed to get WMReader output count, hr %#lx.\n", hr);
477 stream_count = 0;
479 if (stream_count > ARRAY_SIZE(filter->streams))
481 FIXME("Found %lu streams, not supported!\n", stream_count);
482 stream_count = ARRAY_SIZE(filter->streams);
485 for (i = 0; i < stream_count; ++i)
487 struct asf_stream *stream = filter->streams + i;
489 if (FAILED(hr = asf_stream_get_media_type(&stream->source.pin, 0, &stream_media_type)))
490 WARN("Failed to get stream media type, hr %#lx.\n", hr);
491 if (IsEqualGUID(&stream_media_type.majortype, &MEDIATYPE_Video))
492 swprintf(name, ARRAY_SIZE(name), L"Raw Video %u", stream->index);
493 else
494 swprintf(name, ARRAY_SIZE(name), L"Raw Audio %u", stream->index);
495 FreeMediaType(&stream_media_type);
497 strmbase_source_init(&stream->source, &filter->filter, name, &source_ops);
499 filter->stream_count = stream_count;
500 BaseFilterImpl_IncrementPinVersion(&filter->filter);
502 EnterCriticalSection(&filter->status_cs);
503 filter->result = result;
504 filter->status = WMT_OPENED;
505 LeaveCriticalSection(&filter->status_cs);
506 WakeConditionVariable(&filter->status_cv);
507 break;
509 default:
510 WARN("Ignoring status %#x.\n", status);
511 break;
514 return S_OK;
517 static HRESULT WINAPI reader_callback_OnSample(IWMReaderCallback *iface, DWORD output, QWORD time,
518 QWORD duration, DWORD flags, INSSBuffer *sample, void *context)
520 FIXME("iface %p, output %lu, time %I64u, duration %I64u, flags %#lx, sample %p, context %p stub!\n",
521 iface, output, time, duration, flags, sample, context);
522 return E_NOTIMPL;
525 static const IWMReaderCallbackVtbl reader_callback_vtbl =
527 reader_callback_QueryInterface,
528 reader_callback_AddRef,
529 reader_callback_Release,
530 reader_callback_OnStatus,
531 reader_callback_OnSample,
534 static HRESULT asf_callback_create(struct asf_reader *filter, IWMReaderCallback **out)
536 struct asf_callback *callback;
538 if (!(callback = calloc(1, sizeof(*callback))))
539 return E_OUTOFMEMORY;
541 callback->IWMReaderCallback_iface.lpVtbl = &reader_callback_vtbl;
542 callback->filter = filter;
543 callback->ref = 1;
545 *out = &callback->IWMReaderCallback_iface;
546 return S_OK;
549 HRESULT asf_reader_create(IUnknown *outer, IUnknown **out)
551 struct asf_reader *object;
552 HRESULT hr;
553 int i;
555 if (!(object = calloc(1, sizeof(*object))))
556 return E_OUTOFMEMORY;
558 if (FAILED(hr = WMCreateReader(NULL, 0, &object->reader)))
560 free(object);
561 return hr;
563 if (FAILED(hr = asf_callback_create(object, &object->callback)))
565 IWMReader_Release(object->reader);
566 free(object);
567 return hr;
570 for (i = 0; i < ARRAY_SIZE(object->streams); ++i) object->streams[i].index = i;
571 strmbase_filter_init(&object->filter, outer, &CLSID_WMAsfReader, &filter_ops);
572 object->IFileSourceFilter_iface.lpVtbl = &file_source_vtbl;
574 InitializeCriticalSection(&object->status_cs);
575 object->status_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": status_cs");
577 TRACE("Created WM ASF reader %p.\n", object);
578 *out = &object->filter.IUnknown_inner;
580 return S_OK;