2016-07-25 11:45:40 +00:00
|
|
|
/* 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
|
2017-01-23 19:36:11 +00:00
|
|
|
* @title: rawaudioparse
|
2016-07-25 11:45:40 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
2017-01-23 19:36:11 +00:00
|
|
|
* ## Example pipelines
|
2016-07-25 11:45:40 +00:00
|
|
|
* |[
|
|
|
|
* gst-launch-1.0 souphttpsrc http://my-dlna-server/track.l16 \
|
|
|
|
* rawaudioparse ! audioconvert ! audioresample ! autoaudiosink
|
2017-01-23 19:36:11 +00:00
|
|
|
* ]|
|
|
|
|
* Receive L16 data from a DLNA server, parse and timestamp it with
|
2016-07-25 11:45:40 +00:00
|
|
|
* 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
|
2017-01-23 19:36:11 +00:00
|
|
|
* ]|
|
|
|
|
* Read raw data from a local file and parse it as PCM data with 48000 Hz sample
|
2016-07-25 11:45:40 +00:00
|
|
|
* 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.
|
2017-01-23 19:36:11 +00:00
|
|
|
*
|
2016-07-25 11:45:40 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#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);
|
2016-11-27 09:44:14 +00:00
|
|
|
static gint gst_raw_audio_parse_get_alignment (GstRawBaseParse * raw_base_parse,
|
|
|
|
GstRawBaseParseConfig config);
|
2016-07-25 11:45:40 +00:00
|
|
|
|
|
|
|
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);
|
2016-11-27 09:44:14 +00:00
|
|
|
rawbaseparse_class->get_alignment =
|
|
|
|
GST_DEBUG_FUNCPTR (gst_raw_audio_parse_get_alignment);
|
2016-07-25 11:45:40 +00:00
|
|
|
|
|
|
|
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);
|
2017-02-28 13:26:36 +00:00
|
|
|
} else if (valarray != NULL) {
|
2016-07-25 11:45:40 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2016-11-27 09:44:14 +00:00
|
|
|
static guint
|
|
|
|
round_up_pow2 (guint n)
|
|
|
|
{
|
|
|
|
n = n - 1;
|
|
|
|
n = n | (n >> 1);
|
|
|
|
n = n | (n >> 2);
|
|
|
|
n = n | (n >> 4);
|
|
|
|
n = n | (n >> 8);
|
|
|
|
n = n | (n >> 16);
|
|
|
|
return n + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gint
|
|
|
|
gst_raw_audio_parse_get_alignment (GstRawBaseParse * raw_base_parse,
|
|
|
|
GstRawBaseParseConfig config)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
gint width;
|
|
|
|
|
|
|
|
if (config_ptr->format != GST_RAW_AUDIO_PARSE_FORMAT_PCM)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
width =
|
|
|
|
GST_AUDIO_FORMAT_INFO_WIDTH (gst_audio_format_get_info
|
|
|
|
(config_ptr->pcm_format)) / 8;
|
|
|
|
width = GST_ROUND_UP_8 (width);
|
|
|
|
width = round_up_pow2 (width);
|
|
|
|
|
|
|
|
return width;
|
|
|
|
}
|
2016-07-25 11:45:40 +00:00
|
|
|
|
|
|
|
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,
|
2016-07-29 07:49:26 +00:00
|
|
|
"input caps have no channel mask - using fallback mask %#"
|
|
|
|
G_GINT64_MODIFIER "x for %u channels", channel_mask, num_channels);
|
2016-07-25 11:45:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!gst_raw_audio_parse_set_config_channels (config, num_channels,
|
|
|
|
channel_mask, TRUE)) {
|
|
|
|
GST_ERROR_OBJECT (raw_audio_parse,
|
2016-07-29 07:49:26 +00:00
|
|
|
"could not use channel mask %#" G_GINT64_MODIFIER
|
|
|
|
"x for channel positions", channel_mask);
|
2016-07-25 11:45:40 +00:00
|
|
|
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;
|
|
|
|
}
|