From b2876ad8a4f7b9f1cdf499ab40c4b73f841c9031 Mon Sep 17 00:00:00 2001 From: Seungha Yang Date: Sun, 7 Oct 2018 16:43:34 +0900 Subject: [PATCH] qtdemux: Use GPtrArray to store QtDemuxStream structure GPtrArray has less overhead than linked list and the length also can be auto updated by using it. https://bugzilla.gnome.org/show_bug.cgi?id=797239 --- gst/isomp4/qtdemux.c | 418 ++++++++++++++++++++++--------------------- gst/isomp4/qtdemux.h | 5 +- 2 files changed, 216 insertions(+), 207 deletions(-) diff --git a/gst/isomp4/qtdemux.c b/gst/isomp4/qtdemux.c index 54ffd9ff0c..c99546ef51 100644 --- a/gst/isomp4/qtdemux.c +++ b/gst/isomp4/qtdemux.c @@ -97,9 +97,12 @@ #define ABSDIFF(x, y) ( (x) > (y) ? ((x) - (y)) : ((y) - (x)) ) -#define QTDEMUX_FIRST_STREAM(demux) ((QtDemuxStream *)(demux)->active_streams \ - ? (QtDemuxStream *)(demux)->active_streams->data : NULL) #define QTDEMUX_STREAM(s) ((QtDemuxStream *)(s)) +#define QTDEMUX_N_STREAMS(demux) ((demux)->active_streams->len) +#define QTDEMUX_NTH_STREAM(demux,idx) \ + QTDEMUX_STREAM(g_ptr_array_index((demux)->active_streams,idx)) +#define QTDEMUX_NTH_OLD_STREAM(demux,idx) \ + QTDEMUX_STREAM(g_ptr_array_index((demux)->old_streams,idx)) GST_DEBUG_CATEGORY (qtdemux_debug); #define GST_CAT_DEFAULT qtdemux_debug @@ -578,10 +581,9 @@ static GstCaps *qtdemux_generic_caps (GstQTDemux * qtdemux, static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n); static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux); +static QtDemuxStream *gst_qtdemux_stream_ref (QtDemuxStream * stream); static void gst_qtdemux_stream_unref (QtDemuxStream * stream); static void gst_qtdemux_stream_clear (QtDemuxStream * stream); -static void gst_qtdemux_remove_stream (GstQTDemux * qtdemux, - QtDemuxStream * stream); static GstFlowReturn qtdemux_prepare_streams (GstQTDemux * qtdemux); static void qtdemux_do_allocation (QtDemuxStream * stream, GstQTDemux * qtdemux); @@ -661,6 +663,11 @@ gst_qtdemux_init (GstQTDemux * qtdemux) qtdemux->flowcombiner = gst_flow_combiner_new (); g_mutex_init (&qtdemux->expose_lock); + qtdemux->active_streams = g_ptr_array_new_with_free_func + ((GDestroyNotify) gst_qtdemux_stream_unref); + qtdemux->old_streams = g_ptr_array_new_with_free_func + ((GDestroyNotify) gst_qtdemux_stream_unref); + GST_OBJECT_FLAG_SET (qtdemux, GST_ELEMENT_FLAG_INDEXABLE); gst_qtdemux_reset (qtdemux, TRUE); @@ -685,6 +692,9 @@ gst_qtdemux_dispose (GObject * object) qtdemux->cenc_aux_info_sizes = NULL; g_mutex_clear (&qtdemux->expose_lock); + g_ptr_array_free (qtdemux->active_streams, TRUE); + g_ptr_array_free (qtdemux->old_streams, TRUE); + G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -1002,14 +1012,14 @@ gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event) { gboolean has_valid_stream = FALSE; GstEventType etype = GST_EVENT_TYPE (event); - GList *iter; + guint i; GST_DEBUG_OBJECT (qtdemux, "pushing %s event on all source pads", GST_EVENT_TYPE_NAME (event)); - for (iter = qtdemux->active_streams; iter; iter = g_list_next (iter)) { + for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) { GstPad *pad; - QtDemuxStream *stream = QTDEMUX_STREAM (iter->data); + QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i); GST_DEBUG_OBJECT (qtdemux, "pushing on track-id %u", stream->track_id); if ((pad = stream->pad)) { @@ -1306,13 +1316,13 @@ gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time, { guint64 min_offset; gint64 min_byte_offset = -1; - GList *iter; + guint i; min_offset = desired_time; /* for each stream, find the index of the sample in the segment * and move back to the previous keyframe. */ - for (iter = qtdemux->active_streams; iter; iter = g_list_next (iter)) { + for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) { QtDemuxStream *str; guint32 index, kindex; guint32 seg_idx; @@ -1322,7 +1332,7 @@ gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time, QtDemuxSegment *seg; gboolean empty_segment = FALSE; - str = QTDEMUX_STREAM (iter->data); + str = QTDEMUX_NTH_STREAM (qtdemux, i); if (CUR_STREAM (str)->sparse && !use_sparse) continue; @@ -1552,7 +1562,7 @@ gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment, guint32 seqnum, GstSeekFlags flags) { gint64 desired_offset; - GList *iter; + guint i; desired_offset = segment->position; @@ -1582,8 +1592,8 @@ gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment, /* and set all streams to the final position */ gst_flow_combiner_reset (qtdemux->flowcombiner); qtdemux->segment_seqnum = seqnum; - for (iter = qtdemux->active_streams; iter; iter = g_list_next (iter)) { - QtDemuxStream *stream = QTDEMUX_STREAM (iter->data); + for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) { + QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i); stream->time_position = desired_offset; stream->accumulated_base = 0; @@ -1727,13 +1737,13 @@ no_format: static gboolean qtdemux_ensure_index (GstQTDemux * qtdemux) { - GList *iter; + guint i; GST_DEBUG_OBJECT (qtdemux, "collecting all metadata for all streams"); /* Build complete index */ - for (iter = qtdemux->active_streams; iter; iter = g_list_next (iter)) { - QtDemuxStream *stream = QTDEMUX_STREAM (iter->data); + for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) { + QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i); if (!qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) { GST_LOG_OBJECT (qtdemux, @@ -1793,7 +1803,8 @@ gst_qtdemux_handle_src_event (GstPad * pad, GstObject * parent, } else if (gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (event))) { GST_DEBUG_OBJECT (qtdemux, "Upstream successfully seeked"); res = TRUE; - } else if (qtdemux->state == QTDEMUX_STATE_MOVIE && qtdemux->n_streams + } else if (qtdemux->state == QTDEMUX_STATE_MOVIE + && QTDEMUX_N_STREAMS (qtdemux) && !qtdemux->fragmented) { res = gst_qtdemux_do_push_seek (qtdemux, pad, event); } else { @@ -1842,18 +1853,18 @@ gst_qtdemux_find_sample (GstQTDemux * qtdemux, gint64 byte_pos, gboolean fw, gint i, index; gint64 time, min_time; QtDemuxStream *stream; - GList *iter; + gint iter; min_time = -1; stream = NULL; index = -1; - for (iter = qtdemux->active_streams; iter; iter = g_list_next (iter)) { + for (iter = 0; iter < QTDEMUX_N_STREAMS (qtdemux); iter++) { QtDemuxStream *str; gint inc; gboolean set_sample; - str = QTDEMUX_STREAM (iter->data); + str = QTDEMUX_NTH_STREAM (qtdemux, iter); set_sample = !set; if (fw) { @@ -2013,7 +2024,7 @@ gst_qtdemux_setcaps (GstQTDemux * demux, GstCaps * caps) demux->fragmented = TRUE; demux->mss_mode = TRUE; - if (demux->n_streams > 1) { + if (QTDEMUX_N_STREAMS (demux) > 1) { /* can't do this, we can only renegotiate for another mss format */ return FALSE; } @@ -2025,16 +2036,15 @@ gst_qtdemux_setcaps (GstQTDemux * demux, GstCaps * caps) /* TODO update when stream changes during playback */ - if (demux->n_streams == 0) { + if (QTDEMUX_N_STREAMS (demux) == 0) { stream = _create_stream (demux, 1); - demux->active_streams = g_list_append (demux->active_streams, stream); - demux->n_streams = 1; + g_ptr_array_add (demux->active_streams, stream); /* mss has no stsd/stsd entry, use id 0 as default */ stream->stsd_entries_length = 1; stream->stsd_sample_description_id = stream->cur_stsd_entry_index = 0; stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, 1); } else { - stream = QTDEMUX_FIRST_STREAM (demux); + stream = QTDEMUX_NTH_STREAM (demux, 0); } timescale_v = gst_structure_get_value (structure, "timescale"); @@ -2083,7 +2093,7 @@ gst_qtdemux_setcaps (GstQTDemux * demux, GstCaps * caps) static void gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard) { - GList *iter; + gint i; GST_DEBUG_OBJECT (qtdemux, "Resetting demux"); gst_pad_stop_task (qtdemux->sinkpad); @@ -2155,13 +2165,10 @@ gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard) if (hard) { qtdemux->segment_seqnum = GST_SEQNUM_INVALID; - g_list_free_full (qtdemux->active_streams, - (GDestroyNotify) gst_qtdemux_stream_unref); - g_list_free_full (qtdemux->old_streams, - (GDestroyNotify) gst_qtdemux_stream_unref); - qtdemux->active_streams = NULL; - qtdemux->old_streams = NULL; - qtdemux->n_streams = 0; + g_ptr_array_remove_range (qtdemux->active_streams, + 0, qtdemux->active_streams->len); + g_ptr_array_remove_range (qtdemux->old_streams, + 0, qtdemux->old_streams->len); qtdemux->n_video_streams = 0; qtdemux->n_audio_streams = 0; qtdemux->n_sub_streams = 0; @@ -2188,12 +2195,12 @@ gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard) } } else if (qtdemux->mss_mode) { gst_flow_combiner_reset (qtdemux->flowcombiner); - g_list_foreach (qtdemux->active_streams, + g_ptr_array_foreach (qtdemux->active_streams, (GFunc) gst_qtdemux_stream_clear, NULL); } else { gst_flow_combiner_reset (qtdemux->flowcombiner); - for (iter = qtdemux->active_streams; iter; iter = g_list_next (iter)) { - QtDemuxStream *stream = QTDEMUX_STREAM (iter->data); + for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) { + QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i); stream->sent_eos = FALSE; stream->time_position = 0; stream->accumulated_base = 0; @@ -2212,11 +2219,10 @@ gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard) static void gst_qtdemux_map_and_push_segments (GstQTDemux * qtdemux, GstSegment * segment) { - gint i; - GList *iter; + gint i, iter; - for (iter = qtdemux->active_streams; iter; iter = g_list_next (iter)) { - QtDemuxStream *stream = QTDEMUX_STREAM (iter->data); + for (iter = 0; iter < QTDEMUX_N_STREAMS (qtdemux); iter++) { + QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, iter); stream->time_position = segment->start; @@ -2246,6 +2252,31 @@ gst_qtdemux_map_and_push_segments (GstQTDemux * qtdemux, GstSegment * segment) } } +static void +gst_qtdemux_stream_concat (GstQTDemux * qtdemux, GPtrArray * dest, + GPtrArray * src) +{ + guint i; + guint len; + + len = src->len; + + if (len == 0) + return; + + for (i = 0; i < len; i++) { + QtDemuxStream *stream = g_ptr_array_index (src, i); + +#ifndef GST_DISABLE_GST_DEBUG + GST_DEBUG_OBJECT (qtdemux, "Move stream %p (stream-id %s) to %p", + stream, GST_STR_NULL (stream->stream_id), dest); +#endif + g_ptr_array_add (dest, gst_qtdemux_stream_ref (stream)); + } + + g_ptr_array_remove_range (src, 0, len); +} + static gboolean gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent, GstEvent * event) @@ -2276,7 +2307,7 @@ gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent, "not in time format"); /* chain will send initial newsegment after pads have been added */ - if (demux->state != QTDEMUX_STATE_MOVIE || !demux->n_streams) { + if (demux->state != QTDEMUX_STATE_MOVIE || !QTDEMUX_N_STREAMS (demux)) { GST_DEBUG_OBJECT (demux, "still starting, eating event"); goto exit; } @@ -2344,7 +2375,7 @@ gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent, GST_DEBUG_OBJECT (demux, "Pushing newseg %" GST_SEGMENT_FORMAT, &segment); /* map segment to internal qt segments and push on each stream */ - if (demux->n_streams) { + if (QTDEMUX_N_STREAMS (demux)) { demux->need_segment = TRUE; gst_qtdemux_check_send_pending_segment (demux); } @@ -2405,10 +2436,10 @@ gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent, /* If we are in push mode, and get an EOS before we've seen any streams, * then error out - we have nowhere to send the EOS */ if (!demux->pullbased) { - GList *iter; + gint i; gboolean has_valid_stream = FALSE; - for (iter = demux->active_streams; iter; iter = g_list_next (iter)) { - if (QTDEMUX_STREAM (iter->data)->pad != NULL) { + for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) { + if (QTDEMUX_NTH_STREAM (demux, i)->pad != NULL) { has_valid_stream = TRUE; break; } @@ -2457,9 +2488,8 @@ gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent, gst_qtdemux_process_adapter (demux, TRUE); gst_qtdemux_reset (demux, FALSE); /* We expect new moov box after new stream-start event */ - demux->old_streams = - g_list_concat (demux->old_streams, demux->active_streams); - demux->active_streams = NULL; + gst_qtdemux_stream_concat (demux, + demux->old_streams, demux->active_streams); goto drop; } @@ -2628,6 +2658,14 @@ gst_qtdemux_stream_reset (QtDemuxStream * stream) stream->stsd_entries_length = 0; } +static QtDemuxStream * +gst_qtdemux_stream_ref (QtDemuxStream * stream) +{ + g_atomic_int_add (&stream->ref_count, 1); + + return stream; +} + static void gst_qtdemux_stream_unref (QtDemuxStream * stream) { @@ -2644,14 +2682,6 @@ gst_qtdemux_stream_unref (QtDemuxStream * stream) } } -static void -gst_qtdemux_remove_stream (GstQTDemux * qtdemux, QtDemuxStream * stream) -{ - qtdemux->active_streams = g_list_remove (qtdemux->active_streams, stream); - gst_qtdemux_stream_unref (stream); - qtdemux->n_streams--; -} - static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element, GstStateChange transition) { @@ -2815,10 +2845,11 @@ qtdemux_parse_piff (GstQTDemux * qtdemux, const guint8 * buffer, gint length, gboolean uses_sub_sample_encryption = FALSE; guint32 sample_count; - stream = QTDEMUX_FIRST_STREAM (qtdemux); - if (!stream) + if (QTDEMUX_N_STREAMS (qtdemux) == 0) return; + stream = QTDEMUX_NTH_STREAM (qtdemux, 0); + structure = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0); if (!gst_structure_has_name (structure, "application/x-cenc")) { GST_WARNING_OBJECT (qtdemux, @@ -3171,7 +3202,6 @@ check_update_duration (GstQTDemux * qtdemux, GstClockTime duration) guint i; guint64 movdur; GstClockTime prevdur; - GList *iter; movdur = GSTTIME_TO_QTTIME (qtdemux, duration); @@ -3196,9 +3226,9 @@ check_update_duration (GstQTDemux * qtdemux, GstClockTime duration) qtdemux->segment.stop = fixeddur; } } - for (iter = qtdemux->active_streams, i = 0; iter; - iter = g_list_next (iter), i++) { - QtDemuxStream *stream = QTDEMUX_STREAM (iter->data); + + for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) { + QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i); movdur = GSTTIME_TO_QTSTREAMTIME (stream, duration); if (movdur > stream->duration) { @@ -3517,7 +3547,7 @@ static inline QtDemuxStream * qtdemux_find_stream (GstQTDemux * qtdemux, guint32 id) { QtDemuxStream *stream; - GList *iter; + gint i; /* check */ if (G_UNLIKELY (!id)) { @@ -3525,14 +3555,14 @@ qtdemux_find_stream (GstQTDemux * qtdemux, guint32 id) return NULL; } - for (iter = qtdemux->active_streams; iter; iter = g_list_next (iter)) { - stream = QTDEMUX_STREAM (iter->data); + for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) { + stream = QTDEMUX_NTH_STREAM (qtdemux, i); if (stream->track_id == id) return stream; } if (qtdemux->mss_mode) { /* mss should have only 1 stream anyway */ - return QTDEMUX_FIRST_STREAM (qtdemux); + return QTDEMUX_NTH_STREAM (qtdemux, 0); } return NULL; @@ -3945,7 +3975,7 @@ qtdemux_parse_pssh (GstQTDemux * qtdemux, GNode * node) GstBuffer *pssh = NULL; GstEvent *event = NULL; guint32 parent_box_type; - GList *iter; + gint i; if (G_UNLIKELY (pssh_size < 32U)) { GST_ERROR_OBJECT (qtdemux, "invalid box size"); @@ -3966,8 +3996,8 @@ qtdemux_parse_pssh (GstQTDemux * qtdemux, GNode * node) /* Push an event containing the pssh box onto the queues of all streams. */ event = gst_event_new_protection (sysid_string, pssh, (parent_box_type == FOURCC_moov) ? "isobmff/moov" : "isobmff/moof"); - for (iter = qtdemux->active_streams; iter; iter = g_list_next (iter)) { - QtDemuxStream *stream = QTDEMUX_STREAM (iter->data); + for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) { + QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i); GST_TRACE_OBJECT (qtdemux, "adding protection event for stream %s and system %s", stream->stream_id, sysid_string); @@ -4183,7 +4213,7 @@ qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length, * This way if the user opens a file where the first tfdt is 1 hour * into the presentation, they will not have to wait 1 hour for run * time to catch up and actual playback to start. */ - GList *iter; + gint i; GST_DEBUG_OBJECT (qtdemux, "First fragment has a non-zero tfdt, " "performing an internal seek to %" GST_TIME_FORMAT, @@ -4192,8 +4222,8 @@ qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length, qtdemux->segment.start = min_dts; qtdemux->segment.time = qtdemux->segment.position = min_dts; - for (iter = qtdemux->active_streams; iter; iter = g_list_next (iter)) { - QtDemuxStream *stream = QTDEMUX_STREAM (iter->data); + for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) { + QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i); stream->time_position = min_dts; } @@ -4670,13 +4700,13 @@ gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux) QtDemuxStream *ref_str = NULL; guint64 seg_media_start_mov; /* segment media start time in mov format */ guint64 target_ts; - GList *iter; + gint i; /* Now we choose an arbitrary stream, get the previous keyframe timestamp * and finally align all the other streams on that timestamp with their * respective keyframes */ - for (iter = qtdemux->active_streams; iter; iter = g_list_next (iter)) { - QtDemuxStream *str = QTDEMUX_STREAM (iter->data); + for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) { + QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i); /* No candidate yet, take the first stream */ if (!ref_str) { @@ -4771,10 +4801,10 @@ gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux) ref_k_index = k_index; /* Align them all on this */ - for (iter = qtdemux->active_streams; iter; iter = g_list_next (iter)) { + for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) { guint32 index = 0; GstClockTime seg_time = 0; - QtDemuxStream *str = QTDEMUX_STREAM (iter->data); + QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i); /* aligning reference stream again might lead to backing up to yet another * keyframe (due to timestamp rounding issues), @@ -5288,16 +5318,16 @@ next_segment: static void gst_qtdemux_sync_streams (GstQTDemux * demux) { - GList *iter; + gint i; - if (demux->n_streams <= 1) + if (QTDEMUX_N_STREAMS (demux) <= 1) return; - for (iter = demux->active_streams; iter; iter = g_list_next (iter)) { + for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) { QtDemuxStream *stream; GstClockTime end_time; - stream = QTDEMUX_STREAM (iter->data); + stream = QTDEMUX_NTH_STREAM (demux, i); if (!stream->pad) continue; @@ -5947,20 +5977,20 @@ static gboolean gst_qtdemux_do_fragmented_seek (GstQTDemux * qtdemux) { const QtDemuxRandomAccessEntry *best_entry = NULL; - GList *iter; + gint i; GST_OBJECT_LOCK (qtdemux); - g_assert (qtdemux->n_streams > 0); + g_assert (QTDEMUX_N_STREAMS (qtdemux) > 0); /* first see if we can determine where to go to using mfra, * before we start clearing things */ - for (iter = qtdemux->active_streams; iter; iter = g_list_next (iter)) { + for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) { const QtDemuxRandomAccessEntry *entry; QtDemuxStream *stream; gboolean is_audio_or_video; - stream = QTDEMUX_STREAM (iter->data); + stream = QTDEMUX_NTH_STREAM (qtdemux, i); if (stream->ra_entries == NULL) continue; @@ -5994,10 +6024,10 @@ gst_qtdemux_do_fragmented_seek (GstQTDemux * qtdemux) } /* ok, now we can prepare for processing as of located moof */ - for (iter = qtdemux->active_streams; iter; iter = g_list_next (iter)) { + for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) { QtDemuxStream *stream; - stream = QTDEMUX_STREAM (iter->data); + stream = QTDEMUX_NTH_STREAM (qtdemux, i); g_free (stream->samples); stream->samples = NULL; @@ -6020,7 +6050,7 @@ gst_qtdemux_do_fragmented_seek (GstQTDemux * qtdemux) GST_INFO_OBJECT (qtdemux, "seek to %" GST_TIME_FORMAT ", best fragment " "moof offset: %" G_GUINT64_FORMAT ", ts %" GST_TIME_FORMAT, - GST_TIME_ARGS (QTDEMUX_FIRST_STREAM (qtdemux)->time_position), + GST_TIME_ARGS (QTDEMUX_NTH_STREAM (qtdemux, 0)->time_position), best_entry->moof_offset, GST_TIME_ARGS (best_entry->ts)); qtdemux->moof_offset = best_entry->moof_offset; @@ -6046,7 +6076,7 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux) guint sample_size = 0; gboolean empty = 0; guint size; - GList *iter; + gint i; if (qtdemux->fragmented_seek_pending) { GST_INFO_OBJECT (qtdemux, "pending fragmented seek"); @@ -6061,10 +6091,10 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux) /* Figure out the next stream sample to output, min_time is expressed in * global time and runs over the edit list segments. */ min_time = G_MAXUINT64; - for (iter = qtdemux->active_streams; iter; iter = g_list_next (iter)) { + for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) { GstClockTime position; - stream = QTDEMUX_STREAM (iter->data); + stream = QTDEMUX_NTH_STREAM (qtdemux, i); position = stream->time_position; /* position of -1 is EOS */ @@ -6091,8 +6121,8 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux) } /* gap events for subtitle streams */ - for (iter = qtdemux->active_streams; iter; iter = g_list_next (iter)) { - stream = QTDEMUX_STREAM (iter->data); + for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) { + stream = QTDEMUX_NTH_STREAM (qtdemux, i); if (stream->pad && (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl)) { @@ -6332,7 +6362,7 @@ pause: /* fatal errors need special actions */ /* check EOS */ if (ret == GST_FLOW_EOS) { - if (qtdemux->n_streams == 0) { + if (QTDEMUX_N_STREAMS (qtdemux) == 0) { /* we have no streams, post an error */ gst_qtdemux_post_no_playable_stream_error (qtdemux); } @@ -6399,12 +6429,12 @@ static gboolean has_next_entry (GstQTDemux * demux) { QtDemuxStream *stream; - GList *iter; + gint i; GST_DEBUG_OBJECT (demux, "Checking if there are samples not played yet"); - for (iter = demux->active_streams; iter; iter = g_list_next (iter)) { - stream = QTDEMUX_STREAM (iter->data); + for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) { + stream = QTDEMUX_NTH_STREAM (demux, i); if (stream->sample_index == -1) { stream->sample_index = 0; @@ -6435,13 +6465,13 @@ next_entry_size (GstQTDemux * demux) QtDemuxStream *stream, *target_stream = NULL; guint64 smalloffs = (guint64) - 1; QtDemuxSample *sample; - GList *iter; + gint i; GST_LOG_OBJECT (demux, "Finding entry at offset %" G_GUINT64_FORMAT, demux->offset); - for (iter = demux->active_streams; iter; iter = g_list_next (iter)) { - stream = QTDEMUX_STREAM (iter->data); + for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) { + stream = QTDEMUX_NTH_STREAM (demux, i); if (stream->sample_index == -1) { stream->sample_index = 0; @@ -6586,7 +6616,6 @@ gst_qtdemux_check_send_pending_segment (GstQTDemux * demux) { if (G_UNLIKELY (demux->need_segment)) { gint i; - GList *iter; if (!demux->upstream_format_is_time) { gst_qtdemux_map_and_push_segments (demux, &demux->segment); @@ -6601,9 +6630,8 @@ gst_qtdemux_check_send_pending_segment (GstQTDemux * demux) demux->need_segment = FALSE; /* clear to send tags on all streams */ - for (iter = demux->active_streams, i = 0; iter; - iter = g_list_next (iter), i++) { - QtDemuxStream *stream = QTDEMUX_STREAM (iter->data); + for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) { + QtDemuxStream *stream = QTDEMUX_NTH_STREAM (demux, i); gst_qtdemux_push_tags (demux, stream); if (CUR_STREAM (stream)->sparse) { GST_INFO_OBJECT (demux, "Sending gap event on stream %d", i); @@ -6656,12 +6684,12 @@ gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf) if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DISCONT)) { gboolean is_gap_input = FALSE; - GList *iter; + gint i; GST_DEBUG_OBJECT (demux, "Got DISCONT, marking all streams as DISCONT"); - for (iter = demux->active_streams; iter; iter = g_list_next (iter)) { - QTDEMUX_STREAM (iter->data)->discont = TRUE; + for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) { + QTDEMUX_NTH_STREAM (demux, i)->discont = TRUE; } /* Check if we can land back on our feet in the case where upstream is @@ -6669,9 +6697,9 @@ gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf) * in the case of trick-mode DASH for example) */ if (demux->upstream_format_is_time && GST_BUFFER_OFFSET (inbuf) != GST_BUFFER_OFFSET_NONE) { - for (iter = demux->active_streams; iter; iter = g_list_next (iter)) { + for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) { guint32 res; - QtDemuxStream *stream = QTDEMUX_STREAM (iter->data); + QtDemuxStream *stream = QTDEMUX_NTH_STREAM (demux, i); GST_LOG_OBJECT (demux, "track-id #%u , checking if offset %" G_GUINT64_FORMAT " is a sample start", stream->track_id, GST_BUFFER_OFFSET (inbuf)); @@ -6719,7 +6747,7 @@ gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf) * previously received one. */ if (!is_gap_input && demux->fragmented && demux->segment.rate < 0) { gst_qtdemux_process_adapter (demux, TRUE); - g_list_foreach (demux->active_streams, + g_ptr_array_foreach (demux->active_streams, (GFunc) gst_qtdemux_stream_flush_samples_data, NULL); } } @@ -6789,7 +6817,8 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force) } if (fourcc == FOURCC_mdat) { gint next_entry = next_entry_size (demux); - if (demux->n_streams > 0 && (next_entry != -1 || !demux->fragmented)) { + if (QTDEMUX_N_STREAMS (demux) > 0 && (next_entry != -1 + || !demux->fragmented)) { /* we have the headers, start playback */ demux->state = QTDEMUX_STATE_MOVIE; demux->neededbytes = next_entry; @@ -6879,7 +6908,7 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force) if (fourcc == FOURCC_moov) { /* in usual fragmented setup we could try to scan for more * and end up at the the moov (after mdat) again */ - if (demux->got_moov && demux->n_streams > 0 && + if (demux->got_moov && QTDEMUX_N_STREAMS (demux) > 0 && (!demux->fragmented || demux->last_moov_offset == demux->offset)) { GST_DEBUG_OBJECT (demux, @@ -6904,9 +6933,8 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force) demux->last_moov_offset = demux->offset; /* Update streams with new moov */ - demux->old_streams = - g_list_concat (demux->old_streams, demux->active_streams); - demux->active_streams = NULL; + gst_qtdemux_stream_concat (demux, + demux->old_streams, demux->active_streams); qtdemux_parse_moov (demux, data, demux->neededbytes); qtdemux_node_dump (demux, demux->moov_node); @@ -7045,12 +7073,12 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force) gst_adapter_unmap (demux->adapter); data = NULL; - if (demux->mdatbuffer && demux->n_streams) { + if (demux->mdatbuffer && QTDEMUX_N_STREAMS (demux)) { gsize remaining_data_size = 0; /* the mdat was before the header */ GST_DEBUG_OBJECT (demux, "We have n_streams:%d and mdatbuffer:%p", - demux->n_streams, demux->mdatbuffer); + QTDEMUX_N_STREAMS (demux), demux->mdatbuffer); /* restore our adapter/offset view of things with upstream; * put preceding buffered data ahead of current moov data. * This should also handle evil mdat, moov, mdat cases and alike */ @@ -7127,7 +7155,7 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force) QtDemuxSample *sample; GstClockTime dts, pts, duration; gboolean keyframe; - GList *iter; + gint i; GST_DEBUG_OBJECT (demux, "BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset); @@ -7178,7 +7206,7 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force) data = gst_adapter_map (demux->adapter, demux->todrop); gst_byte_reader_init (&br, data + 8, demux->todrop); if (!qtdemux_parse_cenc_aux_info (demux, - QTDEMUX_FIRST_STREAM (demux), &br, + QTDEMUX_NTH_STREAM (demux, 0), &br, demux->cenc_aux_info_sizes, demux->cenc_aux_sample_count)) { GST_ERROR_OBJECT (demux, "failed to parse cenc auxiliary info"); ret = GST_FLOW_ERROR; @@ -7201,8 +7229,8 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force) gst_qtdemux_check_send_pending_segment (demux); /* Figure out which stream this packet belongs to */ - for (iter = demux->active_streams; iter; iter = g_list_next (iter)) { - stream = QTDEMUX_STREAM (iter->data); + for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) { + stream = QTDEMUX_NTH_STREAM (demux, i); if (stream->sample_index >= stream->n_samples) { /* reset to be checked below G_UNLIKELY (stream == NULL) */ stream = NULL; @@ -7253,8 +7281,8 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force) /* check if all streams are eos */ ret = GST_FLOW_EOS; - for (iter = demux->active_streams; iter; iter = g_list_next (iter)) { - if (!STREAM_IS_EOS (QTDEMUX_STREAM (iter->data))) { + for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) { + if (!STREAM_IS_EOS (QTDEMUX_NTH_STREAM (demux, i))) { ret = GST_FLOW_OK; break; } @@ -10096,9 +10124,10 @@ qtdemux_parse_protection_scheme_info (GstQTDemux * qtdemux, } static gint -qtdemux_track_id_compare_func (QtDemuxStream * stream1, QtDemuxStream * stream2) +qtdemux_track_id_compare_func (QtDemuxStream ** stream1, + QtDemuxStream ** stream2) { - return (gint) stream1->track_id - (gint) stream2->track_id; + return (gint) (*stream1)->track_id - (gint) (*stream2)->track_id; } /* parse the traks. @@ -12225,11 +12254,11 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) /* Insert and sort new stream in track-id order. * This will help in comparing old/new streams during stream update check */ - qtdemux->active_streams = - g_list_insert_sorted (qtdemux->active_streams, stream, + g_ptr_array_add (qtdemux->active_streams, stream); + g_ptr_array_sort (qtdemux->active_streams, (GCompareFunc) qtdemux_track_id_compare_func); - qtdemux->n_streams++; - GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d", qtdemux->n_streams); + GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d", + QTDEMUX_N_STREAMS (qtdemux)); return TRUE; @@ -12284,7 +12313,7 @@ gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux) gint64 size, sys_bitrate, sum_bitrate = 0; GstClockTime duration; guint bitrate; - GList *iter; + gint i; if (qtdemux->fragmented) return; @@ -12312,8 +12341,8 @@ gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux) return; } - for (iter = qtdemux->active_streams; iter; iter = g_list_next (iter)) { - QtDemuxStream *str = QTDEMUX_STREAM (iter->data); + for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) { + QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i); switch (str->subtype) { case FOURCC_soun: case FOURCC_vide: @@ -12381,16 +12410,14 @@ static GstFlowReturn qtdemux_prepare_streams (GstQTDemux * qtdemux) { GstFlowReturn ret = GST_FLOW_OK; - GList *iter, *next; + gint i; GST_DEBUG_OBJECT (qtdemux, "prepare streams"); - for (iter = qtdemux->active_streams; ret == GST_FLOW_OK && iter; iter = next) { - QtDemuxStream *stream = QTDEMUX_STREAM (iter->data); + for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) { + QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i); guint32 sample_num = 0; - next = iter->next; - GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT, stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc)); @@ -12415,13 +12442,15 @@ qtdemux_prepare_streams (GstQTDemux * qtdemux) * in push mode, we'll just have to deal with it */ if (G_UNLIKELY (qtdemux->pullbased && !stream->n_samples)) { GST_DEBUG_OBJECT (qtdemux, "no samples for stream; discarding"); - gst_qtdemux_remove_stream (qtdemux, stream); + g_ptr_array_remove_index (qtdemux->active_streams, i); + i--; continue; } else if (stream->track_id == qtdemux->chapters_track_id && (stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl)) { /* TODO - parse chapters track and expose it as GstToc; For now just ignore it so that it doesn't look like a subtitle track */ - gst_qtdemux_remove_stream (qtdemux, stream); + g_ptr_array_remove_index (qtdemux->active_streams, i); + i--; continue; } @@ -12436,42 +12465,29 @@ qtdemux_prepare_streams (GstQTDemux * qtdemux) return ret; } -static GList * -_stream_in_list (GList * list, QtDemuxStream * stream) +static gboolean +_stream_equal_func (const QtDemuxStream * stream, const gchar * stream_id) { - GList *iter; - - for (iter = list; iter; iter = g_list_next (iter)) { - QtDemuxStream *tmp = QTDEMUX_STREAM (iter->data); - if (!g_strcmp0 (tmp->stream_id, stream->stream_id)) - return iter; - } - - return NULL; + return g_strcmp0 (stream->stream_id, stream_id) == 0; } static gboolean qtdemux_is_streams_update (GstQTDemux * qtdemux) { - GList *new, *old; - - if (!qtdemux->active_streams) - return FALSE; - - /* streams in list are sorted in track-id order */ - for (new = qtdemux->active_streams, old = qtdemux->old_streams; new && old; - new = g_list_next (new), old = g_list_next (old)) { - - /* Different stream-id, updated */ - if (g_strcmp0 (QTDEMUX_STREAM (new->data)->stream_id, - QTDEMUX_STREAM (old->data)->stream_id)) - return TRUE; - } + gint i; /* Different length, updated */ - if (new != NULL || old != NULL) + if (QTDEMUX_N_STREAMS (qtdemux) != qtdemux->old_streams->len) return TRUE; + /* streams in list are sorted in track-id order */ + for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) { + /* Different stream-id, updated */ + if (g_strcmp0 (QTDEMUX_NTH_STREAM (qtdemux, i)->stream_id, + QTDEMUX_NTH_OLD_STREAM (qtdemux, i)->stream_id)) + return TRUE; + } + return FALSE; } @@ -12492,7 +12508,7 @@ qtdemux_reuse_and_configure_stream (GstQTDemux * qtdemux, static gboolean qtdemux_update_streams (GstQTDemux * qtdemux) { - GList *iter, *next; + gint i; g_assert (qtdemux->streams_aware); /* At below, figure out which stream in active_streams has identical stream-id @@ -12504,31 +12520,31 @@ qtdemux_update_streams (GstQTDemux * qtdemux) * old_streams : existing streams (belong to previous moov) */ - /* Count n_streams again */ - qtdemux->n_streams = 0; - - for (iter = qtdemux->active_streams; iter; iter = next) { - GList *tmp; - QtDemuxStream *stream = QTDEMUX_STREAM (iter->data); - - next = iter->next; + for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) { + QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i); + QtDemuxStream *oldstream = NULL; + guint target; GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT, stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc)); - qtdemux->n_streams++; + if (g_ptr_array_find_with_equal_func (qtdemux->old_streams, + stream->stream_id, (GEqualFunc) _stream_equal_func, &target)) { + oldstream = QTDEMUX_NTH_OLD_STREAM (qtdemux, target); - if ((tmp = _stream_in_list (qtdemux->old_streams, stream)) != NULL - && QTDEMUX_STREAM (tmp->data)->pad) { - QtDemuxStream *oldstream = QTDEMUX_STREAM (tmp->data); + /* null pad stream cannot be reused */ + if (oldstream->pad == NULL) + oldstream = NULL; + } + if (oldstream) { GST_DEBUG_OBJECT (qtdemux, "Reuse track-id %d", oldstream->track_id); if (!qtdemux_reuse_and_configure_stream (qtdemux, oldstream, stream)) return FALSE; - qtdemux->old_streams = g_list_remove (qtdemux->old_streams, oldstream); - gst_qtdemux_stream_unref (oldstream); + /* we don't need to preserve order of old streams */ + g_ptr_array_remove_fast (qtdemux->old_streams, oldstream); } else { GstTagList *list; @@ -12547,24 +12563,21 @@ qtdemux_update_streams (GstQTDemux * qtdemux) static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux) { - GList *iter, *next; + gint i; GST_DEBUG_OBJECT (qtdemux, "exposing streams"); if (!qtdemux_is_streams_update (qtdemux)) { - GList *new, *old; - GST_DEBUG_OBJECT (qtdemux, "Reuse all streams"); - for (new = qtdemux->active_streams, old = qtdemux->old_streams; new && old; - new = g_list_next (new), old = g_list_next (old)) { - if (!qtdemux_reuse_and_configure_stream (qtdemux, - QTDEMUX_STREAM (old->data), QTDEMUX_STREAM (new->data))) + for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) { + QtDemuxStream *new_stream = QTDEMUX_NTH_STREAM (qtdemux, i); + QtDemuxStream *old_stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i); + if (!qtdemux_reuse_and_configure_stream (qtdemux, old_stream, new_stream)) return GST_FLOW_ERROR; } - g_list_free_full (qtdemux->old_streams, - (GDestroyNotify) gst_qtdemux_stream_unref); - qtdemux->old_streams = NULL; + g_ptr_array_remove_range (qtdemux->old_streams, + 0, qtdemux->old_streams->len); return GST_FLOW_OK; } @@ -12573,8 +12586,8 @@ qtdemux_expose_streams (GstQTDemux * qtdemux) if (!qtdemux_update_streams (qtdemux)) return GST_FLOW_ERROR; } else { - for (iter = qtdemux->active_streams; iter; iter = g_list_next (iter)) { - QtDemuxStream *stream = QTDEMUX_STREAM (iter->data); + for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) { + QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i); GstTagList *list; /* now we have all info and can expose */ @@ -12591,9 +12604,8 @@ qtdemux_expose_streams (GstQTDemux * qtdemux) gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux)); /* If we have still old_streams, it's no more used stream */ - for (iter = qtdemux->old_streams; iter; iter = next) { - QtDemuxStream *stream = QTDEMUX_STREAM (iter->data); - next = g_list_next (iter); + for (i = 0; i < qtdemux->old_streams->len; i++) { + QtDemuxStream *stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i); if (stream->pad) { GstEvent *event; @@ -12604,15 +12616,14 @@ qtdemux_expose_streams (GstQTDemux * qtdemux) gst_pad_push_event (stream->pad, event); } - - qtdemux->old_streams = g_list_remove (qtdemux->old_streams, stream); - gst_qtdemux_stream_unref (stream); } + g_ptr_array_remove_range (qtdemux->old_streams, 0, qtdemux->old_streams->len); + /* check if we should post a redirect in case there is a single trak * and it is a redirecting trak */ - if (qtdemux->n_streams == 1 && - QTDEMUX_FIRST_STREAM (qtdemux)->redirect_uri != NULL) { + if (QTDEMUX_N_STREAMS (qtdemux) == 1 && + QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri != NULL) { GstMessage *m; GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with " @@ -12620,14 +12631,13 @@ qtdemux_expose_streams (GstQTDemux * qtdemux) m = gst_message_new_element (GST_OBJECT_CAST (qtdemux), gst_structure_new ("redirect", "new-location", G_TYPE_STRING, - QTDEMUX_FIRST_STREAM (qtdemux)->redirect_uri, NULL)); + QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri, NULL)); gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m); qtdemux->posted_redirect = TRUE; } - for (iter = qtdemux->active_streams; iter; iter = g_list_next (iter)) { - qtdemux_do_allocation (QTDEMUX_STREAM (iter->data), qtdemux); - } + g_ptr_array_foreach (qtdemux->active_streams, + (GFunc) qtdemux_do_allocation, qtdemux); qtdemux->need_segment = TRUE; diff --git a/gst/isomp4/qtdemux.h b/gst/isomp4/qtdemux.h index 50d308b8cd..83a050a431 100644 --- a/gst/isomp4/qtdemux.h +++ b/gst/isomp4/qtdemux.h @@ -75,10 +75,9 @@ struct _GstQTDemux { GMutex expose_lock; /* list of QtDemuxStream */ - GList *active_streams; - GList *old_streams; + GPtrArray *active_streams; + GPtrArray *old_streams; - gint n_streams; gint n_video_streams; gint n_audio_streams; gint n_sub_streams;