qasf: Wait for IWMReader_Open to complete in ASF Reader Load.
[wine.git] / dlls / qasf / asfreader.c
blobd5b6483ada32fdd4212e23ff8909f25a68347042
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 const struct strmbase_filter_ops filter_ops =
202 .filter_get_pin = asf_reader_get_pin,
203 .filter_destroy = asf_reader_destroy,
204 .filter_query_interface = asf_reader_query_interface,
207 static HRESULT WINAPI asf_reader_DecideBufferSize(struct strmbase_source *iface,
208 IMemAllocator *allocator, ALLOCATOR_PROPERTIES *req_props)
210 struct asf_stream *stream = impl_from_strmbase_pin(&iface->pin);
211 unsigned int buffer_size = 16384;
212 ALLOCATOR_PROPERTIES ret_props;
214 TRACE("iface %p, allocator %p, req_props %p.\n", iface, allocator, req_props);
216 if (IsEqualGUID(&stream->source.pin.mt.formattype, &FORMAT_VideoInfo))
218 VIDEOINFOHEADER *format = (VIDEOINFOHEADER *)stream->source.pin.mt.pbFormat;
219 buffer_size = format->bmiHeader.biSizeImage;
221 else if (IsEqualGUID(&stream->source.pin.mt.formattype, &FORMAT_WaveFormatEx)
222 && (IsEqualGUID(&stream->source.pin.mt.subtype, &MEDIASUBTYPE_PCM)
223 || IsEqualGUID(&stream->source.pin.mt.subtype, &MEDIASUBTYPE_IEEE_FLOAT)))
225 WAVEFORMATEX *format = (WAVEFORMATEX *)stream->source.pin.mt.pbFormat;
226 buffer_size = format->nAvgBytesPerSec;
229 req_props->cBuffers = max(req_props->cBuffers, 1);
230 req_props->cbBuffer = max(req_props->cbBuffer, buffer_size);
231 req_props->cbAlign = max(req_props->cbAlign, 1);
232 return IMemAllocator_SetProperties(allocator, req_props, &ret_props);
235 static const struct strmbase_source_ops source_ops =
237 .base.pin_query_accept = asf_stream_query_accept,
238 .base.pin_get_media_type = asf_stream_get_media_type,
239 .pfnDecideAllocator = BaseOutputPinImpl_DecideAllocator,
240 .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection,
241 .pfnDecideBufferSize = asf_reader_DecideBufferSize,
244 static inline struct asf_reader *impl_from_IFileSourceFilter(IFileSourceFilter *iface)
246 return CONTAINING_RECORD(iface, struct asf_reader, IFileSourceFilter_iface);
249 static HRESULT WINAPI file_source_QueryInterface(IFileSourceFilter *iface, REFIID iid, void **out)
251 struct asf_reader *filter = impl_from_IFileSourceFilter(iface);
253 return IBaseFilter_QueryInterface(&filter->filter.IBaseFilter_iface, iid, out);
256 static ULONG WINAPI file_source_AddRef(IFileSourceFilter *iface)
258 struct asf_reader *filter = impl_from_IFileSourceFilter(iface);
260 return IBaseFilter_AddRef(&filter->filter.IBaseFilter_iface);
263 static ULONG WINAPI file_source_Release(IFileSourceFilter *iface)
265 struct asf_reader *filter = impl_from_IFileSourceFilter(iface);
267 return IBaseFilter_Release(&filter->filter.IBaseFilter_iface);
270 static HRESULT WINAPI file_source_Load(IFileSourceFilter *iface, LPCOLESTR file_name, const AM_MEDIA_TYPE *media_type)
272 struct asf_reader *filter = impl_from_IFileSourceFilter(iface);
273 HRESULT hr;
275 TRACE("filter %p, file_name %s, media_type %p.\n", filter, debugstr_w(file_name), media_type);
276 strmbase_dump_media_type(media_type);
278 if (!file_name)
279 return E_POINTER;
281 EnterCriticalSection(&filter->filter.filter_cs);
283 if (filter->file_name || !(filter->file_name = wcsdup(file_name)))
285 LeaveCriticalSection(&filter->filter.filter_cs);
286 return E_FAIL;
289 if (media_type && FAILED(hr = CopyMediaType(&filter->media_type, media_type)))
291 LeaveCriticalSection(&filter->filter.filter_cs);
292 return hr;
295 EnterCriticalSection(&filter->status_cs);
296 if (SUCCEEDED(hr = IWMReader_Open(filter->reader, filter->file_name, filter->callback, NULL)))
298 filter->status = -1;
299 while (filter->status != WMT_OPENED)
300 SleepConditionVariableCS(&filter->status_cv, &filter->status_cs, INFINITE);
301 hr = filter->result;
303 LeaveCriticalSection(&filter->status_cs);
305 if (FAILED(hr))
306 WARN("Failed to open WM reader, hr %#lx.\n", hr);
308 LeaveCriticalSection(&filter->filter.filter_cs);
310 return S_OK;
313 static HRESULT WINAPI file_source_GetCurFile(IFileSourceFilter *iface, LPOLESTR *file_name, AM_MEDIA_TYPE *media_type)
315 struct asf_reader *filter = impl_from_IFileSourceFilter(iface);
317 TRACE("filter %p, file_name %p, media_type %p.\n", filter, file_name, media_type);
319 if (!file_name)
320 return E_POINTER;
321 *file_name = NULL;
323 if (media_type)
325 media_type->majortype = filter->media_type.majortype;
326 media_type->subtype = filter->media_type.subtype;
327 media_type->lSampleSize = filter->media_type.lSampleSize;
328 media_type->pUnk = filter->media_type.pUnk;
329 media_type->cbFormat = filter->media_type.cbFormat;
332 if (filter->file_name)
334 *file_name = CoTaskMemAlloc((wcslen(filter->file_name) + 1) * sizeof(WCHAR));
335 wcscpy(*file_name, filter->file_name);
338 return S_OK;
341 static const IFileSourceFilterVtbl file_source_vtbl =
343 file_source_QueryInterface,
344 file_source_AddRef,
345 file_source_Release,
346 file_source_Load,
347 file_source_GetCurFile,
350 struct asf_callback
352 IWMReaderCallback IWMReaderCallback_iface;
353 LONG ref;
355 struct asf_reader *filter;
358 static inline struct asf_callback *impl_from_IWMReaderCallback(IWMReaderCallback *iface)
360 return CONTAINING_RECORD(iface, struct asf_callback, IWMReaderCallback_iface);
363 static HRESULT WINAPI reader_callback_QueryInterface(IWMReaderCallback *iface, const IID *iid, void **out)
365 struct asf_callback *callback = impl_from_IWMReaderCallback(iface);
367 TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out);
369 if (IsEqualGUID(iid, &IID_IUnknown)
370 || IsEqualGUID(iid, &IID_IWMStatusCallback)
371 || IsEqualGUID(iid, &IID_IWMReaderCallback))
372 *out = &callback->IWMReaderCallback_iface;
373 else
375 *out = NULL;
376 WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid));
377 return E_NOINTERFACE;
380 IUnknown_AddRef((IUnknown *)*out);
381 return S_OK;
384 static ULONG WINAPI reader_callback_AddRef(IWMReaderCallback *iface)
386 struct asf_callback *callback = impl_from_IWMReaderCallback(iface);
387 ULONG ref = InterlockedIncrement(&callback->ref);
389 TRACE("%p increasing ref to %lu.\n", callback, ref);
391 return ref;
394 static ULONG WINAPI reader_callback_Release(IWMReaderCallback *iface)
396 struct asf_callback *callback = impl_from_IWMReaderCallback(iface);
397 ULONG ref = InterlockedDecrement(&callback->ref);
399 TRACE("%p decreasing ref to %lu.\n", callback, ref);
401 if (!ref)
402 free(callback);
404 return ref;
407 static HRESULT WINAPI reader_callback_OnStatus(IWMReaderCallback *iface, WMT_STATUS status, HRESULT result,
408 WMT_ATTR_DATATYPE type, BYTE *value, void *context)
410 struct asf_reader *filter = impl_from_IWMReaderCallback(iface)->filter;
411 AM_MEDIA_TYPE stream_media_type = {{0}};
412 DWORD i, stream_count;
413 WCHAR name[MAX_PATH];
414 HRESULT hr;
416 TRACE("iface %p, status %d, result %#lx, type %d, value %p, context %p.\n",
417 iface, status, result, type, value, context);
419 switch (status)
421 case WMT_OPENED:
422 if (FAILED(hr = IWMReader_GetOutputCount(filter->reader, &stream_count)))
424 ERR("Failed to get WMReader output count, hr %#lx.\n", hr);
425 stream_count = 0;
427 if (stream_count > ARRAY_SIZE(filter->streams))
429 FIXME("Found %lu streams, not supported!\n", stream_count);
430 stream_count = ARRAY_SIZE(filter->streams);
433 for (i = 0; i < stream_count; ++i)
435 struct asf_stream *stream = filter->streams + i;
437 if (FAILED(hr = asf_stream_get_media_type(&stream->source.pin, 0, &stream_media_type)))
438 WARN("Failed to get stream media type, hr %#lx.\n", hr);
439 if (IsEqualGUID(&stream_media_type.majortype, &MEDIATYPE_Video))
440 swprintf(name, ARRAY_SIZE(name), L"Raw Video %u", stream->index);
441 else
442 swprintf(name, ARRAY_SIZE(name), L"Raw Audio %u", stream->index);
443 FreeMediaType(&stream_media_type);
445 strmbase_source_init(&stream->source, &filter->filter, name, &source_ops);
447 filter->stream_count = stream_count;
448 BaseFilterImpl_IncrementPinVersion(&filter->filter);
450 EnterCriticalSection(&filter->status_cs);
451 filter->result = result;
452 filter->status = WMT_OPENED;
453 LeaveCriticalSection(&filter->status_cs);
454 WakeConditionVariable(&filter->status_cv);
455 break;
457 default:
458 WARN("Ignoring status %#x.\n", status);
459 break;
462 return S_OK;
465 static HRESULT WINAPI reader_callback_OnSample(IWMReaderCallback *iface, DWORD output, QWORD time,
466 QWORD duration, DWORD flags, INSSBuffer *sample, void *context)
468 FIXME("iface %p, output %lu, time %I64u, duration %I64u, flags %#lx, sample %p, context %p stub!\n",
469 iface, output, time, duration, flags, sample, context);
470 return E_NOTIMPL;
473 static const IWMReaderCallbackVtbl reader_callback_vtbl =
475 reader_callback_QueryInterface,
476 reader_callback_AddRef,
477 reader_callback_Release,
478 reader_callback_OnStatus,
479 reader_callback_OnSample,
482 static HRESULT asf_callback_create(struct asf_reader *filter, IWMReaderCallback **out)
484 struct asf_callback *callback;
486 if (!(callback = calloc(1, sizeof(*callback))))
487 return E_OUTOFMEMORY;
489 callback->IWMReaderCallback_iface.lpVtbl = &reader_callback_vtbl;
490 callback->filter = filter;
491 callback->ref = 1;
493 *out = &callback->IWMReaderCallback_iface;
494 return S_OK;
497 HRESULT asf_reader_create(IUnknown *outer, IUnknown **out)
499 struct asf_reader *object;
500 HRESULT hr;
501 int i;
503 if (!(object = calloc(1, sizeof(*object))))
504 return E_OUTOFMEMORY;
506 if (FAILED(hr = WMCreateReader(NULL, 0, &object->reader)))
508 free(object);
509 return hr;
511 if (FAILED(hr = asf_callback_create(object, &object->callback)))
513 IWMReader_Release(object->reader);
514 free(object);
515 return hr;
518 for (i = 0; i < ARRAY_SIZE(object->streams); ++i) object->streams[i].index = i;
519 strmbase_filter_init(&object->filter, outer, &CLSID_WMAsfReader, &filter_ops);
520 object->IFileSourceFilter_iface.lpVtbl = &file_source_vtbl;
522 InitializeCriticalSection(&object->status_cs);
523 object->status_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": status_cs");
525 TRACE("Created WM ASF reader %p.\n", object);
526 *out = &object->filter.IUnknown_inner;
528 return S_OK;