mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-03 15:06:34 +00:00
b415d7b34f
This helps in cases where raw audio data is being delivered, but the buffers do not come in sample aligned sizes. The new unalignedaudioparse bin can be autoplugged and configures an internal audioparse element to align the data. audioparse itself gets support for audio/x-unaligned-raw input caps; the output caps then contain the same information, except that the name is changed to audio/x-raw (since audioparse aligns the data). This ensures that souphttpsrc ! audioparse still works. https://bugzilla.gnome.org/show_bug.cgi?id=689460
457 lines
14 KiB
C
457 lines
14 KiB
C
/* GStreamer
|
|
* Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
|
|
*
|
|
* gstaudioparse.c:
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
/**
|
|
* SECTION:element-audioparse
|
|
*
|
|
* Converts a byte stream into audio frames.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
/* FIXME 0.11: suppress warnings for deprecated API such as g_value_array stuff
|
|
* for now with newer GLib versions (>= 2.31.0) */
|
|
#define GLIB_DISABLE_DEPRECATION_WARNINGS
|
|
|
|
#include "gstaudioparse.h"
|
|
#include "unalignedaudio.h"
|
|
|
|
#include <string.h>
|
|
|
|
typedef enum
|
|
{
|
|
GST_AUDIO_PARSE_FORMAT_RAW,
|
|
GST_AUDIO_PARSE_FORMAT_MULAW,
|
|
GST_AUDIO_PARSE_FORMAT_ALAW
|
|
} GstAudioParseFormat;
|
|
|
|
typedef enum
|
|
{
|
|
GST_AUDIO_PARSE_ENDIANNESS_LITTLE = 1234,
|
|
GST_AUDIO_PARSE_ENDIANNESS_BIG = 4321
|
|
} GstAudioParseEndianness;
|
|
|
|
static void gst_audio_parse_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec);
|
|
static void gst_audio_parse_get_property (GObject * object, guint prop_id,
|
|
GValue * value, GParamSpec * pspec);
|
|
static void gst_audio_parse_finalize (GObject * object);
|
|
|
|
static GstCaps *gst_audio_parse_get_caps (GstRawParse * rp);
|
|
|
|
static void gst_audio_parse_update_frame_size (GstAudioParse * ap);
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (gst_audio_parse_debug);
|
|
#define GST_CAT_DEFAULT gst_audio_parse_debug
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_FORMAT,
|
|
PROP_RAW_FORMAT,
|
|
PROP_RATE,
|
|
PROP_CHANNELS,
|
|
PROP_INTERLEAVED,
|
|
PROP_CHANNEL_POSITIONS,
|
|
PROP_USE_SINK_CAPS
|
|
};
|
|
|
|
#define GST_AUDIO_PARSE_FORMAT (gst_audio_parse_format_get_type ())
|
|
static GType
|
|
gst_audio_parse_format_get_type (void)
|
|
{
|
|
static GType audio_parse_format_type = 0;
|
|
static const GEnumValue format_types[] = {
|
|
{GST_AUDIO_PARSE_FORMAT_RAW, "Raw", "raw"},
|
|
{GST_AUDIO_PARSE_FORMAT_ALAW, "A-Law", "alaw"},
|
|
{GST_AUDIO_PARSE_FORMAT_MULAW, "\302\265-Law", "mulaw"},
|
|
{0, NULL, NULL}
|
|
};
|
|
|
|
if (!audio_parse_format_type) {
|
|
audio_parse_format_type =
|
|
g_enum_register_static ("GstAudioParseFormat", format_types);
|
|
}
|
|
|
|
return audio_parse_format_type;
|
|
}
|
|
|
|
|
|
#define gst_audio_parse_parent_class parent_class
|
|
G_DEFINE_TYPE (GstAudioParse, gst_audio_parse, GST_TYPE_RAW_PARSE);
|
|
|
|
static void
|
|
gst_audio_parse_class_init (GstAudioParseClass * klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
|
|
GstRawParseClass *rp_class = GST_RAW_PARSE_CLASS (klass);
|
|
GstCaps *caps;
|
|
|
|
gobject_class->set_property = gst_audio_parse_set_property;
|
|
gobject_class->get_property = gst_audio_parse_get_property;
|
|
gobject_class->finalize = gst_audio_parse_finalize;
|
|
|
|
rp_class->get_caps = gst_audio_parse_get_caps;
|
|
|
|
g_object_class_install_property (gobject_class, PROP_FORMAT,
|
|
g_param_spec_enum ("format", "Format",
|
|
"Format of audio samples in raw stream", GST_AUDIO_PARSE_FORMAT,
|
|
GST_AUDIO_PARSE_FORMAT_RAW,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_RAW_FORMAT,
|
|
g_param_spec_enum ("raw-format", "Raw Format",
|
|
"Format of audio samples in raw stream", GST_TYPE_AUDIO_FORMAT,
|
|
GST_AUDIO_FORMAT_S16, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_RATE,
|
|
g_param_spec_int ("rate", "Rate", "Rate of audio samples in raw stream",
|
|
1, INT_MAX, 44100, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_CHANNELS,
|
|
g_param_spec_int ("channels", "Channels",
|
|
"Number of channels in raw stream", 1, 64, 2,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_INTERLEAVED,
|
|
g_param_spec_boolean ("interleaved", "Interleaved Layout",
|
|
"True if audio has interleaved layout", TRUE,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_CHANNEL_POSITIONS,
|
|
g_param_spec_value_array ("channel-positions", "Channel positions",
|
|
"Channel positions used on the output",
|
|
g_param_spec_enum ("channel-position", "Channel position",
|
|
"Channel position of the n-th input",
|
|
GST_TYPE_AUDIO_CHANNEL_POSITION,
|
|
GST_AUDIO_CHANNEL_POSITION_NONE,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS),
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_USE_SINK_CAPS,
|
|
g_param_spec_boolean ("use-sink-caps", "Use sink caps",
|
|
"Use the sink caps for the format, only performing timestamping",
|
|
FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
gst_element_class_set_static_metadata (gstelement_class, "Audio Parse",
|
|
"Filter/Audio",
|
|
"Converts stream into audio frames",
|
|
"Sebastian Dröge <sebastian.droege@collabora.co.uk>");
|
|
|
|
caps = gst_caps_from_string (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL)
|
|
", layout = (string) { interleaved, non-interleaved }; "
|
|
GST_UNALIGNED_RAW_AUDIO_CAPS "; "
|
|
"audio/x-alaw, rate=(int)[1,MAX], channels=(int)[1,MAX]; "
|
|
"audio/x-mulaw, rate=(int)[1,MAX], channels=(int)[1,MAX]");
|
|
|
|
gst_raw_parse_class_set_src_pad_template (rp_class, caps);
|
|
gst_raw_parse_class_set_multiple_frames_per_buffer (rp_class, TRUE);
|
|
gst_caps_unref (caps);
|
|
|
|
GST_DEBUG_CATEGORY_INIT (gst_audio_parse_debug, "audioparse", 0,
|
|
"audioparse element");
|
|
}
|
|
|
|
static void
|
|
gst_audio_parse_init (GstAudioParse * ap)
|
|
{
|
|
ap->format = GST_AUDIO_PARSE_FORMAT_RAW;
|
|
ap->raw_format = GST_AUDIO_FORMAT_S16;
|
|
ap->channels = 2;
|
|
ap->interleaved = TRUE;
|
|
|
|
gst_audio_parse_update_frame_size (ap);
|
|
gst_raw_parse_set_fps (GST_RAW_PARSE (ap), 44100, 1);
|
|
}
|
|
|
|
static void
|
|
gst_audio_parse_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstAudioParse *ap = GST_AUDIO_PARSE (object);
|
|
|
|
g_return_if_fail (!gst_raw_parse_is_negotiated (GST_RAW_PARSE (ap)));
|
|
|
|
switch (prop_id) {
|
|
case PROP_FORMAT:
|
|
ap->format = g_value_get_enum (value);
|
|
break;
|
|
case PROP_RAW_FORMAT:
|
|
ap->raw_format = g_value_get_enum (value);
|
|
break;
|
|
case PROP_RATE:
|
|
gst_raw_parse_set_fps (GST_RAW_PARSE (ap), g_value_get_int (value), 1);
|
|
break;
|
|
case PROP_CHANNELS:
|
|
ap->channels = g_value_get_int (value);
|
|
break;
|
|
case PROP_INTERLEAVED:
|
|
ap->interleaved = g_value_get_boolean (value);
|
|
break;
|
|
case PROP_CHANNEL_POSITIONS:
|
|
if (ap->channel_positions)
|
|
g_value_array_free (ap->channel_positions);
|
|
|
|
ap->channel_positions = g_value_dup_boxed (value);
|
|
break;
|
|
case PROP_USE_SINK_CAPS:
|
|
ap->use_sink_caps = g_value_get_boolean (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
|
|
gst_audio_parse_update_frame_size (ap);
|
|
}
|
|
|
|
static void
|
|
gst_audio_parse_get_property (GObject * object, guint prop_id, GValue * value,
|
|
GParamSpec * pspec)
|
|
{
|
|
GstAudioParse *ap = GST_AUDIO_PARSE (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_FORMAT:
|
|
g_value_set_enum (value, ap->format);
|
|
break;
|
|
case PROP_RAW_FORMAT:
|
|
g_value_set_enum (value, ap->raw_format);
|
|
break;
|
|
case PROP_RATE:{
|
|
gint fps_n, fps_d;
|
|
|
|
gst_raw_parse_get_fps (GST_RAW_PARSE (ap), &fps_n, &fps_d);
|
|
g_value_set_int (value, fps_n);
|
|
break;
|
|
}
|
|
case PROP_CHANNELS:
|
|
g_value_set_int (value, ap->channels);
|
|
break;
|
|
case PROP_INTERLEAVED:
|
|
g_value_set_boolean (value, ap->interleaved);
|
|
break;
|
|
case PROP_CHANNEL_POSITIONS:
|
|
g_value_set_boxed (value, ap->channel_positions);
|
|
break;
|
|
case PROP_USE_SINK_CAPS:
|
|
g_value_set_boolean (value, ap->use_sink_caps);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_audio_parse_finalize (GObject * object)
|
|
{
|
|
GstAudioParse *ap = GST_AUDIO_PARSE (object);
|
|
|
|
if (ap->channel_positions) {
|
|
g_value_array_free (ap->channel_positions);
|
|
ap->channel_positions = NULL;
|
|
}
|
|
|
|
g_free (ap->channel_pos);
|
|
g_free (ap->channel_order);
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
void
|
|
gst_audio_parse_update_frame_size (GstAudioParse * ap)
|
|
{
|
|
gint framesize, width;
|
|
|
|
switch (ap->format) {
|
|
case GST_AUDIO_PARSE_FORMAT_ALAW:
|
|
case GST_AUDIO_PARSE_FORMAT_MULAW:
|
|
width = 8;
|
|
break;
|
|
case GST_AUDIO_PARSE_FORMAT_RAW:
|
|
default:
|
|
{
|
|
GstAudioInfo info;
|
|
|
|
gst_audio_info_init (&info);
|
|
/* rate, etc do not really matter here */
|
|
gst_audio_info_set_format (&info, ap->raw_format, 44100, ap->channels,
|
|
NULL);
|
|
width = GST_AUDIO_INFO_WIDTH (&info);
|
|
break;
|
|
}
|
|
}
|
|
|
|
framesize = (width / 8) * ap->channels;
|
|
|
|
gst_raw_parse_set_framesize (GST_RAW_PARSE (ap), framesize);
|
|
}
|
|
|
|
static GstAudioChannelPosition *
|
|
gst_audio_parse_get_channel_positions (GValueArray * positions)
|
|
{
|
|
gint i;
|
|
guint channels;
|
|
GstAudioChannelPosition *pos;
|
|
|
|
channels = positions->n_values;
|
|
pos = g_new (GstAudioChannelPosition, positions->n_values);
|
|
|
|
for (i = 0; i < channels; i++) {
|
|
GValue *v = g_value_array_get_nth (positions, i);
|
|
|
|
pos[i] = g_value_get_enum (v);
|
|
}
|
|
|
|
return pos;
|
|
}
|
|
|
|
static void
|
|
gst_audio_parse_setup_channel_positions (GstAudioParse * ap)
|
|
{
|
|
GstAudioChannelPosition *pos, *to;
|
|
|
|
g_free (ap->channel_pos);
|
|
g_free (ap->channel_order);
|
|
ap->channel_pos = NULL;
|
|
ap->channel_order = NULL;
|
|
|
|
if (!ap->channel_positions) {
|
|
GST_DEBUG_OBJECT (ap, "no channel positions");
|
|
/* implicit mapping for 1- and 2-channel audio is okay */
|
|
/* will come up with one in other cases also */
|
|
return;
|
|
}
|
|
|
|
pos = gst_audio_parse_get_channel_positions (ap->channel_positions);
|
|
if (ap->channels != ap->channel_positions->n_values ||
|
|
!gst_audio_check_valid_channel_positions (pos, ap->channels, FALSE)) {
|
|
GST_DEBUG_OBJECT (ap, "invalid channel position");
|
|
g_free (pos);
|
|
return;
|
|
}
|
|
|
|
/* ok, got something we can work with now */
|
|
to = g_new (GstAudioChannelPosition, ap->channels);
|
|
memcpy (to, pos, ap->channels * sizeof (to[0]));
|
|
gst_audio_channel_positions_to_valid_order (to, ap->channels);
|
|
|
|
ap->channel_pos = pos;
|
|
ap->channel_order = to;
|
|
}
|
|
|
|
static GstCaps *
|
|
gst_audio_parse_get_caps (GstRawParse * rp)
|
|
{
|
|
GstAudioParse *ap = GST_AUDIO_PARSE (rp);
|
|
GstCaps *caps, *ncaps;
|
|
GstAudioInfo info;
|
|
gint fps_n, fps_d;
|
|
const GValue *val;
|
|
|
|
if (ap->use_sink_caps) {
|
|
gint rate;
|
|
GstCaps *caps = gst_pad_get_current_caps (rp->sinkpad);
|
|
GstStructure *structure;
|
|
|
|
if (!caps) {
|
|
GST_WARNING_OBJECT (ap,
|
|
"Sink pad has no caps, but we were asked to use its caps");
|
|
return NULL;
|
|
}
|
|
|
|
/* For unaligned raw data, the output caps stay the same,
|
|
* except that audio/x-unaligned-raw becomes audio/x-raw,
|
|
* since audioparse aligns the sample data */
|
|
structure = gst_caps_get_structure (caps, 0);
|
|
if (gst_structure_has_name (structure, "audio/x-unaligned-raw")) {
|
|
caps = gst_caps_make_writable (caps);
|
|
structure = gst_caps_get_structure (caps, 0);
|
|
gst_structure_set_name (structure, "audio/x-raw");
|
|
}
|
|
|
|
if (!gst_audio_info_from_caps (&info, caps)) {
|
|
GST_WARNING_OBJECT (ap, "Failed to parse caps %" GST_PTR_FORMAT, caps);
|
|
gst_caps_unref (caps);
|
|
return NULL;
|
|
}
|
|
|
|
ap->format = GST_AUDIO_PARSE_FORMAT_RAW;
|
|
ap->raw_format = GST_AUDIO_INFO_FORMAT (&info);
|
|
ap->channels = GST_AUDIO_INFO_CHANNELS (&info);
|
|
ap->interleaved = info.layout == GST_AUDIO_LAYOUT_INTERLEAVED;
|
|
|
|
rate = GST_AUDIO_INFO_RATE (&info);
|
|
gst_raw_parse_set_fps (GST_RAW_PARSE (ap), rate, 1);
|
|
gst_audio_parse_update_frame_size (ap);
|
|
|
|
return caps;
|
|
}
|
|
|
|
gst_raw_parse_get_fps (rp, &fps_n, &fps_d);
|
|
gst_audio_parse_setup_channel_positions (ap);
|
|
|
|
/* yes, even when format not raw */
|
|
gst_audio_info_init (&info);
|
|
gst_audio_info_set_format (&info, ap->raw_format, fps_n, ap->channels,
|
|
ap->channel_order);
|
|
info.layout = ap->interleaved ? GST_AUDIO_LAYOUT_INTERLEAVED :
|
|
GST_AUDIO_LAYOUT_NON_INTERLEAVED;
|
|
caps = gst_audio_info_to_caps (&info);
|
|
|
|
switch (ap->format) {
|
|
case GST_AUDIO_PARSE_FORMAT_RAW:
|
|
break;
|
|
case GST_AUDIO_PARSE_FORMAT_ALAW:
|
|
ncaps = gst_caps_new_simple ("audio/x-alaw",
|
|
"rate", G_TYPE_INT, fps_n,
|
|
"channels", G_TYPE_INT, ap->channels, NULL);
|
|
/* pick mask stuff from faked raw format */
|
|
val = gst_structure_get_value (gst_caps_get_structure (caps, 0),
|
|
"channel-mask");
|
|
if (val)
|
|
gst_caps_set_value (ncaps, "channel-mask", val);
|
|
gst_caps_unref (caps);
|
|
caps = ncaps;
|
|
break;
|
|
case GST_AUDIO_PARSE_FORMAT_MULAW:
|
|
ncaps = gst_caps_new_simple ("audio/x-mulaw",
|
|
"rate", G_TYPE_INT, fps_n,
|
|
"channels", G_TYPE_INT, ap->channels, NULL);
|
|
/* pick mask stuff from faked raw format */
|
|
val = gst_structure_get_value (gst_caps_get_structure (caps, 0),
|
|
"channel-mask");
|
|
if (val)
|
|
gst_caps_set_value (ncaps, "channel-mask", val);
|
|
gst_caps_unref (caps);
|
|
caps = ncaps;
|
|
break;
|
|
default:
|
|
caps = gst_caps_new_empty ();
|
|
GST_ERROR_OBJECT (rp, "unexpected format %d", ap->format);
|
|
break;
|
|
}
|
|
|
|
return caps;
|
|
}
|