mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 16:50:47 +00:00
rawparse: Add new raw audio and video parser elements
The new rawaudioparse and rawvideoparse elements are based on GstBaseParse and completely replace audioparse and videoparse https://bugzilla.gnome.org/show_bug.cgi?id=767011
This commit is contained in:
parent
f6e6264711
commit
d508525119
14 changed files with 4447 additions and 2 deletions
|
@ -4,8 +4,12 @@ plugin_LTLIBRARIES = libgstrawparse.la
|
|||
libgstrawparse_la_SOURCES = \
|
||||
gstrawparse.c \
|
||||
gstunalignedaudioparse.c \
|
||||
gstunalignedvideoparse.c \
|
||||
gstaudioparse.c \
|
||||
gstvideoparse.c \
|
||||
gstrawbaseparse.c \
|
||||
gstrawaudioparse.c \
|
||||
gstrawvideoparse.c \
|
||||
plugin.c
|
||||
libgstrawparse_la_CFLAGS = \
|
||||
$(GST_PLUGINS_BASE_CFLAGS) \
|
||||
|
@ -21,7 +25,12 @@ libgstrawparse_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
|
|||
|
||||
noinst_HEADERS = \
|
||||
unalignedaudio.h \
|
||||
unalignedvideo.h \
|
||||
gstunalignedaudioparse.h \
|
||||
gstunalignedvideoparse.h \
|
||||
gstaudioparse.h \
|
||||
gstrawparse.h \
|
||||
gstvideoparse.h
|
||||
gstvideoparse.h \
|
||||
gstrawbaseparse.h \
|
||||
gstrawaudioparse.h \
|
||||
gstrawvideoparse.h
|
||||
|
|
1088
gst/rawparse/gstrawaudioparse.c
Normal file
1088
gst/rawparse/gstrawaudioparse.c
Normal file
File diff suppressed because it is too large
Load diff
131
gst/rawparse/gstrawaudioparse.h
Normal file
131
gst/rawparse/gstrawaudioparse.h
Normal file
|
@ -0,0 +1,131 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#ifndef __GST_RAW_AUDIO_PARSE_H__
|
||||
#define __GST_RAW_AUDIO_PARSE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/audio/audio.h>
|
||||
#include "gstrawbaseparse.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_RAW_AUDIO_PARSE \
|
||||
(gst_raw_audio_parse_get_type())
|
||||
#define GST_RAW_AUDIO_PARSE(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_RAW_AUDIO_PARSE, GstRawAudioParse))
|
||||
#define GST_RAW_AUDIO_PARSE_CAST(obj) \
|
||||
((GstRawAudioParse *)(obj))
|
||||
#define GST_RAW_AUDIO_PARSE_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_RAW_AUDIO_PARSE, GstRawAudioParseClass))
|
||||
#define GST_IS_RAW_AUDIO_PARSE(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_RAW_AUDIO_PARSE))
|
||||
#define GST_IS_RAW_AUDIO_PARSE_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_RAW_AUDIO_PARSE))
|
||||
|
||||
|
||||
typedef enum _GstRawAudioParseFormat GstRawAudioParseFormat;
|
||||
|
||||
typedef struct _GstRawAudioParseConfig GstRawAudioParseConfig;
|
||||
typedef struct _GstRawAudioParse GstRawAudioParse;
|
||||
typedef struct _GstRawAudioParseClass GstRawAudioParseClass;
|
||||
|
||||
|
||||
enum _GstRawAudioParseFormat
|
||||
{
|
||||
GST_RAW_AUDIO_PARSE_FORMAT_PCM,
|
||||
GST_RAW_AUDIO_PARSE_FORMAT_MULAW,
|
||||
GST_RAW_AUDIO_PARSE_FORMAT_ALAW
|
||||
};
|
||||
|
||||
|
||||
/* Contains information about the sample rate, format, and channel count to use. */
|
||||
struct _GstRawAudioParseConfig
|
||||
{
|
||||
/* If TRUE, then this configuration is ready to use */
|
||||
gboolean ready;
|
||||
/* Format of the configuration. Can be PCM, a-law, mu-law. */
|
||||
GstRawAudioParseFormat format;
|
||||
/* If format is set to PCM, this specifies the exact PCM format in use.
|
||||
* Meaningless if format is set to anything other than PCM. */
|
||||
GstAudioFormat pcm_format;
|
||||
/* Bytes per frame. Calculated as: bpf = bytes_per_sample * num_channels
|
||||
* Must be nonzero. This is the size of one frame, the value returned
|
||||
* by the GstRawBaseParseClass get_config_frame_size() vfunc. */
|
||||
guint bpf;
|
||||
/* Sample rate in Hz - must be nonzero */
|
||||
guint sample_rate;
|
||||
/* Number of channels - must be nonzero */
|
||||
guint num_channels;
|
||||
/* TRUE if the data is interleaved, FALSE otherwise */
|
||||
gboolean interleaved;
|
||||
|
||||
/* Array of channel positions, one position per channel; its first
|
||||
* num_channels values are valid. They are computed out of the number
|
||||
* of channels if no positions are explicitely given. */
|
||||
GstAudioChannelPosition channel_positions[64];
|
||||
|
||||
/* If the channel_positions are in a valid GStreamer channel order, then
|
||||
* this is not used, and needs_channel_reordering is FALSE. Otherwise,
|
||||
* this contains the same positions as in channel_positions, but in the
|
||||
* order GStreamer expects. needs_channel_reordering will be TRUE in that
|
||||
* case. This is used for reordering samples in outgoing buffers if
|
||||
* necessary. */
|
||||
GstAudioChannelPosition reordered_channel_positions[64];
|
||||
|
||||
/* TRUE if channel reordering is necessary, FALSE otherwise. See above
|
||||
* for details. */
|
||||
gboolean needs_channel_reordering;
|
||||
};
|
||||
|
||||
|
||||
struct _GstRawAudioParse
|
||||
{
|
||||
GstRawBaseParse parent;
|
||||
|
||||
/*< private > */
|
||||
|
||||
/* Configuration controlled by the object properties. Its ready value
|
||||
* is set to TRUE from the start, so it can be used right away.
|
||||
*/
|
||||
GstRawAudioParseConfig properties_config;
|
||||
/* Configuration controlled by the sink caps. Its ready value is
|
||||
* initially set to FALSE until valid sink caps come in. It is set to
|
||||
* FALSE again when the stream-start event is observed.
|
||||
*/
|
||||
GstRawAudioParseConfig sink_caps_config;
|
||||
/* Currently active configuration. Points either to properties_config
|
||||
* or to sink_caps_config. This is never NULL. */
|
||||
GstRawAudioParseConfig *current_config;
|
||||
};
|
||||
|
||||
|
||||
struct _GstRawAudioParseClass
|
||||
{
|
||||
GstRawBaseParseClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
GType gst_raw_audio_parse_get_type (void);
|
||||
GType gst_raw_audio_parse_format_get_type (void);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
748
gst/rawparse/gstrawbaseparse.c
Normal file
748
gst/rawparse/gstrawbaseparse.c
Normal file
|
@ -0,0 +1,748 @@
|
|||
/* 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:gstrawbaseparse
|
||||
* @short_description: Base class for raw media data parsers
|
||||
*
|
||||
* This base class is for parsers which read raw media data and output
|
||||
* timestamped buffers with an integer number of frames inside.
|
||||
*
|
||||
* The format of the raw media data is specified in one of two ways: either,
|
||||
* the information from the sink pad's caps is taken, or the information from
|
||||
* the properties is used (this is chosen by the use-sink-caps property).
|
||||
* These two ways are internally referred to as "configurations". The configuration
|
||||
* that receives its information from the sink pad's caps is called the
|
||||
* "sink caps configuration", while the one that depends on the information from
|
||||
* the properties is the "properties configuration". Configurations have a
|
||||
* "readiness". A configuration is "ready" when it contains valid information.
|
||||
* For example, with an audio parser, a configuration is not ready unless it
|
||||
* contains a valid sample rate, sample format, and channel count.
|
||||
*
|
||||
* The properties configuration must always be ready, even right from the start.
|
||||
* Subclasses must ensure this. The underlying reason is that properties have valid
|
||||
* values right from the start, and with the properties configuration, there is
|
||||
* nothing that readies it before actual data is sent (unlike with the sink caps
|
||||
* configuration, where a sink caps event will ready it before data is pushed
|
||||
* downstream).
|
||||
*
|
||||
* It is possible to switch between the configurations during a stream by
|
||||
* setting the use-sink-caps property. Subclasses typically allow for updating the
|
||||
* properties configuration during a stream by setting the various properties
|
||||
* (like sample-rate for a raw audio parser).
|
||||
* In these cases, the parser will produce a new CAPS event and push it downstream
|
||||
* to announce the caps for the new configuration. This also happens if the sink
|
||||
* caps change.
|
||||
*
|
||||
* A common mistake when trying to parse raw data with no input caps (for example,
|
||||
* a file with raw PCM samples when using rawaudioparse) is to forget to set the
|
||||
* use-sink-caps property to FALSE. In this case, the parser will report an error
|
||||
* when it tries to access the current configuration (because then the sink caps
|
||||
* configuration will be the current one and it will not contain valid values
|
||||
* since no sink caps were seen at this point).
|
||||
*
|
||||
* Subclasses must ensure that the properties configuration is the default one.
|
||||
*
|
||||
* The sink caps configuration is mostly useful with push-based sources, because these
|
||||
* will produce caps events and send them downstream. With pull-based sources, it is
|
||||
* possible that this doesn't happen. Since the sink caps configuration requires a caps
|
||||
* event to arrive at the sinkpad, this will cause the parser to fail then.
|
||||
*
|
||||
* The base class identifies the configurations by means of the GstRawAudioParseConfig
|
||||
* enum. It instructs the subclass to switch between configurations this way, and
|
||||
* also requests information about the current configuration, a configuration's
|
||||
* frame size, its readiness, etc. Subclasses are not required to use any particular
|
||||
* structure for the configuration implementations.
|
||||
*
|
||||
* Use the GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK and GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK
|
||||
* macros to protect configuration modifications.
|
||||
*
|
||||
* <listitem>
|
||||
* <itemizedlist>
|
||||
* <title>Summary of the subclass requirements</title>
|
||||
* <listitem><para>
|
||||
* Sink caps and properties configurations must both be
|
||||
* implemented and supported. It must also be ensured that there is a
|
||||
* "current" configuration.
|
||||
* </para></listitem>
|
||||
* Modifications to the configurations must be protected with the
|
||||
* GstRawBaseParse lock. This is typically necessary when the
|
||||
* properties configuration is modified by setting new property values.
|
||||
* (Note that the lock is held during *all* vfunc calls.)
|
||||
* <listitem><para>
|
||||
* If the properties configuration is updated (typically by
|
||||
* setting new property values), gst_raw_base_parse_invalidate_src_caps()
|
||||
* must be called if the properties config is the current one. This is
|
||||
* necessary to ensure that GstBaseParse pushes a new caps event downstream
|
||||
* which contains caps from the updated configuration.
|
||||
* </para></listitem>
|
||||
* <listitem><para>
|
||||
* In case there are bytes in each frame that aren't part of the actual
|
||||
* payload, the get_overhead_size() vfunc must be defined, and the
|
||||
* @get_config_frame_size() vfunc must return a frame size that includes
|
||||
* the number of non-payload bytes (= the overhead). Otherwise, the
|
||||
* timestamps will incorrectly include the overhead bytes.
|
||||
* </para></listitem>
|
||||
* </listitem>
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include "gstrawbaseparse.h"
|
||||
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (raw_base_parse_debug);
|
||||
#define GST_CAT_DEFAULT raw_base_parse_debug
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_USE_SINK_CAPS
|
||||
};
|
||||
|
||||
|
||||
#define DEFAULT_USE_SINK_CAPS FALSE
|
||||
#define INITIAL_PARSER_CONFIG \
|
||||
((DEFAULT_USE_SINK_CAPS) ? GST_RAW_BASE_PARSE_CONFIG_SINKCAPS : \
|
||||
GST_RAW_BASE_PARSE_CONFIG_PROPERTIES)
|
||||
|
||||
|
||||
#define gst_raw_base_parse_parent_class parent_class
|
||||
G_DEFINE_ABSTRACT_TYPE (GstRawBaseParse, gst_raw_base_parse,
|
||||
GST_TYPE_BASE_PARSE);
|
||||
|
||||
|
||||
static void gst_raw_base_parse_finalize (GObject * object);
|
||||
static void gst_raw_base_parse_set_property (GObject * object, guint prop_id,
|
||||
GValue const *value, GParamSpec * pspec);
|
||||
static void gst_raw_base_parse_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
static gboolean gst_raw_base_parse_start (GstBaseParse * parse);
|
||||
static gboolean gst_raw_base_parse_stop (GstBaseParse * parse);
|
||||
static gboolean gst_raw_base_parse_set_sink_caps (GstBaseParse * parse,
|
||||
GstCaps * caps);
|
||||
static GstFlowReturn gst_raw_base_parse_handle_frame (GstBaseParse * parse,
|
||||
GstBaseParseFrame * frame, gint * skipsize);
|
||||
static gboolean gst_raw_base_parse_convert (GstBaseParse * parse,
|
||||
GstFormat src_format, gint64 src_value, GstFormat dest_format,
|
||||
gint64 * dest_value);
|
||||
|
||||
static gboolean gst_raw_base_parse_is_using_sink_caps (GstRawBaseParse *
|
||||
raw_base_parse);
|
||||
static gboolean gst_raw_base_parse_is_gstformat_supported (GstRawBaseParse *
|
||||
raw_base_parse, GstFormat format);
|
||||
|
||||
|
||||
|
||||
static void
|
||||
gst_raw_base_parse_class_init (GstRawBaseParseClass * klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GstBaseParseClass *baseparse_class;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (raw_base_parse_debug, "rawbaseparse", 0,
|
||||
"raw base parse class");
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
baseparse_class = GST_BASE_PARSE_CLASS (klass);
|
||||
|
||||
object_class->finalize = GST_DEBUG_FUNCPTR (gst_raw_base_parse_finalize);
|
||||
object_class->set_property =
|
||||
GST_DEBUG_FUNCPTR (gst_raw_base_parse_set_property);
|
||||
object_class->get_property =
|
||||
GST_DEBUG_FUNCPTR (gst_raw_base_parse_get_property);
|
||||
|
||||
baseparse_class->start = GST_DEBUG_FUNCPTR (gst_raw_base_parse_start);
|
||||
baseparse_class->stop = GST_DEBUG_FUNCPTR (gst_raw_base_parse_stop);
|
||||
baseparse_class->set_sink_caps =
|
||||
GST_DEBUG_FUNCPTR (gst_raw_base_parse_set_sink_caps);
|
||||
baseparse_class->handle_frame =
|
||||
GST_DEBUG_FUNCPTR (gst_raw_base_parse_handle_frame);
|
||||
baseparse_class->convert = GST_DEBUG_FUNCPTR (gst_raw_base_parse_convert);
|
||||
|
||||
/**
|
||||
* GstRawBaseParse::use-sink-caps:
|
||||
*
|
||||
* Use sink caps configuration. If set to false, the parser
|
||||
* will use the properties configuration instead. It is possible
|
||||
* to switch between these during playback.
|
||||
*/
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_USE_SINK_CAPS,
|
||||
g_param_spec_boolean ("use-sink-caps",
|
||||
"Use sink caps",
|
||||
"Use the sink caps for defining the output format",
|
||||
DEFAULT_USE_SINK_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gst_raw_base_parse_init (GstRawBaseParse * raw_base_parse)
|
||||
{
|
||||
raw_base_parse->src_caps_set = FALSE;
|
||||
g_mutex_init (&(raw_base_parse->config_mutex));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gst_raw_base_parse_finalize (GObject * object)
|
||||
{
|
||||
GstRawBaseParse *raw_base_parse = GST_RAW_BASE_PARSE (object);
|
||||
|
||||
g_mutex_clear (&(raw_base_parse->config_mutex));
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gst_raw_base_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);
|
||||
GstRawBaseParseClass *klass = GST_RAW_BASE_PARSE_GET_CLASS (object);
|
||||
|
||||
g_assert (klass->is_config_ready);
|
||||
g_assert (klass->set_current_config);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_USE_SINK_CAPS:
|
||||
{
|
||||
gboolean new_state, cur_state;
|
||||
GstRawBaseParseConfig new_config;
|
||||
|
||||
GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object);
|
||||
|
||||
/* Check to ensure nothing is done if the value stays the same */
|
||||
new_state = g_value_get_boolean (value);
|
||||
cur_state = gst_raw_base_parse_is_using_sink_caps (raw_base_parse);
|
||||
if (new_state == cur_state) {
|
||||
GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object);
|
||||
break;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (raw_base_parse, "switching to %s config",
|
||||
new_state ? "sink caps" : "properties");
|
||||
new_config =
|
||||
new_state ? GST_RAW_BASE_PARSE_CONFIG_SINKCAPS :
|
||||
GST_RAW_BASE_PARSE_CONFIG_PROPERTIES;
|
||||
|
||||
if (!klass->set_current_config (raw_base_parse, new_config)) {
|
||||
GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object);
|
||||
GST_ELEMENT_ERROR (raw_base_parse, STREAM, FAILED,
|
||||
("could not set new current config"), ("use-sink-caps property: %d",
|
||||
new_state));
|
||||
break;
|
||||
}
|
||||
|
||||
/* Update the minimum frame size if the config is ready. This ensures that
|
||||
* the next buffer that is passed to handle_frame contains complete frames.
|
||||
* If the current config is the properties config, then it will always be
|
||||
* ready, and its frame size will be valid. Ensure that the baseparse minimum
|
||||
* frame size is set properly then.
|
||||
* If the current config is the sink caps config, then it will initially not
|
||||
* be ready until the sink caps are set, so the minimum frame size cannot be
|
||||
* set right here. However, since the caps always come in *before* the actual
|
||||
* data, the config will be readied in the set_sink_caps function, and be ready
|
||||
* by the time handle_frame is called. There, the minimum frame size is set as
|
||||
* well. */
|
||||
if (klass->is_config_ready (raw_base_parse,
|
||||
GST_RAW_BASE_PARSE_CONFIG_CURRENT)) {
|
||||
gsize frame_size = klass->get_config_frame_size (raw_base_parse,
|
||||
GST_RAW_BASE_PARSE_CONFIG_CURRENT);
|
||||
gst_base_parse_set_min_frame_size (base_parse, frame_size);
|
||||
}
|
||||
|
||||
/* Since the current config was switched, the source caps change. Ensure the
|
||||
* new caps are pushed downstream by setting src_caps_set to FALSE: This way,
|
||||
* the next handle_frame call will take care of that. */
|
||||
raw_base_parse->src_caps_set = FALSE;
|
||||
|
||||
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_base_parse_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstRawBaseParse *raw_base_parse = GST_RAW_BASE_PARSE (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_USE_SINK_CAPS:
|
||||
GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object);
|
||||
g_value_set_boolean (value,
|
||||
gst_raw_base_parse_is_using_sink_caps (raw_base_parse));
|
||||
GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
gst_raw_base_parse_start (GstBaseParse * parse)
|
||||
{
|
||||
GstBaseParse *base_parse = GST_BASE_PARSE (parse);
|
||||
GstRawBaseParse *raw_base_parse = GST_RAW_BASE_PARSE (parse);
|
||||
GstRawBaseParseClass *klass = GST_RAW_BASE_PARSE_GET_CLASS (parse);
|
||||
|
||||
g_assert (klass->set_current_config);
|
||||
|
||||
GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (raw_base_parse);
|
||||
|
||||
/* If the config is ready from the start, set the min frame size
|
||||
* (this will happen with the properties config) */
|
||||
if (klass->is_config_ready (raw_base_parse,
|
||||
GST_RAW_BASE_PARSE_CONFIG_CURRENT)) {
|
||||
gsize frame_size = klass->get_config_frame_size (raw_base_parse,
|
||||
GST_RAW_BASE_PARSE_CONFIG_CURRENT);
|
||||
gst_base_parse_set_min_frame_size (base_parse, frame_size);
|
||||
}
|
||||
|
||||
GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
gst_raw_base_parse_stop (GstBaseParse * parse)
|
||||
{
|
||||
GstRawBaseParse *raw_base_parse = GST_RAW_BASE_PARSE (parse);
|
||||
|
||||
GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (raw_base_parse);
|
||||
raw_base_parse->src_caps_set = FALSE;
|
||||
GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
gst_raw_base_parse_set_sink_caps (GstBaseParse * parse, GstCaps * caps)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GstRawBaseParse *raw_base_parse = GST_RAW_BASE_PARSE (parse);
|
||||
GstRawBaseParseClass *klass = GST_RAW_BASE_PARSE_GET_CLASS (parse);
|
||||
|
||||
g_assert (klass->set_config_from_caps);
|
||||
g_assert (klass->get_caps_from_config);
|
||||
g_assert (klass->get_config_frame_size);
|
||||
|
||||
|
||||
GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (raw_base_parse);
|
||||
|
||||
GST_DEBUG_OBJECT (parse, "getting config from new sink caps");
|
||||
|
||||
/* Convert the new sink caps to sink caps config. This also
|
||||
* readies the config. */
|
||||
ret =
|
||||
klass->set_config_from_caps (raw_base_parse,
|
||||
GST_RAW_BASE_PARSE_CONFIG_SINKCAPS, caps);
|
||||
if (!ret) {
|
||||
GST_ERROR_OBJECT (raw_base_parse, "could not get config from sink caps");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* If the sink caps config is currently active, push caps downstream,
|
||||
* set the minimum frame size (to guarantee that input buffers hold
|
||||
* complete frames), and update the src_caps_set flag. If the sink
|
||||
* caps config isn't the currently active config, just exit, since in
|
||||
* that case, the caps will always be pushed downstream in handle_frame. */
|
||||
if (gst_raw_base_parse_is_using_sink_caps (raw_base_parse)) {
|
||||
GstCaps *new_src_caps;
|
||||
gsize frame_size;
|
||||
|
||||
GST_DEBUG_OBJECT (parse,
|
||||
"sink caps config is the current one; trying to push new caps downstream");
|
||||
|
||||
/* Convert back to caps. The caps may have changed, for example
|
||||
* audio/x-unaligned-raw may have been replaced with audio/x-raw.
|
||||
* (Also, this keeps the behavior in sync with that of the block
|
||||
* in handle_frame that pushes caps downstream if not done already.) */
|
||||
if (!klass->get_caps_from_config (raw_base_parse,
|
||||
GST_RAW_BASE_PARSE_CONFIG_CURRENT, &new_src_caps)) {
|
||||
GST_ERROR_OBJECT (raw_base_parse,
|
||||
"could not get src caps from current config");
|
||||
goto done;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (raw_base_parse,
|
||||
"got new sink caps; updating src caps to %" GST_PTR_FORMAT,
|
||||
(gpointer) new_src_caps);
|
||||
|
||||
frame_size =
|
||||
klass->get_config_frame_size (raw_base_parse,
|
||||
GST_RAW_BASE_PARSE_CONFIG_CURRENT);
|
||||
gst_base_parse_set_min_frame_size (parse, frame_size);
|
||||
|
||||
raw_base_parse->src_caps_set = TRUE;
|
||||
|
||||
GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse);
|
||||
|
||||
/* Push caps outside of the lock */
|
||||
gst_pad_push_event (GST_BASE_PARSE_SRC_PAD (raw_base_parse),
|
||||
gst_event_new_caps (new_src_caps)
|
||||
);
|
||||
|
||||
gst_caps_unref (new_src_caps);
|
||||
} else {
|
||||
GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse);
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static GstFlowReturn
|
||||
gst_raw_base_parse_handle_frame (GstBaseParse * parse,
|
||||
GstBaseParseFrame * frame, gint * skipsize)
|
||||
{
|
||||
gsize in_size, out_size;
|
||||
guint frame_size;
|
||||
guint num_out_frames;
|
||||
gsize units_n, units_d;
|
||||
guint64 buffer_duration;
|
||||
GstFlowReturn flow_ret = GST_FLOW_OK;
|
||||
GstEvent *new_caps_event = NULL;
|
||||
GstRawBaseParse *raw_base_parse = GST_RAW_BASE_PARSE (parse);
|
||||
GstRawBaseParseClass *klass = GST_RAW_BASE_PARSE_GET_CLASS (parse);
|
||||
|
||||
g_assert (klass->is_config_ready);
|
||||
g_assert (klass->get_caps_from_config);
|
||||
g_assert (klass->get_config_frame_size);
|
||||
g_assert (klass->get_units_per_second);
|
||||
|
||||
|
||||
/* We never skip any bytes this way. Instead, subclass takes care
|
||||
* of skipping any overhead (necessary, since the way it needs to
|
||||
* be skipped is completely subclass specific). */
|
||||
*skipsize = 0;
|
||||
|
||||
|
||||
/* The operations below access the current config. Protect
|
||||
* against race conditions by using the object lock. */
|
||||
GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (raw_base_parse);
|
||||
|
||||
|
||||
/* If the source pad caps haven't been set yet, or need to be
|
||||
* set again, do so now, BEFORE any buffers are pushed out */
|
||||
if (G_UNLIKELY (!raw_base_parse->src_caps_set)) {
|
||||
GstCaps *new_src_caps;
|
||||
|
||||
if (G_UNLIKELY (!klass->is_config_ready (raw_base_parse,
|
||||
GST_RAW_BASE_PARSE_CONFIG_CURRENT))) {
|
||||
/* The current configuration is not ready. No caps can be
|
||||
* generated out of it.
|
||||
* The most likely reason for this is that the sink caps config
|
||||
* is the current one and no valid sink caps have been pushed
|
||||
* by upstream. Report the problem and exit. */
|
||||
|
||||
if (gst_raw_base_parse_is_using_sink_caps (raw_base_parse)) {
|
||||
goto config_not_ready;
|
||||
} else {
|
||||
/* This should not be reached if the property config is active */
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (parse,
|
||||
"setting src caps since this has not been done yet");
|
||||
|
||||
/* Convert the current config to a caps structure to
|
||||
* inform downstream about the new format */
|
||||
if (!klass->get_caps_from_config (raw_base_parse,
|
||||
GST_RAW_BASE_PARSE_CONFIG_CURRENT, &new_src_caps)) {
|
||||
GST_ERROR_OBJECT (raw_base_parse,
|
||||
"could not get src caps from current config");
|
||||
flow_ret = GST_FLOW_NOT_NEGOTIATED;
|
||||
goto error_locked;
|
||||
}
|
||||
|
||||
new_caps_event = gst_event_new_caps (new_src_caps);
|
||||
gst_caps_unref (new_src_caps);
|
||||
|
||||
raw_base_parse->src_caps_set = TRUE;
|
||||
}
|
||||
|
||||
frame_size =
|
||||
klass->get_config_frame_size (raw_base_parse,
|
||||
GST_RAW_BASE_PARSE_CONFIG_CURRENT);
|
||||
|
||||
|
||||
in_size = gst_buffer_get_size (frame->buffer);
|
||||
|
||||
/* gst_base_parse_set_min_frame_size() is called when the current
|
||||
* configuration changes and the change affects the frame size. This
|
||||
* means that a buffer must contain at least as many bytes as indicated
|
||||
* by the frame size. If there are fewer inside an error occurred;
|
||||
* either something in the parser went wrong, or the min frame size
|
||||
* wasn't updated properly. */
|
||||
g_assert (in_size >= frame_size);
|
||||
|
||||
/* Determine how many complete frames would fit in the input buffer.
|
||||
* Then check if this amount exceeds the maximum number of frames
|
||||
* as indicated by the subclass. */
|
||||
num_out_frames = (in_size / frame_size);
|
||||
if (klass->get_max_frames_per_buffer) {
|
||||
guint max_num_out_frames = klass->get_max_frames_per_buffer (raw_base_parse,
|
||||
GST_RAW_BASE_PARSE_CONFIG_CURRENT);
|
||||
num_out_frames = MIN (num_out_frames, max_num_out_frames);
|
||||
}
|
||||
|
||||
/* Ensure that the size of the buffers that get pushed downstream
|
||||
* is always an integer multiple of the frame size to prevent cases
|
||||
* where downstream gets buffers with incomplete frames. */
|
||||
out_size = num_out_frames * frame_size;
|
||||
|
||||
/* Set the overhead size to ensure that timestamping excludes these
|
||||
* extra overhead bytes. */
|
||||
frame->overhead =
|
||||
klass->get_overhead_size ? klass->get_overhead_size (raw_base_parse,
|
||||
GST_RAW_BASE_PARSE_CONFIG_CURRENT) : 0;
|
||||
|
||||
g_assert (out_size >= (guint) (frame->overhead));
|
||||
out_size -= frame->overhead;
|
||||
|
||||
GST_LOG_OBJECT (raw_base_parse,
|
||||
"%" G_GSIZE_FORMAT " bytes input %" G_GSIZE_FORMAT
|
||||
" bytes output (%u frame(s)) %d bytes overhead", in_size, out_size,
|
||||
num_out_frames, frame->overhead);
|
||||
|
||||
/* Calculate buffer duration */
|
||||
klass->get_units_per_second (raw_base_parse, GST_FORMAT_BYTES,
|
||||
GST_RAW_BASE_PARSE_CONFIG_CURRENT, &units_n, &units_d);
|
||||
if (units_n == 0 || units_d == 0)
|
||||
buffer_duration = GST_CLOCK_TIME_NONE;
|
||||
else
|
||||
buffer_duration =
|
||||
gst_util_uint64_scale (out_size, GST_SECOND * units_d, units_n);
|
||||
|
||||
if (klass->process) {
|
||||
GstBuffer *processed_data = NULL;
|
||||
|
||||
if (!klass->process (raw_base_parse, GST_RAW_BASE_PARSE_CONFIG_CURRENT,
|
||||
frame->buffer, in_size, out_size, &processed_data))
|
||||
goto process_error;
|
||||
|
||||
frame->out_buffer = processed_data;
|
||||
} else {
|
||||
frame->out_buffer = NULL;
|
||||
}
|
||||
|
||||
/* Set the duration of the output buffer, or if none exists, of
|
||||
* the input buffer. Do this after the process() call, since in
|
||||
* case out_buffer is set, the subclass has created a new buffer.
|
||||
* Instead of requiring subclasses to set the duration (which
|
||||
* anyway must always be buffer_duration), let's do it here. */
|
||||
if (frame->out_buffer != NULL)
|
||||
GST_BUFFER_DURATION (frame->out_buffer) = buffer_duration;
|
||||
else
|
||||
GST_BUFFER_DURATION (frame->buffer) = buffer_duration;
|
||||
|
||||
/* Access to the current config is not needed in subsequent
|
||||
* operations, so the lock can be released */
|
||||
GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse);
|
||||
|
||||
|
||||
/* If any new caps have to be pushed downstrean, do so
|
||||
* *before* the frame is finished */
|
||||
if (G_UNLIKELY (new_caps_event != NULL)) {
|
||||
gst_pad_push_event (GST_BASE_PARSE_SRC_PAD (raw_base_parse),
|
||||
new_caps_event);
|
||||
new_caps_event = NULL;
|
||||
}
|
||||
|
||||
gst_base_parse_finish_frame (parse, frame, out_size + frame->overhead);
|
||||
|
||||
|
||||
return flow_ret;
|
||||
|
||||
|
||||
config_not_ready:
|
||||
GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse);
|
||||
GST_ELEMENT_ERROR (parse, STREAM, FORMAT,
|
||||
("sink caps config is the current config, and it is not ready -"
|
||||
"upstream may not have pushed a caps event yet"), (NULL));
|
||||
flow_ret = GST_FLOW_ERROR;
|
||||
goto error_end;
|
||||
|
||||
process_error:
|
||||
GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse);
|
||||
GST_ELEMENT_ERROR (parse, STREAM, DECODE, ("could not process data"), (NULL));
|
||||
flow_ret = GST_FLOW_ERROR;
|
||||
goto error_end;
|
||||
|
||||
error_locked:
|
||||
GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse);
|
||||
goto error_end;
|
||||
|
||||
error_end:
|
||||
frame->flags |= GST_BASE_PARSE_FRAME_FLAG_DROP;
|
||||
if (new_caps_event != NULL)
|
||||
gst_event_unref (new_caps_event);
|
||||
return flow_ret;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
gst_raw_base_parse_convert (GstBaseParse * parse, GstFormat src_format,
|
||||
gint64 src_value, GstFormat dest_format, gint64 * dest_value)
|
||||
{
|
||||
GstRawBaseParse *raw_base_parse = GST_RAW_BASE_PARSE (parse);
|
||||
GstRawBaseParseClass *klass = GST_RAW_BASE_PARSE_GET_CLASS (parse);
|
||||
gboolean ret = TRUE;
|
||||
gsize units_n, units_d;
|
||||
|
||||
g_assert (klass->is_config_ready);
|
||||
g_assert (klass->get_units_per_second);
|
||||
|
||||
|
||||
/* The operations below access the current config. Protect
|
||||
* against race conditions by using the object lock. */
|
||||
GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (raw_base_parse);
|
||||
|
||||
|
||||
if (!klass->is_config_ready (raw_base_parse,
|
||||
GST_RAW_BASE_PARSE_CONFIG_CURRENT)) {
|
||||
if (gst_raw_base_parse_is_using_sink_caps (raw_base_parse)) {
|
||||
goto config_not_ready;
|
||||
} else {
|
||||
/* This should not be reached if the property config is active */
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
if (G_UNLIKELY (src_format == dest_format)) {
|
||||
*dest_value = src_value;
|
||||
} else if ((src_format == GST_FORMAT_TIME || dest_format == GST_FORMAT_TIME)
|
||||
&& gst_raw_base_parse_is_gstformat_supported (raw_base_parse, src_format)
|
||||
&& gst_raw_base_parse_is_gstformat_supported (raw_base_parse, src_format)) {
|
||||
/* Perform conversions here if either the src or dest format
|
||||
* are GST_FORMAT_TIME and the other format is supported by
|
||||
* the subclass. This is because we perform TIME<->non-TIME
|
||||
* conversions here. Typically, subclasses only support
|
||||
* BYTES and DEFAULT formats. */
|
||||
|
||||
if (src_format == GST_FORMAT_TIME) {
|
||||
/* The source format is time, so perform a TIME -> non-TIME conversion */
|
||||
klass->get_units_per_second (raw_base_parse, dest_format,
|
||||
GST_RAW_BASE_PARSE_CONFIG_CURRENT, &units_n, &units_d);
|
||||
*dest_value = (units_n == 0
|
||||
|| units_d == 0) ? src_value : gst_util_uint64_scale (src_value,
|
||||
units_n, GST_SECOND * units_d);
|
||||
} else {
|
||||
/* The dest format is time, so perform a non-TIME -> TIME conversion */
|
||||
klass->get_units_per_second (raw_base_parse, src_format,
|
||||
GST_RAW_BASE_PARSE_CONFIG_CURRENT, &units_n, &units_d);
|
||||
*dest_value = (units_n == 0
|
||||
|| units_d == 0) ? src_value : gst_util_uint64_scale (src_value,
|
||||
GST_SECOND * units_d, units_n);
|
||||
}
|
||||
} else {
|
||||
/* Fallback for other conversions */
|
||||
ret =
|
||||
gst_base_parse_convert_default (parse, src_format, src_value,
|
||||
dest_format, dest_value);
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (parse,
|
||||
"converted %s -> %s %" G_GINT64_FORMAT " -> %" GST_TIME_FORMAT,
|
||||
gst_format_get_name (src_format), gst_format_get_name (dest_format),
|
||||
src_value, GST_TIME_ARGS (*dest_value));
|
||||
|
||||
GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse);
|
||||
return ret;
|
||||
|
||||
|
||||
config_not_ready:
|
||||
GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse);
|
||||
GST_ELEMENT_ERROR (parse, STREAM, FORMAT,
|
||||
("sink caps config is the current config, and it is not ready - "
|
||||
"upstream may not have pushed a caps event yet"), (NULL));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
gst_raw_base_parse_is_using_sink_caps (GstRawBaseParse * raw_base_parse)
|
||||
{
|
||||
/* must be called with lock */
|
||||
GstRawBaseParseClass *klass = GST_RAW_BASE_PARSE_GET_CLASS (raw_base_parse);
|
||||
g_assert (klass->get_current_config);
|
||||
return klass->get_current_config (raw_base_parse) ==
|
||||
GST_RAW_BASE_PARSE_CONFIG_SINKCAPS;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
gst_raw_base_parse_is_gstformat_supported (GstRawBaseParse * raw_base_parse,
|
||||
GstFormat format)
|
||||
{
|
||||
/* must be called with lock */
|
||||
GstRawBaseParseClass *klass = GST_RAW_BASE_PARSE_GET_CLASS (raw_base_parse);
|
||||
g_assert (klass->is_unit_format_supported);
|
||||
return klass->is_unit_format_supported (raw_base_parse, format);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* gst_raw_base_parse_invalidate_src_caps:
|
||||
* @raw_base_parse: a #GstRawBaseParse instance
|
||||
*
|
||||
* Flags the current source caps as invalid. Before the next downstream
|
||||
* buffer push, @get_caps_from_config is called, and the created caps are
|
||||
* pushed downstream in a new caps event, This is used if for example the
|
||||
* properties configuration is modified in the subclass.
|
||||
*
|
||||
* Note that this must be called with the parser lock held. Use the
|
||||
* GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK() and GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK()
|
||||
* macros for this purpose.
|
||||
*/
|
||||
void
|
||||
gst_raw_base_parse_invalidate_src_caps (GstRawBaseParse * raw_base_parse)
|
||||
{
|
||||
/* must be called with lock */
|
||||
g_assert (raw_base_parse != NULL);
|
||||
raw_base_parse->src_caps_set = FALSE;
|
||||
}
|
207
gst/rawparse/gstrawbaseparse.h
Normal file
207
gst/rawparse/gstrawbaseparse.h
Normal file
|
@ -0,0 +1,207 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#ifndef __GST_RAW_BASE_PARSE_H__
|
||||
#define __GST_RAW_BASE_PARSE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/base.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_RAW_BASE_PARSE \
|
||||
(gst_raw_base_parse_get_type())
|
||||
#define GST_RAW_BASE_PARSE(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_RAW_BASE_PARSE, GstRawBaseParse))
|
||||
#define GST_RAW_BASE_PARSE_CAST(obj) \
|
||||
((GstRawBaseParse *)(obj))
|
||||
#define GST_RAW_BASE_PARSE_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_RAW_BASE_PARSE, GstRawBaseParseClass))
|
||||
#define GST_RAW_BASE_PARSE_GET_CLASS(obj) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_RAW_BASE_PARSE, GstRawBaseParseClass))
|
||||
#define GST_IS_RAW_BASE_PARSE(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_RAW_BASE_PARSE))
|
||||
#define GST_IS_RAW_BASE_PARSE_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_RAW_BASE_PARSE))
|
||||
|
||||
#define GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK(obj) g_mutex_lock(&(((GstRawBaseParse *)(obj))->config_mutex))
|
||||
#define GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK(obj) g_mutex_unlock(&(((GstRawBaseParse *)(obj))->config_mutex))
|
||||
|
||||
|
||||
typedef enum _GstRawBaseParseConfig GstRawBaseParseConfig;
|
||||
typedef struct _GstRawBaseParse GstRawBaseParse;
|
||||
typedef struct _GstRawBaseParseClass GstRawBaseParseClass;
|
||||
|
||||
|
||||
/**
|
||||
* GstRawBaseParseConfig:
|
||||
* @GST_RAW_BASE_PARSE_CONFIG_CURRENT: configuration that is currently active
|
||||
* @GST_RAW_BASE_PARSE_CONFIG_SINKCAPS: configuration that is defined by the input sink caps
|
||||
* @GST_RAW_BASE_PARSE_CONFIG_PROPERTIES: configuration that is defined by class properties
|
||||
*
|
||||
* Identifier for the type of parser configuration.
|
||||
*/
|
||||
enum _GstRawBaseParseConfig
|
||||
{
|
||||
GST_RAW_BASE_PARSE_CONFIG_CURRENT = 1,
|
||||
GST_RAW_BASE_PARSE_CONFIG_SINKCAPS,
|
||||
GST_RAW_BASE_PARSE_CONFIG_PROPERTIES
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* GstRawBaseParse:
|
||||
*
|
||||
* The opaque #GstRawBaseParse data structure.
|
||||
*/
|
||||
struct _GstRawBaseParse
|
||||
{
|
||||
GstBaseParse parent;
|
||||
|
||||
/*< private > */
|
||||
|
||||
/* TRUE if the source pad caps have been set already. This is used
|
||||
* for checking if the source pad caps have to be set. */
|
||||
gboolean src_caps_set;
|
||||
|
||||
/* Mutex which protects access to and modifications on the configs. */
|
||||
GMutex config_mutex;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* GstRawBaseParseClass:
|
||||
* @parent_class: The parent class structure
|
||||
* @set_current_config: Sets the new current configuration. Subclasses must internally
|
||||
* switch to this new configuration. Return FALSE if this failed,
|
||||
* TRUE otherwise.
|
||||
* @get_current_config: Gets the current configuration. All return values except
|
||||
* except GST_RAW_BASE_PARSE_CONFIG_CURRENT are valid.
|
||||
* @set_config_from_caps: Parses the caps and copies its information to the configuration.
|
||||
* Returns FALSE if this failed, TRUE otheriwse. Specified caps
|
||||
* are not unref'd.
|
||||
* @get_caps_from_config: Creates a new caps structure out of the information from the
|
||||
* specified configuration. Ownership over the returned caps are
|
||||
* transferred to the caller. If something fails during the caps
|
||||
* creation, the vfunc must make sure to destroy any partially
|
||||
* created caps; the *caps value is always set to NULL in case of
|
||||
* failure. Returns FALSE in case of failure,
|
||||
* TRUE in case of success.
|
||||
* @get_config_frame_size: Gets the size of one frame, in bytes, from the specified
|
||||
* configuration. This must be the size of the complete frame,
|
||||
* including any overhead (metadata, headers, padding bytes etc.).
|
||||
* @get_max_frames_per_buffer: Optional.
|
||||
* Returns up to how many complete frames one output buffer may
|
||||
* contain. The value must be nonzero. This is useful for example
|
||||
* with video parsers which need to ensure that one output buffer
|
||||
* contains only one video frame, even if the input buffer contains
|
||||
* several complete frames. If this vfunc is not set, then there
|
||||
* is no maximum number of frames per buffer - the parser reads
|
||||
* as many complete frames as possible from the input buffer.
|
||||
* @is_config_ready: Returns TRUE if the specified configuration is ready, FALSE
|
||||
* otherwise.
|
||||
* @process: Optional.
|
||||
* This is useful to do any last minute processing before the
|
||||
* data is pushed downstream. One example is channel reordering
|
||||
* in audio parsers.
|
||||
* in_data is the complete input buffer, total_num_in_bytes is
|
||||
* the total amount of bytes this input buffer contains (including
|
||||
* excess bytes that form an incomplete rame). num_valid_in_bytes
|
||||
* is the subset of these bytes that are to be pushed downstream.
|
||||
* If for example the frame size is 4, and total_num_in_bytes is
|
||||
* 411, then num_valid_in_bytes will be 408, since the last 3
|
||||
* bytes form an incomplete frame.
|
||||
* The value of num_valid_in_bytes excludes the overhead bytes
|
||||
* indicated by @get_overhead_size.
|
||||
* If the subclass creates a new buffer here, *processed_data
|
||||
* must be set to the new buffer's pointer. If the subclass does
|
||||
* not create any new buffer, and just expects the first
|
||||
* num_valid_in_bytes of the input buffer to be pushed downstream,
|
||||
* then *processed_data must be set to NULL.
|
||||
* If this vfunc is not set, then the parser behaves as if this
|
||||
* vfunc set *processed_data data to NULL.
|
||||
* @is_unit_format_supported: Returns TRUE if the given format is supported by the
|
||||
* @get_units_per_second function, FALSE otherwise.
|
||||
* @get_units_per_second: Returns how many units per second exist for a given format.
|
||||
* For example, with an audio parser and format DEFAULT, the units
|
||||
* per second are typically the number of samples per second
|
||||
* (= the sample rate). For video parsers, this would be the frame
|
||||
* rate. If BYTES or TIME are used as format, then the result must
|
||||
* not include any extra overhead (metadata, headers, padding etc.)
|
||||
* @get_overhead_size: Optional.
|
||||
* Returns the number of bytes that make up the portion of a frame
|
||||
* that isn't payload. Examples are padding bytes, headers, and
|
||||
* other kinds of metadata. If this vfunc isn't defined, then an
|
||||
* overhead size of 0 bytes is assumed.
|
||||
*
|
||||
* Subclasses are required to override all vfuncs except for @process, which is optional.
|
||||
* The raw base parser lock is held during all vfunc calls.
|
||||
*/
|
||||
struct _GstRawBaseParseClass
|
||||
{
|
||||
GstBaseParseClass parent_class;
|
||||
|
||||
gboolean (*set_current_config) (GstRawBaseParse *raw_base_parse,
|
||||
GstRawBaseParseConfig config);
|
||||
GstRawBaseParseConfig (*get_current_config) (GstRawBaseParse *raw_base_parse);
|
||||
|
||||
gboolean (*set_config_from_caps) (GstRawBaseParse * raw_base_parse,
|
||||
GstRawBaseParseConfig config,
|
||||
GstCaps * caps);
|
||||
gboolean (*get_caps_from_config) (GstRawBaseParse * raw_base_parse,
|
||||
GstRawBaseParseConfig config,
|
||||
GstCaps ** caps);
|
||||
|
||||
gsize (*get_config_frame_size) (GstRawBaseParse * raw_base_parse,
|
||||
GstRawBaseParseConfig config);
|
||||
guint (*get_max_frames_per_buffer) (GstRawBaseParse * raw_base_parse,
|
||||
GstRawBaseParseConfig config);
|
||||
|
||||
gboolean (*is_config_ready) (GstRawBaseParse * raw_base_parse,
|
||||
GstRawBaseParseConfig config);
|
||||
|
||||
gboolean (*process) (GstRawBaseParse * raw_base_parse,
|
||||
GstRawBaseParseConfig config,
|
||||
GstBuffer * in_data,
|
||||
gsize total_num_in_bytes,
|
||||
gsize num_valid_in_bytes,
|
||||
GstBuffer ** processed_data);
|
||||
|
||||
gboolean (*is_unit_format_supported) (GstRawBaseParse * raw_base_parse,
|
||||
GstFormat format);
|
||||
void (*get_units_per_second) (GstRawBaseParse * raw_base_parse,
|
||||
GstFormat format,
|
||||
GstRawBaseParseConfig config,
|
||||
gsize * units_per_sec_n,
|
||||
gsize * units_per_sec_d);
|
||||
|
||||
gint (*get_overhead_size) (GstRawBaseParse * raw_base_parse,
|
||||
GstRawBaseParseConfig config);
|
||||
};
|
||||
|
||||
|
||||
void gst_raw_base_parse_invalidate_src_caps (GstRawBaseParse * raw_base_parse);
|
||||
|
||||
|
||||
GType gst_raw_base_parse_get_type (void);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
1131
gst/rawparse/gstrawvideoparse.c
Normal file
1131
gst/rawparse/gstrawvideoparse.c
Normal file
File diff suppressed because it is too large
Load diff
116
gst/rawparse/gstrawvideoparse.h
Normal file
116
gst/rawparse/gstrawvideoparse.h
Normal file
|
@ -0,0 +1,116 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#ifndef __GST_RAW_VIDEO_PARSE_H__
|
||||
#define __GST_RAW_VIDEO_PARSE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/video/video.h>
|
||||
#include "gstrawbaseparse.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_RAW_VIDEO_PARSE \
|
||||
(gst_raw_video_parse_get_type())
|
||||
#define GST_RAW_VIDEO_PARSE(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_RAW_VIDEO_PARSE, GstRawVideoParse))
|
||||
#define GST_RAW_VIDEO_PARSE_CAST(obj) \
|
||||
((GstRawVideoParse *)(obj))
|
||||
#define GST_RAW_VIDEO_PARSE_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_RAW_VIDEO_PARSE, GstRawVideoParseClass))
|
||||
#define GST_IS_RAW_VIDEO_PARSE(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_RAW_VIDEO_PARSE))
|
||||
#define GST_IS_RAW_VIDEO_PARSE_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_RAW_VIDEO_PARSE))
|
||||
|
||||
|
||||
typedef struct _GstRawVideoParseConfig GstRawVideoParseConfig;
|
||||
typedef struct _GstRawVideoParse GstRawVideoParse;
|
||||
typedef struct _GstRawVideoParseClass GstRawVideoParseClass;
|
||||
|
||||
|
||||
/* Contains information about the video frame format. */
|
||||
struct _GstRawVideoParseConfig
|
||||
{
|
||||
/* If TRUE, then this configuration is ready to use */
|
||||
gboolean ready;
|
||||
|
||||
/* FIXME: These values should not be necessary, since there's
|
||||
* GstVideoInfo. However, setting these values in the video
|
||||
* info independently is currently difficult. For example,
|
||||
* setting the video format requires the gst_video_info_set_format()
|
||||
* function, but this function also overwrites plane strides
|
||||
* and offsets. */
|
||||
gint width, height;
|
||||
GstVideoFormat format;
|
||||
gint pixel_aspect_ratio_n, pixel_aspect_ratio_d;
|
||||
gint framerate_n, framerate_d;
|
||||
gboolean interlaced;
|
||||
gsize plane_offsets[GST_VIDEO_MAX_PLANES];
|
||||
gint plane_strides[GST_VIDEO_MAX_PLANES];
|
||||
|
||||
/* If TRUE, then TFF flags are added to outgoing buffers and
|
||||
* their video metadata */
|
||||
gboolean top_field_first;
|
||||
|
||||
/* Distance between the start of each frame, in bytes. If this value
|
||||
* is larger than the actual size of a frame, then the extra bytes
|
||||
* are skipped. For example, with frames that have 115200 bytes, a
|
||||
* frame_stride value of 120000 means that 4800 trailing bytes are
|
||||
* skipped after the 115200 frame bytes. This is useful to skip
|
||||
* metadata in between frames. */
|
||||
guint frame_stride;
|
||||
|
||||
GstVideoInfo info;
|
||||
};
|
||||
|
||||
|
||||
struct _GstRawVideoParse
|
||||
{
|
||||
GstRawBaseParse parent;
|
||||
|
||||
/*< private > */
|
||||
|
||||
/* Configuration controlled by the object properties. Its ready value
|
||||
* is set to TRUE from the start, so it can be used right away.
|
||||
*/
|
||||
GstRawVideoParseConfig properties_config;
|
||||
/* Configuration controlled by the sink caps. Its ready value is
|
||||
* initially set to FALSE until valid sink caps come in. It is set to
|
||||
* FALSE again when the stream-start event is observed.
|
||||
*/
|
||||
GstRawVideoParseConfig sink_caps_config;
|
||||
/* Currently active configuration. Points either to properties_config
|
||||
* or to sink_caps_config. This is never NULL. */
|
||||
GstRawVideoParseConfig *current_config;
|
||||
};
|
||||
|
||||
|
||||
struct _GstRawVideoParseClass
|
||||
{
|
||||
GstRawBaseParseClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
GType gst_raw_video_parse_get_type (void);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
|
@ -96,7 +96,7 @@ gst_unaligned_audio_parse_init (GstUnalignedAudioParse * unaligned_audio_parse)
|
|||
GstPad *ghostpad;
|
||||
|
||||
unaligned_audio_parse->inner_parser =
|
||||
gst_element_factory_make ("audioparse", "inner_parser");
|
||||
gst_element_factory_make ("rawaudioaudioparse", "inner_parser");
|
||||
g_assert (unaligned_audio_parse->inner_parser != NULL);
|
||||
|
||||
g_object_set (G_OBJECT (unaligned_audio_parse->inner_parser),
|
||||
|
|
124
gst/rawparse/gstunalignedvideoparse.c
Normal file
124
gst/rawparse/gstunalignedvideoparse.c
Normal file
|
@ -0,0 +1,124 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2016 Carlos Rafael Giani <dv@pseudoterminal.org>
|
||||
*
|
||||
* gstunalignedvideoparse.c:
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/video/video.h>
|
||||
#include "gstunalignedvideoparse.h"
|
||||
#include "unalignedvideo.h"
|
||||
|
||||
|
||||
GST_DEBUG_CATEGORY (unaligned_video_parse_debug);
|
||||
#define GST_CAT_DEFAULT unaligned_video_parse_debug
|
||||
|
||||
|
||||
struct _GstUnalignedVideoParse
|
||||
{
|
||||
GstBin parent;
|
||||
GstElement *inner_parser;
|
||||
};
|
||||
|
||||
|
||||
struct _GstUnalignedVideoParseClass
|
||||
{
|
||||
GstBinClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
static GstStaticPadTemplate static_sink_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS (GST_UNALIGNED_RAW_VIDEO_CAPS)
|
||||
);
|
||||
|
||||
|
||||
static GstStaticPadTemplate static_src_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL))
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
G_DEFINE_TYPE (GstUnalignedVideoParse, gst_unaligned_video_parse, GST_TYPE_BIN);
|
||||
|
||||
|
||||
static void
|
||||
gst_unaligned_video_parse_class_init (GstUnalignedVideoParseClass * klass)
|
||||
{
|
||||
GstElementClass *element_class;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (unaligned_video_parse_debug, "unalignedvideoparse",
|
||||
0, "Unaligned raw video parser");
|
||||
|
||||
element_class = GST_ELEMENT_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));
|
||||
|
||||
gst_element_class_set_static_metadata (element_class,
|
||||
"unalignedvideoparse",
|
||||
"Codec/Parser/Bin/Video",
|
||||
"Parse unaligned raw video data",
|
||||
"Carlos Rafael Giani <dv@pseudoterminal.org>");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gst_unaligned_video_parse_init (GstUnalignedVideoParse * unaligned_video_parse)
|
||||
{
|
||||
GstPad *inner_pad;
|
||||
GstPad *ghostpad;
|
||||
|
||||
unaligned_video_parse->inner_parser =
|
||||
gst_element_factory_make ("rawvideoparse", "inner_parser");
|
||||
g_assert (unaligned_video_parse->inner_parser != NULL);
|
||||
|
||||
g_object_set (G_OBJECT (unaligned_video_parse->inner_parser),
|
||||
"use-sink-caps", TRUE, NULL);
|
||||
|
||||
gst_bin_add (GST_BIN (unaligned_video_parse),
|
||||
unaligned_video_parse->inner_parser);
|
||||
|
||||
inner_pad =
|
||||
gst_element_get_static_pad (unaligned_video_parse->inner_parser, "sink");
|
||||
ghostpad =
|
||||
gst_ghost_pad_new_from_template ("sink", inner_pad,
|
||||
gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS
|
||||
(unaligned_video_parse), "sink"));
|
||||
gst_element_add_pad (GST_ELEMENT (unaligned_video_parse), ghostpad);
|
||||
gst_object_unref (GST_OBJECT (inner_pad));
|
||||
|
||||
inner_pad = gst_element_get_static_pad (unaligned_video_parse->inner_parser,
|
||||
"src");
|
||||
ghostpad =
|
||||
gst_ghost_pad_new_from_template ("src", inner_pad,
|
||||
gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS
|
||||
(unaligned_video_parse), "src"));
|
||||
gst_element_add_pad (GST_ELEMENT (unaligned_video_parse), ghostpad);
|
||||
gst_object_unref (GST_OBJECT (inner_pad));
|
||||
}
|
49
gst/rawparse/gstunalignedvideoparse.h
Normal file
49
gst/rawparse/gstunalignedvideoparse.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2016 Carlos Rafael Giani <dv@pseudoterminal.org>
|
||||
*
|
||||
* gstunalignedvideoparse.h:
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __GST_UNALIGNED_VIDEO_PARSE_H___
|
||||
#define __GST_UNALIGNED_VIDEO_PARSE_H___
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_UNALIGNED_VIDEO_PARSE \
|
||||
(gst_unaligned_video_parse_get_type())
|
||||
#define GST_UNALIGNED_VIDEO_PARSE(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_UNALIGNED_VIDEO_PARSE, GstUnalignedVideoParse))
|
||||
#define GST_UNALIGNED_VIDEO_PARSE_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_UNALIGNED_VIDEO_PARSE, GstUnalignedVideoParseClass))
|
||||
#define GST_UNALIGNED_VIDEO_PARSE_CAST(obj) \
|
||||
((GstUnalignedVideoParse *)(obj))
|
||||
#define GST_IS_UNALIGNED_VIDEO_PARSE(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_UNALIGNED_VIDEO_PARSE))
|
||||
#define GST_IS_UNALIGNED_VIDEO_PARSE_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_UNALIGNED_VIDEO_PARSE))
|
||||
|
||||
typedef struct _GstUnalignedVideoParse GstUnalignedVideoParse;
|
||||
typedef struct _GstUnalignedVideoParseClass GstUnalignedVideoParseClass;
|
||||
|
||||
GType gst_unaligned_video_parse_get_type (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_UNALIGNED_VIDEO_PARSE_H___ */
|
|
@ -3,7 +3,10 @@
|
|||
#endif
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstrawaudioparse.h"
|
||||
#include "gstrawvideoparse.h"
|
||||
#include "gstunalignedaudioparse.h"
|
||||
#include "gstunalignedvideoparse.h"
|
||||
#include "gstaudioparse.h"
|
||||
#include "gstvideoparse.h"
|
||||
|
||||
|
@ -18,6 +21,12 @@ plugin_init (GstPlugin * plugin)
|
|||
gst_audio_parse_get_type ());
|
||||
ret &= gst_element_register (plugin, "unalignedaudioparse", GST_RANK_MARGINAL,
|
||||
gst_unaligned_audio_parse_get_type ());
|
||||
ret &= gst_element_register (plugin, "unalignedvideoparse", GST_RANK_MARGINAL,
|
||||
gst_unaligned_video_parse_get_type ());
|
||||
ret &= gst_element_register (plugin, "rawaudioparse", GST_RANK_NONE,
|
||||
gst_raw_audio_parse_get_type ());
|
||||
ret &= gst_element_register (plugin, "rawvideoparse", GST_RANK_NONE,
|
||||
gst_raw_video_parse_get_type ());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
35
gst/rawparse/unalignedvideo.h
Normal file
35
gst/rawparse/unalignedvideo.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2016 Carlos Rafael Giani <dv@pseudoterminal.org>
|
||||
*
|
||||
* unalignedvideo.h:
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __GST_UNALIGNED_VIDEO_H__
|
||||
#define __GST_UNALIGNED_VIDEO_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/video/video.h>
|
||||
|
||||
#define GST_UNALIGNED_RAW_VIDEO_CAPS \
|
||||
"video/x-unaligned-raw" \
|
||||
", format = (string) " GST_VIDEO_FORMATS_ALL \
|
||||
", width = " GST_VIDEO_SIZE_RANGE \
|
||||
", height = " GST_VIDEO_SIZE_RANGE \
|
||||
", framerate = " GST_VIDEO_FPS_RANGE
|
||||
|
||||
#endif /* __GST_UNALIGNED_VIDEO_H__ */
|
339
tests/check/elements/rawaudioparse.c
Normal file
339
tests/check/elements/rawaudioparse.c
Normal file
|
@ -0,0 +1,339 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* unit test for rawaudioparse
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* 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 <gst/check/gstcheck.h>
|
||||
#include <gst/audio/audio.h>
|
||||
|
||||
/* Checks are hardcoded to expect stereo 16-bit data. The sample rate
|
||||
* however varies from the default of 40 kHz in some tests to see the
|
||||
* differences in calculated buffer durations. */
|
||||
#define NUM_TEST_SAMPLES 512
|
||||
#define NUM_TEST_CHANNELS 2
|
||||
#define TEST_SAMPLE_RATE 40000
|
||||
#define TEST_SAMPLE_FORMAT GST_AUDIO_FORMAT_S16
|
||||
|
||||
/* For ease of programming we use globals to keep refs for our floating
|
||||
* src and sink pads we create; otherwise we always have to do get_pad,
|
||||
* get_peer, and then remove references in every test function */
|
||||
static GstPad *mysrcpad, *mysinkpad;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstElement *rawaudioparse;
|
||||
GstAdapter *test_data_adapter;
|
||||
}
|
||||
RawAudParseTestCtx;
|
||||
|
||||
/* Sets up a rawaudioparse element and a GstAdapter that contains 512 test
|
||||
* audio samples. The samples a monotonically increasing set from the values
|
||||
* 0 to 511 for the left and 512 to 1023 for the right channel. The result
|
||||
* is a GstAdapter that contains the interleaved 16-bit integer values:
|
||||
* 0,512,1,513,2,514, ... 511,1023 . This set is used in the checks to see
|
||||
* if rawaudioparse's output buffers contain valid data. */
|
||||
static void
|
||||
setup_rawaudioparse (RawAudParseTestCtx * testctx, gboolean use_sink_caps,
|
||||
gboolean set_properties, GstCaps * incaps, GstFormat format)
|
||||
{
|
||||
GstElement *rawaudioparse;
|
||||
GstAdapter *test_data_adapter;
|
||||
GstBuffer *buffer;
|
||||
guint i;
|
||||
guint16 samples[NUM_TEST_SAMPLES * NUM_TEST_CHANNELS];
|
||||
|
||||
|
||||
/* Setup the rawaudioparse element and the pads */
|
||||
|
||||
static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL))
|
||||
);
|
||||
static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS_ANY);
|
||||
|
||||
rawaudioparse = gst_check_setup_element ("rawaudioparse");
|
||||
|
||||
g_object_set (G_OBJECT (rawaudioparse), "use-sink-caps", use_sink_caps, NULL);
|
||||
if (set_properties)
|
||||
g_object_set (G_OBJECT (rawaudioparse), "sample-rate", TEST_SAMPLE_RATE,
|
||||
"num-channels", NUM_TEST_CHANNELS, "pcm-format", TEST_SAMPLE_FORMAT,
|
||||
NULL);
|
||||
|
||||
fail_unless (gst_element_set_state (rawaudioparse,
|
||||
GST_STATE_PAUSED) == GST_STATE_CHANGE_SUCCESS,
|
||||
"could not set to paused");
|
||||
|
||||
mysrcpad = gst_check_setup_src_pad (rawaudioparse, &srctemplate);
|
||||
mysinkpad = gst_check_setup_sink_pad (rawaudioparse, &sinktemplate);
|
||||
|
||||
gst_pad_set_active (mysrcpad, TRUE);
|
||||
gst_pad_set_active (mysinkpad, TRUE);
|
||||
|
||||
gst_check_setup_events (mysrcpad, rawaudioparse, incaps, format);
|
||||
if (incaps)
|
||||
gst_caps_unref (incaps);
|
||||
|
||||
|
||||
/* Fill the adapter with the interleaved 0..511 and
|
||||
* 512..1023 samples */
|
||||
for (i = 0; i < NUM_TEST_SAMPLES; ++i) {
|
||||
guint c;
|
||||
for (c = 0; c < NUM_TEST_CHANNELS; ++c)
|
||||
samples[i * NUM_TEST_CHANNELS + c] = c * NUM_TEST_SAMPLES + i;
|
||||
}
|
||||
|
||||
test_data_adapter = gst_adapter_new ();
|
||||
buffer = gst_buffer_new_allocate (NULL, sizeof (samples), NULL);
|
||||
gst_buffer_fill (buffer, 0, samples, sizeof (samples));
|
||||
gst_adapter_push (test_data_adapter, buffer);
|
||||
|
||||
|
||||
testctx->rawaudioparse = rawaudioparse;
|
||||
testctx->test_data_adapter = test_data_adapter;
|
||||
}
|
||||
|
||||
static void
|
||||
cleanup_rawaudioparse (RawAudParseTestCtx * testctx)
|
||||
{
|
||||
gst_pad_set_active (mysrcpad, FALSE);
|
||||
gst_pad_set_active (mysinkpad, FALSE);
|
||||
gst_check_teardown_src_pad (testctx->rawaudioparse);
|
||||
gst_check_teardown_sink_pad (testctx->rawaudioparse);
|
||||
gst_check_teardown_element (testctx->rawaudioparse);
|
||||
|
||||
g_object_unref (G_OBJECT (testctx->test_data_adapter));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
push_data_and_check_output (RawAudParseTestCtx * testctx, gsize num_in_bytes,
|
||||
gsize expected_num_out_bytes, gint64 expected_pts, gint64 expected_dur,
|
||||
guint expected_num_buffers_in_list, guint bpf, guint16 channel0_start,
|
||||
guint16 channel1_start)
|
||||
{
|
||||
GstBuffer *inbuf, *outbuf;
|
||||
guint num_buffers;
|
||||
|
||||
/* Simulate upstream input by taking num_in_bytes bytes from the adapter */
|
||||
inbuf = gst_adapter_take_buffer (testctx->test_data_adapter, num_in_bytes);
|
||||
fail_unless (inbuf != NULL);
|
||||
|
||||
/* Push the input data and check that the output buffers list grew as
|
||||
* expected */
|
||||
fail_unless (gst_pad_push (mysrcpad, inbuf) == GST_FLOW_OK);
|
||||
num_buffers = g_list_length (buffers);
|
||||
fail_unless_equals_int (num_buffers, expected_num_buffers_in_list);
|
||||
|
||||
/* Take the latest output buffer */
|
||||
outbuf = g_list_nth_data (buffers, num_buffers - 1);
|
||||
fail_unless (outbuf != NULL);
|
||||
|
||||
/* Verify size, PTS, duration of the output buffer */
|
||||
fail_unless_equals_uint64 (expected_num_out_bytes,
|
||||
gst_buffer_get_size (outbuf));
|
||||
fail_unless_equals_uint64 (expected_pts, GST_BUFFER_PTS (outbuf));
|
||||
fail_unless_equals_uint64 (expected_dur, GST_BUFFER_DURATION (outbuf));
|
||||
|
||||
/* Go through all of the samples in the output buffer and check that they are
|
||||
* valid. The samples are interleaved. The offsets specified by channel0_start
|
||||
* and channel1_start are the expected values of the first sample for each
|
||||
* channel in the buffer. So, if channel0_start is 512, then sample #0 in the
|
||||
* buffer must have value 512, and if channel1_start is 700, then sample #1
|
||||
* in the buffer must have value 700 etc. */
|
||||
{
|
||||
guint i, num_frames;
|
||||
guint16 *s;
|
||||
GstMapInfo map_info;
|
||||
guint channel_starts[2] = { channel0_start, channel1_start };
|
||||
|
||||
gst_buffer_map (outbuf, &map_info, GST_MAP_READ);
|
||||
num_frames = map_info.size / bpf;
|
||||
s = (guint16 *) (map_info.data);
|
||||
|
||||
for (i = 0; i < num_frames; ++i) {
|
||||
guint c;
|
||||
|
||||
for (c = 0; i < NUM_TEST_CHANNELS; ++i) {
|
||||
guint16 expected = channel_starts[c] + i;
|
||||
guint16 actual = s[i * NUM_TEST_CHANNELS + c];
|
||||
|
||||
fail_unless_equals_int (expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
gst_buffer_unmap (outbuf, &map_info);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GST_START_TEST (test_push_unaligned_data_properties_config)
|
||||
{
|
||||
RawAudParseTestCtx testctx;
|
||||
|
||||
setup_rawaudioparse (&testctx, FALSE, TRUE, NULL, GST_FORMAT_BYTES);
|
||||
|
||||
/* Send in data buffers that are not aligned to multiples of the
|
||||
* frame size (= sample size * num_channels). This tests if rawaudioparse
|
||||
* aligns output data properly.
|
||||
*
|
||||
* The second line sends in 99 bytes, and expects 100 bytes in the
|
||||
* output buffer. This is because the first buffer contains 45 bytes,
|
||||
* and rawaudioparse is expected to output 44 bytes (which is an integer
|
||||
* multiple of the frame size). The leftover 1 byte then gets prepended
|
||||
* to the input buffer with 99 bytes, resulting in 100 bytes, which is
|
||||
* an integer multiple of the frame size.
|
||||
*/
|
||||
|
||||
push_data_and_check_output (&testctx, 45, 44, GST_USECOND * 0,
|
||||
GST_USECOND * 275, 1, 4, 0, 512);
|
||||
push_data_and_check_output (&testctx, 99, 100, GST_USECOND * 275,
|
||||
GST_USECOND * 625, 2, 4, 11, 523);
|
||||
push_data_and_check_output (&testctx, 18, 16, GST_USECOND * 900,
|
||||
GST_USECOND * 100, 3, 4, 36, 548);
|
||||
|
||||
cleanup_rawaudioparse (&testctx);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_push_unaligned_data_sink_caps_config)
|
||||
{
|
||||
RawAudParseTestCtx testctx;
|
||||
GstAudioInfo ainfo;
|
||||
GstCaps *caps;
|
||||
|
||||
/* This test is essentially the same as test_push_unaligned_data_properties_config,
|
||||
* except that rawaudioparse uses the sink caps config instead of the property config. */
|
||||
|
||||
gst_audio_info_set_format (&ainfo, TEST_SAMPLE_FORMAT, TEST_SAMPLE_RATE,
|
||||
NUM_TEST_CHANNELS, NULL);
|
||||
caps = gst_audio_info_to_caps (&ainfo);
|
||||
|
||||
setup_rawaudioparse (&testctx, TRUE, FALSE, caps, GST_FORMAT_BYTES);
|
||||
|
||||
push_data_and_check_output (&testctx, 45, 44, GST_USECOND * 0,
|
||||
GST_USECOND * 275, 1, 4, 0, 512);
|
||||
push_data_and_check_output (&testctx, 99, 100, GST_USECOND * 275,
|
||||
GST_USECOND * 625, 2, 4, 11, 523);
|
||||
push_data_and_check_output (&testctx, 18, 16, GST_USECOND * 900,
|
||||
GST_USECOND * 100, 3, 4, 36, 548);
|
||||
|
||||
cleanup_rawaudioparse (&testctx);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_push_swapped_channels)
|
||||
{
|
||||
RawAudParseTestCtx testctx;
|
||||
GValueArray *valarray;
|
||||
GValue val = G_VALUE_INIT;
|
||||
|
||||
/* Send in 40 bytes and use a nonstandard channel order (left and right channels
|
||||
* swapped). Expected behavior is for rawaudioparse to reorder the samples inside
|
||||
* output buffers to conform to the GStreamer channel order. For this reason,
|
||||
* channel0 offset is 512 and channel1 offset is 0 in the check below. */
|
||||
|
||||
setup_rawaudioparse (&testctx, FALSE, TRUE, NULL, GST_FORMAT_BYTES);
|
||||
|
||||
valarray = g_value_array_new (2);
|
||||
g_value_init (&val, GST_TYPE_AUDIO_CHANNEL_POSITION);
|
||||
g_value_set_enum (&val, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT);
|
||||
g_value_array_insert (valarray, 0, &val);
|
||||
g_value_set_enum (&val, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT);
|
||||
g_value_array_insert (valarray, 1, &val);
|
||||
g_object_set (G_OBJECT (testctx.rawaudioparse), "channel-positions",
|
||||
valarray, NULL);
|
||||
g_value_array_free (valarray);
|
||||
g_value_unset (&val);
|
||||
|
||||
push_data_and_check_output (&testctx, 40, 40, GST_USECOND * 0,
|
||||
GST_USECOND * 250, 1, 4, 512, 0);
|
||||
|
||||
cleanup_rawaudioparse (&testctx);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_config_switch)
|
||||
{
|
||||
RawAudParseTestCtx testctx;
|
||||
GstAudioInfo ainfo;
|
||||
GstCaps *caps;
|
||||
|
||||
/* Start processing with the properties config active, then mid-stream switch to
|
||||
* the sink caps config. The properties config is altered to have a different
|
||||
* sample rate than the sink caps to be able to detect the switch. The net effect
|
||||
* is that output buffer durations are altered. For example, 40 bytes equal
|
||||
* 10 samples, and this equals 500 us with 20 kHz or 250 us with 40 kHz. */
|
||||
|
||||
gst_audio_info_set_format (&ainfo, TEST_SAMPLE_FORMAT, TEST_SAMPLE_RATE,
|
||||
NUM_TEST_CHANNELS, NULL);
|
||||
caps = gst_audio_info_to_caps (&ainfo);
|
||||
|
||||
setup_rawaudioparse (&testctx, FALSE, TRUE, caps, GST_FORMAT_BYTES);
|
||||
|
||||
g_object_set (G_OBJECT (testctx.rawaudioparse), "sample-rate", 20000, NULL);
|
||||
|
||||
/* Push in data with properties config active, expecting duration calculations
|
||||
* to be based on the 20 kHz sample rate */
|
||||
push_data_and_check_output (&testctx, 40, 40, GST_USECOND * 0,
|
||||
GST_USECOND * 500, 1, 4, 0, 512);
|
||||
push_data_and_check_output (&testctx, 20, 20, GST_USECOND * 500,
|
||||
GST_USECOND * 250, 2, 4, 10, 522);
|
||||
|
||||
/* Perform the switch */
|
||||
g_object_set (G_OBJECT (testctx.rawaudioparse), "use-sink-caps", TRUE, NULL);
|
||||
|
||||
/* Push in data with sink caps config active, expecting duration calculations
|
||||
* to be based on the 40 kHz sample rate */
|
||||
push_data_and_check_output (&testctx, 40, 40, GST_USECOND * 750,
|
||||
GST_USECOND * 250, 3, 4, 15, 527);
|
||||
|
||||
cleanup_rawaudioparse (&testctx);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
|
||||
static Suite *
|
||||
rawaudioparse_suite (void)
|
||||
{
|
||||
Suite *s = suite_create ("rawaudioparse");
|
||||
TCase *tc_chain = tcase_create ("general");
|
||||
|
||||
suite_add_tcase (s, tc_chain);
|
||||
tcase_add_test (tc_chain, test_push_unaligned_data_properties_config);
|
||||
tcase_add_test (tc_chain, test_push_unaligned_data_sink_caps_config);
|
||||
tcase_add_test (tc_chain, test_push_swapped_channels);
|
||||
tcase_add_test (tc_chain, test_config_switch);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
GST_CHECK_MAIN (rawaudioparse);
|
459
tests/check/elements/rawvideoparse.c
Normal file
459
tests/check/elements/rawvideoparse.c
Normal file
|
@ -0,0 +1,459 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* unit test for rawvideoparse
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* 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 <gst/check/gstcheck.h>
|
||||
#include <gst/video/video.h>
|
||||
|
||||
/* The checks use as test data an 8x8 Y444 image, with 25 Hz framerate. In the
|
||||
* sink caps configuration, the stride is 8 bytes, and the frames are tightly
|
||||
* packed together. In the properties configuration, the stride is 10 bytes, the
|
||||
* planes aren't tightly packed (there are 20 bytes between the planes), and the
|
||||
* frames overall have padding between them (the overall frame size is
|
||||
* stride (10) * height (8) * num-planes (3) + bytes-between-planes (20) * 2
|
||||
* = 280 bytes, and the frame stride is 500 bytes, so there are 220 bytes of
|
||||
* extra padding between frames).
|
||||
*
|
||||
* In the test 8x8 frame, the pixels are all set to #000000, except for two
|
||||
* pixels: (xofs+1 yofs+0) is set to #8899AA, (xofs+0 yofs+1) is set to #112233.
|
||||
* The first frame uses the offsets xofs=0 yofs=0. The second frame uses
|
||||
* xofs=1 yofs=0 etc. For each configuration, there is a separate set of frames,
|
||||
* each stored in the GstAdapter in the Context struct.
|
||||
*
|
||||
* During the tests, as part of the checks, the pixels are verified to have the
|
||||
* right values. The pattern of the pixels was chosen to easily detect stride
|
||||
* errors, incorrect plane offsets etc.
|
||||
*/
|
||||
|
||||
#define TEST_WIDTH 8
|
||||
#define TEST_HEIGHT 8
|
||||
#define TEST_FRAMERATE_N 25
|
||||
#define TEST_FRAMERATE_D 1
|
||||
#define TEST_FRAME_FORMAT GST_VIDEO_FORMAT_Y444
|
||||
#define NUM_TEST_PLANES 3
|
||||
|
||||
#define PROP_CTX_PLANE_STRIDE 10
|
||||
#define PROP_CTX_FRAME_STRIDE 500
|
||||
#define PROP_CTX_PLANE_PADDING 20
|
||||
#define PROP_CTX_PLANE_SIZE (PROP_CTX_PLANE_STRIDE * TEST_HEIGHT + PROP_CTX_PLANE_PADDING)
|
||||
|
||||
GstElement *rawvideoparse;
|
||||
|
||||
/* For ease of programming we use globals to keep refs for our floating
|
||||
* src and sink pads we create; otherwise we always have to do get_pad,
|
||||
* get_peer, and then remove references in every test function */
|
||||
static GstPad *mysrcpad, *mysinkpad;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstAdapter *data;
|
||||
guint plane_stride;
|
||||
guint plane_size;
|
||||
}
|
||||
Context;
|
||||
|
||||
static Context properties_ctx, sinkcaps_ctx;
|
||||
|
||||
static void
|
||||
set_pixel (Context const *ctx, guint8 * pixels, guint x, guint y, guint32 color)
|
||||
{
|
||||
guint i;
|
||||
guint ofs = y * ctx->plane_stride + x;
|
||||
for (i = 0; i < NUM_TEST_PLANES; ++i)
|
||||
pixels[ctx->plane_size * i + ofs] =
|
||||
(color >> ((NUM_TEST_PLANES - 1 - i) * 8)) & 0xFF;
|
||||
}
|
||||
|
||||
static guint32
|
||||
get_pixel (Context const *ctx, const guint8 * pixels, guint x, guint y)
|
||||
{
|
||||
guint i;
|
||||
guint ofs = y * ctx->plane_stride + x;
|
||||
guint32 color = 0;
|
||||
for (i = 0; i < NUM_TEST_PLANES; ++i)
|
||||
color |=
|
||||
((guint32) (pixels[ctx->plane_size * i + ofs])) << ((NUM_TEST_PLANES -
|
||||
1 - i) * 8);
|
||||
return color;
|
||||
}
|
||||
|
||||
static void
|
||||
fill_test_pattern (Context const *ctx, GstBuffer * buffer, guint xofs,
|
||||
guint yofs)
|
||||
{
|
||||
guint8 *pixels;
|
||||
GstMapInfo map_info;
|
||||
|
||||
gst_buffer_map (buffer, &map_info, GST_MAP_WRITE);
|
||||
pixels = map_info.data;
|
||||
|
||||
memset (pixels, 0, ctx->plane_size * NUM_TEST_PLANES);
|
||||
set_pixel (ctx, pixels, 1 + xofs, 0 + yofs, 0x8899AA);
|
||||
set_pixel (ctx, pixels, 0 + xofs, 1 + yofs, 0x112233);
|
||||
|
||||
gst_buffer_unmap (buffer, &map_info);
|
||||
}
|
||||
|
||||
static void
|
||||
check_test_pattern (Context const *ctx, GstBuffer * buffer, guint xofs,
|
||||
guint yofs)
|
||||
{
|
||||
guint x, y;
|
||||
guint8 *pixels;
|
||||
GstMapInfo map_info;
|
||||
|
||||
gst_buffer_map (buffer, &map_info, GST_MAP_READ);
|
||||
pixels = map_info.data;
|
||||
|
||||
fail_unless_equals_uint64_hex (get_pixel (ctx, pixels, 1 + xofs, 0 + yofs),
|
||||
0x8899AA);
|
||||
fail_unless_equals_uint64_hex (get_pixel (ctx, pixels, 0 + xofs, 1 + yofs),
|
||||
0x112233);
|
||||
|
||||
for (y = 0; y < TEST_HEIGHT; ++y) {
|
||||
for (x = 0; x < TEST_WIDTH; ++x) {
|
||||
if ((x == (1 + xofs) && y == (0 + yofs)) || (x == (0 + xofs)
|
||||
&& y == (1 + yofs)))
|
||||
continue;
|
||||
|
||||
fail_unless_equals_uint64_hex (get_pixel (ctx, pixels, x, y), 0x000000);
|
||||
}
|
||||
}
|
||||
|
||||
gst_buffer_unmap (buffer, &map_info);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
setup_rawvideoparse (gboolean use_sink_caps,
|
||||
gboolean set_properties, GstCaps * incaps, GstFormat format)
|
||||
{
|
||||
guint i;
|
||||
|
||||
|
||||
/* Setup the rawvideoparse element and the pads */
|
||||
|
||||
static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL))
|
||||
);
|
||||
static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS_ANY);
|
||||
|
||||
rawvideoparse = gst_check_setup_element ("rawvideoparse");
|
||||
|
||||
properties_ctx.plane_stride = PROP_CTX_PLANE_STRIDE;
|
||||
properties_ctx.plane_size = PROP_CTX_PLANE_SIZE;
|
||||
properties_ctx.data = gst_adapter_new ();
|
||||
|
||||
sinkcaps_ctx.plane_stride = TEST_WIDTH;
|
||||
sinkcaps_ctx.plane_size = TEST_WIDTH * TEST_HEIGHT;
|
||||
sinkcaps_ctx.data = gst_adapter_new ();
|
||||
|
||||
g_object_set (G_OBJECT (rawvideoparse), "use-sink-caps", use_sink_caps, NULL);
|
||||
if (set_properties) {
|
||||
GValueArray *plane_offsets, *plane_strides;
|
||||
GValue val = G_VALUE_INIT;
|
||||
|
||||
g_value_init (&val, G_TYPE_UINT);
|
||||
|
||||
plane_offsets = g_value_array_new (NUM_TEST_PLANES);
|
||||
for (i = 0; i < NUM_TEST_PLANES; ++i) {
|
||||
g_value_set_uint (&val, properties_ctx.plane_size * i);
|
||||
g_value_array_insert (plane_offsets, i, &val);
|
||||
}
|
||||
|
||||
plane_strides = g_value_array_new (NUM_TEST_PLANES);
|
||||
for (i = 0; i < NUM_TEST_PLANES; ++i) {
|
||||
g_value_set_uint (&val, properties_ctx.plane_stride);
|
||||
g_value_array_insert (plane_strides, i, &val);
|
||||
}
|
||||
|
||||
g_value_unset (&val);
|
||||
|
||||
g_object_set (G_OBJECT (rawvideoparse), "width", TEST_WIDTH, "height",
|
||||
TEST_HEIGHT, "frame-stride", PROP_CTX_FRAME_STRIDE, "framerate",
|
||||
TEST_FRAMERATE_N, TEST_FRAMERATE_D, "plane-offsets", plane_offsets,
|
||||
"plane-strides", plane_strides, "format", TEST_FRAME_FORMAT, NULL);
|
||||
|
||||
g_value_array_free (plane_offsets);
|
||||
g_value_array_free (plane_strides);
|
||||
}
|
||||
|
||||
/* Check that the plane stride/offset values are correct */
|
||||
{
|
||||
GValueArray *plane_offsets_array;
|
||||
GValueArray *plane_strides_array;
|
||||
/* By default, 320x240 i420 is used as format */
|
||||
guint plane_offsets[3] = { 0, 76800, 96000 };
|
||||
guint plane_strides[3] = { 320, 160, 160 };
|
||||
|
||||
if (set_properties) {
|
||||
/* When properties are explicitely set, we use Y444 as video format,
|
||||
* so in that case, plane stride values are all the same */
|
||||
plane_offsets[0] = properties_ctx.plane_size * 0;
|
||||
plane_offsets[1] = properties_ctx.plane_size * 1;
|
||||
plane_offsets[2] = properties_ctx.plane_size * 2;
|
||||
plane_strides[0] = plane_strides[1] = plane_strides[2] =
|
||||
properties_ctx.plane_stride;
|
||||
}
|
||||
|
||||
g_object_get (G_OBJECT (rawvideoparse), "plane-offsets",
|
||||
&plane_offsets_array, "plane-strides", &plane_strides_array, NULL);
|
||||
fail_unless (plane_offsets_array != NULL);
|
||||
fail_unless (plane_strides_array != NULL);
|
||||
fail_unless (plane_offsets_array->n_values ==
|
||||
plane_strides_array->n_values);
|
||||
|
||||
for (i = 0; i < plane_offsets_array->n_values; ++i) {
|
||||
GValue *gvalue;
|
||||
|
||||
gvalue = g_value_array_get_nth (plane_offsets_array, i);
|
||||
fail_unless (gvalue != NULL);
|
||||
fail_unless_equals_uint64 (plane_offsets[i], g_value_get_uint (gvalue));
|
||||
|
||||
gvalue = g_value_array_get_nth (plane_strides_array, i);
|
||||
fail_unless (gvalue != NULL);
|
||||
fail_unless_equals_uint64 (plane_strides[i], g_value_get_uint (gvalue));
|
||||
}
|
||||
|
||||
g_value_array_free (plane_offsets_array);
|
||||
g_value_array_free (plane_strides_array);
|
||||
}
|
||||
|
||||
fail_unless (gst_element_set_state (rawvideoparse,
|
||||
GST_STATE_PAUSED) == GST_STATE_CHANGE_SUCCESS,
|
||||
"could not set to paused");
|
||||
|
||||
mysrcpad = gst_check_setup_src_pad (rawvideoparse, &srctemplate);
|
||||
mysinkpad = gst_check_setup_sink_pad (rawvideoparse, &sinktemplate);
|
||||
|
||||
gst_pad_set_active (mysrcpad, TRUE);
|
||||
gst_pad_set_active (mysinkpad, TRUE);
|
||||
|
||||
gst_check_setup_events (mysrcpad, rawvideoparse, incaps, format);
|
||||
if (incaps)
|
||||
gst_caps_unref (incaps);
|
||||
|
||||
|
||||
/* Fill the adapters with test frames */
|
||||
|
||||
for (i = 0; i < 10; ++i) {
|
||||
GstBuffer *buffer =
|
||||
gst_buffer_new_allocate (NULL, PROP_CTX_FRAME_STRIDE, NULL);
|
||||
gst_buffer_memset (buffer, 0, 0xCC, gst_buffer_get_size (buffer));
|
||||
fill_test_pattern (&properties_ctx, buffer, i, 0);
|
||||
gst_adapter_push (properties_ctx.data, buffer);
|
||||
}
|
||||
|
||||
for (i = 0; i < 10; ++i) {
|
||||
GstBuffer *buffer =
|
||||
gst_buffer_new_allocate (NULL, sinkcaps_ctx.plane_size * 3, NULL);
|
||||
gst_buffer_memset (buffer, 0, 0xCC, gst_buffer_get_size (buffer));
|
||||
fill_test_pattern (&sinkcaps_ctx, buffer, i, 0);
|
||||
gst_adapter_push (sinkcaps_ctx.data, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cleanup_rawvideoparse (void)
|
||||
{
|
||||
gst_pad_set_active (mysrcpad, FALSE);
|
||||
gst_pad_set_active (mysinkpad, FALSE);
|
||||
gst_check_teardown_src_pad (rawvideoparse);
|
||||
gst_check_teardown_sink_pad (rawvideoparse);
|
||||
gst_check_teardown_element (rawvideoparse);
|
||||
|
||||
g_object_unref (G_OBJECT (properties_ctx.data));
|
||||
g_object_unref (G_OBJECT (sinkcaps_ctx.data));
|
||||
}
|
||||
|
||||
static void
|
||||
push_data_and_check_output (Context * ctx, gsize num_in_bytes,
|
||||
gsize expected_num_out_bytes, gint64 expected_pts, gint64 expected_dur,
|
||||
guint expected_num_buffers_in_list, guint buf_idx, guint xofs, guint yofs)
|
||||
{
|
||||
GstBuffer *inbuf, *outbuf;
|
||||
guint num_buffers;
|
||||
|
||||
/* Simulate upstream input by taking num_in_bytes bytes from the adapter */
|
||||
inbuf = gst_adapter_take_buffer (ctx->data, num_in_bytes);
|
||||
fail_unless (inbuf != NULL);
|
||||
|
||||
/* Push the input data and check that the output buffers list grew as
|
||||
* expected */
|
||||
fail_unless (gst_pad_push (mysrcpad, inbuf) == GST_FLOW_OK);
|
||||
num_buffers = g_list_length (buffers);
|
||||
fail_unless_equals_int (num_buffers, expected_num_buffers_in_list);
|
||||
|
||||
/* Take the output buffer */
|
||||
outbuf = g_list_nth_data (buffers, buf_idx);
|
||||
fail_unless (outbuf != NULL);
|
||||
|
||||
/* Verify size, PTS, duration of the output buffer */
|
||||
fail_unless_equals_uint64 (expected_num_out_bytes,
|
||||
gst_buffer_get_size (outbuf));
|
||||
fail_unless_equals_uint64 (expected_pts, GST_BUFFER_PTS (outbuf));
|
||||
fail_unless_equals_uint64 (expected_dur, GST_BUFFER_DURATION (outbuf));
|
||||
|
||||
/* Check that the pixels have the correct values */
|
||||
check_test_pattern (ctx, outbuf, xofs, yofs);
|
||||
}
|
||||
|
||||
|
||||
GST_START_TEST (test_push_unaligned_data_properties_config)
|
||||
{
|
||||
setup_rawvideoparse (FALSE, TRUE, NULL, GST_FORMAT_BYTES);
|
||||
|
||||
/* Send in data buffers that are not aligned to multiples of the
|
||||
* frame size (= sample size * num_channels). This tests if rawvideoparse
|
||||
* aligns output data properly.
|
||||
*
|
||||
* The second line sends a buffer with multiple frames inside.
|
||||
* rawvideoparse will then parse this buffer repeatedly (and prepend
|
||||
* leftover data from the earlier parse iteration), explaining why
|
||||
* all of a sudden there are 4 output buffers, compared to just one
|
||||
* earlier. The output data is expected to be 280 bytes large, since this
|
||||
* is the size of the actual frame, without extra padding at the end.
|
||||
*/
|
||||
push_data_and_check_output (&properties_ctx, 511, 280, GST_MSECOND * 0,
|
||||
GST_MSECOND * 40, 1, 0, 0, 0);
|
||||
push_data_and_check_output (&properties_ctx, 1940, 280, GST_MSECOND * 40,
|
||||
GST_MSECOND * 40, 4, 1, 1, 0);
|
||||
push_data_and_check_output (&properties_ctx, 10, 280, GST_MSECOND * 80,
|
||||
GST_MSECOND * 40, 4, 2, 2, 0);
|
||||
|
||||
cleanup_rawvideoparse ();
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_push_unaligned_data_sink_caps_config)
|
||||
{
|
||||
GstVideoInfo vinfo;
|
||||
GstCaps *caps;
|
||||
|
||||
/* This test is essentially the same as test_push_unaligned_data_properties_config,
|
||||
* except that rawvideoparse uses the sink caps config instead of the property config.
|
||||
* Also, the input sizes are different, since the sink caps config does not use extra
|
||||
* padding between planes and does use a stride that directly corresponds to the width,
|
||||
* resulting in smaller frame size (192 bytes vs 280 bytes). */
|
||||
|
||||
gst_video_info_set_format (&vinfo, TEST_FRAME_FORMAT, TEST_WIDTH,
|
||||
TEST_HEIGHT);
|
||||
GST_VIDEO_INFO_FPS_N (&vinfo) = 25;
|
||||
GST_VIDEO_INFO_FPS_D (&vinfo) = 1;
|
||||
caps = gst_video_info_to_caps (&vinfo);
|
||||
|
||||
setup_rawvideoparse (TRUE, FALSE, caps, GST_FORMAT_BYTES);
|
||||
|
||||
push_data_and_check_output (&sinkcaps_ctx, 250, 192, GST_MSECOND * 0,
|
||||
GST_MSECOND * 40, 1, 0, 0, 0);
|
||||
push_data_and_check_output (&sinkcaps_ctx, 811, 192, GST_MSECOND * 40,
|
||||
GST_MSECOND * 40, 5, 1, 1, 0);
|
||||
push_data_and_check_output (&sinkcaps_ctx, 10, 192, GST_MSECOND * 80,
|
||||
GST_MSECOND * 40, 5, 2, 2, 0);
|
||||
|
||||
cleanup_rawvideoparse ();
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_config_switch)
|
||||
{
|
||||
GstVideoInfo vinfo;
|
||||
GstCaps *caps;
|
||||
|
||||
/* Start processing with the properties config active, then mid-stream switch to
|
||||
* the sink caps config. Since the sink caps config does not use padding, its
|
||||
* frame size is smaller. The buffer duration stays the same (since it only depends
|
||||
* on the framerate), but the expected output buffer size is different). */
|
||||
|
||||
gst_video_info_set_format (&vinfo, TEST_FRAME_FORMAT, TEST_WIDTH,
|
||||
TEST_HEIGHT);
|
||||
GST_VIDEO_INFO_FPS_N (&vinfo) = 25;
|
||||
GST_VIDEO_INFO_FPS_D (&vinfo) = 1;
|
||||
caps = gst_video_info_to_caps (&vinfo);
|
||||
|
||||
setup_rawvideoparse (FALSE, TRUE, caps, GST_FORMAT_BYTES);
|
||||
|
||||
/* Push in data with properties config active */
|
||||
push_data_and_check_output (&properties_ctx, 500, 280, GST_MSECOND * 0,
|
||||
GST_MSECOND * 40, 1, 0, 0, 0);
|
||||
push_data_and_check_output (&properties_ctx, 500, 280, GST_MSECOND * 40,
|
||||
GST_MSECOND * 40, 2, 1, 1, 0);
|
||||
|
||||
/* Perform the switch */
|
||||
g_object_set (G_OBJECT (rawvideoparse), "use-sink-caps", TRUE, NULL);
|
||||
|
||||
/* Push in data with sink caps config active, expecting a different frame size */
|
||||
push_data_and_check_output (&sinkcaps_ctx, 192, 192, GST_MSECOND * 80,
|
||||
GST_MSECOND * 40, 3, 2, 0, 0);
|
||||
|
||||
cleanup_rawvideoparse ();
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_push_with_no_framerate)
|
||||
{
|
||||
/* Test the special case when no framerate is set. The parser is expected to
|
||||
* still work then, but without setting duration or PTS/DTS (it cannot do that,
|
||||
* because these require a nonzero framerate). The output buffers have PTS 0,
|
||||
* all subsequent ones have no set PTS. */
|
||||
|
||||
setup_rawvideoparse (FALSE, TRUE, NULL, GST_FORMAT_BYTES);
|
||||
g_object_set (G_OBJECT (rawvideoparse), "framerate", 0, 1, NULL);
|
||||
|
||||
push_data_and_check_output (&properties_ctx, 500, 280, 0, GST_CLOCK_TIME_NONE,
|
||||
1, 0, 0, 0);
|
||||
push_data_and_check_output (&properties_ctx, 500, 280, GST_CLOCK_TIME_NONE,
|
||||
GST_CLOCK_TIME_NONE, 2, 1, 1, 0);
|
||||
|
||||
cleanup_rawvideoparse ();
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
|
||||
static Suite *
|
||||
rawvideoparse_suite (void)
|
||||
{
|
||||
Suite *s = suite_create ("rawvideoparse");
|
||||
TCase *tc_chain = tcase_create ("general");
|
||||
|
||||
suite_add_tcase (s, tc_chain);
|
||||
tcase_add_test (tc_chain, test_push_unaligned_data_properties_config);
|
||||
tcase_add_test (tc_chain, test_push_unaligned_data_sink_caps_config);
|
||||
tcase_add_test (tc_chain, test_config_switch);
|
||||
tcase_add_test (tc_chain, test_push_with_no_framerate);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
GST_CHECK_MAIN (rawvideoparse);
|
Loading…
Reference in a new issue