mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-30 13:41:48 +00:00
Multi-channel support with channel positions.
This queries port roles from the LV2 data and converts it into GStreamer channel positions. This should allow any type of multi-channel plugin (including beyond stereo, e.g. surround) to work fine in GStreamer, and with elements that require channel positions to be explicitly stated.
This commit is contained in:
parent
8538d382e3
commit
374d52d257
5 changed files with 144 additions and 8 deletions
|
@ -82,6 +82,7 @@ gst_ladspa_base_init (gpointer g_class)
|
||||||
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
|
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
|
||||||
GstSignalProcessorClass *gsp_class = GST_SIGNAL_PROCESSOR_CLASS (g_class);
|
GstSignalProcessorClass *gsp_class = GST_SIGNAL_PROCESSOR_CLASS (g_class);
|
||||||
GstElementDetails *details;
|
GstElementDetails *details;
|
||||||
|
GstAudioChannelPosition mono_position = GST_AUDIO_CHANNEL_POSITION_FRONT_MONO;
|
||||||
LADSPA_Descriptor *desc;
|
LADSPA_Descriptor *desc;
|
||||||
guint j, audio_in_count, audio_out_count, control_in_count, control_out_count;
|
guint j, audio_in_count, audio_out_count, control_in_count, control_out_count;
|
||||||
gchar *klass_tags;
|
gchar *klass_tags;
|
||||||
|
@ -125,10 +126,10 @@ gst_ladspa_base_init (gpointer g_class)
|
||||||
|
|
||||||
if (LADSPA_IS_PORT_INPUT (p))
|
if (LADSPA_IS_PORT_INPUT (p))
|
||||||
gst_signal_processor_class_add_pad_template (gsp_class, name,
|
gst_signal_processor_class_add_pad_template (gsp_class, name,
|
||||||
GST_PAD_SINK, gsp_class->num_audio_in++, 1);
|
GST_PAD_SINK, gsp_class->num_audio_in++, 1, &mono_position);
|
||||||
else
|
else
|
||||||
gst_signal_processor_class_add_pad_template (gsp_class, name,
|
gst_signal_processor_class_add_pad_template (gsp_class, name,
|
||||||
GST_PAD_SRC, gsp_class->num_audio_out++, 1);
|
GST_PAD_SRC, gsp_class->num_audio_out++, 1, &mono_position);
|
||||||
|
|
||||||
g_free (name);
|
g_free (name);
|
||||||
} else if (LADSPA_IS_PORT_CONTROL (p)) {
|
} else if (LADSPA_IS_PORT_CONTROL (p)) {
|
||||||
|
|
132
ext/lv2/gstlv2.c
132
ext/lv2/gstlv2.c
|
@ -40,6 +40,7 @@
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <gst/audio/audio.h>
|
#include <gst/audio/audio.h>
|
||||||
#include <gst/controller/gstcontroller.h>
|
#include <gst/controller/gstcontroller.h>
|
||||||
|
#include <gst/audio/multichannel.h>
|
||||||
|
|
||||||
#include "gstlv2.h"
|
#include "gstlv2.h"
|
||||||
#include <slv2/slv2.h>
|
#include <slv2/slv2.h>
|
||||||
|
@ -67,8 +68,21 @@ SLV2Value integer_prop;
|
||||||
SLV2Value toggled_prop;
|
SLV2Value toggled_prop;
|
||||||
SLV2Value in_place_broken_pred;
|
SLV2Value in_place_broken_pred;
|
||||||
SLV2Value in_group_pred;
|
SLV2Value in_group_pred;
|
||||||
|
SLV2Value has_role_pred;
|
||||||
SLV2Value lv2_symbol_pred;
|
SLV2Value lv2_symbol_pred;
|
||||||
|
|
||||||
|
SLV2Value center_role;
|
||||||
|
SLV2Value left_role;
|
||||||
|
SLV2Value right_role;
|
||||||
|
SLV2Value rear_center_role;
|
||||||
|
SLV2Value rear_left_role;
|
||||||
|
SLV2Value rear_right_role;
|
||||||
|
SLV2Value lfe_role;
|
||||||
|
SLV2Value center_left_role;
|
||||||
|
SLV2Value center_right_role;
|
||||||
|
SLV2Value side_left_role;
|
||||||
|
SLV2Value side_right_role;
|
||||||
|
|
||||||
static GstSignalProcessorClass *parent_class;
|
static GstSignalProcessorClass *parent_class;
|
||||||
|
|
||||||
static GstPlugin *gst_lv2_plugin;
|
static GstPlugin *gst_lv2_plugin;
|
||||||
|
@ -76,6 +90,51 @@ static GstPlugin *gst_lv2_plugin;
|
||||||
GST_DEBUG_CATEGORY_STATIC (lv2_debug);
|
GST_DEBUG_CATEGORY_STATIC (lv2_debug);
|
||||||
#define GST_CAT_DEFAULT lv2_debug
|
#define GST_CAT_DEFAULT lv2_debug
|
||||||
|
|
||||||
|
/** Convert an LV2 port role to a Gst channel positon */
|
||||||
|
static GstAudioChannelPosition
|
||||||
|
gst_lv2_role_to_position (SLV2Value role)
|
||||||
|
{
|
||||||
|
/* Front. Mono and left/right are mututally exclusive */
|
||||||
|
if (slv2_value_equals (role, center_role)) {
|
||||||
|
/** WARNING: If the group has only a single port, this must be changed to
|
||||||
|
* GST_AUDIO_CHANNEL_POSITION_FRONT_MONO. This can't be done by this
|
||||||
|
* function because this information isn't known at the time it is used.
|
||||||
|
*/
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER;
|
||||||
|
} else if (slv2_value_equals (role, left_role)) {
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
|
||||||
|
} else if (slv2_value_equals (role, right_role)) {
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
|
||||||
|
|
||||||
|
/* Rear. Left/right and center are mututally exclusive */
|
||||||
|
} else if (slv2_value_equals (role, rear_center_role)) {
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_REAR_CENTER;
|
||||||
|
} else if (slv2_value_equals (role, rear_left_role)) {
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_REAR_LEFT;
|
||||||
|
} else if (slv2_value_equals (role, rear_right_role)) {
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT;
|
||||||
|
|
||||||
|
/* Subwoofer/low-frequency-effects */
|
||||||
|
} else if (slv2_value_equals (role, lfe_role)) {
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_LFE;
|
||||||
|
|
||||||
|
/* Center front speakers. Center and left/right_of_center
|
||||||
|
* are mutually exclusive */
|
||||||
|
} else if (slv2_value_equals (role, center_left_role)) {
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
|
||||||
|
} else if (slv2_value_equals (role, center_right_role)) {
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
|
||||||
|
|
||||||
|
/* sides */
|
||||||
|
} else if (slv2_value_equals (role, side_left_role)) {
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT;
|
||||||
|
} else if (slv2_value_equals (role, side_right_role)) {
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
/** Find and return the group @a uri in @a groups, or NULL if not found */
|
/** Find and return the group @a uri in @a groups, or NULL if not found */
|
||||||
static GstLV2Group *
|
static GstLV2Group *
|
||||||
gst_lv2_class_find_group (GArray * groups, SLV2Value uri)
|
gst_lv2_class_find_group (GArray * groups, SLV2Value uri)
|
||||||
|
@ -87,6 +146,23 @@ gst_lv2_class_find_group (GArray * groups, SLV2Value uri)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GstAudioChannelPosition *
|
||||||
|
gst_lv2_build_positions (GstLV2Group * group)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
GstAudioChannelPosition *positions = NULL;
|
||||||
|
if (group->has_roles) {
|
||||||
|
positions = malloc (group->ports->len * sizeof (GstAudioChannelPosition));
|
||||||
|
for (i = 0; i < group->ports->len; ++i)
|
||||||
|
positions[i] = g_array_index (group->ports, GstLV2Port, i).position;
|
||||||
|
|
||||||
|
// Fix up mono groups (see WARNING above)
|
||||||
|
if (group->ports->len == 1)
|
||||||
|
positions[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_MONO;
|
||||||
|
}
|
||||||
|
return positions;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_lv2_base_init (gpointer g_class)
|
gst_lv2_base_init (gpointer g_class)
|
||||||
{
|
{
|
||||||
|
@ -98,6 +174,9 @@ gst_lv2_base_init (gpointer g_class)
|
||||||
SLV2Value val;
|
SLV2Value val;
|
||||||
SLV2Values values, sub_values;
|
SLV2Values values, sub_values;
|
||||||
GstLV2Group *group = NULL;
|
GstLV2Group *group = NULL;
|
||||||
|
GstAudioChannelPosition position = GST_AUDIO_CHANNEL_POSITION_INVALID;
|
||||||
|
GstAudioChannelPosition mono_position = GST_AUDIO_CHANNEL_POSITION_FRONT_MONO;
|
||||||
|
GstAudioChannelPosition *positions = NULL;
|
||||||
guint j, in_pad_index = 0, out_pad_index = 0;
|
guint j, in_pad_index = 0, out_pad_index = 0;
|
||||||
gchar *klass_tags;
|
gchar *klass_tags;
|
||||||
|
|
||||||
|
@ -139,6 +218,7 @@ gst_lv2_base_init (gpointer g_class)
|
||||||
g.uri = slv2_value_duplicate (group_uri);
|
g.uri = slv2_value_duplicate (group_uri);
|
||||||
g.pad = is_input ? in_pad_index++ : out_pad_index++;
|
g.pad = is_input ? in_pad_index++ : out_pad_index++;
|
||||||
g.ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
|
g.ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
|
||||||
|
g.has_roles = TRUE;
|
||||||
sub_values = slv2_plugin_get_value_for_subject (lv2plugin, group_uri,
|
sub_values = slv2_plugin_get_value_for_subject (lv2plugin, group_uri,
|
||||||
lv2_symbol_pred);
|
lv2_symbol_pred);
|
||||||
if (slv2_values_size (sub_values) > 0)
|
if (slv2_values_size (sub_values) > 0)
|
||||||
|
@ -151,6 +231,19 @@ gst_lv2_base_init (gpointer g_class)
|
||||||
group = &g_array_index (groups, GstLV2Group, groups->len - 1);
|
group = &g_array_index (groups, GstLV2Group, groups->len - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
position = GST_AUDIO_CHANNEL_POSITION_INVALID;
|
||||||
|
sub_values = slv2_port_get_value (lv2plugin, port, has_role_pred);
|
||||||
|
if (slv2_values_size (sub_values) > 0) {
|
||||||
|
SLV2Value role = slv2_values_get_at (sub_values, 0);
|
||||||
|
position = gst_lv2_role_to_position (role);
|
||||||
|
slv2_values_free (sub_values);
|
||||||
|
}
|
||||||
|
if (position != GST_AUDIO_CHANNEL_POSITION_INVALID) {
|
||||||
|
desc.position = position;
|
||||||
|
} else {
|
||||||
|
group->has_roles = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
g_array_append_val (group->ports, desc);
|
g_array_append_val (group->ports, desc);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -184,17 +277,35 @@ gst_lv2_base_init (gpointer g_class)
|
||||||
/* add input group pad templates */
|
/* add input group pad templates */
|
||||||
for (j = 0; j < gsp_class->num_group_in; ++j) {
|
for (j = 0; j < gsp_class->num_group_in; ++j) {
|
||||||
group = &g_array_index (klass->in_groups, GstLV2Group, j);
|
group = &g_array_index (klass->in_groups, GstLV2Group, j);
|
||||||
|
if (group->has_roles) {
|
||||||
|
positions = gst_lv2_build_positions (group);
|
||||||
|
}
|
||||||
|
|
||||||
gst_signal_processor_class_add_pad_template (gsp_class,
|
gst_signal_processor_class_add_pad_template (gsp_class,
|
||||||
slv2_value_as_string (group->symbol),
|
slv2_value_as_string (group->symbol),
|
||||||
GST_PAD_SINK, j, group->ports->len);
|
GST_PAD_SINK, j, group->ports->len, positions);
|
||||||
|
|
||||||
|
if (group->has_roles) {
|
||||||
|
free (positions);
|
||||||
|
positions = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* add output group pad templates */
|
/* add output group pad templates */
|
||||||
for (j = 0; j < gsp_class->num_group_out; ++j) {
|
for (j = 0; j < gsp_class->num_group_out; ++j) {
|
||||||
group = &g_array_index (klass->out_groups, GstLV2Group, j);
|
group = &g_array_index (klass->out_groups, GstLV2Group, j);
|
||||||
|
if (group->has_roles) {
|
||||||
|
positions = gst_lv2_build_positions (group);
|
||||||
|
}
|
||||||
|
|
||||||
gst_signal_processor_class_add_pad_template (gsp_class,
|
gst_signal_processor_class_add_pad_template (gsp_class,
|
||||||
slv2_value_as_string (group->symbol),
|
slv2_value_as_string (group->symbol),
|
||||||
GST_PAD_SRC, j, group->ports->len);
|
GST_PAD_SRC, j, group->ports->len, positions);
|
||||||
|
|
||||||
|
if (group->has_roles) {
|
||||||
|
free (positions);
|
||||||
|
positions = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* add non-grouped input port pad templates */
|
/* add non-grouped input port pad templates */
|
||||||
|
@ -205,7 +316,7 @@ gst_lv2_base_init (gpointer g_class)
|
||||||
const gchar *name =
|
const gchar *name =
|
||||||
slv2_value_as_string (slv2_port_get_symbol (lv2plugin, port));
|
slv2_value_as_string (slv2_port_get_symbol (lv2plugin, port));
|
||||||
gst_signal_processor_class_add_pad_template (gsp_class, name, GST_PAD_SINK,
|
gst_signal_processor_class_add_pad_template (gsp_class, name, GST_PAD_SINK,
|
||||||
j, 1);
|
j, 1, &mono_position);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* add non-grouped output port pad templates */
|
/* add non-grouped output port pad templates */
|
||||||
|
@ -216,7 +327,7 @@ gst_lv2_base_init (gpointer g_class)
|
||||||
const gchar *name =
|
const gchar *name =
|
||||||
slv2_value_as_string (slv2_port_get_symbol (lv2plugin, port));
|
slv2_value_as_string (slv2_port_get_symbol (lv2plugin, port));
|
||||||
gst_signal_processor_class_add_pad_template (gsp_class, name, GST_PAD_SINK,
|
gst_signal_processor_class_add_pad_template (gsp_class, name, GST_PAD_SINK,
|
||||||
j, 1);
|
j, 1, &mono_position);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* construct the element details struct */
|
/* construct the element details struct */
|
||||||
|
@ -651,8 +762,21 @@ plugin_init (GstPlugin * plugin)
|
||||||
toggled_prop = slv2_value_new_uri (world, NS_LV2 "toggled");
|
toggled_prop = slv2_value_new_uri (world, NS_LV2 "toggled");
|
||||||
in_place_broken_pred = slv2_value_new_uri (world, NS_LV2 "inPlaceBroken");
|
in_place_broken_pred = slv2_value_new_uri (world, NS_LV2 "inPlaceBroken");
|
||||||
in_group_pred = slv2_value_new_uri (world, NS_PG "inGroup");
|
in_group_pred = slv2_value_new_uri (world, NS_PG "inGroup");
|
||||||
|
has_role_pred = slv2_value_new_uri (world, NS_PG "role");
|
||||||
lv2_symbol_pred = slv2_value_new_string (world, NS_LV2 "symbol");
|
lv2_symbol_pred = slv2_value_new_string (world, NS_LV2 "symbol");
|
||||||
|
|
||||||
|
center_role = slv2_value_new_uri (world, NS_PG "centerChannel");
|
||||||
|
left_role = slv2_value_new_uri (world, NS_PG "leftChannel");
|
||||||
|
right_role = slv2_value_new_uri (world, NS_PG "rightChannel");
|
||||||
|
rear_center_role = slv2_value_new_uri (world, NS_PG "rearCenterChannel");
|
||||||
|
rear_left_role = slv2_value_new_uri (world, NS_PG "rearLeftChannel");
|
||||||
|
rear_right_role = slv2_value_new_uri (world, NS_PG "rearRightChannel");
|
||||||
|
lfe_role = slv2_value_new_uri (world, NS_PG "lfeChannel");
|
||||||
|
center_left_role = slv2_value_new_uri (world, NS_PG "centerLeftChannel");
|
||||||
|
center_right_role = slv2_value_new_uri (world, NS_PG "centerRightChannel");
|
||||||
|
side_left_role = slv2_value_new_uri (world, NS_PG "sideLeftChannel");
|
||||||
|
side_right_role = slv2_value_new_uri (world, NS_PG "sideRightChannel");
|
||||||
|
|
||||||
parent_class = g_type_class_ref (GST_TYPE_SIGNAL_PROCESSOR);
|
parent_class = g_type_class_ref (GST_TYPE_SIGNAL_PROCESSOR);
|
||||||
|
|
||||||
gst_lv2_plugin = plugin;
|
gst_lv2_plugin = plugin;
|
||||||
|
|
|
@ -64,11 +64,14 @@ struct _GstLV2Group {
|
||||||
guint pad; /**< Gst pad index */
|
guint pad; /**< Gst pad index */
|
||||||
SLV2Value symbol; /**< Gst pad name / LV2 group symbol */
|
SLV2Value symbol; /**< Gst pad name / LV2 group symbol */
|
||||||
GArray *ports; /**< Array of GstLV2Port */
|
GArray *ports; /**< Array of GstLV2Port */
|
||||||
|
gboolean has_roles; /**< TRUE iff all ports have a known role */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstLV2Port {
|
struct _GstLV2Port {
|
||||||
gint index; /**< LV2 port index (on LV2 plugin) */
|
gint index; /**< LV2 port index (on LV2 plugin) */
|
||||||
gint pad; /**< Gst pad index (iff not part of a group) */
|
gint pad; /**< Gst pad index (iff not part of a group) */
|
||||||
|
SLV2Value role; /**< Channel position / port role */
|
||||||
|
GstAudioChannelPosition position; /**< Channel position */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstLV2Class {
|
struct _GstLV2Class {
|
||||||
|
|
|
@ -90,11 +90,14 @@ gst_signal_processor_pad_template_get_type (void)
|
||||||
* @name: pad name
|
* @name: pad name
|
||||||
* @direction: pad direction (src/sink)
|
* @direction: pad direction (src/sink)
|
||||||
* @index: index for the pad per direction (starting from 0)
|
* @index: index for the pad per direction (starting from 0)
|
||||||
|
* @channels: number of channels in this pad
|
||||||
|
* @positions: array of channel positions in order
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
gst_signal_processor_class_add_pad_template (GstSignalProcessorClass * klass,
|
gst_signal_processor_class_add_pad_template (GstSignalProcessorClass * klass,
|
||||||
const gchar * name, GstPadDirection direction, guint index, guint channels)
|
const gchar * name, GstPadDirection direction, guint index, guint channels,
|
||||||
|
const GstAudioChannelPosition * pos)
|
||||||
{
|
{
|
||||||
GstPadTemplate *new;
|
GstPadTemplate *new;
|
||||||
GstCaps *caps;
|
GstCaps *caps;
|
||||||
|
@ -107,6 +110,9 @@ gst_signal_processor_class_add_pad_template (GstSignalProcessorClass * klass,
|
||||||
"endianness", G_TYPE_INT, G_BYTE_ORDER,
|
"endianness", G_TYPE_INT, G_BYTE_ORDER,
|
||||||
"width", G_TYPE_INT, 32, "channels", G_TYPE_INT, channels, NULL);
|
"width", G_TYPE_INT, 32, "channels", G_TYPE_INT, channels, NULL);
|
||||||
|
|
||||||
|
if (pos)
|
||||||
|
gst_audio_set_caps_channel_positions_list (caps, pos, channels);
|
||||||
|
|
||||||
new = g_object_new (gst_signal_processor_pad_template_get_type (),
|
new = g_object_new (gst_signal_processor_pad_template_get_type (),
|
||||||
"name", name, "name-template", name,
|
"name", name, "name-template", name,
|
||||||
"direction", direction, "presence", GST_PAD_ALWAYS, "caps", caps, NULL);
|
"direction", direction, "presence", GST_PAD_ALWAYS, "caps", caps, NULL);
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#define __GST_SIGNAL_PROCESSOR_H__
|
#define __GST_SIGNAL_PROCESSOR_H__
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
|
#include <gst/audio/multichannel.h>
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
@ -131,7 +132,8 @@ struct _GstSignalProcessorClass {
|
||||||
|
|
||||||
GType gst_signal_processor_get_type (void);
|
GType gst_signal_processor_get_type (void);
|
||||||
void gst_signal_processor_class_add_pad_template (GstSignalProcessorClass *klass,
|
void gst_signal_processor_class_add_pad_template (GstSignalProcessorClass *klass,
|
||||||
const gchar *name, GstPadDirection direction, guint index, guint channels);
|
const gchar *name, GstPadDirection direction, guint index, guint channels,
|
||||||
|
const GstAudioChannelPosition *pos);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue