From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/winegstreamer/Makefile.in | 3 +- dlls/winegstreamer/resampler.c | 568 +++++++++++++++++++++++++++++++-- 2 files changed, 549 insertions(+), 22 deletions(-) diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 1c7a7b417c1..ec762f9bdc5 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -1,7 +1,8 @@ MODULE = winegstreamer.dll UNIXLIB = winegstreamer.so IMPORTLIB = winegstreamer -IMPORTS = strmbase ole32 oleaut32 msdmo msvcrt user32 +IMPORTS = $(FFMPEG_PE_LIBS) strmbase ole32 oleaut32 msdmo user32 +EXTRAINCL = $(FFMPEG_PE_CFLAGS) DELAYIMPORTS = mfplat mf UNIX_CFLAGS = $(GSTREAMER_CFLAGS) UNIX_LIBS = $(GSTREAMER_LIBS) $(PTHREAD_LIBS) diff --git a/dlls/winegstreamer/resampler.c b/dlls/winegstreamer/resampler.c index 5ac5eb823ce..a10b46d7d26 100644 --- a/dlls/winegstreamer/resampler.c +++ b/dlls/winegstreamer/resampler.c @@ -15,19 +15,156 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include <stdarg.h> +#include <stddef.h> + +#include "windef.h" +#include "winbase.h" + #include "gst_private.h" +#include "dmoreg.h" +#include "dmort.h" +#include "dshow.h" +#include "mediaerr.h" #include "mfapi.h" #include "mferror.h" #include "mfobjects.h" #include "mftransform.h" #include "wmcodecdsp.h" +#include <libavutil/avutil.h> +#include <libavutil/opt.h> +#include <libswresample/swresample.h> + #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(mfplat); WINE_DECLARE_DEBUG_CHANNEL(winediag); +static const AVRational USER_TIME_BASE_Q = {1, 10000000}; + +static void clear_dmo_media_type(DMO_MEDIA_TYPE *mt) +{ + MoFreeMediaType(mt); + memset(mt, 0, sizeof(*mt)); +} + +static enum AVSampleFormat sample_format_from_wave_format_tag(UINT tag, UINT depth) +{ + switch (tag) + { + case WAVE_FORMAT_PCM: + if (depth == 32) return AV_SAMPLE_FMT_S32; + if (depth == 16) return AV_SAMPLE_FMT_S16; + if (depth == 8) return AV_SAMPLE_FMT_U8; + break; + case WAVE_FORMAT_IEEE_FLOAT: + if (depth == 64) return AV_SAMPLE_FMT_DBL; + if (depth == 32) return AV_SAMPLE_FMT_FLT; + break; + } + + FIXME("Format tag %#x depth %#x implemented\n", tag, depth); + return AV_SAMPLE_FMT_NONE; +} + +static void audio_frame_init_from_format(AVFrame *frame, const WAVEFORMATEXTENSIBLE *format) +{ + if (format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) + { + WAVEFORMATEXTENSIBLE *extensible = CONTAINING_RECORD(format, WAVEFORMATEXTENSIBLE, Format); + frame->format = sample_format_from_wave_format_tag(extensible->SubFormat.Data1, extensible->Format.wBitsPerSample); + av_channel_layout_from_mask(&frame->ch_layout, extensible->dwChannelMask); + } + else + { + frame->format = sample_format_from_wave_format_tag(format->Format.wFormatTag, format->Format.wBitsPerSample); + av_channel_layout_default(&frame->ch_layout, format->Format.nChannels); + } + frame->sample_rate = format->Format.nSamplesPerSec; +} + +static const char *debugstr_avtime(INT64 time, AVRational time_base) +{ + time = av_rescale_q_rnd(time, time_base, USER_TIME_BASE_Q, AV_ROUND_PASS_MINMAX); + if (time == AV_NOPTS_VALUE) + return "(none)"; + return wine_dbg_sprintf("%I64d", time); +} + +static void media_buffer_release(void *opaque, uint8_t *data) +{ + IMediaBuffer *media_buffer = opaque; + IMediaBuffer_Release(media_buffer); +} + +static AVBufferRef *buffer_from_media_buffer(IMediaBuffer *media_buffer, int flags) +{ + AVBufferRef *buffer; + HRESULT hr; + DWORD size; + BYTE *data; + + if (FAILED(hr = IMediaBuffer_GetBufferAndLength(media_buffer, &data, &size))) + return NULL; + if (!flags && FAILED(hr = IMediaBuffer_GetMaxLength(media_buffer, &size))) + return NULL; + + if (!(buffer = av_buffer_create(data, size, media_buffer_release, media_buffer, flags))) + return NULL; + IMediaBuffer_AddRef(media_buffer); + return buffer; +} + +static int audio_frame_wrap_buffer(AVFrame *frame, const WAVEFORMATEXTENSIBLE *format, AVBufferRef *buffer) +{ + int size; + + TRACE("frame %p, type %p, buffer %p\n", frame, format, buffer); + + audio_frame_init_from_format(frame, format); + frame->nb_samples = buffer->size / format->Format.nBlockAlign; + + size = av_samples_fill_arrays(frame->data, frame->linesize, buffer->data, + frame->ch_layout.nb_channels, frame->nb_samples, frame->format, 1); + if (size > buffer->size) + return -1; + + frame->opaque = (void *)-1; /* avoid reusing frame */ + frame->buf[0] = av_buffer_ref(buffer); + frame->extended_data = frame->data; + return size; +} + +static int audio_frame_copy_from_buffer(AVFrame *frame, const WAVEFORMATEXTENSIBLE *format, AVBufferRef *buffer) +{ + UINT8 *input_planes[4]; + int size; + + ERR("frame %p, buffer %p\n", frame, buffer); + + size = av_samples_fill_arrays(input_planes, NULL, buffer->data, frame->ch_layout.nb_channels, + frame->nb_samples, frame->format, 1); + av_samples_copy(frame->data, input_planes, 0, 0, buffer->size / format->Format.nBlockAlign, + frame->ch_layout.nb_channels, frame->format); + return size; +} + +static int audio_frame_copy_to_buffer(AVFrame *frame, const WAVEFORMATEXTENSIBLE *format, AVBufferRef *buffer) +{ + UINT8 *output_planes[4]; + int size; + + ERR("frame %p, buffer %p\n", frame, buffer); + + size = av_samples_fill_arrays(output_planes, NULL, buffer->data, frame->ch_layout.nb_channels, + frame->nb_samples, frame->format, 1); + av_samples_copy(output_planes, frame->data, 0, 0, frame->nb_samples, + frame->ch_layout.nb_channels, frame->format); + return size; +} + static const GUID *audio_formats[] = { &MFAudioFormat_Float, @@ -46,14 +183,233 @@ struct resampler LONG refcount; IMFMediaType *input_type; + DMO_MEDIA_TYPE input_mt; + WAVEFORMATEXTENSIBLE input_format; MFT_INPUT_STREAM_INFO input_info; IMFMediaType *output_type; + DMO_MEDIA_TYPE output_mt; + WAVEFORMATEXTENSIBLE output_format; MFT_OUTPUT_STREAM_INFO output_info; wg_transform_t wg_transform; struct wg_sample_queue *wg_sample_queue; + + struct SwrContext *context; + AVFrame input_frame; + AVFrame output_frame; + AVFrame current_frame; + AVRational time_base; }; +static INT64 resampler_time_to_user(struct resampler *impl, INT64 time) +{ + return av_rescale_q_rnd(time, impl->time_base, USER_TIME_BASE_Q, AV_ROUND_PASS_MINMAX); +} + +static INT64 resampler_time_from_user(struct resampler *impl, INT64 time) +{ + return av_rescale_q_rnd(time, USER_TIME_BASE_Q, impl->time_base, AV_ROUND_PASS_MINMAX); +} + +static HRESULT resampler_process_frame(struct resampler *impl, AVFrame *input_frame, DMO_OUTPUT_DATA_BUFFER *output) +{ + INT64 pts, duration = INT64_MAX; + AVFrame output_frame = {0}; + AVBufferRef *buffer; + IMFSample *sample; + HRESULT hr; + int ret; + + if (!(buffer = buffer_from_media_buffer(output->pBuffer, 0))) + return E_OUTOFMEMORY; + if ((ret = audio_frame_wrap_buffer(&output_frame, &impl->output_format, buffer)) < 0) + av_frame_move_ref(&output_frame, &impl->output_frame); + + pts = swr_next_pts(impl->context, input_frame->pts); + if ((ret = swr_convert(impl->context, output_frame.data, buffer->size / impl->output_format.Format.nBlockAlign, + (const uint8_t **)input_frame->data, input_frame->nb_samples)) < 0) + ERR("error ret %d (%s)\n", -ret, av_err2str(ret)); + else if ((output_frame.nb_samples = ret)) + { + if (!output_frame.opaque) + ret = audio_frame_copy_to_buffer(&output_frame, &impl->output_format, buffer); + else + ret = output_frame.nb_samples * impl->output_format.Format.nBlockAlign; + duration = av_rescale(output_frame.nb_samples, 10000000, output_frame.sample_rate); + } + + if (!output_frame.opaque) + av_frame_move_ref(&impl->output_frame, &output_frame); + else + av_frame_unref(&output_frame); + av_buffer_unref(&buffer); + + output->dwStatus = 0; + IMediaBuffer_SetLength(output->pBuffer, ret >= 0 ? ret : 0); + if (ret < 0) + return E_FAIL; + if (!ret) + return S_FALSE; + + output->dwStatus |= DMO_OUTPUT_DATA_BUFFERF_SYNCPOINT; + if (swr_get_out_samples(impl->context, 0)) + output->dwStatus |= DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE; + output->rtTimestamp = resampler_time_to_user(impl, pts); + if (output->rtTimestamp != INT64_MIN) + output->dwStatus |= DMO_OUTPUT_DATA_BUFFERF_TIME; + output->rtTimelength = duration; + if (output->rtTimelength != INT64_MIN) + output->dwStatus |= DMO_OUTPUT_DATA_BUFFERF_TIMELENGTH; + + if (SUCCEEDED(hr = IMediaBuffer_QueryInterface(output->pBuffer, &IID_IMFSample, (void **)&sample))) + { + if (output->rtTimestamp != INT64_MIN) + IMFSample_SetSampleTime(sample, output->rtTimestamp); + if (output->rtTimelength != INT64_MIN) + IMFSample_SetSampleDuration(sample, output->rtTimelength); + if (output->dwStatus & DMO_OUTPUT_DATA_BUFFERF_SYNCPOINT) + IMFSample_SetUINT32(sample, &MFSampleExtension_CleanPoint, 1); + IMFSample_Release(sample); + } + + TRACE("returning output size %#x, pts %s, duration %s status %#lx\n", ret, + debugstr_avtime(output->rtTimestamp, USER_TIME_BASE_Q), + debugstr_avtime(output->rtTimelength, USER_TIME_BASE_Q), output->dwStatus); + return S_OK; +} + +static HRESULT resampler_process_input(struct resampler *impl, const DMO_OUTPUT_DATA_BUFFER *input) +{ + LONGLONG pts = input->rtTimestamp, duration = input->rtTimelength; + AVBufferRef *buffer; + IMFSample *sample; + HRESULT hr; + int ret; + + if (!impl->context) + return DMO_E_TYPE_NOT_SET; + if (impl->current_frame.nb_samples) + return DMO_E_NOTACCEPTING; + + if (!(buffer = buffer_from_media_buffer(input->pBuffer, AV_BUFFER_FLAG_READONLY))) + return E_OUTOFMEMORY; + if ((ret = audio_frame_wrap_buffer(&impl->current_frame, &impl->input_format, buffer)) < 0) + { + ret = audio_frame_copy_from_buffer(&impl->input_frame, &impl->input_format, buffer); + av_frame_move_ref(&impl->current_frame, &impl->input_frame); + } + av_buffer_unref(&buffer); + + if (SUCCEEDED(hr = IMediaBuffer_QueryInterface(input->pBuffer, &IID_IMFSample, (void **)&sample))) + { + if (FAILED(IMFSample_GetSampleTime(sample, &pts))) + pts = INT64_MIN; + if (FAILED(IMFSample_GetSampleDuration(sample, &duration))) + duration = INT64_MIN; + IMFSample_Release(sample); + } + + impl->current_frame.pts = resampler_time_from_user(impl, pts); + impl->current_frame.duration = resampler_time_from_user(impl, duration); + TRACE("input samples %u, time %s, duration %s\n", impl->current_frame.nb_samples, + debugstr_avtime(impl->current_frame.pts, impl->time_base), + debugstr_avtime(impl->current_frame.duration, impl->time_base)); + return S_OK; +} + +static HRESULT resampler_process_output(struct resampler *impl, DMO_OUTPUT_DATA_BUFFER *output) +{ + HRESULT hr; + + if (!impl->context) + return DMO_E_TYPE_NOT_SET; + + hr = resampler_process_frame(impl, &impl->current_frame, output); + if (!impl->current_frame.opaque) + av_frame_move_ref(&impl->input_frame, &impl->current_frame); + else + av_frame_unref(&impl->current_frame); + + return hr; +} + +static void resampler_cleanup(struct resampler *impl) +{ + impl->time_base.num = impl->time_base.den = 1; + memset(&impl->input_format, 0, sizeof(impl->input_format)); + memset(&impl->output_format, 0, sizeof(impl->output_format)); + av_frame_unref(&impl->current_frame); + av_frame_unref(&impl->output_frame); + av_frame_unref(&impl->input_frame); + swr_free(&impl->context); +} + +static HRESULT init_audio_format(const DMO_MEDIA_TYPE *type, WAVEFORMATEXTENSIBLE *format) +{ + if (IsEqualGUID(&type->formattype, &FORMAT_WaveFormatEx)) + { + const WAVEFORMATEX *wfx = (WAVEFORMATEX *)type->pbFormat; + if (wfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) + *format = *(WAVEFORMATEXTENSIBLE *)wfx; + else + { + memset(format, 0, sizeof(*format)); + format->Format = *wfx; + format->dwChannelMask = 0; + format->SubFormat = type->subtype; + } + return S_OK; + } + + FIXME("Format %s not implemented\n", debugstr_guid(&type->formattype)); + return E_NOTIMPL; +} + +static HRESULT resampler_init(struct resampler *impl) +{ + HRESULT hr; + int ret; + + resampler_cleanup(impl); + + if (!(impl->context = swr_alloc())) + return E_OUTOFMEMORY; + if (FAILED(hr = init_audio_format(&impl->input_mt, &impl->input_format)) + || FAILED(hr = init_audio_format(&impl->output_mt, &impl->output_format))) + { + resampler_cleanup(impl); + return hr; + } + + audio_frame_init_from_format(&impl->input_frame, &impl->input_format); + impl->input_frame.nb_samples = impl->input_format.Format.nSamplesPerSec; + if ((ret = av_frame_get_buffer(&impl->input_frame, 0)) < 0) + goto failed; + audio_frame_init_from_format(&impl->output_frame, &impl->output_format); + impl->output_frame.nb_samples = impl->output_format.Format.nSamplesPerSec; + if ((ret = av_frame_get_buffer(&impl->output_frame, 0)) < 0) + goto failed; + + av_opt_set_chlayout(impl->context, "in_chlayout", &impl->input_frame.ch_layout, 0); + av_opt_set_int(impl->context, "in_sample_rate", impl->input_frame.sample_rate, 0); + av_opt_set_sample_fmt(impl->context, "in_sample_fmt", impl->input_frame.format, 0); + impl->time_base.den *= impl->input_frame.sample_rate; + + av_opt_set_chlayout(impl->context, "out_chlayout", &impl->output_frame.ch_layout, 0); + av_opt_set_int(impl->context, "out_sample_rate", impl->output_frame.sample_rate, 0); + av_opt_set_sample_fmt(impl->context, "out_sample_fmt", impl->output_frame.format, 0); + impl->time_base.den *= impl->output_frame.sample_rate; + + if ((ret = swr_init(impl->context)) < 0) + goto failed; + return S_OK; + +failed: + ERR("ret %d\n", ret); + resampler_cleanup(impl); + return E_FAIL; +} + static HRESULT try_create_wg_transform(struct resampler *impl) { struct wg_transform_attrs attrs = {0}; @@ -128,6 +484,10 @@ static ULONG WINAPI unknown_Release(IUnknown *iface) IMFMediaType_Release(impl->output_type); wg_sample_queue_destroy(impl->wg_sample_queue); + + MoFreeMediaType(&impl->input_mt); + MoFreeMediaType(&impl->output_mt); + resampler_cleanup(impl); free(impl); } @@ -611,7 +971,7 @@ static ULONG WINAPI media_object_Release(IMediaObject *iface) 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); + TRACE("iface %p, input %p, output %p\n", iface, input, output); *input = *output = 1; return S_OK; } @@ -628,44 +988,187 @@ static HRESULT WINAPI media_object_GetOutputStreamInfo(IMediaObject *iface, DWOR return E_NOTIMPL; } +static HRESULT get_available_dmo_media_type(DWORD index, DMO_MEDIA_TYPE *type, BOOL output) +{ + const GUID *subtype; + WAVEFORMATEX *wfx; + + if (!(wfx = CoTaskMemAlloc(sizeof(*wfx)))) + return E_OUTOFMEMORY; + subtype = audio_formats[index % ARRAY_SIZE(audio_formats)]; + + memset(wfx, 0, sizeof(*wfx)); + wfx->wFormatTag = subtype->Data1; + + memset(type, 0, sizeof(*type)); + type->majortype = MFMediaType_Audio; + type->formattype = FORMAT_WaveFormatEx; + type->subtype = *subtype; + type->pbFormat = (BYTE *)wfx; + type->cbFormat = sizeof(*wfx); + if (index < ARRAY_SIZE(audio_formats)) + return S_OK; + + wfx->wBitsPerSample = IsEqualGUID(subtype, &MFAudioFormat_Float) ? 32 : 16; + wfx->nChannels = 2; + wfx->nSamplesPerSec = 48000; + wfx->nBlockAlign = wfx->wBitsPerSample * wfx->nChannels / 8; + wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign; + return S_OK; +} + 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; + TRACE("iface %p, index %lu, type_index %lu, type %p\n", impl_from_IMediaObject(iface), index, + type_index, type); + + if (index > 0) + return DMO_E_INVALIDSTREAMINDEX; + if (type_index >= ARRAY_SIZE(audio_formats)) + return DMO_E_NO_MORE_ITEMS; + return type ? get_available_dmo_media_type(type_index, type, FALSE) : S_OK; } 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; + TRACE("iface %p, index %lu, type_index %lu, type %p\n", impl_from_IMediaObject(iface), index, + type_index, type); + + if (index > 0) + return DMO_E_INVALIDSTREAMINDEX; + if (type_index >= 2 * ARRAY_SIZE(audio_formats)) + return DMO_E_NO_MORE_ITEMS; + return type ? get_available_dmo_media_type(type_index, type, TRUE) : S_OK; +} + +static HRESULT check_dmo_media_type(const DMO_MEDIA_TYPE *type, UINT *block_alignment) +{ + WAVEFORMATEX *wfx; + ULONG i; + + if (!IsEqualGUID(&type->majortype, &MEDIATYPE_Audio)) + return DMO_E_INVALIDTYPE; + if (!IsEqualGUID(&type->formattype, &FORMAT_WaveFormatEx) || type->cbFormat < sizeof(*wfx)) + return DMO_E_INVALIDTYPE; + + for (i = 0; i < ARRAY_SIZE(audio_formats); ++i) + if (IsEqualGUID(&type->subtype, audio_formats[i])) + break; + if (i == ARRAY_SIZE(audio_formats)) + return DMO_E_INVALIDTYPE; + + wfx = (WAVEFORMATEX *)type->pbFormat; + if (!wfx->wBitsPerSample || !wfx->nAvgBytesPerSec || !wfx->nChannels || !wfx->nSamplesPerSec || !wfx->nBlockAlign) + return DMO_E_INVALIDTYPE; + + *block_alignment = wfx->nBlockAlign; + return S_OK; } 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; + struct resampler *impl = impl_from_IMediaObject(iface); + UINT32 block_alignment; + HRESULT hr; + + TRACE("iface %p, index %#lx, type %p, flags %#lx.\n", iface, index, type, flags); + + if (index != 0) + return DMO_E_INVALIDSTREAMINDEX; + if (!type) + { + clear_dmo_media_type(&impl->input_mt); + resampler_cleanup(impl); + return S_OK; + } + + if (FAILED(hr = check_dmo_media_type(type, &block_alignment))) + return hr; + if (flags & DMO_SET_TYPEF_TEST_ONLY) + return S_OK; + + MoFreeMediaType(&impl->input_mt); + if (FAILED(hr = MoCopyMediaType(&impl->input_mt, type))) + return hr; + impl->input_info.cbSize = block_alignment; + + if (!IsEqualGUID(&impl->output_mt.majortype, &GUID_NULL) + && FAILED(hr = resampler_init(impl))) + { + clear_dmo_media_type(&impl->input_mt); + impl->input_info.cbSize = 0; + } + + return hr; } 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; + struct resampler *impl = impl_from_IMediaObject(iface); + UINT32 block_alignment; + HRESULT hr; + + TRACE("iface %p, index %#lx, type %p, flags %#lx.\n", iface, index, type, flags); + + if (index != 0) + return DMO_E_INVALIDSTREAMINDEX; + if (!type) + { + clear_dmo_media_type(&impl->output_mt); + resampler_cleanup(impl); + return S_OK; + } + + if (IsEqualGUID(&impl->input_mt.majortype, &GUID_NULL)) + return DMO_E_TYPE_NOT_SET; + if (FAILED(hr = check_dmo_media_type(type, &block_alignment))) + return hr; + if (flags & DMO_SET_TYPEF_TEST_ONLY) + return S_OK; + + MoFreeMediaType(&impl->output_mt); + if (FAILED(hr = MoCopyMediaType(&impl->output_mt, type))) + return hr; + impl->output_info.cbSize = block_alignment; + + if (!IsEqualGUID(&impl->input_mt.majortype, &GUID_NULL) + && FAILED(hr = resampler_init(impl))) + { + clear_dmo_media_type(&impl->output_mt); + impl->output_info.cbSize = 0; + } + + return hr; } 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; + struct resampler *impl = impl_from_IMediaObject(iface); + + TRACE("iface %p, index %#lx, type %p.\n", iface, index, type); + + if (index != 0) + return DMO_E_INVALIDSTREAMINDEX; + if (IsEqualGUID(&impl->input_mt.majortype, &GUID_NULL)) + return DMO_E_TYPE_NOT_SET; + return MoCopyMediaType(type, &impl->input_mt); } 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; + struct resampler *impl = impl_from_IMediaObject(iface); + + TRACE("iface %p, index %#lx, type %p.\n", iface, index, type); + + if (index != 0) + return DMO_E_INVALIDSTREAMINDEX; + if (IsEqualGUID(&impl->output_mt.majortype, &GUID_NULL)) + return DMO_E_TYPE_NOT_SET; + return MoCopyMediaType(type, &impl->output_mt); } static HRESULT WINAPI media_object_GetInputSizeInfo(IMediaObject *iface, DWORD index, DWORD *size, @@ -690,7 +1193,7 @@ static HRESULT WINAPI media_object_GetInputMaxLatency(IMediaObject *iface, DWORD 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)); + FIXME("iface %p, index %lu, latency %I64d stub!\n", iface, index, latency); return E_NOTIMPL; } @@ -727,16 +1230,30 @@ static HRESULT WINAPI media_object_GetInputStatus(IMediaObject *iface, DWORD ind 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; + DMO_OUTPUT_DATA_BUFFER input = {.pBuffer = buffer, .dwStatus = flags, .rtTimestamp = timestamp, + .rtTimelength = timelength}; + struct resampler *impl = impl_from_IMediaObject(iface); + + TRACE("iface %p, index %lu, buffer %p, flags %#lx, timestamp %I64d, timelength %I64d\n", iface, + index, buffer, flags, timestamp, timelength); + + return resampler_process_input(impl, &input); } static HRESULT WINAPI media_object_ProcessOutput(IMediaObject *iface, DWORD flags, DWORD count, - DMO_OUTPUT_DATA_BUFFER *buffers, DWORD *status) + DMO_OUTPUT_DATA_BUFFER *output, DWORD *output_status) { - FIXME("iface %p, flags %#lx, count %lu, buffers %p, status %p stub!\n", iface, flags, count, buffers, status); - return E_NOTIMPL; + struct resampler *impl = impl_from_IMediaObject(iface); + + TRACE("iface %p, flags %#lx, count %lu, output %p, output_status %p\n", iface, flags, count, + output, output_status); + + if (count != 1) + return E_INVALIDARG; + if (flags) + FIXME("Unimplemented flags %#lx\n", flags); + + return resampler_process_output(impl, output); } static HRESULT WINAPI media_object_Lock(IMediaObject *iface, LONG lock) @@ -918,6 +1435,12 @@ static const IWMResamplerPropsVtbl resampler_props_vtbl = resampler_props_SetUserChannelMtx, }; +static const char *debugstr_version(UINT version) +{ + return wine_dbg_sprintf("%u.%u.%u", AV_VERSION_MAJOR(version), AV_VERSION_MINOR(version), + AV_VERSION_MICRO(version)); +} + HRESULT resampler_create(IUnknown *outer, IUnknown **out) { static const WAVEFORMATEX output_format = @@ -933,6 +1456,9 @@ HRESULT resampler_create(IUnknown *outer, IUnknown **out) TRACE("outer %p, out %p.\n", outer, out); + TRACE("avutil version %s\n", debugstr_version(avutil_version())); + TRACE("swresample version %s\n", debugstr_version(swresample_version())); + if (FAILED(hr = check_audio_transform_support(&input_format, &output_format))) { ERR_(winediag)("GStreamer doesn't support audio resampling, please install appropriate plugins.\n"); @@ -961,6 +1487,6 @@ HRESULT resampler_create(IUnknown *outer, IUnknown **out) impl->output_info.cbAlignment = 1; *out = &impl->IUnknown_inner; - TRACE("Created %p\n", *out); + TRACE("Created resampler %p\n", *out); return S_OK; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11111