| /* 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 %#" |
| G_GINT64_MODIFIER "x 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 %#" G_GINT64_MODIFIER |
| "x 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; |
| } |