wine/dlls/winegstreamer/resampler.c
Conor McCarthy c767b6a641 winegstreamer: Do not pass a sample size to wg_transform_read_mf().
The size is not used. We could perhaps check it against the maximum size
of the MF sample, but it would be more relevant to check the maximum size
against the size GStreamer requires, which is unknown in this function.
2025-08-05 22:11:12 +09:00

949 lines
29 KiB
C

/* Copyright 2022 Rémi Bernon for CodeWeavers
*
* 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"
#include "mfapi.h"
#include "mferror.h"
#include "mfobjects.h"
#include "mftransform.h"
#include "wmcodecdsp.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
WINE_DECLARE_DEBUG_CHANNEL(winediag);
static const GUID *audio_formats[] =
{
&MFAudioFormat_Float,
&MFAudioFormat_PCM,
};
struct resampler
{
IUnknown IUnknown_inner;
IMFTransform IMFTransform_iface;
IMediaObject IMediaObject_iface;
IPropertyBag IPropertyBag_iface;
IPropertyStore IPropertyStore_iface;
IWMResamplerProps IWMResamplerProps_iface;
IUnknown *outer;
LONG refcount;
IMFMediaType *input_type;
MFT_INPUT_STREAM_INFO input_info;
IMFMediaType *output_type;
MFT_OUTPUT_STREAM_INFO output_info;
wg_transform_t wg_transform;
struct wg_sample_queue *wg_sample_queue;
};
static HRESULT try_create_wg_transform(struct resampler *impl)
{
struct wg_transform_attrs attrs = {0};
if (impl->wg_transform)
{
wg_transform_destroy(impl->wg_transform);
impl->wg_transform = 0;
}
return wg_transform_create_mf(impl->input_type, impl->output_type, &attrs, &impl->wg_transform);
}
static inline struct resampler *impl_from_IUnknown(IUnknown *iface)
{
return CONTAINING_RECORD(iface, struct resampler, IUnknown_inner);
}
static HRESULT WINAPI unknown_QueryInterface(IUnknown *iface, REFIID iid, void **out)
{
struct resampler *impl = impl_from_IUnknown(iface);
TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out);
if (IsEqualGUID(iid, &IID_IUnknown))
*out = &impl->IUnknown_inner;
else if (IsEqualGUID(iid, &IID_IMFTransform))
*out = &impl->IMFTransform_iface;
else if (IsEqualGUID(iid, &IID_IMediaObject))
*out = &impl->IMediaObject_iface;
else if (IsEqualIID(iid, &IID_IPropertyBag))
*out = &impl->IPropertyBag_iface;
else if (IsEqualIID(iid, &IID_IPropertyStore))
*out = &impl->IPropertyStore_iface;
else if (IsEqualIID(iid, &IID_IWMResamplerProps))
*out = &impl->IWMResamplerProps_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 unknown_AddRef(IUnknown *iface)
{
struct resampler *impl = impl_from_IUnknown(iface);
ULONG refcount = InterlockedIncrement(&impl->refcount);
TRACE("iface %p increasing refcount to %lu.\n", iface, refcount);
return refcount;
}
static ULONG WINAPI unknown_Release(IUnknown *iface)
{
struct resampler *impl = impl_from_IUnknown(iface);
ULONG refcount = InterlockedDecrement(&impl->refcount);
TRACE("iface %p decreasing refcount to %lu.\n", iface, refcount);
if (!refcount)
{
if (impl->wg_transform)
wg_transform_destroy(impl->wg_transform);
if (impl->input_type)
IMFMediaType_Release(impl->input_type);
if (impl->output_type)
IMFMediaType_Release(impl->output_type);
wg_sample_queue_destroy(impl->wg_sample_queue);
free(impl);
}
return refcount;
}
static const IUnknownVtbl unknown_vtbl =
{
unknown_QueryInterface,
unknown_AddRef,
unknown_Release,
};
static struct resampler *impl_from_IMFTransform(IMFTransform *iface)
{
return CONTAINING_RECORD(iface, struct resampler, IMFTransform_iface);
}
static HRESULT WINAPI transform_QueryInterface(IMFTransform *iface, REFIID iid, void **out)
{
return IUnknown_QueryInterface(impl_from_IMFTransform(iface)->outer, iid, out);
}
static ULONG WINAPI transform_AddRef(IMFTransform *iface)
{
return IUnknown_AddRef(impl_from_IMFTransform(iface)->outer);
}
static ULONG WINAPI transform_Release(IMFTransform *iface)
{
return IUnknown_Release(impl_from_IMFTransform(iface)->outer);
}
static HRESULT WINAPI transform_GetStreamLimits(IMFTransform *iface, DWORD *input_minimum,
DWORD *input_maximum, DWORD *output_minimum, DWORD *output_maximum)
{
TRACE("iface %p, input_minimum %p, input_maximum %p, output_minimum %p, output_maximum %p.\n",
iface, input_minimum, input_maximum, output_minimum, output_maximum);
*input_minimum = *input_maximum = *output_minimum = *output_maximum = 1;
return S_OK;
}
static HRESULT WINAPI transform_GetStreamCount(IMFTransform *iface, DWORD *inputs, DWORD *outputs)
{
TRACE("iface %p, inputs %p, outputs %p.\n", iface, inputs, outputs);
*inputs = *outputs = 1;
return S_OK;
}
static HRESULT WINAPI transform_GetStreamIDs(IMFTransform *iface, DWORD input_size, DWORD *inputs,
DWORD output_size, DWORD *outputs)
{
TRACE("iface %p, input_size %lu, inputs %p, output_size %lu, outputs %p.\n", iface,
input_size, inputs, output_size, outputs);
return E_NOTIMPL;
}
static HRESULT WINAPI transform_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info)
{
struct resampler *impl = impl_from_IMFTransform(iface);
TRACE("iface %p, id %#lx, info %p.\n", iface, id, info);
if (!impl->input_type || !impl->output_type)
{
memset(info, 0, sizeof(*info));
return MF_E_TRANSFORM_TYPE_NOT_SET;
}
*info = impl->input_info;
return S_OK;
}
static HRESULT WINAPI transform_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info)
{
struct resampler *impl = impl_from_IMFTransform(iface);
TRACE("iface %p, id %#lx, info %p.\n", iface, id, info);
if (!impl->input_type || !impl->output_type)
{
memset(info, 0, sizeof(*info));
return MF_E_TRANSFORM_TYPE_NOT_SET;
}
*info = impl->output_info;
return S_OK;
}
static HRESULT WINAPI transform_GetAttributes(IMFTransform *iface, IMFAttributes **attributes)
{
TRACE("iface %p, attributes %p.\n", iface, attributes);
return E_NOTIMPL;
}
static HRESULT WINAPI transform_GetInputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes)
{
TRACE("iface %p, id %#lx, attributes %p.\n", iface, id, attributes);
return E_NOTIMPL;
}
static HRESULT WINAPI transform_GetOutputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes)
{
TRACE("iface %p, id %#lx, attributes %p.\n", iface, id, attributes);
return E_NOTIMPL;
}
static HRESULT WINAPI transform_DeleteInputStream(IMFTransform *iface, DWORD id)
{
TRACE("iface %p, id %#lx.\n", iface, id);
return E_NOTIMPL;
}
static HRESULT WINAPI transform_AddInputStreams(IMFTransform *iface, DWORD streams, DWORD *ids)
{
TRACE("iface %p, streams %lu, ids %p.\n", iface, streams, ids);
return E_NOTIMPL;
}
static HRESULT get_available_media_type(DWORD index, IMFMediaType **type, BOOL output)
{
UINT32 sample_size, sample_rate = 48000, block_alignment, channel_count = 2;
IMFMediaType *media_type;
const GUID *subtype;
HRESULT hr;
*type = NULL;
if (index >= (output ? 2 : 1) * ARRAY_SIZE(audio_formats))
return MF_E_NO_MORE_TYPES;
subtype = audio_formats[index % ARRAY_SIZE(audio_formats)];
if (FAILED(hr = MFCreateMediaType(&media_type)))
return hr;
if (FAILED(hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio)))
goto done;
if (FAILED(hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, subtype)))
goto done;
if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, 1)))
goto done;
if (index < ARRAY_SIZE(audio_formats))
goto done;
if (IsEqualGUID(subtype, &MFAudioFormat_Float))
sample_size = 32;
else if (IsEqualGUID(subtype, &MFAudioFormat_PCM))
sample_size = 16;
else
{
FIXME("Subtype %s not implemented!\n", debugstr_guid(subtype));
hr = E_NOTIMPL;
goto done;
}
if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, sample_size)))
goto done;
if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_NUM_CHANNELS, channel_count)))
goto done;
if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, sample_rate)))
goto done;
block_alignment = sample_size * channel_count / 8;
if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, block_alignment)))
goto done;
if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, sample_rate * block_alignment)))
goto done;
if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1)))
goto done;
done:
if (SUCCEEDED(hr))
IMFMediaType_AddRef((*type = media_type));
IMFMediaType_Release(media_type);
return hr;
}
static HRESULT WINAPI transform_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index,
IMFMediaType **type)
{
TRACE("iface %p, id %#lx, index %#lx, type %p.\n", iface, id, index, type);
return get_available_media_type(index, type, FALSE);
}
static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWORD id, DWORD index,
IMFMediaType **type)
{
TRACE("iface %p, id %#lx, index %#lx, type %p.\n", iface, id, index, type);
return get_available_media_type(index, type, TRUE);
}
static HRESULT check_media_type(IMFMediaType *type)
{
MF_ATTRIBUTE_TYPE item_type;
GUID major, subtype;
HRESULT hr;
ULONG i;
if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major)) ||
FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype)))
return MF_E_ATTRIBUTENOTFOUND;
if (!IsEqualGUID(&major, &MFMediaType_Audio))
return MF_E_INVALIDMEDIATYPE;
for (i = 0; i < ARRAY_SIZE(audio_formats); ++i)
if (IsEqualGUID(&subtype, audio_formats[i]))
break;
if (i == ARRAY_SIZE(audio_formats))
return MF_E_INVALIDMEDIATYPE;
if (FAILED(IMFMediaType_GetItemType(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &item_type)) ||
item_type != MF_ATTRIBUTE_UINT32)
return MF_E_INVALIDMEDIATYPE;
if (FAILED(IMFMediaType_GetItemType(type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &item_type)) ||
item_type != MF_ATTRIBUTE_UINT32)
return MF_E_INVALIDMEDIATYPE;
if (FAILED(IMFMediaType_GetItemType(type, &MF_MT_AUDIO_NUM_CHANNELS, &item_type)) ||
item_type != MF_ATTRIBUTE_UINT32)
return MF_E_INVALIDMEDIATYPE;
if (FAILED(IMFMediaType_GetItemType(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &item_type)) ||
item_type != MF_ATTRIBUTE_UINT32)
return MF_E_INVALIDMEDIATYPE;
return S_OK;
}
static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags)
{
struct resampler *impl = impl_from_IMFTransform(iface);
UINT32 block_alignment;
HRESULT hr;
TRACE("iface %p, id %#lx, type %p, flags %#lx.\n", iface, id, type, flags);
if (FAILED(hr = check_media_type(type)))
return hr;
if (FAILED(hr = IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &block_alignment)))
return MF_E_INVALIDMEDIATYPE;
if (flags & MFT_SET_TYPE_TEST_ONLY)
return S_OK;
if (!impl->input_type && FAILED(hr = MFCreateMediaType(&impl->input_type)))
return hr;
if (impl->output_type)
{
IMFMediaType_Release(impl->output_type);
impl->output_type = NULL;
}
if (SUCCEEDED(hr = IMFMediaType_CopyAllItems(type, (IMFAttributes *)impl->input_type)))
impl->input_info.cbSize = block_alignment;
else
{
IMFMediaType_Release(impl->input_type);
impl->input_info.cbSize = 0;
impl->input_type = NULL;
}
return hr;
}
static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags)
{
struct resampler *impl = impl_from_IMFTransform(iface);
UINT32 block_alignment;
HRESULT hr;
TRACE("iface %p, id %#lx, type %p, flags %#lx.\n", iface, id, type, flags);
if (!impl->input_type)
return MF_E_TRANSFORM_TYPE_NOT_SET;
if (FAILED(hr = check_media_type(type)))
return hr;
if (FAILED(hr = IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &block_alignment)))
return MF_E_INVALIDMEDIATYPE;
if (flags & MFT_SET_TYPE_TEST_ONLY)
return S_OK;
if (!impl->output_type && FAILED(hr = MFCreateMediaType(&impl->output_type)))
return hr;
if (FAILED(hr = IMFMediaType_CopyAllItems(type, (IMFAttributes *)impl->output_type)))
goto failed;
if (FAILED(hr = try_create_wg_transform(impl)))
goto failed;
impl->output_info.cbSize = block_alignment;
return hr;
failed:
IMFMediaType_Release(impl->output_type);
impl->output_info.cbSize = 0;
impl->output_type = NULL;
return hr;
}
static HRESULT WINAPI transform_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type)
{
struct resampler *impl = impl_from_IMFTransform(iface);
HRESULT hr;
TRACE("iface %p, id %#lx, type %p.\n", iface, id, type);
if (id != 0)
return MF_E_INVALIDSTREAMNUMBER;
if (!impl->input_type)
return MF_E_TRANSFORM_TYPE_NOT_SET;
if (FAILED(hr = MFCreateMediaType(type)))
return hr;
if (FAILED(hr = IMFMediaType_CopyAllItems(impl->input_type, (IMFAttributes *)*type)))
IMFMediaType_Release(*type);
return hr;
}
static HRESULT WINAPI transform_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type)
{
struct resampler *impl = impl_from_IMFTransform(iface);
HRESULT hr;
TRACE("iface %p, id %#lx, type %p.\n", iface, id, type);
if (id != 0)
return MF_E_INVALIDSTREAMNUMBER;
if (!impl->output_type)
return MF_E_TRANSFORM_TYPE_NOT_SET;
if (FAILED(hr = MFCreateMediaType(type)))
return hr;
if (FAILED(hr = IMFMediaType_CopyAllItems(impl->output_type, (IMFAttributes *)*type)))
IMFMediaType_Release(*type);
return hr;
}
static HRESULT WINAPI transform_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags)
{
FIXME("iface %p, id %#lx, flags %p stub!\n", iface, id, flags);
return E_NOTIMPL;
}
static HRESULT WINAPI transform_GetOutputStatus(IMFTransform *iface, DWORD *flags)
{
FIXME("iface %p, flags %p stub!\n", iface, flags);
return E_NOTIMPL;
}
static HRESULT WINAPI transform_SetOutputBounds(IMFTransform *iface, LONGLONG lower, LONGLONG upper)
{
TRACE("iface %p, lower %I64d, upper %I64d.\n", iface, lower, upper);
return E_NOTIMPL;
}
static HRESULT WINAPI transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFMediaEvent *event)
{
FIXME("iface %p, id %#lx, event %p stub!\n", iface, id, event);
return E_NOTIMPL;
}
static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param)
{
FIXME("iface %p, message %#x, param %p stub!\n", iface, message, (void *)param);
return S_OK;
}
static HRESULT WINAPI transform_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags)
{
struct resampler *impl = impl_from_IMFTransform(iface);
TRACE("iface %p, id %#lx, sample %p, flags %#lx.\n", iface, id, sample, flags);
if (!impl->wg_transform)
return MF_E_TRANSFORM_TYPE_NOT_SET;
return wg_transform_push_mf(impl->wg_transform, sample, impl->wg_sample_queue);
}
static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count,
MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status)
{
struct resampler *impl = impl_from_IMFTransform(iface);
HRESULT hr;
TRACE("iface %p, flags %#lx, count %lu, samples %p, status %p.\n", iface, flags, count, samples, status);
if (count != 1)
return E_INVALIDARG;
if (!impl->wg_transform)
return MF_E_TRANSFORM_TYPE_NOT_SET;
*status = samples->dwStatus = 0;
if (!samples->pSample)
{
samples->dwStatus = MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE;
return MF_E_TRANSFORM_NEED_MORE_INPUT;
}
if (SUCCEEDED(hr = wg_transform_read_mf(impl->wg_transform, samples->pSample, &samples->dwStatus, NULL)))
wg_sample_queue_flush(impl->wg_sample_queue, false);
return hr;
}
static const IMFTransformVtbl transform_vtbl =
{
transform_QueryInterface,
transform_AddRef,
transform_Release,
transform_GetStreamLimits,
transform_GetStreamCount,
transform_GetStreamIDs,
transform_GetInputStreamInfo,
transform_GetOutputStreamInfo,
transform_GetAttributes,
transform_GetInputStreamAttributes,
transform_GetOutputStreamAttributes,
transform_DeleteInputStream,
transform_AddInputStreams,
transform_GetInputAvailableType,
transform_GetOutputAvailableType,
transform_SetInputType,
transform_SetOutputType,
transform_GetInputCurrentType,
transform_GetOutputCurrentType,
transform_GetInputStatus,
transform_GetOutputStatus,
transform_SetOutputBounds,
transform_ProcessEvent,
transform_ProcessMessage,
transform_ProcessInput,
transform_ProcessOutput,
};
static inline struct resampler *impl_from_IMediaObject(IMediaObject *iface)
{
return CONTAINING_RECORD(iface, struct resampler, IMediaObject_iface);
}
static HRESULT WINAPI media_object_QueryInterface(IMediaObject *iface, REFIID iid, void **obj)
{
return IUnknown_QueryInterface(impl_from_IMediaObject(iface)->outer, iid, obj);
}
static ULONG WINAPI media_object_AddRef(IMediaObject *iface)
{
return IUnknown_AddRef(impl_from_IMediaObject(iface)->outer);
}
static ULONG WINAPI media_object_Release(IMediaObject *iface)
{
return IUnknown_Release(impl_from_IMediaObject(iface)->outer);
}
static HRESULT WINAPI media_object_GetStreamCount(IMediaObject *iface, DWORD *input, DWORD *output)
{
FIXME("iface %p, input %p, output %p semi-stub!\n", iface, input, output);
*input = *output = 1;
return S_OK;
}
static HRESULT WINAPI media_object_GetInputStreamInfo(IMediaObject *iface, DWORD index, DWORD *flags)
{
FIXME("iface %p, index %lu, flags %p stub!\n", iface, index, flags);
return E_NOTIMPL;
}
static HRESULT WINAPI media_object_GetOutputStreamInfo(IMediaObject *iface, DWORD index, DWORD *flags)
{
FIXME("iface %p, index %lu, flags %p stub!\n", iface, index, flags);
return E_NOTIMPL;
}
static HRESULT WINAPI media_object_GetInputType(IMediaObject *iface, DWORD index, DWORD type_index,
DMO_MEDIA_TYPE *type)
{
FIXME("iface %p, index %lu, type_index %lu, type %p stub!\n", iface, index, type_index, type);
return E_NOTIMPL;
}
static HRESULT WINAPI media_object_GetOutputType(IMediaObject *iface, DWORD index, DWORD type_index,
DMO_MEDIA_TYPE *type)
{
FIXME("iface %p, index %lu, type_index %lu, type %p stub!\n", iface, index, type_index, type);
return E_NOTIMPL;
}
static HRESULT WINAPI media_object_SetInputType(IMediaObject *iface, DWORD index,
const DMO_MEDIA_TYPE *type, DWORD flags)
{
FIXME("iface %p, index %lu, type %p, flags %#lx stub!\n", iface, index, type, flags);
return E_NOTIMPL;
}
static HRESULT WINAPI media_object_SetOutputType(IMediaObject *iface, DWORD index,
const DMO_MEDIA_TYPE *type, DWORD flags)
{
FIXME("iface %p, index %lu, type %p, flags %#lx stub!\n", iface, index, type, flags);
return E_NOTIMPL;
}
static HRESULT WINAPI media_object_GetInputCurrentType(IMediaObject *iface, DWORD index, DMO_MEDIA_TYPE *type)
{
FIXME("iface %p, index %lu, type %p stub!\n", iface, index, type);
return E_NOTIMPL;
}
static HRESULT WINAPI media_object_GetOutputCurrentType(IMediaObject *iface, DWORD index, DMO_MEDIA_TYPE *type)
{
FIXME("iface %p, index %lu, type %p stub!\n", iface, index, type);
return E_NOTIMPL;
}
static HRESULT WINAPI media_object_GetInputSizeInfo(IMediaObject *iface, DWORD index, DWORD *size,
DWORD *lookahead, DWORD *alignment)
{
FIXME("iface %p, index %lu, size %p, lookahead %p, alignment %p stub!\n", iface, index, size,
lookahead, alignment);
return E_NOTIMPL;
}
static HRESULT WINAPI media_object_GetOutputSizeInfo(IMediaObject *iface, DWORD index, DWORD *size, DWORD *alignment)
{
FIXME("iface %p, index %lu, size %p, alignment %p stub!\n", iface, index, size, alignment);
return E_NOTIMPL;
}
static HRESULT WINAPI media_object_GetInputMaxLatency(IMediaObject *iface, DWORD index, REFERENCE_TIME *latency)
{
FIXME("iface %p, index %lu, latency %p stub!\n", iface, index, latency);
return E_NOTIMPL;
}
static HRESULT WINAPI media_object_SetInputMaxLatency(IMediaObject *iface, DWORD index, REFERENCE_TIME latency)
{
FIXME("iface %p, index %lu, latency %s stub!\n", iface, index, wine_dbgstr_longlong(latency));
return E_NOTIMPL;
}
static HRESULT WINAPI media_object_Flush(IMediaObject *iface)
{
FIXME("iface %p stub!\n", iface);
return E_NOTIMPL;
}
static HRESULT WINAPI media_object_Discontinuity(IMediaObject *iface, DWORD index)
{
FIXME("iface %p, index %lu stub!\n", iface, index);
return E_NOTIMPL;
}
static HRESULT WINAPI media_object_AllocateStreamingResources(IMediaObject *iface)
{
FIXME("iface %p stub!\n", iface);
return E_NOTIMPL;
}
static HRESULT WINAPI media_object_FreeStreamingResources(IMediaObject *iface)
{
FIXME("iface %p stub!\n", iface);
return E_NOTIMPL;
}
static HRESULT WINAPI media_object_GetInputStatus(IMediaObject *iface, DWORD index, DWORD *flags)
{
FIXME("iface %p, index %lu, flags %p stub!\n", iface, index, flags);
return E_NOTIMPL;
}
static HRESULT WINAPI media_object_ProcessInput(IMediaObject *iface, DWORD index,
IMediaBuffer *buffer, DWORD flags, REFERENCE_TIME timestamp, REFERENCE_TIME timelength)
{
FIXME("iface %p, index %lu, buffer %p, flags %#lx, timestamp %s, timelength %s stub!\n", iface,
index, buffer, flags, wine_dbgstr_longlong(timestamp), wine_dbgstr_longlong(timelength));
return E_NOTIMPL;
}
static HRESULT WINAPI media_object_ProcessOutput(IMediaObject *iface, DWORD flags, DWORD count,
DMO_OUTPUT_DATA_BUFFER *buffers, DWORD *status)
{
FIXME("iface %p, flags %#lx, count %lu, buffers %p, status %p stub!\n", iface, flags, count, buffers, status);
return E_NOTIMPL;
}
static HRESULT WINAPI media_object_Lock(IMediaObject *iface, LONG lock)
{
FIXME("iface %p, lock %ld stub!\n", iface, lock);
return E_NOTIMPL;
}
static const IMediaObjectVtbl media_object_vtbl =
{
media_object_QueryInterface,
media_object_AddRef,
media_object_Release,
media_object_GetStreamCount,
media_object_GetInputStreamInfo,
media_object_GetOutputStreamInfo,
media_object_GetInputType,
media_object_GetOutputType,
media_object_SetInputType,
media_object_SetOutputType,
media_object_GetInputCurrentType,
media_object_GetOutputCurrentType,
media_object_GetInputSizeInfo,
media_object_GetOutputSizeInfo,
media_object_GetInputMaxLatency,
media_object_SetInputMaxLatency,
media_object_Flush,
media_object_Discontinuity,
media_object_AllocateStreamingResources,
media_object_FreeStreamingResources,
media_object_GetInputStatus,
media_object_ProcessInput,
media_object_ProcessOutput,
media_object_Lock,
};
static inline struct resampler *impl_from_IPropertyBag(IPropertyBag *iface)
{
return CONTAINING_RECORD(iface, struct resampler, IPropertyBag_iface);
}
static HRESULT WINAPI property_bag_QueryInterface(IPropertyBag *iface, REFIID iid, void **out)
{
return IUnknown_QueryInterface(impl_from_IPropertyBag(iface)->outer, iid, out);
}
static ULONG WINAPI property_bag_AddRef(IPropertyBag *iface)
{
return IUnknown_AddRef(impl_from_IPropertyBag(iface)->outer);
}
static ULONG WINAPI property_bag_Release(IPropertyBag *iface)
{
return IUnknown_Release(impl_from_IPropertyBag(iface)->outer);
}
static HRESULT WINAPI property_bag_Read(IPropertyBag *iface, const WCHAR *prop_name, VARIANT *value,
IErrorLog *error_log)
{
FIXME("iface %p, prop_name %s, value %p, error_log %p stub!\n", iface, debugstr_w(prop_name), value, error_log);
return E_NOTIMPL;
}
static HRESULT WINAPI property_bag_Write(IPropertyBag *iface, const WCHAR *prop_name, VARIANT *value)
{
FIXME("iface %p, prop_name %s, value %p stub!\n", iface, debugstr_w(prop_name), value);
return S_OK;
}
static const IPropertyBagVtbl property_bag_vtbl =
{
property_bag_QueryInterface,
property_bag_AddRef,
property_bag_Release,
property_bag_Read,
property_bag_Write,
};
static inline struct resampler *impl_from_IPropertyStore(IPropertyStore *iface)
{
return CONTAINING_RECORD(iface, struct resampler, IPropertyStore_iface);
}
static HRESULT WINAPI property_store_QueryInterface(IPropertyStore *iface, REFIID iid, void **out)
{
return IUnknown_QueryInterface(impl_from_IPropertyStore(iface)->outer, iid, out);
}
static ULONG WINAPI property_store_AddRef(IPropertyStore *iface)
{
return IUnknown_AddRef(impl_from_IPropertyStore(iface)->outer);
}
static ULONG WINAPI property_store_Release(IPropertyStore *iface)
{
return IUnknown_Release(impl_from_IPropertyStore(iface)->outer);
}
static HRESULT WINAPI property_store_GetCount(IPropertyStore *iface, DWORD *count)
{
FIXME("iface %p, count %p stub!\n", iface, count);
return E_NOTIMPL;
}
static HRESULT WINAPI property_store_GetAt(IPropertyStore *iface, DWORD index, PROPERTYKEY *key)
{
FIXME("iface %p, index %lu, key %p stub!\n", iface, index, key);
return E_NOTIMPL;
}
static HRESULT WINAPI property_store_GetValue(IPropertyStore *iface, REFPROPERTYKEY key, PROPVARIANT *value)
{
FIXME("iface %p, key %p, value %p stub!\n", iface, key, value);
return E_NOTIMPL;
}
static HRESULT WINAPI property_store_SetValue(IPropertyStore *iface, REFPROPERTYKEY key, REFPROPVARIANT value)
{
FIXME("iface %p, key %p, value %p stub!\n", iface, key, value);
return E_NOTIMPL;
}
static HRESULT WINAPI property_store_Commit(IPropertyStore *iface)
{
FIXME("iface %p stub!\n", iface);
return E_NOTIMPL;
}
static const IPropertyStoreVtbl property_store_vtbl =
{
property_store_QueryInterface,
property_store_AddRef,
property_store_Release,
property_store_GetCount,
property_store_GetAt,
property_store_GetValue,
property_store_SetValue,
property_store_Commit,
};
static inline struct resampler *impl_from_IWMResamplerProps(IWMResamplerProps *iface)
{
return CONTAINING_RECORD(iface, struct resampler, IWMResamplerProps_iface);
}
static HRESULT WINAPI resampler_props_QueryInterface(IWMResamplerProps *iface, REFIID iid, void **out)
{
return IUnknown_QueryInterface(impl_from_IWMResamplerProps(iface)->outer, iid, out);
}
static ULONG WINAPI resampler_props_AddRef(IWMResamplerProps *iface)
{
return IUnknown_AddRef(impl_from_IWMResamplerProps(iface)->outer);
}
static ULONG WINAPI resampler_props_Release(IWMResamplerProps *iface)
{
return IUnknown_Release(impl_from_IWMResamplerProps(iface)->outer);
}
static HRESULT WINAPI resampler_props_SetHalfFilterLength(IWMResamplerProps *iface, LONG length)
{
FIXME("iface %p, count %lu stub!\n", iface, length);
return E_NOTIMPL;
}
static HRESULT WINAPI resampler_props_SetUserChannelMtx(IWMResamplerProps *iface, ChMtxType *conversion_matrix)
{
FIXME("iface %p, userChannelMtx %p stub!\n", iface, conversion_matrix);
return E_NOTIMPL;
}
static const IWMResamplerPropsVtbl resampler_props_vtbl =
{
resampler_props_QueryInterface,
resampler_props_AddRef,
resampler_props_Release,
resampler_props_SetHalfFilterLength,
resampler_props_SetUserChannelMtx,
};
HRESULT resampler_create(IUnknown *outer, IUnknown **out)
{
static const WAVEFORMATEX output_format =
{
.wFormatTag = WAVE_FORMAT_IEEE_FLOAT, .wBitsPerSample = 32, .nSamplesPerSec = 44100, .nChannels = 1,
};
static const WAVEFORMATEX input_format =
{
.wFormatTag = WAVE_FORMAT_PCM, .wBitsPerSample = 16, .nSamplesPerSec = 44100, .nChannels = 1,
};
struct resampler *impl;
HRESULT hr;
TRACE("outer %p, out %p.\n", outer, out);
if (FAILED(hr = check_audio_transform_support(&input_format, &output_format)))
{
ERR_(winediag)("GStreamer doesn't support audio resampling, please install appropriate plugins.\n");
return hr;
}
if (!(impl = calloc(1, sizeof(*impl))))
return E_OUTOFMEMORY;
if (FAILED(hr = wg_sample_queue_create(&impl->wg_sample_queue)))
{
free(impl);
return hr;
}
impl->IUnknown_inner.lpVtbl = &unknown_vtbl;
impl->IMFTransform_iface.lpVtbl = &transform_vtbl;
impl->IMediaObject_iface.lpVtbl = &media_object_vtbl;
impl->IPropertyBag_iface.lpVtbl = &property_bag_vtbl;
impl->IPropertyStore_iface.lpVtbl = &property_store_vtbl;
impl->IWMResamplerProps_iface.lpVtbl = &resampler_props_vtbl;
impl->refcount = 1;
impl->outer = outer ? outer : &impl->IUnknown_inner;
impl->input_info.cbAlignment = 1;
impl->output_info.cbAlignment = 1;
*out = &impl->IUnknown_inner;
TRACE("Created %p\n", *out);
return S_OK;
}