diff options
Diffstat (limited to 'gst/rawparse/gstrawaudioparse.c')
-rw-r--r-- | gst/rawparse/gstrawaudioparse.c | 1088 |
1 files changed, 1088 insertions, 0 deletions
diff --git a/gst/rawparse/gstrawaudioparse.c b/gst/rawparse/gstrawaudioparse.c new file mode 100644 index 000000000..d955f0e59 --- /dev/null +++ b/gst/rawparse/gstrawaudioparse.c @@ -0,0 +1,1088 @@ +/* GStreamer + * Copyright (C) <2016> Carlos Rafael Giani <dv at pseudoterminal dot org> + * + * 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-rawaudioparse + * + * This element parses incoming data as raw audio samples and timestamps it. + * It also handles seek queries in said raw audio data, and ensures that output + * buffers contain an integer number of samples, even if the input buffers don't. + * For example, with sample format S16LE and 2 channels, an input buffer of 411 + * bytes contains 102.75 samples. rawaudioparse will then output 102 samples + * (= 408 bytes) and keep the remaining 3 bytes. These will then be prepended to + * the next input data. + * + * The element implements the properties and sink caps configuration as specified + * in the #GstRawBaseParse documentation. The properties configuration can be + * modified by using the sample-rate, num-channels, channel-positions, format, + * and pcm-format properties. + * + * Currently, this parser supports raw data in a-law, mu-law, or linear PCM format. + * + * To facilitate operation with the unalignedaudioparse element, rawaudioparse + * supports the "audio/x-unaligned-raw" media type. This is treated identically to + * "audio/x-raw", except that it is used by source elements which do not guarantee + * that the buffers they push out are timestamped and contain an integer amount of + * samples (see the 411 bytes example above). By using a different media type, it + * is guaranteed that unalignedaudioparse is autoplugged, making sure that the + * autoplugged chain does not push unparsed content downstream. The source caps' + * media type with linear PCM data is always "audio/x-raw", even if the sink caps + * use "audio/x-unaligned-raw". + * + * The channel-positions property can be used to set explicit position information + * for each channel. If the array that is passed to this property does not match + * the number of channels indicated by num-channels, then said number of channels + * is updated to the array length. If channel-positions is NULL, then the default + * GStreamer positioning is used. This property is also useful for swapping left + * and right in a stereo signal for example. + * + * <refsect2> + * <title>Example pipelines</title> + * |[ + * gst-launch-1.0 souphttpsrc http://my-dlna-server/track.l16 \ + * rawaudioparse ! audioconvert ! audioresample ! autoaudiosink + * ]| Receive L16 data from a DLNA server, parse and timestamp it with + * rawaudioparse, and play it. use-sink-caps is set to true since souphttpsrc + * will set its source pad's caps to audio/x-unaligned-raw for the L16 stream. + * |[ + * gst-launch-1.0 filesrc location=audio.raw ! rawaudioparse use-sink-caps=false \ + * format=pcm pcm-format=s16le sample-rate=48000 num-channels=2 \ + * audioconvert ! audioresample ! autoaudiosink + * ]| Read raw data from a local file and parse it as PCM data with 48000 Hz sample + * rate, signed 16 bit integer samples, and 2 channels. use-sink-caps is set to + * false to ensure the property information is used and the parser does not expect + * audio/x-raw or audio/x-unaligned-raw caps. + * </refsect2> + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +/* FIXME: GValueArray is deprecated, but there is currently no viabla alternative + * See https://bugzilla.gnome.org/show_bug.cgi?id=667228 */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + +#include <string.h> +#include "gstrawaudioparse.h" +#include "unalignedaudio.h" + + +GST_DEBUG_CATEGORY_STATIC (raw_audio_parse_debug); +#define GST_CAT_DEFAULT raw_audio_parse_debug + + +enum +{ + PROP_0, + PROP_FORMAT, + PROP_PCM_FORMAT, + PROP_SAMPLE_RATE, + PROP_NUM_CHANNELS, + PROP_INTERLEAVED, + PROP_CHANNEL_POSITIONS +}; + + +#define DEFAULT_FORMAT GST_RAW_AUDIO_PARSE_FORMAT_PCM +#define DEFAULT_PCM_FORMAT GST_AUDIO_FORMAT_S16 +#define DEFAULT_SAMPLE_RATE 44100 +#define DEFAULT_NUM_CHANNELS 2 +#define DEFAULT_INTERLEAVED TRUE + + +#define GST_RAW_AUDIO_PARSE_CAPS \ + 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 ]; " + + +static GstStaticPadTemplate static_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_UNALIGNED_RAW_AUDIO_CAPS "; " GST_RAW_AUDIO_PARSE_CAPS) + ); + + +static GstStaticPadTemplate static_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_RAW_AUDIO_PARSE_CAPS) + ); + + +#define gst_raw_audio_parse_parent_class parent_class +G_DEFINE_TYPE (GstRawAudioParse, gst_raw_audio_parse, GST_TYPE_RAW_BASE_PARSE); + + +static void gst_raw_audio_parse_set_property (GObject * object, guint prop_id, + GValue const *value, GParamSpec * pspec); +static void gst_raw_audio_parse_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_raw_audio_parse_stop (GstBaseParse * parse); + +static gboolean gst_raw_audio_parse_set_current_config (GstRawBaseParse * + raw_base_parse, GstRawBaseParseConfig config); +static GstRawBaseParseConfig +gst_raw_audio_parse_get_current_config (GstRawBaseParse * raw_base_parse); +static gboolean gst_raw_audio_parse_set_config_from_caps (GstRawBaseParse * + raw_base_parse, GstRawBaseParseConfig config, GstCaps * caps); +static gboolean gst_raw_audio_parse_get_caps_from_config (GstRawBaseParse * + raw_base_parse, GstRawBaseParseConfig config, GstCaps ** caps); +static gsize gst_raw_audio_parse_get_config_frame_size (GstRawBaseParse * + raw_base_parse, GstRawBaseParseConfig config); +static gboolean gst_raw_audio_parse_is_config_ready (GstRawBaseParse * + raw_base_parse, GstRawBaseParseConfig config); +static gboolean gst_raw_audio_parse_process (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config, GstBuffer * in_data, gsize total_num_in_bytes, + gsize num_valid_in_bytes, GstBuffer ** processed_data); +static gboolean gst_raw_audio_parse_is_unit_format_supported (GstRawBaseParse * + raw_base_parse, GstFormat format); +static void gst_raw_audio_parse_get_units_per_second (GstRawBaseParse * + raw_base_parse, GstFormat format, GstRawBaseParseConfig config, + gsize * units_per_sec_n, gsize * units_per_sec_d); + +static gboolean gst_raw_audio_parse_is_using_sink_caps (GstRawAudioParse * + raw_audio_parse); +static GstRawAudioParseConfig + * gst_raw_audio_parse_get_config_ptr (GstRawAudioParse * raw_audio_parse, + GstRawBaseParseConfig config); + +static void gst_raw_audio_parse_init_config (GstRawAudioParseConfig * config); +static gboolean gst_raw_audio_parse_set_config_channels (GstRawAudioParseConfig + * config, guint num_channels, guint64 channel_mask, gboolean set_positions); +static gboolean +gst_raw_audio_parse_update_channel_reordering_flag (GstRawAudioParseConfig * + config); +static void gst_raw_audio_parse_update_config_bpf (GstRawAudioParseConfig * + config); +static gboolean gst_raw_audio_parse_caps_to_config (GstRawAudioParse * + raw_audio_parse, GstCaps * caps, GstRawAudioParseConfig * config); +static gboolean gst_raw_audio_parse_config_to_caps (GstRawAudioParse * + raw_audio_parse, GstCaps ** caps, GstRawAudioParseConfig * config); + + + +static void +gst_raw_audio_parse_class_init (GstRawAudioParseClass * klass) +{ + GObjectClass *object_class; + GstElementClass *element_class; + GstBaseParseClass *baseparse_class; + GstRawBaseParseClass *rawbaseparse_class; + + GST_DEBUG_CATEGORY_INIT (raw_audio_parse_debug, "rawaudioparse", 0, + "rawaudioparse element"); + + object_class = G_OBJECT_CLASS (klass); + element_class = GST_ELEMENT_CLASS (klass); + baseparse_class = GST_BASE_PARSE_CLASS (klass); + rawbaseparse_class = GST_RAW_BASE_PARSE_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&static_sink_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&static_src_template)); + + object_class->set_property = + GST_DEBUG_FUNCPTR (gst_raw_audio_parse_set_property); + object_class->get_property = + GST_DEBUG_FUNCPTR (gst_raw_audio_parse_get_property); + + baseparse_class->stop = GST_DEBUG_FUNCPTR (gst_raw_audio_parse_stop); + + rawbaseparse_class->set_current_config = + GST_DEBUG_FUNCPTR (gst_raw_audio_parse_set_current_config); + rawbaseparse_class->get_current_config = + GST_DEBUG_FUNCPTR (gst_raw_audio_parse_get_current_config); + rawbaseparse_class->set_config_from_caps = + GST_DEBUG_FUNCPTR (gst_raw_audio_parse_set_config_from_caps); + rawbaseparse_class->get_caps_from_config = + GST_DEBUG_FUNCPTR (gst_raw_audio_parse_get_caps_from_config); + rawbaseparse_class->get_config_frame_size = + GST_DEBUG_FUNCPTR (gst_raw_audio_parse_get_config_frame_size); + rawbaseparse_class->is_config_ready = + GST_DEBUG_FUNCPTR (gst_raw_audio_parse_is_config_ready); + rawbaseparse_class->process = GST_DEBUG_FUNCPTR (gst_raw_audio_parse_process); + rawbaseparse_class->is_unit_format_supported = + GST_DEBUG_FUNCPTR (gst_raw_audio_parse_is_unit_format_supported); + rawbaseparse_class->get_units_per_second = + GST_DEBUG_FUNCPTR (gst_raw_audio_parse_get_units_per_second); + + g_object_class_install_property (object_class, + PROP_FORMAT, + g_param_spec_enum ("format", + "Format", + "Format of the raw audio stream", + gst_raw_audio_parse_format_get_type (), + GST_RAW_AUDIO_PARSE_FORMAT_PCM, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + g_object_class_install_property (object_class, + PROP_PCM_FORMAT, + g_param_spec_enum ("pcm-format", + "PCM format", + "Format of audio samples in PCM stream (ignored if format property is not set to pcm)", + GST_TYPE_AUDIO_FORMAT, + GST_RAW_AUDIO_PARSE_FORMAT_PCM, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + g_object_class_install_property (object_class, + PROP_SAMPLE_RATE, + g_param_spec_int ("sample-rate", + "Sample rate", + "Rate of audio samples in raw stream", + 1, INT_MAX, + DEFAULT_SAMPLE_RATE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + g_object_class_install_property (object_class, + PROP_NUM_CHANNELS, + g_param_spec_int ("num-channels", + "Number of channels", + "Number of channels in raw stream", + 1, INT_MAX, + DEFAULT_NUM_CHANNELS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + g_object_class_install_property (object_class, + PROP_INTERLEAVED, + g_param_spec_boolean ("interleaved", + "Interleaved layout", + "True if audio has interleaved layout", + DEFAULT_INTERLEAVED, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + g_object_class_install_property (object_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) + ); + + gst_element_class_set_static_metadata (element_class, + "rawaudioparse", + "Codec/Parser/Audio", + "Converts unformatted data streams into timestamped raw audio frames", + "Carlos Rafael Giani <dv@pseudoterminal.org>"); +} + + +static void +gst_raw_audio_parse_init (GstRawAudioParse * raw_audio_parse) +{ + /* Setup configs and select which one shall be the current one from the start. */ + gst_raw_audio_parse_init_config (&(raw_audio_parse->properties_config)); + gst_raw_audio_parse_init_config (&(raw_audio_parse->sink_caps_config)); + /* As required by GstRawBaseParse, ensure that the current configuration + * is initially set to be the properties config */ + raw_audio_parse->current_config = &(raw_audio_parse->properties_config); + + /* Properties config must be valid from the start, so set its ready value + * to TRUE, and make sure its bpf value is valid. */ + raw_audio_parse->properties_config.ready = TRUE; + gst_raw_audio_parse_update_config_bpf (&(raw_audio_parse->properties_config)); +} + + +static void +gst_raw_audio_parse_set_property (GObject * object, guint prop_id, + GValue const *value, GParamSpec * pspec) +{ + GstBaseParse *base_parse = GST_BASE_PARSE (object); + GstRawBaseParse *raw_base_parse = GST_RAW_BASE_PARSE (object); + GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (object); + + /* All properties are handled similarly: + * - if the new value is the same as the current value, nothing is done + * - the parser lock is held while the new value is set + * - if the properties config is the current config, the source caps are + * invalidated to ensure that the code in handle_frame pushes a new CAPS + * event out + * - properties that affect the bpf value call the function to update + * the bpf and also call gst_base_parse_set_min_frame_size() to ensure + * that the minimum frame size can hold 1 frame (= one sample for each + * channel) + */ + + switch (prop_id) { + case PROP_FORMAT: + { + GstRawAudioParseFormat new_format = g_value_get_enum (value); + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + if (new_format != raw_audio_parse->properties_config.format) { + raw_audio_parse->properties_config.format = new_format; + gst_raw_audio_parse_update_config_bpf (& + (raw_audio_parse->properties_config)); + + if (!gst_raw_audio_parse_is_using_sink_caps (raw_audio_parse)) { + gst_raw_base_parse_invalidate_src_caps (raw_base_parse); + gst_base_parse_set_min_frame_size (base_parse, + raw_audio_parse->properties_config.bpf); + } + } + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + } + + case PROP_PCM_FORMAT: + { + GstAudioFormat new_pcm_format = g_value_get_enum (value); + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + if (new_pcm_format != raw_audio_parse->properties_config.pcm_format) { + raw_audio_parse->properties_config.pcm_format = new_pcm_format; + gst_raw_audio_parse_update_config_bpf (& + (raw_audio_parse->properties_config)); + + if (!gst_raw_audio_parse_is_using_sink_caps (raw_audio_parse)) { + gst_raw_base_parse_invalidate_src_caps (raw_base_parse); + gst_base_parse_set_min_frame_size (base_parse, + raw_audio_parse->properties_config.bpf); + } + } + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + } + + case PROP_SAMPLE_RATE: + { + guint new_sample_rate = g_value_get_int (value); + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + if (new_sample_rate != raw_audio_parse->properties_config.sample_rate) { + raw_audio_parse->properties_config.sample_rate = new_sample_rate; + + if (!gst_raw_audio_parse_is_using_sink_caps (raw_audio_parse)) + gst_raw_base_parse_invalidate_src_caps (raw_base_parse); + } + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + } + + case PROP_NUM_CHANNELS: + { + guint new_num_channels = g_value_get_int (value); + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + if (new_num_channels != raw_audio_parse->properties_config.num_channels) { + gst_raw_audio_parse_set_config_channels (& + (raw_audio_parse->properties_config), new_num_channels, 0, TRUE); + + raw_audio_parse->properties_config.num_channels = new_num_channels; + gst_raw_audio_parse_update_config_bpf (& + (raw_audio_parse->properties_config)); + + if (!gst_raw_audio_parse_is_using_sink_caps (raw_audio_parse)) { + gst_raw_base_parse_invalidate_src_caps (raw_base_parse); + gst_base_parse_set_min_frame_size (base_parse, + raw_audio_parse->properties_config.bpf); + } + } + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + } + + case PROP_INTERLEAVED: + { + gboolean new_interleaved = g_value_get_boolean (value); + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + if (new_interleaved != raw_audio_parse->properties_config.interleaved) { + raw_audio_parse->properties_config.interleaved = new_interleaved; + + if (!gst_raw_audio_parse_is_using_sink_caps (raw_audio_parse)) + gst_raw_base_parse_invalidate_src_caps (raw_base_parse); + } + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + } + + case PROP_CHANNEL_POSITIONS: + { + GValueArray *valarray = g_value_get_boxed (value); + GstRawAudioParseConfig *config = &(raw_audio_parse->properties_config); + + /* Sanity check - reject empty arrays */ + if ((valarray != NULL) && (valarray->n_values == 0)) { + GST_ELEMENT_ERROR (raw_audio_parse, LIBRARY, SETTINGS, + ("channel position property holds an empty array"), (NULL)); + break; + } + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + if ((valarray == NULL) && (config->num_channels > 0)) { + /* NULL value given, and number of channels is nonzero. + * Use the default GStreamer positioning. Call + * set_config_channels with the set_positions parameter + * set to TRUE to ensure the position values are filled. */ + gst_raw_audio_parse_set_config_channels (& + (raw_audio_parse->properties_config), config->num_channels, 0, + TRUE); + } else { + /* Non-NULL value given. Make sure the channel_positions + * array in the properties config has enough room, and that + * the num_channels value equals the array length. Then copy + * the values from the valarray to channel_positions, and + * produce a copy of that array in case its channel positions + * are not in a valid GStreamer order (to be able to apply + * channel reordering later). + */ + + guint i; + + if (valarray->n_values != config->num_channels) { + /* Call with set_positions == FALSE to ensure that + * the array is properly allocated but not filled + * (it is filled below) */ + gst_raw_audio_parse_set_config_channels (config, valarray->n_values, + 0, FALSE); + } + + for (i = 0; i < config->num_channels; ++i) { + GValue *val = g_value_array_get_nth (valarray, i); + config->channel_positions[i] = g_value_get_enum (val); + } + + gst_raw_audio_parse_update_channel_reordering_flag (config); + } + + gst_raw_audio_parse_update_config_bpf (& + (raw_audio_parse->properties_config)); + + if (!gst_raw_audio_parse_is_using_sink_caps (raw_audio_parse)) { + gst_raw_base_parse_invalidate_src_caps (raw_base_parse); + gst_base_parse_set_min_frame_size (base_parse, + raw_audio_parse->properties_config.bpf); + } + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + } + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +gst_raw_audio_parse_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (object); + + switch (prop_id) { + case PROP_FORMAT: + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + g_value_set_enum (value, raw_audio_parse->properties_config.format); + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + + case PROP_PCM_FORMAT: + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + g_value_set_enum (value, raw_audio_parse->properties_config.pcm_format); + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + + case PROP_SAMPLE_RATE: + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + g_value_set_int (value, raw_audio_parse->properties_config.sample_rate); + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + + case PROP_NUM_CHANNELS: + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + g_value_set_int (value, raw_audio_parse->properties_config.num_channels); + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + + case PROP_INTERLEAVED: + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + g_value_set_boolean (value, + raw_audio_parse->properties_config.interleaved); + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + + case PROP_CHANNEL_POSITIONS: + { + GstRawAudioParseConfig *config; + GValueArray *valarray; + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + valarray = NULL; + config = &(raw_audio_parse->properties_config); + + /* Copy channel positions into the valuearray */ + if (config->num_channels > 0) { + guint i; + GValue val = G_VALUE_INIT; + g_assert (config->channel_positions); + + g_value_init (&val, GST_TYPE_AUDIO_CHANNEL_POSITION); + valarray = g_value_array_new (config->num_channels); + + for (i = 0; i < config->num_channels; ++i) { + g_value_set_enum (&val, config->channel_positions[i]); + g_value_array_insert (valarray, i, &val); + } + + g_value_unset (&val); + } + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + + /* Pass on ownership to the value array, + * since we don't need it anymore */ + g_value_take_boxed (value, valarray); + + break; + } + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static gboolean +gst_raw_audio_parse_stop (GstBaseParse * parse) +{ + GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (parse); + + /* Sink caps config is not ready until caps come in. + * We are stopping processing, the element is being reset, + * so the config has to be un-readied. + * (Since the properties config is not depending on caps, + * its ready status is always TRUE.) */ + raw_audio_parse->sink_caps_config.ready = FALSE; + + return GST_BASE_PARSE_CLASS (parent_class)->stop (parse); +} + + +static gboolean +gst_raw_audio_parse_set_current_config (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config) +{ + GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (raw_base_parse); + + switch (config) { + case GST_RAW_BASE_PARSE_CONFIG_PROPERTIES: + raw_audio_parse->current_config = &(raw_audio_parse->properties_config); + break; + + case GST_RAW_BASE_PARSE_CONFIG_SINKCAPS: + raw_audio_parse->current_config = &(raw_audio_parse->sink_caps_config); + break; + + default: + g_assert_not_reached (); + } + + return TRUE; +} + + +static GstRawBaseParseConfig +gst_raw_audio_parse_get_current_config (GstRawBaseParse * raw_base_parse) +{ + GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (raw_base_parse); + return gst_raw_audio_parse_is_using_sink_caps (raw_audio_parse) ? + GST_RAW_BASE_PARSE_CONFIG_SINKCAPS : GST_RAW_BASE_PARSE_CONFIG_PROPERTIES; +} + + +static gboolean +gst_raw_audio_parse_set_config_from_caps (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config, GstCaps * caps) +{ + GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (raw_base_parse); + return gst_raw_audio_parse_caps_to_config (raw_audio_parse, caps, + gst_raw_audio_parse_get_config_ptr (raw_audio_parse, config)); +} + + +static gboolean +gst_raw_audio_parse_get_caps_from_config (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config, GstCaps ** caps) +{ + GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (raw_base_parse); + return gst_raw_audio_parse_config_to_caps (raw_audio_parse, caps, + gst_raw_audio_parse_get_config_ptr (raw_audio_parse, config)); +} + + +static gsize +gst_raw_audio_parse_get_config_frame_size (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config) +{ + GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (raw_base_parse); + return gst_raw_audio_parse_get_config_ptr (raw_audio_parse, config)->bpf; +} + + +static gboolean +gst_raw_audio_parse_is_config_ready (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config) +{ + GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (raw_base_parse); + return gst_raw_audio_parse_get_config_ptr (raw_audio_parse, config)->ready; +} + + +static gboolean +gst_raw_audio_parse_process (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config, GstBuffer * in_data, gsize total_num_in_bytes, + gsize num_valid_in_bytes, GstBuffer ** processed_data) +{ + GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (raw_base_parse); + GstRawAudioParseConfig *config_ptr = + gst_raw_audio_parse_get_config_ptr (raw_audio_parse, config); + + if ((config_ptr->format == GST_RAW_AUDIO_PARSE_FORMAT_PCM) + && config_ptr->needs_channel_reordering) { + /* Need to reorder samples, since they are in an invalid + * channel order. */ + + GstBuffer *outbuf; + + GST_LOG_OBJECT (raw_audio_parse, + "using %" G_GSIZE_FORMAT " bytes out of the %" G_GSIZE_FORMAT + " bytes from the input buffer with reordering", num_valid_in_bytes, + total_num_in_bytes); + + outbuf = + gst_buffer_copy_region (in_data, + GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS | + GST_BUFFER_COPY_META | GST_BUFFER_COPY_MEMORY, 0, num_valid_in_bytes); + + gst_audio_buffer_reorder_channels (outbuf, + config_ptr->pcm_format, + config_ptr->num_channels, + config_ptr->channel_positions, config_ptr->reordered_channel_positions); + + *processed_data = outbuf; + } else { + /* Nothing needs to be done with the sample data. + * Instruct the baseparse class to just take out_size bytes + * from the input buffer */ + + GST_LOG_OBJECT (raw_audio_parse, + "using %" G_GSIZE_FORMAT " bytes out of the %" G_GSIZE_FORMAT + " bytes from the input buffer without reordering", num_valid_in_bytes, + total_num_in_bytes); + + *processed_data = NULL; + } + + return TRUE; +} + + +static gboolean +gst_raw_audio_parse_is_unit_format_supported (G_GNUC_UNUSED GstRawBaseParse * + raw_base_parse, GstFormat format) +{ + switch (format) { + case GST_FORMAT_BYTES: + case GST_FORMAT_DEFAULT: + return TRUE; + default: + return FALSE; + } +} + + +static void +gst_raw_audio_parse_get_units_per_second (GstRawBaseParse * raw_base_parse, + GstFormat format, GstRawBaseParseConfig config, gsize * units_per_sec_n, + gsize * units_per_sec_d) +{ + GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (raw_base_parse); + GstRawAudioParseConfig *config_ptr = + gst_raw_audio_parse_get_config_ptr (raw_audio_parse, config); + + switch (format) { + case GST_FORMAT_BYTES: + *units_per_sec_n = config_ptr->sample_rate * config_ptr->bpf; + *units_per_sec_d = 1; + break; + + case GST_FORMAT_DEFAULT: + *units_per_sec_n = config_ptr->sample_rate; + *units_per_sec_d = 1; + break; + + default: + g_assert_not_reached (); + } +} + + +static gboolean +gst_raw_audio_parse_is_using_sink_caps (GstRawAudioParse * raw_audio_parse) +{ + return raw_audio_parse->current_config == + &(raw_audio_parse->sink_caps_config); +} + + +static GstRawAudioParseConfig * +gst_raw_audio_parse_get_config_ptr (GstRawAudioParse * raw_audio_parse, + GstRawBaseParseConfig config) +{ + g_assert (raw_audio_parse->current_config != NULL); + + switch (config) { + case GST_RAW_BASE_PARSE_CONFIG_PROPERTIES: + return &(raw_audio_parse->properties_config); + + case GST_RAW_BASE_PARSE_CONFIG_SINKCAPS: + return &(raw_audio_parse->sink_caps_config); + + default: + g_assert (raw_audio_parse->current_config != NULL); + return raw_audio_parse->current_config; + } +} + + +static void +gst_raw_audio_parse_init_config (GstRawAudioParseConfig * config) +{ + config->ready = FALSE; + config->format = DEFAULT_FORMAT; + config->pcm_format = DEFAULT_PCM_FORMAT; + config->bpf = 0; + config->sample_rate = DEFAULT_SAMPLE_RATE; + config->num_channels = DEFAULT_NUM_CHANNELS; + config->interleaved = DEFAULT_INTERLEAVED; + config->needs_channel_reordering = FALSE; + + gst_raw_audio_parse_set_config_channels (config, config->num_channels, 0, + TRUE); +} + + +static gboolean +gst_raw_audio_parse_set_config_channels (GstRawAudioParseConfig * config, + guint num_channels, guint64 channel_mask, gboolean set_positions) +{ + g_assert (num_channels > 0); + + config->num_channels = num_channels; + /* Setting this to FALSE, since initially, after setting the channels, + * the default GStreamer channel ordering is used. */ + config->needs_channel_reordering = FALSE; + + /* Set the channel positions based on the given channel mask if set_positions + * is set to TRUE. A channel mask of 0 signifies that a fallback mask should be + * used for the given number of channels. */ + if (set_positions) { + if (channel_mask == 0) + channel_mask = gst_audio_channel_get_fallback_mask (config->num_channels); + + return gst_audio_channel_positions_from_mask (config->num_channels, + channel_mask, config->channel_positions); + } else { + return TRUE; + } +} + + +static gboolean +gst_raw_audio_parse_update_channel_reordering_flag (GstRawAudioParseConfig * + config) +{ + g_assert (config->num_channels > 0); + + /* If the channel_positions array contains channel positions which are in an + * order that conforms to the valid GStreamer order, ensure that channel + * reordering is disabled. + * Otherwise, if the order of the positions in the channel_positions array + * does not conform to the GStreamer order, ensure it is enabled. + */ + + if (gst_audio_check_valid_channel_positions (config->channel_positions, + config->num_channels, TRUE)) { + + config->needs_channel_reordering = FALSE; + + return TRUE; + } else { + config->needs_channel_reordering = TRUE; + memcpy (config->reordered_channel_positions, config->channel_positions, + sizeof (GstAudioChannelPosition) * config->num_channels); + return + gst_audio_channel_positions_to_valid_order + (config->reordered_channel_positions, config->num_channels); + } +} + + +static void +gst_raw_audio_parse_update_config_bpf (GstRawAudioParseConfig * config) +{ + switch (config->format) { + case GST_RAW_AUDIO_PARSE_FORMAT_PCM: + { + GstAudioFormatInfo const *fmt_info = + gst_audio_format_get_info (config->pcm_format); + g_assert (fmt_info != NULL); + + config->bpf = + GST_AUDIO_FORMAT_INFO_WIDTH (fmt_info) * config->num_channels / 8; + + break; + } + + case GST_RAW_AUDIO_PARSE_FORMAT_ALAW: + case GST_RAW_AUDIO_PARSE_FORMAT_MULAW: + /* A-law and mu-law both use 1 byte per sample */ + config->bpf = 1 * config->num_channels; + break; + + default: + g_assert_not_reached (); + } +} + + +static gboolean +gst_raw_audio_parse_caps_to_config (GstRawAudioParse * raw_audio_parse, + GstCaps * caps, GstRawAudioParseConfig * config) +{ + gboolean ret = FALSE; + GstStructure *structure; + + /* Caps might get copied, and the copy needs to be unref'd. + * Also, the caller retains ownership over the original caps. + * So, to make this mechanism also work with cases where the + * caps are *not* copied, ref the original caps here first. */ + gst_caps_ref (caps); + + structure = gst_caps_get_structure (caps, 0); + + /* For unaligned raw data, the output caps stay the same, + * except that audio/x-unaligned-raw becomes audio/x-raw, + * since the parser aligns the sample data */ + if (gst_structure_has_name (structure, "audio/x-unaligned-raw")) { + /* Copy the caps to be able to modify them */ + GstCaps *new_caps = gst_caps_copy (caps); + gst_caps_unref (caps); + caps = new_caps; + + /* Change the media type to audio/x-raw , otherwise + * gst_audio_info_from_caps() won't work */ + structure = gst_caps_get_structure (caps, 0); + gst_structure_set_name (structure, "audio/x-raw"); + } + + if (gst_structure_has_name (structure, "audio/x-raw")) { + guint num_channels; + GstAudioInfo info; + if (!gst_audio_info_from_caps (&info, caps)) { + GST_ERROR_OBJECT (raw_audio_parse, + "failed to parse caps %" GST_PTR_FORMAT, (gpointer) caps); + goto done; + } + + num_channels = GST_AUDIO_INFO_CHANNELS (&info); + + config->format = GST_RAW_AUDIO_PARSE_FORMAT_PCM; + config->pcm_format = GST_AUDIO_INFO_FORMAT (&info); + config->bpf = GST_AUDIO_INFO_BPF (&info); + config->sample_rate = GST_AUDIO_INFO_RATE (&info); + config->interleaved = + (GST_AUDIO_INFO_LAYOUT (&info) == GST_AUDIO_LAYOUT_INTERLEAVED); + + gst_raw_audio_parse_set_config_channels (config, num_channels, 0, FALSE); + memcpy (config->channel_positions, &(GST_AUDIO_INFO_POSITION (&info, 0)), + sizeof (GstAudioChannelPosition) * num_channels); + } else if (gst_structure_has_name (structure, "audio/x-alaw") + || gst_structure_has_name (structure, "audio/x-mulaw")) { + gint i; + guint64 channel_mask; + guint num_channels; + + config->format = + gst_structure_has_name (structure, + "audio/x-alaw") ? GST_RAW_AUDIO_PARSE_FORMAT_ALAW : + GST_RAW_AUDIO_PARSE_FORMAT_MULAW; + + if (!gst_structure_get_int (structure, "rate", &i)) { + GST_ERROR_OBJECT (raw_audio_parse, + "missing rate value in caps %" GST_PTR_FORMAT, (gpointer) caps); + goto done; + } + config->sample_rate = i; + + if (!gst_structure_get_int (structure, "channels", &i)) { + GST_ERROR_OBJECT (raw_audio_parse, + "missing channels value in caps %" GST_PTR_FORMAT, (gpointer) caps); + goto done; + } + num_channels = i; + + if (!gst_structure_get (structure, "channel-mask", GST_TYPE_BITMASK, + &channel_mask, NULL)) { + channel_mask = gst_audio_channel_get_fallback_mask (num_channels); + GST_DEBUG_OBJECT (raw_audio_parse, + "input caps have no channel mask - using fallback mask %#lx for %u channels", + channel_mask, num_channels); + } + + if (!gst_raw_audio_parse_set_config_channels (config, num_channels, + channel_mask, TRUE)) { + GST_ERROR_OBJECT (raw_audio_parse, + "could not use channel mask %#lx for channel positions", + channel_mask); + goto done; + } + + /* A-law and mu-law both use 1 byte per sample */ + config->bpf = 1 * num_channels; + } else { + GST_ERROR_OBJECT (raw_audio_parse, + "caps %" GST_PTR_FORMAT " have an unsupported media type", + (gpointer) caps); + goto done; + } + + ret = TRUE; + +done: + gst_caps_unref (caps); + if (ret) + config->ready = TRUE; + return ret; +} + + +static gboolean +gst_raw_audio_parse_config_to_caps (GstRawAudioParse * raw_audio_parse, + GstCaps ** caps, GstRawAudioParseConfig * config) +{ + gboolean ret = TRUE; + GstAudioChannelPosition *channel_positions; + + g_assert (caps != NULL); + + if (config->bpf == 0) { + GST_ERROR_OBJECT (raw_audio_parse, + "cannot convert config to caps - config not filled with valid values"); + *caps = NULL; + return FALSE; + } + + channel_positions = + config->needs_channel_reordering ? &(config-> + reordered_channel_positions[0]) : &(config->channel_positions[0]); + + switch (config->format) { + case GST_RAW_AUDIO_PARSE_FORMAT_PCM: + { + GstAudioInfo info; + gst_audio_info_init (&info); + gst_audio_info_set_format (&info, + config->pcm_format, + config->sample_rate, config->num_channels, channel_positions); + + *caps = gst_audio_info_to_caps (&info); + + break; + } + + case GST_RAW_AUDIO_PARSE_FORMAT_ALAW: + case GST_RAW_AUDIO_PARSE_FORMAT_MULAW: + { + guint64 channel_mask; + + if (!gst_audio_channel_positions_to_mask (channel_positions, + config->num_channels, TRUE, &channel_mask)) { + GST_ERROR_OBJECT (raw_audio_parse, "invalid channel positions"); + ret = FALSE; + break; + } + + *caps = gst_caps_new_simple ( + (config->format == + GST_RAW_AUDIO_PARSE_FORMAT_ALAW) ? "audio/x-alaw" : + "audio/x-mulaw", "rate", G_TYPE_INT, config->sample_rate, "channels", + G_TYPE_INT, config->num_channels, "channel-mask", GST_TYPE_BITMASK, + channel_mask, NULL); + + break; + } + + default: + g_assert_not_reached (); + ret = FALSE; + } + + if (!ret) + *caps = NULL; + + return ret; +} + + + + +GType +gst_raw_audio_parse_format_get_type (void) +{ + static GType audio_parse_format_gtype = 0; + static const GEnumValue types[] = { + {GST_RAW_AUDIO_PARSE_FORMAT_PCM, "PCM", "pcm"}, + {GST_RAW_AUDIO_PARSE_FORMAT_ALAW, "A-Law", "alaw"}, + {GST_RAW_AUDIO_PARSE_FORMAT_MULAW, "\302\265-Law", "mulaw"}, + {0, NULL, NULL} + }; + + if (!audio_parse_format_gtype) + audio_parse_format_gtype = + g_enum_register_static ("GstRawAudioParseFormat", types); + + return audio_parse_format_gtype; +} |