X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=libs%2Fgst%2Fbase%2Fgstbaseparse.c;h=99283b8e85a5bfe639ab7e87063b95de459273f0;hb=91798e16cc09420163684d13779f13f374164ea2;hp=ac357d911ce7a746ffaad2fe7e3d9aebf2a9813a;hpb=d10c488d63638e12490f5036dc50581d22592b5e;p=platform%2Fupstream%2Fgstreamer.git diff --git a/libs/gst/base/gstbaseparse.c b/libs/gst/base/gstbaseparse.c index ac357d9..99283b8 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,109 +31,88 @@ * 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 @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. * * Subclass is responsible for providing pad template caps for * source and sink pads. The pads need to be named "sink" and "src". It also @@ -151,44 +131,30 @@ * 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 @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. * */ @@ -273,6 +239,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; @@ -478,8 +445,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 gint64 gst_base_parse_find_offset (GstBaseParse * parse, GstClockTime time, gboolean before, GstClockTime * _ts); static GstFlowReturn gst_base_parse_locate_time (GstBaseParse * parse, @@ -690,7 +655,7 @@ gst_base_parse_get_property (GObject * object, guint prop_id, GValue * value, } } -static GstBaseParseFrame * +GstBaseParseFrame * gst_base_parse_frame_copy (GstBaseParseFrame * frame) { GstBaseParseFrame *copy; @@ -791,6 +756,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) { @@ -949,23 +916,25 @@ gst_base_parse_queue_tag_event_update (GstBaseParse * parse) return; } - /* 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); + 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 = @@ -1053,6 +1022,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. @@ -1073,7 +1120,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. @@ -1230,6 +1276,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; @@ -1321,6 +1368,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) @@ -1590,6 +1678,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; @@ -1653,7 +1749,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 @@ -1695,7 +1796,9 @@ static void gst_base_parse_update_bitrates (GstBaseParse * parse, GstBaseParseFrame * frame) { guint64 data_len, frame_dur; - gint overhead, frame_bitrate; + gint overhead; + guint frame_bitrate; + guint64 frame_bitrate64; GstBuffer *buffer = frame->buffer; overhead = frame->overhead; @@ -1708,11 +1811,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 = avg_bitrate; } else { /* No way to figure out frame duration (is this even possible?) */ return; @@ -1722,15 +1831,21 @@ 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) + 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); @@ -2102,6 +2217,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; @@ -2351,6 +2472,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; @@ -2395,6 +2524,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) { @@ -2565,13 +2696,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; @@ -2629,6 +2764,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 */ @@ -2666,6 +2807,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 */ @@ -3036,6 +3178,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 ... */ @@ -3060,11 +3203,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, @@ -3072,9 +3219,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); @@ -3102,7 +3252,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: @@ -3457,8 +3608,7 @@ 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) { @@ -3507,6 +3657,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); @@ -3545,6 +3697,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; @@ -3720,7 +3873,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); } @@ -3899,7 +4056,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 && @@ -3909,9 +4069,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); } @@ -3961,8 +4122,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", @@ -4469,10 +4630,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)); @@ -4741,8 +4898,10 @@ 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; + } } /**