From c4f6ce789dd614d2a4c782620b2e7ddd734e1dd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 13 Jan 2020 20:02:58 +0200 Subject: [PATCH] avimux: Add support for >2 raw audio channels For this case write a WAVEFORMATEXTENSIBLE header and also reorder the raw audio channels to the AVI channel order if needed. --- gst/avi/gstavimux.c | 116 ++++++++++++++++++++++++++++++++++++++++++-- gst/avi/gstavimux.h | 11 +++++ 2 files changed, 124 insertions(+), 3 deletions(-) diff --git a/gst/avi/gstavimux.c b/gst/avi/gstavimux.c index 1a1f38c641..1a8766fffd 100644 --- a/gst/avi/gstavimux.c +++ b/gst/avi/gstavimux.c @@ -159,7 +159,7 @@ static GstStaticPadTemplate audio_sink_factory = GST_STATIC_CAPS ("audio/x-raw, " "format = (string) { U8, S16LE, S24LE, S32LE }, " "rate = (int) [ 1000, 96000 ], " - "channels = (int) [ 1, 2 ]; " + "channels = (int) [ 1, 65535 ]; " "audio/mpeg, " "mpegversion = (int) 1, " "layer = (int) [ 1, 3 ], " @@ -747,6 +747,69 @@ gst_avi_mux_audsink_set_fields (GstAviMux * avimux, GstAviAudioPad * avipad) } } +/* Taken from wavenc */ +static guint64 +gstmask_to_wavmask (guint64 gstmask, GstAudioChannelPosition * pos) +{ + const GstAudioChannelPosition valid_pos = + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT | + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT | + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER | + GST_AUDIO_CHANNEL_POSITION_LFE1 | + GST_AUDIO_CHANNEL_POSITION_REAR_LEFT | + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT | + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER | + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER | + GST_AUDIO_CHANNEL_POSITION_REAR_CENTER | + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT | + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT | + GST_AUDIO_CHANNEL_POSITION_TOP_CENTER | + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT | + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER | + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT | + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT | + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER | + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT; + + const GstAudioChannelPosition wav_pos[] = { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_LFE1, + GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, + GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, + GST_AUDIO_CHANNEL_POSITION_TOP_CENTER, + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER, + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT, + }; + int k; + int chan = 0; + guint64 ret = 0; + guint64 mask = 1; + + if (gstmask == 0 || ((gstmask & ~valid_pos) != 0)) + return 0; + + for (k = 0; k < G_N_ELEMENTS (wav_pos); ++k) { + if (gstmask & (G_GUINT64_CONSTANT (1) << wav_pos[k])) { + ret |= mask; + pos[chan++] = wav_pos[k]; + } + mask <<= 1; + } + + return ret; +} + static gboolean gst_avi_mux_audsink_set_caps (GstPad * pad, GstCaps * vscaps) { @@ -795,10 +858,15 @@ gst_avi_mux_audsink_set_caps (GstPad * pad, GstCaps * vscaps) if (!strcmp (mimetype, "audio/x-raw")) { const gchar *format; GstAudioFormat fmt; + guint64 channel_mask; format = gst_structure_get_string (structure, "format"); fmt = gst_audio_format_from_string (format); + if (!gst_structure_get (structure, "channel-mask", GST_TYPE_BITMASK, + &channel_mask, NULL)) + channel_mask = gst_audio_channel_get_fallback_mask (channels); + switch (fmt) { case GST_AUDIO_FORMAT_U8: avipad->auds.blockalign = 8; @@ -820,11 +888,28 @@ gst_avi_mux_audsink_set_caps (GstPad * pad, GstCaps * vscaps) goto refuse_caps; } + avipad->audio_format = fmt; + avipad->auds.format = GST_RIFF_WAVE_FORMAT_PCM; /* set some more info straight */ avipad->auds.blockalign /= 8; avipad->auds.blockalign *= avipad->auds.channels; avipad->auds.av_bps = avipad->auds.blockalign * avipad->auds.rate; + + if (channels > 2 || (channels == 1 && channel_mask != 0x0) + || (channels == 2 && channel_mask != 0x3)) { + avipad->write_waveformatex = TRUE; + /* The same for now as we don't support e.g. S24_32 */ + avipad->valid_bits_per_sample = avipad->auds.bits_per_sample; + avipad->channel_mask = + gstmask_to_wavmask (channel_mask, avipad->wav_positions); + + gst_audio_channel_positions_from_mask (channels, channel_mask, + avipad->gst_positions); + avipad->needs_reorder = + memcmp (avipad->gst_positions, avipad->wav_positions, + channels * sizeof (*avipad->gst_positions)) != 0; + } } else { avipad->auds.format = 0; /* set some defaults */ @@ -1333,13 +1418,27 @@ gst_avi_mux_riff_get_avi_header (GstAviMux * avimux) /* the audio header */ strf = gst_avi_mux_start_chunk (&bw, "strf", 0); /* the actual header */ - hdl &= gst_byte_writer_put_uint16_le (&bw, audpad->auds.format); + if (audpad->write_waveformatex) + hdl &= gst_byte_writer_put_uint16_le (&bw, 0xfffe); + else + hdl &= gst_byte_writer_put_uint16_le (&bw, audpad->auds.format); hdl &= gst_byte_writer_put_uint16_le (&bw, audpad->auds.channels); hdl &= gst_byte_writer_put_uint32_le (&bw, audpad->auds.rate); hdl &= gst_byte_writer_put_uint32_le (&bw, audpad->auds.av_bps); hdl &= gst_byte_writer_put_uint16_le (&bw, audpad->auds.blockalign); hdl &= gst_byte_writer_put_uint16_le (&bw, audpad->auds.bits_per_sample); - hdl &= gst_byte_writer_put_uint16_le (&bw, codec_size); + if (audpad->write_waveformatex) { + hdl &= gst_byte_writer_put_uint16_le (&bw, codec_size + 22); + hdl &= + gst_byte_writer_put_uint16_le (&bw, audpad->valid_bits_per_sample); + hdl &= gst_byte_writer_put_uint32_le (&bw, audpad->channel_mask); + hdl &= gst_byte_writer_put_uint32_le (&bw, audpad->auds.format); + hdl &= gst_byte_writer_put_uint32_le (&bw, 0x00100000); + hdl &= gst_byte_writer_put_uint32_le (&bw, 0xAA000080); + hdl &= gst_byte_writer_put_uint32_le (&bw, 0x719B3800); + } else { + hdl &= gst_byte_writer_put_uint16_le (&bw, codec_size); + } if (audpad->auds_codec_data) { gst_buffer_map (audpad->auds_codec_data, &map, GST_MAP_READ); hdl &= gst_byte_writer_put_data (&bw, map.data, map.size); @@ -2070,6 +2169,17 @@ gst_avi_mux_do_buffer (GstAviMux * avimux, GstAviPad * avipad) if (gst_avi_mux_is_uncompressed (avipad->hdr.fcc_handler)) { data = gst_avi_mux_invert (avipad, data); } + } else { + GstAviAudioPad *audpad = (GstAviAudioPad *) avipad; + + if (audpad->needs_reorder) { + data = gst_buffer_make_writable (data); + if (!gst_audio_buffer_reorder_channels (data, audpad->audio_format, + audpad->auds.channels, audpad->gst_positions, + audpad->wav_positions)) { + GST_WARNING_OBJECT (avimux, "Could not reorder channels"); + } + } } if (avimux->restart) { diff --git a/gst/avi/gstavimux.h b/gst/avi/gstavimux.h index 6fd5343e1f..05db3cdc15 100644 --- a/gst/avi/gstavimux.h +++ b/gst/avi/gstavimux.h @@ -25,6 +25,7 @@ #include #include #include +#include #include "avi-ids.h" G_BEGIN_DECLS @@ -114,6 +115,16 @@ typedef struct _GstAviAudioPad { /* stream format */ gst_riff_strf_auds auds; + /* additional fields for WAVEFORMATEX */ + gboolean write_waveformatex; + guint16 valid_bits_per_sample; + guint32 channel_mask; + + /* for raw audio */ + gboolean needs_reorder; + GstAudioFormat audio_format; + GstAudioChannelPosition gst_positions[64], wav_positions[64]; + /* audio info for bps calculation */ guint32 audio_size; guint64 audio_time;