• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 2008 Nokia Corporation. All rights reserved.
3  *   Contact: Stefan Kost <stefan.kost@nokia.com>
4  * Copyright (C) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk>.
5  * Copyright (C) 2011, Hewlett-Packard Development Company, L.P.
6  *   Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23 
24 /**
25  * SECTION:gstbaseparse
26  * @title: GstBaseParse
27  * @short_description: Base class for stream parsers
28  * @see_also: #GstBaseTransform
29  *
30  * This base class is for parser elements that process data and splits it
31  * into separate audio/video/whatever frames.
32  *
33  * It provides for:
34  *
35  *   * provides one sink pad and one source pad
36  *   * handles state changes
37  *   * can operate in pull mode or push mode
38  *   * handles seeking in both modes
39  *   * handles events (SEGMENT/EOS/FLUSH)
40  *   * handles queries (POSITION/DURATION/SEEKING/FORMAT/CONVERT)
41  *   * handles flushing
42  *
43  * The purpose of this base class is to provide the basic functionality of
44  * a parser and share a lot of rather complex code.
45  *
46  * # Description of the parsing mechanism:
47  *
48  * ## Set-up phase
49  *
50  *  * #GstBaseParse calls #GstBaseParseClass::start to inform subclass
51  *    that data processing is about to start now.
52  *
53  *  * #GstBaseParse class calls #GstBaseParseClass::set_sink_caps to
54  *    inform the subclass about incoming sinkpad caps. Subclass could
55  *    already set the srcpad caps accordingly, but this might be delayed
56  *    until calling gst_base_parse_finish_frame() with a non-queued frame.
57  *
58  *  * At least at this point subclass needs to tell the #GstBaseParse class
59  *    how big data chunks it wants to receive (minimum frame size ). It can
60  *    do this with gst_base_parse_set_min_frame_size().
61  *
62  *  * #GstBaseParse class sets up appropriate data passing mode (pull/push)
63  *    and starts to process the data.
64  *
65  * ## Parsing phase
66  *
67  *  * #GstBaseParse gathers at least min_frame_size bytes of data either
68  *    by pulling it from upstream or collecting buffers in an internal
69  *    #GstAdapter.
70  *
71  *  * A buffer of (at least) min_frame_size bytes is passed to subclass
72  *    with #GstBaseParseClass::handle_frame. Subclass checks the contents
73  *    and can optionally return #GST_FLOW_OK along with an amount of data
74  *    to be skipped to find a valid frame (which will result in a
75  *    subsequent DISCONT).  If, otherwise, the buffer does not hold a
76  *    complete frame, #GstBaseParseClass::handle_frame can merely return
77  *    and will be called again when additional data is available.  In push
78  *    mode this amounts to an additional input buffer (thus minimal
79  *    additional latency), in pull mode this amounts to some arbitrary
80  *    reasonable buffer size increase.
81  *
82  *    Of course, gst_base_parse_set_min_frame_size() could also be used if
83  *    a very specific known amount of additional data is required.  If,
84  *    however, the buffer holds a complete valid frame, it can pass the
85  *    size of this frame to gst_base_parse_finish_frame().
86  *
87  *    If acting as a converter, it can also merely indicate consumed input
88  *    data while simultaneously providing custom output data.  Note that
89  *    baseclass performs some processing (such as tracking overall consumed
90  *    data rate versus duration) for each finished frame, but other state
91  *    is only updated upon each call to #GstBaseParseClass::handle_frame
92  *    (such as tracking upstream input timestamp).
93  *
94  *    Subclass is also responsible for setting the buffer metadata
95  *    (e.g. buffer timestamp and duration, or keyframe if applicable).
96  *    (although the latter can also be done by #GstBaseParse if it is
97  *    appropriately configured, see below).  Frame is provided with
98  *    timestamp derived from upstream (as much as generally possible),
99  *    duration obtained from configuration (see below), and offset
100  *    if meaningful (in pull mode).
101  *
102  *    Note that #GstBaseParseClass::handle_frame might receive any small
103  *    amount of input data when leftover data is being drained (e.g. at
104  *    EOS).
105  *
106  *  * As part of finish frame processing, just prior to actually pushing
107  *    the buffer in question, it is passed to
108  *    #GstBaseParseClass::pre_push_frame which gives subclass yet one last
109  *    chance to examine buffer metadata, or to send some custom (tag)
110  *    events, or to perform custom (segment) filtering.
111  *
112  *  * During the parsing process #GstBaseParseClass will handle both srcpad
113  *    and sinkpad events. They will be passed to subclass if
114  *    #GstBaseParseClass::sink_event or #GstBaseParseClass::src_event
115  *    implementations have been provided.
116  *
117  * ## Shutdown phase
118  *
119  * * #GstBaseParse class calls #GstBaseParseClass::stop to inform the
120  *   subclass that data parsing will be stopped.
121  *
122  * Subclass is responsible for providing pad template caps for source and
123  * sink pads. The pads need to be named "sink" and "src". It also needs to
124  * set the fixed caps on srcpad, when the format is ensured (e.g.  when
125  * base class calls subclass' #GstBaseParseClass::set_sink_caps function).
126  *
127  * This base class uses %GST_FORMAT_DEFAULT as a meaning of frames. So,
128  * subclass conversion routine needs to know that conversion from
129  * %GST_FORMAT_TIME to %GST_FORMAT_DEFAULT must return the
130  * frame number that can be found from the given byte position.
131  *
132  * #GstBaseParse uses subclasses conversion methods also for seeking (or
133  * otherwise uses its own default one, see also below).
134  *
135  * Subclass @start and @stop functions will be called to inform the beginning
136  * and end of data processing.
137  *
138  * Things that subclass need to take care of:
139  *
140  * * Provide pad templates
141  * * Fixate the source pad caps when appropriate
142  * * Inform base class how big data chunks should be retrieved. This is
143  *   done with gst_base_parse_set_min_frame_size() function.
144  * * Examine data chunks passed to subclass with
145  *   #GstBaseParseClass::handle_frame and pass proper frame(s) to
146  *   gst_base_parse_finish_frame(), and setting src pad caps and timestamps
147  *   on frame.
148  * * Provide conversion functions
149  * * Update the duration information with gst_base_parse_set_duration()
150  * * Optionally passthrough using gst_base_parse_set_passthrough()
151  * * Configure various baseparse parameters using
152  *   gst_base_parse_set_average_bitrate(), gst_base_parse_set_syncable()
153  *   and gst_base_parse_set_frame_rate().
154  *
155  * * In particular, if subclass is unable to determine a duration, but
156  *   parsing (or specs) yields a frames per seconds rate, then this can be
157  *   provided to #GstBaseParse to enable it to cater for buffer time
158  *   metadata (which will be taken from upstream as much as
159  *   possible). Internally keeping track of frame durations and respective
160  *   sizes that have been pushed provides #GstBaseParse with an estimated
161  *   bitrate. A default #GstBaseParseClass::convert (used if not
162  *   overridden) will then use these rates to perform obvious conversions.
163  *   These rates are also used to update (estimated) duration at regular
164  *   frame intervals.
165  *
166  */
167 
168 /* TODO:
169  *  - In push mode provide a queue of adapter-"queued" buffers for upstream
170  *    buffer metadata
171  *  - Queue buffers/events until caps are set
172  */
173 
174 #ifdef HAVE_CONFIG_H
175 #  include "config.h"
176 #endif
177 
178 #include <stdlib.h>
179 #include <string.h>
180 
181 #include <gst/base/gstadapter.h>
182 
183 #include "gstbaseparse.h"
184 
185 /* FIXME: get rid of old GstIndex code */
186 #include "gstindex.h"
187 #include "gstindex.c"
188 #include "gstmemindex.c"
189 #ifdef OHOS_OPT_PERFORMANCE
190 // ohos.opt.performance.0005
191 // add trace
192 #include "gst_trace.h"
193 #endif
194 #define GST_BASE_PARSE_FRAME_PRIVATE_FLAG_NOALLOC  (1 << 0)
195 
196 #define MIN_FRAMES_TO_POST_BITRATE 10
197 #define TARGET_DIFFERENCE          (20 * GST_SECOND)
198 #define MAX_INDEX_ENTRIES          4096
199 #define UPDATE_THRESHOLD           2
200 
201 #define ABSDIFF(a,b) (((a) > (b)) ? ((a) - (b)) : ((b) - (a)))
202 
203 GST_DEBUG_CATEGORY_STATIC (gst_base_parse_debug);
204 #define GST_CAT_DEFAULT gst_base_parse_debug
205 
206 /* Supported formats */
207 static const GstFormat fmtlist[] = {
208   GST_FORMAT_DEFAULT,
209   GST_FORMAT_BYTES,
210   GST_FORMAT_TIME,
211   GST_FORMAT_UNDEFINED
212 };
213 
214 struct _GstBaseParsePrivate
215 {
216   GstPadMode pad_mode;
217 
218   GstAdapter *adapter;
219 
220   gint64 duration;
221   GstFormat duration_fmt;
222   gint64 estimated_duration;
223   gint64 estimated_drift;
224 
225   guint min_frame_size;
226   gboolean disable_passthrough;
227   gboolean passthrough;
228   gboolean pts_interpolate;
229   gboolean infer_ts;
230   gboolean syncable;
231   gboolean has_timing_info;
232   guint fps_num, fps_den;
233   gint update_interval;
234   guint bitrate;
235   guint lead_in, lead_out;
236   GstClockTime lead_in_ts, lead_out_ts;
237   GstClockTime min_latency, max_latency;
238 
239   gboolean discont;
240   gboolean flushing;
241   gboolean drain;
242   gboolean saw_gaps;
243 
244   gint64 offset;
245   gint64 sync_offset;
246   GstClockTime next_pts;
247   GstClockTime next_dts;
248   GstClockTime prev_pts;
249   GstClockTime prev_dts;
250   gboolean prev_dts_from_pts;
251   GstClockTime frame_duration;
252   gboolean seen_keyframe;
253   gboolean is_video;
254   gint flushed;
255 
256   guint64 framecount;
257   guint64 bytecount;
258   guint64 data_bytecount;
259   guint64 acc_duration;
260   GstClockTime first_frame_pts;
261   GstClockTime first_frame_dts;
262   gint64 first_frame_offset;
263 
264   gboolean post_min_bitrate;
265   gboolean post_avg_bitrate;
266   gboolean post_max_bitrate;
267 
268   guint min_bitrate;
269   guint avg_bitrate;
270   guint max_bitrate;
271   guint posted_avg_bitrate;
272 
273   /* frames/buffers that are queued and ready to go on OK */
274   GQueue queued_frames;
275 
276   GstBuffer *cache;
277 
278   /* index entry storage, either ours or provided */
279   GstIndex *index;
280   gint index_id;
281   gboolean own_index;
282   GMutex index_lock;
283 
284   /* seek table entries only maintained if upstream is BYTE seekable */
285   gboolean upstream_seekable;
286   gboolean upstream_has_duration;
287   gint64 upstream_size;
288   GstFormat upstream_format;
289   /* minimum distance between two index entries */
290   GstClockTimeDiff idx_interval;
291   guint64 idx_byte_interval;
292   /* ts and offset of last entry added */
293   GstClockTime index_last_ts;
294   gint64 index_last_offset;
295   gboolean index_last_valid;
296 
297   /* timestamps currently produced are accurate, e.g. started from 0 onwards */
298   gboolean exact_position;
299   /* seek events are temporarily kept to match them with newsegments */
300   GSList *pending_seeks;
301 
302   /* reverse playback */
303   GSList *buffers_pending;
304   GSList *buffers_head;
305   GSList *buffers_queued;
306   GSList *buffers_send;
307   GstClockTime last_pts;
308   GstClockTime last_dts;
309   gint64 last_offset;
310 
311   /* Pending serialized events */
312   GList *pending_events;
313 
314   /* If baseparse has checked the caps to identify if it is
315    * handling video or audio */
316   gboolean checked_media;
317 
318   /* offset of last parsed frame/data */
319   gint64 prev_offset;
320   /* force a new frame, regardless of offset */
321   gboolean new_frame;
322   /* whether we are merely scanning for a frame */
323   gboolean scanning;
324   /* ... and resulting frame, if any */
325   GstBaseParseFrame *scanned_frame;
326 
327   /* TRUE if we're still detecting the format, i.e.
328    * if ::detect() is still called for future buffers */
329   gboolean detecting;
330   GList *detect_buffers;
331   guint detect_buffers_size;
332 
333   /* True when no buffers have been received yet */
334   gboolean first_buffer;
335 
336   /* if TRUE, a STREAM_START event needs to be pushed */
337   gboolean push_stream_start;
338 
339   /* When we need to skip more data than we have currently */
340   guint skip;
341 
342   /* Tag handling (stream tags only, global tags are passed through as-is) */
343   GstTagList *upstream_tags;
344   GstTagList *parser_tags;
345   GstTagMergeMode parser_tags_merge_mode;
346   gboolean tags_changed;
347 
348   /* Current segment seqnum */
349   guint32 segment_seqnum;
350 };
351 
352 typedef struct _GstBaseParseSeek
353 {
354   GstSegment segment;
355   gboolean accurate;
356   gint64 offset;
357   GstClockTime start_ts;
358 } GstBaseParseSeek;
359 
360 #define DEFAULT_DISABLE_PASSTHROUGH        FALSE
361 
362 enum
363 {
364   PROP_0,
365   PROP_DISABLE_PASSTHROUGH,
366   PROP_LAST
367 };
368 
369 #define GST_BASE_PARSE_INDEX_LOCK(parse) \
370   g_mutex_lock (&parse->priv->index_lock);
371 #define GST_BASE_PARSE_INDEX_UNLOCK(parse) \
372   g_mutex_unlock (&parse->priv->index_lock);
373 
374 static GstElementClass *parent_class = NULL;
375 static gint base_parse_private_offset = 0;
376 
377 static void gst_base_parse_class_init (GstBaseParseClass * klass);
378 static void gst_base_parse_init (GstBaseParse * parse,
379     GstBaseParseClass * klass);
380 
381 GType
gst_base_parse_get_type(void)382 gst_base_parse_get_type (void)
383 {
384   static gsize base_parse_type = 0;
385 
386   if (g_once_init_enter (&base_parse_type)) {
387     static const GTypeInfo base_parse_info = {
388       sizeof (GstBaseParseClass),
389       (GBaseInitFunc) NULL,
390       (GBaseFinalizeFunc) NULL,
391       (GClassInitFunc) gst_base_parse_class_init,
392       NULL,
393       NULL,
394       sizeof (GstBaseParse),
395       0,
396       (GInstanceInitFunc) gst_base_parse_init,
397     };
398     GType _type;
399 
400     _type = g_type_register_static (GST_TYPE_ELEMENT,
401         "GstBaseParse", &base_parse_info, G_TYPE_FLAG_ABSTRACT);
402 
403     base_parse_private_offset =
404         g_type_add_instance_private (_type, sizeof (GstBaseParsePrivate));
405 
406     g_once_init_leave (&base_parse_type, _type);
407   }
408   return (GType) base_parse_type;
409 }
410 
411 static inline GstBaseParsePrivate *
gst_base_parse_get_instance_private(GstBaseParse * self)412 gst_base_parse_get_instance_private (GstBaseParse * self)
413 {
414   return (G_STRUCT_MEMBER_P (self, base_parse_private_offset));
415 }
416 
417 static void gst_base_parse_finalize (GObject * object);
418 
419 static GstStateChangeReturn gst_base_parse_change_state (GstElement * element,
420     GstStateChange transition);
421 static void gst_base_parse_reset (GstBaseParse * parse);
422 
423 #if 0
424 static void gst_base_parse_set_index (GstElement * element, GstIndex * index);
425 static GstIndex *gst_base_parse_get_index (GstElement * element);
426 #endif
427 
428 static gboolean gst_base_parse_sink_activate (GstPad * sinkpad,
429     GstObject * parent);
430 static gboolean gst_base_parse_sink_activate_mode (GstPad * pad,
431     GstObject * parent, GstPadMode mode, gboolean active);
432 static gboolean gst_base_parse_handle_seek (GstBaseParse * parse,
433     GstEvent * event);
434 static void gst_base_parse_set_upstream_tags (GstBaseParse * parse,
435     GstTagList * taglist);
436 
437 static void gst_base_parse_set_property (GObject * object, guint prop_id,
438     const GValue * value, GParamSpec * pspec);
439 static void gst_base_parse_get_property (GObject * object, guint prop_id,
440     GValue * value, GParamSpec * pspec);
441 
442 static gboolean gst_base_parse_src_event (GstPad * pad, GstObject * parent,
443     GstEvent * event);
444 static gboolean gst_base_parse_src_query (GstPad * pad, GstObject * parent,
445     GstQuery * query);
446 
447 static gboolean gst_base_parse_sink_event (GstPad * pad, GstObject * parent,
448     GstEvent * event);
449 static gboolean gst_base_parse_sink_query (GstPad * pad, GstObject * parent,
450     GstQuery * query);
451 
452 #ifdef OHOS_OPT_PERFORMANCE
453 // ohos.opt.performance.0005
454 // add trace
455 static GstFlowReturn gst_base_parse_chain_trace (GstPad * pad, GstObject * parent,
456     GstBuffer * buffer);
457 #endif
458 static GstFlowReturn gst_base_parse_chain (GstPad * pad, GstObject * parent,
459     GstBuffer * buffer);
460 static void gst_base_parse_loop (GstPad * pad);
461 
462 static GstFlowReturn gst_base_parse_parse_frame (GstBaseParse * parse,
463     GstBaseParseFrame * frame);
464 
465 static gboolean gst_base_parse_sink_event_default (GstBaseParse * parse,
466     GstEvent * event);
467 
468 static gboolean gst_base_parse_src_event_default (GstBaseParse * parse,
469     GstEvent * event);
470 
471 static gboolean gst_base_parse_sink_query_default (GstBaseParse * parse,
472     GstQuery * query);
473 static gboolean gst_base_parse_src_query_default (GstBaseParse * parse,
474     GstQuery * query);
475 
476 static gint64 gst_base_parse_find_offset (GstBaseParse * parse,
477     GstClockTime time, gboolean before, GstClockTime * _ts);
478 static GstFlowReturn gst_base_parse_locate_time (GstBaseParse * parse,
479     GstClockTime * _time, gint64 * _offset);
480 
481 static GstFlowReturn gst_base_parse_start_fragment (GstBaseParse * parse);
482 static GstFlowReturn gst_base_parse_finish_fragment (GstBaseParse * parse,
483     gboolean prev_head);
484 static GstFlowReturn gst_base_parse_send_buffers (GstBaseParse * parse);
485 
486 static inline GstFlowReturn gst_base_parse_check_sync (GstBaseParse * parse);
487 
488 static gboolean gst_base_parse_is_seekable (GstBaseParse * parse);
489 
490 static void gst_base_parse_push_pending_events (GstBaseParse * parse);
491 
492 static void
gst_base_parse_clear_queues(GstBaseParse * parse)493 gst_base_parse_clear_queues (GstBaseParse * parse)
494 {
495   g_slist_foreach (parse->priv->buffers_queued, (GFunc) gst_buffer_unref, NULL);
496   g_slist_free (parse->priv->buffers_queued);
497   parse->priv->buffers_queued = NULL;
498   g_slist_foreach (parse->priv->buffers_pending, (GFunc) gst_buffer_unref,
499       NULL);
500   g_slist_free (parse->priv->buffers_pending);
501   parse->priv->buffers_pending = NULL;
502   g_slist_foreach (parse->priv->buffers_head, (GFunc) gst_buffer_unref, NULL);
503   g_slist_free (parse->priv->buffers_head);
504   parse->priv->buffers_head = NULL;
505   g_slist_foreach (parse->priv->buffers_send, (GFunc) gst_buffer_unref, NULL);
506   g_slist_free (parse->priv->buffers_send);
507   parse->priv->buffers_send = NULL;
508 
509   g_list_foreach (parse->priv->detect_buffers, (GFunc) gst_buffer_unref, NULL);
510   g_list_free (parse->priv->detect_buffers);
511   parse->priv->detect_buffers = NULL;
512   parse->priv->detect_buffers_size = 0;
513 
514   g_queue_foreach (&parse->priv->queued_frames,
515       (GFunc) gst_base_parse_frame_free, NULL);
516   g_queue_clear (&parse->priv->queued_frames);
517 
518   gst_buffer_replace (&parse->priv->cache, NULL);
519 
520   g_list_foreach (parse->priv->pending_events, (GFunc) gst_event_unref, NULL);
521   g_list_free (parse->priv->pending_events);
522   parse->priv->pending_events = NULL;
523 
524   parse->priv->checked_media = FALSE;
525 }
526 
527 static void
gst_base_parse_finalize(GObject * object)528 gst_base_parse_finalize (GObject * object)
529 {
530   GstBaseParse *parse = GST_BASE_PARSE (object);
531 
532   g_object_unref (parse->priv->adapter);
533 
534   if (parse->priv->index) {
535     gst_object_unref (parse->priv->index);
536     parse->priv->index = NULL;
537   }
538   g_mutex_clear (&parse->priv->index_lock);
539 
540   gst_base_parse_clear_queues (parse);
541 
542   G_OBJECT_CLASS (parent_class)->finalize (object);
543 }
544 
545 static void
gst_base_parse_class_init(GstBaseParseClass * klass)546 gst_base_parse_class_init (GstBaseParseClass * klass)
547 {
548   GObjectClass *gobject_class;
549   GstElementClass *gstelement_class;
550 
551   gobject_class = G_OBJECT_CLASS (klass);
552 
553   if (base_parse_private_offset != 0)
554     g_type_class_adjust_private_offset (klass, &base_parse_private_offset);
555 
556   parent_class = g_type_class_peek_parent (klass);
557 
558   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_base_parse_finalize);
559   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_base_parse_set_property);
560   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_base_parse_get_property);
561 
562   /**
563    * GstBaseParse:disable-passthrough:
564    *
565    * If set to %TRUE, baseparse will unconditionally force parsing of the
566    * incoming data. This can be required in the rare cases where the incoming
567    * side-data (caps, pts, dts, ...) is not trusted by the user and wants to
568    * force validation and parsing of the incoming data.
569    * If set to %FALSE, decision of whether to parse the data or not is up to
570    * the implementation (standard behaviour).
571    */
572   g_object_class_install_property (gobject_class, PROP_DISABLE_PASSTHROUGH,
573       g_param_spec_boolean ("disable-passthrough", "Disable passthrough",
574           "Force processing (disables passthrough)",
575           DEFAULT_DISABLE_PASSTHROUGH,
576           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
577 
578   gstelement_class = (GstElementClass *) klass;
579   gstelement_class->change_state =
580       GST_DEBUG_FUNCPTR (gst_base_parse_change_state);
581 
582 #if 0
583   gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_base_parse_set_index);
584   gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_base_parse_get_index);
585 #endif
586 
587   /* Default handlers */
588   klass->sink_event = gst_base_parse_sink_event_default;
589   klass->src_event = gst_base_parse_src_event_default;
590   klass->sink_query = gst_base_parse_sink_query_default;
591   klass->src_query = gst_base_parse_src_query_default;
592   klass->convert = gst_base_parse_convert_default;
593 
594   GST_DEBUG_CATEGORY_INIT (gst_base_parse_debug, "baseparse", 0,
595       "baseparse element");
596 }
597 
598 static void
gst_base_parse_init(GstBaseParse * parse,GstBaseParseClass * bclass)599 gst_base_parse_init (GstBaseParse * parse, GstBaseParseClass * bclass)
600 {
601   GstPadTemplate *pad_template;
602 
603   GST_DEBUG_OBJECT (parse, "gst_base_parse_init");
604 
605   parse->priv = gst_base_parse_get_instance_private (parse);
606 
607   pad_template =
608       gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "sink");
609   g_return_if_fail (pad_template != NULL);
610   parse->sinkpad = gst_pad_new_from_template (pad_template, "sink");
611   gst_pad_set_event_function (parse->sinkpad,
612       GST_DEBUG_FUNCPTR (gst_base_parse_sink_event));
613   gst_pad_set_query_function (parse->sinkpad,
614       GST_DEBUG_FUNCPTR (gst_base_parse_sink_query));
615 #ifdef OHOS_OPT_PERFORMANCE
616 // ohos.opt.performance.0005
617 // add trace
618   gst_pad_set_chain_function (parse->sinkpad,
619       GST_DEBUG_FUNCPTR (gst_base_parse_chain_trace));
620 #else
621   gst_pad_set_chain_function (parse->sinkpad,
622       GST_DEBUG_FUNCPTR (gst_base_parse_chain));
623 #endif
624   gst_pad_set_activate_function (parse->sinkpad,
625       GST_DEBUG_FUNCPTR (gst_base_parse_sink_activate));
626   gst_pad_set_activatemode_function (parse->sinkpad,
627       GST_DEBUG_FUNCPTR (gst_base_parse_sink_activate_mode));
628   GST_PAD_SET_PROXY_ALLOCATION (parse->sinkpad);
629   gst_element_add_pad (GST_ELEMENT (parse), parse->sinkpad);
630 
631   GST_DEBUG_OBJECT (parse, "sinkpad created");
632 
633   pad_template =
634       gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "src");
635   g_return_if_fail (pad_template != NULL);
636   parse->srcpad = gst_pad_new_from_template (pad_template, "src");
637   gst_pad_set_event_function (parse->srcpad,
638       GST_DEBUG_FUNCPTR (gst_base_parse_src_event));
639   gst_pad_set_query_function (parse->srcpad,
640       GST_DEBUG_FUNCPTR (gst_base_parse_src_query));
641   gst_pad_use_fixed_caps (parse->srcpad);
642   gst_element_add_pad (GST_ELEMENT (parse), parse->srcpad);
643   GST_DEBUG_OBJECT (parse, "src created");
644 
645   g_queue_init (&parse->priv->queued_frames);
646 
647   parse->priv->adapter = gst_adapter_new ();
648 
649   parse->priv->pad_mode = GST_PAD_MODE_NONE;
650 
651   g_mutex_init (&parse->priv->index_lock);
652 
653   /* init state */
654   gst_base_parse_reset (parse);
655   GST_DEBUG_OBJECT (parse, "init ok");
656 
657   GST_OBJECT_FLAG_SET (parse, GST_ELEMENT_FLAG_INDEXABLE);
658 
659   parse->priv->upstream_tags = NULL;
660   parse->priv->parser_tags = NULL;
661   parse->priv->parser_tags_merge_mode = GST_TAG_MERGE_APPEND;
662   parse->priv->disable_passthrough = DEFAULT_DISABLE_PASSTHROUGH;
663 }
664 
665 static void
gst_base_parse_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)666 gst_base_parse_set_property (GObject * object, guint prop_id,
667     const GValue * value, GParamSpec * pspec)
668 {
669   GstBaseParse *parse = GST_BASE_PARSE (object);
670 
671   switch (prop_id) {
672     case PROP_DISABLE_PASSTHROUGH:
673       parse->priv->disable_passthrough = g_value_get_boolean (value);
674       break;
675     default:
676       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
677       break;
678   }
679 }
680 
681 static void
gst_base_parse_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)682 gst_base_parse_get_property (GObject * object, guint prop_id, GValue * value,
683     GParamSpec * pspec)
684 {
685   GstBaseParse *parse = GST_BASE_PARSE (object);
686 
687   switch (prop_id) {
688     case PROP_DISABLE_PASSTHROUGH:
689       g_value_set_boolean (value, parse->priv->disable_passthrough);
690       break;
691     default:
692       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
693       break;
694   }
695 }
696 
697 /**
698  * gst_base_parse_frame_copy:
699  * @frame: a #GstBaseParseFrame
700  *
701  * Copies a #GstBaseParseFrame.
702  *
703  * Returns: A copy of @frame
704  *
705  * Since: 1.12.1
706  */
707 GstBaseParseFrame *
gst_base_parse_frame_copy(GstBaseParseFrame * frame)708 gst_base_parse_frame_copy (GstBaseParseFrame * frame)
709 {
710   GstBaseParseFrame *copy;
711 
712   copy = g_slice_dup (GstBaseParseFrame, frame);
713   copy->buffer = gst_buffer_ref (frame->buffer);
714   copy->_private_flags &= ~GST_BASE_PARSE_FRAME_PRIVATE_FLAG_NOALLOC;
715 
716   GST_TRACE ("copied frame %p -> %p", frame, copy);
717 
718   return copy;
719 }
720 
721 /**
722  * gst_base_parse_frame_free:
723  * @frame: A #GstBaseParseFrame
724  *
725  * Frees the provided @frame.
726  */
727 void
gst_base_parse_frame_free(GstBaseParseFrame * frame)728 gst_base_parse_frame_free (GstBaseParseFrame * frame)
729 {
730   GST_TRACE ("freeing frame %p", frame);
731 
732   if (frame->buffer) {
733     gst_buffer_unref (frame->buffer);
734     frame->buffer = NULL;
735   }
736 
737   if (!(frame->_private_flags & GST_BASE_PARSE_FRAME_PRIVATE_FLAG_NOALLOC)) {
738     g_slice_free (GstBaseParseFrame, frame);
739   } else {
740     memset (frame, 0, sizeof (*frame));
741   }
742 }
743 
744 G_DEFINE_BOXED_TYPE (GstBaseParseFrame, gst_base_parse_frame,
745     (GBoxedCopyFunc) gst_base_parse_frame_copy,
746     (GBoxedFreeFunc) gst_base_parse_frame_free);
747 
748 /**
749  * gst_base_parse_frame_init:
750  * @frame: #GstBaseParseFrame.
751  *
752  * Sets a #GstBaseParseFrame to initial state.  Currently this means
753  * all public fields are zero-ed and a private flag is set to make
754  * sure gst_base_parse_frame_free() only frees the contents but not
755  * the actual frame. Use this function to initialise a #GstBaseParseFrame
756  * allocated on the stack.
757  */
758 void
gst_base_parse_frame_init(GstBaseParseFrame * frame)759 gst_base_parse_frame_init (GstBaseParseFrame * frame)
760 {
761   memset (frame, 0, sizeof (GstBaseParseFrame));
762   frame->_private_flags = GST_BASE_PARSE_FRAME_PRIVATE_FLAG_NOALLOC;
763   GST_TRACE ("inited frame %p", frame);
764 }
765 
766 /**
767  * gst_base_parse_frame_new:
768  * @buffer: (transfer none): a #GstBuffer
769  * @flags: the flags
770  * @overhead: number of bytes in this frame which should be counted as
771  *     metadata overhead, ie. not used to calculate the average bitrate.
772  *     Set to -1 to mark the entire frame as metadata. If in doubt, set to 0.
773  *
774  * Allocates a new #GstBaseParseFrame. This function is mainly for bindings,
775  * elements written in C should usually allocate the frame on the stack and
776  * then use gst_base_parse_frame_init() to initialise it.
777  *
778  * Returns: a newly-allocated #GstBaseParseFrame. Free with
779  *     gst_base_parse_frame_free() when no longer needed.
780  */
781 GstBaseParseFrame *
gst_base_parse_frame_new(GstBuffer * buffer,GstBaseParseFrameFlags flags,gint overhead)782 gst_base_parse_frame_new (GstBuffer * buffer, GstBaseParseFrameFlags flags,
783     gint overhead)
784 {
785   GstBaseParseFrame *frame;
786 
787   frame = g_slice_new0 (GstBaseParseFrame);
788   frame->buffer = gst_buffer_ref (buffer);
789 
790   GST_TRACE ("created frame %p", frame);
791   return frame;
792 }
793 
794 static inline void
gst_base_parse_update_flags(GstBaseParse * parse)795 gst_base_parse_update_flags (GstBaseParse * parse)
796 {
797   parse->flags = 0;
798 
799   /* set flags one by one for clarity */
800   if (G_UNLIKELY (parse->priv->drain))
801     parse->flags |= GST_BASE_PARSE_FLAG_DRAINING;
802 
803   /* losing sync is pretty much a discont (and vice versa), no ? */
804   if (G_UNLIKELY (parse->priv->discont))
805     parse->flags |= GST_BASE_PARSE_FLAG_LOST_SYNC;
806 }
807 
808 static inline void
gst_base_parse_update_frame(GstBaseParse * parse,GstBaseParseFrame * frame)809 gst_base_parse_update_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
810 {
811   if (G_UNLIKELY (parse->priv->discont)) {
812     GST_DEBUG_OBJECT (parse, "marking DISCONT");
813     GST_BUFFER_FLAG_SET (frame->buffer, GST_BUFFER_FLAG_DISCONT);
814   } else {
815     GST_BUFFER_FLAG_UNSET (frame->buffer, GST_BUFFER_FLAG_DISCONT);
816   }
817 
818   if (parse->priv->prev_offset != parse->priv->offset || parse->priv->new_frame) {
819     GST_LOG_OBJECT (parse, "marking as new frame");
820     frame->flags |= GST_BASE_PARSE_FRAME_FLAG_NEW_FRAME;
821   }
822 
823   frame->offset = parse->priv->prev_offset = parse->priv->offset;
824 }
825 
826 static void
gst_base_parse_reset(GstBaseParse * parse)827 gst_base_parse_reset (GstBaseParse * parse)
828 {
829   GST_OBJECT_LOCK (parse);
830   gst_segment_init (&parse->segment, GST_FORMAT_TIME);
831   parse->priv->duration = -1;
832   parse->priv->min_frame_size = 1;
833   parse->priv->discont = TRUE;
834   parse->priv->flushing = FALSE;
835   parse->priv->saw_gaps = FALSE;
836   parse->priv->offset = 0;
837   parse->priv->sync_offset = 0;
838   parse->priv->update_interval = -1;
839   parse->priv->fps_num = parse->priv->fps_den = 0;
840   parse->priv->frame_duration = GST_CLOCK_TIME_NONE;
841   parse->priv->lead_in = parse->priv->lead_out = 0;
842   parse->priv->lead_in_ts = parse->priv->lead_out_ts = 0;
843   parse->priv->bitrate = 0;
844   parse->priv->framecount = 0;
845   parse->priv->bytecount = 0;
846   parse->priv->data_bytecount = 0;
847   parse->priv->acc_duration = 0;
848   parse->priv->first_frame_pts = GST_CLOCK_TIME_NONE;
849   parse->priv->first_frame_dts = GST_CLOCK_TIME_NONE;
850   parse->priv->first_frame_offset = -1;
851   parse->priv->estimated_duration = -1;
852   parse->priv->estimated_drift = 0;
853   parse->priv->next_pts = GST_CLOCK_TIME_NONE;
854   parse->priv->next_dts = 0;
855   parse->priv->syncable = TRUE;
856   parse->priv->passthrough = FALSE;
857   parse->priv->pts_interpolate = TRUE;
858   parse->priv->infer_ts = TRUE;
859   parse->priv->has_timing_info = FALSE;
860   parse->priv->min_bitrate = G_MAXUINT;
861   parse->priv->max_bitrate = 0;
862   parse->priv->avg_bitrate = 0;
863   parse->priv->posted_avg_bitrate = 0;
864 
865   parse->priv->index_last_ts = GST_CLOCK_TIME_NONE;
866   parse->priv->index_last_offset = -1;
867   parse->priv->index_last_valid = TRUE;
868   parse->priv->upstream_seekable = FALSE;
869   parse->priv->upstream_size = 0;
870   parse->priv->upstream_has_duration = FALSE;
871   parse->priv->upstream_format = GST_FORMAT_UNDEFINED;
872   parse->priv->idx_interval = 0;
873   parse->priv->idx_byte_interval = 0;
874   parse->priv->exact_position = TRUE;
875   parse->priv->seen_keyframe = FALSE;
876   parse->priv->checked_media = FALSE;
877 
878   parse->priv->last_dts = GST_CLOCK_TIME_NONE;
879   parse->priv->last_pts = GST_CLOCK_TIME_NONE;
880   parse->priv->last_offset = 0;
881 
882   parse->priv->skip = 0;
883 
884   g_list_foreach (parse->priv->pending_events, (GFunc) gst_mini_object_unref,
885       NULL);
886   g_list_free (parse->priv->pending_events);
887   parse->priv->pending_events = NULL;
888 
889   if (parse->priv->cache) {
890     gst_buffer_unref (parse->priv->cache);
891     parse->priv->cache = NULL;
892   }
893 
894   g_slist_foreach (parse->priv->pending_seeks, (GFunc) g_free, NULL);
895   g_slist_free (parse->priv->pending_seeks);
896   parse->priv->pending_seeks = NULL;
897 
898   if (parse->priv->adapter)
899     gst_adapter_clear (parse->priv->adapter);
900 
901   gst_base_parse_set_upstream_tags (parse, NULL);
902 
903   if (parse->priv->parser_tags) {
904     gst_tag_list_unref (parse->priv->parser_tags);
905     parse->priv->parser_tags = NULL;
906   }
907   parse->priv->parser_tags_merge_mode = GST_TAG_MERGE_APPEND;
908 
909   parse->priv->new_frame = TRUE;
910 
911   parse->priv->first_buffer = TRUE;
912 
913   g_list_foreach (parse->priv->detect_buffers, (GFunc) gst_buffer_unref, NULL);
914   g_list_free (parse->priv->detect_buffers);
915   parse->priv->detect_buffers = NULL;
916   parse->priv->detect_buffers_size = 0;
917 
918   parse->priv->segment_seqnum = GST_SEQNUM_INVALID;
919   GST_OBJECT_UNLOCK (parse);
920 }
921 
922 static gboolean
gst_base_parse_check_bitrate_tag(GstBaseParse * parse,const gchar * tag)923 gst_base_parse_check_bitrate_tag (GstBaseParse * parse, const gchar * tag)
924 {
925   gboolean got_tag = FALSE;
926   guint n = 0;
927 
928   if (parse->priv->upstream_tags != NULL)
929     got_tag = gst_tag_list_get_uint (parse->priv->upstream_tags, tag, &n);
930 
931   if (!got_tag && parse->priv->parser_tags != NULL)
932     got_tag = gst_tag_list_get_uint (parse->priv->parser_tags, tag, &n);
933 
934   return got_tag;
935 }
936 
937 /* check if upstream or subclass tags contain bitrates already */
938 static void
gst_base_parse_check_bitrate_tags(GstBaseParse * parse)939 gst_base_parse_check_bitrate_tags (GstBaseParse * parse)
940 {
941   parse->priv->post_min_bitrate =
942       !gst_base_parse_check_bitrate_tag (parse, GST_TAG_MINIMUM_BITRATE);
943   parse->priv->post_avg_bitrate =
944       !gst_base_parse_check_bitrate_tag (parse, GST_TAG_BITRATE);
945   parse->priv->post_max_bitrate =
946       !gst_base_parse_check_bitrate_tag (parse, GST_TAG_MAXIMUM_BITRATE);
947 }
948 
949 /* Queues new tag event with the current combined state of the stream tags
950  * (i.e. upstream tags merged with subclass tags and current baseparse tags) */
951 static void
gst_base_parse_queue_tag_event_update(GstBaseParse * parse)952 gst_base_parse_queue_tag_event_update (GstBaseParse * parse)
953 {
954   GstTagList *merged_tags;
955 
956   GST_LOG_OBJECT (parse, "upstream : %" GST_PTR_FORMAT,
957       parse->priv->upstream_tags);
958   GST_LOG_OBJECT (parse, "parser   : %" GST_PTR_FORMAT,
959       parse->priv->parser_tags);
960   GST_LOG_OBJECT (parse, "mode     : %d", parse->priv->parser_tags_merge_mode);
961 
962   merged_tags =
963       gst_tag_list_merge (parse->priv->upstream_tags, parse->priv->parser_tags,
964       parse->priv->parser_tags_merge_mode);
965 
966   GST_DEBUG_OBJECT (parse, "merged   : %" GST_PTR_FORMAT, merged_tags);
967 
968   if (merged_tags == NULL)
969     return;
970 
971   if (gst_tag_list_is_empty (merged_tags)) {
972     gst_tag_list_unref (merged_tags);
973     return;
974   }
975 
976   if (parse->priv->framecount >= MIN_FRAMES_TO_POST_BITRATE) {
977     /* only add bitrate tags to non-empty taglists for now, and only if neither
978      * upstream tags nor the subclass sets the bitrate tag in question already */
979     if (parse->priv->min_bitrate != G_MAXUINT && parse->priv->post_min_bitrate) {
980       GST_LOG_OBJECT (parse, "adding min bitrate %u", parse->priv->min_bitrate);
981       gst_tag_list_add (merged_tags, GST_TAG_MERGE_KEEP,
982           GST_TAG_MINIMUM_BITRATE, parse->priv->min_bitrate, NULL);
983     }
984     if (parse->priv->max_bitrate != 0 && parse->priv->post_max_bitrate) {
985       GST_LOG_OBJECT (parse, "adding max bitrate %u", parse->priv->max_bitrate);
986       gst_tag_list_add (merged_tags, GST_TAG_MERGE_KEEP,
987           GST_TAG_MAXIMUM_BITRATE, parse->priv->max_bitrate, NULL);
988     }
989     if (parse->priv->avg_bitrate != 0 && parse->priv->post_avg_bitrate) {
990       parse->priv->posted_avg_bitrate = parse->priv->avg_bitrate;
991       GST_LOG_OBJECT (parse, "adding avg bitrate %u", parse->priv->avg_bitrate);
992       gst_tag_list_add (merged_tags, GST_TAG_MERGE_KEEP,
993           GST_TAG_BITRATE, parse->priv->avg_bitrate, NULL);
994     }
995   }
996 
997   parse->priv->pending_events =
998       g_list_prepend (parse->priv->pending_events,
999       gst_event_new_tag (merged_tags));
1000 }
1001 
1002 /* gst_base_parse_parse_frame:
1003  * @parse: #GstBaseParse.
1004  * @buffer: #GstBuffer.
1005  *
1006  * Default callback for parse_frame.
1007  */
1008 static GstFlowReturn
gst_base_parse_parse_frame(GstBaseParse * parse,GstBaseParseFrame * frame)1009 gst_base_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
1010 {
1011   GstBuffer *buffer = frame->buffer;
1012   gboolean must_approximate_pts = !GST_BUFFER_PTS_IS_VALID (buffer)
1013       && GST_CLOCK_TIME_IS_VALID (parse->priv->next_pts);
1014   gboolean must_approximate_dts = !GST_BUFFER_DTS_IS_VALID (buffer)
1015       && GST_CLOCK_TIME_IS_VALID (parse->priv->next_dts);
1016 
1017   if (must_approximate_pts) {
1018     GST_BUFFER_PTS (buffer) = parse->priv->next_pts;
1019     if (!must_approximate_dts
1020         && GST_BUFFER_DTS (buffer) > parse->priv->next_pts
1021         && GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DTS (buffer))) {
1022       /* Can't present a frame before it's decoded: change the pts! This can
1023        * happen, for example, when accumulating rounding errors from the
1024        * buffer durations. Assume DTS is correct because only PTS is
1025        * approximated here */
1026       GST_LOG_OBJECT (parse,
1027           "Found DTS (%" GST_TIME_FORMAT ") > PTS (%" GST_TIME_FORMAT
1028           "), set PTS = DTS", GST_TIME_ARGS (GST_BUFFER_DTS (buffer)),
1029           GST_TIME_ARGS (GST_BUFFER_PTS (buffer)));
1030       GST_BUFFER_PTS (buffer) = GST_BUFFER_DTS (buffer);
1031     }
1032   }
1033 
1034   if (must_approximate_dts) {
1035     if (!must_approximate_pts
1036         && GST_BUFFER_PTS (buffer) < parse->priv->next_dts) {
1037       /* Can't present a frame before it's decoded: change the dts! This can
1038        * happen, for example, when accumulating rounding errors from the
1039        * buffer durations. Assume PTS is correct because only DTS is
1040        * approximated here */
1041       GST_LOG_OBJECT (parse,
1042           "Found DTS (%" GST_TIME_FORMAT ") > PTS (%" GST_TIME_FORMAT
1043           "), set DTS = PTS", GST_TIME_ARGS (GST_BUFFER_DTS (buffer)),
1044           GST_TIME_ARGS (GST_BUFFER_PTS (buffer)));
1045       GST_BUFFER_DTS (buffer) = GST_BUFFER_PTS (buffer);
1046     } else {
1047       GST_BUFFER_DTS (buffer) = parse->priv->next_dts;
1048     }
1049   }
1050 
1051   if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_PTS (buffer))
1052       && GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DTS (buffer))
1053       && GST_BUFFER_PTS (buffer) < GST_BUFFER_DTS (buffer)) {
1054     /* Can't present a frame before it's decoded: change the pts! This can
1055      * happen, for example, when accumulating rounding errors from the buffer
1056      * durations. PTS and DTS are either both approximated or both from the
1057      * original buffer timestamp. Set PTS = DTS because the opposite has been
1058      * observed to cause DTS going backwards */
1059     GST_LOG_OBJECT (parse,
1060         "Found DTS (%" GST_TIME_FORMAT ") > PTS (%" GST_TIME_FORMAT
1061         "), set PTS = DTS", GST_TIME_ARGS (GST_BUFFER_DTS (buffer)),
1062         GST_TIME_ARGS (GST_BUFFER_PTS (buffer)));
1063     GST_BUFFER_PTS (buffer) = GST_BUFFER_DTS (buffer);
1064   }
1065 
1066   if (!GST_BUFFER_DURATION_IS_VALID (buffer) &&
1067       GST_CLOCK_TIME_IS_VALID (parse->priv->frame_duration)) {
1068     GST_BUFFER_DURATION (buffer) = parse->priv->frame_duration;
1069   }
1070   return GST_FLOW_OK;
1071 }
1072 
1073 /* gst_base_parse_convert:
1074  * @parse: #GstBaseParse.
1075  * @src_format: #GstFormat describing the source format.
1076  * @src_value: Source value to be converted.
1077  * @dest_format: #GstFormat defining the converted format.
1078  * @dest_value: (out): Pointer where the conversion result will be put.
1079  *
1080  * Converts using configured "convert" vmethod in #GstBaseParse class.
1081  *
1082  * Returns: %TRUE if conversion was successful.
1083  */
1084 static gboolean
gst_base_parse_convert(GstBaseParse * parse,GstFormat src_format,gint64 src_value,GstFormat dest_format,gint64 * dest_value)1085 gst_base_parse_convert (GstBaseParse * parse,
1086     GstFormat src_format,
1087     gint64 src_value, GstFormat dest_format, gint64 * dest_value)
1088 {
1089   GstBaseParseClass *klass = GST_BASE_PARSE_GET_CLASS (parse);
1090   gboolean ret;
1091 
1092   g_return_val_if_fail (dest_value != NULL, FALSE);
1093 
1094   if (!klass->convert)
1095     return FALSE;
1096 
1097   ret = klass->convert (parse, src_format, src_value, dest_format, dest_value);
1098 
1099 #ifndef GST_DISABLE_GST_DEBUG
1100   {
1101     if (ret) {
1102       if (src_format == GST_FORMAT_TIME && dest_format == GST_FORMAT_BYTES) {
1103         GST_LOG_OBJECT (parse,
1104             "TIME -> BYTES: %" GST_TIME_FORMAT " -> %" G_GINT64_FORMAT,
1105             GST_TIME_ARGS (src_value), *dest_value);
1106       } else if (dest_format == GST_FORMAT_TIME &&
1107           src_format == GST_FORMAT_BYTES) {
1108         GST_LOG_OBJECT (parse,
1109             "BYTES -> TIME: %" G_GINT64_FORMAT " -> %" GST_TIME_FORMAT,
1110             src_value, GST_TIME_ARGS (*dest_value));
1111       } else {
1112         GST_LOG_OBJECT (parse,
1113             "%s -> %s: %" G_GINT64_FORMAT " -> %" G_GINT64_FORMAT,
1114             GST_STR_NULL (gst_format_get_name (src_format)),
1115             GST_STR_NULL (gst_format_get_name (dest_format)),
1116             src_value, *dest_value);
1117       }
1118     } else {
1119       GST_DEBUG_OBJECT (parse, "conversion failed");
1120     }
1121   }
1122 #endif
1123 
1124   return ret;
1125 }
1126 
1127 static gboolean
update_upstream_provided(GQuark field_id,const GValue * value,gpointer user_data)1128 update_upstream_provided (GQuark field_id, const GValue * value,
1129     gpointer user_data)
1130 {
1131   GstCaps *default_caps = user_data;
1132   gint i;
1133   gint caps_size;
1134 
1135   caps_size = gst_caps_get_size (default_caps);
1136   for (i = 0; i < caps_size; i++) {
1137     GstStructure *structure = gst_caps_get_structure (default_caps, i);
1138     if (!gst_structure_id_has_field (structure, field_id)) {
1139       gst_structure_id_set_value (structure, field_id, value);
1140     }
1141     /* XXX: maybe try to fixate better than gst_caps_fixate() the
1142      * downstream caps based on upstream values if possible */
1143   }
1144 
1145   return TRUE;
1146 }
1147 
1148 static GstCaps *
gst_base_parse_negotiate_default_caps(GstBaseParse * parse)1149 gst_base_parse_negotiate_default_caps (GstBaseParse * parse)
1150 {
1151   GstCaps *caps, *templcaps;
1152   GstCaps *sinkcaps = NULL;
1153   GstCaps *default_caps = NULL;
1154   GstStructure *structure;
1155 
1156   templcaps = gst_pad_get_pad_template_caps (GST_BASE_PARSE_SRC_PAD (parse));
1157   caps = gst_pad_peer_query_caps (GST_BASE_PARSE_SRC_PAD (parse), templcaps);
1158   if (caps)
1159     gst_caps_unref (templcaps);
1160   else
1161     caps = templcaps;
1162   templcaps = NULL;
1163 
1164   if (!caps || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) {
1165     goto caps_error;
1166   }
1167 
1168   GST_LOG_OBJECT (parse, "peer caps  %" GST_PTR_FORMAT, caps);
1169 
1170   /* before fixating, try to use whatever upstream provided */
1171   default_caps = gst_caps_copy (caps);
1172   sinkcaps = gst_pad_get_current_caps (GST_BASE_PARSE_SINK_PAD (parse));
1173 
1174   GST_LOG_OBJECT (parse, "current caps %" GST_PTR_FORMAT " for sinkpad",
1175       sinkcaps);
1176 
1177   if (sinkcaps) {
1178     structure = gst_caps_get_structure (sinkcaps, 0);
1179     gst_structure_foreach (structure, update_upstream_provided, default_caps);
1180   }
1181 
1182   default_caps = gst_caps_fixate (default_caps);
1183 
1184   if (!default_caps) {
1185     GST_WARNING_OBJECT (parse, "Failed to create default caps !");
1186     goto caps_error;
1187   }
1188 
1189   GST_INFO_OBJECT (parse,
1190       "Chose default caps %" GST_PTR_FORMAT " for initial gap", default_caps);
1191 
1192   if (sinkcaps)
1193     gst_caps_unref (sinkcaps);
1194   gst_caps_unref (caps);
1195 
1196   return default_caps;
1197 
1198 caps_error:
1199   {
1200     if (caps)
1201       gst_caps_unref (caps);
1202     if (sinkcaps)
1203       gst_caps_unref (sinkcaps);
1204     return NULL;
1205   }
1206 }
1207 
1208 /* gst_base_parse_sink_event:
1209  * @pad: #GstPad that received the event.
1210  * @event: #GstEvent to be handled.
1211  *
1212  * Handler for sink pad events.
1213  *
1214  * Returns: %TRUE if the event was handled.
1215  */
1216 static gboolean
gst_base_parse_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)1217 gst_base_parse_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
1218 {
1219   GstBaseParse *parse = GST_BASE_PARSE (parent);
1220   GstBaseParseClass *bclass = GST_BASE_PARSE_GET_CLASS (parse);
1221   gboolean ret;
1222 
1223   ret = bclass->sink_event (parse, event);
1224 
1225   return ret;
1226 }
1227 
1228 /* gst_base_parse_sink_event_default:
1229  * @parse: #GstBaseParse.
1230  * @event: #GstEvent to be handled.
1231  *
1232  * Element-level event handler function.
1233  *
1234  * The event will be unreffed only if it has been handled and this
1235  * function returns %TRUE
1236  *
1237  * Returns: %TRUE if the event was handled and not need forwarding.
1238  */
1239 static gboolean
gst_base_parse_sink_event_default(GstBaseParse * parse,GstEvent * event)1240 gst_base_parse_sink_event_default (GstBaseParse * parse, GstEvent * event)
1241 {
1242   GstBaseParseClass *klass = GST_BASE_PARSE_GET_CLASS (parse);
1243   gboolean ret = FALSE;
1244   gboolean forward_immediate = FALSE;
1245 
1246   GST_DEBUG_OBJECT (parse, "handling event %d, %s", GST_EVENT_TYPE (event),
1247       GST_EVENT_TYPE_NAME (event));
1248 
1249   switch (GST_EVENT_TYPE (event)) {
1250     case GST_EVENT_CAPS:
1251     {
1252       GstCaps *caps;
1253 
1254       gst_event_parse_caps (event, &caps);
1255       GST_DEBUG_OBJECT (parse, "caps: %" GST_PTR_FORMAT, caps);
1256 
1257       if (klass->set_sink_caps)
1258         ret = klass->set_sink_caps (parse, caps);
1259       else
1260         ret = TRUE;
1261 
1262       /* will send our own caps downstream */
1263       gst_event_unref (event);
1264       event = NULL;
1265       break;
1266     }
1267     case GST_EVENT_SEGMENT:
1268     {
1269       const GstSegment *in_segment;
1270       GstSegment out_segment;
1271       gint64 offset = 0, next_dts;
1272 
1273       parse->priv->segment_seqnum = gst_event_get_seqnum (event);
1274       gst_event_parse_segment (event, &in_segment);
1275       gst_segment_init (&out_segment, GST_FORMAT_TIME);
1276       out_segment.rate = in_segment->rate;
1277       out_segment.applied_rate = in_segment->applied_rate;
1278 
1279       GST_DEBUG_OBJECT (parse, "New segment %" GST_SEGMENT_FORMAT, in_segment);
1280       GST_DEBUG_OBJECT (parse, "Current segment %" GST_SEGMENT_FORMAT,
1281           &parse->segment);
1282 
1283       parse->priv->upstream_format = in_segment->format;
1284       if (in_segment->format == GST_FORMAT_BYTES) {
1285         GstBaseParseSeek *seek = NULL;
1286         GSList *node;
1287 
1288         /* stop time is allowed to be open-ended, but not start & pos */
1289         offset = in_segment->time;
1290 
1291         GST_OBJECT_LOCK (parse);
1292         for (node = parse->priv->pending_seeks; node; node = node->next) {
1293           GstBaseParseSeek *tmp = node->data;
1294 
1295           if (tmp->offset == offset) {
1296             seek = tmp;
1297             break;
1298           }
1299         }
1300         parse->priv->pending_seeks =
1301             g_slist_remove (parse->priv->pending_seeks, seek);
1302         GST_OBJECT_UNLOCK (parse);
1303 
1304         if (seek) {
1305           GST_DEBUG_OBJECT (parse,
1306               "Matched newsegment to%s seek: %" GST_SEGMENT_FORMAT,
1307               seek->accurate ? " accurate" : "", &seek->segment);
1308 
1309           out_segment.start = seek->segment.start;
1310           out_segment.stop = seek->segment.stop;
1311           out_segment.time = seek->segment.start;
1312 
1313           next_dts = seek->start_ts;
1314           parse->priv->exact_position = seek->accurate;
1315           g_free (seek);
1316         } else {
1317           /* best attempt convert */
1318           /* as these are only estimates, stop is kept open-ended to avoid
1319            * premature cutting */
1320           gst_base_parse_convert (parse, GST_FORMAT_BYTES, in_segment->start,
1321               GST_FORMAT_TIME, (gint64 *) & next_dts);
1322 
1323           out_segment.start = next_dts;
1324           out_segment.stop = GST_CLOCK_TIME_NONE;
1325           out_segment.time = next_dts;
1326 
1327           parse->priv->exact_position = (in_segment->start == 0);
1328         }
1329 
1330         gst_event_unref (event);
1331 
1332         event = gst_event_new_segment (&out_segment);
1333         gst_event_set_seqnum (event, parse->priv->segment_seqnum);
1334 
1335         GST_DEBUG_OBJECT (parse, "Converted incoming segment to TIME. %"
1336             GST_SEGMENT_FORMAT, in_segment);
1337 
1338       } else if (in_segment->format != GST_FORMAT_TIME) {
1339         /* Unknown incoming segment format. Output a default open-ended
1340          * TIME segment */
1341         gst_event_unref (event);
1342 
1343         out_segment.start = 0;
1344         out_segment.stop = GST_CLOCK_TIME_NONE;
1345         out_segment.time = 0;
1346 
1347         event = gst_event_new_segment (&out_segment);
1348         gst_event_set_seqnum (event, parse->priv->segment_seqnum);
1349 
1350         next_dts = 0;
1351       } else {
1352         /* not considered BYTE seekable if it is talking to us in TIME,
1353          * whatever else it might claim */
1354         parse->priv->upstream_seekable = FALSE;
1355         next_dts = GST_CLOCK_TIME_NONE;
1356         gst_event_copy_segment (event, &out_segment);
1357       }
1358 
1359       GST_DEBUG_OBJECT (parse, "OUT segment %" GST_SEGMENT_FORMAT,
1360           &out_segment);
1361       memcpy (&parse->segment, &out_segment, sizeof (GstSegment));
1362 
1363       /*
1364          gst_segment_set_newsegment (&parse->segment, update, rate,
1365          applied_rate, format, start, stop, start);
1366        */
1367 
1368       ret = TRUE;
1369 
1370       /* save the segment for later, right before we push a new buffer so that
1371        * the caps are fixed and the next linked element can receive
1372        * the segment but finish the current segment */
1373       GST_DEBUG_OBJECT (parse, "draining current segment");
1374       if (in_segment->rate > 0.0)
1375         gst_base_parse_drain (parse);
1376       else
1377         gst_base_parse_finish_fragment (parse, FALSE);
1378       gst_adapter_clear (parse->priv->adapter);
1379 
1380       parse->priv->offset = offset;
1381       parse->priv->sync_offset = offset;
1382       parse->priv->next_dts = next_dts;
1383       parse->priv->next_pts = GST_CLOCK_TIME_NONE;
1384       parse->priv->last_pts = GST_CLOCK_TIME_NONE;
1385       parse->priv->last_dts = GST_CLOCK_TIME_NONE;
1386       parse->priv->prev_pts = GST_CLOCK_TIME_NONE;
1387       parse->priv->prev_dts = GST_CLOCK_TIME_NONE;
1388       parse->priv->prev_dts_from_pts = FALSE;
1389       parse->priv->discont = TRUE;
1390       parse->priv->seen_keyframe = FALSE;
1391       parse->priv->skip = 0;
1392       break;
1393     }
1394 
1395     case GST_EVENT_SEGMENT_DONE:
1396       /* need to drain now, rather than upon a new segment,
1397        * since that would have SEGMENT_DONE come before potential
1398        * delayed last part of the current segment */
1399       GST_DEBUG_OBJECT (parse, "draining current segment");
1400       if (parse->segment.rate > 0.0)
1401         gst_base_parse_drain (parse);
1402       else
1403         gst_base_parse_finish_fragment (parse, FALSE);
1404       /* Also forward event immediately, there might be no new data
1405        * coming afterwards that would allow us to forward it later */
1406       forward_immediate = TRUE;
1407       break;
1408 
1409     case GST_EVENT_FLUSH_START:
1410       GST_OBJECT_LOCK (parse);
1411       parse->priv->flushing = TRUE;
1412       GST_OBJECT_UNLOCK (parse);
1413       break;
1414 
1415     case GST_EVENT_FLUSH_STOP:
1416       gst_adapter_clear (parse->priv->adapter);
1417       gst_base_parse_clear_queues (parse);
1418       parse->priv->flushing = FALSE;
1419       parse->priv->discont = TRUE;
1420       parse->priv->last_pts = GST_CLOCK_TIME_NONE;
1421       parse->priv->last_dts = GST_CLOCK_TIME_NONE;
1422       parse->priv->new_frame = TRUE;
1423       parse->priv->skip = 0;
1424 
1425       forward_immediate = TRUE;
1426       break;
1427 
1428     case GST_EVENT_EOS:
1429       if (parse->segment.rate > 0.0)
1430         gst_base_parse_drain (parse);
1431       else
1432         gst_base_parse_finish_fragment (parse, TRUE);
1433 
1434       /* If we STILL have zero frames processed, fire an error */
1435       if (parse->priv->framecount == 0 && !parse->priv->saw_gaps &&
1436           !parse->priv->first_buffer) {
1437         GST_ELEMENT_ERROR (parse, STREAM, WRONG_TYPE,
1438             ("No valid frames found before end of stream"), (NULL));
1439       }
1440 
1441       if (!parse->priv->saw_gaps
1442           && parse->priv->framecount < MIN_FRAMES_TO_POST_BITRATE) {
1443         /* We've not posted bitrate tags yet - do so now */
1444         gst_base_parse_queue_tag_event_update (parse);
1445       }
1446 
1447       /* newsegment and other serialized events before eos */
1448       gst_base_parse_push_pending_events (parse);
1449 
1450       forward_immediate = TRUE;
1451       break;
1452     case GST_EVENT_CUSTOM_DOWNSTREAM:{
1453       /* FIXME: Code duplicated from libgstvideo because core can't depend on -base */
1454 #ifndef GST_VIDEO_EVENT_STILL_STATE_NAME
1455 #define GST_VIDEO_EVENT_STILL_STATE_NAME "GstEventStillFrame"
1456 #endif
1457 
1458       const GstStructure *s;
1459       gboolean ev_still_state;
1460 
1461       s = gst_event_get_structure (event);
1462       if (s != NULL &&
1463           gst_structure_has_name (s, GST_VIDEO_EVENT_STILL_STATE_NAME) &&
1464           gst_structure_get_boolean (s, "still-state", &ev_still_state)) {
1465         if (ev_still_state) {
1466           GST_DEBUG_OBJECT (parse, "draining current data for still-frame");
1467           if (parse->segment.rate > 0.0)
1468             gst_base_parse_drain (parse);
1469           else
1470             gst_base_parse_finish_fragment (parse, TRUE);
1471         }
1472         forward_immediate = TRUE;
1473       }
1474       break;
1475     }
1476     case GST_EVENT_GAP:
1477     {
1478       GST_DEBUG_OBJECT (parse, "draining current data due to gap event");
1479 
1480       /* Ensure we have caps before forwarding the event */
1481       if (!gst_pad_has_current_caps (GST_BASE_PARSE_SRC_PAD (parse))) {
1482         GstCaps *default_caps = NULL;
1483         if ((default_caps = gst_base_parse_negotiate_default_caps (parse))) {
1484           GList *l;
1485           GstEvent *caps_event = gst_event_new_caps (default_caps);
1486 
1487           GST_DEBUG_OBJECT (parse,
1488               "Store caps event to pending list for initial pre-rolling");
1489 
1490           /* Events are in decreasing order. Go down the list until we
1491            * find the first pre-CAPS event and insert our CAPS event there.
1492            *
1493            * There should be a SEGMENT event already, which is > CAPS */
1494           for (l = parse->priv->pending_events; l; l = l->next) {
1495             GstEvent *e = l->data;
1496 
1497             if (GST_EVENT_TYPE (e) < GST_EVENT_CAPS) {
1498               parse->priv->pending_events =
1499                   g_list_insert_before (parse->priv->pending_events, l,
1500                   caps_event);
1501               break;
1502             }
1503           }
1504           /* No pending event that is < CAPS, so we have to add it at the very
1505            * end of the list */
1506           if (!l) {
1507             parse->priv->pending_events =
1508                 g_list_append (parse->priv->pending_events, caps_event);
1509           }
1510           gst_caps_unref (default_caps);
1511         } else {
1512           gst_event_unref (event);
1513           event = NULL;
1514           ret = FALSE;
1515           GST_ELEMENT_ERROR (parse, STREAM, FORMAT, (NULL),
1516               ("Parser output not negotiated before GAP event."));
1517           break;
1518         }
1519       }
1520 
1521       gst_base_parse_push_pending_events (parse);
1522 
1523       if (parse->segment.rate > 0.0)
1524         gst_base_parse_drain (parse);
1525       else
1526         gst_base_parse_finish_fragment (parse, TRUE);
1527       forward_immediate = TRUE;
1528       parse->priv->saw_gaps = TRUE;
1529       break;
1530     }
1531     case GST_EVENT_TAG:
1532     {
1533       GstTagList *tags = NULL;
1534 
1535       gst_event_parse_tag (event, &tags);
1536 
1537       /* We only care about stream tags here, global tags we just forward */
1538       if (gst_tag_list_get_scope (tags) != GST_TAG_SCOPE_STREAM)
1539         break;
1540 
1541       gst_base_parse_set_upstream_tags (parse, tags);
1542       gst_base_parse_queue_tag_event_update (parse);
1543       parse->priv->tags_changed = FALSE;
1544       gst_event_unref (event);
1545       event = NULL;
1546       ret = TRUE;
1547       break;
1548     }
1549     case GST_EVENT_STREAM_START:
1550     {
1551       if (parse->priv->pad_mode != GST_PAD_MODE_PULL)
1552         forward_immediate = TRUE;
1553 
1554       gst_base_parse_set_upstream_tags (parse, NULL);
1555       parse->priv->tags_changed = TRUE;
1556       break;
1557     }
1558     default:
1559       break;
1560   }
1561 
1562   /* Forward non-serialized events and EOS/FLUSH_STOP immediately.
1563    * For EOS this is required because no buffer or serialized event
1564    * will come after EOS and nothing could trigger another
1565    * _finish_frame() call.   *
1566    * If the subclass handles sending of EOS manually it can return
1567    * _DROPPED from ::finish() and all other subclasses should have
1568    * decoded/flushed all remaining data before this
1569    *
1570    * For FLUSH_STOP this is required because it is expected
1571    * to be forwarded immediately and no buffers are queued anyway.
1572    */
1573   if (event) {
1574     if (!GST_EVENT_IS_SERIALIZED (event) || forward_immediate) {
1575       ret = gst_pad_push_event (parse->srcpad, event);
1576     } else {
1577       parse->priv->pending_events =
1578           g_list_prepend (parse->priv->pending_events, event);
1579       ret = TRUE;
1580     }
1581   }
1582 
1583   GST_DEBUG_OBJECT (parse, "event handled");
1584 
1585   return ret;
1586 }
1587 
1588 static gboolean
gst_base_parse_sink_query_default(GstBaseParse * parse,GstQuery * query)1589 gst_base_parse_sink_query_default (GstBaseParse * parse, GstQuery * query)
1590 {
1591   GstPad *pad;
1592   gboolean res;
1593 
1594   pad = GST_BASE_PARSE_SINK_PAD (parse);
1595 
1596   switch (GST_QUERY_TYPE (query)) {
1597     case GST_QUERY_CAPS:
1598     {
1599       GstBaseParseClass *bclass;
1600 
1601       bclass = GST_BASE_PARSE_GET_CLASS (parse);
1602 
1603       if (bclass->get_sink_caps) {
1604         GstCaps *caps, *filter;
1605 
1606         gst_query_parse_caps (query, &filter);
1607         caps = bclass->get_sink_caps (parse, filter);
1608         GST_LOG_OBJECT (parse, "sink getcaps returning caps %" GST_PTR_FORMAT,
1609             caps);
1610         gst_query_set_caps_result (query, caps);
1611         gst_caps_unref (caps);
1612 
1613         res = TRUE;
1614       } else {
1615         GstCaps *caps, *template_caps, *filter;
1616 
1617         gst_query_parse_caps (query, &filter);
1618         template_caps = gst_pad_get_pad_template_caps (pad);
1619         if (filter != NULL) {
1620           caps =
1621               gst_caps_intersect_full (filter, template_caps,
1622               GST_CAPS_INTERSECT_FIRST);
1623           gst_caps_unref (template_caps);
1624         } else {
1625           caps = template_caps;
1626         }
1627         gst_query_set_caps_result (query, caps);
1628         gst_caps_unref (caps);
1629 
1630         res = TRUE;
1631       }
1632       break;
1633     }
1634     default:
1635     {
1636       res = gst_pad_query_default (pad, GST_OBJECT_CAST (parse), query);
1637       break;
1638     }
1639   }
1640 
1641   return res;
1642 }
1643 
1644 static gboolean
gst_base_parse_sink_query(GstPad * pad,GstObject * parent,GstQuery * query)1645 gst_base_parse_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
1646 {
1647   GstBaseParseClass *bclass;
1648   GstBaseParse *parse;
1649   gboolean ret;
1650 
1651   parse = GST_BASE_PARSE (parent);
1652   bclass = GST_BASE_PARSE_GET_CLASS (parse);
1653 
1654   GST_DEBUG_OBJECT (parse, "%s query", GST_QUERY_TYPE_NAME (query));
1655 
1656   if (bclass->sink_query)
1657     ret = bclass->sink_query (parse, query);
1658   else
1659     ret = FALSE;
1660 
1661   GST_LOG_OBJECT (parse, "%s query result: %d %" GST_PTR_FORMAT,
1662       GST_QUERY_TYPE_NAME (query), ret, query);
1663 
1664   return ret;
1665 }
1666 
1667 static gboolean
gst_base_parse_src_query(GstPad * pad,GstObject * parent,GstQuery * query)1668 gst_base_parse_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
1669 {
1670   GstBaseParseClass *bclass;
1671   GstBaseParse *parse;
1672   gboolean ret;
1673 
1674   parse = GST_BASE_PARSE (parent);
1675   bclass = GST_BASE_PARSE_GET_CLASS (parse);
1676 
1677   GST_DEBUG_OBJECT (parse, "%s query: %" GST_PTR_FORMAT,
1678       GST_QUERY_TYPE_NAME (query), query);
1679 
1680   if (bclass->src_query)
1681     ret = bclass->src_query (parse, query);
1682   else
1683     ret = FALSE;
1684 
1685   GST_LOG_OBJECT (parse, "%s query result: %d %" GST_PTR_FORMAT,
1686       GST_QUERY_TYPE_NAME (query), ret, query);
1687 
1688   return ret;
1689 }
1690 
1691 /* gst_base_parse_src_event:
1692  * @pad: #GstPad that received the event.
1693  * @event: #GstEvent that was received.
1694  *
1695  * Handler for source pad events.
1696  *
1697  * Returns: %TRUE if the event was handled.
1698  */
1699 static gboolean
gst_base_parse_src_event(GstPad * pad,GstObject * parent,GstEvent * event)1700 gst_base_parse_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
1701 {
1702   GstBaseParse *parse;
1703   GstBaseParseClass *bclass;
1704   gboolean ret = TRUE;
1705 
1706   parse = GST_BASE_PARSE (parent);
1707   bclass = GST_BASE_PARSE_GET_CLASS (parse);
1708 
1709   GST_DEBUG_OBJECT (parse, "event %d, %s", GST_EVENT_TYPE (event),
1710       GST_EVENT_TYPE_NAME (event));
1711 
1712   if (bclass->src_event)
1713     ret = bclass->src_event (parse, event);
1714   else
1715     gst_event_unref (event);
1716 
1717   return ret;
1718 }
1719 
1720 static gboolean
gst_base_parse_is_seekable(GstBaseParse * parse)1721 gst_base_parse_is_seekable (GstBaseParse * parse)
1722 {
1723   /* FIXME: could do more here, e.g. check index or just send data from 0
1724    * in pull mode and let decoder/sink clip */
1725   return parse->priv->syncable;
1726 }
1727 
1728 /* gst_base_parse_src_event_default:
1729  * @parse: #GstBaseParse.
1730  * @event: #GstEvent that was received.
1731  *
1732  * Default srcpad event handler.
1733  *
1734  * Returns: %TRUE if the event was handled and can be dropped.
1735  */
1736 static gboolean
gst_base_parse_src_event_default(GstBaseParse * parse,GstEvent * event)1737 gst_base_parse_src_event_default (GstBaseParse * parse, GstEvent * event)
1738 {
1739   gboolean res = FALSE;
1740 
1741   switch (GST_EVENT_TYPE (event)) {
1742     case GST_EVENT_SEEK:
1743       if (gst_base_parse_is_seekable (parse))
1744         res = gst_base_parse_handle_seek (parse, event);
1745       break;
1746     default:
1747       res = gst_pad_event_default (parse->srcpad, GST_OBJECT_CAST (parse),
1748           event);
1749       break;
1750   }
1751   return res;
1752 }
1753 
1754 
1755 /**
1756  * gst_base_parse_convert_default:
1757  * @parse: #GstBaseParse.
1758  * @src_format: #GstFormat describing the source format.
1759  * @src_value: Source value to be converted.
1760  * @dest_format: #GstFormat defining the converted format.
1761  * @dest_value: (out): Pointer where the conversion result will be put.
1762  *
1763  * Default implementation of #GstBaseParseClass::convert.
1764  *
1765  * Returns: %TRUE if conversion was successful.
1766  */
1767 gboolean
gst_base_parse_convert_default(GstBaseParse * parse,GstFormat src_format,gint64 src_value,GstFormat dest_format,gint64 * dest_value)1768 gst_base_parse_convert_default (GstBaseParse * parse,
1769     GstFormat src_format,
1770     gint64 src_value, GstFormat dest_format, gint64 * dest_value)
1771 {
1772   gboolean ret = FALSE;
1773   guint64 bytes, duration;
1774 
1775   if (G_UNLIKELY (src_format == dest_format)) {
1776     *dest_value = src_value;
1777     return TRUE;
1778   }
1779 
1780   if (G_UNLIKELY (src_value == -1)) {
1781     *dest_value = -1;
1782     return TRUE;
1783   }
1784 
1785   if (G_UNLIKELY (src_value == 0)) {
1786     *dest_value = 0;
1787     return TRUE;
1788   }
1789 
1790   if (parse->priv->upstream_format != GST_FORMAT_BYTES) {
1791     /* don't do byte format conversions if we're not really parsing
1792      * a raw elementary stream, since we don't really have BYTES
1793      * position / duration info */
1794     if (src_format == GST_FORMAT_BYTES || dest_format == GST_FORMAT_BYTES)
1795       goto no_slaved_conversions;
1796   }
1797 
1798   /* need at least some frames */
1799   if (!parse->priv->framecount)
1800     goto no_framecount;
1801 
1802   duration = parse->priv->acc_duration;
1803   bytes = parse->priv->bytecount;
1804 
1805   if (G_UNLIKELY (!duration || !bytes))
1806     goto no_duration_bytes;
1807 
1808   if (src_format == GST_FORMAT_BYTES) {
1809     if (dest_format == GST_FORMAT_TIME) {
1810       /* BYTES -> TIME conversion */
1811       GST_DEBUG_OBJECT (parse, "converting bytes -> time");
1812       *dest_value = gst_util_uint64_scale (src_value, duration, bytes);
1813       GST_DEBUG_OBJECT (parse,
1814           "converted %" G_GINT64_FORMAT " bytes to %" GST_TIME_FORMAT,
1815           src_value, GST_TIME_ARGS (*dest_value));
1816       ret = TRUE;
1817     } else {
1818       GST_DEBUG_OBJECT (parse, "converting bytes -> other not implemented");
1819     }
1820   } else if (src_format == GST_FORMAT_TIME) {
1821     if (dest_format == GST_FORMAT_BYTES) {
1822       GST_DEBUG_OBJECT (parse, "converting time -> bytes");
1823       *dest_value = gst_util_uint64_scale (src_value, bytes, duration);
1824       GST_DEBUG_OBJECT (parse,
1825           "converted %" GST_TIME_FORMAT " to %" G_GINT64_FORMAT " bytes",
1826           GST_TIME_ARGS (src_value), *dest_value);
1827       ret = TRUE;
1828     } else {
1829       GST_DEBUG_OBJECT (parse, "converting time -> other not implemented");
1830     }
1831   } else if (src_format == GST_FORMAT_DEFAULT) {
1832     /* DEFAULT == frame-based */
1833     if (dest_format == GST_FORMAT_TIME) {
1834       GST_DEBUG_OBJECT (parse, "converting default -> time");
1835       if (parse->priv->fps_den) {
1836         *dest_value = gst_util_uint64_scale (src_value,
1837             GST_SECOND * parse->priv->fps_den, parse->priv->fps_num);
1838         ret = TRUE;
1839       }
1840     } else {
1841       GST_DEBUG_OBJECT (parse, "converting default -> other not implemented");
1842     }
1843   } else {
1844     GST_DEBUG_OBJECT (parse, "conversion not implemented");
1845   }
1846   return ret;
1847 
1848   /* ERRORS */
1849 no_framecount:
1850   {
1851     GST_DEBUG_OBJECT (parse, "no framecount");
1852     return FALSE;
1853   }
1854 no_duration_bytes:
1855   {
1856     GST_DEBUG_OBJECT (parse, "no duration %" G_GUINT64_FORMAT ", bytes %"
1857         G_GUINT64_FORMAT, duration, bytes);
1858     return FALSE;
1859   }
1860 no_slaved_conversions:
1861   {
1862     GST_DEBUG_OBJECT (parse,
1863         "Can't do format conversions when upstream format is not BYTES");
1864     return FALSE;
1865   }
1866 }
1867 
1868 static void
gst_base_parse_update_duration(GstBaseParse * parse)1869 gst_base_parse_update_duration (GstBaseParse * parse)
1870 {
1871   gint64 ptot, dest_value;
1872 
1873   if (!gst_pad_peer_query_duration (parse->sinkpad, GST_FORMAT_BYTES, &ptot))
1874     return;
1875 
1876   if (!gst_base_parse_convert (parse, GST_FORMAT_BYTES, ptot,
1877           GST_FORMAT_TIME, &dest_value))
1878     return;
1879 
1880   /* inform if duration changed, but try to avoid spamming */
1881   parse->priv->estimated_drift += dest_value - parse->priv->estimated_duration;
1882 
1883   parse->priv->estimated_duration = dest_value;
1884   GST_LOG_OBJECT (parse,
1885       "updated estimated duration to %" GST_TIME_FORMAT,
1886       GST_TIME_ARGS (dest_value));
1887 
1888   if (parse->priv->estimated_drift > GST_SECOND ||
1889       parse->priv->estimated_drift < -GST_SECOND) {
1890     gst_element_post_message (GST_ELEMENT (parse),
1891         gst_message_new_duration_changed (GST_OBJECT (parse)));
1892     parse->priv->estimated_drift = 0;
1893   }
1894 }
1895 
1896 /* gst_base_parse_update_bitrates:
1897  * @parse: #GstBaseParse.
1898  * @buffer: Current frame as a #GstBuffer
1899  *
1900  * Keeps track of the minimum and maximum bitrates, and also maintains a
1901  * running average bitrate of the stream so far.
1902  */
1903 static void
gst_base_parse_update_bitrates(GstBaseParse * parse,GstBaseParseFrame * frame)1904 gst_base_parse_update_bitrates (GstBaseParse * parse, GstBaseParseFrame * frame)
1905 {
1906   guint64 data_len, frame_dur;
1907   gint overhead;
1908   guint frame_bitrate;
1909   guint64 frame_bitrate64;
1910   GstBuffer *buffer = frame->buffer;
1911 
1912   overhead = frame->overhead;
1913   if (overhead == -1)
1914     return;
1915 
1916   data_len = gst_buffer_get_size (buffer) - overhead;
1917   parse->priv->data_bytecount += data_len;
1918 
1919   /* duration should be valid by now,
1920    * either set by subclass or maybe based on fps settings */
1921   if (GST_BUFFER_DURATION_IS_VALID (buffer) && parse->priv->acc_duration != 0) {
1922     guint64 avg_bitrate;
1923 
1924     /* Calculate duration of a frame from buffer properties */
1925     frame_dur = GST_BUFFER_DURATION (buffer);
1926     avg_bitrate = gst_util_uint64_scale (GST_SECOND,
1927         8 * parse->priv->data_bytecount, parse->priv->acc_duration);
1928 
1929     if (avg_bitrate > G_MAXUINT)
1930       return;
1931 
1932     parse->priv->avg_bitrate = (guint) avg_bitrate;
1933   } else {
1934     /* No way to figure out frame duration (is this even possible?) */
1935     return;
1936   }
1937 
1938   /* override if subclass provided bitrate, e.g. metadata based */
1939   if (parse->priv->bitrate) {
1940     parse->priv->avg_bitrate = parse->priv->bitrate;
1941     /* spread this (confirmed) info ASAP */
1942     if (parse->priv->post_avg_bitrate &&
1943         parse->priv->posted_avg_bitrate != parse->priv->avg_bitrate)
1944       parse->priv->tags_changed = TRUE;
1945   }
1946 
1947   if (!frame_dur)
1948     return;
1949 
1950   frame_bitrate64 = gst_util_uint64_scale (GST_SECOND, 8 * data_len, frame_dur);
1951 
1952   if (frame_bitrate64 > G_MAXUINT)
1953     return;
1954 
1955   frame_bitrate = (guint) frame_bitrate64;
1956 
1957   GST_LOG_OBJECT (parse, "frame bitrate %u, avg bitrate %u", frame_bitrate,
1958       parse->priv->avg_bitrate);
1959 
1960   if (parse->priv->framecount < MIN_FRAMES_TO_POST_BITRATE)
1961     return;
1962 
1963   if (parse->priv->framecount == MIN_FRAMES_TO_POST_BITRATE &&
1964       (parse->priv->post_min_bitrate || parse->priv->post_avg_bitrate
1965           || parse->priv->post_max_bitrate))
1966     parse->priv->tags_changed = TRUE;
1967 
1968   if (G_LIKELY (parse->priv->framecount >= MIN_FRAMES_TO_POST_BITRATE)) {
1969     if (frame_bitrate < parse->priv->min_bitrate) {
1970       parse->priv->min_bitrate = frame_bitrate;
1971       if (parse->priv->post_min_bitrate)
1972         parse->priv->tags_changed = TRUE;
1973     }
1974 
1975     if (frame_bitrate > parse->priv->max_bitrate) {
1976       parse->priv->max_bitrate = frame_bitrate;
1977       if (parse->priv->post_max_bitrate)
1978         parse->priv->tags_changed = TRUE;
1979     }
1980 
1981     /* Only update the tag on a 2% change */
1982     if (parse->priv->post_avg_bitrate && parse->priv->avg_bitrate) {
1983       guint64 diffprev = gst_util_uint64_scale (100,
1984           ABSDIFF (parse->priv->avg_bitrate, parse->priv->posted_avg_bitrate),
1985           parse->priv->avg_bitrate);
1986       if (diffprev >= UPDATE_THRESHOLD)
1987         parse->priv->tags_changed = TRUE;
1988     }
1989   }
1990 }
1991 
1992 /**
1993  * gst_base_parse_add_index_entry:
1994  * @parse: #GstBaseParse.
1995  * @offset: offset of entry
1996  * @ts: timestamp associated with offset
1997  * @key: whether entry refers to keyframe
1998  * @force: add entry disregarding sanity checks
1999  *
2000  * Adds an entry to the index associating @offset to @ts.  It is recommended
2001  * to only add keyframe entries.  @force allows to bypass checks, such as
2002  * whether the stream is (upstream) seekable, another entry is already "close"
2003  * to the new entry, etc.
2004  *
2005  * Returns: #gboolean indicating whether entry was added
2006  */
2007 gboolean
gst_base_parse_add_index_entry(GstBaseParse * parse,guint64 offset,GstClockTime ts,gboolean key,gboolean force)2008 gst_base_parse_add_index_entry (GstBaseParse * parse, guint64 offset,
2009     GstClockTime ts, gboolean key, gboolean force)
2010 {
2011   gboolean ret = FALSE;
2012   GstIndexAssociation associations[2];
2013 
2014   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (ts), FALSE);
2015 
2016   GST_LOG_OBJECT (parse, "Adding key=%d index entry %" GST_TIME_FORMAT
2017       " @ offset 0x%08" G_GINT64_MODIFIER "x", key, GST_TIME_ARGS (ts), offset);
2018 
2019   if (G_LIKELY (!force)) {
2020 
2021     if (!parse->priv->upstream_seekable) {
2022       GST_DEBUG_OBJECT (parse, "upstream not seekable; discarding");
2023       goto exit;
2024     }
2025 
2026     /* FIXME need better helper data structure that handles these issues
2027      * related to ongoing collecting of index entries */
2028     if (parse->priv->index_last_offset + parse->priv->idx_byte_interval >=
2029         (gint64) offset) {
2030       GST_LOG_OBJECT (parse,
2031           "already have entries up to offset 0x%08" G_GINT64_MODIFIER "x",
2032           parse->priv->index_last_offset + parse->priv->idx_byte_interval);
2033       goto exit;
2034     }
2035 
2036     if (GST_CLOCK_TIME_IS_VALID (parse->priv->index_last_ts) &&
2037         GST_CLOCK_DIFF (parse->priv->index_last_ts, ts) <
2038         parse->priv->idx_interval) {
2039       GST_LOG_OBJECT (parse, "entry too close to last time %" GST_TIME_FORMAT,
2040           GST_TIME_ARGS (parse->priv->index_last_ts));
2041       goto exit;
2042     }
2043 
2044     /* if last is not really the last one */
2045     if (!parse->priv->index_last_valid) {
2046       GstClockTime prev_ts;
2047 
2048       gst_base_parse_find_offset (parse, ts, TRUE, &prev_ts);
2049       if (GST_CLOCK_DIFF (prev_ts, ts) < parse->priv->idx_interval) {
2050         GST_LOG_OBJECT (parse,
2051             "entry too close to existing entry %" GST_TIME_FORMAT,
2052             GST_TIME_ARGS (prev_ts));
2053         parse->priv->index_last_offset = offset;
2054         parse->priv->index_last_ts = ts;
2055         goto exit;
2056       }
2057     }
2058   }
2059 
2060   associations[0].format = GST_FORMAT_TIME;
2061   associations[0].value = ts;
2062   associations[1].format = GST_FORMAT_BYTES;
2063   associations[1].value = offset;
2064 
2065   /* index might change on-the-fly, although that would be nutty app ... */
2066   GST_BASE_PARSE_INDEX_LOCK (parse);
2067   gst_index_add_associationv (parse->priv->index, parse->priv->index_id,
2068       (key) ? GST_INDEX_ASSOCIATION_FLAG_KEY_UNIT :
2069       GST_INDEX_ASSOCIATION_FLAG_DELTA_UNIT, 2,
2070       (const GstIndexAssociation *) &associations);
2071   GST_BASE_PARSE_INDEX_UNLOCK (parse);
2072 
2073   if (key) {
2074     parse->priv->index_last_offset = offset;
2075     parse->priv->index_last_ts = ts;
2076   }
2077 
2078   ret = TRUE;
2079 
2080 exit:
2081   return ret;
2082 }
2083 
2084 /* check for seekable upstream, above and beyond a mere query */
2085 static void
gst_base_parse_check_seekability(GstBaseParse * parse)2086 gst_base_parse_check_seekability (GstBaseParse * parse)
2087 {
2088   GstQuery *query;
2089   gboolean seekable = FALSE;
2090   gint64 start = -1, stop = -1;
2091   guint idx_interval = 0;
2092   guint64 idx_byte_interval = 0;
2093 
2094   query = gst_query_new_seeking (GST_FORMAT_BYTES);
2095   if (!gst_pad_peer_query (parse->sinkpad, query)) {
2096     GST_DEBUG_OBJECT (parse, "seeking query failed");
2097     goto done;
2098   }
2099 
2100   gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
2101 
2102   /* try harder to query upstream size if we didn't get it the first time */
2103   if (seekable && stop == -1) {
2104     GST_DEBUG_OBJECT (parse, "doing duration query to fix up unset stop");
2105     gst_pad_peer_query_duration (parse->sinkpad, GST_FORMAT_BYTES, &stop);
2106   }
2107 
2108   /* if upstream doesn't know the size, it's likely that it's not seekable in
2109    * practice even if it technically may be seekable */
2110   if (seekable && (start != 0 || stop <= start)) {
2111     GST_DEBUG_OBJECT (parse, "seekable but unknown start/stop -> disable");
2112     seekable = FALSE;
2113   }
2114 
2115   /* let's not put every single frame into our index */
2116   if (seekable) {
2117     if (stop < 10 * 1024 * 1024)
2118       idx_interval = 100;
2119     else if (stop < 100 * 1024 * 1024)
2120       idx_interval = 500;
2121     else
2122       idx_interval = 1000;
2123 
2124     /* ensure that even for large files (e.g. very long audio files), the index
2125      * stays reasonably-size, with some arbitrary limit to the total number of
2126      * index entries */
2127     idx_byte_interval = (stop - start) / MAX_INDEX_ENTRIES;
2128     GST_DEBUG_OBJECT (parse,
2129         "Limiting index entries to %d, indexing byte interval %"
2130         G_GUINT64_FORMAT " bytes", MAX_INDEX_ENTRIES, idx_byte_interval);
2131   }
2132 
2133 done:
2134   gst_query_unref (query);
2135 
2136   GST_DEBUG_OBJECT (parse, "seekable: %d (%" G_GUINT64_FORMAT " - %"
2137       G_GUINT64_FORMAT ")", seekable, start, stop);
2138   parse->priv->upstream_seekable = seekable;
2139   parse->priv->upstream_size = seekable ? stop : 0;
2140 
2141   GST_DEBUG_OBJECT (parse, "idx_interval: %ums", idx_interval);
2142   parse->priv->idx_interval = idx_interval * GST_MSECOND;
2143   parse->priv->idx_byte_interval = idx_byte_interval;
2144 }
2145 
2146 /* some misc checks on upstream */
2147 static void
gst_base_parse_check_upstream(GstBaseParse * parse)2148 gst_base_parse_check_upstream (GstBaseParse * parse)
2149 {
2150   gint64 stop;
2151 
2152   if (gst_pad_peer_query_duration (parse->sinkpad, GST_FORMAT_TIME, &stop))
2153     if (GST_CLOCK_TIME_IS_VALID (stop) && stop) {
2154       /* upstream has one, accept it also, and no further updates */
2155       gst_base_parse_set_duration (parse, GST_FORMAT_TIME, stop, 0);
2156       parse->priv->upstream_has_duration = TRUE;
2157     }
2158 
2159   GST_DEBUG_OBJECT (parse, "upstream_has_duration: %d",
2160       parse->priv->upstream_has_duration);
2161 }
2162 
2163 /* checks src caps to determine if dealing with audio or video */
2164 /* TODO maybe forego automagic stuff and let subclass configure it ? */
2165 static void
gst_base_parse_check_media(GstBaseParse * parse)2166 gst_base_parse_check_media (GstBaseParse * parse)
2167 {
2168   GstCaps *caps;
2169   GstStructure *s;
2170 
2171   caps = gst_pad_get_current_caps (parse->srcpad);
2172   if (G_LIKELY (caps) && (s = gst_caps_get_structure (caps, 0))) {
2173     parse->priv->is_video =
2174         g_str_has_prefix (gst_structure_get_name (s), "video");
2175   } else {
2176     /* historical default */
2177     parse->priv->is_video = FALSE;
2178   }
2179   if (caps)
2180     gst_caps_unref (caps);
2181 
2182   parse->priv->checked_media = TRUE;
2183   GST_DEBUG_OBJECT (parse, "media is video: %d", parse->priv->is_video);
2184 }
2185 
2186 /* takes ownership of frame */
2187 static void
gst_base_parse_queue_frame(GstBaseParse * parse,GstBaseParseFrame * frame)2188 gst_base_parse_queue_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
2189 {
2190   if (!(frame->_private_flags & GST_BASE_PARSE_FRAME_PRIVATE_FLAG_NOALLOC)) {
2191     /* frame allocated on the heap, we can just take ownership */
2192     g_queue_push_tail (&parse->priv->queued_frames, frame);
2193     GST_TRACE ("queued frame %p", frame);
2194   } else {
2195     GstBaseParseFrame *copy;
2196 
2197     /* probably allocated on the stack, must make a proper copy */
2198     copy = gst_base_parse_frame_copy (frame);
2199     g_queue_push_tail (&parse->priv->queued_frames, copy);
2200     GST_TRACE ("queued frame %p (copy of %p)", copy, frame);
2201     gst_base_parse_frame_free (frame);
2202   }
2203 }
2204 
2205 /* makes sure that @buf is properly prepared and decorated for passing
2206  * to baseclass, and an equally setup frame is returned setup with @buf.
2207  * Takes ownership of @buf. */
2208 static GstBaseParseFrame *
gst_base_parse_prepare_frame(GstBaseParse * parse,GstBuffer * buffer)2209 gst_base_parse_prepare_frame (GstBaseParse * parse, GstBuffer * buffer)
2210 {
2211   GstBaseParseFrame *frame = NULL;
2212 
2213   buffer = gst_buffer_make_writable (buffer);
2214 
2215   GST_LOG_OBJECT (parse,
2216       "preparing frame at offset %" G_GUINT64_FORMAT
2217       " (%#" G_GINT64_MODIFIER "x) of size %" G_GSIZE_FORMAT,
2218       GST_BUFFER_OFFSET (buffer), GST_BUFFER_OFFSET (buffer),
2219       gst_buffer_get_size (buffer));
2220 
2221   GST_BUFFER_OFFSET (buffer) = parse->priv->offset;
2222 
2223   gst_base_parse_update_flags (parse);
2224 
2225   frame = gst_base_parse_frame_new (buffer, 0, 0);
2226   gst_buffer_unref (buffer);
2227   gst_base_parse_update_frame (parse, frame);
2228 
2229   /* clear flags for next frame */
2230   parse->priv->discont = FALSE;
2231   parse->priv->new_frame = FALSE;
2232 
2233   /* use default handler to provide initial (upstream) metadata */
2234   gst_base_parse_parse_frame (parse, frame);
2235 
2236   return frame;
2237 }
2238 
2239 /* Wraps buffer in a frame and dispatches to subclass.
2240  * Also manages data skipping and offset handling (including adapter flushing).
2241  * Takes ownership of @buffer */
2242 static GstFlowReturn
gst_base_parse_handle_buffer(GstBaseParse * parse,GstBuffer * buffer,gint * skip,gint * flushed)2243 gst_base_parse_handle_buffer (GstBaseParse * parse, GstBuffer * buffer,
2244     gint * skip, gint * flushed)
2245 {
2246   GstBaseParseClass *klass = GST_BASE_PARSE_GET_CLASS (parse);
2247   GstBaseParseFrame *frame;
2248   GstFlowReturn ret;
2249 
2250   g_return_val_if_fail (skip != NULL || flushed != NULL, GST_FLOW_ERROR);
2251 
2252   GST_LOG_OBJECT (parse,
2253       "handling buffer of size %" G_GSIZE_FORMAT " with dts %" GST_TIME_FORMAT
2254       ", pts %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT,
2255       gst_buffer_get_size (buffer), GST_TIME_ARGS (GST_BUFFER_DTS (buffer)),
2256       GST_TIME_ARGS (GST_BUFFER_PTS (buffer)),
2257       GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)));
2258 
2259   /* track what is being flushed during this single round of frame processing */
2260   parse->priv->flushed = 0;
2261   *skip = 0;
2262 
2263   /* make it easy for _finish_frame to pick up input data */
2264   if (parse->priv->pad_mode == GST_PAD_MODE_PULL) {
2265     gst_buffer_ref (buffer);
2266     gst_adapter_push (parse->priv->adapter, buffer);
2267   }
2268 
2269   frame = gst_base_parse_prepare_frame (parse, buffer);
2270   ret = klass->handle_frame (parse, frame, skip);
2271 
2272   *flushed = parse->priv->flushed;
2273 
2274   GST_LOG_OBJECT (parse, "handle_frame skipped %d, flushed %d",
2275       *skip, *flushed);
2276 
2277   /* subclass can only do one of these, or semantics are too unclear */
2278   g_assert (*skip == 0 || *flushed == 0);
2279 
2280   /* track skipping */
2281   if (*skip > 0) {
2282     GstClockTime pts, dts;
2283     GstBuffer *outbuf;
2284 
2285     GST_LOG_OBJECT (parse, "finding sync, skipping %d bytes", *skip);
2286     if (parse->segment.rate < 0.0 && !parse->priv->buffers_queued) {
2287       /* reverse playback, and no frames found yet, so we are skipping
2288        * the leading part of a fragment, which may form the tail of
2289        * fragment coming later, hopefully subclass skips efficiently ... */
2290       pts = gst_adapter_prev_pts (parse->priv->adapter, NULL);
2291       dts = gst_adapter_prev_dts (parse->priv->adapter, NULL);
2292       outbuf = gst_adapter_take_buffer (parse->priv->adapter, *skip);
2293       outbuf = gst_buffer_make_writable (outbuf);
2294       GST_BUFFER_PTS (outbuf) = pts;
2295       GST_BUFFER_DTS (outbuf) = dts;
2296       parse->priv->buffers_head =
2297           g_slist_prepend (parse->priv->buffers_head, outbuf);
2298       outbuf = NULL;
2299     } else {
2300       /* If we're asked to skip more than is available in the adapter,
2301          we need to remember what we need to skip for next iteration */
2302       gsize av = gst_adapter_available (parse->priv->adapter);
2303       GST_DEBUG ("Asked to skip %u (%" G_GSIZE_FORMAT " available)", *skip, av);
2304       if (av >= *skip) {
2305         gst_adapter_flush (parse->priv->adapter, *skip);
2306       } else {
2307         GST_DEBUG
2308             ("This is more than available, flushing %" G_GSIZE_FORMAT
2309             ", storing %u to skip", av, (guint) (*skip - av));
2310         parse->priv->skip = *skip - av;
2311         gst_adapter_flush (parse->priv->adapter, av);
2312         *skip = av;
2313       }
2314     }
2315     if (!parse->priv->discont)
2316       parse->priv->sync_offset = parse->priv->offset;
2317     parse->priv->offset += *skip;
2318     parse->priv->discont = TRUE;
2319     /* check for indefinite skipping */
2320     if (ret == GST_FLOW_OK)
2321       ret = gst_base_parse_check_sync (parse);
2322   }
2323 
2324   parse->priv->offset += *flushed;
2325 
2326   if (parse->priv->pad_mode == GST_PAD_MODE_PULL) {
2327     gst_adapter_clear (parse->priv->adapter);
2328   }
2329 
2330   if (*skip == 0 && *flushed == 0) {
2331     /* Carry over discont if we need more data */
2332     if (GST_BUFFER_IS_DISCONT (frame->buffer))
2333       parse->priv->discont = TRUE;
2334   }
2335 
2336   gst_base_parse_frame_free (frame);
2337 
2338   return ret;
2339 }
2340 
2341 /* gst_base_parse_push_pending_events:
2342  * @parse: #GstBaseParse
2343  *
2344  * Pushes the pending events
2345  */
2346 static void
gst_base_parse_push_pending_events(GstBaseParse * parse)2347 gst_base_parse_push_pending_events (GstBaseParse * parse)
2348 {
2349   if (G_UNLIKELY (parse->priv->pending_events)) {
2350     GList *r = g_list_reverse (parse->priv->pending_events);
2351     GList *l;
2352 
2353     parse->priv->pending_events = NULL;
2354     for (l = r; l != NULL; l = l->next) {
2355       gst_pad_push_event (parse->srcpad, GST_EVENT_CAST (l->data));
2356     }
2357     g_list_free (r);
2358   }
2359 }
2360 
2361 /* gst_base_parse_handle_and_push_frame:
2362  * @parse: #GstBaseParse.
2363  * @klass: #GstBaseParseClass.
2364  * @frame: (transfer full): a #GstBaseParseFrame
2365  *
2366  * Parses the frame from given buffer and pushes it forward. Also performs
2367  * timestamp handling and checks the segment limits.
2368  *
2369  * This is called with srcpad STREAM_LOCK held.
2370  *
2371  * Returns: #GstFlowReturn
2372  */
2373 static GstFlowReturn
gst_base_parse_handle_and_push_frame(GstBaseParse * parse,GstBaseParseFrame * frame)2374 gst_base_parse_handle_and_push_frame (GstBaseParse * parse,
2375     GstBaseParseFrame * frame)
2376 {
2377   gint64 offset;
2378   GstBuffer *buffer;
2379 
2380   g_return_val_if_fail (frame != NULL, GST_FLOW_ERROR);
2381 
2382   buffer = frame->buffer;
2383   offset = frame->offset;
2384 
2385   /* check if subclass/format can provide ts.
2386    * If so, that allows and enables extra seek and duration determining options */
2387   if (G_UNLIKELY (parse->priv->first_frame_offset < 0)) {
2388     if (GST_BUFFER_PTS_IS_VALID (buffer) && parse->priv->has_timing_info
2389         && parse->priv->pad_mode == GST_PAD_MODE_PULL) {
2390       parse->priv->first_frame_offset = offset;
2391       parse->priv->first_frame_pts = GST_BUFFER_PTS (buffer);
2392       parse->priv->first_frame_dts = GST_BUFFER_DTS (buffer);
2393       GST_DEBUG_OBJECT (parse, "subclass provided dts %" GST_TIME_FORMAT
2394           ", pts %" GST_TIME_FORMAT " for first frame at offset %"
2395           G_GINT64_FORMAT, GST_TIME_ARGS (parse->priv->first_frame_dts),
2396           GST_TIME_ARGS (parse->priv->first_frame_pts),
2397           parse->priv->first_frame_offset);
2398       if (!GST_CLOCK_TIME_IS_VALID (parse->priv->duration)) {
2399         gint64 off;
2400         GstClockTime last_ts = G_MAXINT64;
2401 
2402         GST_DEBUG_OBJECT (parse, "no duration; trying scan to determine");
2403         gst_base_parse_locate_time (parse, &last_ts, &off);
2404         if (GST_CLOCK_TIME_IS_VALID (last_ts))
2405           gst_base_parse_set_duration (parse, GST_FORMAT_TIME, last_ts, 0);
2406       }
2407     } else {
2408       /* disable further checks */
2409       parse->priv->first_frame_offset = 0;
2410     }
2411   }
2412 
2413   /* track upstream time if provided, not subclass' internal notion of it */
2414   if (parse->priv->upstream_format == GST_FORMAT_TIME) {
2415     GST_BUFFER_PTS (frame->buffer) = GST_CLOCK_TIME_NONE;
2416     GST_BUFFER_DTS (frame->buffer) = GST_CLOCK_TIME_NONE;
2417   }
2418 
2419   /* interpolating and no valid pts yet,
2420    * start with dts and carry on from there */
2421   if (parse->priv->infer_ts && parse->priv->pts_interpolate
2422       && !GST_CLOCK_TIME_IS_VALID (parse->priv->next_pts))
2423     parse->priv->next_pts = parse->priv->next_dts;
2424 
2425   /* again use default handler to add missing metadata;
2426    * we may have new information on frame properties */
2427   gst_base_parse_parse_frame (parse, frame);
2428 
2429   parse->priv->next_pts = GST_CLOCK_TIME_NONE;
2430   if (GST_BUFFER_DTS_IS_VALID (buffer) && GST_BUFFER_DURATION_IS_VALID (buffer)) {
2431     parse->priv->next_dts =
2432         GST_BUFFER_DTS (buffer) + GST_BUFFER_DURATION (buffer);
2433     if (parse->priv->pts_interpolate && GST_BUFFER_PTS_IS_VALID (buffer)) {
2434       GstClockTime next_pts =
2435           GST_BUFFER_PTS (buffer) + GST_BUFFER_DURATION (buffer);
2436       if (next_pts >= parse->priv->next_dts)
2437         parse->priv->next_pts = next_pts;
2438     }
2439   } else {
2440     /* we lost track, do not produce bogus time next time around
2441      * (probably means parser subclass has given up on parsing as well) */
2442     GST_DEBUG_OBJECT (parse, "no next fallback timestamp");
2443     parse->priv->next_dts = GST_CLOCK_TIME_NONE;
2444   }
2445 
2446   if (parse->priv->upstream_seekable && parse->priv->exact_position &&
2447       GST_BUFFER_PTS_IS_VALID (buffer))
2448     gst_base_parse_add_index_entry (parse, offset,
2449         GST_BUFFER_PTS (buffer),
2450         !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT), FALSE);
2451 
2452   /* All OK, push queued frames if there are any */
2453   if (G_UNLIKELY (!g_queue_is_empty (&parse->priv->queued_frames))) {
2454     GstBaseParseFrame *queued_frame;
2455 
2456     while ((queued_frame = g_queue_pop_head (&parse->priv->queued_frames))) {
2457       gst_base_parse_push_frame (parse, queued_frame);
2458       gst_base_parse_frame_free (queued_frame);
2459     }
2460   }
2461 
2462   return gst_base_parse_push_frame (parse, frame);
2463 }
2464 
2465 /**
2466  * gst_base_parse_push_frame:
2467  * @parse: #GstBaseParse.
2468  * @frame: (transfer none): a #GstBaseParseFrame
2469  *
2470  * Pushes the frame's buffer downstream, sends any pending events and
2471  * does some timestamp and segment handling. Takes ownership of
2472  * frame's buffer, though caller retains ownership of @frame.
2473  *
2474  * This must be called with sinkpad STREAM_LOCK held.
2475  *
2476  * Returns: #GstFlowReturn
2477  */
2478 GstFlowReturn
gst_base_parse_push_frame(GstBaseParse * parse,GstBaseParseFrame * frame)2479 gst_base_parse_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
2480 {
2481   GstFlowReturn ret = GST_FLOW_OK;
2482   GstClockTime last_start = GST_CLOCK_TIME_NONE;
2483   GstClockTime last_stop = GST_CLOCK_TIME_NONE;
2484   GstBaseParseClass *klass = GST_BASE_PARSE_GET_CLASS (parse);
2485   GstBuffer *buffer;
2486   gsize size;
2487 
2488   g_return_val_if_fail (frame != NULL, GST_FLOW_ERROR);
2489   g_return_val_if_fail (frame->buffer != NULL, GST_FLOW_ERROR);
2490 
2491   GST_TRACE_OBJECT (parse, "pushing frame %p", frame);
2492 
2493   buffer = frame->buffer;
2494 
2495   GST_LOG_OBJECT (parse,
2496       "processing buffer of size %" G_GSIZE_FORMAT " with dts %" GST_TIME_FORMAT
2497       ", pts %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT,
2498       gst_buffer_get_size (buffer),
2499       GST_TIME_ARGS (GST_BUFFER_DTS (buffer)),
2500       GST_TIME_ARGS (GST_BUFFER_PTS (buffer)),
2501       GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)));
2502 
2503   /* update stats */
2504   parse->priv->bytecount += frame->size;
2505   if (G_LIKELY (!(frame->flags & GST_BASE_PARSE_FRAME_FLAG_NO_FRAME))) {
2506     parse->priv->framecount++;
2507     if (GST_BUFFER_DURATION_IS_VALID (buffer)) {
2508       parse->priv->acc_duration += GST_BUFFER_DURATION (buffer);
2509     }
2510   }
2511   /* 0 means disabled */
2512   if (parse->priv->update_interval < 0)
2513     parse->priv->update_interval = 50;
2514   else if (parse->priv->update_interval > 0 &&
2515       (parse->priv->framecount % parse->priv->update_interval) == 0)
2516     gst_base_parse_update_duration (parse);
2517 
2518   if (GST_BUFFER_PTS_IS_VALID (buffer))
2519     last_start = last_stop = GST_BUFFER_PTS (buffer);
2520   if (last_start != GST_CLOCK_TIME_NONE
2521       && GST_BUFFER_DURATION_IS_VALID (buffer))
2522     last_stop = last_start + GST_BUFFER_DURATION (buffer);
2523 
2524   /* should have caps by now */
2525   if (!gst_pad_has_current_caps (parse->srcpad))
2526     goto no_caps;
2527 
2528   if (G_UNLIKELY (!parse->priv->checked_media)) {
2529     /* have caps; check identity */
2530     gst_base_parse_check_media (parse);
2531   }
2532 
2533   if (parse->priv->tags_changed) {
2534     gst_base_parse_queue_tag_event_update (parse);
2535     parse->priv->tags_changed = FALSE;
2536   }
2537 
2538   /* Push pending events, including SEGMENT events */
2539   gst_base_parse_push_pending_events (parse);
2540 
2541   /* update bitrates and optionally post corresponding tags
2542    * (following newsegment) */
2543   gst_base_parse_update_bitrates (parse, frame);
2544 
2545   if (klass->pre_push_frame) {
2546     ret = klass->pre_push_frame (parse, frame);
2547   } else {
2548     frame->flags |= GST_BASE_PARSE_FRAME_FLAG_CLIP;
2549   }
2550 
2551   /* Push pending events, if there are any new ones
2552    * like tags added by pre_push_frame */
2553   if (parse->priv->tags_changed) {
2554     gst_base_parse_queue_tag_event_update (parse);
2555     parse->priv->tags_changed = FALSE;
2556   }
2557   gst_base_parse_push_pending_events (parse);
2558 
2559   /* take final ownership of frame buffer */
2560   if (frame->out_buffer) {
2561     buffer = frame->out_buffer;
2562     frame->out_buffer = NULL;
2563     gst_buffer_replace (&frame->buffer, NULL);
2564   } else {
2565     buffer = frame->buffer;
2566     frame->buffer = NULL;
2567   }
2568 
2569   /* subclass must play nice */
2570   g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR);
2571 
2572   size = gst_buffer_get_size (buffer);
2573 
2574   parse->priv->seen_keyframe |= parse->priv->is_video &&
2575       !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
2576 
2577   if (frame->flags & GST_BASE_PARSE_FRAME_FLAG_CLIP) {
2578     if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) &&
2579         GST_CLOCK_TIME_IS_VALID (parse->segment.stop) &&
2580         GST_BUFFER_TIMESTAMP (buffer) >
2581         parse->segment.stop + parse->priv->lead_out_ts) {
2582       GST_LOG_OBJECT (parse, "Dropped frame, after segment");
2583       ret = GST_FLOW_EOS;
2584     } else if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) &&
2585         GST_BUFFER_DURATION_IS_VALID (buffer) &&
2586         GST_CLOCK_TIME_IS_VALID (parse->segment.start) &&
2587         GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer) +
2588         parse->priv->lead_in_ts < parse->segment.start) {
2589       if (parse->priv->seen_keyframe) {
2590         GST_LOG_OBJECT (parse, "Frame before segment, after keyframe");
2591         ret = GST_FLOW_OK;
2592       } else {
2593         GST_LOG_OBJECT (parse, "Dropped frame, before segment");
2594         ret = GST_BASE_PARSE_FLOW_DROPPED;
2595       }
2596     } else {
2597       ret = GST_FLOW_OK;
2598     }
2599   }
2600 
2601   if (ret == GST_BASE_PARSE_FLOW_DROPPED) {
2602     GST_LOG_OBJECT (parse, "frame (%" G_GSIZE_FORMAT " bytes) dropped", size);
2603     if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT))
2604       parse->priv->discont = TRUE;
2605     gst_buffer_unref (buffer);
2606     ret = GST_FLOW_OK;
2607   } else if (ret == GST_FLOW_OK) {
2608     if (parse->segment.rate > 0.0) {
2609       GST_LOG_OBJECT (parse, "pushing frame (%" G_GSIZE_FORMAT " bytes) now..",
2610           size);
2611       ret = gst_pad_push (parse->srcpad, buffer);
2612       GST_LOG_OBJECT (parse, "frame pushed, flow %s", gst_flow_get_name (ret));
2613     } else if (!parse->priv->disable_passthrough && parse->priv->passthrough) {
2614 
2615       /* in backwards playback mode, if on passthrough we need to push buffers
2616        * directly without accumulating them into the buffers_queued as baseparse
2617        * will never check for a DISCONT while on passthrough and those buffers
2618        * will never be pushed.
2619        *
2620        * also, as we are on reverse playback, it might be possible that
2621        * passthrough might have just been enabled, so make sure to drain the
2622        * buffers_queued list */
2623       if (G_UNLIKELY (parse->priv->buffers_queued != NULL)) {
2624         gst_base_parse_finish_fragment (parse, TRUE);
2625         ret = gst_base_parse_send_buffers (parse);
2626       }
2627 
2628       if (ret == GST_FLOW_OK) {
2629         GST_LOG_OBJECT (parse,
2630             "pushing frame (%" G_GSIZE_FORMAT " bytes) now..", size);
2631         ret = gst_pad_push (parse->srcpad, buffer);
2632         GST_LOG_OBJECT (parse, "frame pushed, flow %s",
2633             gst_flow_get_name (ret));
2634       } else {
2635         GST_LOG_OBJECT (parse,
2636             "frame (%" G_GSIZE_FORMAT " bytes) not pushed: %s", size,
2637             gst_flow_get_name (ret));
2638         gst_buffer_unref (buffer);
2639       }
2640 
2641     } else {
2642       GST_LOG_OBJECT (parse, "frame (%" G_GSIZE_FORMAT " bytes) queued for now",
2643           size);
2644       parse->priv->buffers_queued =
2645           g_slist_prepend (parse->priv->buffers_queued, buffer);
2646       ret = GST_FLOW_OK;
2647     }
2648   } else {
2649     GST_LOG_OBJECT (parse, "frame (%" G_GSIZE_FORMAT " bytes) not pushed: %s",
2650         size, gst_flow_get_name (ret));
2651     gst_buffer_unref (buffer);
2652     /* if we are not sufficiently in control, let upstream decide on EOS */
2653     if (ret == GST_FLOW_EOS && !parse->priv->disable_passthrough &&
2654         (parse->priv->passthrough ||
2655             (parse->priv->pad_mode == GST_PAD_MODE_PUSH &&
2656                 !parse->priv->upstream_seekable)))
2657       ret = GST_FLOW_OK;
2658   }
2659 
2660   /* Update current running segment position */
2661   if ((ret == GST_FLOW_OK || ret == GST_FLOW_NOT_LINKED)
2662       && last_stop != GST_CLOCK_TIME_NONE
2663       && parse->segment.position < last_stop)
2664     parse->segment.position = last_stop;
2665 
2666   return ret;
2667 
2668   /* ERRORS */
2669 no_caps:
2670   {
2671     if (GST_PAD_IS_FLUSHING (parse->srcpad))
2672       return GST_FLOW_FLUSHING;
2673 
2674     GST_ELEMENT_ERROR (parse, STREAM, DECODE, ("No caps set"), (NULL));
2675     return GST_FLOW_ERROR;
2676   }
2677 }
2678 
2679 /**
2680  * gst_base_parse_finish_frame:
2681  * @parse: a #GstBaseParse
2682  * @frame: a #GstBaseParseFrame
2683  * @size: consumed input data represented by frame
2684  *
2685  * Collects parsed data and pushes this downstream.
2686  * Source pad caps must be set when this is called.
2687  *
2688  * If @frame's out_buffer is set, that will be used as subsequent frame data.
2689  * Otherwise, @size samples will be taken from the input and used for output,
2690  * and the output's metadata (timestamps etc) will be taken as (optionally)
2691  * set by the subclass on @frame's (input) buffer (which is otherwise
2692  * ignored for any but the above purpose/information).
2693  *
2694  * Note that the latter buffer is invalidated by this call, whereas the
2695  * caller retains ownership of @frame.
2696  *
2697  * Returns: a #GstFlowReturn that should be escalated to caller (of caller)
2698  */
2699 GstFlowReturn
gst_base_parse_finish_frame(GstBaseParse * parse,GstBaseParseFrame * frame,gint size)2700 gst_base_parse_finish_frame (GstBaseParse * parse, GstBaseParseFrame * frame,
2701     gint size)
2702 {
2703   GstFlowReturn ret = GST_FLOW_OK;
2704   const GstProtectionMeta *prot_meta_buf = NULL;
2705   const GstProtectionMeta *prot_meta_tmp = NULL;
2706   GstStructure *crypto_info = NULL;
2707   g_return_val_if_fail (frame != NULL, GST_FLOW_ERROR);
2708   g_return_val_if_fail (frame->buffer != NULL, GST_FLOW_ERROR);
2709   g_return_val_if_fail (size > 0 || frame->out_buffer, GST_FLOW_ERROR);
2710   g_return_val_if_fail (gst_adapter_available (parse->priv->adapter) >= size,
2711       GST_FLOW_ERROR);
2712 
2713   GST_LOG_OBJECT (parse, "finished frame at offset %" G_GUINT64_FORMAT ", "
2714       "flushing size %d", frame->offset, size);
2715   prot_meta_buf = (GstProtectionMeta*) gst_buffer_get_protection_meta (frame->buffer);
2716   if (prot_meta_buf != NULL) {
2717     crypto_info = gst_structure_copy (prot_meta_buf->info);
2718   }
2719   /* some one-time start-up */
2720   if (G_UNLIKELY (parse->priv->framecount == 0)) {
2721     gst_base_parse_check_seekability (parse);
2722     gst_base_parse_check_upstream (parse);
2723   }
2724 
2725   parse->priv->flushed += size;
2726 
2727   if (parse->priv->scanning && frame->buffer) {
2728     if (!parse->priv->scanned_frame) {
2729       parse->priv->scanned_frame = gst_base_parse_frame_copy (frame);
2730     }
2731     goto exit;
2732   }
2733 
2734   /* either PUSH or PULL mode arranges for adapter data */
2735   /* ensure output buffer */
2736   if (!frame->out_buffer) {
2737     GstBuffer *src, *dest;
2738 
2739     frame->out_buffer = gst_adapter_take_buffer (parse->priv->adapter, size);
2740     dest = frame->out_buffer;
2741     src = frame->buffer;
2742     GST_BUFFER_PTS (dest) = GST_BUFFER_PTS (src);
2743     GST_BUFFER_DTS (dest) = GST_BUFFER_DTS (src);
2744     GST_BUFFER_OFFSET (dest) = GST_BUFFER_OFFSET (src);
2745     GST_BUFFER_DURATION (dest) = GST_BUFFER_DURATION (src);
2746     GST_BUFFER_OFFSET_END (dest) = GST_BUFFER_OFFSET_END (src);
2747     GST_MINI_OBJECT_FLAGS (dest) = GST_MINI_OBJECT_FLAGS (src);
2748   } else {
2749     gst_adapter_flush (parse->priv->adapter, size);
2750   }
2751 
2752   /* use as input for subsequent processing */
2753   gst_buffer_replace (&frame->buffer, frame->out_buffer);
2754   gst_buffer_unref (frame->out_buffer);
2755   frame->out_buffer = NULL;
2756   prot_meta_tmp = (GstProtectionMeta*) gst_buffer_get_protection_meta (frame->buffer);
2757   if (prot_meta_tmp == NULL) {
2758     if (prot_meta_buf != NULL) {
2759       gst_buffer_add_protection_meta (frame->buffer, crypto_info);
2760     }
2761   }
2762 
2763   /* mark input size consumed */
2764   frame->size = size;
2765 
2766   /* subclass might queue frames/data internally if it needs more
2767    * frames to decide on the format, or might request us to queue here. */
2768   if (frame->flags & GST_BASE_PARSE_FRAME_FLAG_DROP) {
2769     gst_buffer_replace (&frame->buffer, NULL);
2770     goto exit;
2771   } else if (frame->flags & GST_BASE_PARSE_FRAME_FLAG_QUEUE) {
2772     GstBaseParseFrame *copy;
2773 
2774     copy = gst_base_parse_frame_copy (frame);
2775     copy->flags &= ~GST_BASE_PARSE_FRAME_FLAG_QUEUE;
2776     gst_base_parse_queue_frame (parse, copy);
2777     goto exit;
2778   }
2779 
2780   ret = gst_base_parse_handle_and_push_frame (parse, frame);
2781 
2782 exit:
2783   return ret;
2784 }
2785 
2786 /**
2787  * gst_base_parse_drain:
2788  * @parse: a #GstBaseParse
2789  *
2790  * Drains the adapter until it is empty. It decreases the min_frame_size to
2791  * match the current adapter size and calls chain method until the adapter
2792  * is emptied or chain returns with error.
2793  *
2794  * Since: 1.12
2795  */
2796 void
gst_base_parse_drain(GstBaseParse * parse)2797 gst_base_parse_drain (GstBaseParse * parse)
2798 {
2799   guint avail;
2800 
2801   GST_DEBUG_OBJECT (parse, "draining");
2802   parse->priv->drain = TRUE;
2803 
2804   for (;;) {
2805     avail = gst_adapter_available (parse->priv->adapter);
2806     if (!avail)
2807       break;
2808 
2809     if (gst_base_parse_chain (parse->sinkpad, GST_OBJECT_CAST (parse),
2810             NULL) != GST_FLOW_OK) {
2811       break;
2812     }
2813 
2814     /* nothing changed, maybe due to truncated frame; break infinite loop */
2815     if (avail == gst_adapter_available (parse->priv->adapter)) {
2816       GST_DEBUG_OBJECT (parse, "no change during draining; flushing");
2817       gst_adapter_clear (parse->priv->adapter);
2818     }
2819   }
2820 
2821   parse->priv->drain = FALSE;
2822 }
2823 
2824 /* gst_base_parse_send_buffers
2825  *
2826  * Sends buffers collected in send_buffers downstream, and ensures that list
2827  * is empty at the end (errors or not).
2828  */
2829 static GstFlowReturn
gst_base_parse_send_buffers(GstBaseParse * parse)2830 gst_base_parse_send_buffers (GstBaseParse * parse)
2831 {
2832   GSList *send = NULL;
2833   GstBuffer *buf;
2834   GstFlowReturn ret = GST_FLOW_OK;
2835   gboolean first = TRUE;
2836 
2837   send = parse->priv->buffers_send;
2838 
2839   /* send buffers */
2840   while (send) {
2841     buf = GST_BUFFER_CAST (send->data);
2842     GST_LOG_OBJECT (parse, "pushing buffer %p, dts %"
2843         GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT
2844         ", offset %" G_GINT64_FORMAT, buf,
2845         GST_TIME_ARGS (GST_BUFFER_DTS (buf)),
2846         GST_TIME_ARGS (GST_BUFFER_PTS (buf)),
2847         GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_BUFFER_OFFSET (buf));
2848 
2849     /* Make sure the first buffer is always DISCONT. If we split
2850      * GOPs inside the parser this is otherwise not guaranteed */
2851     if (first) {
2852       GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
2853       first = FALSE;
2854     } else {
2855       /* likewise, subsequent buffers should never have DISCONT
2856        * according to the "reverse fragment protocol", or such would
2857        * confuse a downstream decoder
2858        * (could be DISCONT due to aggregating upstream fragments by parsing) */
2859       GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
2860     }
2861 
2862     /* iterate output queue an push downstream */
2863     ret = gst_pad_push (parse->srcpad, buf);
2864     send = g_slist_delete_link (send, send);
2865 
2866     /* clear any leftover if error */
2867     if (G_UNLIKELY (ret != GST_FLOW_OK)) {
2868       while (send) {
2869         buf = GST_BUFFER_CAST (send->data);
2870         gst_buffer_unref (buf);
2871         send = g_slist_delete_link (send, send);
2872       }
2873     }
2874   }
2875 
2876   parse->priv->buffers_send = send;
2877 
2878   return ret;
2879 }
2880 
2881 /* gst_base_parse_start_fragment:
2882  *
2883  * Prepares for processing a reverse playback (forward) fragment
2884  * by (re)setting proper state variables.
2885  */
2886 static GstFlowReturn
gst_base_parse_start_fragment(GstBaseParse * parse)2887 gst_base_parse_start_fragment (GstBaseParse * parse)
2888 {
2889   GST_LOG_OBJECT (parse, "starting fragment");
2890 
2891   /* invalidate so no fall-back timestamping is performed;
2892    * ok if taken from subclass or upstream */
2893   parse->priv->next_pts = GST_CLOCK_TIME_NONE;
2894   parse->priv->prev_pts = GST_CLOCK_TIME_NONE;
2895   parse->priv->next_dts = GST_CLOCK_TIME_NONE;
2896   parse->priv->prev_dts = GST_CLOCK_TIME_NONE;
2897   parse->priv->prev_dts_from_pts = FALSE;
2898   /* prevent it hanging around stop all the time */
2899   parse->segment.position = GST_CLOCK_TIME_NONE;
2900   /* mark next run */
2901   parse->priv->discont = TRUE;
2902 
2903   /* head of previous fragment is now pending tail of current fragment */
2904   parse->priv->buffers_pending = parse->priv->buffers_head;
2905   parse->priv->buffers_head = NULL;
2906 
2907   return GST_FLOW_OK;
2908 }
2909 
2910 
2911 /* gst_base_parse_finish_fragment:
2912  *
2913  * Processes a reverse playback (forward) fragment:
2914  * - append head of last fragment that was skipped to current fragment data
2915  * - drain the resulting current fragment data (i.e. repeated chain)
2916  * - add time/duration (if needed) to frames queued by chain
2917  * - push queued data
2918  */
2919 static GstFlowReturn
gst_base_parse_finish_fragment(GstBaseParse * parse,gboolean prev_head)2920 gst_base_parse_finish_fragment (GstBaseParse * parse, gboolean prev_head)
2921 {
2922   GstBuffer *buf;
2923   GstFlowReturn ret = GST_FLOW_OK;
2924   gboolean seen_key = FALSE, seen_delta = FALSE;
2925 
2926   GST_LOG_OBJECT (parse, "finishing fragment");
2927 
2928   /* restore order */
2929   parse->priv->buffers_pending = g_slist_reverse (parse->priv->buffers_pending);
2930   while (parse->priv->buffers_pending) {
2931     buf = GST_BUFFER_CAST (parse->priv->buffers_pending->data);
2932     if (prev_head) {
2933       GST_LOG_OBJECT (parse, "adding pending buffer (size %" G_GSIZE_FORMAT ")",
2934           gst_buffer_get_size (buf));
2935       gst_adapter_push (parse->priv->adapter, buf);
2936     } else {
2937       GST_LOG_OBJECT (parse, "discarding head buffer");
2938       gst_buffer_unref (buf);
2939     }
2940     parse->priv->buffers_pending =
2941         g_slist_delete_link (parse->priv->buffers_pending,
2942         parse->priv->buffers_pending);
2943   }
2944 
2945   /* chain looks for frames and queues resulting ones (in stead of pushing) */
2946   /* initial skipped data is added to buffers_pending */
2947   gst_base_parse_drain (parse);
2948 
2949   if (parse->priv->buffers_send) {
2950     buf = GST_BUFFER_CAST (parse->priv->buffers_send->data);
2951     seen_key |= !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
2952   }
2953 
2954   /* add metadata (if needed to queued buffers */
2955   GST_LOG_OBJECT (parse, "last timestamp: %" GST_TIME_FORMAT,
2956       GST_TIME_ARGS (parse->priv->last_pts));
2957   while (parse->priv->buffers_queued) {
2958     buf = GST_BUFFER_CAST (parse->priv->buffers_queued->data);
2959 
2960     /* no touching if upstream or parsing provided time */
2961     if (GST_BUFFER_PTS_IS_VALID (buf)) {
2962       GST_LOG_OBJECT (parse, "buffer has time %" GST_TIME_FORMAT,
2963           GST_TIME_ARGS (GST_BUFFER_PTS (buf)));
2964     } else if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2965       if (GST_CLOCK_TIME_IS_VALID (parse->priv->last_pts)) {
2966         if (G_LIKELY (GST_BUFFER_DURATION (buf) <= parse->priv->last_pts))
2967           parse->priv->last_pts -= GST_BUFFER_DURATION (buf);
2968         else
2969           parse->priv->last_pts = 0;
2970         GST_BUFFER_PTS (buf) = parse->priv->last_pts;
2971         GST_LOG_OBJECT (parse, "applied time %" GST_TIME_FORMAT,
2972             GST_TIME_ARGS (GST_BUFFER_PTS (buf)));
2973       }
2974       if (GST_CLOCK_TIME_IS_VALID (parse->priv->last_dts)) {
2975         if (G_LIKELY (GST_BUFFER_DURATION (buf) <= parse->priv->last_dts))
2976           parse->priv->last_dts -= GST_BUFFER_DURATION (buf);
2977         else
2978           parse->priv->last_dts = 0;
2979         GST_BUFFER_DTS (buf) = parse->priv->last_dts;
2980         GST_LOG_OBJECT (parse, "applied dts %" GST_TIME_FORMAT,
2981             GST_TIME_ARGS (GST_BUFFER_DTS (buf)));
2982       }
2983     } else {
2984       /* no idea, very bad */
2985       GST_WARNING_OBJECT (parse, "could not determine time for buffer");
2986     }
2987 
2988     parse->priv->last_pts = GST_BUFFER_PTS (buf);
2989     parse->priv->last_dts = GST_BUFFER_DTS (buf);
2990 
2991     /* reverse order for ascending sending */
2992     /* send downstream at keyframe not preceded by a keyframe
2993      * (e.g. that should identify start of collection of IDR nals) */
2994     if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2995       if (seen_key) {
2996         ret = gst_base_parse_send_buffers (parse);
2997         /* if a problem, throw all to sending */
2998         if (ret != GST_FLOW_OK) {
2999           parse->priv->buffers_send =
3000               g_slist_reverse (parse->priv->buffers_queued);
3001           parse->priv->buffers_queued = NULL;
3002           break;
3003         }
3004         seen_key = FALSE;
3005       }
3006       seen_delta = TRUE;
3007     } else {
3008       seen_key = TRUE;
3009     }
3010 
3011     parse->priv->buffers_send =
3012         g_slist_prepend (parse->priv->buffers_send, buf);
3013     parse->priv->buffers_queued =
3014         g_slist_delete_link (parse->priv->buffers_queued,
3015         parse->priv->buffers_queued);
3016   }
3017 
3018   /* audio may have all marked as keyframe, so arrange to send here. Also
3019    * we might have ended the loop above on a keyframe, in which case we
3020    * should */
3021   if (!seen_delta || seen_key)
3022     ret = gst_base_parse_send_buffers (parse);
3023 
3024   /* any trailing unused no longer usable (ideally none) */
3025   if (G_UNLIKELY (gst_adapter_available (parse->priv->adapter))) {
3026     GST_DEBUG_OBJECT (parse, "discarding %" G_GSIZE_FORMAT " trailing bytes",
3027         gst_adapter_available (parse->priv->adapter));
3028     gst_adapter_clear (parse->priv->adapter);
3029   }
3030 
3031   return ret;
3032 }
3033 
3034 /* small helper that checks whether we have been trying to resync too long */
3035 static inline GstFlowReturn
gst_base_parse_check_sync(GstBaseParse * parse)3036 gst_base_parse_check_sync (GstBaseParse * parse)
3037 {
3038   if (G_UNLIKELY (parse->priv->discont &&
3039           parse->priv->offset - parse->priv->sync_offset > 2 * 1024 * 1024)) {
3040     GST_ELEMENT_ERROR (parse, STREAM, DECODE,
3041         ("Failed to parse stream"), (NULL));
3042     return GST_FLOW_ERROR;
3043   }
3044 
3045   return GST_FLOW_OK;
3046 }
3047 
3048 static GstFlowReturn
gst_base_parse_process_streamheader(GstBaseParse * parse)3049 gst_base_parse_process_streamheader (GstBaseParse * parse)
3050 {
3051   GstCaps *caps;
3052   GstStructure *str;
3053   const GValue *value;
3054   GstFlowReturn ret = GST_FLOW_OK;
3055 
3056   caps = gst_pad_get_current_caps (GST_BASE_PARSE_SINK_PAD (parse));
3057   if (caps == NULL)
3058     goto notfound;
3059 
3060   str = gst_caps_get_structure (caps, 0);
3061   value = gst_structure_get_value (str, "streamheader");
3062   if (value == NULL)
3063     goto notfound;
3064 
3065   GST_DEBUG_OBJECT (parse, "Found streamheader field on input caps");
3066 
3067   if (GST_VALUE_HOLDS_ARRAY (value)) {
3068     gint i;
3069     gsize len = gst_value_array_get_size (value);
3070 
3071     for (i = 0; i < len; i++) {
3072       GstBuffer *buffer =
3073           gst_value_get_buffer (gst_value_array_get_value (value, i));
3074       ret =
3075           gst_base_parse_chain (GST_BASE_PARSE_SINK_PAD (parse),
3076           GST_OBJECT_CAST (parse), gst_buffer_ref (buffer));
3077     }
3078 
3079   } else if (GST_VALUE_HOLDS_BUFFER (value)) {
3080     GstBuffer *buffer = gst_value_get_buffer (value);
3081     ret =
3082         gst_base_parse_chain (GST_BASE_PARSE_SINK_PAD (parse),
3083         GST_OBJECT_CAST (parse), gst_buffer_ref (buffer));
3084   }
3085 
3086   gst_caps_unref (caps);
3087 
3088   return ret;
3089 
3090 notfound:
3091   {
3092     if (caps) {
3093       gst_caps_unref (caps);
3094     }
3095 
3096     GST_DEBUG_OBJECT (parse, "No streamheader on caps");
3097     return GST_FLOW_OK;
3098   }
3099 }
3100 
3101 #ifdef OHOS_OPT_PERFORMANCE
3102 // ohos.opt.performance.0005
3103 // add trace
3104 static GstFlowReturn
gst_base_parse_chain_trace(GstPad * pad,GstObject * parent,GstBuffer * buffer)3105 gst_base_parse_chain_trace (GstPad * pad, GstObject * parent, GstBuffer * buffer)
3106 {
3107   GstStartTrace("Parse:chain");
3108   GstFlowReturn ret = gst_base_parse_chain (pad, parent, buffer);
3109   GstFinishTrace();
3110   return ret;
3111 }
3112 #endif
3113 static GstFlowReturn
gst_base_parse_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer)3114 gst_base_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
3115 {
3116   GstBaseParseClass *bclass;
3117   GstBaseParse *parse;
3118   GstFlowReturn ret = GST_FLOW_OK;
3119   GstFlowReturn old_ret = GST_FLOW_OK;
3120   GstBuffer *tmpbuf = NULL;
3121   guint fsize = 1;
3122   gint skip = -1;
3123   guint min_size, av;
3124   GstClockTime pts, dts;
3125 
3126   parse = GST_BASE_PARSE (parent);
3127   bclass = GST_BASE_PARSE_GET_CLASS (parse);
3128   GST_DEBUG_OBJECT (parent, "chain");
3129 
3130   /* early out for speed, if we need to skip */
3131   if (buffer && GST_BUFFER_IS_DISCONT (buffer))
3132     parse->priv->skip = 0;
3133   if (parse->priv->skip > 0) {
3134     gsize bsize = gst_buffer_get_size (buffer);
3135     GST_DEBUG ("Got %" G_GSIZE_FORMAT " buffer, need to skip %u", bsize,
3136         parse->priv->skip);
3137     if (parse->priv->skip >= bsize) {
3138       parse->priv->skip -= bsize;
3139       GST_DEBUG ("All the buffer is skipped");
3140       parse->priv->offset += bsize;
3141       parse->priv->sync_offset = parse->priv->offset;
3142       gst_buffer_unref (buffer);
3143       return GST_FLOW_OK;
3144     }
3145     buffer = gst_buffer_make_writable (buffer);
3146     gst_buffer_resize (buffer, parse->priv->skip, bsize - parse->priv->skip);
3147     parse->priv->offset += parse->priv->skip;
3148     GST_DEBUG ("Done skipping, we have %u left on this buffer",
3149         (unsigned) (bsize - parse->priv->skip));
3150     parse->priv->skip = 0;
3151     parse->priv->discont = TRUE;
3152   }
3153 
3154   if (G_UNLIKELY (parse->priv->first_buffer)) {
3155     parse->priv->first_buffer = FALSE;
3156     if (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_HEADER)) {
3157       /* this stream has no header buffers, check if we just prepend the
3158        * streamheader from caps to the stream */
3159       GST_DEBUG_OBJECT (parse, "Looking for streamheader field on caps to "
3160           "prepend to the stream");
3161       gst_base_parse_process_streamheader (parse);
3162     } else {
3163       GST_DEBUG_OBJECT (parse, "Stream has header buffers, not prepending "
3164           "streamheader from caps");
3165     }
3166   }
3167 
3168   if (parse->priv->detecting) {
3169     GstBuffer *detect_buf;
3170 
3171     if (parse->priv->detect_buffers_size == 0) {
3172       detect_buf = gst_buffer_ref (buffer);
3173     } else {
3174       GList *l;
3175       guint offset = 0;
3176 
3177       detect_buf = gst_buffer_new ();
3178 
3179       for (l = parse->priv->detect_buffers; l; l = l->next) {
3180         gsize tmpsize = gst_buffer_get_size (l->data);
3181 
3182         gst_buffer_copy_into (detect_buf, GST_BUFFER_CAST (l->data),
3183             GST_BUFFER_COPY_MEMORY, offset, tmpsize);
3184         offset += tmpsize;
3185       }
3186       if (buffer)
3187         gst_buffer_copy_into (detect_buf, buffer, GST_BUFFER_COPY_MEMORY,
3188             offset, gst_buffer_get_size (buffer));
3189     }
3190 
3191     ret = bclass->detect (parse, detect_buf);
3192     gst_buffer_unref (detect_buf);
3193 
3194     if (ret == GST_FLOW_OK) {
3195       GList *l;
3196 
3197       /* Detected something */
3198       parse->priv->detecting = FALSE;
3199 
3200       for (l = parse->priv->detect_buffers; l; l = l->next) {
3201         if (ret == GST_FLOW_OK && !parse->priv->flushing)
3202           ret =
3203               gst_base_parse_chain (GST_BASE_PARSE_SINK_PAD (parse),
3204               parent, GST_BUFFER_CAST (l->data));
3205         else
3206           gst_buffer_unref (GST_BUFFER_CAST (l->data));
3207       }
3208       g_list_free (parse->priv->detect_buffers);
3209       parse->priv->detect_buffers = NULL;
3210       parse->priv->detect_buffers_size = 0;
3211 
3212       if (ret != GST_FLOW_OK) {
3213         return ret;
3214       }
3215 
3216       /* Handle the current buffer */
3217     } else if (ret == GST_FLOW_NOT_NEGOTIATED) {
3218       /* Still detecting, append buffer or error out if draining */
3219 
3220       if (parse->priv->drain) {
3221         GST_DEBUG_OBJECT (parse, "Draining but did not detect format yet");
3222         return GST_FLOW_ERROR;
3223       } else if (parse->priv->flushing) {
3224         g_list_foreach (parse->priv->detect_buffers, (GFunc) gst_buffer_unref,
3225             NULL);
3226         g_list_free (parse->priv->detect_buffers);
3227         parse->priv->detect_buffers = NULL;
3228         parse->priv->detect_buffers_size = 0;
3229       } else {
3230         parse->priv->detect_buffers =
3231             g_list_append (parse->priv->detect_buffers, buffer);
3232         parse->priv->detect_buffers_size += gst_buffer_get_size (buffer);
3233         return GST_FLOW_OK;
3234       }
3235     } else {
3236       /* Something went wrong, subclass responsible for error reporting */
3237       return ret;
3238     }
3239 
3240     /* And now handle the current buffer if detection worked */
3241   }
3242 
3243   if (G_LIKELY (buffer)) {
3244     GST_LOG_OBJECT (parse,
3245         "buffer size: %" G_GSIZE_FORMAT ", offset = %" G_GINT64_FORMAT
3246         ", dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT,
3247         gst_buffer_get_size (buffer), GST_BUFFER_OFFSET (buffer),
3248         GST_TIME_ARGS (GST_BUFFER_DTS (buffer)),
3249         GST_TIME_ARGS (GST_BUFFER_PTS (buffer)));
3250 
3251     if (G_UNLIKELY (!parse->priv->disable_passthrough
3252             && parse->priv->passthrough)) {
3253       GstBaseParseFrame frame;
3254 
3255       gst_base_parse_frame_init (&frame);
3256       frame.buffer = gst_buffer_make_writable (buffer);
3257       ret = gst_base_parse_push_frame (parse, &frame);
3258       gst_base_parse_frame_free (&frame);
3259       return ret;
3260     }
3261     if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT))) {
3262       /* upstream feeding us in reverse playback;
3263        * finish previous fragment and start new upon DISCONT */
3264       if (parse->segment.rate < 0.0) {
3265         GST_DEBUG_OBJECT (parse, "buffer starts new reverse playback fragment");
3266         ret = gst_base_parse_finish_fragment (parse, TRUE);
3267         gst_base_parse_start_fragment (parse);
3268       } else {
3269         /* discont in the stream, drain and mark discont for next output */
3270         gst_base_parse_drain (parse);
3271         parse->priv->discont = TRUE;
3272       }
3273     }
3274     gst_adapter_push (parse->priv->adapter, buffer);
3275   }
3276 
3277   /* Parse and push as many frames as possible */
3278   /* Stop either when adapter is empty or we are flushing */
3279   while (!parse->priv->flushing) {
3280     gint flush = 0;
3281     gboolean updated_prev_pts = FALSE;
3282 
3283     /* note: if subclass indicates MAX fsize,
3284      * this will not likely be available anyway ... */
3285     min_size = MAX (parse->priv->min_frame_size, fsize);
3286     av = gst_adapter_available (parse->priv->adapter);
3287 
3288     if (G_UNLIKELY (parse->priv->drain)) {
3289       min_size = av;
3290       GST_DEBUG_OBJECT (parse, "draining, data left: %d", min_size);
3291       if (G_UNLIKELY (!min_size)) {
3292         goto done;
3293       }
3294     }
3295 
3296     /* Collect at least min_frame_size bytes */
3297     if (av < min_size) {
3298       GST_DEBUG_OBJECT (parse, "not enough data available (only %d bytes)", av);
3299       goto done;
3300     }
3301 
3302     /* move along with upstream timestamp (if any),
3303      * but interpolate in between */
3304     pts = gst_adapter_prev_pts (parse->priv->adapter, NULL);
3305     dts = gst_adapter_prev_dts (parse->priv->adapter, NULL);
3306     if (GST_CLOCK_TIME_IS_VALID (pts) && (parse->priv->prev_pts != pts)) {
3307       parse->priv->prev_pts = parse->priv->next_pts = pts;
3308       updated_prev_pts = TRUE;
3309     }
3310 
3311     if (GST_CLOCK_TIME_IS_VALID (dts) && (parse->priv->prev_dts != dts)) {
3312       parse->priv->prev_dts = parse->priv->next_dts = dts;
3313       parse->priv->prev_dts_from_pts = FALSE;
3314     }
3315 
3316     /* we can mess with, erm interpolate, timestamps,
3317      * and incoming stuff has PTS but no DTS seen so far,
3318      * then pick up DTS from PTS and hope for the best ... */
3319     if (parse->priv->infer_ts &&
3320         parse->priv->pts_interpolate &&
3321         !GST_CLOCK_TIME_IS_VALID (dts) &&
3322         (!GST_CLOCK_TIME_IS_VALID (parse->priv->prev_dts)
3323             || (parse->priv->prev_dts_from_pts && updated_prev_pts))
3324         && GST_CLOCK_TIME_IS_VALID (pts)) {
3325       parse->priv->prev_dts = parse->priv->next_dts = pts;
3326       parse->priv->prev_dts_from_pts = TRUE;
3327     }
3328 
3329     /* always pass all available data */
3330     tmpbuf = gst_adapter_get_buffer (parse->priv->adapter, av);
3331 
3332     /* already inform subclass what timestamps we have planned,
3333      * at least if provided by time-based upstream */
3334     if (parse->priv->upstream_format == GST_FORMAT_TIME) {
3335       tmpbuf = gst_buffer_make_writable (tmpbuf);
3336       GST_BUFFER_PTS (tmpbuf) = parse->priv->next_pts;
3337       GST_BUFFER_DTS (tmpbuf) = parse->priv->next_dts;
3338       GST_BUFFER_DURATION (tmpbuf) = GST_CLOCK_TIME_NONE;
3339     }
3340 
3341     /* keep the adapter mapped, so keep track of what has to be flushed */
3342     ret = gst_base_parse_handle_buffer (parse, tmpbuf, &skip, &flush);
3343     tmpbuf = NULL;
3344 
3345     if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) {
3346       goto done;
3347     }
3348     if (skip == 0 && flush == 0) {
3349       GST_LOG_OBJECT (parse, "nothing skipped and no frames finished, "
3350           "breaking to get more data");
3351       /* ignore this return as it produced no data */
3352       ret = old_ret;
3353       goto done;
3354     }
3355     if (old_ret == GST_FLOW_OK)
3356       old_ret = ret;
3357   }
3358 
3359 done:
3360   GST_LOG_OBJECT (parse, "chain leaving");
3361   return ret;
3362 }
3363 
3364 /* Return the number of bytes available in the cached
3365  * read buffer, if any */
3366 static guint
gst_base_parse_get_cached_available(GstBaseParse * parse)3367 gst_base_parse_get_cached_available (GstBaseParse * parse)
3368 {
3369   if (parse->priv->cache != NULL) {
3370     gint64 cache_offset = GST_BUFFER_OFFSET (parse->priv->cache);
3371     gint cache_size = gst_buffer_get_size (parse->priv->cache);
3372 
3373     if (parse->priv->offset >= cache_offset
3374         && parse->priv->offset < cache_offset + cache_size)
3375       return cache_size - (parse->priv->offset - cache_offset); /* Size of the cache minus consumed */
3376   }
3377   return 0;
3378 }
3379 
3380 /* pull @size bytes at current offset,
3381  * i.e. at least try to and possibly return a shorter buffer if near the end */
3382 static GstFlowReturn
gst_base_parse_pull_range(GstBaseParse * parse,guint size,GstBuffer ** buffer)3383 gst_base_parse_pull_range (GstBaseParse * parse, guint size,
3384     GstBuffer ** buffer)
3385 {
3386   GstFlowReturn ret = GST_FLOW_OK;
3387   guint read_size;
3388 
3389   g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR);
3390 
3391   /* Caching here actually makes much less difference than one would expect.
3392    * We do it mainly to avoid pulling buffers of 1 byte all the time */
3393   if (parse->priv->cache) {
3394     gint64 cache_offset = GST_BUFFER_OFFSET (parse->priv->cache);
3395     gint cache_size = gst_buffer_get_size (parse->priv->cache);
3396 
3397     if (cache_offset <= parse->priv->offset &&
3398         (parse->priv->offset + size) <= (cache_offset + cache_size)) {
3399       *buffer = gst_buffer_copy_region (parse->priv->cache, GST_BUFFER_COPY_ALL,
3400           parse->priv->offset - cache_offset, size);
3401       GST_BUFFER_OFFSET (*buffer) = parse->priv->offset;
3402       GST_LOG_OBJECT (parse,
3403           "Satisfying read request of %u bytes from cached buffer with offset %"
3404           G_GINT64_FORMAT, size, cache_offset);
3405       return GST_FLOW_OK;
3406     }
3407     /* not enough data in the cache, free cache and get a new one */
3408     gst_buffer_unref (parse->priv->cache);
3409     parse->priv->cache = NULL;
3410   }
3411 
3412   /* refill the cache */
3413   read_size = MAX (64 * 1024, size);
3414   GST_LOG_OBJECT (parse,
3415       "Reading cache buffer of %u bytes from offset %" G_GINT64_FORMAT,
3416       read_size, parse->priv->offset);
3417   ret =
3418       gst_pad_pull_range (parse->sinkpad, parse->priv->offset, read_size,
3419       &parse->priv->cache);
3420   if (ret != GST_FLOW_OK) {
3421     parse->priv->cache = NULL;
3422     return ret;
3423   }
3424 
3425   if (gst_buffer_get_size (parse->priv->cache) < size) {
3426     GST_DEBUG_OBJECT (parse, "Returning short buffer at offset %"
3427         G_GUINT64_FORMAT ": wanted %u bytes, got %" G_GSIZE_FORMAT " bytes",
3428         parse->priv->offset, size, gst_buffer_get_size (parse->priv->cache));
3429 
3430     *buffer = parse->priv->cache;
3431     parse->priv->cache = NULL;
3432 
3433     return GST_FLOW_OK;
3434   }
3435 
3436   GST_BUFFER_OFFSET (parse->priv->cache) = parse->priv->offset;
3437 
3438   *buffer =
3439       gst_buffer_copy_region (parse->priv->cache, GST_BUFFER_COPY_ALL, 0, size);
3440   GST_BUFFER_OFFSET (*buffer) = parse->priv->offset;
3441 
3442   return GST_FLOW_OK;
3443 }
3444 
3445 static GstFlowReturn
gst_base_parse_handle_previous_fragment(GstBaseParse * parse)3446 gst_base_parse_handle_previous_fragment (GstBaseParse * parse)
3447 {
3448   gint64 offset = 0;
3449   GstClockTime ts = 0;
3450   GstBuffer *buffer;
3451   GstFlowReturn ret;
3452 
3453   GST_DEBUG_OBJECT (parse, "fragment ended; last_ts = %" GST_TIME_FORMAT
3454       ", last_offset = %" G_GINT64_FORMAT,
3455       GST_TIME_ARGS (parse->priv->last_pts), parse->priv->last_offset);
3456 
3457   if (!parse->priv->last_offset
3458       || parse->priv->last_pts <= parse->segment.start) {
3459     GST_DEBUG_OBJECT (parse, "past start of segment %" GST_TIME_FORMAT,
3460         GST_TIME_ARGS (parse->segment.start));
3461     ret = GST_FLOW_EOS;
3462     goto exit;
3463   }
3464 
3465   /* last fragment started at last_offset / last_ts;
3466    * seek back 10s capped at 1MB */
3467   if (parse->priv->last_pts >= 10 * GST_SECOND)
3468     ts = parse->priv->last_pts - 10 * GST_SECOND;
3469   /* if we are exact now, we will be more so going backwards */
3470   if (parse->priv->exact_position) {
3471     offset = gst_base_parse_find_offset (parse, ts, TRUE, NULL);
3472   } else {
3473     if (!gst_base_parse_convert (parse, GST_FORMAT_TIME, ts,
3474             GST_FORMAT_BYTES, &offset)) {
3475       GST_DEBUG_OBJECT (parse, "conversion failed, only BYTE based");
3476     }
3477   }
3478   offset = CLAMP (offset, parse->priv->last_offset - 1024 * 1024,
3479       parse->priv->last_offset - 1024);
3480   offset = MAX (0, offset);
3481 
3482   GST_DEBUG_OBJECT (parse, "next fragment from offset %" G_GINT64_FORMAT,
3483       offset);
3484   parse->priv->offset = offset;
3485 
3486   ret = gst_base_parse_pull_range (parse, parse->priv->last_offset - offset,
3487       &buffer);
3488   if (ret != GST_FLOW_OK)
3489     goto exit;
3490 
3491   /* offset will increase again as fragment is processed/parsed */
3492   parse->priv->last_offset = offset;
3493 
3494   gst_base_parse_start_fragment (parse);
3495   gst_adapter_push (parse->priv->adapter, buffer);
3496   ret = gst_base_parse_finish_fragment (parse, TRUE);
3497   if (ret != GST_FLOW_OK)
3498     goto exit;
3499 
3500   /* force previous fragment */
3501   parse->priv->offset = -1;
3502 
3503 exit:
3504   return ret;
3505 }
3506 
3507 /* PULL mode:
3508  * pull and scan for next frame starting from current offset
3509  * adjusts sync, drain and offset going along */
3510 static GstFlowReturn
gst_base_parse_scan_frame(GstBaseParse * parse,GstBaseParseClass * klass)3511 gst_base_parse_scan_frame (GstBaseParse * parse, GstBaseParseClass * klass)
3512 {
3513   GstBuffer *buffer;
3514   GstFlowReturn ret = GST_FLOW_OK;
3515   guint fsize, min_size;
3516   gint flushed = 0;
3517   gint skip = 0;
3518 
3519   GST_LOG_OBJECT (parse, "scanning for frame at offset %" G_GUINT64_FORMAT
3520       " (%#" G_GINT64_MODIFIER "x)", parse->priv->offset, parse->priv->offset);
3521 
3522   /* let's make this efficient for all subclass once and for all;
3523    * maybe it does not need this much, but in the latter case, we know we are
3524    * in pull mode here and might as well try to read and supply more anyway,
3525    * so start with the cached buffer, or if that's shrunk below 1024 bytes,
3526    * pull a new cache buffer */
3527   fsize = gst_base_parse_get_cached_available (parse);
3528   if (fsize < 1024)
3529     fsize = 64 * 1024;
3530 
3531   while (TRUE) {
3532     min_size = MAX (parse->priv->min_frame_size, fsize);
3533 
3534     GST_LOG_OBJECT (parse, "reading buffer size %u", min_size);
3535 
3536     parse->priv->drain = FALSE;
3537     ret = gst_base_parse_pull_range (parse, min_size, &buffer);
3538     if (ret != GST_FLOW_OK)
3539       goto done;
3540 
3541     /* if we got a short read, inform subclass we are draining leftover
3542      * and no more is to be expected */
3543     if (gst_buffer_get_size (buffer) < min_size) {
3544       GST_LOG_OBJECT (parse, "... but did not get that; marked draining");
3545       parse->priv->drain = TRUE;
3546     }
3547 
3548     if (parse->priv->detecting) {
3549       ret = klass->detect (parse, buffer);
3550       if (ret == GST_FLOW_NOT_NEGOTIATED) {
3551         /* If draining we error out, otherwise request a buffer
3552          * with 64kb more */
3553         if (parse->priv->drain) {
3554           gst_buffer_unref (buffer);
3555           GST_ERROR_OBJECT (parse, "Failed to detect format but draining");
3556           return GST_FLOW_ERROR;
3557         } else {
3558           /* Double our frame size, or increment by at most 64KB */
3559           fsize += MIN (fsize, 64 * 1024);
3560           gst_buffer_unref (buffer);
3561           continue;
3562         }
3563       } else if (ret != GST_FLOW_OK) {
3564         gst_buffer_unref (buffer);
3565         GST_ERROR_OBJECT (parse, "detect() returned %s",
3566             gst_flow_get_name (ret));
3567         return ret;
3568       }
3569 
3570       /* Else handle this buffer normally */
3571     }
3572 
3573     ret = gst_base_parse_handle_buffer (parse, buffer, &skip, &flushed);
3574     if (ret != GST_FLOW_OK)
3575       break;
3576 
3577     /* If a large amount of data was requested to be skipped, _handle_buffer
3578        might have set the priv->skip flag to an extra amount on top of skip.
3579        In pull mode, we can just pull from the new offset directly. */
3580     parse->priv->offset += parse->priv->skip;
3581     parse->priv->skip = 0;
3582 
3583     /* something flushed means something happened,
3584      * and we should bail out of this loop so as not to occupy
3585      * the task thread indefinitely */
3586     if (flushed) {
3587       GST_LOG_OBJECT (parse, "frame finished, breaking loop");
3588       break;
3589     }
3590     if (!skip) {
3591       if (parse->priv->drain) {
3592         /* nothing flushed, no skip and draining, so nothing left to do */
3593         GST_LOG_OBJECT (parse, "no activity or result when draining; "
3594             "breaking loop and marking EOS");
3595         ret = GST_FLOW_EOS;
3596         break;
3597       }
3598       /* otherwise, get some more data
3599        * note that is checked this does not happen indefinitely */
3600       GST_LOG_OBJECT (parse, "getting some more data");
3601 
3602       /* Double our frame size, or increment by at most 64KB */
3603       fsize += MIN (fsize, 64 * 1024);
3604     }
3605   }
3606 
3607 done:
3608   return ret;
3609 }
3610 
3611 /* Loop that is used in pull mode to retrieve data from upstream */
3612 static void
gst_base_parse_loop(GstPad * pad)3613 gst_base_parse_loop (GstPad * pad)
3614 {
3615   GstBaseParse *parse;
3616   GstBaseParseClass *klass;
3617   GstFlowReturn ret = GST_FLOW_OK;
3618 
3619   parse = GST_BASE_PARSE (gst_pad_get_parent (pad));
3620   klass = GST_BASE_PARSE_GET_CLASS (parse);
3621 
3622   GST_LOG_OBJECT (parse, "Entering parse loop");
3623 
3624   if (G_UNLIKELY (parse->priv->push_stream_start)) {
3625     gchar *stream_id;
3626     GstEvent *event;
3627 
3628     stream_id =
3629         gst_pad_create_stream_id (parse->srcpad, GST_ELEMENT_CAST (parse),
3630         NULL);
3631 
3632     event = gst_event_new_stream_start (stream_id);
3633     gst_event_set_group_id (event, gst_util_group_id_next ());
3634 
3635     GST_DEBUG_OBJECT (parse, "Pushing STREAM_START");
3636     gst_pad_push_event (parse->srcpad, event);
3637     parse->priv->push_stream_start = FALSE;
3638     g_free (stream_id);
3639   }
3640 
3641   /* reverse playback:
3642    * first fragment (closest to stop time) is handled normally below,
3643    * then we pull in fragments going backwards */
3644   if (parse->segment.rate < 0.0) {
3645     /* check if we jumped back to a previous fragment,
3646      * which is a post-first fragment */
3647     if (parse->priv->offset < 0) {
3648       ret = gst_base_parse_handle_previous_fragment (parse);
3649       goto done;
3650     }
3651   }
3652 
3653   ret = gst_base_parse_scan_frame (parse, klass);
3654 
3655   /* eat expected eos signalling past segment in reverse playback */
3656   if (parse->segment.rate < 0.0 && ret == GST_FLOW_EOS &&
3657       parse->segment.position >= parse->segment.stop) {
3658     GST_DEBUG_OBJECT (parse, "downstream has reached end of segment");
3659     /* push what was accumulated during loop run */
3660     gst_base_parse_finish_fragment (parse, FALSE);
3661     /* force previous fragment */
3662     parse->priv->offset = -1;
3663     goto eos;
3664   }
3665 
3666   if (ret != GST_FLOW_OK)
3667     goto done;
3668 
3669 done:
3670   if (ret == GST_FLOW_EOS)
3671     goto eos;
3672   else if (ret != GST_FLOW_OK)
3673     goto pause;
3674 
3675   gst_object_unref (parse);
3676   return;
3677 
3678   /* ERRORS */
3679 eos:
3680   {
3681     ret = GST_FLOW_EOS;
3682     GST_DEBUG_OBJECT (parse, "eos");
3683     /* fall-through */
3684   }
3685 pause:
3686   {
3687     gboolean push_eos = FALSE;
3688 
3689     GST_DEBUG_OBJECT (parse, "pausing task, reason %s",
3690         gst_flow_get_name (ret));
3691     gst_pad_pause_task (parse->sinkpad);
3692 
3693     if (ret == GST_FLOW_EOS) {
3694       /* handle end-of-stream/segment */
3695       if (parse->segment.flags & GST_SEGMENT_FLAG_SEGMENT) {
3696         gint64 stop;
3697 
3698         if ((stop = parse->segment.stop) == -1)
3699           stop = parse->segment.duration;
3700 
3701         GST_DEBUG_OBJECT (parse, "sending segment_done");
3702 
3703         gst_element_post_message
3704             (GST_ELEMENT_CAST (parse),
3705             gst_message_new_segment_done (GST_OBJECT_CAST (parse),
3706                 GST_FORMAT_TIME, stop));
3707         gst_pad_push_event (parse->srcpad,
3708             gst_event_new_segment_done (GST_FORMAT_TIME, stop));
3709       } else {
3710         /* If we STILL have zero frames processed, fire an error */
3711         if (parse->priv->framecount == 0) {
3712           GST_ELEMENT_ERROR (parse, STREAM, WRONG_TYPE,
3713               ("No valid frames found before end of stream"), (NULL));
3714         }
3715         push_eos = TRUE;
3716       }
3717     } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
3718       /* for fatal errors we post an error message, wrong-state is
3719        * not fatal because it happens due to flushes and only means
3720        * that we should stop now. */
3721       GST_ELEMENT_FLOW_ERROR (parse, ret);
3722       push_eos = TRUE;
3723     }
3724     if (push_eos) {
3725       GstEvent *topush;
3726       if (parse->priv->estimated_duration <= 0) {
3727         gst_base_parse_update_duration (parse);
3728       }
3729       /* Push pending events, including SEGMENT events */
3730       gst_base_parse_push_pending_events (parse);
3731 
3732       topush = gst_event_new_eos ();
3733       GST_DEBUG_OBJECT (parse, "segment_seqnum:%" G_GUINT32_FORMAT,
3734           parse->priv->segment_seqnum);
3735       if (parse->priv->segment_seqnum != GST_SEQNUM_INVALID)
3736         gst_event_set_seqnum (topush, parse->priv->segment_seqnum);
3737       gst_pad_push_event (parse->srcpad, topush);
3738     }
3739     gst_object_unref (parse);
3740   }
3741 }
3742 
3743 static gboolean
gst_base_parse_sink_activate(GstPad * sinkpad,GstObject * parent)3744 gst_base_parse_sink_activate (GstPad * sinkpad, GstObject * parent)
3745 {
3746   GstSchedulingFlags sched_flags;
3747   GstBaseParse *parse;
3748   GstQuery *query;
3749   gboolean pull_mode;
3750 
3751   parse = GST_BASE_PARSE (parent);
3752 
3753   GST_DEBUG_OBJECT (parse, "sink activate");
3754 
3755   query = gst_query_new_scheduling ();
3756   if (!gst_pad_peer_query (sinkpad, query)) {
3757     gst_query_unref (query);
3758     goto baseparse_push;
3759   }
3760 
3761   gst_query_parse_scheduling (query, &sched_flags, NULL, NULL, NULL);
3762 
3763   pull_mode = gst_query_has_scheduling_mode (query, GST_PAD_MODE_PULL)
3764       && ((sched_flags & GST_SCHEDULING_FLAG_SEEKABLE) != 0);
3765 
3766   gst_query_unref (query);
3767 
3768   if (!pull_mode)
3769     goto baseparse_push;
3770 
3771   GST_DEBUG_OBJECT (parse, "trying to activate in pull mode");
3772   if (!gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE))
3773     goto baseparse_push;
3774 
3775   parse->priv->push_stream_start = TRUE;
3776   /* In pull mode, upstream is BYTES */
3777   parse->priv->upstream_format = GST_FORMAT_BYTES;
3778 
3779   return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_base_parse_loop,
3780       sinkpad, NULL);
3781   /* fallback */
3782 baseparse_push:
3783   {
3784     GST_DEBUG_OBJECT (parse, "trying to activate in push mode");
3785     return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
3786   }
3787 }
3788 
3789 static gboolean
gst_base_parse_activate(GstBaseParse * parse,gboolean active)3790 gst_base_parse_activate (GstBaseParse * parse, gboolean active)
3791 {
3792   GstBaseParseClass *klass;
3793   gboolean result = TRUE;
3794 
3795   GST_DEBUG_OBJECT (parse, "activate %d", active);
3796 
3797   klass = GST_BASE_PARSE_GET_CLASS (parse);
3798 
3799   if (active) {
3800     if (parse->priv->pad_mode == GST_PAD_MODE_NONE && klass->start)
3801       result = klass->start (parse);
3802 
3803     /* If the subclass implements ::detect we want to
3804      * call it for the first buffers now */
3805     parse->priv->detecting = (klass->detect != NULL);
3806   } else {
3807     /* We must make sure streaming has finished before resetting things
3808      * and calling the ::stop vfunc */
3809     GST_PAD_STREAM_LOCK (parse->sinkpad);
3810     GST_PAD_STREAM_UNLOCK (parse->sinkpad);
3811 
3812     if (parse->priv->pad_mode != GST_PAD_MODE_NONE && klass->stop)
3813       result = klass->stop (parse);
3814 
3815     parse->priv->pad_mode = GST_PAD_MODE_NONE;
3816     parse->priv->upstream_format = GST_FORMAT_UNDEFINED;
3817   }
3818   GST_DEBUG_OBJECT (parse, "activate return: %d", result);
3819   return result;
3820 }
3821 
3822 static gboolean
gst_base_parse_sink_activate_mode(GstPad * pad,GstObject * parent,GstPadMode mode,gboolean active)3823 gst_base_parse_sink_activate_mode (GstPad * pad, GstObject * parent,
3824     GstPadMode mode, gboolean active)
3825 {
3826   gboolean result;
3827   GstBaseParse *parse;
3828 
3829   parse = GST_BASE_PARSE (parent);
3830 
3831   GST_DEBUG_OBJECT (parse, "sink %sactivate in %s mode",
3832       (active) ? "" : "de", gst_pad_mode_get_name (mode));
3833 
3834   if (!gst_base_parse_activate (parse, active))
3835     goto activate_failed;
3836 
3837   switch (mode) {
3838     case GST_PAD_MODE_PULL:
3839       if (active) {
3840         GstEvent *ev = gst_event_new_segment (&parse->segment);
3841 
3842         if (parse->priv->segment_seqnum != GST_SEQNUM_INVALID)
3843           gst_event_set_seqnum (ev, parse->priv->segment_seqnum);
3844         else
3845           parse->priv->segment_seqnum = gst_event_get_seqnum (ev);
3846 
3847         parse->priv->pending_events =
3848             g_list_prepend (parse->priv->pending_events, ev);
3849         result = TRUE;
3850       } else {
3851         result = gst_pad_stop_task (pad);
3852       }
3853       break;
3854     default:
3855       result = TRUE;
3856       break;
3857   }
3858   if (result)
3859     parse->priv->pad_mode = active ? mode : GST_PAD_MODE_NONE;
3860 
3861   GST_DEBUG_OBJECT (parse, "sink activate return: %d", result);
3862 
3863   return result;
3864 
3865   /* ERRORS */
3866 activate_failed:
3867   {
3868     GST_DEBUG_OBJECT (parse, "activate failed");
3869     return FALSE;
3870   }
3871 }
3872 
3873 /**
3874  * gst_base_parse_set_duration:
3875  * @parse: #GstBaseParse.
3876  * @fmt: #GstFormat.
3877  * @duration: duration value.
3878  * @interval: how often to update the duration estimate based on bitrate, or 0.
3879  *
3880  * Sets the duration of the currently playing media. Subclass can use this
3881  * when it is able to determine duration and/or notices a change in the media
3882  * duration.  Alternatively, if @interval is non-zero (default), then stream
3883  * duration is determined based on estimated bitrate, and updated every @interval
3884  * frames.
3885  */
3886 void
gst_base_parse_set_duration(GstBaseParse * parse,GstFormat fmt,gint64 duration,gint interval)3887 gst_base_parse_set_duration (GstBaseParse * parse,
3888     GstFormat fmt, gint64 duration, gint interval)
3889 {
3890   gint64 old_duration;
3891 
3892   g_return_if_fail (parse != NULL);
3893 
3894   if (parse->priv->upstream_has_duration) {
3895     GST_DEBUG_OBJECT (parse, "using upstream duration; discarding update");
3896     goto exit;
3897   }
3898 
3899   old_duration = parse->priv->duration;
3900 
3901   parse->priv->duration = duration;
3902   parse->priv->duration_fmt = fmt;
3903   GST_DEBUG_OBJECT (parse, "set duration: %" G_GINT64_FORMAT, duration);
3904   if (fmt == GST_FORMAT_TIME && GST_CLOCK_TIME_IS_VALID (duration)) {
3905     if (interval != 0) {
3906       GST_DEBUG_OBJECT (parse, "valid duration provided, disabling estimate");
3907       interval = 0;
3908     }
3909   }
3910   GST_DEBUG_OBJECT (parse, "set update interval: %d", interval);
3911   parse->priv->update_interval = interval;
3912   if (duration != old_duration) {
3913     GstMessage *m;
3914 
3915     m = gst_message_new_duration_changed (GST_OBJECT (parse));
3916     gst_element_post_message (GST_ELEMENT (parse), m);
3917 
3918     /* TODO: what about duration tag? */
3919   }
3920 exit:
3921   return;
3922 }
3923 
3924 /**
3925  * gst_base_parse_set_average_bitrate:
3926  * @parse: #GstBaseParse.
3927  * @bitrate: average bitrate in bits/second
3928  *
3929  * Optionally sets the average bitrate detected in media (if non-zero),
3930  * e.g. based on metadata, as it will be posted to the application.
3931  *
3932  * By default, announced average bitrate is estimated. The average bitrate
3933  * is used to estimate the total duration of the stream and to estimate
3934  * a seek position, if there's no index and the format is syncable
3935  * (see gst_base_parse_set_syncable()).
3936  */
3937 void
gst_base_parse_set_average_bitrate(GstBaseParse * parse,guint bitrate)3938 gst_base_parse_set_average_bitrate (GstBaseParse * parse, guint bitrate)
3939 {
3940   parse->priv->bitrate = bitrate;
3941   GST_DEBUG_OBJECT (parse, "bitrate %u", bitrate);
3942 }
3943 
3944 /**
3945  * gst_base_parse_set_min_frame_size:
3946  * @parse: #GstBaseParse.
3947  * @min_size: Minimum size in bytes of the data that this base class should
3948  *       give to subclass.
3949  *
3950  * Subclass can use this function to tell the base class that it needs to
3951  * be given buffers of at least @min_size bytes.
3952  */
3953 void
gst_base_parse_set_min_frame_size(GstBaseParse * parse,guint min_size)3954 gst_base_parse_set_min_frame_size (GstBaseParse * parse, guint min_size)
3955 {
3956   g_return_if_fail (parse != NULL);
3957 
3958   parse->priv->min_frame_size = min_size;
3959   GST_LOG_OBJECT (parse, "set frame_min_size: %d", min_size);
3960 }
3961 
3962 /**
3963  * gst_base_parse_set_frame_rate:
3964  * @parse: the #GstBaseParse to set
3965  * @fps_num: frames per second (numerator).
3966  * @fps_den: frames per second (denominator).
3967  * @lead_in: frames needed before a segment for subsequent decode
3968  * @lead_out: frames needed after a segment
3969  *
3970  * If frames per second is configured, parser can take care of buffer duration
3971  * and timestamping.  When performing segment clipping, or seeking to a specific
3972  * location, a corresponding decoder might need an initial @lead_in and a
3973  * following @lead_out number of frames to ensure the desired segment is
3974  * entirely filled upon decoding.
3975  */
3976 void
gst_base_parse_set_frame_rate(GstBaseParse * parse,guint fps_num,guint fps_den,guint lead_in,guint lead_out)3977 gst_base_parse_set_frame_rate (GstBaseParse * parse, guint fps_num,
3978     guint fps_den, guint lead_in, guint lead_out)
3979 {
3980   g_return_if_fail (parse != NULL);
3981 
3982   parse->priv->fps_num = fps_num;
3983   parse->priv->fps_den = fps_den;
3984   if (!fps_num || !fps_den) {
3985     GST_DEBUG_OBJECT (parse, "invalid fps (%d/%d), ignoring parameters",
3986         fps_num, fps_den);
3987     fps_num = fps_den = 0;
3988     parse->priv->frame_duration = GST_CLOCK_TIME_NONE;
3989     parse->priv->lead_in = parse->priv->lead_out = 0;
3990     parse->priv->lead_in_ts = parse->priv->lead_out_ts = 0;
3991   } else {
3992     parse->priv->frame_duration =
3993         gst_util_uint64_scale (GST_SECOND, fps_den, fps_num);
3994     parse->priv->lead_in = lead_in;
3995     parse->priv->lead_out = lead_out;
3996     parse->priv->lead_in_ts =
3997         gst_util_uint64_scale (GST_SECOND, fps_den * lead_in, fps_num);
3998     parse->priv->lead_out_ts =
3999         gst_util_uint64_scale (GST_SECOND, fps_den * lead_out, fps_num);
4000     /* aim for about 1.5s to estimate duration */
4001     if (parse->priv->update_interval < 0) {
4002       guint64 interval = gst_util_uint64_scale (fps_num, 3,
4003           G_GUINT64_CONSTANT (2) * fps_den);
4004 
4005       parse->priv->update_interval = MIN (interval, G_MAXINT);
4006 
4007       GST_LOG_OBJECT (parse, "estimated update interval to %d frames",
4008           parse->priv->update_interval);
4009     }
4010   }
4011   GST_LOG_OBJECT (parse, "set fps: %d/%d => duration: %" G_GINT64_FORMAT " ms",
4012       fps_num, fps_den, parse->priv->frame_duration / GST_MSECOND);
4013   GST_LOG_OBJECT (parse, "set lead in: %d frames = %" G_GUINT64_FORMAT " ms, "
4014       "lead out: %d frames = %" G_GUINT64_FORMAT " ms",
4015       lead_in, parse->priv->lead_in_ts / GST_MSECOND,
4016       lead_out, parse->priv->lead_out_ts / GST_MSECOND);
4017 }
4018 
4019 /**
4020  * gst_base_parse_set_has_timing_info:
4021  * @parse: a #GstBaseParse
4022  * @has_timing: whether frames carry timing information
4023  *
4024  * Set if frames carry timing information which the subclass can (generally)
4025  * parse and provide.  In particular, intrinsic (rather than estimated) time
4026  * can be obtained following a seek.
4027  */
4028 void
gst_base_parse_set_has_timing_info(GstBaseParse * parse,gboolean has_timing)4029 gst_base_parse_set_has_timing_info (GstBaseParse * parse, gboolean has_timing)
4030 {
4031   parse->priv->has_timing_info = has_timing;
4032   GST_INFO_OBJECT (parse, "has_timing: %s", (has_timing) ? "yes" : "no");
4033 }
4034 
4035 /**
4036  * gst_base_parse_set_syncable:
4037  * @parse: a #GstBaseParse
4038  * @syncable: set if frame starts can be identified
4039  *
4040  * Set if frame starts can be identified. This is set by default and
4041  * determines whether seeking based on bitrate averages
4042  * is possible for a format/stream.
4043  */
4044 void
gst_base_parse_set_syncable(GstBaseParse * parse,gboolean syncable)4045 gst_base_parse_set_syncable (GstBaseParse * parse, gboolean syncable)
4046 {
4047   parse->priv->syncable = syncable;
4048   GST_INFO_OBJECT (parse, "syncable: %s", (syncable) ? "yes" : "no");
4049 }
4050 
4051 /**
4052  * gst_base_parse_set_passthrough:
4053  * @parse: a #GstBaseParse
4054  * @passthrough: %TRUE if parser should run in passthrough mode
4055  *
4056  * Set if the nature of the format or configuration does not allow (much)
4057  * parsing, and the parser should operate in passthrough mode (which only
4058  * applies when operating in push mode). That is, incoming buffers are
4059  * pushed through unmodified, i.e. no #GstBaseParseClass::handle_frame
4060  * will be invoked, but #GstBaseParseClass::pre_push_frame will still be
4061  * invoked, so subclass can perform as much or as little is appropriate for
4062  * passthrough semantics in #GstBaseParseClass::pre_push_frame.
4063  */
4064 void
gst_base_parse_set_passthrough(GstBaseParse * parse,gboolean passthrough)4065 gst_base_parse_set_passthrough (GstBaseParse * parse, gboolean passthrough)
4066 {
4067   parse->priv->passthrough = passthrough;
4068   GST_INFO_OBJECT (parse, "passthrough: %s", (passthrough) ? "yes" : "no");
4069 }
4070 
4071 /**
4072  * gst_base_parse_set_pts_interpolation:
4073  * @parse: a #GstBaseParse
4074  * @pts_interpolate: %TRUE if parser should interpolate PTS timestamps
4075  *
4076  * By default, the base class will guess PTS timestamps using a simple
4077  * interpolation (previous timestamp + duration), which is incorrect for
4078  * data streams with reordering, where PTS can go backward. Sub-classes
4079  * implementing such formats should disable PTS interpolation.
4080  */
4081 void
gst_base_parse_set_pts_interpolation(GstBaseParse * parse,gboolean pts_interpolate)4082 gst_base_parse_set_pts_interpolation (GstBaseParse * parse,
4083     gboolean pts_interpolate)
4084 {
4085   parse->priv->pts_interpolate = pts_interpolate;
4086   GST_INFO_OBJECT (parse, "PTS interpolation: %s",
4087       (pts_interpolate) ? "yes" : "no");
4088 }
4089 
4090 /**
4091  * gst_base_parse_set_infer_ts:
4092  * @parse: a #GstBaseParse
4093  * @infer_ts: %TRUE if parser should infer DTS/PTS from each other
4094  *
4095  * By default, the base class might try to infer PTS from DTS and vice
4096  * versa.  While this is generally correct for audio data, it may not
4097  * be otherwise. Sub-classes implementing such formats should disable
4098  * timestamp inferring.
4099  */
4100 void
gst_base_parse_set_infer_ts(GstBaseParse * parse,gboolean infer_ts)4101 gst_base_parse_set_infer_ts (GstBaseParse * parse, gboolean infer_ts)
4102 {
4103   parse->priv->infer_ts = infer_ts;
4104   GST_INFO_OBJECT (parse, "TS inferring: %s", (infer_ts) ? "yes" : "no");
4105 }
4106 
4107 /**
4108  * gst_base_parse_set_latency:
4109  * @parse: a #GstBaseParse
4110  * @min_latency: minimum parse latency
4111  * @max_latency: maximum parse latency
4112  *
4113  * Sets the minimum and maximum (which may likely be equal) latency introduced
4114  * by the parsing process.  If there is such a latency, which depends on the
4115  * particular parsing of the format, it typically corresponds to 1 frame duration.
4116  */
4117 void
gst_base_parse_set_latency(GstBaseParse * parse,GstClockTime min_latency,GstClockTime max_latency)4118 gst_base_parse_set_latency (GstBaseParse * parse, GstClockTime min_latency,
4119     GstClockTime max_latency)
4120 {
4121   g_return_if_fail (GST_CLOCK_TIME_IS_VALID (min_latency));
4122   g_return_if_fail (min_latency <= max_latency);
4123 
4124   GST_OBJECT_LOCK (parse);
4125   parse->priv->min_latency = min_latency;
4126   parse->priv->max_latency = max_latency;
4127   GST_OBJECT_UNLOCK (parse);
4128   GST_INFO_OBJECT (parse, "min/max latency %" GST_TIME_FORMAT ", %"
4129       GST_TIME_FORMAT, GST_TIME_ARGS (min_latency),
4130       GST_TIME_ARGS (max_latency));
4131 }
4132 
4133 static gboolean
gst_base_parse_get_duration(GstBaseParse * parse,GstFormat format,GstClockTime * duration)4134 gst_base_parse_get_duration (GstBaseParse * parse, GstFormat format,
4135     GstClockTime * duration)
4136 {
4137   gboolean res = FALSE;
4138 
4139   g_return_val_if_fail (duration != NULL, FALSE);
4140 
4141   *duration = GST_CLOCK_TIME_NONE;
4142   if (parse->priv->duration != -1 && format == parse->priv->duration_fmt) {
4143     GST_LOG_OBJECT (parse, "using provided duration");
4144     *duration = parse->priv->duration;
4145     res = TRUE;
4146   } else if (parse->priv->duration != -1) {
4147     GST_LOG_OBJECT (parse, "converting provided duration");
4148     res = gst_base_parse_convert (parse, parse->priv->duration_fmt,
4149         parse->priv->duration, format, (gint64 *) duration);
4150   } else if (format == GST_FORMAT_TIME && parse->priv->estimated_duration != -1) {
4151     GST_LOG_OBJECT (parse, "using estimated duration");
4152     *duration = parse->priv->estimated_duration;
4153     res = TRUE;
4154   } else {
4155     GST_LOG_OBJECT (parse, "cannot estimate duration");
4156   }
4157 
4158   GST_LOG_OBJECT (parse, "res: %d, duration %" GST_TIME_FORMAT, res,
4159       GST_TIME_ARGS (*duration));
4160   return res;
4161 }
4162 
4163 static gboolean
gst_base_parse_src_query_default(GstBaseParse * parse,GstQuery * query)4164 gst_base_parse_src_query_default (GstBaseParse * parse, GstQuery * query)
4165 {
4166   gboolean res = FALSE;
4167   GstPad *pad;
4168 
4169   pad = GST_BASE_PARSE_SRC_PAD (parse);
4170 
4171   switch (GST_QUERY_TYPE (query)) {
4172     case GST_QUERY_POSITION:
4173     {
4174       gint64 dest_value;
4175       GstFormat format;
4176 
4177       GST_DEBUG_OBJECT (parse, "position query");
4178       gst_query_parse_position (query, &format, NULL);
4179 
4180       /* try upstream first */
4181       res = gst_pad_query_default (pad, GST_OBJECT_CAST (parse), query);
4182       if (!res) {
4183         /* Fall back on interpreting segment */
4184         GST_OBJECT_LOCK (parse);
4185         /* Only reply BYTES if upstream is in BYTES already, otherwise
4186          * we're not in charge */
4187         if (format == GST_FORMAT_BYTES
4188             && parse->priv->upstream_format == GST_FORMAT_BYTES) {
4189           dest_value = parse->priv->offset;
4190           res = TRUE;
4191         } else if (format == parse->segment.format &&
4192             GST_CLOCK_TIME_IS_VALID (parse->segment.position)) {
4193           dest_value = gst_segment_to_stream_time (&parse->segment,
4194               parse->segment.format, parse->segment.position);
4195           res = TRUE;
4196         }
4197         GST_OBJECT_UNLOCK (parse);
4198         if (!res && parse->priv->upstream_format == GST_FORMAT_BYTES) {
4199           /* no precise result, upstream no idea either, then best estimate */
4200           /* priv->offset is updated in both PUSH/PULL modes, *iff* we're
4201            * in charge of things */
4202           res = gst_base_parse_convert (parse,
4203               GST_FORMAT_BYTES, parse->priv->offset, format, &dest_value);
4204         }
4205         if (res)
4206           gst_query_set_position (query, format, dest_value);
4207       }
4208       break;
4209     }
4210     case GST_QUERY_DURATION:
4211     {
4212       GstFormat format;
4213       GstClockTime duration;
4214 
4215       GST_DEBUG_OBJECT (parse, "duration query");
4216       gst_query_parse_duration (query, &format, NULL);
4217 
4218       /* consult upstream */
4219       res = gst_pad_query_default (pad, GST_OBJECT_CAST (parse), query);
4220 
4221       /* otherwise best estimate from us */
4222       if (!res) {
4223         res = gst_base_parse_get_duration (parse, format, &duration);
4224         if (res)
4225           gst_query_set_duration (query, format, duration);
4226       }
4227       break;
4228     }
4229     case GST_QUERY_SEEKING:
4230     {
4231       GstFormat fmt;
4232       GstClockTime duration = GST_CLOCK_TIME_NONE;
4233       gboolean seekable = FALSE;
4234 
4235       GST_DEBUG_OBJECT (parse, "seeking query");
4236       gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
4237 
4238       /* consult upstream */
4239       res = gst_pad_query_default (pad, GST_OBJECT_CAST (parse), query);
4240 
4241       /* we may be able to help if in TIME */
4242       if (fmt == GST_FORMAT_TIME && gst_base_parse_is_seekable (parse)) {
4243         gst_query_parse_seeking (query, &fmt, &seekable, NULL, NULL);
4244         /* already OK if upstream takes care */
4245         GST_LOG_OBJECT (parse, "upstream handled %d, seekable %d",
4246             res, seekable);
4247         if (!(res && seekable)) {
4248           if (!gst_base_parse_get_duration (parse, GST_FORMAT_TIME, &duration)
4249               || duration == -1) {
4250             /* seekable if we still have a chance to get duration later on */
4251             seekable = parse->priv->upstream_seekable &&
4252                 (parse->priv->update_interval > 0);
4253           } else {
4254             seekable = parse->priv->upstream_seekable;
4255             GST_LOG_OBJECT (parse, "already determine upstream seekabled: %d",
4256                 seekable);
4257           }
4258           gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, duration);
4259           res = TRUE;
4260         }
4261       }
4262       break;
4263     }
4264     case GST_QUERY_FORMATS:
4265       gst_query_set_formatsv (query, 3, fmtlist);
4266       res = TRUE;
4267       break;
4268     case GST_QUERY_CONVERT:
4269     {
4270       GstFormat src_format, dest_format;
4271       gint64 src_value, dest_value;
4272 
4273       gst_query_parse_convert (query, &src_format, &src_value,
4274           &dest_format, &dest_value);
4275 
4276       res = gst_base_parse_convert (parse, src_format, src_value,
4277           dest_format, &dest_value);
4278       if (res) {
4279         gst_query_set_convert (query, src_format, src_value,
4280             dest_format, dest_value);
4281       }
4282       break;
4283     }
4284     case GST_QUERY_LATENCY:
4285     {
4286       if ((res = gst_pad_peer_query (parse->sinkpad, query))) {
4287         gboolean live;
4288         GstClockTime min_latency, max_latency;
4289 
4290         gst_query_parse_latency (query, &live, &min_latency, &max_latency);
4291         GST_DEBUG_OBJECT (parse, "Peer latency: live %d, min %"
4292             GST_TIME_FORMAT " max %" GST_TIME_FORMAT, live,
4293             GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
4294 
4295         GST_OBJECT_LOCK (parse);
4296         /* add our latency */
4297         min_latency += parse->priv->min_latency;
4298         if (max_latency == -1 || parse->priv->max_latency == -1)
4299           max_latency = -1;
4300         else
4301           max_latency += parse->priv->max_latency;
4302         GST_OBJECT_UNLOCK (parse);
4303 
4304         gst_query_set_latency (query, live, min_latency, max_latency);
4305       }
4306       break;
4307     }
4308     case GST_QUERY_SEGMENT:
4309     {
4310       GstFormat format;
4311       gint64 start, stop;
4312 
4313       format = parse->segment.format;
4314 
4315       start =
4316           gst_segment_to_stream_time (&parse->segment, format,
4317           parse->segment.start);
4318       if ((stop = parse->segment.stop) == -1)
4319         stop = parse->segment.duration;
4320       else
4321         stop = gst_segment_to_stream_time (&parse->segment, format, stop);
4322 
4323       gst_query_set_segment (query, parse->segment.rate, format, start, stop);
4324       res = TRUE;
4325       break;
4326     }
4327     default:
4328       res = gst_pad_query_default (pad, GST_OBJECT_CAST (parse), query);
4329       break;
4330   }
4331   return res;
4332 }
4333 
4334 /* scans for a cluster start from @pos,
4335  * return GST_FLOW_OK and frame position/time in @pos/@time if found */
4336 static GstFlowReturn
gst_base_parse_find_frame(GstBaseParse * parse,gint64 * pos,GstClockTime * time,GstClockTime * duration)4337 gst_base_parse_find_frame (GstBaseParse * parse, gint64 * pos,
4338     GstClockTime * time, GstClockTime * duration)
4339 {
4340   GstBaseParseClass *klass;
4341   gint64 orig_offset;
4342   gboolean orig_drain, orig_discont;
4343   GstFlowReturn ret = GST_FLOW_OK;
4344   GstBuffer *buf = NULL;
4345   GstBaseParseFrame *sframe = NULL;
4346 
4347   g_return_val_if_fail (pos != NULL, GST_FLOW_ERROR);
4348   g_return_val_if_fail (time != NULL, GST_FLOW_ERROR);
4349   g_return_val_if_fail (duration != NULL, GST_FLOW_ERROR);
4350 
4351   klass = GST_BASE_PARSE_GET_CLASS (parse);
4352 
4353   *time = GST_CLOCK_TIME_NONE;
4354   *duration = GST_CLOCK_TIME_NONE;
4355 
4356   /* save state */
4357   orig_offset = parse->priv->offset;
4358   orig_discont = parse->priv->discont;
4359   orig_drain = parse->priv->drain;
4360 
4361   GST_DEBUG_OBJECT (parse, "scanning for frame starting at %" G_GINT64_FORMAT
4362       " (%#" G_GINT64_MODIFIER "x)", *pos, *pos);
4363 
4364   /* jump elsewhere and locate next frame */
4365   parse->priv->offset = *pos;
4366   /* mark as scanning so frames don't get processed all the way */
4367   parse->priv->scanning = TRUE;
4368   ret = gst_base_parse_scan_frame (parse, klass);
4369   parse->priv->scanning = FALSE;
4370   /* retrieve frame found during scan */
4371   sframe = parse->priv->scanned_frame;
4372   parse->priv->scanned_frame = NULL;
4373 
4374   if (ret != GST_FLOW_OK || !sframe)
4375     goto done;
4376 
4377   /* get offset first, subclass parsing might dump other stuff in there */
4378   *pos = sframe->offset;
4379   buf = sframe->buffer;
4380   g_assert (buf);
4381 
4382   /* but it should provide proper time */
4383   *time = GST_BUFFER_TIMESTAMP (buf);
4384   *duration = GST_BUFFER_DURATION (buf);
4385 
4386   GST_LOG_OBJECT (parse,
4387       "frame with time %" GST_TIME_FORMAT " at offset %" G_GINT64_FORMAT,
4388       GST_TIME_ARGS (*time), *pos);
4389 
4390 done:
4391   if (sframe)
4392     gst_base_parse_frame_free (sframe);
4393 
4394   /* restore state */
4395   parse->priv->offset = orig_offset;
4396   parse->priv->discont = orig_discont;
4397   parse->priv->drain = orig_drain;
4398 
4399   return ret;
4400 }
4401 
4402 /* bisect and scan through file for frame starting before @time,
4403  * returns OK and @time/@offset if found, NONE and/or error otherwise
4404  * If @time == G_MAXINT64, scan for duration ( == last frame) */
4405 static GstFlowReturn
gst_base_parse_locate_time(GstBaseParse * parse,GstClockTime * _time,gint64 * _offset)4406 gst_base_parse_locate_time (GstBaseParse * parse, GstClockTime * _time,
4407     gint64 * _offset)
4408 {
4409   GstFlowReturn ret = GST_FLOW_OK;
4410   gint64 lpos, hpos, newpos;
4411   GstClockTime time, ltime, htime, newtime, dur;
4412   gboolean cont = TRUE;
4413   const GstClockTime tolerance = TARGET_DIFFERENCE;
4414   const guint chunk = 4 * 1024;
4415 
4416   g_return_val_if_fail (_time != NULL, GST_FLOW_ERROR);
4417   g_return_val_if_fail (_offset != NULL, GST_FLOW_ERROR);
4418 
4419   GST_DEBUG_OBJECT (parse, "Bisecting for time %" GST_TIME_FORMAT,
4420       GST_TIME_ARGS (*_time));
4421 
4422   /* TODO also make keyframe aware if useful some day */
4423 
4424   time = *_time;
4425 
4426   /* basic cases */
4427   if (time == 0) {
4428     *_offset = 0;
4429     return GST_FLOW_OK;
4430   }
4431 
4432   if (time == -1) {
4433     *_offset = -1;
4434     return GST_FLOW_OK;
4435   }
4436 
4437   /* do not know at first */
4438   *_offset = -1;
4439   *_time = GST_CLOCK_TIME_NONE;
4440 
4441   /* need initial positions; start and end */
4442   lpos = parse->priv->first_frame_offset;
4443   ltime = parse->priv->first_frame_pts;
4444   /* try other one if no luck */
4445   if (!GST_CLOCK_TIME_IS_VALID (ltime))
4446     ltime = parse->priv->first_frame_dts;
4447   if (!gst_base_parse_get_duration (parse, GST_FORMAT_TIME, &htime)) {
4448     GST_DEBUG_OBJECT (parse, "Unknown time duration, cannot bisect");
4449     return GST_FLOW_ERROR;
4450   }
4451   hpos = parse->priv->upstream_size;
4452 
4453   GST_DEBUG_OBJECT (parse,
4454       "Bisection initial bounds: bytes %" G_GINT64_FORMAT " %" G_GINT64_FORMAT
4455       ", times %" GST_TIME_FORMAT " %" GST_TIME_FORMAT, lpos, hpos,
4456       GST_TIME_ARGS (ltime), GST_TIME_ARGS (htime));
4457 
4458   /* check preconditions are satisfied;
4459    * start and end are needed, except for special case where we scan for
4460    * last frame to determine duration */
4461   if (parse->priv->pad_mode != GST_PAD_MODE_PULL || !hpos ||
4462       !GST_CLOCK_TIME_IS_VALID (ltime) ||
4463       (!GST_CLOCK_TIME_IS_VALID (htime) && time != G_MAXINT64)) {
4464     return GST_FLOW_OK;
4465   }
4466 
4467   /* shortcut cases */
4468   if (time < ltime) {
4469     goto exit;
4470   } else if (time < ltime + tolerance) {
4471     *_offset = lpos;
4472     *_time = ltime;
4473     goto exit;
4474   } else if (time >= htime) {
4475     *_offset = hpos;
4476     *_time = htime;
4477     goto exit;
4478   }
4479 
4480   while (htime > ltime && cont) {
4481     GST_LOG_OBJECT (parse,
4482         "lpos: %" G_GUINT64_FORMAT ", ltime: %" GST_TIME_FORMAT, lpos,
4483         GST_TIME_ARGS (ltime));
4484     GST_LOG_OBJECT (parse,
4485         "hpos: %" G_GUINT64_FORMAT ", htime: %" GST_TIME_FORMAT, hpos,
4486         GST_TIME_ARGS (htime));
4487     if (G_UNLIKELY (time == G_MAXINT64)) {
4488       newpos = hpos;
4489     } else if (G_LIKELY (hpos > lpos)) {
4490       newpos =
4491           gst_util_uint64_scale (hpos - lpos, time - ltime, htime - ltime) +
4492           lpos - chunk;
4493     } else {
4494       /* should mean lpos == hpos, since lpos <= hpos is invariant */
4495       newpos = lpos;
4496       /* we check this case once, but not forever, so break loop */
4497       cont = FALSE;
4498     }
4499 
4500     /* ensure */
4501     newpos = CLAMP (newpos, lpos, hpos);
4502     GST_LOG_OBJECT (parse,
4503         "estimated _offset for %" GST_TIME_FORMAT ": %" G_GINT64_FORMAT,
4504         GST_TIME_ARGS (time), newpos);
4505 
4506     ret = gst_base_parse_find_frame (parse, &newpos, &newtime, &dur);
4507     if (ret == GST_FLOW_EOS) {
4508       /* heuristic HACK */
4509       hpos = MAX (lpos, hpos - chunk);
4510       continue;
4511     } else if (ret != GST_FLOW_OK) {
4512       goto exit;
4513     }
4514 
4515     if (newtime == -1 || newpos == -1) {
4516       GST_DEBUG_OBJECT (parse, "subclass did not provide metadata; aborting");
4517       break;
4518     }
4519 
4520     if (G_UNLIKELY (time == G_MAXINT64)) {
4521       *_offset = newpos;
4522       *_time = newtime;
4523       if (GST_CLOCK_TIME_IS_VALID (dur))
4524         *_time += dur;
4525       break;
4526     } else if (newtime > time) {
4527       /* overshoot */
4528       hpos = (newpos >= hpos) ? MAX (lpos, hpos - chunk) : MAX (lpos, newpos);
4529       htime = newtime;
4530     } else if (newtime + tolerance > time) {
4531       /* close enough undershoot */
4532       *_offset = newpos;
4533       *_time = newtime;
4534       break;
4535     } else if (newtime < ltime) {
4536       /* so a position beyond lpos resulted in earlier time than ltime ... */
4537       GST_DEBUG_OBJECT (parse, "non-ascending time; aborting");
4538       break;
4539     } else {
4540       /* undershoot too far */
4541       newpos += newpos == lpos ? chunk : 0;
4542       lpos = CLAMP (newpos, lpos, hpos);
4543       ltime = newtime;
4544     }
4545   }
4546 
4547 exit:
4548   GST_LOG_OBJECT (parse, "return offset %" G_GINT64_FORMAT ", time %"
4549       GST_TIME_FORMAT, *_offset, GST_TIME_ARGS (*_time));
4550   return ret;
4551 }
4552 
4553 static gint64
gst_base_parse_find_offset(GstBaseParse * parse,GstClockTime time,gboolean before,GstClockTime * _ts)4554 gst_base_parse_find_offset (GstBaseParse * parse, GstClockTime time,
4555     gboolean before, GstClockTime * _ts)
4556 {
4557   gint64 bytes = 0, ts = 0;
4558   GstIndexEntry *entry = NULL;
4559 
4560   if (time == GST_CLOCK_TIME_NONE) {
4561     ts = time;
4562     bytes = -1;
4563     goto exit;
4564   }
4565 
4566   GST_BASE_PARSE_INDEX_LOCK (parse);
4567   if (parse->priv->index) {
4568     /* Let's check if we have an index entry for that time */
4569     entry = gst_index_get_assoc_entry (parse->priv->index,
4570         parse->priv->index_id,
4571         before ? GST_INDEX_LOOKUP_BEFORE : GST_INDEX_LOOKUP_AFTER,
4572         GST_INDEX_ASSOCIATION_FLAG_KEY_UNIT, GST_FORMAT_TIME, time);
4573   }
4574 
4575   if (entry) {
4576     gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &bytes);
4577     gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &ts);
4578 
4579     GST_DEBUG_OBJECT (parse, "found index entry for %" GST_TIME_FORMAT
4580         " at %" GST_TIME_FORMAT ", offset %" G_GINT64_FORMAT,
4581         GST_TIME_ARGS (time), GST_TIME_ARGS (ts), bytes);
4582   } else {
4583     GST_DEBUG_OBJECT (parse, "no index entry found for %" GST_TIME_FORMAT,
4584         GST_TIME_ARGS (time));
4585     if (!before) {
4586       bytes = -1;
4587       ts = GST_CLOCK_TIME_NONE;
4588     }
4589   }
4590   GST_BASE_PARSE_INDEX_UNLOCK (parse);
4591 
4592 exit:
4593   if (_ts)
4594     *_ts = ts;
4595 
4596   return bytes;
4597 }
4598 
4599 /* returns TRUE if seek succeeded */
4600 static gboolean
gst_base_parse_handle_seek(GstBaseParse * parse,GstEvent * event)4601 gst_base_parse_handle_seek (GstBaseParse * parse, GstEvent * event)
4602 {
4603   gdouble rate;
4604   GstFormat format;
4605   GstSeekFlags flags;
4606   GstSeekType start_type = GST_SEEK_TYPE_NONE, stop_type;
4607   gboolean flush, update, res = TRUE, accurate;
4608   gint64 start, stop, seekpos, seekstop;
4609   GstSegment seeksegment = { 0, };
4610   GstClockTime start_ts;
4611   guint32 seqnum;
4612   GstEvent *segment_event;
4613 
4614   /* try upstream first, unless we're driving the streaming thread ourselves */
4615   if (parse->priv->pad_mode != GST_PAD_MODE_PULL) {
4616     res = gst_pad_push_event (parse->sinkpad, gst_event_ref (event));
4617     if (res)
4618       goto done;
4619   }
4620 
4621   gst_event_parse_seek (event, &rate, &format, &flags,
4622       &start_type, &start, &stop_type, &stop);
4623   seqnum = gst_event_get_seqnum (event);
4624   parse->priv->segment_seqnum = seqnum;
4625 
4626   GST_DEBUG_OBJECT (parse, "seek to format %s, rate %f, "
4627       "start type %d at %" GST_TIME_FORMAT ", end type %d at %"
4628       GST_TIME_FORMAT, gst_format_get_name (format), rate,
4629       start_type, GST_TIME_ARGS (start), stop_type, GST_TIME_ARGS (stop));
4630 
4631   /* we can only handle TIME, so check if subclass can convert
4632    * to TIME format if it's some other format (such as DEFAULT) */
4633   if (format != GST_FORMAT_TIME) {
4634     if (!gst_base_parse_convert (parse, format, start, GST_FORMAT_TIME, &start)
4635         || !gst_base_parse_convert (parse, format, stop, GST_FORMAT_TIME,
4636             &stop))
4637       goto no_convert_to_time;
4638 
4639     GST_INFO_OBJECT (parse, "converted %s format to start time "
4640         "%" GST_TIME_FORMAT " and stop time %" GST_TIME_FORMAT,
4641         gst_format_get_name (format), GST_TIME_ARGS (start),
4642         GST_TIME_ARGS (stop));
4643 
4644     format = GST_FORMAT_TIME;
4645   }
4646 
4647   /* no negative rates in push mode (unless upstream takes care of that, but
4648    * we've already tried upstream and it didn't handle the seek request) */
4649   if (rate < 0.0 && parse->priv->pad_mode == GST_PAD_MODE_PUSH)
4650     goto negative_rate;
4651 
4652   if (start_type != GST_SEEK_TYPE_SET ||
4653       (stop_type != GST_SEEK_TYPE_SET && stop_type != GST_SEEK_TYPE_NONE))
4654     goto wrong_type;
4655 
4656   /* get flush flag */
4657   flush = flags & GST_SEEK_FLAG_FLUSH;
4658 
4659   /* copy segment, we need this because we still need the old
4660    * segment when we close the current segment. */
4661   gst_segment_copy_into (&parse->segment, &seeksegment);
4662 
4663   GST_DEBUG_OBJECT (parse, "configuring seek");
4664   gst_segment_do_seek (&seeksegment, rate, format, flags,
4665       start_type, start, stop_type, stop, &update);
4666 
4667   /* accurate seeking implies seek tables are used to obtain position,
4668    * and the requested segment is maintained exactly, not adjusted any way */
4669   accurate = flags & GST_SEEK_FLAG_ACCURATE;
4670 
4671   /* maybe we can be accurate for (almost) free */
4672   gst_base_parse_find_offset (parse, seeksegment.position, TRUE, &start_ts);
4673   if (seeksegment.position <= start_ts + TARGET_DIFFERENCE) {
4674     GST_DEBUG_OBJECT (parse, "accurate seek possible");
4675     accurate = TRUE;
4676   }
4677 
4678   if (accurate) {
4679     GstClockTime startpos;
4680     if (rate >= 0)
4681       startpos = seeksegment.position;
4682     else
4683       startpos = start;
4684 
4685     /* accurate requested, so ... seek a bit before target */
4686     if (startpos < parse->priv->lead_in_ts)
4687       startpos = 0;
4688     else
4689       startpos -= parse->priv->lead_in_ts;
4690 
4691     if (seeksegment.stop == -1 && seeksegment.duration != -1)
4692       seeksegment.stop = seeksegment.start + seeksegment.duration;
4693 
4694     seekpos = gst_base_parse_find_offset (parse, startpos, TRUE, &start_ts);
4695     seekstop = gst_base_parse_find_offset (parse, seeksegment.stop, FALSE,
4696         NULL);
4697   } else {
4698     if (rate >= 0)
4699       start_ts = seeksegment.position;
4700     else
4701       start_ts = start;
4702 
4703     if (seeksegment.stop == -1 && seeksegment.duration != -1)
4704       seeksegment.stop = seeksegment.start + seeksegment.duration;
4705 
4706     if (!gst_base_parse_convert (parse, format, start_ts,
4707             GST_FORMAT_BYTES, &seekpos))
4708       goto convert_failed;
4709     if (!gst_base_parse_convert (parse, format, seeksegment.stop,
4710             GST_FORMAT_BYTES, &seekstop))
4711       goto convert_failed;
4712   }
4713 
4714   GST_DEBUG_OBJECT (parse,
4715       "seek position %" G_GINT64_FORMAT " in bytes: %" G_GINT64_FORMAT,
4716       start_ts, seekpos);
4717   GST_DEBUG_OBJECT (parse,
4718       "seek stop %" G_GINT64_FORMAT " in bytes: %" G_GINT64_FORMAT,
4719       seeksegment.stop, seekstop);
4720 
4721   if (parse->priv->pad_mode == GST_PAD_MODE_PULL) {
4722     gint64 last_stop;
4723 
4724     GST_DEBUG_OBJECT (parse, "seek in PULL mode");
4725 
4726     if (flush) {
4727       if (parse->srcpad) {
4728         GstEvent *fevent = gst_event_new_flush_start ();
4729         GST_DEBUG_OBJECT (parse, "sending flush start");
4730 
4731         gst_event_set_seqnum (fevent, seqnum);
4732 
4733         gst_pad_push_event (parse->srcpad, gst_event_ref (fevent));
4734         /* unlock upstream pull_range */
4735         gst_pad_push_event (parse->sinkpad, fevent);
4736       }
4737     } else {
4738       gst_pad_pause_task (parse->sinkpad);
4739     }
4740 
4741     /* we should now be able to grab the streaming thread because we stopped it
4742      * with the above flush/pause code */
4743     GST_PAD_STREAM_LOCK (parse->sinkpad);
4744 
4745     /* save current position */
4746     last_stop = parse->segment.position;
4747     GST_DEBUG_OBJECT (parse, "stopped streaming at %" G_GINT64_FORMAT,
4748         last_stop);
4749 
4750     /* now commit to new position */
4751 
4752     /* prepare for streaming again */
4753     if (flush) {
4754       GstEvent *fevent = gst_event_new_flush_stop (TRUE);
4755       GST_DEBUG_OBJECT (parse, "sending flush stop");
4756       gst_event_set_seqnum (fevent, seqnum);
4757       gst_pad_push_event (parse->srcpad, gst_event_ref (fevent));
4758       gst_pad_push_event (parse->sinkpad, fevent);
4759       gst_base_parse_clear_queues (parse);
4760     }
4761 
4762     memcpy (&parse->segment, &seeksegment, sizeof (GstSegment));
4763 
4764     /* store the newsegment event so it can be sent from the streaming thread. */
4765     /* This will be sent later in _loop() */
4766     segment_event = gst_event_new_segment (&parse->segment);
4767     gst_event_set_seqnum (segment_event, seqnum);
4768     parse->priv->pending_events =
4769         g_list_prepend (parse->priv->pending_events, segment_event);
4770 
4771     GST_DEBUG_OBJECT (parse, "Created newseg format %d, "
4772         "start = %" GST_TIME_FORMAT ", stop = %" GST_TIME_FORMAT
4773         ", time = %" GST_TIME_FORMAT, format,
4774         GST_TIME_ARGS (parse->segment.start),
4775         GST_TIME_ARGS (parse->segment.stop),
4776         GST_TIME_ARGS (parse->segment.time));
4777 
4778     /* one last chance in pull mode to stay accurate;
4779      * maybe scan and subclass can find where to go */
4780     if (!accurate) {
4781       gint64 scanpos;
4782       GstClockTime ts = seeksegment.position;
4783 
4784       gst_base_parse_locate_time (parse, &ts, &scanpos);
4785       if (scanpos >= 0) {
4786         accurate = TRUE;
4787         seekpos = scanpos;
4788         /* running collected index now consists of several intervals,
4789          * so optimized check no longer possible */
4790         parse->priv->index_last_valid = FALSE;
4791         parse->priv->index_last_offset = 0;
4792         parse->priv->index_last_ts = 0;
4793       }
4794     }
4795 
4796     /* mark discont if we are going to stream from another position. */
4797     if (seekpos != parse->priv->offset) {
4798       GST_DEBUG_OBJECT (parse,
4799           "mark DISCONT, we did a seek to another position");
4800       parse->priv->offset = seekpos;
4801       parse->priv->last_offset = seekpos;
4802       parse->priv->seen_keyframe = FALSE;
4803       parse->priv->discont = TRUE;
4804       parse->priv->next_dts = start_ts;
4805       parse->priv->next_pts = GST_CLOCK_TIME_NONE;
4806       parse->priv->last_dts = GST_CLOCK_TIME_NONE;
4807       parse->priv->last_pts = GST_CLOCK_TIME_NONE;
4808       parse->priv->sync_offset = seekpos;
4809       parse->priv->exact_position = accurate;
4810     }
4811 
4812     /* Start streaming thread if paused */
4813     gst_pad_start_task (parse->sinkpad,
4814         (GstTaskFunction) gst_base_parse_loop, parse->sinkpad, NULL);
4815 
4816     GST_PAD_STREAM_UNLOCK (parse->sinkpad);
4817 
4818     /* handled seek */
4819     res = TRUE;
4820   } else {
4821     GstEvent *new_event;
4822     GstBaseParseSeek *seek;
4823     GstSeekFlags flags = (flush ? GST_SEEK_FLAG_FLUSH : GST_SEEK_FLAG_NONE);
4824 
4825     /* The only thing we need to do in PUSH-mode is to send the
4826        seek event (in bytes) to upstream. Segment / flush handling happens
4827        in corresponding src event handlers */
4828     GST_DEBUG_OBJECT (parse, "seek in PUSH mode");
4829     if (seekstop >= 0 && seekstop <= seekpos)
4830       seekstop = seekpos;
4831     new_event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags,
4832         GST_SEEK_TYPE_SET, seekpos, stop_type, seekstop);
4833     gst_event_set_seqnum (new_event, seqnum);
4834 
4835     /* store segment info so its precise details can be reconstructed when
4836      * receiving newsegment;
4837      * this matters for all details when accurate seeking,
4838      * is most useful to preserve NONE stop time otherwise */
4839     seek = g_new0 (GstBaseParseSeek, 1);
4840     seek->segment = seeksegment;
4841     seek->accurate = accurate;
4842     seek->offset = seekpos;
4843     seek->start_ts = start_ts;
4844     GST_OBJECT_LOCK (parse);
4845     /* less optimal, but preserves order */
4846     parse->priv->pending_seeks =
4847         g_slist_append (parse->priv->pending_seeks, seek);
4848     GST_OBJECT_UNLOCK (parse);
4849 
4850     res = gst_pad_push_event (parse->sinkpad, new_event);
4851 
4852     if (!res) {
4853       GST_OBJECT_LOCK (parse);
4854       parse->priv->pending_seeks =
4855           g_slist_remove (parse->priv->pending_seeks, seek);
4856       GST_OBJECT_UNLOCK (parse);
4857       g_free (seek);
4858     }
4859   }
4860 
4861 done:
4862   gst_event_unref (event);
4863   return res;
4864 
4865   /* ERRORS */
4866 negative_rate:
4867   {
4868     GST_DEBUG_OBJECT (parse, "negative playback rates delegated upstream.");
4869     res = FALSE;
4870     goto done;
4871   }
4872 wrong_type:
4873   {
4874     GST_DEBUG_OBJECT (parse, "unsupported seek type.");
4875     res = FALSE;
4876     goto done;
4877   }
4878 no_convert_to_time:
4879   {
4880     GST_DEBUG_OBJECT (parse, "seek in %s format was requested, but subclass "
4881         "couldn't convert that into TIME format", gst_format_get_name (format));
4882     res = FALSE;
4883     goto done;
4884   }
4885 convert_failed:
4886   {
4887     GST_DEBUG_OBJECT (parse, "conversion TIME to BYTES failed.");
4888     res = FALSE;
4889     goto done;
4890   }
4891 }
4892 
4893 static void
gst_base_parse_set_upstream_tags(GstBaseParse * parse,GstTagList * taglist)4894 gst_base_parse_set_upstream_tags (GstBaseParse * parse, GstTagList * taglist)
4895 {
4896   if (taglist == parse->priv->upstream_tags)
4897     return;
4898 
4899   if (parse->priv->upstream_tags) {
4900     gst_tag_list_unref (parse->priv->upstream_tags);
4901     parse->priv->upstream_tags = NULL;
4902   }
4903 
4904   GST_INFO_OBJECT (parse, "upstream tags: %" GST_PTR_FORMAT, taglist);
4905 
4906   if (taglist != NULL)
4907     parse->priv->upstream_tags = gst_tag_list_ref (taglist);
4908 
4909   gst_base_parse_check_bitrate_tags (parse);
4910 }
4911 
4912 #if 0
4913 static void
4914 gst_base_parse_set_index (GstElement * element, GstIndex * index)
4915 {
4916   GstBaseParse *parse = GST_BASE_PARSE (element);
4917 
4918   GST_BASE_PARSE_INDEX_LOCK (parse);
4919   if (parse->priv->index)
4920     gst_object_unref (parse->priv->index);
4921   if (index) {
4922     parse->priv->index = gst_object_ref (index);
4923     gst_index_get_writer_id (index, GST_OBJECT_CAST (element),
4924         &parse->priv->index_id);
4925     parse->priv->own_index = FALSE;
4926   } else {
4927     parse->priv->index = NULL;
4928   }
4929   GST_BASE_PARSE_INDEX_UNLOCK (parse);
4930 }
4931 
4932 static GstIndex *
4933 gst_base_parse_get_index (GstElement * element)
4934 {
4935   GstBaseParse *parse = GST_BASE_PARSE (element);
4936   GstIndex *result = NULL;
4937 
4938   GST_BASE_PARSE_INDEX_LOCK (parse);
4939   if (parse->priv->index)
4940     result = gst_object_ref (parse->priv->index);
4941   GST_BASE_PARSE_INDEX_UNLOCK (parse);
4942 
4943   return result;
4944 }
4945 #endif
4946 
4947 static GstStateChangeReturn
gst_base_parse_change_state(GstElement * element,GstStateChange transition)4948 gst_base_parse_change_state (GstElement * element, GstStateChange transition)
4949 {
4950   GstBaseParse *parse;
4951   GstStateChangeReturn result;
4952 
4953   parse = GST_BASE_PARSE (element);
4954 
4955   switch (transition) {
4956     case GST_STATE_CHANGE_READY_TO_PAUSED:
4957       /* If this is our own index destroy it as the
4958        * old entries might be wrong for the new stream */
4959       GST_BASE_PARSE_INDEX_LOCK (parse);
4960       if (parse->priv->own_index) {
4961         gst_object_unref (parse->priv->index);
4962         parse->priv->index = NULL;
4963         parse->priv->own_index = FALSE;
4964       }
4965 
4966       /* If no index was created, generate one */
4967       if (G_UNLIKELY (!parse->priv->index)) {
4968         GST_DEBUG_OBJECT (parse, "no index provided creating our own");
4969 
4970         parse->priv->index = g_object_new (gst_mem_index_get_type (), NULL);
4971         gst_index_get_writer_id (parse->priv->index, GST_OBJECT (parse),
4972             &parse->priv->index_id);
4973         parse->priv->own_index = TRUE;
4974       }
4975       GST_BASE_PARSE_INDEX_UNLOCK (parse);
4976       break;
4977     default:
4978       break;
4979   }
4980 
4981   result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
4982 
4983   switch (transition) {
4984     case GST_STATE_CHANGE_PAUSED_TO_READY:
4985       gst_base_parse_reset (parse);
4986       break;
4987     default:
4988       break;
4989   }
4990 
4991   return result;
4992 }
4993 
4994 /**
4995  * gst_base_parse_set_ts_at_offset:
4996  * @parse: a #GstBaseParse
4997  * @offset: offset into current buffer
4998  *
4999  * This function should only be called from a @handle_frame implementation.
5000  *
5001  * #GstBaseParse creates initial timestamps for frames by using the last
5002  * timestamp seen in the stream before the frame starts.  In certain
5003  * cases, the correct timestamps will occur in the stream after the
5004  * start of the frame, but before the start of the actual picture data.
5005  * This function can be used to set the timestamps based on the offset
5006  * into the frame data that the picture starts.
5007  *
5008  * Since: 1.2
5009  */
5010 void
gst_base_parse_set_ts_at_offset(GstBaseParse * parse,gsize offset)5011 gst_base_parse_set_ts_at_offset (GstBaseParse * parse, gsize offset)
5012 {
5013   GstClockTime pts, dts;
5014 
5015   g_return_if_fail (GST_IS_BASE_PARSE (parse));
5016 
5017   pts = gst_adapter_prev_pts_at_offset (parse->priv->adapter, offset, NULL);
5018   dts = gst_adapter_prev_dts_at_offset (parse->priv->adapter, offset, NULL);
5019 
5020   if (!GST_CLOCK_TIME_IS_VALID (pts) || !GST_CLOCK_TIME_IS_VALID (dts)) {
5021     GST_DEBUG_OBJECT (parse,
5022         "offset adapter timestamps dts=%" GST_TIME_FORMAT " pts=%"
5023         GST_TIME_FORMAT, GST_TIME_ARGS (dts), GST_TIME_ARGS (pts));
5024   }
5025   if (GST_CLOCK_TIME_IS_VALID (pts) && (parse->priv->prev_pts != pts))
5026     parse->priv->prev_pts = parse->priv->next_pts = pts;
5027 
5028   if (GST_CLOCK_TIME_IS_VALID (dts) && (parse->priv->prev_dts != dts)) {
5029     parse->priv->prev_dts = parse->priv->next_dts = dts;
5030     parse->priv->prev_dts_from_pts = FALSE;
5031   }
5032 }
5033 
5034 /**
5035  * gst_base_parse_merge_tags:
5036  * @parse: a #GstBaseParse
5037  * @tags: (allow-none): a #GstTagList to merge, or NULL to unset
5038  *     previously-set tags
5039  * @mode: the #GstTagMergeMode to use, usually #GST_TAG_MERGE_REPLACE
5040  *
5041  * Sets the parser subclass's tags and how they should be merged with any
5042  * upstream stream tags. This will override any tags previously-set
5043  * with gst_base_parse_merge_tags().
5044  *
5045  * Note that this is provided for convenience, and the subclass is
5046  * not required to use this and can still do tag handling on its own.
5047  *
5048  * Since: 1.6
5049  */
5050 void
gst_base_parse_merge_tags(GstBaseParse * parse,GstTagList * tags,GstTagMergeMode mode)5051 gst_base_parse_merge_tags (GstBaseParse * parse, GstTagList * tags,
5052     GstTagMergeMode mode)
5053 {
5054   g_return_if_fail (GST_IS_BASE_PARSE (parse));
5055   g_return_if_fail (tags == NULL || GST_IS_TAG_LIST (tags));
5056   g_return_if_fail (tags == NULL || mode != GST_TAG_MERGE_UNDEFINED);
5057 
5058   GST_OBJECT_LOCK (parse);
5059 
5060   if (tags != parse->priv->parser_tags) {
5061     if (parse->priv->parser_tags) {
5062       gst_tag_list_unref (parse->priv->parser_tags);
5063       parse->priv->parser_tags = NULL;
5064       parse->priv->parser_tags_merge_mode = GST_TAG_MERGE_APPEND;
5065     }
5066     if (tags) {
5067       parse->priv->parser_tags = gst_tag_list_ref (tags);
5068       parse->priv->parser_tags_merge_mode = mode;
5069     }
5070 
5071     GST_DEBUG_OBJECT (parse, "setting parser tags to %" GST_PTR_FORMAT
5072         " (mode %d)", tags, parse->priv->parser_tags_merge_mode);
5073 
5074     gst_base_parse_check_bitrate_tags (parse);
5075     parse->priv->tags_changed = TRUE;
5076   }
5077 
5078   GST_OBJECT_UNLOCK (parse);
5079 }
5080