From: Torge Matthies tmatthies@codeweavers.com
--- dlls/winegstreamer/gst_private.h | 2 + dlls/winegstreamer/main.c | 25 +++ dlls/winegstreamer/mfplat.c | 60 ++++++- dlls/winegstreamer/unix_private.h | 2 + dlls/winegstreamer/unixlib.h | 12 ++ dlls/winegstreamer/wg_format.c | 262 ++++++++++++++++++++++++++++++ dlls/winegstreamer/wg_parser.c | 4 + 7 files changed, 363 insertions(+), 4 deletions(-)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 2374fb6fddd..59893ff1bad 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -149,6 +149,8 @@ unsigned int wg_format_get_stride(const struct wg_format *format);
bool wg_video_format_is_rgb(enum wg_video_format format);
+HRESULT wg_create_aac_codec_data(uint32_t rate, unsigned int channels, uint8_t *out, size_t *out_size); + HRESULT aac_decoder_create(REFIID riid, void **ret); HRESULT h264_decoder_create(REFIID riid, void **ret); HRESULT video_processor_create(REFIID riid, void **ret); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 74a6eee5b1d..7ebd44ff0e8 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -600,6 +600,31 @@ HRESULT wg_muxer_finalize(wg_muxer_t muxer) return S_OK; }
+HRESULT wg_create_aac_codec_data(uint32_t rate, unsigned int channels, uint8_t *out, size_t *out_size) +{ + struct wg_create_aac_codec_data_params params = + { + .rate = rate, + .channels = channels, + .buffer = out, + .size = *out_size, + .err_on = ERR_ON(quartz), + .warn_on = WARN_ON(quartz), + }; + NTSTATUS status; + + TRACE("rate %u, channels %u, out %p, out_size %p.\n", rate, channels, out, out_size); + + if ((status = WINE_UNIX_CALL(unix_wg_create_aac_codec_data, ¶ms))) + { + WARN("Failed to create AAC codec_data, status %#lx.\n", status); + return HRESULT_FROM_NT(status); + } + + *out_size = params.size; + return S_OK; +} + #define ALIGN(n, alignment) (((n) + (alignment) - 1) & ~((alignment) - 1))
unsigned int wg_format_get_stride(const struct wg_format *format) diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index e6d9fb9fd2c..3664ad295b2 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -25,6 +25,7 @@ #include "initguid.h" #include "d3d9types.h" #include "mfapi.h" +#include "mferror.h" #include "mmreg.h"
#include "wine/debug.h" @@ -632,22 +633,73 @@ static void mf_media_type_to_wg_format_audio(IMFMediaType *type, const GUID *sub FIXME("Unrecognized audio subtype %s, depth %u.\n", debugstr_guid(subtype), depth); }
+static HRESULT make_dummy_aac_user_data(IMFMediaType *type, BOOL raw, BYTE *out, size_t *len) +{ + UINT32 payload_type, profile_level, rate, channels; + HEAACWAVEFORMAT *user_data; + size_t cdlen = *len, hdrlen; + NTSTATUS status; + + if (!raw) + { + hdrlen = FIELD_OFFSET(HEAACWAVEFORMAT, pbAudioSpecificConfig) - FIELD_OFFSET(HEAACWAVEFORMAT, wfInfo.wPayloadType); + if (*len < hdrlen) + return E_NOT_SUFFICIENT_BUFFER; + + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AAC_PAYLOAD_TYPE, &payload_type))) + payload_type = 0; + else if (payload_type > 3) + return MF_E_INVALIDMEDIATYPE; + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, &profile_level))) + profile_level = 0x29; + else if (profile_level > 0xFFFF) + return MF_E_INVALIDMEDIATYPE; + + user_data = (HEAACWAVEFORMAT *)(out - FIELD_OFFSET(HEAACWAVEFORMAT, wfInfo.wPayloadType)); + user_data->wfInfo.wPayloadType = payload_type; + user_data->wfInfo.wAudioProfileLevelIndication = profile_level; + out = user_data->pbAudioSpecificConfig; + cdlen -= hdrlen; + } + + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &rate)) || + rate > 0xFFFFFF) + return MF_E_INVALIDMEDIATYPE; + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, &channels)) || + channels < 1 || channels > 8 || channels == 7) + return MF_E_INVALIDMEDIATYPE; + + status = wg_create_aac_codec_data(rate, channels, out, &cdlen); + if (status) + return HRESULT_FROM_NT(status); + + if (raw) + *len = cdlen; + else + *len = FIELD_OFFSET(HEAACWAVEFORMAT, pbAudioSpecificConfig[cdlen]) - FIELD_OFFSET(HEAACWAVEFORMAT, wfInfo.wPayloadType); + return S_OK; +} + static void mf_media_type_to_wg_format_audio_mpeg4(IMFMediaType *type, const GUID *subtype, struct wg_format *format) { BYTE buffer[sizeof(HEAACWAVEFORMAT) + 64]; HEAACWAVEFORMAT *wfx = (HEAACWAVEFORMAT *)buffer; UINT32 codec_data_size; - BOOL raw_aac; + BOOL raw_aac = IsEqualGUID(subtype, &MFAudioFormat_RAW_AAC); + size_t len = sizeof(buffer) - sizeof(wfx->wfInfo.wfx);
wfx->wfInfo.wfx.cbSize = sizeof(buffer) - sizeof(wfx->wfInfo.wfx); if (FAILED(IMFMediaType_GetBlob(type, &MF_MT_USER_DATA, (BYTE *)(&wfx->wfInfo.wfx + 1), wfx->wfInfo.wfx.cbSize, &codec_data_size))) { - FIXME("Codec data is not set.\n"); - return; + if (FAILED(make_dummy_aac_user_data(type, raw_aac, (BYTE *)(&wfx->wfInfo.wfx + 1), &len))) + { + FIXME("Codec data is not set.\n"); + return; + } + codec_data_size = len; }
- raw_aac = IsEqualGUID(subtype, &MFAudioFormat_RAW_AAC); if (!raw_aac) codec_data_size -= min(codec_data_size, sizeof(HEAACWAVEINFO) - sizeof(WAVEFORMATEX)); if (codec_data_size > sizeof(format->u.audio_mpeg4.codec_data)) diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index bb2cb864735..1ef07168c61 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -48,6 +48,8 @@ extern bool push_event(GstPad *pad, GstEvent *event);
/* wg_format.c */
+extern NTSTATUS wg_create_aac_codec_data(void *args); + extern void wg_format_from_caps(struct wg_format *format, const GstCaps *caps); extern bool wg_format_compare(const struct wg_format *a, const struct wg_format *b); extern GstCaps *wg_format_to_caps(const struct wg_format *format); diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 4ec9fce515e..85f7f89938b 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -412,6 +412,16 @@ struct wg_muxer_read_data_params UINT64 offset; };
+struct wg_create_aac_codec_data_params +{ + UINT32 rate; + UINT32 channels; + void *buffer; + UINT64 size; + UINT8 err_on; + UINT8 warn_on; +}; + enum unix_funcs { unix_wg_init_gstreamer, @@ -461,6 +471,8 @@ enum unix_funcs unix_wg_muxer_read_data, unix_wg_muxer_finalize,
+ unix_wg_create_aac_codec_data, + unix_wg_funcs_count, };
diff --git a/dlls/winegstreamer/wg_format.c b/dlls/winegstreamer/wg_format.c index 6da97685736..d59b665c770 100644 --- a/dlls/winegstreamer/wg_format.c +++ b/dlls/winegstreamer/wg_format.c @@ -34,6 +34,8 @@ #include <gst/video/video.h> #include <gst/audio/audio.h>
+#include "ntstatus.h" +#define WIN32_NO_STATUS #include "winternl.h" #include "codecapi.h" #include "dshow.h" @@ -390,6 +392,266 @@ static void wg_format_from_caps_video_mpeg1(struct wg_format *format, const GstC format->u.video_mpeg1.fps_d = fps_d; }
+struct wg_create_aac_codec_data_state +{ + pthread_mutex_t mutex; + pthread_cond_t cond; + struct wg_create_aac_codec_data_params *params; + BOOL got_caps, error; +}; + +static GstBusSyncReply bus_handler_cb(GstBus *bus, GstMessage *msg, gpointer user) +{ + struct wg_create_aac_codec_data_state *state = user; + gchar *dbg_info = NULL; + GError *err = NULL; + + GST_LOG("message type %s.", GST_MESSAGE_TYPE_NAME(msg)); + + switch (msg->type) + { + case GST_MESSAGE_ERROR: + gst_message_parse_error(msg, &err, &dbg_info); + if (state->params->err_on) + { + fprintf(stderr, "winegstreamer error: %s: %s\n", GST_OBJECT_NAME(msg->src), err->message); + fprintf(stderr, "winegstreamer error: %s: %s\n", GST_OBJECT_NAME(msg->src), dbg_info); + } + g_error_free(err); + g_free(dbg_info); + pthread_mutex_lock(&state->mutex); + state->error = true; + pthread_mutex_unlock(&state->mutex); + pthread_cond_signal(&state->cond); + break; + + case GST_MESSAGE_WARNING: + gst_message_parse_warning(msg, &err, &dbg_info); + if (state->params->warn_on) + { + fprintf(stderr, "winegstreamer warning: %s: %s\n", GST_OBJECT_NAME(msg->src), err->message); + fprintf(stderr, "winegstreamer warning: %s: %s\n", GST_OBJECT_NAME(msg->src), dbg_info); + } + g_error_free(err); + g_free(dbg_info); + break; + + default: + break; + } + gst_message_unref(msg); + return GST_BUS_DROP; +} + +static GstFlowReturn sink_chain_cb(GstPad *pad, GstObject *parent, GstBuffer *buffer) +{ + gst_buffer_unref(buffer); + return GST_FLOW_OK; +} + +static gboolean sink_event_cb(GstPad *pad, GstObject *parent, GstEvent *event) +{ + struct wg_create_aac_codec_data_state *state = gst_pad_get_element_private(pad); + + GST_LOG("state %p, type "%s".", state, GST_EVENT_TYPE_NAME(event)); + + switch (event->type) + { + case GST_EVENT_CAPS: + { + const GstStructure *structure = NULL; + const GValue *codec_data_value; + GstBuffer *codec_data; + GstMapInfo map; + GstCaps *caps; + + gst_event_parse_caps(event, &caps); + structure = gst_caps_get_structure(caps, 0); + if (!(codec_data_value = gst_structure_get_value(structure, "codec_data")) || + !(codec_data = gst_value_get_buffer(codec_data_value))) + { + GST_WARNING("Missing "codec_data" value in %" GST_PTR_FORMAT ".", caps); + break; + } + + gst_buffer_map(codec_data, &map, GST_MAP_READ); + if (map.size <= state->params->size) + { + state->params->size = map.size; + memcpy(state->params->buffer, map.data, map.size); + } + else + GST_WARNING("Too big codec_data value (%u) in %" GST_PTR_FORMAT ".", (UINT)map.size, caps); + gst_buffer_unmap(codec_data, &map); + + pthread_mutex_lock(&state->mutex); + state->got_caps = TRUE; + pthread_mutex_unlock(&state->mutex); + pthread_cond_signal(&state->cond); + break; + } + + default: + break; + } + gst_event_unref(event); + return TRUE; +} + +extern NTSTATUS wg_create_aac_codec_data(void *args) +{ + struct wg_create_aac_codec_data_params *params = args; + GstPad *encoder_sink_pad = NULL, *encoder_src_pad = NULL, *parser_sink_pad = NULL, + *parser_src_pad = NULL, *sink_pad = NULL; + struct wg_create_aac_codec_data_state state; + GstCaps *raw_caps = NULL, *aac_caps = NULL; + GstElement *encoder = NULL, *parser = NULL; + NTSTATUS status = STATUS_NO_MEMORY; + GstPadTemplate *sink_template; + GstElement *pipeline = NULL; + GstBuffer *buffer = NULL; + GstFlowReturn flowret; + GstSegment segment; + GstBus *bus = NULL; + + memset(&state, 0, sizeof(state)); + if (pthread_mutex_init(&state.mutex, NULL) || pthread_cond_init(&state.cond, NULL)) + goto out; + state.params = params; + + if (!(raw_caps = gst_caps_new_simple("audio/x-raw", + "format", G_TYPE_STRING, "S16LE", + "layout", G_TYPE_STRING, "interleaved", + "rate", G_TYPE_INT, params->rate, + "channels", G_TYPE_INT, params->channels, + "channel-mask", GST_TYPE_BITMASK, gst_audio_channel_get_fallback_mask(params->channels), + NULL))) + goto out; + + if (!(aac_caps = gst_caps_new_simple("audio/mpeg", + "mpegversion", G_TYPE_INT, 4, + "rate", G_TYPE_INT, params->rate, + "channels", G_TYPE_INT, params->channels, + "stream-format", G_TYPE_STRING, "raw", + "profile", G_TYPE_STRING, "lc", + "framed", G_TYPE_BOOLEAN, TRUE, + NULL))) + goto out; + + if (!(pipeline = gst_pipeline_new(NULL))) + goto out; + + if (!(sink_template = gst_pad_template_new("aac_codec_data_sink", GST_PAD_SINK, GST_PAD_ALWAYS, aac_caps))) + goto out; + sink_pad = gst_pad_new_from_template(sink_template, "aac_codec_data_sink"); + gst_object_unref(sink_template); + if (!sink_pad) + goto out; + GST_PAD_SET_ACCEPT_TEMPLATE(sink_pad); + gst_pad_use_fixed_caps(sink_pad); + gst_pad_set_element_private(sink_pad, &state); + gst_pad_set_chain_function(sink_pad, sink_chain_cb); + gst_pad_set_event_function(sink_pad, sink_event_cb); + + if (!(buffer = gst_buffer_new_allocate(NULL, 0, NULL))) + goto out; + buffer->dts = GST_CLOCK_TIME_NONE; + buffer->pts = 0; + buffer->duration = 0; + buffer->offset = 0; + buffer->offset_end = 0; + + status = STATUS_UNSUCCESSFUL; + + if (!(bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline)))) + goto out; + gst_bus_set_sync_handler(bus, bus_handler_cb, &state, NULL); + + if (!(encoder = find_element(GST_ELEMENT_FACTORY_TYPE_ENCODER, raw_caps, aac_caps))) + goto out; + if (!gst_bin_add(GST_BIN(pipeline), encoder)) + { + gst_object_unref(encoder); + goto out; + } + if (!gst_element_sync_state_with_parent(encoder) + || !(encoder_sink_pad = gst_element_get_static_pad(encoder, "sink")) + || !(encoder_src_pad = gst_element_get_static_pad(encoder, "src"))) + goto out; + + if (!(parser = find_element(GST_ELEMENT_FACTORY_TYPE_PARSER, aac_caps, NULL))) + goto out; + if (!gst_bin_add(GST_BIN(pipeline), parser)) + { + gst_object_unref(parser); + goto out; + } + if (!gst_element_sync_state_with_parent(parser) + || !(parser_sink_pad = gst_element_get_static_pad(parser, "sink")) + || !(parser_src_pad = gst_element_get_static_pad(parser, "src"))) + goto out; + + if (!gst_element_link(encoder, parser) + || !link_src_to_sink(parser_src_pad, sink_pad) + || !gst_pad_set_active(parser_src_pad, 1) + || !gst_pad_set_active(parser_sink_pad, 1) + || !gst_pad_set_active(encoder_src_pad, 1) + || !gst_pad_set_active(encoder_sink_pad, 1) + || !gst_pad_set_active(sink_pad, 1)) + goto out; + + gst_segment_init(&segment, GST_FORMAT_TIME); + if (gst_element_set_state(pipeline, GST_STATE_READY) == GST_STATE_CHANGE_FAILURE + || gst_element_get_state(pipeline, NULL, NULL, -1) == GST_STATE_CHANGE_FAILURE + || !gst_pad_send_event(encoder_sink_pad, gst_event_new_stream_start("aac_codec_data")) + || !gst_pad_send_event(encoder_sink_pad, gst_event_new_caps(raw_caps)) + || !gst_pad_send_event(encoder_sink_pad, gst_event_new_segment(&segment)) + || gst_element_set_state(pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE + || gst_element_get_state(pipeline, NULL, NULL, -1) == GST_STATE_CHANGE_FAILURE) + goto out; + + flowret = gst_pad_chain(encoder_sink_pad, buffer); + buffer = NULL; + if (flowret == GST_FLOW_ERROR || !gst_pad_send_event(encoder_sink_pad, gst_event_new_eos())) + goto out; + + pthread_mutex_lock(&state.mutex); + while (!state.got_caps && !state.error) + pthread_cond_wait(&state.cond, &state.mutex); + pthread_mutex_unlock(&state.mutex); + +out: + if (pipeline && gst_element_set_state(pipeline, GST_STATE_NULL) != GST_STATE_CHANGE_FAILURE) + gst_element_get_state(pipeline, NULL, NULL, -1); + + if (parser_src_pad) gst_object_unref(parser_src_pad); + if (parser_sink_pad) gst_object_unref(parser_sink_pad); + if (encoder_src_pad) gst_object_unref(encoder_src_pad); + if (encoder_sink_pad) gst_object_unref(encoder_sink_pad); + if (buffer) gst_buffer_unref(buffer); + if (pipeline) gst_object_unref(pipeline); + if (aac_caps) gst_caps_unref(aac_caps); + if (raw_caps) gst_caps_unref(raw_caps); + if (bus) + { + gst_bus_set_sync_handler(bus, NULL, NULL, NULL); + gst_object_unref(bus); + } + if (sink_pad) + { + gst_pad_set_event_function(sink_pad, NULL); + gst_pad_set_chain_function(sink_pad, NULL); + gst_pad_set_element_private(sink_pad, NULL); + gst_object_unref(sink_pad); + } + pthread_mutex_destroy(&state.mutex); + pthread_cond_destroy(&state.cond); + if (state.got_caps) + return STATUS_SUCCESS; + GST_ERROR("Failed creating AAC codec data, status %#x.", (unsigned int)status); + return status; +} + void wg_format_from_caps(struct wg_format *format, const GstCaps *caps) { const GstStructure *structure = gst_caps_get_structure(caps, 0); diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index b0a344d5186..ea7bfed41c3 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1928,6 +1928,8 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_muxer_push_sample), X(wg_muxer_read_data), X(wg_muxer_finalize), + + X(wg_create_aac_codec_data), };
C_ASSERT(ARRAYSIZE(__wine_unix_call_funcs) == unix_wg_funcs_count); @@ -2261,6 +2263,8 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = X64(wg_muxer_push_sample), X64(wg_muxer_read_data), X(wg_muxer_finalize), + + X(wg_create_aac_codec_data), };
C_ASSERT(ARRAYSIZE(__wine_unix_call_wow64_funcs) == unix_wg_funcs_count);