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.
This commit is contained in:
Sebastian Dröge 2020-01-13 20:02:58 +02:00 committed by GStreamer Merge Bot
parent 451fc5c112
commit c4f6ce789d
2 changed files with 124 additions and 3 deletions

View file

@ -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) {

View file

@ -25,6 +25,7 @@
#include <gst/gst.h>
#include <gst/base/gstcollectpads.h>
#include <gst/riff/riff-ids.h>
#include <gst/audio/audio.h>
#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;