| /* 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 GstBuffer * |
| gst_raw_base_parse_align_buffer (GstRawBaseParse * raw_base_parse, |
| gsize alignment, GstBuffer * buffer, gsize out_size) |
| { |
| GstMapInfo map; |
| |
| gst_buffer_map (buffer, &map, GST_MAP_READ); |
| |
| if (map.size < sizeof (guintptr)) { |
| gst_buffer_unmap (buffer, &map); |
| return NULL; |
| } |
| |
| if (((guintptr) map.data) & (alignment - 1)) { |
| GstBuffer *new_buffer; |
| GstAllocationParams params = { 0, alignment - 1, 0, 0, }; |
| |
| new_buffer = gst_buffer_new_allocate (NULL, out_size, ¶ms); |
| |
| /* Copy data "by hand", so ensure alignment is kept: */ |
| gst_buffer_fill (new_buffer, 0, map.data, out_size); |
| |
| gst_buffer_copy_into (new_buffer, buffer, GST_BUFFER_COPY_METADATA, 0, |
| out_size); |
| GST_DEBUG_OBJECT (raw_base_parse, |
| "We want output aligned on %" G_GSIZE_FORMAT ", reallocated", |
| alignment); |
| |
| gst_buffer_unmap (buffer, &map); |
| |
| return new_buffer; |
| } |
| |
| gst_buffer_unmap (buffer, &map); |
| |
| return NULL; |
| } |
| |
| 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; |
| gint alignment; |
| 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); |
| |
| /* drop incomplete frame at the end of the stream |
| * https://bugzilla.gnome.org/show_bug.cgi?id=773666 |
| */ |
| if (GST_BASE_PARSE_DRAINING (parse) && in_size < frame_size) { |
| GST_DEBUG_OBJECT (raw_base_parse, |
| "Dropping %" G_GSIZE_FORMAT " bytes at EOS", in_size); |
| frame->flags |= GST_BASE_PARSE_FRAME_FLAG_DROP; |
| GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse); |
| |
| return gst_base_parse_finish_frame (parse, frame, in_size); |
| } |
| |
| /* 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; |
| } |
| |
| if (klass->get_alignment |
| && (alignment = |
| klass->get_alignment (raw_base_parse, |
| GST_RAW_BASE_PARSE_CONFIG_CURRENT)) != 1) { |
| GstBuffer *aligned_buffer; |
| |
| aligned_buffer = |
| gst_raw_base_parse_align_buffer (raw_base_parse, alignment, |
| frame->out_buffer ? frame->out_buffer : frame->buffer, out_size); |
| |
| if (aligned_buffer) { |
| if (frame->out_buffer) |
| gst_buffer_unref (frame->out_buffer); |
| frame->out_buffer = aligned_buffer; |
| } |
| } |
| |
| /* 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; |
| } |
| |
| flow_ret = |
| 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; |
| } |