/* GStreamer * Copyright (C) 2007 Sebastian Dröge * * 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 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 "); caps = gst_caps_from_string (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL) ", layout = (string) { interleaved, non-interleaved }; " "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); if (!caps) { GST_WARNING_OBJECT (ap, "Sink pad has no caps, but we were asked to use its caps"); return NULL; } 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; }