summaryrefslogtreecommitdiff
path: root/gst/rawparse/gstrawaudioparse.c
diff options
context:
space:
mode:
Diffstat (limited to 'gst/rawparse/gstrawaudioparse.c')
-rw-r--r--gst/rawparse/gstrawaudioparse.c1088
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;
+}