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"
28 WINE_DEFAULT_DEBUG_CHANNEL(quartz
);
32 struct strmbase_source source
;
38 struct strmbase_filter filter
;
39 IFileSourceFilter IFileSourceFilter_iface
;
41 AM_MEDIA_TYPE media_type
;
46 CRITICAL_SECTION status_cs
;
47 CONDITION_VARIABLE status_cv
;
49 IWMReaderCallback
*callback
;
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
;
75 TRACE("iface %p, media_type %p.\n", iface
, media_type
);
77 if (FAILED(hr
= IWMReader_GetOutputFormat(filter
->reader
, stream
->index
, i
, &props
)))
79 if (FAILED(hr
= IWMOutputMediaProps_GetMediaType(props
, NULL
, &size
)))
81 IWMOutputMediaProps_Release(props
);
84 if (!(mt
= malloc(size
)))
86 IWMOutputMediaProps_Release(props
);
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
);
100 IWMOutputMediaProps_Release(props
);
101 } while (SUCCEEDED(hr
= IWMReader_GetOutputFormat(filter
->reader
, stream
->index
, ++i
, &props
)));
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
;
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
);
125 if (!(mt
= malloc(size
)))
127 IWMOutputMediaProps_Release(props
);
128 return E_OUTOFMEMORY
;
131 hr
= IWMOutputMediaProps_GetMediaType(props
, mt
, &size
);
133 hr
= CopyMediaType(media_type
, (AM_MEDIA_TYPE
*)mt
);
136 IWMOutputMediaProps_Release(props
);
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
);
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
);
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
);
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
);
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
)
215 if (FAILED(hr
= IMemAllocator_Commit(stream
->source
.pAllocator
)))
217 WARN("Failed to commit stream %u allocator, hr %#lx\n", i
, hr
);
225 static HRESULT
asf_reader_cleanup_stream(struct strmbase_filter
*iface
)
227 struct asf_reader
*filter
= impl_from_strmbase_filter(iface
);
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
)
240 if (FAILED(hr
= IMemAllocator_Decommit(stream
->source
.pAllocator
)))
242 WARN("Failed to decommit stream %u allocator, hr %#lx\n", i
, 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
);
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
);
333 EnterCriticalSection(&filter
->filter
.filter_cs
);
335 if (filter
->file_name
|| !(filter
->file_name
= wcsdup(file_name
)))
337 LeaveCriticalSection(&filter
->filter
.filter_cs
);
341 if (media_type
&& FAILED(hr
= CopyMediaType(&filter
->media_type
, media_type
)))
343 LeaveCriticalSection(&filter
->filter
.filter_cs
);
347 EnterCriticalSection(&filter
->status_cs
);
348 if (SUCCEEDED(hr
= IWMReader_Open(filter
->reader
, filter
->file_name
, filter
->callback
, NULL
)))
351 while (filter
->status
!= WMT_OPENED
)
352 SleepConditionVariableCS(&filter
->status_cv
, &filter
->status_cs
, INFINITE
);
355 LeaveCriticalSection(&filter
->status_cs
);
358 WARN("Failed to open WM reader, hr %#lx.\n", hr
);
360 LeaveCriticalSection(&filter
->filter
.filter_cs
);
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
);
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
);
393 static const IFileSourceFilterVtbl file_source_vtbl
=
395 file_source_QueryInterface
,
399 file_source_GetCurFile
,
404 IWMReaderCallback IWMReaderCallback_iface
;
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
;
428 WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid
));
429 return E_NOINTERFACE
;
432 IUnknown_AddRef((IUnknown
*)*out
);
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
);
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
);
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
];
468 TRACE("iface %p, status %d, result %#lx, type %d, value %p, context %p.\n",
469 iface
, status
, result
, type
, value
, context
);
474 if (FAILED(hr
= IWMReader_GetOutputCount(filter
->reader
, &stream_count
)))
476 ERR("Failed to get WMReader output count, hr %#lx.\n", hr
);
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
);
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
);
510 WARN("Ignoring status %#x.\n", status
);
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
);
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
;
545 *out
= &callback
->IWMReaderCallback_iface
;
549 HRESULT
asf_reader_create(IUnknown
*outer
, IUnknown
**out
)
551 struct asf_reader
*object
;
555 if (!(object
= calloc(1, sizeof(*object
))))
556 return E_OUTOFMEMORY
;
558 if (FAILED(hr
= WMCreateReader(NULL
, 0, &object
->reader
)))
563 if (FAILED(hr
= asf_callback_create(object
, &object
->callback
)))
565 IWMReader_Release(object
->reader
);
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
;