2021-10-20 18:09:45 +02:00
|
|
|
/*
|
|
|
|
* Copyright 2012 Austin English
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "gst_private.h"
|
|
|
|
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(wmvcore);
|
|
|
|
|
2021-10-28 18:45:57 +02:00
|
|
|
static struct wm_stream *get_stream_by_output_number(struct wm_reader *reader, DWORD output)
|
|
|
|
{
|
|
|
|
if (output < reader->stream_count)
|
|
|
|
return &reader->streams[output];
|
2022-02-04 01:45:24 +01:00
|
|
|
WARN("Invalid output number %lu.\n", output);
|
2021-10-28 18:45:57 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct output_props
|
|
|
|
{
|
|
|
|
IWMOutputMediaProps IWMOutputMediaProps_iface;
|
|
|
|
LONG refcount;
|
2021-10-28 18:45:58 +02:00
|
|
|
|
|
|
|
AM_MEDIA_TYPE mt;
|
2021-10-28 18:45:57 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static inline struct output_props *impl_from_IWMOutputMediaProps(IWMOutputMediaProps *iface)
|
|
|
|
{
|
|
|
|
return CONTAINING_RECORD(iface, struct output_props, IWMOutputMediaProps_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI output_props_QueryInterface(IWMOutputMediaProps *iface, REFIID iid, void **out)
|
|
|
|
{
|
|
|
|
struct output_props *props = impl_from_IWMOutputMediaProps(iface);
|
|
|
|
|
|
|
|
TRACE("props %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out);
|
|
|
|
|
|
|
|
if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IWMOutputMediaProps))
|
|
|
|
*out = &props->IWMOutputMediaProps_iface;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*out = NULL;
|
|
|
|
WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid));
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
}
|
|
|
|
|
|
|
|
IUnknown_AddRef((IUnknown *)*out);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI output_props_AddRef(IWMOutputMediaProps *iface)
|
|
|
|
{
|
|
|
|
struct output_props *props = impl_from_IWMOutputMediaProps(iface);
|
|
|
|
ULONG refcount = InterlockedIncrement(&props->refcount);
|
|
|
|
|
2022-02-04 01:45:24 +01:00
|
|
|
TRACE("%p increasing refcount to %lu.\n", props, refcount);
|
2021-10-28 18:45:57 +02:00
|
|
|
|
|
|
|
return refcount;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI output_props_Release(IWMOutputMediaProps *iface)
|
|
|
|
{
|
|
|
|
struct output_props *props = impl_from_IWMOutputMediaProps(iface);
|
|
|
|
ULONG refcount = InterlockedDecrement(&props->refcount);
|
|
|
|
|
2022-02-04 01:45:24 +01:00
|
|
|
TRACE("%p decreasing refcount to %lu.\n", props, refcount);
|
2021-10-28 18:45:57 +02:00
|
|
|
|
|
|
|
if (!refcount)
|
|
|
|
free(props);
|
|
|
|
|
|
|
|
return refcount;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI output_props_GetType(IWMOutputMediaProps *iface, GUID *major_type)
|
|
|
|
{
|
2021-11-08 22:24:33 +01:00
|
|
|
const struct output_props *props = impl_from_IWMOutputMediaProps(iface);
|
|
|
|
|
|
|
|
TRACE("iface %p, major_type %p.\n", iface, major_type);
|
|
|
|
|
|
|
|
*major_type = props->mt.majortype;
|
|
|
|
return S_OK;
|
2021-10-28 18:45:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI output_props_GetMediaType(IWMOutputMediaProps *iface, WM_MEDIA_TYPE *mt, DWORD *size)
|
|
|
|
{
|
2021-10-28 18:45:58 +02:00
|
|
|
const struct output_props *props = impl_from_IWMOutputMediaProps(iface);
|
|
|
|
const DWORD req_size = *size;
|
|
|
|
|
|
|
|
TRACE("iface %p, mt %p, size %p.\n", iface, mt, size);
|
|
|
|
|
|
|
|
*size = sizeof(*mt) + props->mt.cbFormat;
|
|
|
|
if (!mt)
|
|
|
|
return S_OK;
|
|
|
|
if (req_size < *size)
|
|
|
|
return ASF_E_BUFFERTOOSMALL;
|
|
|
|
|
|
|
|
strmbase_dump_media_type(&props->mt);
|
|
|
|
|
|
|
|
memcpy(mt, &props->mt, sizeof(*mt));
|
|
|
|
memcpy(mt + 1, props->mt.pbFormat, props->mt.cbFormat);
|
|
|
|
mt->pbFormat = (BYTE *)(mt + 1);
|
|
|
|
return S_OK;
|
2021-10-28 18:45:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI output_props_SetMediaType(IWMOutputMediaProps *iface, WM_MEDIA_TYPE *mt)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, mt %p, stub!\n", iface, mt);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI output_props_GetStreamGroupName(IWMOutputMediaProps *iface, WCHAR *name, WORD *len)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, name %p, len %p, stub!\n", iface, name, len);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI output_props_GetConnectionName(IWMOutputMediaProps *iface, WCHAR *name, WORD *len)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, name %p, len %p, stub!\n", iface, name, len);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct IWMOutputMediaPropsVtbl output_props_vtbl =
|
|
|
|
{
|
|
|
|
output_props_QueryInterface,
|
|
|
|
output_props_AddRef,
|
|
|
|
output_props_Release,
|
|
|
|
output_props_GetType,
|
|
|
|
output_props_GetMediaType,
|
|
|
|
output_props_SetMediaType,
|
|
|
|
output_props_GetStreamGroupName,
|
|
|
|
output_props_GetConnectionName,
|
|
|
|
};
|
|
|
|
|
2021-11-01 01:03:32 +01:00
|
|
|
static struct output_props *unsafe_impl_from_IWMOutputMediaProps(IWMOutputMediaProps *iface)
|
|
|
|
{
|
|
|
|
if (!iface)
|
|
|
|
return NULL;
|
|
|
|
assert(iface->lpVtbl == &output_props_vtbl);
|
|
|
|
return impl_from_IWMOutputMediaProps(iface);
|
|
|
|
}
|
|
|
|
|
2021-10-28 18:45:58 +02:00
|
|
|
static IWMOutputMediaProps *output_props_create(const struct wg_format *format)
|
2021-10-28 18:45:57 +02:00
|
|
|
{
|
|
|
|
struct output_props *object;
|
|
|
|
|
|
|
|
if (!(object = calloc(1, sizeof(*object))))
|
|
|
|
return NULL;
|
|
|
|
object->IWMOutputMediaProps_iface.lpVtbl = &output_props_vtbl;
|
|
|
|
object->refcount = 1;
|
|
|
|
|
2021-11-02 06:12:58 +01:00
|
|
|
if (!amt_from_wg_format(&object->mt, format, true))
|
2021-10-28 18:45:58 +02:00
|
|
|
{
|
|
|
|
free(object);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-10-28 18:45:57 +02:00
|
|
|
TRACE("Created output properties %p.\n", object);
|
|
|
|
return &object->IWMOutputMediaProps_iface;
|
|
|
|
}
|
|
|
|
|
2021-11-01 01:03:33 +01:00
|
|
|
struct buffer
|
|
|
|
{
|
|
|
|
INSSBuffer INSSBuffer_iface;
|
|
|
|
LONG refcount;
|
2021-11-01 01:03:34 +01:00
|
|
|
|
2021-11-08 22:24:31 +01:00
|
|
|
DWORD size, capacity;
|
2021-11-01 01:03:34 +01:00
|
|
|
BYTE data[1];
|
2021-11-01 01:03:33 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct buffer *impl_from_INSSBuffer(INSSBuffer *iface)
|
|
|
|
{
|
|
|
|
return CONTAINING_RECORD(iface, struct buffer, INSSBuffer_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI buffer_QueryInterface(INSSBuffer *iface, REFIID iid, void **out)
|
|
|
|
{
|
|
|
|
struct buffer *buffer = impl_from_INSSBuffer(iface);
|
|
|
|
|
|
|
|
TRACE("buffer %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out);
|
|
|
|
|
|
|
|
if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_INSSBuffer))
|
|
|
|
*out = &buffer->INSSBuffer_iface;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*out = NULL;
|
|
|
|
WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid));
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
}
|
|
|
|
|
|
|
|
IUnknown_AddRef((IUnknown *)*out);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI buffer_AddRef(INSSBuffer *iface)
|
|
|
|
{
|
|
|
|
struct buffer *buffer = impl_from_INSSBuffer(iface);
|
|
|
|
ULONG refcount = InterlockedIncrement(&buffer->refcount);
|
|
|
|
|
2022-02-04 01:45:24 +01:00
|
|
|
TRACE("%p increasing refcount to %lu.\n", buffer, refcount);
|
2021-11-01 01:03:33 +01:00
|
|
|
|
|
|
|
return refcount;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI buffer_Release(INSSBuffer *iface)
|
|
|
|
{
|
|
|
|
struct buffer *buffer = impl_from_INSSBuffer(iface);
|
|
|
|
ULONG refcount = InterlockedDecrement(&buffer->refcount);
|
|
|
|
|
2022-02-04 01:45:24 +01:00
|
|
|
TRACE("%p decreasing refcount to %lu.\n", buffer, refcount);
|
2021-11-01 01:03:33 +01:00
|
|
|
|
|
|
|
if (!refcount)
|
|
|
|
free(buffer);
|
|
|
|
|
|
|
|
return refcount;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI buffer_GetLength(INSSBuffer *iface, DWORD *size)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, size %p, stub!\n", iface, size);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI buffer_SetLength(INSSBuffer *iface, DWORD size)
|
|
|
|
{
|
2021-11-08 22:24:31 +01:00
|
|
|
struct buffer *buffer = impl_from_INSSBuffer(iface);
|
|
|
|
|
2022-02-04 01:45:24 +01:00
|
|
|
TRACE("iface %p, size %lu.\n", buffer, size);
|
2021-11-08 22:24:31 +01:00
|
|
|
|
|
|
|
if (size > buffer->capacity)
|
|
|
|
return E_INVALIDARG;
|
|
|
|
|
|
|
|
buffer->size = size;
|
|
|
|
return S_OK;
|
2021-11-01 01:03:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI buffer_GetMaxLength(INSSBuffer *iface, DWORD *size)
|
|
|
|
{
|
2021-11-08 22:24:30 +01:00
|
|
|
struct buffer *buffer = impl_from_INSSBuffer(iface);
|
|
|
|
|
|
|
|
TRACE("buffer %p, size %p.\n", buffer, size);
|
|
|
|
|
2021-11-08 22:24:31 +01:00
|
|
|
*size = buffer->capacity;
|
2021-11-08 22:24:30 +01:00
|
|
|
return S_OK;
|
2021-11-01 01:03:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI buffer_GetBuffer(INSSBuffer *iface, BYTE **data)
|
|
|
|
{
|
2021-11-08 22:24:29 +01:00
|
|
|
struct buffer *buffer = impl_from_INSSBuffer(iface);
|
|
|
|
|
|
|
|
TRACE("buffer %p, data %p.\n", buffer, data);
|
|
|
|
|
|
|
|
*data = buffer->data;
|
|
|
|
return S_OK;
|
2021-11-01 01:03:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI buffer_GetBufferAndLength(INSSBuffer *iface, BYTE **data, DWORD *size)
|
|
|
|
{
|
2021-11-01 01:03:34 +01:00
|
|
|
struct buffer *buffer = impl_from_INSSBuffer(iface);
|
|
|
|
|
|
|
|
TRACE("buffer %p, data %p, size %p.\n", buffer, data, size);
|
|
|
|
|
|
|
|
*size = buffer->size;
|
|
|
|
*data = buffer->data;
|
|
|
|
return S_OK;
|
2021-11-01 01:03:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static const INSSBufferVtbl buffer_vtbl =
|
|
|
|
{
|
|
|
|
buffer_QueryInterface,
|
|
|
|
buffer_AddRef,
|
|
|
|
buffer_Release,
|
|
|
|
buffer_GetLength,
|
|
|
|
buffer_SetLength,
|
|
|
|
buffer_GetMaxLength,
|
|
|
|
buffer_GetBuffer,
|
|
|
|
buffer_GetBufferAndLength,
|
|
|
|
};
|
|
|
|
|
2021-10-27 05:22:05 +02:00
|
|
|
struct stream_config
|
|
|
|
{
|
|
|
|
IWMStreamConfig IWMStreamConfig_iface;
|
2021-11-12 06:43:22 +01:00
|
|
|
IWMMediaProps IWMMediaProps_iface;
|
2021-10-27 05:22:05 +02:00
|
|
|
LONG refcount;
|
|
|
|
|
|
|
|
const struct wm_stream *stream;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct stream_config *impl_from_IWMStreamConfig(IWMStreamConfig *iface)
|
|
|
|
{
|
|
|
|
return CONTAINING_RECORD(iface, struct stream_config, IWMStreamConfig_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI stream_config_QueryInterface(IWMStreamConfig *iface, REFIID iid, void **out)
|
|
|
|
{
|
|
|
|
struct stream_config *config = impl_from_IWMStreamConfig(iface);
|
|
|
|
|
|
|
|
TRACE("config %p, iid %s, out %p.\n", config, debugstr_guid(iid), out);
|
|
|
|
|
|
|
|
if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IWMStreamConfig))
|
|
|
|
*out = &config->IWMStreamConfig_iface;
|
2021-11-12 06:43:22 +01:00
|
|
|
else if (IsEqualGUID(iid, &IID_IWMMediaProps))
|
|
|
|
*out = &config->IWMMediaProps_iface;
|
2021-10-27 05:22:05 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
*out = NULL;
|
|
|
|
WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid));
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
}
|
|
|
|
|
|
|
|
IUnknown_AddRef((IUnknown *)*out);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI stream_config_AddRef(IWMStreamConfig *iface)
|
|
|
|
{
|
|
|
|
struct stream_config *config = impl_from_IWMStreamConfig(iface);
|
|
|
|
ULONG refcount = InterlockedIncrement(&config->refcount);
|
|
|
|
|
2022-02-04 01:45:24 +01:00
|
|
|
TRACE("%p increasing refcount to %lu.\n", config, refcount);
|
2021-10-27 05:22:05 +02:00
|
|
|
|
|
|
|
return refcount;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI stream_config_Release(IWMStreamConfig *iface)
|
|
|
|
{
|
|
|
|
struct stream_config *config = impl_from_IWMStreamConfig(iface);
|
|
|
|
ULONG refcount = InterlockedDecrement(&config->refcount);
|
|
|
|
|
2022-02-04 01:45:24 +01:00
|
|
|
TRACE("%p decreasing refcount to %lu.\n", config, refcount);
|
2021-10-27 05:22:05 +02:00
|
|
|
|
|
|
|
if (!refcount)
|
|
|
|
{
|
|
|
|
IWMProfile3_Release(&config->stream->reader->IWMProfile3_iface);
|
|
|
|
free(config);
|
|
|
|
}
|
|
|
|
|
|
|
|
return refcount;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI stream_config_GetStreamType(IWMStreamConfig *iface, GUID *type)
|
|
|
|
{
|
2021-10-28 18:45:56 +02:00
|
|
|
struct stream_config *config = impl_from_IWMStreamConfig(iface);
|
|
|
|
struct wm_reader *reader = config->stream->reader;
|
|
|
|
AM_MEDIA_TYPE mt;
|
|
|
|
|
|
|
|
TRACE("config %p, type %p.\n", config, type);
|
|
|
|
|
|
|
|
EnterCriticalSection(&reader->cs);
|
|
|
|
|
2021-11-02 06:12:58 +01:00
|
|
|
if (!amt_from_wg_format(&mt, &config->stream->format, true))
|
2021-10-28 18:45:56 +02:00
|
|
|
{
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
*type = mt.majortype;
|
|
|
|
FreeMediaType(&mt);
|
|
|
|
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
|
|
|
|
return S_OK;
|
2021-10-27 05:22:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI stream_config_GetStreamNumber(IWMStreamConfig *iface, WORD *number)
|
|
|
|
{
|
2021-10-27 05:22:06 +02:00
|
|
|
struct stream_config *config = impl_from_IWMStreamConfig(iface);
|
|
|
|
|
|
|
|
TRACE("config %p, number %p.\n", config, number);
|
|
|
|
|
|
|
|
*number = config->stream->index + 1;
|
|
|
|
return S_OK;
|
2021-10-27 05:22:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI stream_config_SetStreamNumber(IWMStreamConfig *iface, WORD number)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, number %u, stub!\n", iface, number);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI stream_config_GetStreamName(IWMStreamConfig *iface, WCHAR *name, WORD *len)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, name %p, len %p, stub!\n", iface, name, len);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI stream_config_SetStreamName(IWMStreamConfig *iface, const WCHAR *name)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, name %s, stub!\n", iface, debugstr_w(name));
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI stream_config_GetConnectionName(IWMStreamConfig *iface, WCHAR *name, WORD *len)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, name %p, len %p, stub!\n", iface, name, len);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI stream_config_SetConnectionName(IWMStreamConfig *iface, const WCHAR *name)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, name %s, stub!\n", iface, debugstr_w(name));
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI stream_config_GetBitrate(IWMStreamConfig *iface, DWORD *bitrate)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, bitrate %p, stub!\n", iface, bitrate);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI stream_config_SetBitrate(IWMStreamConfig *iface, DWORD bitrate)
|
|
|
|
{
|
2022-02-04 01:45:24 +01:00
|
|
|
FIXME("iface %p, bitrate %lu, stub!\n", iface, bitrate);
|
2021-10-27 05:22:05 +02:00
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI stream_config_GetBufferWindow(IWMStreamConfig *iface, DWORD *window)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, window %p, stub!\n", iface, window);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI stream_config_SetBufferWindow(IWMStreamConfig *iface, DWORD window)
|
|
|
|
{
|
2022-02-04 01:45:24 +01:00
|
|
|
FIXME("iface %p, window %lu, stub!\n", iface, window);
|
2021-10-27 05:22:05 +02:00
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const IWMStreamConfigVtbl stream_config_vtbl =
|
|
|
|
{
|
|
|
|
stream_config_QueryInterface,
|
|
|
|
stream_config_AddRef,
|
|
|
|
stream_config_Release,
|
|
|
|
stream_config_GetStreamType,
|
|
|
|
stream_config_GetStreamNumber,
|
|
|
|
stream_config_SetStreamNumber,
|
|
|
|
stream_config_GetStreamName,
|
|
|
|
stream_config_SetStreamName,
|
|
|
|
stream_config_GetConnectionName,
|
|
|
|
stream_config_SetConnectionName,
|
|
|
|
stream_config_GetBitrate,
|
|
|
|
stream_config_SetBitrate,
|
|
|
|
stream_config_GetBufferWindow,
|
|
|
|
stream_config_SetBufferWindow,
|
|
|
|
};
|
|
|
|
|
2021-11-12 06:43:22 +01:00
|
|
|
static struct stream_config *impl_from_IWMMediaProps(IWMMediaProps *iface)
|
|
|
|
{
|
|
|
|
return CONTAINING_RECORD(iface, struct stream_config, IWMMediaProps_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI stream_props_QueryInterface(IWMMediaProps *iface, REFIID iid, void **out)
|
|
|
|
{
|
|
|
|
struct stream_config *config = impl_from_IWMMediaProps(iface);
|
|
|
|
return IWMStreamConfig_QueryInterface(&config->IWMStreamConfig_iface, iid, out);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI stream_props_AddRef(IWMMediaProps *iface)
|
|
|
|
{
|
|
|
|
struct stream_config *config = impl_from_IWMMediaProps(iface);
|
|
|
|
return IWMStreamConfig_AddRef(&config->IWMStreamConfig_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI stream_props_Release(IWMMediaProps *iface)
|
|
|
|
{
|
|
|
|
struct stream_config *config = impl_from_IWMMediaProps(iface);
|
|
|
|
return IWMStreamConfig_Release(&config->IWMStreamConfig_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI stream_props_GetType(IWMMediaProps *iface, GUID *major_type)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, major_type %p, stub!\n", iface, major_type);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI stream_props_GetMediaType(IWMMediaProps *iface, WM_MEDIA_TYPE *mt, DWORD *size)
|
|
|
|
{
|
2021-11-12 06:43:23 +01:00
|
|
|
struct stream_config *config = impl_from_IWMMediaProps(iface);
|
|
|
|
const DWORD req_size = *size;
|
|
|
|
AM_MEDIA_TYPE stream_mt;
|
|
|
|
|
|
|
|
TRACE("iface %p, mt %p, size %p.\n", iface, mt, size);
|
|
|
|
|
|
|
|
if (!amt_from_wg_format(&stream_mt, &config->stream->format, true))
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
|
|
|
|
*size = sizeof(stream_mt) + stream_mt.cbFormat;
|
|
|
|
if (!mt)
|
|
|
|
return S_OK;
|
|
|
|
if (req_size < *size)
|
|
|
|
return ASF_E_BUFFERTOOSMALL;
|
|
|
|
|
|
|
|
strmbase_dump_media_type(&stream_mt);
|
|
|
|
|
|
|
|
memcpy(mt, &stream_mt, sizeof(*mt));
|
|
|
|
memcpy(mt + 1, stream_mt.pbFormat, stream_mt.cbFormat);
|
|
|
|
mt->pbFormat = (BYTE *)(mt + 1);
|
|
|
|
return S_OK;
|
2021-11-12 06:43:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI stream_props_SetMediaType(IWMMediaProps *iface, WM_MEDIA_TYPE *mt)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, mt %p, stub!\n", iface, mt);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const IWMMediaPropsVtbl stream_props_vtbl =
|
|
|
|
{
|
|
|
|
stream_props_QueryInterface,
|
|
|
|
stream_props_AddRef,
|
|
|
|
stream_props_Release,
|
|
|
|
stream_props_GetType,
|
|
|
|
stream_props_GetMediaType,
|
|
|
|
stream_props_SetMediaType,
|
|
|
|
};
|
|
|
|
|
2021-10-26 01:52:12 +02:00
|
|
|
static DWORD CALLBACK read_thread(void *arg)
|
|
|
|
{
|
|
|
|
struct wm_reader *reader = arg;
|
|
|
|
IStream *stream = reader->source_stream;
|
2021-11-02 06:13:04 +01:00
|
|
|
HANDLE file = reader->file;
|
2021-10-26 01:52:12 +02:00
|
|
|
size_t buffer_size = 4096;
|
|
|
|
uint64_t file_size;
|
|
|
|
void *data;
|
|
|
|
|
|
|
|
if (!(data = malloc(buffer_size)))
|
|
|
|
return 0;
|
|
|
|
|
2021-11-02 06:13:04 +01:00
|
|
|
if (file)
|
|
|
|
{
|
|
|
|
LARGE_INTEGER size;
|
|
|
|
|
|
|
|
GetFileSizeEx(file, &size);
|
|
|
|
file_size = size.QuadPart;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
STATSTG stat;
|
|
|
|
|
|
|
|
IStream_Stat(stream, &stat, STATFLAG_NONAME);
|
|
|
|
file_size = stat.cbSize.QuadPart;
|
|
|
|
}
|
2021-10-26 01:52:12 +02:00
|
|
|
|
|
|
|
TRACE("Starting read thread for reader %p.\n", reader);
|
|
|
|
|
|
|
|
while (!reader->read_thread_shutdown)
|
|
|
|
{
|
2021-11-02 06:13:04 +01:00
|
|
|
LARGE_INTEGER large_offset;
|
2021-10-26 01:52:12 +02:00
|
|
|
uint64_t offset;
|
|
|
|
ULONG ret_size;
|
|
|
|
uint32_t size;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
if (!wg_parser_get_next_read_offset(reader->wg_parser, &offset, &size))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (offset >= file_size)
|
|
|
|
size = 0;
|
|
|
|
else if (offset + size >= file_size)
|
|
|
|
size = file_size - offset;
|
|
|
|
|
|
|
|
if (!size)
|
|
|
|
{
|
|
|
|
wg_parser_push_data(reader->wg_parser, data, 0);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!array_reserve(&data, &buffer_size, size, 1))
|
|
|
|
{
|
|
|
|
free(data);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret_size = 0;
|
|
|
|
|
2021-11-02 06:13:04 +01:00
|
|
|
large_offset.QuadPart = offset;
|
|
|
|
if (file)
|
|
|
|
{
|
|
|
|
if (!SetFilePointerEx(file, large_offset, NULL, FILE_BEGIN)
|
|
|
|
|| !ReadFile(file, data, size, &ret_size, NULL))
|
|
|
|
{
|
2022-02-04 01:45:24 +01:00
|
|
|
ERR("Failed to read %u bytes at offset %I64u, error %lu.\n", size, offset, GetLastError());
|
2021-11-02 06:13:04 +01:00
|
|
|
wg_parser_push_data(reader->wg_parser, NULL, 0);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (SUCCEEDED(hr = IStream_Seek(stream, large_offset, STREAM_SEEK_SET, NULL)))
|
|
|
|
hr = IStream_Read(stream, data, size, &ret_size);
|
|
|
|
if (FAILED(hr))
|
|
|
|
{
|
2022-02-04 01:45:24 +01:00
|
|
|
ERR("Failed to read %u bytes at offset %I64u, hr %#lx.\n", size, offset, hr);
|
2021-11-02 06:13:04 +01:00
|
|
|
wg_parser_push_data(reader->wg_parser, NULL, 0);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret_size != size)
|
2022-02-04 01:45:24 +01:00
|
|
|
ERR("Unexpected short read: requested %u bytes, got %lu.\n", size, ret_size);
|
2021-11-02 06:13:04 +01:00
|
|
|
wg_parser_push_data(reader->wg_parser, data, ret_size);
|
2021-10-26 01:52:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
free(data);
|
|
|
|
TRACE("Reader is shutting down; exiting.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-10-20 18:09:45 +02:00
|
|
|
static struct wm_reader *impl_from_IWMProfile3(IWMProfile3 *iface)
|
|
|
|
{
|
|
|
|
return CONTAINING_RECORD(iface, struct wm_reader, IWMProfile3_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_QueryInterface(IWMProfile3 *iface, REFIID iid, void **out)
|
|
|
|
{
|
|
|
|
struct wm_reader *reader = impl_from_IWMProfile3(iface);
|
|
|
|
|
|
|
|
TRACE("reader %p, iid %s, out %p.\n", reader, debugstr_guid(iid), out);
|
|
|
|
|
2021-10-20 18:09:48 +02:00
|
|
|
if (IsEqualIID(iid, &IID_IWMHeaderInfo)
|
|
|
|
|| IsEqualIID(iid, &IID_IWMHeaderInfo2)
|
|
|
|
|| IsEqualIID(iid, &IID_IWMHeaderInfo3))
|
|
|
|
{
|
|
|
|
*out = &reader->IWMHeaderInfo3_iface;
|
|
|
|
}
|
2021-10-20 18:09:49 +02:00
|
|
|
else if (IsEqualIID(iid, &IID_IWMLanguageList))
|
|
|
|
{
|
|
|
|
*out = &reader->IWMLanguageList_iface;
|
|
|
|
}
|
2021-10-21 05:48:39 +02:00
|
|
|
else if (IsEqualIID(iid, &IID_IWMPacketSize)
|
|
|
|
|| IsEqualIID(iid, &IID_IWMPacketSize2))
|
|
|
|
{
|
|
|
|
*out = &reader->IWMPacketSize2_iface;
|
|
|
|
}
|
2021-10-20 18:09:48 +02:00
|
|
|
else if (IsEqualIID(iid, &IID_IUnknown)
|
2021-10-20 18:09:45 +02:00
|
|
|
|| IsEqualIID(iid, &IID_IWMProfile)
|
|
|
|
|| IsEqualIID(iid, &IID_IWMProfile2)
|
|
|
|
|| IsEqualIID(iid, &IID_IWMProfile3))
|
|
|
|
{
|
|
|
|
*out = &reader->IWMProfile3_iface;
|
|
|
|
}
|
2021-10-21 05:48:40 +02:00
|
|
|
else if (IsEqualIID(iid, &IID_IWMReaderPlaylistBurn))
|
|
|
|
{
|
|
|
|
*out = &reader->IWMReaderPlaylistBurn_iface;
|
|
|
|
}
|
2021-10-21 05:48:41 +02:00
|
|
|
else if (IsEqualIID(iid, &IID_IWMReaderTimecode))
|
|
|
|
{
|
|
|
|
*out = &reader->IWMReaderTimecode_iface;
|
|
|
|
}
|
2021-10-20 18:09:45 +02:00
|
|
|
else if (!(*out = reader->ops->query_interface(reader, iid)))
|
|
|
|
{
|
|
|
|
WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid));
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
}
|
|
|
|
|
|
|
|
IUnknown_AddRef((IUnknown *)*out);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI profile_AddRef(IWMProfile3 *iface)
|
|
|
|
{
|
|
|
|
struct wm_reader *reader = impl_from_IWMProfile3(iface);
|
|
|
|
ULONG refcount = InterlockedIncrement(&reader->refcount);
|
|
|
|
|
2022-02-04 01:45:24 +01:00
|
|
|
TRACE("%p increasing refcount to %lu.\n", reader, refcount);
|
2021-10-20 18:09:45 +02:00
|
|
|
|
|
|
|
return refcount;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI profile_Release(IWMProfile3 *iface)
|
|
|
|
{
|
|
|
|
struct wm_reader *reader = impl_from_IWMProfile3(iface);
|
|
|
|
ULONG refcount = InterlockedDecrement(&reader->refcount);
|
|
|
|
|
2022-02-04 01:45:24 +01:00
|
|
|
TRACE("%p decreasing refcount to %lu.\n", reader, refcount);
|
2021-10-20 18:09:45 +02:00
|
|
|
|
|
|
|
if (!refcount)
|
|
|
|
reader->ops->destroy(reader);
|
|
|
|
|
|
|
|
return refcount;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_GetVersion(IWMProfile3 *iface, WMT_VERSION *version)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, version %p, stub!\n", iface, version);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_GetName(IWMProfile3 *iface, WCHAR *name, DWORD *length)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, name %p, length %p, stub!\n", iface, name, length);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_SetName(IWMProfile3 *iface, const WCHAR *name)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, name %s, stub!\n", iface, debugstr_w(name));
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_GetDescription(IWMProfile3 *iface, WCHAR *description, DWORD *length)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, description %p, length %p, stub!\n", iface, description, length);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_SetDescription(IWMProfile3 *iface, const WCHAR *description)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, description %s, stub!\n", iface, debugstr_w(description));
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_GetStreamCount(IWMProfile3 *iface, DWORD *count)
|
|
|
|
{
|
2021-10-26 01:52:13 +02:00
|
|
|
struct wm_reader *reader = impl_from_IWMProfile3(iface);
|
|
|
|
|
|
|
|
TRACE("reader %p, count %p.\n", reader, count);
|
2021-10-20 18:09:45 +02:00
|
|
|
|
|
|
|
if (!count)
|
|
|
|
return E_INVALIDARG;
|
|
|
|
|
2021-10-26 01:52:13 +02:00
|
|
|
EnterCriticalSection(&reader->cs);
|
|
|
|
*count = reader->stream_count;
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
2021-10-20 18:09:45 +02:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_GetStream(IWMProfile3 *iface, DWORD index, IWMStreamConfig **config)
|
|
|
|
{
|
2021-10-27 05:22:05 +02:00
|
|
|
struct wm_reader *reader = impl_from_IWMProfile3(iface);
|
|
|
|
struct stream_config *object;
|
|
|
|
|
2022-02-04 01:45:24 +01:00
|
|
|
TRACE("reader %p, index %lu, config %p.\n", reader, index, config);
|
2021-10-27 05:22:05 +02:00
|
|
|
|
|
|
|
EnterCriticalSection(&reader->cs);
|
|
|
|
|
|
|
|
if (index >= reader->stream_count)
|
|
|
|
{
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
2022-02-04 01:45:24 +01:00
|
|
|
WARN("Index %lu exceeds stream count %u; returning E_INVALIDARG.\n", index, reader->stream_count);
|
2021-10-27 05:22:05 +02:00
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(object = calloc(1, sizeof(*object))))
|
|
|
|
{
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
object->IWMStreamConfig_iface.lpVtbl = &stream_config_vtbl;
|
2021-11-12 06:43:22 +01:00
|
|
|
object->IWMMediaProps_iface.lpVtbl = &stream_props_vtbl;
|
2021-10-27 05:22:05 +02:00
|
|
|
object->refcount = 1;
|
|
|
|
object->stream = &reader->streams[index];
|
|
|
|
IWMProfile3_AddRef(&reader->IWMProfile3_iface);
|
|
|
|
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
|
|
|
|
TRACE("Created stream config %p.\n", object);
|
|
|
|
*config = &object->IWMStreamConfig_iface;
|
|
|
|
return S_OK;
|
2021-10-20 18:09:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_GetStreamByNumber(IWMProfile3 *iface, WORD stream_number, IWMStreamConfig **config)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, stream_number %u, config %p, stub!\n", iface, stream_number, config);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_RemoveStream(IWMProfile3 *iface, IWMStreamConfig *config)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, config %p, stub!\n", iface, config);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_RemoveStreamByNumber(IWMProfile3 *iface, WORD stream_number)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, stream_number %u, stub!\n", iface, stream_number);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_AddStream(IWMProfile3 *iface, IWMStreamConfig *config)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, config %p, stub!\n", iface, config);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_ReconfigStream(IWMProfile3 *iface, IWMStreamConfig *config)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, config %p, stub!\n", iface, config);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_CreateNewStream(IWMProfile3 *iface, REFGUID type, IWMStreamConfig **config)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, type %s, config %p, stub!\n", iface, debugstr_guid(type), config);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_GetMutualExclusionCount(IWMProfile3 *iface, DWORD *count)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, count %p, stub!\n", iface, count);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_GetMutualExclusion(IWMProfile3 *iface, DWORD index, IWMMutualExclusion **excl)
|
|
|
|
{
|
2022-02-04 01:45:24 +01:00
|
|
|
FIXME("iface %p, index %lu, excl %p, stub!\n", iface, index, excl);
|
2021-10-20 18:09:45 +02:00
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_RemoveMutualExclusion(IWMProfile3 *iface, IWMMutualExclusion *excl)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, excl %p, stub!\n", iface, excl);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_AddMutualExclusion(IWMProfile3 *iface, IWMMutualExclusion *excl)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, excl %p, stub!\n", iface, excl);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_CreateNewMutualExclusion(IWMProfile3 *iface, IWMMutualExclusion **excl)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, excl %p, stub!\n", iface, excl);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_GetProfileID(IWMProfile3 *iface, GUID *id)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, id %p, stub!\n", iface, id);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_GetStorageFormat(IWMProfile3 *iface, WMT_STORAGE_FORMAT *format)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, format %p, stub!\n", iface, format);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_SetStorageFormat(IWMProfile3 *iface, WMT_STORAGE_FORMAT format)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, format %#x, stub!\n", iface, format);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_GetBandwidthSharingCount(IWMProfile3 *iface, DWORD *count)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, count %p, stub!\n", iface, count);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_GetBandwidthSharing(IWMProfile3 *iface, DWORD index, IWMBandwidthSharing **sharing)
|
|
|
|
{
|
2022-02-04 01:45:24 +01:00
|
|
|
FIXME("iface %p, index %lu, sharing %p, stub!\n", iface, index, sharing);
|
2021-10-20 18:09:45 +02:00
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_RemoveBandwidthSharing( IWMProfile3 *iface, IWMBandwidthSharing *sharing)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, sharing %p, stub!\n", iface, sharing);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_AddBandwidthSharing(IWMProfile3 *iface, IWMBandwidthSharing *sharing)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, sharing %p, stub!\n", iface, sharing);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_CreateNewBandwidthSharing( IWMProfile3 *iface, IWMBandwidthSharing **sharing)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, sharing %p, stub!\n", iface, sharing);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_GetStreamPrioritization(IWMProfile3 *iface, IWMStreamPrioritization **stream)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, stream %p, stub!\n", iface, stream);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_SetStreamPrioritization(IWMProfile3 *iface, IWMStreamPrioritization *stream)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, stream %p, stub!\n", iface, stream);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_RemoveStreamPrioritization(IWMProfile3 *iface)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, stub!\n", iface);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_CreateNewStreamPrioritization(IWMProfile3 *iface, IWMStreamPrioritization **stream)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, stream %p, stub!\n", iface, stream);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI profile_GetExpectedPacketCount(IWMProfile3 *iface, QWORD duration, QWORD *count)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, duration %s, count %p, stub!\n", iface, debugstr_time(duration), count);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const IWMProfile3Vtbl profile_vtbl =
|
|
|
|
{
|
|
|
|
profile_QueryInterface,
|
|
|
|
profile_AddRef,
|
|
|
|
profile_Release,
|
|
|
|
profile_GetVersion,
|
|
|
|
profile_GetName,
|
|
|
|
profile_SetName,
|
|
|
|
profile_GetDescription,
|
|
|
|
profile_SetDescription,
|
|
|
|
profile_GetStreamCount,
|
|
|
|
profile_GetStream,
|
|
|
|
profile_GetStreamByNumber,
|
|
|
|
profile_RemoveStream,
|
|
|
|
profile_RemoveStreamByNumber,
|
|
|
|
profile_AddStream,
|
|
|
|
profile_ReconfigStream,
|
|
|
|
profile_CreateNewStream,
|
|
|
|
profile_GetMutualExclusionCount,
|
|
|
|
profile_GetMutualExclusion,
|
|
|
|
profile_RemoveMutualExclusion,
|
|
|
|
profile_AddMutualExclusion,
|
|
|
|
profile_CreateNewMutualExclusion,
|
|
|
|
profile_GetProfileID,
|
|
|
|
profile_GetStorageFormat,
|
|
|
|
profile_SetStorageFormat,
|
|
|
|
profile_GetBandwidthSharingCount,
|
|
|
|
profile_GetBandwidthSharing,
|
|
|
|
profile_RemoveBandwidthSharing,
|
|
|
|
profile_AddBandwidthSharing,
|
|
|
|
profile_CreateNewBandwidthSharing,
|
|
|
|
profile_GetStreamPrioritization,
|
|
|
|
profile_SetStreamPrioritization,
|
|
|
|
profile_RemoveStreamPrioritization,
|
|
|
|
profile_CreateNewStreamPrioritization,
|
|
|
|
profile_GetExpectedPacketCount,
|
|
|
|
};
|
|
|
|
|
2021-10-20 18:09:48 +02:00
|
|
|
static struct wm_reader *impl_from_IWMHeaderInfo3(IWMHeaderInfo3 *iface)
|
|
|
|
{
|
|
|
|
return CONTAINING_RECORD(iface, struct wm_reader, IWMHeaderInfo3_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI header_info_QueryInterface(IWMHeaderInfo3 *iface, REFIID iid, void **out)
|
|
|
|
{
|
|
|
|
struct wm_reader *reader = impl_from_IWMHeaderInfo3(iface);
|
|
|
|
|
|
|
|
return IWMProfile3_QueryInterface(&reader->IWMProfile3_iface, iid, out);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI header_info_AddRef(IWMHeaderInfo3 *iface)
|
|
|
|
{
|
|
|
|
struct wm_reader *reader = impl_from_IWMHeaderInfo3(iface);
|
|
|
|
|
|
|
|
return IWMProfile3_AddRef(&reader->IWMProfile3_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI header_info_Release(IWMHeaderInfo3 *iface)
|
|
|
|
{
|
|
|
|
struct wm_reader *reader = impl_from_IWMHeaderInfo3(iface);
|
|
|
|
|
|
|
|
return IWMProfile3_Release(&reader->IWMProfile3_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI header_info_GetAttributeCount(IWMHeaderInfo3 *iface, WORD stream_number, WORD *count)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, stream_number %u, count %p, stub!\n", iface, stream_number, count);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI header_info_GetAttributeByIndex(IWMHeaderInfo3 *iface, WORD index, WORD *stream_number,
|
|
|
|
WCHAR *name, WORD *name_len, WMT_ATTR_DATATYPE *type, BYTE *value, WORD *size)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, index %u, stream_number %p, name %p, name_len %p, type %p, value %p, size %p, stub!\n",
|
|
|
|
iface, index, stream_number, name, name_len, type, value, size);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI header_info_GetAttributeByName(IWMHeaderInfo3 *iface, WORD *stream_number,
|
|
|
|
const WCHAR *name, WMT_ATTR_DATATYPE *type, BYTE *value, WORD *size)
|
|
|
|
{
|
2021-11-02 06:13:02 +01:00
|
|
|
struct wm_reader *reader = impl_from_IWMHeaderInfo3(iface);
|
|
|
|
const WORD req_size = *size;
|
|
|
|
|
|
|
|
TRACE("reader %p, stream_number %p, name %s, type %p, value %p, size %u.\n",
|
|
|
|
reader, stream_number, debugstr_w(name), type, value, *size);
|
|
|
|
|
|
|
|
if (!stream_number)
|
|
|
|
return E_INVALIDARG;
|
|
|
|
|
|
|
|
if (!wcscmp(name, L"Duration"))
|
|
|
|
{
|
|
|
|
QWORD duration;
|
|
|
|
|
|
|
|
if (*stream_number)
|
|
|
|
{
|
|
|
|
WARN("Requesting duration for stream %u, returning ASF_E_NOTFOUND.\n", *stream_number);
|
|
|
|
return ASF_E_NOTFOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
*size = sizeof(QWORD);
|
|
|
|
if (!value)
|
|
|
|
{
|
|
|
|
*type = WMT_TYPE_QWORD;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
if (req_size < *size)
|
|
|
|
return ASF_E_BUFFERTOOSMALL;
|
|
|
|
|
|
|
|
*type = WMT_TYPE_QWORD;
|
|
|
|
EnterCriticalSection(&reader->cs);
|
|
|
|
duration = wg_parser_stream_get_duration(wg_parser_get_stream(reader->wg_parser, 0));
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
TRACE("Returning duration %s.\n", debugstr_time(duration));
|
|
|
|
memcpy(value, &duration, sizeof(QWORD));
|
|
|
|
return S_OK;
|
|
|
|
}
|
2021-11-02 06:13:03 +01:00
|
|
|
else if (!wcscmp(name, L"Seekable"))
|
|
|
|
{
|
|
|
|
if (*stream_number)
|
|
|
|
{
|
|
|
|
WARN("Requesting duration for stream %u, returning ASF_E_NOTFOUND.\n", *stream_number);
|
|
|
|
return ASF_E_NOTFOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
*size = sizeof(BOOL);
|
|
|
|
if (!value)
|
|
|
|
{
|
|
|
|
*type = WMT_TYPE_BOOL;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
if (req_size < *size)
|
|
|
|
return ASF_E_BUFFERTOOSMALL;
|
|
|
|
|
|
|
|
*type = WMT_TYPE_BOOL;
|
|
|
|
*(BOOL *)value = TRUE;
|
|
|
|
return S_OK;
|
|
|
|
}
|
2021-11-02 06:13:02 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
FIXME("Unknown attribute %s.\n", debugstr_w(name));
|
|
|
|
return ASF_E_NOTFOUND;
|
|
|
|
}
|
2021-10-20 18:09:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI header_info_SetAttribute(IWMHeaderInfo3 *iface, WORD stream_number,
|
|
|
|
const WCHAR *name, WMT_ATTR_DATATYPE type, const BYTE *value, WORD size)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, stream_number %u, name %s, type %#x, value %p, size %u, stub!\n",
|
|
|
|
iface, stream_number, debugstr_w(name), type, value, size);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI header_info_GetMarkerCount(IWMHeaderInfo3 *iface, WORD *count)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, count %p, stub!\n", iface, count);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI header_info_GetMarker(IWMHeaderInfo3 *iface,
|
|
|
|
WORD index, WCHAR *name, WORD *len, QWORD *time)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, index %u, name %p, len %p, time %p, stub!\n", iface, index, name, len, time);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI header_info_AddMarker(IWMHeaderInfo3 *iface, const WCHAR *name, QWORD time)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, name %s, time %s, stub!\n", iface, debugstr_w(name), debugstr_time(time));
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI header_info_RemoveMarker(IWMHeaderInfo3 *iface, WORD index)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, index %u, stub!\n", iface, index);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI header_info_GetScriptCount(IWMHeaderInfo3 *iface, WORD *count)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, count %p, stub!\n", iface, count);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI header_info_GetScript(IWMHeaderInfo3 *iface, WORD index, WCHAR *type,
|
|
|
|
WORD *type_len, WCHAR *command, WORD *command_len, QWORD *time)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, index %u, type %p, type_len %p, command %p, command_len %p, time %p, stub!\n",
|
|
|
|
iface, index, type, type_len, command, command_len, time);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI header_info_AddScript(IWMHeaderInfo3 *iface,
|
|
|
|
const WCHAR *type, const WCHAR *command, QWORD time)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, type %s, command %s, time %s, stub!\n",
|
|
|
|
iface, debugstr_w(type), debugstr_w(command), debugstr_time(time));
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI header_info_RemoveScript(IWMHeaderInfo3 *iface, WORD index)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, index %u, stub!\n", iface, index);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI header_info_GetCodecInfoCount(IWMHeaderInfo3 *iface, DWORD *count)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, count %p, stub!\n", iface, count);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI header_info_GetCodecInfo(IWMHeaderInfo3 *iface, DWORD index, WORD *name_len,
|
|
|
|
WCHAR *name, WORD *desc_len, WCHAR *desc, WMT_CODEC_INFO_TYPE *type, WORD *size, BYTE *info)
|
|
|
|
{
|
2022-02-04 01:45:24 +01:00
|
|
|
FIXME("iface %p, index %lu, name_len %p, name %p, desc_len %p, desc %p, type %p, size %p, info %p, stub!\n",
|
2021-10-20 18:09:48 +02:00
|
|
|
iface, index, name_len, name, desc_len, desc, type, size, info);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI header_info_GetAttributeCountEx(IWMHeaderInfo3 *iface, WORD stream_number, WORD *count)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, stream_number %u, count %p, stub!\n", iface, stream_number, count);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI header_info_GetAttributeIndices(IWMHeaderInfo3 *iface, WORD stream_number,
|
|
|
|
const WCHAR *name, WORD *lang_index, WORD *indices, WORD *count)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, stream_number %u, name %s, lang_index %p, indices %p, count %p, stub!\n",
|
|
|
|
iface, stream_number, debugstr_w(name), lang_index, indices, count);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI header_info_GetAttributeByIndexEx(IWMHeaderInfo3 *iface,
|
|
|
|
WORD stream_number, WORD index, WCHAR *name, WORD *name_len,
|
|
|
|
WMT_ATTR_DATATYPE *type, WORD *lang_index, BYTE *value, DWORD *size)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, stream_number %u, index %u, name %p, name_len %p,"
|
|
|
|
" type %p, lang_index %p, value %p, size %p, stub!\n",
|
2021-11-05 22:03:55 +01:00
|
|
|
iface, stream_number, index, name, name_len, type, lang_index, value, size);
|
2021-10-20 18:09:48 +02:00
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI header_info_ModifyAttribute(IWMHeaderInfo3 *iface, WORD stream_number,
|
|
|
|
WORD index, WMT_ATTR_DATATYPE type, WORD lang_index, const BYTE *value, DWORD size)
|
|
|
|
{
|
2022-02-04 01:45:24 +01:00
|
|
|
FIXME("iface %p, stream_number %u, index %u, type %#x, lang_index %u, value %p, size %lu, stub!\n",
|
2021-10-20 18:09:48 +02:00
|
|
|
iface, stream_number, index, type, lang_index, value, size);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI header_info_AddAttribute(IWMHeaderInfo3 *iface,
|
|
|
|
WORD stream_number, const WCHAR *name, WORD *index,
|
|
|
|
WMT_ATTR_DATATYPE type, WORD lang_index, const BYTE *value, DWORD size)
|
|
|
|
{
|
2022-02-04 01:45:24 +01:00
|
|
|
FIXME("iface %p, stream_number %u, name %s, index %p, type %#x, lang_index %u, value %p, size %lu, stub!\n",
|
2021-10-20 18:09:48 +02:00
|
|
|
iface, stream_number, debugstr_w(name), index, type, lang_index, value, size);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI header_info_DeleteAttribute(IWMHeaderInfo3 *iface, WORD stream_number, WORD index)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, stream_number %u, index %u, stub!\n", iface, stream_number, index);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI header_info_AddCodecInfo(IWMHeaderInfo3 *iface, const WCHAR *name,
|
|
|
|
const WCHAR *desc, WMT_CODEC_INFO_TYPE type, WORD size, BYTE *info)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, name %s, desc %s, type %#x, size %u, info %p, stub!\n",
|
|
|
|
info, debugstr_w(name), debugstr_w(desc), type, size, info);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const IWMHeaderInfo3Vtbl header_info_vtbl =
|
|
|
|
{
|
|
|
|
header_info_QueryInterface,
|
|
|
|
header_info_AddRef,
|
|
|
|
header_info_Release,
|
|
|
|
header_info_GetAttributeCount,
|
|
|
|
header_info_GetAttributeByIndex,
|
|
|
|
header_info_GetAttributeByName,
|
|
|
|
header_info_SetAttribute,
|
|
|
|
header_info_GetMarkerCount,
|
|
|
|
header_info_GetMarker,
|
|
|
|
header_info_AddMarker,
|
|
|
|
header_info_RemoveMarker,
|
|
|
|
header_info_GetScriptCount,
|
|
|
|
header_info_GetScript,
|
|
|
|
header_info_AddScript,
|
|
|
|
header_info_RemoveScript,
|
|
|
|
header_info_GetCodecInfoCount,
|
|
|
|
header_info_GetCodecInfo,
|
|
|
|
header_info_GetAttributeCountEx,
|
|
|
|
header_info_GetAttributeIndices,
|
|
|
|
header_info_GetAttributeByIndexEx,
|
|
|
|
header_info_ModifyAttribute,
|
|
|
|
header_info_AddAttribute,
|
|
|
|
header_info_DeleteAttribute,
|
|
|
|
header_info_AddCodecInfo,
|
|
|
|
};
|
|
|
|
|
2021-10-20 18:09:49 +02:00
|
|
|
static struct wm_reader *impl_from_IWMLanguageList(IWMLanguageList *iface)
|
|
|
|
{
|
|
|
|
return CONTAINING_RECORD(iface, struct wm_reader, IWMLanguageList_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI language_list_QueryInterface(IWMLanguageList *iface, REFIID iid, void **out)
|
|
|
|
{
|
|
|
|
struct wm_reader *reader = impl_from_IWMLanguageList(iface);
|
|
|
|
|
|
|
|
return IWMProfile3_QueryInterface(&reader->IWMProfile3_iface, iid, out);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI language_list_AddRef(IWMLanguageList *iface)
|
|
|
|
{
|
|
|
|
struct wm_reader *reader = impl_from_IWMLanguageList(iface);
|
|
|
|
|
|
|
|
return IWMProfile3_AddRef(&reader->IWMProfile3_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI language_list_Release(IWMLanguageList *iface)
|
|
|
|
{
|
|
|
|
struct wm_reader *reader = impl_from_IWMLanguageList(iface);
|
|
|
|
|
|
|
|
return IWMProfile3_Release(&reader->IWMProfile3_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI language_list_GetLanguageCount(IWMLanguageList *iface, WORD *count)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, count %p, stub!\n", iface, count);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI language_list_GetLanguageDetails(IWMLanguageList *iface,
|
|
|
|
WORD index, WCHAR *lang, WORD *len)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, index %u, lang %p, len %p, stub!\n", iface, index, lang, len);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI language_list_AddLanguageByRFC1766String(IWMLanguageList *iface,
|
|
|
|
const WCHAR *lang, WORD *index)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, lang %s, index %p, stub!\n", iface, debugstr_w(lang), index);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const IWMLanguageListVtbl language_list_vtbl =
|
|
|
|
{
|
|
|
|
language_list_QueryInterface,
|
|
|
|
language_list_AddRef,
|
|
|
|
language_list_Release,
|
|
|
|
language_list_GetLanguageCount,
|
|
|
|
language_list_GetLanguageDetails,
|
|
|
|
language_list_AddLanguageByRFC1766String,
|
|
|
|
};
|
|
|
|
|
2021-10-21 05:48:39 +02:00
|
|
|
static struct wm_reader *impl_from_IWMPacketSize2(IWMPacketSize2 *iface)
|
|
|
|
{
|
|
|
|
return CONTAINING_RECORD(iface, struct wm_reader, IWMPacketSize2_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI packet_size_QueryInterface(IWMPacketSize2 *iface, REFIID iid, void **out)
|
|
|
|
{
|
|
|
|
struct wm_reader *reader = impl_from_IWMPacketSize2(iface);
|
|
|
|
|
|
|
|
return IWMProfile3_QueryInterface(&reader->IWMProfile3_iface, iid, out);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI packet_size_AddRef(IWMPacketSize2 *iface)
|
|
|
|
{
|
|
|
|
struct wm_reader *reader = impl_from_IWMPacketSize2(iface);
|
|
|
|
|
|
|
|
return IWMProfile3_AddRef(&reader->IWMProfile3_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI packet_size_Release(IWMPacketSize2 *iface)
|
|
|
|
{
|
|
|
|
struct wm_reader *reader = impl_from_IWMPacketSize2(iface);
|
|
|
|
|
|
|
|
return IWMProfile3_Release(&reader->IWMProfile3_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI packet_size_GetMaxPacketSize(IWMPacketSize2 *iface, DWORD *size)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, size %p, stub!\n", iface, size);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI packet_size_SetMaxPacketSize(IWMPacketSize2 *iface, DWORD size)
|
|
|
|
{
|
2022-02-04 01:45:24 +01:00
|
|
|
FIXME("iface %p, size %lu, stub!\n", iface, size);
|
2021-10-21 05:48:39 +02:00
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI packet_size_GetMinPacketSize(IWMPacketSize2 *iface, DWORD *size)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, size %p, stub!\n", iface, size);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI packet_size_SetMinPacketSize(IWMPacketSize2 *iface, DWORD size)
|
|
|
|
{
|
2022-02-04 01:45:24 +01:00
|
|
|
FIXME("iface %p, size %lu, stub!\n", iface, size);
|
2021-10-21 05:48:39 +02:00
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const IWMPacketSize2Vtbl packet_size_vtbl =
|
|
|
|
{
|
|
|
|
packet_size_QueryInterface,
|
|
|
|
packet_size_AddRef,
|
|
|
|
packet_size_Release,
|
|
|
|
packet_size_GetMaxPacketSize,
|
|
|
|
packet_size_SetMaxPacketSize,
|
|
|
|
packet_size_GetMinPacketSize,
|
|
|
|
packet_size_SetMinPacketSize,
|
|
|
|
};
|
|
|
|
|
2021-10-21 05:48:40 +02:00
|
|
|
static struct wm_reader *impl_from_IWMReaderPlaylistBurn(IWMReaderPlaylistBurn *iface)
|
|
|
|
{
|
|
|
|
return CONTAINING_RECORD(iface, struct wm_reader, IWMReaderPlaylistBurn_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI playlist_QueryInterface(IWMReaderPlaylistBurn *iface, REFIID iid, void **out)
|
|
|
|
{
|
|
|
|
struct wm_reader *reader = impl_from_IWMReaderPlaylistBurn(iface);
|
|
|
|
|
|
|
|
return IWMProfile3_QueryInterface(&reader->IWMProfile3_iface, iid, out);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI playlist_AddRef(IWMReaderPlaylistBurn *iface)
|
|
|
|
{
|
|
|
|
struct wm_reader *reader = impl_from_IWMReaderPlaylistBurn(iface);
|
|
|
|
|
|
|
|
return IWMProfile3_AddRef(&reader->IWMProfile3_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI playlist_Release(IWMReaderPlaylistBurn *iface)
|
|
|
|
{
|
|
|
|
struct wm_reader *reader = impl_from_IWMReaderPlaylistBurn(iface);
|
|
|
|
|
|
|
|
return IWMProfile3_Release(&reader->IWMProfile3_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI playlist_InitPlaylistBurn(IWMReaderPlaylistBurn *iface, DWORD count,
|
|
|
|
const WCHAR **filenames, IWMStatusCallback *callback, void *context)
|
|
|
|
{
|
2022-02-04 01:45:24 +01:00
|
|
|
FIXME("iface %p, count %lu, filenames %p, callback %p, context %p, stub!\n",
|
2021-10-21 05:48:40 +02:00
|
|
|
iface, count, filenames, callback, context);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI playlist_GetInitResults(IWMReaderPlaylistBurn *iface, DWORD count, HRESULT *hrs)
|
|
|
|
{
|
2022-02-04 01:45:24 +01:00
|
|
|
FIXME("iface %p, count %lu, hrs %p, stub!\n", iface, count, hrs);
|
2021-10-21 05:48:40 +02:00
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI playlist_Cancel(IWMReaderPlaylistBurn *iface)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, stub!\n", iface);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI playlist_EndPlaylistBurn(IWMReaderPlaylistBurn *iface, HRESULT hr)
|
|
|
|
{
|
2022-02-04 01:45:24 +01:00
|
|
|
FIXME("iface %p, hr %#lx, stub!\n", iface, hr);
|
2021-10-21 05:48:40 +02:00
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const IWMReaderPlaylistBurnVtbl playlist_vtbl =
|
|
|
|
{
|
|
|
|
playlist_QueryInterface,
|
|
|
|
playlist_AddRef,
|
|
|
|
playlist_Release,
|
|
|
|
playlist_InitPlaylistBurn,
|
|
|
|
playlist_GetInitResults,
|
|
|
|
playlist_Cancel,
|
|
|
|
playlist_EndPlaylistBurn,
|
|
|
|
};
|
|
|
|
|
2021-10-21 05:48:41 +02:00
|
|
|
static struct wm_reader *impl_from_IWMReaderTimecode(IWMReaderTimecode *iface)
|
|
|
|
{
|
|
|
|
return CONTAINING_RECORD(iface, struct wm_reader, IWMReaderTimecode_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI timecode_QueryInterface(IWMReaderTimecode *iface, REFIID iid, void **out)
|
|
|
|
{
|
|
|
|
struct wm_reader *reader = impl_from_IWMReaderTimecode(iface);
|
|
|
|
|
|
|
|
return IWMProfile3_QueryInterface(&reader->IWMProfile3_iface, iid, out);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI timecode_AddRef(IWMReaderTimecode *iface)
|
|
|
|
{
|
|
|
|
struct wm_reader *reader = impl_from_IWMReaderTimecode(iface);
|
|
|
|
|
|
|
|
return IWMProfile3_AddRef(&reader->IWMProfile3_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI timecode_Release(IWMReaderTimecode *iface)
|
|
|
|
{
|
|
|
|
struct wm_reader *reader = impl_from_IWMReaderTimecode(iface);
|
|
|
|
|
|
|
|
return IWMProfile3_Release(&reader->IWMProfile3_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI timecode_GetTimecodeRangeCount(IWMReaderTimecode *iface,
|
|
|
|
WORD stream_number, WORD *count)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, stream_number %u, count %p, stub!\n", iface, stream_number, count);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI timecode_GetTimecodeRangeBounds(IWMReaderTimecode *iface,
|
|
|
|
WORD stream_number, WORD index, DWORD *start, DWORD *end)
|
|
|
|
{
|
|
|
|
FIXME("iface %p, stream_number %u, index %u, start %p, end %p, stub!\n",
|
|
|
|
iface, stream_number, index, start, end);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const IWMReaderTimecodeVtbl timecode_vtbl =
|
|
|
|
{
|
|
|
|
timecode_QueryInterface,
|
|
|
|
timecode_AddRef,
|
|
|
|
timecode_Release,
|
|
|
|
timecode_GetTimecodeRangeCount,
|
|
|
|
timecode_GetTimecodeRangeBounds,
|
|
|
|
};
|
|
|
|
|
2021-11-02 06:13:04 +01:00
|
|
|
static HRESULT init_stream(struct wm_reader *reader, QWORD file_size)
|
2021-10-26 01:52:11 +02:00
|
|
|
{
|
2021-10-26 01:52:12 +02:00
|
|
|
struct wg_parser *wg_parser;
|
|
|
|
HRESULT hr;
|
2021-10-27 05:22:05 +02:00
|
|
|
WORD i;
|
2021-10-26 01:52:12 +02:00
|
|
|
|
|
|
|
if (!(wg_parser = wg_parser_create(WG_PARSER_DECODEBIN, false)))
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
|
|
|
|
reader->wg_parser = wg_parser;
|
|
|
|
reader->read_thread_shutdown = false;
|
|
|
|
if (!(reader->read_thread = CreateThread(NULL, 0, read_thread, reader, 0, NULL)))
|
|
|
|
{
|
|
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
goto out_destroy_parser;
|
|
|
|
}
|
2021-10-26 01:52:11 +02:00
|
|
|
|
2021-11-02 06:13:04 +01:00
|
|
|
if (FAILED(hr = wg_parser_connect(reader->wg_parser, file_size)))
|
2021-10-26 01:52:12 +02:00
|
|
|
{
|
2022-02-04 01:45:24 +01:00
|
|
|
ERR("Failed to connect parser, hr %#lx.\n", hr);
|
2021-10-26 01:52:12 +02:00
|
|
|
goto out_shutdown_thread;
|
|
|
|
}
|
2021-10-26 01:52:11 +02:00
|
|
|
|
2021-10-26 01:52:13 +02:00
|
|
|
reader->stream_count = wg_parser_get_stream_count(reader->wg_parser);
|
|
|
|
|
2021-10-27 05:22:05 +02:00
|
|
|
if (!(reader->streams = calloc(reader->stream_count, sizeof(*reader->streams))))
|
|
|
|
{
|
|
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
goto out_disconnect_parser;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < reader->stream_count; ++i)
|
|
|
|
{
|
|
|
|
struct wm_stream *stream = &reader->streams[i];
|
|
|
|
|
|
|
|
stream->wg_stream = wg_parser_get_stream(reader->wg_parser, i);
|
|
|
|
stream->reader = reader;
|
2021-10-27 05:22:06 +02:00
|
|
|
stream->index = i;
|
2021-11-10 17:41:09 +01:00
|
|
|
stream->selection = WMT_ON;
|
2021-10-28 18:45:56 +02:00
|
|
|
wg_parser_stream_get_preferred_format(stream->wg_stream, &stream->format);
|
2021-10-28 18:45:58 +02:00
|
|
|
if (stream->format.major_type == WG_MAJOR_TYPE_AUDIO)
|
|
|
|
{
|
|
|
|
/* R.U.S.E enumerates available audio types, picks the first one it
|
|
|
|
* likes, and then sets the wrong stream to that type. libav might
|
|
|
|
* give us WG_AUDIO_FORMAT_F32LE by default, which will result in
|
|
|
|
* the game incorrectly interpreting float data as integer.
|
|
|
|
* Therefore just match native and always set our default format to
|
|
|
|
* S16LE. */
|
|
|
|
stream->format.u.audio.format = WG_AUDIO_FORMAT_S16LE;
|
|
|
|
}
|
|
|
|
else if (stream->format.major_type == WG_MAJOR_TYPE_VIDEO)
|
|
|
|
{
|
|
|
|
/* Call of Juarez: Bound in Blood breaks if I420 is enumerated.
|
|
|
|
* Some native decoders output I420, but the msmpeg4v3 decoder
|
2021-12-30 05:04:35 +01:00
|
|
|
* never does.
|
|
|
|
*
|
|
|
|
* Shadowgrounds provides wmv3 video and assumes that the initial
|
|
|
|
* video type will be BGR. */
|
|
|
|
stream->format.u.video.format = WG_VIDEO_FORMAT_BGR;
|
2021-10-28 18:45:58 +02:00
|
|
|
}
|
2021-11-01 01:03:33 +01:00
|
|
|
wg_parser_stream_enable(stream->wg_stream, &stream->format);
|
2021-10-27 05:22:05 +02:00
|
|
|
}
|
|
|
|
|
2021-11-01 01:03:33 +01:00
|
|
|
/* We probably discarded events because streams weren't enabled yet.
|
|
|
|
* Now that they're all enabled seek back to the start again. */
|
|
|
|
wg_parser_stream_seek(reader->streams[0].wg_stream, 1.0, 0, 0,
|
|
|
|
AM_SEEKING_AbsolutePositioning, AM_SEEKING_NoPositioning);
|
|
|
|
|
2021-10-26 01:52:11 +02:00
|
|
|
return S_OK;
|
2021-10-26 01:52:12 +02:00
|
|
|
|
2021-10-27 05:22:05 +02:00
|
|
|
out_disconnect_parser:
|
|
|
|
wg_parser_disconnect(reader->wg_parser);
|
|
|
|
|
2021-10-26 01:52:12 +02:00
|
|
|
out_shutdown_thread:
|
|
|
|
reader->read_thread_shutdown = true;
|
|
|
|
WaitForSingleObject(reader->read_thread, INFINITE);
|
|
|
|
CloseHandle(reader->read_thread);
|
|
|
|
reader->read_thread = NULL;
|
|
|
|
|
|
|
|
out_destroy_parser:
|
|
|
|
wg_parser_destroy(reader->wg_parser);
|
|
|
|
reader->wg_parser = NULL;
|
2021-11-02 06:13:04 +01:00
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT wm_reader_open_stream(struct wm_reader *reader, IStream *stream)
|
|
|
|
{
|
|
|
|
STATSTG stat;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
if (FAILED(hr = IStream_Stat(stream, &stat, STATFLAG_NONAME)))
|
|
|
|
{
|
2022-02-04 01:45:24 +01:00
|
|
|
ERR("Failed to stat stream, hr %#lx.\n", hr);
|
2021-11-02 06:13:04 +01:00
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
EnterCriticalSection(&reader->cs);
|
|
|
|
|
|
|
|
IStream_AddRef(reader->source_stream = stream);
|
|
|
|
if (FAILED(hr = init_stream(reader, stat.cbSize.QuadPart)))
|
|
|
|
{
|
|
|
|
IStream_Release(stream);
|
|
|
|
reader->source_stream = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT wm_reader_open_file(struct wm_reader *reader, const WCHAR *filename)
|
|
|
|
{
|
|
|
|
LARGE_INTEGER size;
|
|
|
|
HANDLE file;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
if ((file = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
|
|
|
|
OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
2022-02-04 01:45:24 +01:00
|
|
|
ERR("Failed to open %s, error %lu.\n", debugstr_w(filename), GetLastError());
|
2021-11-02 06:13:04 +01:00
|
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!GetFileSizeEx(file, &size))
|
|
|
|
{
|
2022-02-04 01:45:24 +01:00
|
|
|
ERR("Failed to get the size of %s, error %lu.\n", debugstr_w(filename), GetLastError());
|
2021-11-02 06:13:04 +01:00
|
|
|
CloseHandle(file);
|
|
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
}
|
|
|
|
|
|
|
|
EnterCriticalSection(&reader->cs);
|
|
|
|
|
|
|
|
reader->file = file;
|
|
|
|
|
|
|
|
if (FAILED(hr = init_stream(reader, size.QuadPart)))
|
|
|
|
reader->file = NULL;
|
2021-10-26 01:52:12 +02:00
|
|
|
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return hr;
|
2021-10-26 01:52:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT wm_reader_close(struct wm_reader *reader)
|
|
|
|
{
|
|
|
|
EnterCriticalSection(&reader->cs);
|
|
|
|
|
2021-11-02 06:13:04 +01:00
|
|
|
if (!reader->wg_parser)
|
2021-10-26 01:52:11 +02:00
|
|
|
{
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return NS_E_INVALID_REQUEST;
|
|
|
|
}
|
|
|
|
|
2021-10-26 01:52:12 +02:00
|
|
|
wg_parser_disconnect(reader->wg_parser);
|
|
|
|
|
|
|
|
reader->read_thread_shutdown = true;
|
|
|
|
WaitForSingleObject(reader->read_thread, INFINITE);
|
|
|
|
CloseHandle(reader->read_thread);
|
|
|
|
reader->read_thread = NULL;
|
|
|
|
|
2021-11-11 02:05:46 +01:00
|
|
|
if (reader->callback_advanced)
|
|
|
|
IWMReaderCallbackAdvanced_Release(reader->callback_advanced);
|
|
|
|
reader->callback_advanced = NULL;
|
|
|
|
|
2021-10-26 01:52:12 +02:00
|
|
|
wg_parser_destroy(reader->wg_parser);
|
|
|
|
reader->wg_parser = NULL;
|
2021-11-02 06:13:04 +01:00
|
|
|
|
|
|
|
if (reader->source_stream)
|
|
|
|
IStream_Release(reader->source_stream);
|
2021-10-26 01:52:11 +02:00
|
|
|
reader->source_stream = NULL;
|
2021-11-02 06:13:04 +01:00
|
|
|
if (reader->file)
|
|
|
|
CloseHandle(reader->file);
|
|
|
|
reader->file = NULL;
|
2021-10-26 01:52:11 +02:00
|
|
|
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2021-11-01 01:03:33 +01:00
|
|
|
struct wm_stream *wm_reader_get_stream_by_stream_number(struct wm_reader *reader, WORD stream_number)
|
|
|
|
{
|
|
|
|
if (stream_number && stream_number <= reader->stream_count)
|
|
|
|
return &reader->streams[stream_number - 1];
|
|
|
|
WARN("Invalid stream number %u.\n", stream_number);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-10-28 18:45:57 +02:00
|
|
|
HRESULT wm_reader_get_output_props(struct wm_reader *reader, DWORD output, IWMOutputMediaProps **props)
|
|
|
|
{
|
|
|
|
struct wm_stream *stream;
|
|
|
|
|
|
|
|
EnterCriticalSection(&reader->cs);
|
|
|
|
|
|
|
|
if (!(stream = get_stream_by_output_number(reader, output)))
|
|
|
|
{
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
|
2021-10-28 18:45:58 +02:00
|
|
|
*props = output_props_create(&stream->format);
|
2021-10-28 18:45:57 +02:00
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return *props ? S_OK : E_OUTOFMEMORY;
|
|
|
|
}
|
|
|
|
|
2021-10-28 18:45:59 +02:00
|
|
|
static const enum wg_video_format video_formats[] =
|
|
|
|
{
|
|
|
|
/* Try to prefer YUV formats over RGB ones. Most decoders output in the
|
|
|
|
* YUV color space, and it's generally much less expensive for
|
|
|
|
* videoconvert to do YUV -> YUV transformations. */
|
|
|
|
WG_VIDEO_FORMAT_NV12,
|
|
|
|
WG_VIDEO_FORMAT_YV12,
|
|
|
|
WG_VIDEO_FORMAT_YUY2,
|
|
|
|
WG_VIDEO_FORMAT_UYVY,
|
|
|
|
WG_VIDEO_FORMAT_YVYU,
|
|
|
|
WG_VIDEO_FORMAT_BGRx,
|
|
|
|
WG_VIDEO_FORMAT_BGR,
|
|
|
|
WG_VIDEO_FORMAT_RGB16,
|
|
|
|
WG_VIDEO_FORMAT_RGB15,
|
|
|
|
};
|
|
|
|
|
2021-10-28 18:46:00 +02:00
|
|
|
HRESULT wm_reader_get_output_format_count(struct wm_reader *reader, DWORD output, DWORD *count)
|
|
|
|
{
|
|
|
|
struct wm_stream *stream;
|
|
|
|
struct wg_format format;
|
|
|
|
|
|
|
|
EnterCriticalSection(&reader->cs);
|
|
|
|
|
|
|
|
if (!(stream = get_stream_by_output_number(reader, output)))
|
|
|
|
{
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
wg_parser_stream_get_preferred_format(stream->wg_stream, &format);
|
|
|
|
switch (format.major_type)
|
|
|
|
{
|
|
|
|
case WG_MAJOR_TYPE_VIDEO:
|
|
|
|
*count = ARRAY_SIZE(video_formats);
|
|
|
|
break;
|
|
|
|
|
2022-02-16 12:19:07 +01:00
|
|
|
case WG_MAJOR_TYPE_WMA:
|
|
|
|
FIXME("WMA format not implemented!\n");
|
|
|
|
/* fallthrough */
|
2021-10-28 18:46:00 +02:00
|
|
|
case WG_MAJOR_TYPE_AUDIO:
|
|
|
|
case WG_MAJOR_TYPE_UNKNOWN:
|
|
|
|
*count = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2021-10-28 18:45:59 +02:00
|
|
|
HRESULT wm_reader_get_output_format(struct wm_reader *reader, DWORD output,
|
|
|
|
DWORD index, IWMOutputMediaProps **props)
|
|
|
|
{
|
|
|
|
struct wm_stream *stream;
|
|
|
|
struct wg_format format;
|
|
|
|
|
|
|
|
EnterCriticalSection(&reader->cs);
|
|
|
|
|
|
|
|
if (!(stream = get_stream_by_output_number(reader, output)))
|
|
|
|
{
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
wg_parser_stream_get_preferred_format(stream->wg_stream, &format);
|
|
|
|
|
|
|
|
switch (format.major_type)
|
|
|
|
{
|
|
|
|
case WG_MAJOR_TYPE_VIDEO:
|
|
|
|
if (index >= ARRAY_SIZE(video_formats))
|
|
|
|
{
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return NS_E_INVALID_OUTPUT_FORMAT;
|
|
|
|
}
|
|
|
|
format.u.video.format = video_formats[index];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WG_MAJOR_TYPE_AUDIO:
|
|
|
|
if (index)
|
|
|
|
{
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return NS_E_INVALID_OUTPUT_FORMAT;
|
|
|
|
}
|
|
|
|
format.u.audio.format = WG_AUDIO_FORMAT_S16LE;
|
|
|
|
break;
|
|
|
|
|
2022-02-16 12:19:07 +01:00
|
|
|
case WG_MAJOR_TYPE_WMA:
|
|
|
|
FIXME("WMA format not implemented!\n");
|
|
|
|
break;
|
2021-10-28 18:45:59 +02:00
|
|
|
case WG_MAJOR_TYPE_UNKNOWN:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
|
|
|
|
*props = output_props_create(&format);
|
|
|
|
return *props ? S_OK : E_OUTOFMEMORY;
|
|
|
|
}
|
|
|
|
|
2021-11-01 01:03:32 +01:00
|
|
|
HRESULT wm_reader_set_output_props(struct wm_reader *reader, DWORD output,
|
|
|
|
IWMOutputMediaProps *props_iface)
|
|
|
|
{
|
|
|
|
struct output_props *props = unsafe_impl_from_IWMOutputMediaProps(props_iface);
|
|
|
|
struct wg_format format, pref_format;
|
|
|
|
struct wm_stream *stream;
|
|
|
|
|
|
|
|
strmbase_dump_media_type(&props->mt);
|
|
|
|
|
|
|
|
if (!amt_to_wg_format(&props->mt, &format))
|
|
|
|
{
|
|
|
|
ERR("Failed to convert media type to winegstreamer format.\n");
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
EnterCriticalSection(&reader->cs);
|
|
|
|
|
|
|
|
if (!(stream = get_stream_by_output_number(reader, output)))
|
|
|
|
{
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
wg_parser_stream_get_preferred_format(stream->wg_stream, &pref_format);
|
|
|
|
if (pref_format.major_type != format.major_type)
|
|
|
|
{
|
|
|
|
/* R.U.S.E sets the type of the wrong stream, apparently by accident. */
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
WARN("Major types don't match; returning NS_E_INCOMPATIBLE_FORMAT.\n");
|
|
|
|
return NS_E_INCOMPATIBLE_FORMAT;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream->format = format;
|
|
|
|
wg_parser_stream_enable(stream->wg_stream, &format);
|
|
|
|
|
2021-11-01 01:03:37 +01:00
|
|
|
/* Re-decode any buffers that might have been generated with the old format.
|
|
|
|
*
|
|
|
|
* FIXME: Seeking in-place will cause some buffers to be dropped.
|
|
|
|
* Unfortunately, we can't really store the last received PTS and seek there
|
|
|
|
* either: since seeks are inexact and we aren't guaranteed to receive
|
|
|
|
* samples in order, some buffers might be duplicated or dropped anyway.
|
|
|
|
* In order to really seamlessly allow for format changes, we need
|
|
|
|
* cooperation from each individual GStreamer stream, to be able to tell
|
|
|
|
* upstream exactly which buffers they need resent...
|
|
|
|
*
|
|
|
|
* In all likelihood this function is being called not mid-stream but rather
|
|
|
|
* while setting the stream up, before consuming any events. Accordingly
|
|
|
|
* let's just seek back to the beginning. */
|
|
|
|
wg_parser_stream_seek(reader->streams[0].wg_stream, 1.0, reader->start_time, 0,
|
|
|
|
AM_SEEKING_AbsolutePositioning, AM_SEEKING_NoPositioning);
|
|
|
|
|
2021-11-01 01:03:32 +01:00
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2021-11-01 01:03:33 +01:00
|
|
|
static const char *get_major_type_string(enum wg_major_type type)
|
|
|
|
{
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case WG_MAJOR_TYPE_AUDIO:
|
|
|
|
return "audio";
|
|
|
|
case WG_MAJOR_TYPE_VIDEO:
|
|
|
|
return "video";
|
|
|
|
case WG_MAJOR_TYPE_UNKNOWN:
|
|
|
|
return "unknown";
|
2022-02-16 12:19:07 +01:00
|
|
|
case WG_MAJOR_TYPE_WMA:
|
|
|
|
return "wma";
|
2021-11-01 01:03:33 +01:00
|
|
|
}
|
|
|
|
assert(0);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT wm_reader_get_stream_sample(struct wm_stream *stream,
|
|
|
|
INSSBuffer **ret_sample, QWORD *pts, QWORD *duration, DWORD *flags)
|
|
|
|
{
|
2021-11-11 02:05:46 +01:00
|
|
|
IWMReaderCallbackAdvanced *callback_advanced = stream->reader->callback_advanced;
|
2021-11-01 01:03:33 +01:00
|
|
|
struct wg_parser_stream *wg_stream = stream->wg_stream;
|
|
|
|
struct wg_parser_event event;
|
|
|
|
|
2021-11-10 17:41:09 +01:00
|
|
|
if (stream->selection == WMT_OFF)
|
|
|
|
return NS_E_INVALID_REQUEST;
|
|
|
|
|
2021-11-01 01:03:33 +01:00
|
|
|
if (stream->eos)
|
|
|
|
return NS_E_NO_MORE_SAMPLES;
|
|
|
|
|
|
|
|
for (;;)
|
|
|
|
{
|
winegstreamer: Remove support for flushing the wg_parser object.
Aside from EOS logic, which is now handled entirely on the client side,
wg_parser_stream_get_event() now only waits for data processing—that is,
demuxing, decoding, and format conversion. While unblocking waits in
wg_parser_stream_get_event() does allow that function to return immediately, a
subsequent seek request in GStreamer will still have to wait for that data
processing to complete and for the stream thread to return to the demuxer's main
loop. In essence, wg_parser_begin_flush() is only moving costs around.
In theory we could force the GStreamer pipeline to complete faster by actually
flushing it. In practice this isn't really true. Individual elements do check
whether they are flushing before processing, but even elements which take a
relatively long time (i.e. multiple milliseconds) to process data don't
periodically check whether they are flushing while doing so. Although there is
arguably a benefit to skipping some elements by flushing the GStreamer pipeline,
it does not seem worth the added code complexity in Wine.
The real point of flushing in DirectShow or GStreamer is to unblock long or
unbounded waits in sink elements (i.e. waits for PTS, or waits for running state
while rendering preroll frames). None of these waits apply here. Waits for
actual sample processing complete in bounded time, and should ideally take less
than the sample DTS to complete (or we are already in trouble).
Signed-off-by: Zebediah Figura <zfigura@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2022-02-23 22:46:47 +01:00
|
|
|
wg_parser_stream_get_event(wg_stream, &event);
|
2021-11-01 01:03:33 +01:00
|
|
|
|
|
|
|
TRACE("Got event of type %#x for %s stream %p.\n", event.type,
|
|
|
|
get_major_type_string(stream->format.major_type), stream);
|
|
|
|
|
|
|
|
switch (event.type)
|
|
|
|
{
|
|
|
|
case WG_PARSER_EVENT_BUFFER:
|
2021-11-11 02:05:46 +01:00
|
|
|
{
|
|
|
|
DWORD size, capacity;
|
|
|
|
INSSBuffer *sample;
|
|
|
|
HRESULT hr;
|
|
|
|
BYTE *data;
|
|
|
|
|
2021-11-11 02:05:49 +01:00
|
|
|
if (callback_advanced && stream->read_compressed && stream->allocate_stream)
|
|
|
|
{
|
|
|
|
if (FAILED(hr = IWMReaderCallbackAdvanced_AllocateForStream(callback_advanced,
|
|
|
|
stream->index + 1, event.u.buffer.size, &sample, NULL)))
|
|
|
|
{
|
2022-02-04 01:45:24 +01:00
|
|
|
ERR("Failed to allocate stream sample of %u bytes, hr %#lx.\n", event.u.buffer.size, hr);
|
2021-11-11 02:05:49 +01:00
|
|
|
wg_parser_stream_release_buffer(wg_stream);
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (callback_advanced && !stream->read_compressed && stream->allocate_output)
|
2021-11-01 01:03:33 +01:00
|
|
|
{
|
2021-11-11 02:05:46 +01:00
|
|
|
if (FAILED(hr = IWMReaderCallbackAdvanced_AllocateForOutput(callback_advanced,
|
|
|
|
stream->index, event.u.buffer.size, &sample, NULL)))
|
|
|
|
{
|
2022-02-04 01:45:24 +01:00
|
|
|
ERR("Failed to allocate output sample of %u bytes, hr %#lx.\n", event.u.buffer.size, hr);
|
2021-11-11 02:05:46 +01:00
|
|
|
wg_parser_stream_release_buffer(wg_stream);
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
struct buffer *object;
|
|
|
|
|
|
|
|
/* FIXME: Should these be pooled? */
|
|
|
|
if (!(object = calloc(1, offsetof(struct buffer, data[event.u.buffer.size]))))
|
|
|
|
{
|
|
|
|
wg_parser_stream_release_buffer(wg_stream);
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
object->INSSBuffer_iface.lpVtbl = &buffer_vtbl;
|
|
|
|
object->refcount = 1;
|
|
|
|
object->capacity = event.u.buffer.size;
|
|
|
|
|
|
|
|
TRACE("Created buffer %p.\n", object);
|
|
|
|
sample = &object->INSSBuffer_iface;
|
2021-11-01 01:03:33 +01:00
|
|
|
}
|
|
|
|
|
2021-11-11 02:05:46 +01:00
|
|
|
if (FAILED(hr = INSSBuffer_GetBufferAndLength(sample, &data, &size)))
|
2022-02-04 01:45:24 +01:00
|
|
|
ERR("Failed to get data pointer, hr %#lx.\n", hr);
|
2021-11-11 02:05:46 +01:00
|
|
|
if (FAILED(hr = INSSBuffer_GetMaxLength(sample, &capacity)))
|
2022-02-04 01:45:24 +01:00
|
|
|
ERR("Failed to get capacity, hr %#lx.\n", hr);
|
2021-11-11 02:05:46 +01:00
|
|
|
if (event.u.buffer.size > capacity)
|
2022-02-04 01:45:24 +01:00
|
|
|
ERR("Returned capacity %lu is less than requested capacity %u.\n",
|
2021-11-11 02:05:46 +01:00
|
|
|
capacity, event.u.buffer.size);
|
2021-11-01 01:03:34 +01:00
|
|
|
|
2021-11-11 02:05:46 +01:00
|
|
|
if (!wg_parser_stream_copy_buffer(wg_stream, data, 0, event.u.buffer.size))
|
2021-11-01 01:03:34 +01:00
|
|
|
{
|
|
|
|
/* The GStreamer pin has been flushed. */
|
2021-11-11 02:05:46 +01:00
|
|
|
INSSBuffer_Release(sample);
|
2021-11-01 01:03:34 +01:00
|
|
|
break;
|
|
|
|
}
|
2021-11-01 01:03:33 +01:00
|
|
|
|
2021-11-11 02:05:46 +01:00
|
|
|
if (FAILED(hr = INSSBuffer_SetLength(sample, event.u.buffer.size)))
|
2022-02-04 01:45:24 +01:00
|
|
|
ERR("Failed to set size %u, hr %#lx.\n", event.u.buffer.size, hr);
|
2021-11-11 02:05:46 +01:00
|
|
|
|
2021-11-01 01:03:33 +01:00
|
|
|
wg_parser_stream_release_buffer(wg_stream);
|
|
|
|
|
|
|
|
if (!event.u.buffer.has_pts)
|
|
|
|
FIXME("Missing PTS.\n");
|
|
|
|
if (!event.u.buffer.has_duration)
|
|
|
|
FIXME("Missing duration.\n");
|
|
|
|
|
|
|
|
*pts = event.u.buffer.pts;
|
|
|
|
*duration = event.u.buffer.duration;
|
|
|
|
*flags = 0;
|
|
|
|
if (event.u.buffer.discontinuity)
|
|
|
|
*flags |= WM_SF_DISCONTINUITY;
|
|
|
|
if (!event.u.buffer.delta)
|
|
|
|
*flags |= WM_SF_CLEANPOINT;
|
|
|
|
|
2021-11-11 02:05:46 +01:00
|
|
|
*ret_sample = sample;
|
2021-11-01 01:03:33 +01:00
|
|
|
return S_OK;
|
2021-11-11 02:05:46 +01:00
|
|
|
}
|
2021-11-01 01:03:33 +01:00
|
|
|
|
|
|
|
case WG_PARSER_EVENT_EOS:
|
|
|
|
stream->eos = true;
|
|
|
|
TRACE("End of stream.\n");
|
|
|
|
return NS_E_NO_MORE_SAMPLES;
|
|
|
|
|
|
|
|
case WG_PARSER_EVENT_NONE:
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-01 01:03:36 +01:00
|
|
|
void wm_reader_seek(struct wm_reader *reader, QWORD start, LONGLONG duration)
|
|
|
|
{
|
|
|
|
WORD i;
|
|
|
|
|
|
|
|
EnterCriticalSection(&reader->cs);
|
|
|
|
|
2021-11-01 01:03:37 +01:00
|
|
|
reader->start_time = start;
|
|
|
|
|
2021-11-01 01:03:36 +01:00
|
|
|
wg_parser_stream_seek(reader->streams[0].wg_stream, 1.0, start, start + duration,
|
|
|
|
AM_SEEKING_AbsolutePositioning, duration ? AM_SEEKING_AbsolutePositioning : AM_SEEKING_NoPositioning);
|
|
|
|
|
|
|
|
for (i = 0; i < reader->stream_count; ++i)
|
|
|
|
reader->streams[i].eos = false;
|
|
|
|
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
}
|
|
|
|
|
2021-11-10 17:41:09 +01:00
|
|
|
HRESULT wm_reader_set_streams_selected(struct wm_reader *reader, WORD count,
|
|
|
|
const WORD *stream_numbers, const WMT_STREAM_SELECTION *selections)
|
|
|
|
{
|
|
|
|
struct wm_stream *stream;
|
|
|
|
WORD i;
|
|
|
|
|
|
|
|
if (!count)
|
|
|
|
return E_INVALIDARG;
|
|
|
|
|
|
|
|
EnterCriticalSection(&reader->cs);
|
|
|
|
|
|
|
|
for (i = 0; i < count; ++i)
|
|
|
|
{
|
|
|
|
if (!(stream = wm_reader_get_stream_by_stream_number(reader, stream_numbers[i])))
|
|
|
|
{
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
WARN("Invalid stream number %u; returning NS_E_INVALID_REQUEST.\n", stream_numbers[i]);
|
|
|
|
return NS_E_INVALID_REQUEST;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < count; ++i)
|
|
|
|
{
|
|
|
|
stream = wm_reader_get_stream_by_stream_number(reader, stream_numbers[i]);
|
|
|
|
stream->selection = selections[i];
|
|
|
|
if (selections[i] == WMT_OFF)
|
|
|
|
{
|
|
|
|
TRACE("Disabling stream %u.\n", stream_numbers[i]);
|
|
|
|
wg_parser_stream_disable(stream->wg_stream);
|
|
|
|
}
|
|
|
|
else if (selections[i] == WMT_ON)
|
|
|
|
{
|
|
|
|
if (selections[i] != WMT_ON)
|
|
|
|
FIXME("Ignoring selection %#x for stream %u; treating as enabled.\n",
|
|
|
|
selections[i], stream_numbers[i]);
|
|
|
|
TRACE("Enabling stream %u.\n", stream_numbers[i]);
|
|
|
|
wg_parser_stream_enable(stream->wg_stream, &stream->format);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2021-11-10 17:41:11 +01:00
|
|
|
HRESULT wm_reader_get_stream_selection(struct wm_reader *reader,
|
|
|
|
WORD stream_number, WMT_STREAM_SELECTION *selection)
|
|
|
|
{
|
|
|
|
struct wm_stream *stream;
|
|
|
|
|
|
|
|
EnterCriticalSection(&reader->cs);
|
|
|
|
|
|
|
|
if (!(stream = wm_reader_get_stream_by_stream_number(reader, stream_number)))
|
|
|
|
{
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
*selection = stream->selection;
|
|
|
|
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2021-11-11 02:05:46 +01:00
|
|
|
HRESULT wm_reader_set_allocate_for_output(struct wm_reader *reader, DWORD output, BOOL allocate)
|
|
|
|
{
|
|
|
|
struct wm_stream *stream;
|
|
|
|
|
|
|
|
EnterCriticalSection(&reader->cs);
|
|
|
|
|
|
|
|
if (!(stream = get_stream_by_output_number(reader, output)))
|
|
|
|
{
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream->allocate_output = !!allocate;
|
|
|
|
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2021-11-11 02:05:49 +01:00
|
|
|
HRESULT wm_reader_set_allocate_for_stream(struct wm_reader *reader, WORD stream_number, BOOL allocate)
|
|
|
|
{
|
|
|
|
struct wm_stream *stream;
|
|
|
|
|
|
|
|
EnterCriticalSection(&reader->cs);
|
|
|
|
|
|
|
|
if (!(stream = wm_reader_get_stream_by_stream_number(reader, stream_number)))
|
|
|
|
{
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream->allocate_stream = !!allocate;
|
|
|
|
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2021-11-11 02:05:48 +01:00
|
|
|
HRESULT wm_reader_set_read_compressed(struct wm_reader *reader, WORD stream_number, BOOL compressed)
|
|
|
|
{
|
|
|
|
struct wm_stream *stream;
|
|
|
|
|
|
|
|
EnterCriticalSection(&reader->cs);
|
|
|
|
|
|
|
|
if (!(stream = wm_reader_get_stream_by_stream_number(reader, stream_number)))
|
|
|
|
{
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream->read_compressed = compressed;
|
|
|
|
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2021-11-12 06:43:21 +01:00
|
|
|
HRESULT wm_reader_get_max_stream_size(struct wm_reader *reader, WORD stream_number, DWORD *size)
|
|
|
|
{
|
|
|
|
struct wm_stream *stream;
|
|
|
|
|
|
|
|
EnterCriticalSection(&reader->cs);
|
|
|
|
|
|
|
|
if (!(stream = wm_reader_get_stream_by_stream_number(reader, stream_number)))
|
|
|
|
{
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
*size = wg_format_get_max_size(&stream->format);
|
|
|
|
|
|
|
|
LeaveCriticalSection(&reader->cs);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2021-10-20 18:09:45 +02:00
|
|
|
void wm_reader_init(struct wm_reader *reader, const struct wm_reader_ops *ops)
|
|
|
|
{
|
2021-10-20 18:09:48 +02:00
|
|
|
reader->IWMHeaderInfo3_iface.lpVtbl = &header_info_vtbl;
|
2021-10-20 18:09:49 +02:00
|
|
|
reader->IWMLanguageList_iface.lpVtbl = &language_list_vtbl;
|
2021-10-21 05:48:39 +02:00
|
|
|
reader->IWMPacketSize2_iface.lpVtbl = &packet_size_vtbl;
|
2021-10-20 18:09:45 +02:00
|
|
|
reader->IWMProfile3_iface.lpVtbl = &profile_vtbl;
|
2021-10-21 05:48:40 +02:00
|
|
|
reader->IWMReaderPlaylistBurn_iface.lpVtbl = &playlist_vtbl;
|
2021-10-21 05:48:41 +02:00
|
|
|
reader->IWMReaderTimecode_iface.lpVtbl = &timecode_vtbl;
|
2021-10-20 18:09:45 +02:00
|
|
|
reader->refcount = 1;
|
|
|
|
reader->ops = ops;
|
2021-10-26 01:52:11 +02:00
|
|
|
|
|
|
|
InitializeCriticalSection(&reader->cs);
|
|
|
|
reader->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": wm_reader.cs");
|
|
|
|
}
|
|
|
|
|
|
|
|
void wm_reader_cleanup(struct wm_reader *reader)
|
|
|
|
{
|
|
|
|
reader->cs.DebugInfo->Spare[0] = 0;
|
|
|
|
DeleteCriticalSection(&reader->cs);
|
2021-10-20 18:09:45 +02:00
|
|
|
}
|