diff --git a/subprojects/gst-plugins-base/docs/plugins/gst_plugins_cache.json b/subprojects/gst-plugins-base/docs/plugins/gst_plugins_cache.json index cc5d060ab2..1778d14b80 100644 --- a/subprojects/gst-plugins-base/docs/plugins/gst_plugins_cache.json +++ b/subprojects/gst-plugins-base/docs/plugins/gst_plugins_cache.json @@ -865,6 +865,30 @@ "type": "guint", "writable": true }, + "input-channels-reorder": { + "blurb": "The positions configuration to use to reorder the input channels consecutively according to their index.", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "gst (0)", + "mutable": "null", + "readable": true, + "type": "GstAudioConvertInputChannelsReorder", + "writable": true + }, + "input-channels-reorder-mode": { + "blurb": "The input channels reordering mode used to apply the selected positions configuration.", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "none (0)", + "mutable": "null", + "readable": true, + "type": "GstAudioConvertInputChannelsReorderMode", + "writable": true + }, "mix-matrix": { "blurb": "Transformation matrix for input/output channels.", "conditionally-available": false, @@ -894,7 +918,68 @@ }, "filename": "gstaudioconvert", "license": "LGPL", - "other-types": {}, + "other-types": { + "GstAudioConvertInputChannelsReorder": { + "kind": "enum", + "values": [ + { + "desc": "Reorder the input channels using the default GStreamer order", + "name": "gst", + "value": "0" + }, + { + "desc": "Reorder the input channels using the SMPTE order", + "name": "smpte", + "value": "1" + }, + { + "desc": "Reorder the input channels using the CINE order", + "name": "cine", + "value": "2" + }, + { + "desc": "Reorder the input channels using the AC3 order", + "name": "ac3", + "value": "3" + }, + { + "desc": "Reorder the input channels using the AAC order", + "name": "aac", + "value": "4" + }, + { + "desc": "Reorder and mix all input channels to a single mono channel", + "name": "mono", + "value": "5" + }, + { + "desc": "Reorder and mix all input channels to a single left and a single right stereo channels alternately", + "name": "alternate", + "value": "6" + } + ] + }, + "GstAudioConvertInputChannelsReorderMode": { + "kind": "enum", + "values": [ + { + "desc": "Never reorder the input channels", + "name": "none", + "value": "0" + }, + { + "desc": "Reorder the input channels only if they are unpositioned", + "name": "unpositioned", + "value": "1" + }, + { + "desc": "Always reorder the input channels according to the selected configuration", + "name": "force", + "value": "2" + } + ] + } + }, "package": "GStreamer Base Plug-ins", "source": "gst-plugins-base", "tracers": {}, diff --git a/subprojects/gst-plugins-base/gst-libs/gst/audio/audio-channel-mixer.c b/subprojects/gst-plugins-base/gst-libs/gst/audio/audio-channel-mixer.c index 62ce6fb143..7b0f132534 100644 --- a/subprojects/gst-plugins-base/gst-libs/gst/audio/audio-channel-mixer.c +++ b/subprojects/gst-plugins-base/gst-libs/gst/audio/audio-channel-mixer.c @@ -631,6 +631,75 @@ gst_audio_channel_mixer_fill_special (gfloat ** matrix, gint in_channels, * Automagically generate conversion matrix. */ +typedef enum +{ + GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_NONE = 0, + GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_MONO, + GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_STEREO +} GstAudioChannelMixerVirtualInput; + +/* Detects specific input channels configurations introduced in the + * audioconvert element (since version 1.26) with the + * `GstAudioConvertInputChannelsReorder` configurations. + * + * If all input channels are positioned to GST_AUDIO_CHANNEL_POSITION_MONO, + * the automatic mixing matrix should be configured like if there was only one + * virtual input mono channel. This virtual mono channel is the mix of all the + * real mono channels. + * + * If all input channels with an even index are positioned to + * GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT and all input channels with an odd + * index are positioned to GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, then the + * automatic mixing matrix should be configured like if there were only one + * virtual input left channel and one virtual input right channel. This virtual + * left or right channel is the mix of all the real left or right channels. + */ +static gboolean +gst_audio_channel_mixer_detect_virtual_input_channels (gint channels, + GstAudioChannelPosition * position, + GstAudioChannelMixerVirtualInput * virtual_input) +{ + g_return_val_if_fail (position != NULL, FALSE); + g_return_val_if_fail (virtual_input != NULL, FALSE); + + *virtual_input = GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_NONE; + + if (channels < 2) + return FALSE; + + static const GstAudioChannelPosition alternate_positions[2] = + { GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT + }; + + gboolean is_mono = TRUE; + gboolean is_alternate = TRUE; + for (gint i = 0; i < channels; ++i) { + if (position[i] != GST_AUDIO_CHANNEL_POSITION_MONO) + is_mono = FALSE; + + if (position[i] != alternate_positions[i % 2]) + is_alternate = FALSE; + + if (!is_mono && !is_alternate) + return FALSE; + } + + if (is_mono) { + g_assert (!is_alternate); + *virtual_input = GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_MONO; + return TRUE; + } + + if (is_alternate && (channels > 2)) { + g_assert (!is_mono); + *virtual_input = GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_STEREO; + return TRUE; + } + + return FALSE; +} + static void gst_audio_channel_mixer_fill_matrix (gfloat ** matrix, GstAudioChannelMixerFlags flags, gint in_channels, @@ -641,15 +710,71 @@ gst_audio_channel_mixer_fill_matrix (gfloat ** matrix, out_channels, out_position)) return; - gst_audio_channel_mixer_fill_identical (matrix, in_channels, in_position, + /* If all input channels are positioned to mono, the mix matrix should be + * configured like if there was only one virtual input mono channel. This + * virtual mono channel is the mix of all the real input mono channels. + * + * If all input channels are positioned to left and right alternately, the mix + * matrix should be configured like if there were only two virtual input + * channels: one left and one right. This virtual left or right channel is the + * mix of all the real input left or right channels. + */ + gint in_size = in_channels; + GstAudioChannelMixerVirtualInput virtual_input = + GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_NONE; + if (gst_audio_channel_mixer_detect_virtual_input_channels (in_size, + in_position, &virtual_input)) { + switch (virtual_input) { + case GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_MONO: + in_size = 1; + break; + case GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_STEREO: + in_size = 2; + break; + default: + break; + } + } + + gst_audio_channel_mixer_fill_identical (matrix, in_size, in_position, out_channels, out_position, flags); if (!(flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_IN)) { - gst_audio_channel_mixer_fill_compatible (matrix, in_channels, in_position, + gst_audio_channel_mixer_fill_compatible (matrix, in_size, in_position, out_channels, out_position); - gst_audio_channel_mixer_fill_others (matrix, in_channels, in_position, + gst_audio_channel_mixer_fill_others (matrix, in_size, in_position, out_channels, out_position); - gst_audio_channel_mixer_fill_normalize (matrix, in_channels, out_channels); + gst_audio_channel_mixer_fill_normalize (matrix, in_size, out_channels); + } + + switch (virtual_input) { + case GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_MONO:{ + for (gint out = 0; out < out_channels; ++out) + matrix[0][out] /= in_channels; + + for (gint in = 1; in < in_channels; ++in) + memcpy (matrix[in], matrix[0], out_channels * sizeof (gfloat)); + + break; + } + + case GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_STEREO:{ + gint right_channels = in_channels >> 1; + gint left_channels = right_channels + (in_channels % 2); + + for (gint out = 0; out < out_channels; ++out) { + matrix[0][out] /= left_channels; + matrix[1][out] /= right_channels; + } + + for (gint in = 2; in < in_channels; ++in) + memcpy (matrix[in], matrix[in % 2], out_channels * sizeof (gfloat)); + + break; + } + + default: + break; } } diff --git a/subprojects/gst-plugins-base/gst/audioconvert/gstaudioconvert.c b/subprojects/gst-plugins-base/gst/audioconvert/gstaudioconvert.c index 4cf55ffc71..7dd815b62b 100644 --- a/subprojects/gst-plugins-base/gst/audioconvert/gstaudioconvert.c +++ b/subprojects/gst-plugins-base/gst/audioconvert/gstaudioconvert.c @@ -90,6 +90,31 @@ * |[ * gst-launch-1.0 -v audiotestsrc ! audio/x-raw,channels=8 ! audioconvert mix-matrix="<>" ! audio/x-raw,channels=16,channel-mask=\(bitmask\)0x0000000000000000 ! fakesink * ]| + * + * If input channels are unpositioned but follow a standard layout, they can be + * automatically positioned according to their index using one of the reorder + * configurations. + * + * ## Example with unpositioned input channels reordering + * |[ + * gst-launch-1.0 -v audiotestsrc ! audio/x-raw,channels=6,channel-mask=\(bitmask\)0x0000000000000000 ! audioconvert input-channels-reorder-mode=unpositioned input-channels-reorder=smpte ! fakesink + * ]| + * In this case the input channels will be automatically positioned to the + * SMPTE order (left, right, center, lfe, rear-left and rear-right). + * + * The input channels reorder configurations can also be used to force the + * repositioning of the input channels when needed, for example when channels' + * positions are not correctly identified in an encoded file. + * + * ## Example with the forced reordering of input channels wrongly positioned + * |[ + * gst-launch-1.0 -v audiotestsrc ! audio/x-raw,channels=3,channel-mask=\(bitmask\)0x0000000000000034 ! audioconvert input-channels-reorder-mode=force input-channels-reorder=aac ! fakesink + * ]| + * In this case the input channels are positioned upstream as center, + * rear-left and rear-right in this order. Using the "force" reorder mode and + * the "aac" order, the input channels are going to be repositioned to left, + * right and lfe, ignoring the actual value of the `channel-mask` in the input + * caps. */ /* @@ -158,7 +183,9 @@ enum PROP_DITHERING, PROP_NOISE_SHAPING, PROP_MIX_MATRIX, - PROP_DITHERING_THRESHOLD + PROP_DITHERING_THRESHOLD, + PROP_INPUT_CHANNELS_REORDER, + PROP_INPUT_CHANNELS_REORDER_MODE }; #define DEBUG_INIT \ @@ -192,6 +219,80 @@ GST_STATIC_PAD_TEMPLATE ("sink", static GQuark meta_tag_audio_quark; /*** TYPE FUNCTIONS ***********************************************************/ + +#define GST_TYPE_AUDIO_CONVERT_INPUT_CHANNELS_REORDER (gst_audio_convert_input_channels_reorder_get_type ()) + +static GType +gst_audio_convert_input_channels_reorder_get_type (void) +{ + static GType reorder_type = 0; + + if (g_once_init_enter (&reorder_type)) { + static GEnumValue reorder_types[] = { + {GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_GST, + "Reorder the input channels using the default GStreamer order", + "gst"}, + {GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_SMPTE, + "Reorder the input channels using the SMPTE order", + "smpte"}, + {GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_CINE, + "Reorder the input channels using the CINE order", + "cine"}, + {GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_AC3, + "Reorder the input channels using the AC3 order", + "ac3"}, + {GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_AAC, + "Reorder the input channels using the AAC order", + "aac"}, + {GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MONO, + "Reorder and mix all input channels to a single mono channel", + "mono"}, + {GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_ALTERNATE, + "Reorder and mix all input channels to a single left and a single right stereo channels alternately", + "alternate"}, + {0, NULL, NULL}, + }; + + GType type = g_enum_register_static ("GstAudioConvertInputChannelsReorder", + reorder_types); + + g_once_init_leave (&reorder_type, type); + } + + return reorder_type; +} + +#define GST_TYPE_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MODE (gst_audio_convert_input_channels_reorder_mode_get_type ()) + +static GType +gst_audio_convert_input_channels_reorder_mode_get_type (void) +{ + static GType reorder_mode_type = 0; + + if (g_once_init_enter (&reorder_mode_type)) { + static GEnumValue reorder_mode_types[] = { + {GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MODE_NONE, + "Never reorder the input channels", + "none"}, + {GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MODE_UNPOSITIONED, + "Reorder the input channels only if they are unpositioned", + "unpositioned"}, + {GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MODE_FORCE, + "Always reorder the input channels according to the selected configuration", + "force"}, + {0, NULL, NULL}, + }; + + GType type = + g_enum_register_static ("GstAudioConvertInputChannelsReorderMode", + reorder_mode_types); + + g_once_init_leave (&reorder_mode_type, type); + } + + return reorder_mode_type; +} + static void gst_audio_convert_class_init (GstAudioConvertClass * klass) { @@ -246,6 +347,53 @@ gst_audio_convert_class_init (GstAudioConvertClass * klass) "Threshold for the output bit depth at/below which to apply dithering.", 0, 32, 20, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * GstAudioConvert:input-channels-reorder: + * + * The positions configuration to use to reorder the input channels + * consecutively according to their index. If a `mix-matrix` is specified, + * this configuration is ignored. + * + * When the input channels reordering is activated (because the + * `input-channels-reorder-mode` property is + * @GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MODE_FORCE or the input channels + * are unpositioned and the reorder mode is + * @GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MODE_UNPOSITIONED), input + * channels will be reordered consecutively according to their index + * independently of the `channel-mask` value in the sink pad audio caps. + * + * Since: 1.26 + */ + g_object_class_install_property (gobject_class, + PROP_INPUT_CHANNELS_REORDER, + g_param_spec_enum ("input-channels-reorder", + "Input Channels Reorder", + "The positions configuration to use to reorder the input channels consecutively according to their index.", + GST_TYPE_AUDIO_CONVERT_INPUT_CHANNELS_REORDER, + GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_GST, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gst_type_mark_as_plugin_api (GST_TYPE_AUDIO_CONVERT_INPUT_CHANNELS_REORDER, + 0); + + /** + * GstAudioConvert:input-channels-reorder-mode: + * + * The input channels reordering mode used to apply the selected positions + * configuration. + * + * Since: 1.26 + */ + g_object_class_install_property (gobject_class, + PROP_INPUT_CHANNELS_REORDER_MODE, + g_param_spec_enum ("input-channels-reorder-mode", + "Input Channels Reorder Mode", + "The input channels reordering mode used to apply the selected positions configuration.", + GST_TYPE_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MODE, + GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MODE_NONE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gst_type_mark_as_plugin_api + (GST_TYPE_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MODE, 0); + gst_element_class_add_static_pad_template (element_class, &gst_audio_convert_src_template); gst_element_class_add_static_pad_template (element_class, @@ -304,6 +452,574 @@ gst_audio_convert_dispose (GObject * obj) G_OBJECT_CLASS (parent_class)->dispose (obj); } +/*** INPUT CHANNELS REORDER FUNCTIONS *****************************************/ + +typedef struct +{ + gboolean has_stereo; + gboolean lfe_as_last_channel; +} GstAudioConvertInputChannelsReorderConfig; + +static const GstAudioConvertInputChannelsReorderConfig + input_channels_reorder_config[] = { + // GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_GST + {TRUE, FALSE}, + // GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_SMPTE + {TRUE, FALSE}, + // GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_CINE + {TRUE, TRUE}, + // GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_AC3 + {TRUE, TRUE}, + // GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_AAC + {TRUE, TRUE}, + // GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MONO + {FALSE, FALSE}, + // GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_ALTERNATE + {TRUE, FALSE} +}; + +#define GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_NB G_N_ELEMENTS (input_channels_reorder_config) + +static const GstAudioChannelPosition + channel_position_per_reorder_config + [GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_NB][64] = { + // GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_GST + { + 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_LFE2, + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_TOP_CENTER, + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT, + GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_RIGHT, + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER, + GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_WIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_WIDE_RIGHT, + GST_AUDIO_CHANNEL_POSITION_SURROUND_LEFT, + GST_AUDIO_CHANNEL_POSITION_SURROUND_RIGHT, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + }, + // GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_SMPTE (see: https://www.sis.se/api/document/preview/919377/) + { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // Left front (L) + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // Right front (R) + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, // Center front (C) + GST_AUDIO_CHANNEL_POSITION_LFE1, // Low frequency enhancement (LFE) + GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, // Left surround (Ls) + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, // Right surround (Rs) + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, // Left front center (Lc) + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, // Right front center (Rc) + GST_AUDIO_CHANNEL_POSITION_SURROUND_LEFT, // Rear surround left (Lsr) + GST_AUDIO_CHANNEL_POSITION_SURROUND_RIGHT, // Rear surround right (Rsr) + GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, // Rear center (Cs) + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, // Left side surround (Lss) + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, // Right side surround (Rss) + GST_AUDIO_CHANNEL_POSITION_WIDE_LEFT, // Left wide front (Lw) + GST_AUDIO_CHANNEL_POSITION_WIDE_RIGHT, // Right wide front (Rw) + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT, // Left front vertical height (Lv) + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT, // Right front vertical height (Rv) + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER, // Center front vertical height (Cv) + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT, // Left surround vertical height rear (Lvr) + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT, // Right surround vertical height rear (Rvr) + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER, // Center vertical height rear (Cvr) + GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_LEFT, // Left vertical height side surround (Lvss) + GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_RIGHT, // Right vertical height side surround (Rvss) + GST_AUDIO_CHANNEL_POSITION_TOP_CENTER, // Top center surround (Ts) + GST_AUDIO_CHANNEL_POSITION_LFE2, // Low frequency enhancement 2 (LFE2) + GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_LEFT, // Left front vertical bottom (Lb) + GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_RIGHT, // Right front vertical bottom (Rb) + GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_CENTER, // Center front vertical bottom (Cb) + GST_AUDIO_CHANNEL_POSITION_INVALID, // Left vertical height surround (Lvs) + GST_AUDIO_CHANNEL_POSITION_INVALID, // Right vertical height surround (Rvs) + GST_AUDIO_CHANNEL_POSITION_INVALID, // Reserved + GST_AUDIO_CHANNEL_POSITION_INVALID, // Reserved + GST_AUDIO_CHANNEL_POSITION_INVALID, // Reserved + GST_AUDIO_CHANNEL_POSITION_INVALID, // Reserved + GST_AUDIO_CHANNEL_POSITION_INVALID, // Low frequency enhancement 3 (LFE3) + GST_AUDIO_CHANNEL_POSITION_INVALID, // Left edge of screen (Leos) + GST_AUDIO_CHANNEL_POSITION_INVALID, // Right edge of screen (Reos) + GST_AUDIO_CHANNEL_POSITION_INVALID, // Half-way between center of screen and left edge of screen (Hwbcal) + GST_AUDIO_CHANNEL_POSITION_INVALID, // Half-way between center of screen and right edge of screen (Hwbcar) + GST_AUDIO_CHANNEL_POSITION_INVALID, // Left back surround (Lbs) + GST_AUDIO_CHANNEL_POSITION_INVALID, // Right back surround (Rbs) + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + }, + // GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_CINE + { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, // C + GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, // Ls + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, // Rs + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, // Lc + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, // Rc + GST_AUDIO_CHANNEL_POSITION_SURROUND_LEFT, // Lsr + GST_AUDIO_CHANNEL_POSITION_SURROUND_RIGHT, // Rsr + GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, // Cs + GST_AUDIO_CHANNEL_POSITION_TOP_CENTER, // Ts + GST_AUDIO_CHANNEL_POSITION_WIDE_LEFT, // Lw + GST_AUDIO_CHANNEL_POSITION_WIDE_RIGHT, // Rw + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT, // Lv + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT, // Rv + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER, // Cv + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT, // Lvr + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT, // Rvr + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER, // Cvr + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, // Lss + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, // Rss + GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_LEFT, // Lvss + GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_RIGHT, // Rvss + GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_LEFT, // Lb + GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_RIGHT, // Rb + GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_CENTER, // Cb + GST_AUDIO_CHANNEL_POSITION_LFE2, // LFE2 + GST_AUDIO_CHANNEL_POSITION_LFE1, // LFE1 + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + }, + // GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_AC3 + { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, // C + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, // Ls + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, // Rs + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, // Lc + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, // Rc + GST_AUDIO_CHANNEL_POSITION_SURROUND_LEFT, // Lsr + GST_AUDIO_CHANNEL_POSITION_SURROUND_RIGHT, // Rsr + GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, // Cs + GST_AUDIO_CHANNEL_POSITION_TOP_CENTER, // Ts + GST_AUDIO_CHANNEL_POSITION_WIDE_LEFT, // Lw + GST_AUDIO_CHANNEL_POSITION_WIDE_RIGHT, // Rw + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT, // Lv + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT, // Rv + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER, // Cv + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT, // Lvr + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT, // Rvr + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER, // Cvr + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, // Lss + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, // Rss + GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_LEFT, // Lvss + GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_RIGHT, // Rvss + GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_LEFT, // Lb + GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_RIGHT, // Rb + GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_CENTER, // Cb + GST_AUDIO_CHANNEL_POSITION_LFE2, // LFE2 + GST_AUDIO_CHANNEL_POSITION_LFE1, // LFE1 + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + }, + // GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_AAC + { + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, // C + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, // Ls + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, // Rs + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, // Lc + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, // Rc + GST_AUDIO_CHANNEL_POSITION_SURROUND_LEFT, // Lsr + GST_AUDIO_CHANNEL_POSITION_SURROUND_RIGHT, // Rsr + GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, // Cs + GST_AUDIO_CHANNEL_POSITION_TOP_CENTER, // Ts + GST_AUDIO_CHANNEL_POSITION_WIDE_LEFT, // Lw + GST_AUDIO_CHANNEL_POSITION_WIDE_RIGHT, // Rw + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT, // Lv + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT, // Rv + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER, // Cv + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT, // Lvr + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT, // Rvr + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER, // Cvr + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, // Lss + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, // Rss + GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_LEFT, // Lvss + GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_RIGHT, // Rvss + GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_LEFT, // Lb + GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_RIGHT, // Rb + GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_CENTER, // Cb + GST_AUDIO_CHANNEL_POSITION_LFE2, // LFE2 + GST_AUDIO_CHANNEL_POSITION_LFE1, // LFE1 + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + }, + // GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MONO + { + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + GST_AUDIO_CHANNEL_POSITION_MONO, + }, + // GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_ALTERNATE + { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, // L + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, // R + } +}; + +static const gchar *gst_audio_convert_input_channels_reorder_to_string + (GstAudioConvertInputChannelsReorder reorder) +{ + switch (reorder) { + case GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_GST: + return "GST"; + case GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_SMPTE: + return "SMPTE"; + case GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_CINE: + return "CINE"; + case GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_AC3: + return "AC3"; + case GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_AAC: + return "AAC"; + case GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MONO: + return "MONO"; + case GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_ALTERNATE: + return "ALTERNATE"; + default: + return "UNKNOWN"; + } +} + +static gboolean +gst_audio_convert_position_channels_from_reorder_configuration (gint channels, + GstAudioConvertInputChannelsReorder reorder, + GstAudioChannelPosition * position) +{ + g_return_val_if_fail (channels > 0, FALSE); + g_return_val_if_fail (reorder >= 0 + && reorder < GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_NB, FALSE); + g_return_val_if_fail (position != NULL, FALSE); + + GST_DEBUG ("ordering %d audio channel(s) according to the %s configuration", + channels, gst_audio_convert_input_channels_reorder_to_string (reorder)); + + if (channels == 1) { + position[0] = GST_AUDIO_CHANNEL_POSITION_MONO; + return TRUE; + } + + if (channels == 2 && input_channels_reorder_config[reorder].has_stereo) { + position[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; + position[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; + return TRUE; + } + + for (gint i = 0; i < channels; ++i) { + if (i < G_N_ELEMENTS (channel_position_per_reorder_config[reorder])) + position[i] = channel_position_per_reorder_config[reorder][i]; + else + position[i] = GST_AUDIO_CHANNEL_POSITION_INVALID; + } + + if (channels > 2 + && input_channels_reorder_config[reorder].lfe_as_last_channel) { + position[channels - 1] = GST_AUDIO_CHANNEL_POSITION_LFE1; + if (channels == 3 && input_channels_reorder_config[reorder].has_stereo) { + position[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; + position[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; + } + } + + return TRUE; +} + /*** GSTREAMER FUNCTIONS ******************************************************/ /* BaseTransform vmethods */ @@ -352,12 +1068,14 @@ remove_channels_from_structure (GstCapsFeatures * features, GstStructure * s, { guint64 mask; gint channels; - GstAudioConvert *this = GST_AUDIO_CONVERT (user_data); + gboolean force_removing = *(gboolean *) user_data; - /* Only remove the channels and channel-mask if a (empty) mix matrix was manually specified, - * if no channel-mask is specified, for non-NONE channel layouts or for a single channel layout + /* Only remove the channels and channel-mask if a mix matrix was manually + * specified or an input channels reordering is applied, or if no + * channel-mask is specified, for non-NONE channel layouts or for a single + * channel layout. */ - if (this->mix_matrix_is_set || + if (force_removing || !gst_structure_get (s, "channel-mask", GST_TYPE_BITMASK, &mask, NULL) || (mask != 0 || (gst_structure_get_int (s, "channels", &channels) && channels == 1))) { @@ -393,7 +1111,12 @@ gst_audio_convert_transform_caps (GstBaseTransform * btrans, gst_caps_map_in_place (tmp, remove_format_from_structure, NULL); gst_caps_map_in_place (tmp, remove_layout_from_structure, NULL); - gst_caps_map_in_place (tmp, remove_channels_from_structure, btrans); + + gboolean force_removing = this->mix_matrix_is_set + || (direction == GST_PAD_SINK + && this->input_channels_reorder_mode != + GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MODE_NONE); + gst_caps_map_in_place (tmp, remove_channels_from_structure, &force_removing); /* We can infer the required input / output channels based on the * matrix dimensions */ @@ -796,11 +1519,46 @@ gst_audio_convert_set_caps (GstBaseTransform * base, GstCaps * incaps, GST_AUDIO_CONVERTER_OPT_NOISE_SHAPING_METHOD, GST_TYPE_AUDIO_NOISE_SHAPING_METHOD, this->ns, NULL); - if (this->mix_matrix_is_set) + if (this->mix_matrix_is_set) { gst_structure_set_value (config, GST_AUDIO_CONVERTER_OPT_MIX_MATRIX, &this->mix_matrix); - this->convert = gst_audio_converter_new (0, &in_info, &out_info, config); + this->convert = gst_audio_converter_new (0, &in_info, &out_info, config); + + } else if (this->input_channels_reorder_mode != + GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MODE_NONE) { + GstAudioFlags in_flags; + GstAudioChannelPosition in_position[64]; + gboolean restore_in = FALSE; + + if (this->input_channels_reorder_mode == + GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MODE_FORCE + || GST_AUDIO_INFO_IS_UNPOSITIONED (&in_info)) { + in_flags = GST_AUDIO_INFO_FLAGS (&in_info); + memcpy (in_position, in_info.position, + GST_AUDIO_INFO_CHANNELS (&in_info) * + sizeof (GstAudioChannelPosition)); + + if (gst_audio_convert_position_channels_from_reorder_configuration + (GST_AUDIO_INFO_CHANNELS (&in_info), this->input_channels_reorder, + in_info.position)) { + GST_AUDIO_INFO_FLAGS (&in_info) &= ~GST_AUDIO_FLAG_UNPOSITIONED; + restore_in = TRUE; + } + } + + this->convert = gst_audio_converter_new (0, &in_info, &out_info, config); + + if (restore_in) { + GST_AUDIO_INFO_FLAGS (&in_info) = in_flags; + memcpy (in_info.position, in_position, + GST_AUDIO_INFO_CHANNELS (&in_info) * + sizeof (GstAudioChannelPosition)); + } + + } else { + this->convert = gst_audio_converter_new (0, &in_info, &out_info, config); + } if (this->convert == NULL) goto no_converter; @@ -1033,6 +1791,12 @@ gst_audio_convert_set_property (GObject * object, guint prop_id, } } break; + case PROP_INPUT_CHANNELS_REORDER: + this->input_channels_reorder = g_value_get_enum (value); + break; + case PROP_INPUT_CHANNELS_REORDER_MODE: + this->input_channels_reorder_mode = g_value_get_enum (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1059,6 +1823,12 @@ gst_audio_convert_get_property (GObject * object, guint prop_id, if (this->mix_matrix_is_set) g_value_copy (&this->mix_matrix, value); break; + case PROP_INPUT_CHANNELS_REORDER: + g_value_set_enum (value, this->input_channels_reorder); + break; + case PROP_INPUT_CHANNELS_REORDER_MODE: + g_value_set_enum (value, this->input_channels_reorder_mode); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; diff --git a/subprojects/gst-plugins-base/gst/audioconvert/gstaudioconvert.h b/subprojects/gst-plugins-base/gst/audioconvert/gstaudioconvert.h index 0413d916e4..62a689b8ef 100644 --- a/subprojects/gst-plugins-base/gst/audioconvert/gstaudioconvert.h +++ b/subprojects/gst-plugins-base/gst/audioconvert/gstaudioconvert.h @@ -26,6 +26,150 @@ #include #include +/** + * GstAudioConvertInputChannelsReorder: + * @GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_GST: reorder input channels + * according to the default ordering in GStreamer: FRONT_LEFT, FRONT_RIGHT, + * FRONT_CENTER, LFE1 and then the other channels. If there is only one + * input channel available, it will be positioned to MONO. + * @GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_SMPTE: reorder input channels + * according to the SMPTE standard: FRONT_LEFT, FRONT_RIGHT, FRONT_CENTER, + * LFE1 and then the other channels (the ordering is slightly different from + * the default GStreamer order). This audio channels ordering is the only + * one that is officially standardized and used by default in many audio + * softwares (see: https://www.sis.se/api/document/preview/919377/). If + * there is only one input channel available, it will be positioned to MONO. + * @GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_CINE: reorder input channels as it + * is commonly used in the cinema industry: FRONT_LEFT, FRONT_RIGHT, + * FRONT_CENTER, the other channels and then LFE1. This configuration is not + * standardized but usually appears in the literature related to the cinema + * industry and as an alternate ordering in different audio softwares. On + * some web sites, this configuration and the + * @GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_AC3 ordering are switched. If + * there is only one input channel available, it will be positioned to + * MONO. If the number of available input channels is > 2, the last channel + * will always be positioned to LFE1. + * @GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_AC3: reorder input channels in the + * same order as the default order of the AC3 format: FRONT_LEFT, + * FRONT_CENTER, FRONT_RIGHT, the other channels (same order as in the + * @GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_CINE policy) and then LFE1. + * This configuration is also commonly used in the cinema industry and in + * professional audio softwares (like ProTools under the name "FILM" + * ordering). The only difference with the + * @GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_CINE configuration is the + * order of the first 3 channels. If there is only one input channel + * available, it will be positioned to MONO. If the number of available + * input channels is > 2, the last channel will always be positioned to + * LFE1. If the number of available input channels is 2 or 3, the first two + * channels will be positioned to FRONT_LEFT and FRONT_RIGHT. + * @GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_AAC: reorder input channels in the + * same order as the default order of the AAC format: FRONT_CENTER, + * FRONT_LEFT, FRONT_RIGHT, the other channels (same order as in the + * @GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_CINE configuration) and then + * LFE1. The only difference with the + * @GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_CINE configuration is the + * order of the first 3 channels. If there is only one input channel + * available, it will be positioned to MONO. If the number of available + * input channels is > 2, the last channel will always be positioned to + * LFE1. If the number of available input channels is 2 or 3, the first two + * channels will be positioned to FRONT_LEFT and FRONT_RIGHT. + * @GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MONO: reorder all input channels + * to MONO. All input channels are mixed together at the same level to a + * virtual single mono channel. For `n` input channels, the virtual output + * sample value is computed as: + * `output_sample[MONO] = (1/n) x ∑ input_sample_for_channel(i)` with + * `0 <= i < n`. A concrete usage for this configuration is, for example, + * when importing audio from an array of multiple mono microphones and you + * want to use them as a unique mono channel. + * @GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_ALTERNATE: reorder all input + * channels to FRONT_LEFT and FRONT_RIGHT channels alternately (or MONO if + * there is only one input channel available). All left input channels are + * mixed together, at the same level, to a single FRONT_LEFT virtual + * channel and all right input channels are mixed together to a single + * FRONT_RIGHT virtual channel. For `2n` input channels the FRONT_LEFT and + * FRONT_RIGHT virtual output samples are computed as: + * `output_sample[FRONT_LEFT] = (1/n) x ∑ input_sample_for_channel(2i)` and + * `output_sample[FRONT_RIGHT] = (1/n) x ∑ input_sample_for_channel(2i+1)` + * with `0 <= i < n` (in case of an odd number of input channels the + * principle is the same but with an extra input left channel). A concrete + * usage for this configuration is, for example, when importing audio from + * an array of multiple stereo microphones and you want to use them as a + * simple pair of stereo channels. + * + * Input audio channels reordering configurations. + * + * It defines different ways of reordering input audio channels when they are + * not positioned by GStreamer. As a general matter, channels are always ordered + * in the @GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_GST order and the + * `channel-mask` field in the audio caps allows specifying which channels are + * active. + * + * Depending on the selected mode (see: + * @GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MODE_UNPOSITIONED), input channels + * can be automatically positioned when the `channel-mask` is not specified or + * equals 0. In this case, all input channels will be positioned according to + * the selected reordering configuration and the index of each input channel. + * This can be useful when importing audio from an array of independent + * microphones for example. + * + * The reordering configuration can also be forced (see: + * @GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MODE_FORCE) to reposition all + * input channels according to each channel index. In this case the + * `channel-mask` will be totally ignored and input channels will be reordered + * just like if they were unpositioned. This can be useful when importing + * multi-channels audio with errors in the channels positioning. + * + * For any of the former configurations, when the reordering is applied + * (input channels are unpositioned or the "force" mode is active): + * - When there is only one input channel available, it is positioned to MONO + * always, independently of the selected configuration. + * - When there are 2 input channels available, they are positioned to + * FRONT_LEFT and FRONT_RIGHT (except for the + * @GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MONO configuration where all + * input channels are positioned to MONO). + * + * Since: 1.26 + */ +typedef enum { + GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_GST = 0, + GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_SMPTE, + GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_CINE, + GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_AC3, + GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_AAC, + GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MONO, + GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_ALTERNATE +} GstAudioConvertInputChannelsReorder; + +/** + * GstAudioConvertInputChannelsReorderMode: + * @GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MODE_NONE: never reorder the input + * channels. If input channels are unpositioned and there are, at least, 3 + * input channels, an error will be generated. + * @GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MODE_UNPOSITIONED: automatically + * reorder the input channels according to the selected + * #GstAudioConvertInputChannelsReorder configuration when, and only when, + * they are unpositioned (the `channel-mask` equals 0 or is not specified + * in the input caps). + * @GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MODE_FORCE: always reorder the + * input channels according to the selected + * #GstAudioConvertInputChannelsReorder configuration. The `channel-mask` + * value in the input caps is completely ignored. Input channels are always + * reordered as if they were unpositioned independently of the input caps. + * + * The different usage modes of the input channels reordering configuration. + * + * Independently of the selected mode, the explicit definition of a mix matrix + * takes precedence over the reorder configuration. In this case, the provided + * mix matrix will override the reorder configuration. + * + * Since: 1.26 + */ +typedef enum { + GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MODE_NONE = 0, + GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MODE_UNPOSITIONED, + GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MODE_FORCE +} GstAudioConvertInputChannelsReorderMode; + #define GST_TYPE_AUDIO_CONVERT (gst_audio_convert_get_type()) G_DECLARE_FINAL_TYPE (GstAudioConvert, gst_audio_convert, GST, AUDIO_CONVERT, GstBaseTransform); @@ -45,6 +189,8 @@ struct _GstAudioConvert GstAudioNoiseShapingMethod ns; GValue mix_matrix; gboolean mix_matrix_is_set; + GstAudioConvertInputChannelsReorder input_channels_reorder; + GstAudioConvertInputChannelsReorderMode input_channels_reorder_mode; GstAudioInfo in_info; GstAudioInfo out_info; diff --git a/subprojects/gst-plugins-base/tests/check/elements/audioconvert.c b/subprojects/gst-plugins-base/tests/check/elements/audioconvert.c index c628caf4d4..9d0b63cc85 100644 --- a/subprojects/gst-plugins-base/tests/check/elements/audioconvert.c +++ b/subprojects/gst-plugins-base/tests/check/elements/audioconvert.c @@ -26,6 +26,7 @@ #include #include +#include /* For ease of programming we use globals to keep refs for our floating * src and sink pads we create; otherwise we always have to do get_pad, @@ -82,6 +83,46 @@ setup_audioconvert (GstCaps * outcaps, gboolean use_mix_matrix, return audioconvert; } +static GstElement * +setup_audioconvert_with_input_channels_reorder (GstCaps * outcaps, + GstAudioConvertInputChannelsReorder reorder) +{ + GstPadTemplate *sinktemplate; + static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (CONVERT_CAPS_TEMPLATE_STRING) + ); + GstElement *audioconvert; + + ASSERT_CAPS_REFCOUNT (outcaps, "outcaps", 1); + sinktemplate = + gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, outcaps); + + GST_DEBUG ("setup_audioconvert with caps %" GST_PTR_FORMAT, outcaps); + audioconvert = gst_check_setup_element ("audioconvert"); + g_object_set (G_OBJECT (audioconvert), "dithering", 0, NULL); + g_object_set (G_OBJECT (audioconvert), "noise-shaping", 0, NULL); + g_object_set (G_OBJECT (audioconvert), "input-channels-reorder", + reorder, "input-channels-reorder-mode", + GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MODE_UNPOSITIONED, NULL); + + mysrcpad = gst_check_setup_src_pad (audioconvert, &srctemplate); + mysinkpad = + gst_check_setup_sink_pad_from_template (audioconvert, sinktemplate); + /* this installs a getcaps func that will always return the caps we set + * later */ + gst_pad_use_fixed_caps (mysinkpad); + + gst_pad_set_active (mysrcpad, TRUE); + gst_pad_set_active (mysinkpad, TRUE); + + gst_object_unref (sinktemplate); + + ASSERT_CAPS_REFCOUNT (outcaps, "outcaps", 2); + return audioconvert; +} + static void cleanup_audioconvert (GstElement * audioconvert) { @@ -94,6 +135,41 @@ cleanup_audioconvert (GstElement * audioconvert) gst_check_teardown_element (audioconvert); } +static gchar * +format_input_channels_reorder_test_name (const gchar * test_name, + GstAudioConvertInputChannelsReorder reorder) +{ + const gchar *reorder_name = "unknown"; + switch (reorder) { + case GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_GST: + reorder_name = "gst"; + break; + case GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_SMPTE: + reorder_name = "smpte"; + break; + case GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_CINE: + reorder_name = "cine"; + break; + case GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_AC3: + reorder_name = "ac3"; + break; + case GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_AAC: + reorder_name = "aac"; + break; + case GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_MONO: + reorder_name = "mono"; + break; + case GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_ALTERNATE: + reorder_name = "alternate"; + break; + default: + break; + } + + return g_strdup_printf ("%s with input channels %s reorder", test_name, + reorder_name); +} + /* returns a newly allocated caps */ static GstCaps * get_int_caps (guint channels, gint endianness, guint width, @@ -159,6 +235,29 @@ get_float_caps (guint channels, gint endianness, guint width, return caps; } +static GstCaps * +get_unpositioned_input_caps (guint channels) +{ + GstCaps *caps; + GstAudioInfo info; + + gst_audio_info_init (&info); + gst_audio_info_set_format (&info, + gst_audio_format_build_integer (TRUE, G_BYTE_ORDER, 16, 16), + GST_AUDIO_DEF_RATE, channels, NULL); + info.layout = GST_AUDIO_LAYOUT_INTERLEAVED; + + info.flags = GST_AUDIO_FLAG_UNPOSITIONED; + for (guint i = 0; i < MIN (64, channels); ++i) + info.position[i] = GST_AUDIO_CHANNEL_POSITION_NONE; + + caps = gst_audio_info_to_caps (&info); + fail_unless (caps != NULL); + GST_DEBUG ("returning caps %" GST_PTR_FORMAT, caps); + + return caps; +} + /* Copied from vorbis; the particular values used don't matter */ static GstAudioChannelPosition channelpositions[][6] = { { /* Mono */ @@ -413,7 +512,8 @@ static void verify_convert (const gchar * which, void *in, int inlength, GstCaps * incaps, void *out, int outlength, GstCaps * outcaps, GstFlowReturn expected_flow, gboolean in_place_allowed, - gboolean use_mix_matrix, GValue * mix_matrix) + gboolean use_mix_matrix, GValue * mix_matrix, + GstElement * custom_audioconvert) { GstBuffer *inbuffer, *outbuffer; GstElement *audioconvert; @@ -423,8 +523,13 @@ verify_convert (const gchar * which, void *in, int inlength, GST_DEBUG ("incaps: %" GST_PTR_FORMAT, incaps); GST_DEBUG ("outcaps: %" GST_PTR_FORMAT, outcaps); ASSERT_CAPS_REFCOUNT (incaps, "incaps", 1); - ASSERT_CAPS_REFCOUNT (outcaps, "outcaps", 1); - audioconvert = setup_audioconvert (outcaps, use_mix_matrix, mix_matrix); + + if (custom_audioconvert) { + audioconvert = custom_audioconvert; + } else { + ASSERT_CAPS_REFCOUNT (outcaps, "outcaps", 1); + audioconvert = setup_audioconvert (outcaps, use_mix_matrix, mix_matrix); + } ASSERT_CAPS_REFCOUNT (outcaps, "outcaps", 2); fail_unless (gst_element_set_state (audioconvert, @@ -510,22 +615,36 @@ done: #define RUN_CONVERSION(which, inarray, in_get_caps, outarray, out_get_caps) \ verify_convert (which, inarray, sizeof (inarray), \ in_get_caps, outarray, sizeof (outarray), out_get_caps, GST_FLOW_OK, \ - TRUE, FALSE, &(GValue) G_VALUE_INIT); + TRUE, FALSE, &(GValue) G_VALUE_INIT, NULL); -#define RUN_CONVERSION_WITH_MATRIX(which, inarray, in_get_caps, outarray, out_get_caps, mix_matrix) \ +#define RUN_CONVERSION_WITH_MATRIX(which, inarray, in_get_caps, outarray, out_get_caps, mix_matrix) \ verify_convert (which, inarray, sizeof (inarray), \ in_get_caps, outarray, sizeof (outarray), out_get_caps, GST_FLOW_OK, \ - TRUE, TRUE, mix_matrix); + TRUE, TRUE, mix_matrix, NULL); + +#define RUN_CONVERSION_WITH_INPUT_CHANNELS_REORDER(which, inarray, \ + in_channels, reorder, outarray, out_channels) \ + { \ + GstCaps *in_get_caps = get_unpositioned_input_caps (in_channels); \ + GstCaps *out_get_caps = get_int_mc_caps (out_channels, G_BYTE_ORDER, 16, \ + 16, TRUE, GST_AUDIO_LAYOUT_INTERLEAVED, NULL); \ + verify_convert ( \ + which, inarray, sizeof (inarray), in_get_caps, outarray, \ + sizeof (outarray), out_get_caps, GST_FLOW_OK, TRUE, FALSE, \ + &(GValue) G_VALUE_INIT, \ + setup_audioconvert_with_input_channels_reorder (out_get_caps, \ + reorder)); \ + } #define RUN_CONVERSION_TO_FAIL(which, inarray, in_caps, outarray, out_caps) \ verify_convert (which, inarray, sizeof (inarray), \ in_caps, outarray, sizeof (outarray), out_caps, \ - GST_FLOW_NOT_NEGOTIATED, TRUE, FALSE, &(GValue) G_VALUE_INIT); + GST_FLOW_NOT_NEGOTIATED, TRUE, FALSE, &(GValue) G_VALUE_INIT, NULL); -#define RUN_CONVERSION_NOT_INPLACE(which, inarray, in_get_caps, outarray, out_get_caps) \ +#define RUN_CONVERSION_NOT_INPLACE(which, inarray, in_get_caps, outarray, out_get_caps) \ verify_convert (which, inarray, sizeof (inarray), \ in_get_caps, outarray, sizeof (outarray), out_get_caps, GST_FLOW_OK, \ - FALSE, FALSE, &(GValue) G_VALUE_INIT); + FALSE, FALSE, &(GValue) G_VALUE_INIT, NULL); #define INTERLEAVED GST_AUDIO_LAYOUT_INTERLEAVED #define PLANAR GST_AUDIO_LAYOUT_NON_INTERLEAVED @@ -1094,6 +1213,245 @@ GST_START_TEST (test_multichannel_conversion) GST_END_TEST; +GST_START_TEST (test_multichannel_crossmixing_with_input_channels_reorder) +{ + { + gint16 in[] = { 12400, -120 }; + gint16 out[] = { 12400, -120 }; + + for (gint reorder = GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_GST; + reorder <= GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_ALTERNATE; + ++reorder) { + gchar *test_name = + format_input_channels_reorder_test_name ("1 channel to 1", reorder); + RUN_CONVERSION_WITH_INPUT_CHANNELS_REORDER (test_name, in, 1, reorder, + out, 1); + g_free (test_name); + } + } + + { + gint16 in[] = { 12400, -120 }; + gint16 out[] = { 12400, 12400, -120, -120 }; + + for (gint reorder = GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_GST; + reorder <= GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_ALTERNATE; + ++reorder) { + gchar *test_name = + format_input_channels_reorder_test_name ("1 channel to 2", reorder); + RUN_CONVERSION_WITH_INPUT_CHANNELS_REORDER (test_name, in, 1, reorder, + out, 2); + g_free (test_name); + } + } + + { + gint16 in[] = { 12400, -120 }; + gint16 out[] = { 12400, 12400, 8767, -120, -120, -85 }; + + for (gint reorder = GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_GST; + reorder <= GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_ALTERNATE; + ++reorder) { + gchar *test_name = + format_input_channels_reorder_test_name ("1 channel to 3", reorder); + RUN_CONVERSION_WITH_INPUT_CHANNELS_REORDER (test_name, in, 1, reorder, + out, 3); + g_free (test_name); + } + } + + { + gint16 in[] = { 12400, -120, -10844, 5842 }; + gint16 out[] = { 6140, -2501 }; + + for (gint reorder = GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_GST; + reorder <= GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_ALTERNATE; + ++reorder) { + gchar *test_name = + format_input_channels_reorder_test_name ("2 channels to 1", reorder); + RUN_CONVERSION_WITH_INPUT_CHANNELS_REORDER (test_name, in, 2, reorder, + out, 1); + g_free (test_name); + } + } + + { + gint16 in[] = { 12400, -120, -10844, 5842 }; + gint16 out[][4] = { + {12400, -120, -10844, 5842}, + {12400, -120, -10844, 5842}, + {12400, -120, -10844, 5842}, + {12400, -120, -10844, 5842}, + {12400, -120, -10844, 5842}, + {6140, 6140, -2501, -2501}, + {12400, -120, -10844, 5842} + }; + + for (gint reorder = GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_GST; + reorder <= GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_ALTERNATE; + ++reorder) { + gchar *test_name = + format_input_channels_reorder_test_name ("2 channels to 2", reorder); + RUN_CONVERSION_WITH_INPUT_CHANNELS_REORDER (test_name, in, 2, reorder, + out[reorder], 2); + g_free (test_name); + } + } + + { + gint16 in[] = { 12400, -120, -10844, 5842 }; + gint16 out[][6] = { + {8767, -85, 6140, -7667, 4130, -2501}, + {8767, -85, 6140, -7667, 4130, -2501}, + {8767, -85, 6140, -7667, 4130, -2501}, + {8767, -85, 6140, -7667, 4130, -2501}, + {8767, -85, 6140, -7667, 4130, -2501}, + {6140, 6140, 4341, -2501, -2501, -1768}, + {8767, -85, 6140, -7667, 4130, -2501} + }; + + for (gint reorder = GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_GST; + reorder <= GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_ALTERNATE; + ++reorder) { + gchar *test_name = + format_input_channels_reorder_test_name ("2 channels to 3", reorder); + RUN_CONVERSION_WITH_INPUT_CHANNELS_REORDER (test_name, in, 2, reorder, + out[reorder], 3); + g_free (test_name); + } + } + + { + gint16 in[] = { 12400, -120, 1120, -10844, 5842, -48 }; + gint16 out[][2] = { + {4825, -1859}, + {4825, -1859}, + {4462, -1682}, + {4462, -1682}, + {4462, -1682}, + {4462, -1682}, + {3320, 198} + }; + + for (gint reorder = GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_GST; + reorder <= GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_ALTERNATE; + ++reorder) { + gchar *test_name = + format_input_channels_reorder_test_name ("3 channels to 1", reorder); + RUN_CONVERSION_WITH_INPUT_CHANNELS_REORDER (test_name, in, 3, reorder, + out[reorder], 1); + g_free (test_name); + } + } + + { + gint16 in[] = { 12400, -120, 1120, -10844, 5842, -48 }; + gint16 out[][4] = { + {7717, 394, -6363, 3397}, + {7717, 394, -6363, 3397}, + {6760, 500, -5446, 2897}, + {6760, 500, -5446, 2897}, + {6760, 500, -5446, 2897}, + {4462, 4462, -1682, -1682}, + {6760, -120, -5446, 5842} + }; + + for (gint reorder = GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_GST; + reorder <= GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_ALTERNATE; + ++reorder) { + gchar *test_name = + format_input_channels_reorder_test_name ("3 channels to 2", reorder); + RUN_CONVERSION_WITH_INPUT_CHANNELS_REORDER (test_name, in, 3, reorder, + out[reorder], 2); + g_free (test_name); + } + } + + { + gint16 in[] = { 12400, -120, 1120, -10844, 5842, -48 }; + gint16 out[][6] = { + {12400, -120, 1120, -10844, 5842, -48}, + {12400, -120, 1120, -10844, 5842, -48}, + {6364, 471, 4462, -5127, 2727, -1682}, + {6364, 471, 4462, -5127, 2727, -1682}, + {6364, 471, 4462, -5127, 2727, -1682}, + {4462, 4462, 3154, -1682, -1682, -1189}, + {4780, -85, 3320, -3850, 4130, 198} + }; + + for (gint reorder = GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_GST; + reorder <= GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_ALTERNATE; + ++reorder) { + gchar *test_name = + format_input_channels_reorder_test_name ("3 channels to 3", reorder); + RUN_CONVERSION_WITH_INPUT_CHANNELS_REORDER (test_name, in, 3, reorder, + out[reorder], 3); + g_free (test_name); + } + } +} + +GST_END_TEST; + +GST_START_TEST + (test_multichannel_downmixing_to_stereo_with_input_channels_reorder) { + { + gint16 in[] = + { 12400, -120, 1248, 10140, 368, -32124, 8145, 7411, -212, -5489, 18523, + 10003 + }; + gint16 out[][4] = { + {7353, -1592, 3657, 2105}, + {7353, -1592, 3657, 2105}, + {-4296, -9713, 4755, 8254}, + {-4596, -9588, 6430, 7555}, + {-5746, -6837, 6362, 7716}, + {-1343, -1343, 6372, 6372}, + {4667, -7361, 8810, 3971} + }; + + for (gint reorder = GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_GST; + reorder <= GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_ALTERNATE; + ++reorder) { + gchar *test_name = + format_input_channels_reorder_test_name ("5.1 channels to stereo", + reorder); + RUN_CONVERSION_WITH_INPUT_CHANNELS_REORDER (test_name, in, 6, reorder, + out[reorder], 2); + g_free (test_name); + } + } + + { + gint16 in[] = + { 12400, -120, 1248, 10140, 368, -32124, 1247, -458, 8145, 7411, -212, + -5489, 18523, 10003, 789, -5557 + }; + gint16 out[][4] = { + {6739, -1645, 3467, 813}, + {6739, -1645, 3467, 813}, + {-1482, 260, 1921, 3242}, + {-1617, 508, 2673, 1857}, + {-3891, 1743, 2539, 1930}, + {-912, -912, 4202, 4202}, + {3816, -5640, 6811, 1592} + }; + + for (gint reorder = GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_GST; + reorder <= GST_AUDIO_CONVERT_INPUT_CHANNELS_REORDER_ALTERNATE; + ++reorder) { + gchar *test_name = + format_input_channels_reorder_test_name ("7.1 channels to stereo", + reorder); + RUN_CONVERSION_WITH_INPUT_CHANNELS_REORDER (test_name, in, 8, reorder, + out[reorder], 2); + g_free (test_name); + } + } +} + +GST_END_TEST; + GST_START_TEST (test_passthrough) { /* int 8 bit */ @@ -1894,6 +2252,10 @@ audioconvert_suite (void) tcase_add_test (tc_chain, test_float_conversion); tcase_add_test (tc_chain, test_int_float_conversion); tcase_add_test (tc_chain, test_multichannel_conversion); + tcase_add_test (tc_chain, + test_multichannel_crossmixing_with_input_channels_reorder); + tcase_add_test (tc_chain, + test_multichannel_downmixing_to_stereo_with_input_channels_reorder); tcase_add_test (tc_chain, test_passthrough); tcase_add_test (tc_chain, test_caps_negotiation); tcase_add_test (tc_chain, test_convert_undefined_multichannel);