X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=libs%2Fgst%2Fbase%2Fgstbaseparse.c;h=ce6895650e5523a9120cdfc10734b8a8799c7890;hb=5bf13cdd5314bc3c6c81bd620e712acdcab14eb2;hp=395b43b1832666af754afa49065941890e237fc1;hpb=bc1fb2d8b06d99e0c9a95e156d9aa13cf9487243;p=platform%2Fupstream%2Fgstreamer.git diff --git a/libs/gst/base/gstbaseparse.c b/libs/gst/base/gstbaseparse.c index 395b43b..ce68956 100644 --- a/libs/gst/base/gstbaseparse.c +++ b/libs/gst/base/gstbaseparse.c @@ -23,6 +23,7 @@ /** * SECTION:gstbaseparse + * @title: GstBaseParse * @short_description: Base class for stream parsers * @see_also: #GstBaseTransform * @@ -30,114 +31,98 @@ * into separate audio/video/whatever frames. * * It provides for: - * - * provides one sink pad and one source pad - * handles state changes - * can operate in pull mode or push mode - * handles seeking in both modes - * handles events (SEGMENT/EOS/FLUSH) - * - * handles queries (POSITION/DURATION/SEEKING/FORMAT/CONVERT) - * - * handles flushing - * + * + * * provides one sink pad and one source pad + * * handles state changes + * * can operate in pull mode or push mode + * * handles seeking in both modes + * * handles events (SEGMENT/EOS/FLUSH) + * * handles queries (POSITION/DURATION/SEEKING/FORMAT/CONVERT) + * * handles flushing * * The purpose of this base class is to provide the basic functionality of * a parser and share a lot of rather complex code. * - * Description of the parsing mechanism: - * - * - * Set-up phase - * - * #GstBaseParse calls @start to inform subclass that data processing is - * about to start now. - * - * - * #GstBaseParse class calls @set_sink_caps to inform the subclass about - * incoming sinkpad caps. Subclass could already set the srcpad caps - * accordingly, but this might be delayed until calling - * gst_base_parse_finish_frame() with a non-queued frame. - * - * - * At least at this point subclass needs to tell the #GstBaseParse class - * how big data chunks it wants to receive (min_frame_size). It can do - * this with gst_base_parse_set_min_frame_size(). - * - * - * #GstBaseParse class sets up appropriate data passing mode (pull/push) - * and starts to process the data. - * - * - * - * - * - * Parsing phase - * - * #GstBaseParse gathers at least min_frame_size bytes of data either - * by pulling it from upstream or collecting buffers in an internal - * #GstAdapter. - * - * - * A buffer of (at least) min_frame_size bytes is passed to subclass with - * @handle_frame. Subclass checks the contents and can optionally - * return GST_FLOW_OK along with an amount of data to be skipped to find - * a valid frame (which will result in a subsequent DISCONT). - * If, otherwise, the buffer does not hold a complete frame, - * @handle_frame can merely return and will be called again when additional - * data is available. In push mode this amounts to an - * additional input buffer (thus minimal additional latency), in pull mode - * this amounts to some arbitrary reasonable buffer size increase. - * Of course, gst_base_parse_set_min_frame_size() could also be used if a - * very specific known amount of additional data is required. - * If, however, the buffer holds a complete valid frame, it can pass - * the size of this frame to gst_base_parse_finish_frame(). - * If acting as a converter, it can also merely indicate consumed input data - * while simultaneously providing custom output data. - * Note that baseclass performs some processing (such as tracking - * overall consumed data rate versus duration) for each finished frame, - * but other state is only updated upon each call to @handle_frame - * (such as tracking upstream input timestamp). - * - * Subclass is also responsible for setting the buffer metadata - * (e.g. buffer timestamp and duration, or keyframe if applicable). - * (although the latter can also be done by #GstBaseParse if it is - * appropriately configured, see below). Frame is provided with - * timestamp derived from upstream (as much as generally possible), - * duration obtained from configuration (see below), and offset - * if meaningful (in pull mode). - * - * Note that @check_valid_frame might receive any small - * amount of input data when leftover data is being drained (e.g. at EOS). - * - * - * As part of finish frame processing, - * just prior to actually pushing the buffer in question, - * it is passed to @pre_push_frame which gives subclass yet one - * last chance to examine buffer metadata, or to send some custom (tag) - * events, or to perform custom (segment) filtering. - * - * - * During the parsing process #GstBaseParseClass will handle both srcpad - * and sinkpad events. They will be passed to subclass if @event or - * @src_event callbacks have been provided. - * - * - * - * - * Shutdown phase - * - * #GstBaseParse class calls @stop to inform the subclass that data - * parsing will be stopped. - * - * - * - * + * # Description of the parsing mechanism: + * + * ## Set-up phase + * + * * #GstBaseParse calls #GstBaseParseClass.start() to inform subclass + * that data processing is about to start now. + * + * * #GstBaseParse class calls #GstBaseParseClass.set_sink_caps() to + * inform the subclass about incoming sinkpad caps. Subclass could + * already set the srcpad caps accordingly, but this might be delayed + * until calling gst_base_parse_finish_frame() with a non-queued frame. + * + * * At least at this point subclass needs to tell the #GstBaseParse class + * how big data chunks it wants to receive (minimum frame size ). It can + * do this with gst_base_parse_set_min_frame_size(). + * + * * #GstBaseParse class sets up appropriate data passing mode (pull/push) + * and starts to process the data. + * + * ## Parsing phase + * + * * #GstBaseParse gathers at least min_frame_size bytes of data either + * by pulling it from upstream or collecting buffers in an internal + * #GstAdapter. + * + * * A buffer of (at least) min_frame_size bytes is passed to subclass + * with #GstBaseParseClass.handle_frame(). Subclass checks the contents + * and can optionally return #GST_FLOW_OK along with an amount of data + * to be skipped to find a valid frame (which will result in a + * subsequent DISCONT). If, otherwise, the buffer does not hold a + * complete frame, #GstBaseParseClass.handle_frame() can merely return + * and will be called again when additional data is available. In push + * mode this amounts to an additional input buffer (thus minimal + * additional latency), in pull mode this amounts to some arbitrary + * reasonable buffer size increase. + * + * Of course, gst_base_parse_set_min_frame_size() could also be used if + * a very specific known amount of additional data is required. If, + * however, the buffer holds a complete valid frame, it can pass the + * size of this frame to gst_base_parse_finish_frame(). + * + * If acting as a converter, it can also merely indicate consumed input + * data while simultaneously providing custom output data. Note that + * baseclass performs some processing (such as tracking overall consumed + * data rate versus duration) for each finished frame, but other state + * is only updated upon each call to #GstBaseParseClass.handle_frame() + * (such as tracking upstream input timestamp). + * + * Subclass is also responsible for setting the buffer metadata + * (e.g. buffer timestamp and duration, or keyframe if applicable). + * (although the latter can also be done by #GstBaseParse if it is + * appropriately configured, see below). Frame is provided with + * timestamp derived from upstream (as much as generally possible), + * duration obtained from configuration (see below), and offset + * if meaningful (in pull mode). + * + * Note that #GstBaseParseClass.handle_frame() might receive any small + * amount of input data when leftover data is being drained (e.g. at + * EOS). + * + * * As part of finish frame processing, just prior to actually pushing + * the buffer in question, it is passed to + * #GstBaseParseClass.pre_push_frame() which gives subclass yet one last + * chance to examine buffer metadata, or to send some custom (tag) + * events, or to perform custom (segment) filtering. + * + * * During the parsing process #GstBaseParseClass will handle both srcpad + * and sinkpad events. They will be passed to subclass if + * #GstBaseParseClass.event() or #GstBaseParseClass.src_event() + * implementations have been provided. + * + * ## Shutdown phase * - * Subclass is responsible for providing pad template caps for - * source and sink pads. The pads need to be named "sink" and "src". It also - * needs to set the fixed caps on srcpad, when the format is ensured (e.g. - * when base class calls subclass' @set_sink_caps function). + * * #GstBaseParse class calls #GstBaseParseClass.stop() to inform the + * subclass that data parsing will be stopped. + * + * Subclass is responsible for providing pad template caps for source and + * sink pads. The pads need to be named "sink" and "src". It also needs to + * set the fixed caps on srcpad, when the format is ensured (e.g. when + * base class calls subclass' #GstBaseParseClass.set_sink_caps() function). * * This base class uses %GST_FORMAT_DEFAULT as a meaning of frames. So, * subclass conversion routine needs to know that conversion from @@ -151,44 +136,32 @@ * and end of data processing. * * Things that subclass need to take care of: - * - * Provide pad templates - * - * Fixate the source pad caps when appropriate - * - * - * Inform base class how big data chunks should be retrieved. This is - * done with gst_base_parse_set_min_frame_size() function. - * - * - * Examine data chunks passed to subclass with @handle_frame and pass - * proper frame(s) to gst_base_parse_finish_frame(), and setting src pad - * caps and timestamps on frame. - * - * Provide conversion functions - * - * Update the duration information with gst_base_parse_set_duration() - * - * - * Optionally passthrough using gst_base_parse_set_passthrough() - * - * - * Configure various baseparse parameters using - * gst_base_parse_set_average_bitrate(), gst_base_parse_set_syncable() - * and gst_base_parse_set_frame_rate(). - * - * - * In particular, if subclass is unable to determine a duration, but - * parsing (or specs) yields a frames per seconds rate, then this can be - * provided to #GstBaseParse to enable it to cater for - * buffer time metadata (which will be taken from upstream as much as - * possible). Internally keeping track of frame durations and respective - * sizes that have been pushed provides #GstBaseParse with an estimated - * bitrate. A default @convert (used if not overridden) will then use these - * rates to perform obvious conversions. These rates are also used to - * update (estimated) duration at regular frame intervals. - * - * + * + * * Provide pad templates + * * Fixate the source pad caps when appropriate + * * Inform base class how big data chunks should be retrieved. This is + * done with gst_base_parse_set_min_frame_size() function. + * * Examine data chunks passed to subclass with + * #GstBaseParseClass.handle_frame() and pass proper frame(s) to + * gst_base_parse_finish_frame(), and setting src pad caps and timestamps + * on frame. + * * Provide conversion functions + * * Update the duration information with gst_base_parse_set_duration() + * * Optionally passthrough using gst_base_parse_set_passthrough() + * * Configure various baseparse parameters using + * gst_base_parse_set_average_bitrate(), gst_base_parse_set_syncable() + * and gst_base_parse_set_frame_rate(). + * + * * In particular, if subclass is unable to determine a duration, but + * parsing (or specs) yields a frames per seconds rate, then this can be + * provided to #GstBaseParse to enable it to cater for buffer time + * metadata (which will be taken from upstream as much as + * possible). Internally keeping track of frame durations and respective + * sizes that have been pushed provides #GstBaseParse with an estimated + * bitrate. A default #GstBaseParseClass.convert() (used if not + * overridden) will then use these rates to perform obvious conversions. + * These rates are also used to update (estimated) duration at regular + * frame intervals. * */ @@ -219,6 +192,9 @@ #define MIN_FRAMES_TO_POST_BITRATE 10 #define TARGET_DIFFERENCE (20 * GST_SECOND) #define MAX_INDEX_ENTRIES 4096 +#define UPDATE_THRESHOLD 2 + +#define ABSDIFF(a,b) (((a) > (b)) ? ((a) - (b)) : ((b) - (a))) GST_DEBUG_CATEGORY_STATIC (gst_base_parse_debug); #define GST_CAT_DEFAULT gst_base_parse_debug @@ -231,9 +207,6 @@ static const GstFormat fmtlist[] = { GST_FORMAT_UNDEFINED }; -#define GST_BASE_PARSE_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BASE_PARSE, GstBaseParsePrivate)) - struct _GstBaseParsePrivate { GstPadMode pad_mode; @@ -270,6 +243,7 @@ struct _GstBaseParsePrivate GstClockTime next_dts; GstClockTime prev_pts; GstClockTime prev_dts; + gboolean prev_dts_from_pts; GstClockTime frame_duration; gboolean seen_keyframe; gboolean is_video; @@ -286,6 +260,7 @@ struct _GstBaseParsePrivate gboolean post_min_bitrate; gboolean post_avg_bitrate; gboolean post_max_bitrate; + guint min_bitrate; guint avg_bitrate; guint max_bitrate; @@ -359,6 +334,15 @@ struct _GstBaseParsePrivate /* When we need to skip more data than we have currently */ guint skip; + + /* Tag handling (stream tags only, global tags are passed through as-is) */ + GstTagList *upstream_tags; + GstTagList *parser_tags; + GstTagMergeMode parser_tags_merge_mode; + gboolean tags_changed; + + /* Current segment seqnum */ + guint32 segment_seqnum; }; typedef struct _GstBaseParseSeek @@ -384,6 +368,7 @@ enum g_mutex_unlock (&parse->priv->index_lock); static GstElementClass *parent_class = NULL; +static gint base_parse_private_offset = 0; static void gst_base_parse_class_init (GstBaseParseClass * klass); static void gst_base_parse_init (GstBaseParse * parse, @@ -410,11 +395,21 @@ gst_base_parse_get_type (void) _type = g_type_register_static (GST_TYPE_ELEMENT, "GstBaseParse", &base_parse_info, G_TYPE_FLAG_ABSTRACT); + + base_parse_private_offset = + g_type_add_instance_private (_type, sizeof (GstBaseParsePrivate)); + g_once_init_leave (&base_parse_type, _type); } return (GType) base_parse_type; } +static inline GstBaseParsePrivate * +gst_base_parse_get_instance_private (GstBaseParse * self) +{ + return (G_STRUCT_MEMBER_P (self, base_parse_private_offset)); +} + static void gst_base_parse_finalize (GObject * object); static GstStateChangeReturn gst_base_parse_change_state (GstElement * element, @@ -432,7 +427,8 @@ static gboolean gst_base_parse_sink_activate_mode (GstPad * pad, GstObject * parent, GstPadMode mode, gboolean active); static gboolean gst_base_parse_handle_seek (GstBaseParse * parse, GstEvent * event); -static void gst_base_parse_handle_tag (GstBaseParse * parse, GstEvent * event); +static void gst_base_parse_set_upstream_tags (GstBaseParse * parse, + GstTagList * taglist); static void gst_base_parse_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); @@ -467,11 +463,6 @@ static gboolean gst_base_parse_sink_query_default (GstBaseParse * parse, static gboolean gst_base_parse_src_query_default (GstBaseParse * parse, GstQuery * query); -static void gst_base_parse_drain (GstBaseParse * parse); - -static void gst_base_parse_post_bitrates (GstBaseParse * parse, - gboolean post_min, gboolean post_avg, gboolean post_max); - static gint64 gst_base_parse_find_offset (GstBaseParse * parse, GstClockTime time, gboolean before, GstClockTime * _ts); static GstFlowReturn gst_base_parse_locate_time (GstBaseParse * parse, @@ -548,7 +539,10 @@ gst_base_parse_class_init (GstBaseParseClass * klass) GstElementClass *gstelement_class; gobject_class = G_OBJECT_CLASS (klass); - g_type_class_add_private (klass, sizeof (GstBaseParsePrivate)); + + if (base_parse_private_offset != 0) + g_type_class_adjust_private_offset (klass, &base_parse_private_offset); + parent_class = g_type_class_peek_parent (klass); gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_base_parse_finalize); @@ -598,7 +592,7 @@ gst_base_parse_init (GstBaseParse * parse, GstBaseParseClass * bclass) GST_DEBUG_OBJECT (parse, "gst_base_parse_init"); - parse->priv = GST_BASE_PARSE_GET_PRIVATE (parse); + parse->priv = gst_base_parse_get_instance_private (parse); pad_template = gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "sink"); @@ -644,6 +638,11 @@ gst_base_parse_init (GstBaseParse * parse, GstBaseParseClass * bclass) GST_DEBUG_OBJECT (parse, "init ok"); GST_OBJECT_FLAG_SET (parse, GST_ELEMENT_FLAG_INDEXABLE); + + parse->priv->upstream_tags = NULL; + parse->priv->parser_tags = NULL; + parse->priv->parser_tags_merge_mode = GST_TAG_MERGE_APPEND; + parse->priv->disable_passthrough = DEFAULT_DISABLE_PASSTHROUGH; } static void @@ -678,7 +677,16 @@ gst_base_parse_get_property (GObject * object, guint prop_id, GValue * value, } } -static GstBaseParseFrame * +/** + * gst_base_parse_frame_copy: + * @frame: a #GstBaseParseFrame + * + * Copies a #GstBaseParseFrame. + * + * Returns: A copy of @frame + */ + +GstBaseParseFrame * gst_base_parse_frame_copy (GstBaseParseFrame * frame) { GstBaseParseFrame *copy; @@ -692,6 +700,12 @@ gst_base_parse_frame_copy (GstBaseParseFrame * frame) return copy; } +/** + * gst_base_parse_frame_free: + * @frame: A #GstBaseParseFrame + * + * Frees the provided @frame. + */ void gst_base_parse_frame_free (GstBaseParseFrame * frame) { @@ -779,6 +793,8 @@ gst_base_parse_update_frame (GstBaseParse * parse, GstBaseParseFrame * frame) if (G_UNLIKELY (parse->priv->discont)) { GST_DEBUG_OBJECT (parse, "marking DISCONT"); GST_BUFFER_FLAG_SET (frame->buffer, GST_BUFFER_FLAG_DISCONT); + } else { + GST_BUFFER_FLAG_UNSET (frame->buffer, GST_BUFFER_FLAG_DISCONT); } if (parse->priv->prev_offset != parse->priv->offset || parse->priv->new_frame) { @@ -818,14 +834,10 @@ gst_base_parse_reset (GstBaseParse * parse) parse->priv->next_pts = GST_CLOCK_TIME_NONE; parse->priv->next_dts = 0; parse->priv->syncable = TRUE; - parse->priv->disable_passthrough = DEFAULT_DISABLE_PASSTHROUGH; parse->priv->passthrough = FALSE; parse->priv->pts_interpolate = TRUE; parse->priv->infer_ts = TRUE; parse->priv->has_timing_info = FALSE; - parse->priv->post_min_bitrate = TRUE; - parse->priv->post_avg_bitrate = TRUE; - parse->priv->post_max_bitrate = TRUE; parse->priv->min_bitrate = G_MAXUINT; parse->priv->max_bitrate = 0; parse->priv->avg_bitrate = 0; @@ -867,6 +879,14 @@ gst_base_parse_reset (GstBaseParse * parse) if (parse->priv->adapter) gst_adapter_clear (parse->priv->adapter); + gst_base_parse_set_upstream_tags (parse, NULL); + + if (parse->priv->parser_tags) { + gst_tag_list_unref (parse->priv->parser_tags); + parse->priv->parser_tags = NULL; + } + parse->priv->parser_tags_merge_mode = GST_TAG_MERGE_APPEND; + parse->priv->new_frame = TRUE; parse->priv->first_buffer = TRUE; @@ -875,9 +895,91 @@ gst_base_parse_reset (GstBaseParse * parse) g_list_free (parse->priv->detect_buffers); parse->priv->detect_buffers = NULL; parse->priv->detect_buffers_size = 0; + + parse->priv->segment_seqnum = GST_SEQNUM_INVALID; GST_OBJECT_UNLOCK (parse); } +static gboolean +gst_base_parse_check_bitrate_tag (GstBaseParse * parse, const gchar * tag) +{ + gboolean got_tag = FALSE; + guint n = 0; + + if (parse->priv->upstream_tags != NULL) + got_tag = gst_tag_list_get_uint (parse->priv->upstream_tags, tag, &n); + + if (!got_tag && parse->priv->parser_tags != NULL) + got_tag = gst_tag_list_get_uint (parse->priv->parser_tags, tag, &n); + + return got_tag; +} + +/* check if upstream or subclass tags contain bitrates already */ +static void +gst_base_parse_check_bitrate_tags (GstBaseParse * parse) +{ + parse->priv->post_min_bitrate = + !gst_base_parse_check_bitrate_tag (parse, GST_TAG_MINIMUM_BITRATE); + parse->priv->post_avg_bitrate = + !gst_base_parse_check_bitrate_tag (parse, GST_TAG_BITRATE); + parse->priv->post_max_bitrate = + !gst_base_parse_check_bitrate_tag (parse, GST_TAG_MAXIMUM_BITRATE); +} + +/* Queues new tag event with the current combined state of the stream tags + * (i.e. upstream tags merged with subclass tags and current baseparse tags) */ +static void +gst_base_parse_queue_tag_event_update (GstBaseParse * parse) +{ + GstTagList *merged_tags; + + GST_LOG_OBJECT (parse, "upstream : %" GST_PTR_FORMAT, + parse->priv->upstream_tags); + GST_LOG_OBJECT (parse, "parser : %" GST_PTR_FORMAT, + parse->priv->parser_tags); + GST_LOG_OBJECT (parse, "mode : %d", parse->priv->parser_tags_merge_mode); + + merged_tags = + gst_tag_list_merge (parse->priv->upstream_tags, parse->priv->parser_tags, + parse->priv->parser_tags_merge_mode); + + GST_DEBUG_OBJECT (parse, "merged : %" GST_PTR_FORMAT, merged_tags); + + if (merged_tags == NULL) + return; + + if (gst_tag_list_is_empty (merged_tags)) { + gst_tag_list_unref (merged_tags); + return; + } + + if (parse->priv->framecount >= MIN_FRAMES_TO_POST_BITRATE) { + /* only add bitrate tags to non-empty taglists for now, and only if neither + * upstream tags nor the subclass sets the bitrate tag in question already */ + if (parse->priv->min_bitrate != G_MAXUINT && parse->priv->post_min_bitrate) { + GST_LOG_OBJECT (parse, "adding min bitrate %u", parse->priv->min_bitrate); + gst_tag_list_add (merged_tags, GST_TAG_MERGE_KEEP, + GST_TAG_MINIMUM_BITRATE, parse->priv->min_bitrate, NULL); + } + if (parse->priv->max_bitrate != 0 && parse->priv->post_max_bitrate) { + GST_LOG_OBJECT (parse, "adding max bitrate %u", parse->priv->max_bitrate); + gst_tag_list_add (merged_tags, GST_TAG_MERGE_KEEP, + GST_TAG_MAXIMUM_BITRATE, parse->priv->max_bitrate, NULL); + } + if (parse->priv->avg_bitrate != 0 && parse->priv->post_avg_bitrate) { + parse->priv->posted_avg_bitrate = parse->priv->avg_bitrate; + GST_LOG_OBJECT (parse, "adding avg bitrate %u", parse->priv->avg_bitrate); + gst_tag_list_add (merged_tags, GST_TAG_MERGE_KEEP, + GST_TAG_BITRATE, parse->priv->avg_bitrate, NULL); + } + } + + parse->priv->pending_events = + g_list_prepend (parse->priv->pending_events, + gst_event_new_tag (merged_tags)); +} + /* gst_base_parse_parse_frame: * @parse: #GstBaseParse. * @buffer: #GstBuffer. @@ -958,6 +1060,84 @@ gst_base_parse_convert (GstBaseParse * parse, return ret; } +static gboolean +update_upstream_provided (GQuark field_id, const GValue * value, + gpointer user_data) +{ + GstCaps *default_caps = user_data; + gint i; + gint caps_size; + + caps_size = gst_caps_get_size (default_caps); + for (i = 0; i < caps_size; i++) { + GstStructure *structure = gst_caps_get_structure (default_caps, i); + if (gst_structure_id_has_field (structure, field_id)) + gst_structure_id_set_value (structure, field_id, value); + } + + return TRUE; +} + +static GstCaps * +gst_base_parse_negotiate_default_caps (GstBaseParse * parse) +{ + GstCaps *caps, *templcaps; + GstCaps *sinkcaps = NULL; + GstCaps *default_caps = NULL; + GstStructure *structure; + + templcaps = gst_pad_get_pad_template_caps (GST_BASE_PARSE_SRC_PAD (parse)); + caps = gst_pad_peer_query_caps (GST_BASE_PARSE_SRC_PAD (parse), templcaps); + if (caps) + gst_caps_unref (templcaps); + else + caps = templcaps; + templcaps = NULL; + + if (!caps || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) { + goto caps_error; + } + + GST_LOG_OBJECT (parse, "peer caps %" GST_PTR_FORMAT, caps); + + /* before fixating, try to use whatever upstream provided */ + default_caps = gst_caps_copy (caps); + sinkcaps = gst_pad_get_current_caps (GST_BASE_PARSE_SINK_PAD (parse)); + + GST_LOG_OBJECT (parse, "current caps %" GST_PTR_FORMAT " for sinkpad", + sinkcaps); + + if (sinkcaps) { + structure = gst_caps_get_structure (sinkcaps, 0); + gst_structure_foreach (structure, update_upstream_provided, default_caps); + } + + default_caps = gst_caps_fixate (default_caps); + + if (!default_caps) { + GST_WARNING_OBJECT (parse, "Failed to create default caps !"); + goto caps_error; + } + + GST_INFO_OBJECT (parse, + "Chose default caps %" GST_PTR_FORMAT " for initial gap", default_caps); + + if (sinkcaps) + gst_caps_unref (sinkcaps); + gst_caps_unref (caps); + + return default_caps; + +caps_error: + { + if (caps) + gst_caps_unref (caps); + if (sinkcaps) + gst_caps_unref (sinkcaps); + return NULL; + } +} + /* gst_base_parse_sink_event: * @pad: #GstPad that received the event. * @event: #GstEvent to be handled. @@ -978,7 +1158,6 @@ gst_base_parse_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) return ret; } - /* gst_base_parse_sink_event_default: * @parse: #GstBaseParse. * @event: #GstEvent to be handled. @@ -1023,14 +1202,16 @@ gst_base_parse_sink_event_default (GstBaseParse * parse, GstEvent * event) const GstSegment *in_segment; GstSegment out_segment; gint64 offset = 0, next_dts; - guint32 seqnum = gst_event_get_seqnum (event); + parse->priv->segment_seqnum = gst_event_get_seqnum (event); gst_event_parse_segment (event, &in_segment); gst_segment_init (&out_segment, GST_FORMAT_TIME); out_segment.rate = in_segment->rate; out_segment.applied_rate = in_segment->applied_rate; - GST_DEBUG_OBJECT (parse, "segment %" GST_SEGMENT_FORMAT, in_segment); + GST_DEBUG_OBJECT (parse, "New segment %" GST_SEGMENT_FORMAT, in_segment); + GST_DEBUG_OBJECT (parse, "Current segment %" GST_SEGMENT_FORMAT, + &parse->segment); parse->priv->upstream_format = in_segment->format; if (in_segment->format == GST_FORMAT_BYTES) { @@ -1082,7 +1263,7 @@ gst_base_parse_sink_event_default (GstBaseParse * parse, GstEvent * event) gst_event_unref (event); event = gst_event_new_segment (&out_segment); - gst_event_set_seqnum (event, seqnum); + gst_event_set_seqnum (event, parse->priv->segment_seqnum); GST_DEBUG_OBJECT (parse, "Converted incoming segment to TIME. %" GST_SEGMENT_FORMAT, in_segment); @@ -1097,7 +1278,7 @@ gst_base_parse_sink_event_default (GstBaseParse * parse, GstEvent * event) out_segment.time = 0; event = gst_event_new_segment (&out_segment); - gst_event_set_seqnum (event, seqnum); + gst_event_set_seqnum (event, parse->priv->segment_seqnum); next_dts = 0; } else { @@ -1108,6 +1289,8 @@ gst_base_parse_sink_event_default (GstBaseParse * parse, GstEvent * event) gst_event_copy_segment (event, &out_segment); } + GST_DEBUG_OBJECT (parse, "OUT segment %" GST_SEGMENT_FORMAT, + &out_segment); memcpy (&parse->segment, &out_segment, sizeof (GstSegment)); /* @@ -1135,6 +1318,7 @@ gst_base_parse_sink_event_default (GstBaseParse * parse, GstEvent * event) parse->priv->last_dts = GST_CLOCK_TIME_NONE; parse->priv->prev_pts = GST_CLOCK_TIME_NONE; parse->priv->prev_dts = GST_CLOCK_TIME_NONE; + parse->priv->prev_dts_from_pts = FALSE; parse->priv->discont = TRUE; parse->priv->seen_keyframe = FALSE; parse->priv->skip = 0; @@ -1186,14 +1370,16 @@ gst_base_parse_sink_event_default (GstBaseParse * parse, GstEvent * event) GST_ELEMENT_ERROR (parse, STREAM, WRONG_TYPE, ("No valid frames found before end of stream"), (NULL)); } - /* newsegment and other serialized events before eos */ - gst_base_parse_push_pending_events (parse); if (!parse->priv->saw_gaps && parse->priv->framecount < MIN_FRAMES_TO_POST_BITRATE) { /* We've not posted bitrate tags yet - do so now */ - gst_base_parse_post_bitrates (parse, TRUE, TRUE, TRUE); + gst_base_parse_queue_tag_event_update (parse); } + + /* newsegment and other serialized events before eos */ + gst_base_parse_push_pending_events (parse); + forward_immediate = TRUE; break; case GST_EVENT_CUSTOM_DOWNSTREAM:{ @@ -1224,6 +1410,47 @@ gst_base_parse_sink_event_default (GstBaseParse * parse, GstEvent * event) { GST_DEBUG_OBJECT (parse, "draining current data due to gap event"); + /* Ensure we have caps before forwarding the event */ + if (!gst_pad_has_current_caps (GST_BASE_PARSE_SRC_PAD (parse))) { + GstCaps *default_caps = NULL; + if ((default_caps = gst_base_parse_negotiate_default_caps (parse))) { + GList *l; + GstEvent *caps_event = gst_event_new_caps (default_caps); + + GST_DEBUG_OBJECT (parse, + "Store caps event to pending list for initial pre-rolling"); + + /* Events are in decreasing order. Go down the list until we + * find the first pre-CAPS event and insert our CAPS event there. + * + * There should be a SEGMENT event already, which is > CAPS */ + for (l = parse->priv->pending_events; l; l = l->next) { + GstEvent *e = l->data; + + if (GST_EVENT_TYPE (e) < GST_EVENT_CAPS) { + parse->priv->pending_events = + g_list_insert_before (parse->priv->pending_events, l, + caps_event); + break; + } + } + /* No pending event that is < CAPS, so we have to add it at the very + * end of the list */ + if (!l) { + parse->priv->pending_events = + g_list_append (parse->priv->pending_events, caps_event); + } + gst_caps_unref (default_caps); + } else { + gst_event_unref (event); + event = NULL; + ret = FALSE; + GST_ELEMENT_ERROR (parse, STREAM, FORMAT, (NULL), + ("Parser output not negotiated before GAP event.")); + break; + } + } + gst_base_parse_push_pending_events (parse); if (parse->segment.rate > 0.0) @@ -1235,15 +1462,32 @@ gst_base_parse_sink_event_default (GstBaseParse * parse, GstEvent * event) break; } case GST_EVENT_TAG: - /* See if any bitrate tags were posted */ - gst_base_parse_handle_tag (parse, event); - break; + { + GstTagList *tags = NULL; + gst_event_parse_tag (event, &tags); + + /* We only care about stream tags here, global tags we just forward */ + if (gst_tag_list_get_scope (tags) != GST_TAG_SCOPE_STREAM) + break; + + gst_base_parse_set_upstream_tags (parse, tags); + gst_base_parse_queue_tag_event_update (parse); + parse->priv->tags_changed = FALSE; + gst_event_unref (event); + event = NULL; + ret = TRUE; + break; + } case GST_EVENT_STREAM_START: + { if (parse->priv->pad_mode != GST_PAD_MODE_PULL) forward_immediate = TRUE; - break; + gst_base_parse_set_upstream_tags (parse, NULL); + parse->priv->tags_changed = TRUE; + break; + } default: break; } @@ -1447,9 +1691,9 @@ gst_base_parse_src_event_default (GstBaseParse * parse, GstEvent * event) * @src_format: #GstFormat describing the source format. * @src_value: Source value to be converted. * @dest_format: #GstFormat defining the converted format. - * @dest_value: Pointer where the conversion result will be put. + * @dest_value: (out): Pointer where the conversion result will be put. * - * Default implementation of "convert" vmethod in #GstBaseParse class. + * Default implementation of #GstBaseParseClass.convert(). * * Returns: %TRUE if conversion was successful. */ @@ -1476,6 +1720,14 @@ gst_base_parse_convert_default (GstBaseParse * parse, return TRUE; } + if (parse->priv->upstream_format != GST_FORMAT_BYTES) { + /* don't do byte format conversions if we're not really parsing + * a raw elementary stream, since we don't really have BYTES + * position / duration info */ + if (src_format == GST_FORMAT_BYTES || dest_format == GST_FORMAT_BYTES) + goto no_slaved_conversions; + } + /* need at least some frames */ if (!parse->priv->framecount) goto no_framecount; @@ -1539,7 +1791,12 @@ no_duration_bytes: G_GUINT64_FORMAT, duration, bytes); return FALSE; } - +no_slaved_conversions: + { + GST_DEBUG_OBJECT (parse, + "Can't do format conversions when upstream format is not BYTES"); + return FALSE; + } } static void @@ -1570,45 +1827,6 @@ gst_base_parse_update_duration (GstBaseParse * parse) } } -static void -gst_base_parse_post_bitrates (GstBaseParse * parse, gboolean post_min, - gboolean post_avg, gboolean post_max) -{ - GstTagList *taglist = NULL; - - if (post_min && parse->priv->post_min_bitrate) { - taglist = gst_tag_list_new_empty (); - - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, - GST_TAG_MINIMUM_BITRATE, parse->priv->min_bitrate, NULL); - } - - if (post_avg && parse->priv->post_avg_bitrate) { - if (taglist == NULL) - taglist = gst_tag_list_new_empty (); - - parse->priv->posted_avg_bitrate = parse->priv->avg_bitrate; - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE, - parse->priv->avg_bitrate, NULL); - } - - if (post_max && parse->priv->post_max_bitrate) { - if (taglist == NULL) - taglist = gst_tag_list_new_empty (); - - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, - GST_TAG_MAXIMUM_BITRATE, parse->priv->max_bitrate, NULL); - } - - GST_DEBUG_OBJECT (parse, "Updated bitrates. Min: %u, Avg: %u, Max: %u", - parse->priv->min_bitrate, parse->priv->avg_bitrate, - parse->priv->max_bitrate); - - if (taglist != NULL) { - gst_pad_push_event (parse->srcpad, gst_event_new_tag (taglist)); - } -} - /* gst_base_parse_update_bitrates: * @parse: #GstBaseParse. * @buffer: Current frame as a #GstBuffer @@ -1619,12 +1837,10 @@ gst_base_parse_post_bitrates (GstBaseParse * parse, gboolean post_min, static void gst_base_parse_update_bitrates (GstBaseParse * parse, GstBaseParseFrame * frame) { - /* Only update the tag on a 10 kbps delta */ - static const gint update_threshold = 10000; - guint64 data_len, frame_dur; - gint overhead, frame_bitrate, old_avg_bitrate; - gboolean update_min = FALSE, update_avg = FALSE, update_max = FALSE; + gint overhead; + guint frame_bitrate; + guint64 frame_bitrate64; GstBuffer *buffer = frame->buffer; overhead = frame->overhead; @@ -1637,11 +1853,17 @@ gst_base_parse_update_bitrates (GstBaseParse * parse, GstBaseParseFrame * frame) /* duration should be valid by now, * either set by subclass or maybe based on fps settings */ if (GST_BUFFER_DURATION_IS_VALID (buffer) && parse->priv->acc_duration != 0) { + guint64 avg_bitrate; + /* Calculate duration of a frame from buffer properties */ frame_dur = GST_BUFFER_DURATION (buffer); - parse->priv->avg_bitrate = (8 * parse->priv->data_bytecount * GST_SECOND) / - parse->priv->acc_duration; + avg_bitrate = gst_util_uint64_scale (GST_SECOND, + 8 * parse->priv->data_bytecount, parse->priv->acc_duration); + + if (avg_bitrate > G_MAXUINT) + return; + parse->priv->avg_bitrate = (guint) avg_bitrate; } else { /* No way to figure out frame duration (is this even possible?) */ return; @@ -1651,48 +1873,54 @@ gst_base_parse_update_bitrates (GstBaseParse * parse, GstBaseParseFrame * frame) if (parse->priv->bitrate) { parse->priv->avg_bitrate = parse->priv->bitrate; /* spread this (confirmed) info ASAP */ - if (parse->priv->posted_avg_bitrate != parse->priv->avg_bitrate) - gst_base_parse_post_bitrates (parse, FALSE, TRUE, FALSE); + if (parse->priv->post_avg_bitrate && + parse->priv->posted_avg_bitrate != parse->priv->avg_bitrate) + parse->priv->tags_changed = TRUE; } - if (frame_dur) - frame_bitrate = (8 * data_len * GST_SECOND) / frame_dur; - else + if (!frame_dur) + return; + + frame_bitrate64 = gst_util_uint64_scale (GST_SECOND, 8 * data_len, frame_dur); + + if (frame_bitrate64 > G_MAXUINT) return; + frame_bitrate = (guint) frame_bitrate64; + GST_LOG_OBJECT (parse, "frame bitrate %u, avg bitrate %u", frame_bitrate, parse->priv->avg_bitrate); - if (parse->priv->framecount < MIN_FRAMES_TO_POST_BITRATE) { - goto exit; - } else if (parse->priv->framecount == MIN_FRAMES_TO_POST_BITRATE) { - /* always post all at threshold time */ - update_min = update_max = update_avg = TRUE; - } + if (parse->priv->framecount < MIN_FRAMES_TO_POST_BITRATE) + return; + + if (parse->priv->framecount == MIN_FRAMES_TO_POST_BITRATE && + (parse->priv->post_min_bitrate || parse->priv->post_avg_bitrate + || parse->priv->post_max_bitrate)) + parse->priv->tags_changed = TRUE; if (G_LIKELY (parse->priv->framecount >= MIN_FRAMES_TO_POST_BITRATE)) { if (frame_bitrate < parse->priv->min_bitrate) { parse->priv->min_bitrate = frame_bitrate; - update_min = TRUE; + if (parse->priv->post_min_bitrate) + parse->priv->tags_changed = TRUE; } if (frame_bitrate > parse->priv->max_bitrate) { parse->priv->max_bitrate = frame_bitrate; - update_max = TRUE; + if (parse->priv->post_max_bitrate) + parse->priv->tags_changed = TRUE; } - old_avg_bitrate = parse->priv->posted_avg_bitrate; - if ((gint) (old_avg_bitrate - parse->priv->avg_bitrate) > update_threshold - || (gint) (parse->priv->avg_bitrate - old_avg_bitrate) > - update_threshold) - update_avg = TRUE; + /* Only update the tag on a 2% change */ + if (parse->priv->post_avg_bitrate && parse->priv->avg_bitrate) { + guint64 diffprev = gst_util_uint64_scale (100, + ABSDIFF (parse->priv->avg_bitrate, parse->priv->posted_avg_bitrate), + parse->priv->avg_bitrate); + if (diffprev >= UPDATE_THRESHOLD) + parse->priv->tags_changed = TRUE; + } } - - if ((update_min || update_avg || update_max)) - gst_base_parse_post_bitrates (parse, update_min, update_avg, update_max); - -exit: - return; } /** @@ -2031,6 +2259,12 @@ gst_base_parse_handle_buffer (GstBaseParse * parse, GstBuffer * buffer, gst_adapter_clear (parse->priv->adapter); } + if (*skip == 0 && *flushed == 0) { + /* Carry over discont if we need more data */ + if (GST_BUFFER_IS_DISCONT (frame->buffer)) + parse->priv->discont = TRUE; + } + gst_base_parse_frame_free (frame); return ret; @@ -2228,6 +2462,11 @@ gst_base_parse_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) gst_base_parse_check_media (parse); } + if (parse->priv->tags_changed) { + gst_base_parse_queue_tag_event_update (parse); + parse->priv->tags_changed = FALSE; + } + /* Push pending events, including SEGMENT events */ gst_base_parse_push_pending_events (parse); @@ -2248,6 +2487,7 @@ gst_base_parse_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) && last_start > parse->segment.start && (!GST_CLOCK_TIME_IS_VALID (parse->segment.stop) || last_start < parse->segment.stop))) { + GstEvent *topush; GST_DEBUG_OBJECT (parse, "Gap of %" G_GINT64_FORMAT " ns detected in stream " "(%" @@ -2257,8 +2497,10 @@ gst_base_parse_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) GST_TIME_ARGS (last_start)); /* skip gap FIXME */ - gst_pad_push_event (parse->srcpad, - gst_event_new_segment (&parse->segment)); + topush = gst_event_new_segment (&parse->segment); + if (parse->priv->segment_seqnum != GST_SEQNUM_INVALID) + gst_event_set_seqnum (topush, parse->priv->segment_seqnum); + gst_pad_push_event (parse->srcpad, topush); parse->segment.position = last_start; } @@ -2275,6 +2517,14 @@ gst_base_parse_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) frame->flags |= GST_BASE_PARSE_FRAME_FLAG_CLIP; } + /* Push pending events, if there are any new ones + * like tags added by pre_push_frame */ + if (parse->priv->tags_changed) { + gst_base_parse_queue_tag_event_update (parse); + parse->priv->tags_changed = FALSE; + } + gst_base_parse_push_pending_events (parse); + /* take final ownership of frame buffer */ if (frame->out_buffer) { buffer = frame->out_buffer; @@ -2319,6 +2569,8 @@ gst_base_parse_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) if (ret == GST_BASE_PARSE_FLOW_DROPPED) { GST_LOG_OBJECT (parse, "frame (%" G_GSIZE_FORMAT " bytes) dropped", size); + if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) + parse->priv->discont = TRUE; gst_buffer_unref (buffer); ret = GST_FLOW_OK; } else if (ret == GST_FLOW_OK) { @@ -2375,8 +2627,9 @@ gst_base_parse_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) } /* Update current running segment position */ - if (ret == GST_FLOW_OK && last_stop != GST_CLOCK_TIME_NONE && - parse->segment.position < last_stop) + if ((ret == GST_FLOW_OK || ret == GST_FLOW_NOT_LINKED) + && last_stop != GST_CLOCK_TIME_NONE + && parse->segment.position < last_stop) parse->segment.position = last_stop; return ret; @@ -2488,13 +2741,17 @@ exit: return ret; } -/* gst_base_parse_drain: +/** + * gst_base_parse_drain: + * @parse: a #GstBaseParse * * Drains the adapter until it is empty. It decreases the min_frame_size to * match the current adapter size and calls chain method until the adapter * is emptied or chain returns with error. + * + * Since: 1.12 */ -static void +void gst_base_parse_drain (GstBaseParse * parse) { guint avail; @@ -2552,6 +2809,12 @@ gst_base_parse_send_buffers (GstBaseParse * parse) if (first) { GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); first = FALSE; + } else { + /* likewise, subsequent buffers should never have DISCONT + * according to the "reverse fragment protocol", or such would + * confuse a downstream decoder + * (could be DISCONT due to aggregating upstream fragments by parsing) */ + GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT); } /* iterate output queue an push downstream */ @@ -2589,6 +2852,7 @@ gst_base_parse_start_fragment (GstBaseParse * parse) parse->priv->prev_pts = GST_CLOCK_TIME_NONE; parse->priv->next_dts = GST_CLOCK_TIME_NONE; parse->priv->prev_dts = GST_CLOCK_TIME_NONE; + parse->priv->prev_dts_from_pts = FALSE; /* prevent it hanging around stop all the time */ parse->segment.position = GST_CLOCK_TIME_NONE; /* mark next run */ @@ -2959,6 +3223,7 @@ gst_base_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) /* Stop either when adapter is empty or we are flushing */ while (!parse->priv->flushing) { gint flush = 0; + gboolean updated_prev_pts = FALSE; /* note: if subclass indicates MAX fsize, * this will not likely be available anyway ... */ @@ -2983,11 +3248,15 @@ gst_base_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) * but interpolate in between */ pts = gst_adapter_prev_pts (parse->priv->adapter, NULL); dts = gst_adapter_prev_dts (parse->priv->adapter, NULL); - if (GST_CLOCK_TIME_IS_VALID (pts) && (parse->priv->prev_pts != pts)) + if (GST_CLOCK_TIME_IS_VALID (pts) && (parse->priv->prev_pts != pts)) { parse->priv->prev_pts = parse->priv->next_pts = pts; + updated_prev_pts = TRUE; + } - if (GST_CLOCK_TIME_IS_VALID (dts) && (parse->priv->prev_dts != dts)) + if (GST_CLOCK_TIME_IS_VALID (dts) && (parse->priv->prev_dts != dts)) { parse->priv->prev_dts = parse->priv->next_dts = dts; + parse->priv->prev_dts_from_pts = FALSE; + } /* we can mess with, erm interpolate, timestamps, * and incoming stuff has PTS but no DTS seen so far, @@ -2995,9 +3264,12 @@ gst_base_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) if (parse->priv->infer_ts && parse->priv->pts_interpolate && !GST_CLOCK_TIME_IS_VALID (dts) && - !GST_CLOCK_TIME_IS_VALID (parse->priv->prev_dts) && - GST_CLOCK_TIME_IS_VALID (pts)) - parse->priv->next_dts = pts; + (!GST_CLOCK_TIME_IS_VALID (parse->priv->prev_dts) + || (parse->priv->prev_dts_from_pts && updated_prev_pts)) + && GST_CLOCK_TIME_IS_VALID (pts)) { + parse->priv->prev_dts = parse->priv->next_dts = pts; + parse->priv->prev_dts_from_pts = TRUE; + } /* always pass all available data */ tmpbuf = gst_adapter_get_buffer (parse->priv->adapter, av); @@ -3025,7 +3297,8 @@ gst_base_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) ret = old_ret; goto done; } - old_ret = ret; + if (old_ret == GST_FLOW_OK) + old_ret = ret; } done: @@ -3070,28 +3343,6 @@ gst_base_parse_pull_range (GstBaseParse * parse, guint size, return ret; } - if (gst_buffer_get_size (parse->priv->cache) >= size) { - *buffer = - gst_buffer_copy_region (parse->priv->cache, GST_BUFFER_COPY_ALL, 0, - size); - GST_BUFFER_OFFSET (*buffer) = parse->priv->offset; - return GST_FLOW_OK; - } - - /* Not possible to get enough data, try a last time with - * requesting exactly the size we need */ - gst_buffer_unref (parse->priv->cache); - parse->priv->cache = NULL; - - ret = gst_pad_pull_range (parse->sinkpad, parse->priv->offset, size, - &parse->priv->cache); - - if (ret != GST_FLOW_OK) { - GST_DEBUG_OBJECT (parse, "pull_range returned %d", ret); - *buffer = NULL; - return ret; - } - if (gst_buffer_get_size (parse->priv->cache) < size) { GST_DEBUG_OBJECT (parse, "Returning short buffer at offset %" G_GUINT64_FORMAT ": wanted %u bytes, got %" G_GSIZE_FORMAT " bytes", @@ -3380,18 +3631,23 @@ pause: /* for fatal errors we post an error message, wrong-state is * not fatal because it happens due to flushes and only means * that we should stop now. */ - GST_ELEMENT_ERROR (parse, STREAM, FAILED, (NULL), - ("streaming stopped, reason %s", gst_flow_get_name (ret))); + GST_ELEMENT_FLOW_ERROR (parse, ret); push_eos = TRUE; } if (push_eos) { + GstEvent *topush; if (parse->priv->estimated_duration <= 0) { gst_base_parse_update_duration (parse); } /* Push pending events, including SEGMENT events */ gst_base_parse_push_pending_events (parse); - gst_pad_push_event (parse->srcpad, gst_event_new_eos ()); + topush = gst_event_new_eos (); + GST_DEBUG_OBJECT (parse, "segment_seqnum:%" G_GUINT32_FORMAT, + parse->priv->segment_seqnum); + if (parse->priv->segment_seqnum != GST_SEQNUM_INVALID) + gst_event_set_seqnum (topush, parse->priv->segment_seqnum); + gst_pad_push_event (parse->srcpad, topush); } gst_object_unref (parse); } @@ -3430,6 +3686,8 @@ gst_base_parse_sink_activate (GstPad * sinkpad, GstObject * parent) goto baseparse_push; parse->priv->push_stream_start = TRUE; + /* In pull mode, upstream is BYTES */ + parse->priv->upstream_format = GST_FORMAT_BYTES; return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_base_parse_loop, sinkpad, NULL); @@ -3468,6 +3726,7 @@ gst_base_parse_activate (GstBaseParse * parse, gboolean active) result = klass->stop (parse); parse->priv->pad_mode = GST_PAD_MODE_NONE; + parse->priv->upstream_format = GST_FORMAT_UNDEFINED; } GST_DEBUG_OBJECT (parse, "activate return: %d", result); return result; @@ -3491,9 +3750,10 @@ gst_base_parse_sink_activate_mode (GstPad * pad, GstObject * parent, switch (mode) { case GST_PAD_MODE_PULL: if (active) { + GstEvent *ev = gst_event_new_segment (&parse->segment); + parse->priv->segment_seqnum = gst_event_get_seqnum (ev); parse->priv->pending_events = - g_list_prepend (parse->priv->pending_events, - gst_event_new_segment (&parse->segment)); + g_list_prepend (parse->priv->pending_events, ev); result = TRUE; } else { result = gst_pad_stop_task (pad); @@ -3588,11 +3848,11 @@ gst_base_parse_set_average_bitrate (GstBaseParse * parse, guint bitrate) /** * gst_base_parse_set_min_frame_size: * @parse: #GstBaseParse. - * @min_size: Minimum size of the data that this base class should give to - * subclass. + * @min_size: Minimum size in bytes of the data that this base class should + * give to subclass. * * Subclass can use this function to tell the base class that it needs to - * give at least #min_size buffers. + * be given buffers of at least @min_size bytes. */ void gst_base_parse_set_min_frame_size (GstBaseParse * parse, guint min_size) @@ -3643,7 +3903,11 @@ gst_base_parse_set_frame_rate (GstBaseParse * parse, guint fps_num, gst_util_uint64_scale (GST_SECOND, fps_den * lead_out, fps_num); /* aim for about 1.5s to estimate duration */ if (parse->priv->update_interval < 0) { - parse->priv->update_interval = fps_num * 3 / (fps_den * 2); + guint64 interval = gst_util_uint64_scale (fps_num, 3, + G_GUINT64_CONSTANT (2) * fps_den); + + parse->priv->update_interval = MIN (interval, G_MAXINT); + GST_LOG_OBJECT (parse, "estimated update interval to %d frames", parse->priv->update_interval); } @@ -3696,10 +3960,10 @@ gst_base_parse_set_syncable (GstBaseParse * parse, gboolean syncable) * Set if the nature of the format or configuration does not allow (much) * parsing, and the parser should operate in passthrough mode (which only * applies when operating in push mode). That is, incoming buffers are - * pushed through unmodified, i.e. no @check_valid_frame or @parse_frame - * callbacks will be invoked, but @pre_push_frame will still be invoked, - * so subclass can perform as much or as little is appropriate for - * passthrough semantics in @pre_push_frame. + * pushed through unmodified, i.e. no #GstBaseParseClass.handle_frame() + * will be invoked, but #GstBaseParseClass.pre_push_frame() will still be + * invoked, so subclass can perform as much or as little is appropriate for + * passthrough semantics in #GstBaseParseClass.pre_push_frame(). */ void gst_base_parse_set_passthrough (GstBaseParse * parse, gboolean passthrough) @@ -3822,7 +4086,10 @@ gst_base_parse_src_query_default (GstBaseParse * parse, GstQuery * query) if (!res) { /* Fall back on interpreting segment */ GST_OBJECT_LOCK (parse); - if (format == GST_FORMAT_BYTES) { + /* Only reply BYTES if upstream is in BYTES already, otherwise + * we're not in charge */ + if (format == GST_FORMAT_BYTES + && parse->priv->upstream_format == GST_FORMAT_BYTES) { dest_value = parse->priv->offset; res = TRUE; } else if (format == parse->segment.format && @@ -3832,9 +4099,10 @@ gst_base_parse_src_query_default (GstBaseParse * parse, GstQuery * query) res = TRUE; } GST_OBJECT_UNLOCK (parse); - if (!res) { + if (!res && parse->priv->upstream_format == GST_FORMAT_BYTES) { /* no precise result, upstream no idea either, then best estimate */ - /* priv->offset is updated in both PUSH/PULL modes */ + /* priv->offset is updated in both PUSH/PULL modes, *iff* we're + * in charge of things */ res = gst_base_parse_convert (parse, GST_FORMAT_BYTES, parse->priv->offset, format, &dest_value); } @@ -3884,8 +4152,8 @@ gst_base_parse_src_query_default (GstBaseParse * parse, GstQuery * query) if (!gst_base_parse_get_duration (parse, GST_FORMAT_TIME, &duration) || duration == -1) { /* seekable if we still have a chance to get duration later on */ - seekable = - parse->priv->upstream_seekable && parse->priv->update_interval; + seekable = parse->priv->upstream_seekable && + (parse->priv->update_interval > 0); } else { seekable = parse->priv->upstream_seekable; GST_LOG_OBJECT (parse, "already determine upstream seekabled: %d", @@ -4392,10 +4660,6 @@ gst_base_parse_handle_seek (GstBaseParse * parse, GstEvent * event) gst_pad_push_event (parse->srcpad, gst_event_ref (fevent)); gst_pad_push_event (parse->sinkpad, fevent); gst_base_parse_clear_queues (parse); - } else { - /* keep track of our position */ - seeksegment.base = gst_segment_to_running_time (&seeksegment, - seeksegment.format, parse->segment.position); } memcpy (&parse->segment, &seeksegment, sizeof (GstSegment)); @@ -4529,33 +4793,23 @@ convert_failed: } } -/* Checks if bitrates are available from upstream tags so that we don't - * override them later - */ static void -gst_base_parse_handle_tag (GstBaseParse * parse, GstEvent * event) +gst_base_parse_set_upstream_tags (GstBaseParse * parse, GstTagList * taglist) { - GstTagList *taglist = NULL; - guint tmp; - - gst_event_parse_tag (event, &taglist); - - /* We only care about stream tags here */ - if (gst_tag_list_get_scope (taglist) != GST_TAG_SCOPE_STREAM) + if (taglist == parse->priv->upstream_tags) return; - if (gst_tag_list_get_uint (taglist, GST_TAG_MINIMUM_BITRATE, &tmp)) { - GST_DEBUG_OBJECT (parse, "upstream min bitrate %d", tmp); - parse->priv->post_min_bitrate = FALSE; - } - if (gst_tag_list_get_uint (taglist, GST_TAG_BITRATE, &tmp)) { - GST_DEBUG_OBJECT (parse, "upstream avg bitrate %d", tmp); - parse->priv->post_avg_bitrate = FALSE; - } - if (gst_tag_list_get_uint (taglist, GST_TAG_MAXIMUM_BITRATE, &tmp)) { - GST_DEBUG_OBJECT (parse, "upstream max bitrate %d", tmp); - parse->priv->post_max_bitrate = FALSE; + if (parse->priv->upstream_tags) { + gst_tag_list_unref (parse->priv->upstream_tags); + parse->priv->upstream_tags = NULL; } + + GST_INFO_OBJECT (parse, "upstream tags: %" GST_PTR_FORMAT, taglist); + + if (taglist != NULL) + parse->priv->upstream_tags = gst_tag_list_ref (taglist); + + gst_base_parse_check_bitrate_tags (parse); } #if 0 @@ -4674,6 +4928,55 @@ gst_base_parse_set_ts_at_offset (GstBaseParse * parse, gsize offset) if (GST_CLOCK_TIME_IS_VALID (pts) && (parse->priv->prev_pts != pts)) parse->priv->prev_pts = parse->priv->next_pts = pts; - if (GST_CLOCK_TIME_IS_VALID (dts) && (parse->priv->prev_dts != dts)) + if (GST_CLOCK_TIME_IS_VALID (dts) && (parse->priv->prev_dts != dts)) { parse->priv->prev_dts = parse->priv->next_dts = dts; + parse->priv->prev_dts_from_pts = FALSE; + } +} + +/** + * gst_base_parse_merge_tags: + * @parse: a #GstBaseParse + * @tags: (allow-none): a #GstTagList to merge, or NULL to unset + * previously-set tags + * @mode: the #GstTagMergeMode to use, usually #GST_TAG_MERGE_REPLACE + * + * Sets the parser subclass's tags and how they should be merged with any + * upstream stream tags. This will override any tags previously-set + * with gst_base_parse_merge_tags(). + * + * Note that this is provided for convenience, and the subclass is + * not required to use this and can still do tag handling on its own. + * + * Since: 1.6 + */ +void +gst_base_parse_merge_tags (GstBaseParse * parse, GstTagList * tags, + GstTagMergeMode mode) +{ + g_return_if_fail (GST_IS_BASE_PARSE (parse)); + g_return_if_fail (tags == NULL || GST_IS_TAG_LIST (tags)); + g_return_if_fail (tags == NULL || mode != GST_TAG_MERGE_UNDEFINED); + + GST_OBJECT_LOCK (parse); + + if (tags != parse->priv->parser_tags) { + if (parse->priv->parser_tags) { + gst_tag_list_unref (parse->priv->parser_tags); + parse->priv->parser_tags = NULL; + parse->priv->parser_tags_merge_mode = GST_TAG_MERGE_APPEND; + } + if (tags) { + parse->priv->parser_tags = gst_tag_list_ref (tags); + parse->priv->parser_tags_merge_mode = mode; + } + + GST_DEBUG_OBJECT (parse, "setting parser tags to %" GST_PTR_FORMAT + " (mode %d)", tags, parse->priv->parser_tags_merge_mode); + + gst_base_parse_check_bitrate_tags (parse); + parse->priv->tags_changed = TRUE; + } + + GST_OBJECT_UNLOCK (parse); }