diff options
author | Wim Taymans <wim.taymans@gmail.com> | 2006-04-04 08:31:10 +0000 |
---|---|---|
committer | Wim Taymans <wim.taymans@gmail.com> | 2006-04-04 08:31:10 +0000 |
commit | d1e0df82da8caf874d06a89f40538c067c165f4b (patch) | |
tree | 0c4b547e6b8b14bd21b0b20ccf6ddbca3dfc5101 | |
parent | 9c10d61e50fa75852ea5fb80e3bad0b7a9d8a4de (diff) | |
download | gst-plugins-bad-d1e0df82da8caf874d06a89f40538c067c165f4b.tar.gz gst-plugins-bad-d1e0df82da8caf874d06a89f40538c067c165f4b.tar.bz2 gst-plugins-bad-d1e0df82da8caf874d06a89f40538c067c165f4b.zip |
gst/qtdemux/qtdemux.*: Handle stss boxes so we can mark and find keyframes.
Original commit message from CVS:
* gst/qtdemux/qtdemux.c: (gst_qtdemux_init),
(gst_qtdemux_push_event), (gst_qtdemux_go_back),
(gst_qtdemux_perform_seek), (gst_qtdemux_do_seek),
(gst_qtdemux_handle_src_event), (plugin_init),
(gst_qtdemux_change_state), (gst_qtdemux_loop_state_movie),
(gst_qtdemux_loop), (gst_qtdemux_chain),
(qtdemux_sink_activate_pull), (gst_qtdemux_add_stream),
(qtdemux_parse), (qtdemux_parse_tree), (qtdemux_parse_trak),
(qtdemux_parse_udta), (qtdemux_tag_add_str), (qtdemux_tag_add_num),
(qtdemux_tag_add_gnre), (gst_qtdemux_handle_esds):
* gst/qtdemux/qtdemux.h:
Handle stss boxes so we can mark and find keyframes.
Implement correct accurate and keyframe seeking.
Use _DEBUG_OBJECT when possible.
-rw-r--r-- | ChangeLog | 17 | ||||
-rw-r--r-- | gst/qtdemux/qtdemux.c | 439 | ||||
-rw-r--r-- | gst/qtdemux/qtdemux.h | 10 |
3 files changed, 339 insertions, 127 deletions
@@ -1,3 +1,20 @@ +2006-04-04 Wim Taymans <wim@fluendo.com> + + * gst/qtdemux/qtdemux.c: (gst_qtdemux_init), + (gst_qtdemux_push_event), (gst_qtdemux_go_back), + (gst_qtdemux_perform_seek), (gst_qtdemux_do_seek), + (gst_qtdemux_handle_src_event), (plugin_init), + (gst_qtdemux_change_state), (gst_qtdemux_loop_state_movie), + (gst_qtdemux_loop), (gst_qtdemux_chain), + (qtdemux_sink_activate_pull), (gst_qtdemux_add_stream), + (qtdemux_parse), (qtdemux_parse_tree), (qtdemux_parse_trak), + (qtdemux_parse_udta), (qtdemux_tag_add_str), (qtdemux_tag_add_num), + (qtdemux_tag_add_gnre), (gst_qtdemux_handle_esds): + * gst/qtdemux/qtdemux.h: + Handle stss boxes so we can mark and find keyframes. + Implement correct accurate and keyframe seeking. + Use _DEBUG_OBJECT when possible. + 2006-04-03 Tim-Philipp Müller <tim at centricular dot net> * tests/check/Makefile.am: diff --git a/gst/qtdemux/qtdemux.c b/gst/qtdemux/qtdemux.c index 0795e5c7..77c6d577 100644 --- a/gst/qtdemux/qtdemux.c +++ b/gst/qtdemux/qtdemux.c @@ -72,6 +72,7 @@ struct _QtDemuxSample guint64 offset; guint64 timestamp; /* In GstClockTime */ guint32 duration; /* in stream->timescale units */ + gboolean keyframe; /* TRUE when this packet is a keyframe */ }; struct _QtDemuxStream @@ -83,6 +84,7 @@ struct _QtDemuxStream int n_samples; QtDemuxSample *samples; int timescale; + gboolean all_keyframe; /* TRUE when all packets are keyframes (no stss) */ int sample_index; @@ -338,6 +340,7 @@ gst_qtdemux_init (GstQTDemux * qtdemux) qtdemux->offset = 0; qtdemux->mdatoffset = GST_CLOCK_TIME_NONE; qtdemux->mdatbuffer = NULL; + gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME); } static void @@ -454,9 +457,9 @@ gst_qtdemux_handle_src_query (GstPad * pad, GstQuery * query) return res; } -/* sends event to all source pads; takes ownership of the event */ +/* push event on all source pads; takes ownership of the event */ static void -gst_qtdemux_send_event (GstQTDemux * qtdemux, GstEvent * event) +gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event) { guint n; @@ -469,79 +472,242 @@ gst_qtdemux_send_event (GstQTDemux * qtdemux, GstEvent * event) gst_event_unref (event); } +/* move all streams back to the given position, This function will + * always position the streams on a keyframe before offset. + * + * Start to search back from the stream sample_index position or + * when end is TRUE, from the last sample of the stream. + * + * Returns the minimum of the timestamps of the positions of all streams. + */ +static guint64 +gst_qtdemux_go_back (GstQTDemux * qtdemux, gboolean end, guint64 offset) +{ + gint n; + guint64 min_time = G_MAXUINT64; + + /* resync to new time */ + for (n = 0; n < qtdemux->n_streams; n++) { + QtDemuxStream *str; + gboolean keyframe; + gint i; + + str = qtdemux->streams[n]; + keyframe = str->all_keyframe; + + if (end) + str->sample_index = str->n_samples - 1; + + if (str->sample_index == 0) { + /* can't go back, we're at the beginning */ + min_time = 0; + continue; + } + + for (i = str->sample_index; i >= 0; i--) { + guint64 timestamp; + + timestamp = str->samples[i].timestamp; + + /* Seek to the sample just before the desired offset and + * let downstream throw away bits outside of the segment */ + if (timestamp <= offset) { + /* update the keyframe flag */ + keyframe = keyframe | str->samples[i].keyframe; + if (keyframe) { + GST_DEBUG_OBJECT (qtdemux, + "found keyframe at sample %d, %" GST_TIME_FORMAT, i, + GST_TIME_ARGS (timestamp)); + if (timestamp < min_time) + min_time = timestamp; + break; + } + } + } + + /* did not find anything, position to beginning */ + if (i <= 0) { + i = 0; + min_time = 0; + } + + str->sample_index = i; + } + return min_time; +} + +/* perform the seek. + * + * We always go to the keyframe before the desired seek position. If + * the seek was to a keyframe, we update the last_stop with the position + * of the keyframe, else we leve the event as-is and it will be clipped + * automatically to the right segment boundaries by downstream elements. + */ static gboolean -gst_qtdemux_handle_src_event (GstPad * pad, GstEvent * event) +gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment) { - gboolean res = TRUE; - GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad)); + gint64 desired_offset; + guint64 min; - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK:{ - GstFormat format; - GstSeekFlags flags; - gint64 desired_offset; + desired_offset = segment->last_stop; - /* FIXME do seeking correctly */ - gst_event_parse_seek (event, NULL, &format, &flags, NULL, - &desired_offset, NULL, NULL); - GST_DEBUG ("seek format %d", format); + GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT, + GST_TIME_ARGS (desired_offset)); - switch (format) { - case GST_FORMAT_TIME:{ - gint i = 0, n; - QtDemuxStream *stream = gst_pad_get_element_private (pad); + /* position all streams to key unit before the desired time, + * start searching from the last sample in the stream. */ + min = gst_qtdemux_go_back (qtdemux, TRUE, desired_offset); - GST_DEBUG ("seeking to %" G_GINT64_FORMAT, desired_offset); + if (segment->flags & GST_SEEK_FLAG_KEY_UNIT) { + GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %" GST_TIME_FORMAT, + GST_TIME_ARGS (min)); + /* key unit, we seek to min, so back off streams to this new + * position. We start from our current position. */ + gst_qtdemux_go_back (qtdemux, FALSE, min); - if (!stream->n_samples) { - res = FALSE; - break; - } + /* update the segment to the position of the keyframes */ + segment->last_stop = min; + segment->time = min; + } + return TRUE; +} - /* unlock upstream pull_range */ - gst_pad_push_event (qtdemux->sinkpad, gst_event_new_flush_start ()); - /* make sure out loop function exits */ - gst_qtdemux_send_event (qtdemux, gst_event_new_flush_start ()); - - /* wait for streaming to finish */ - GST_PAD_STREAM_LOCK (qtdemux->sinkpad); - - /* resync to new time */ - for (n = 0; n < qtdemux->n_streams; n++) { - QtDemuxStream *str = qtdemux->streams[n]; - - for (i = 0; i < str->n_samples; i++) { - /* Seek to the sample just before the desired offset and - * let downstream throw away bits outside of the segment */ - if (str->samples[i].timestamp > desired_offset) { - if (i > 0) - --i; - break; - } - } - str->sample_index = i; - } - /* prepare for streaming again */ - gst_pad_push_event (qtdemux->sinkpad, gst_event_new_flush_stop ()); - gst_qtdemux_send_event (qtdemux, gst_event_new_flush_stop ()); +static gboolean +gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event) +{ + gdouble rate; + GstFormat format; + GstSeekFlags flags; + GstSeekType cur_type, stop_type; + gint64 cur, stop; + gboolean flush; + gboolean res; + gboolean update; + GstSegment seeksegment; + GstEvent *newsegment; + + if (event) { + GST_DEBUG_OBJECT (qtdemux, "doing seek with event"); + + gst_event_parse_seek (event, &rate, &format, &flags, + &cur_type, &cur, &stop_type, &stop); + + /* we have to have a format as the segment format. Try to convert + * if not. */ + if (format != GST_FORMAT_TIME) { + GstFormat fmt; + + fmt = GST_FORMAT_TIME; + res = TRUE; + if (cur_type != GST_SEEK_TYPE_NONE) + res = gst_pad_query_convert (pad, format, cur, &fmt, &cur); + if (res && stop_type != GST_SEEK_TYPE_NONE) + res = gst_pad_query_convert (pad, format, stop, &fmt, &stop); + if (!res) + goto no_format; + + format = fmt; + } + } else { + GST_DEBUG_OBJECT (qtdemux, "doing seek without event"); + flags = 0; + } - gst_qtdemux_send_event (qtdemux, - gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, - desired_offset, GST_CLOCK_TIME_NONE, desired_offset)); + flush = flags & GST_SEEK_FLAG_FLUSH; - /* and restart */ - gst_pad_start_task (qtdemux->sinkpad, - (GstTaskFunction) gst_qtdemux_loop, qtdemux->sinkpad); + GST_DEBUG_OBJECT (qtdemux, "seek format %d", format); - GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad); - break; - } - default: - res = FALSE; - break; - } + if (flush) { + /* unlock upstream pull_range */ + gst_pad_push_event (qtdemux->sinkpad, gst_event_new_flush_start ()); + /* make sure out loop function exits */ + gst_qtdemux_push_event (qtdemux, gst_event_new_flush_start ()); + } else { + qtdemux->segment_running = FALSE; + gst_pad_pause_task (qtdemux->sinkpad); + } + + /* wait for streaming to finish */ + GST_PAD_STREAM_LOCK (qtdemux->sinkpad); + + /* copy segment, we need this because we still need the old + * segment when we close the current segment. */ + memcpy (&seeksegment, &qtdemux->segment, sizeof (GstSegment)); + + if (event) { + /* configure the segment with the seek variables */ + GST_DEBUG_OBJECT (qtdemux, "configuring seek"); + gst_segment_set_seek (&seeksegment, rate, format, flags, + cur_type, cur, stop_type, stop, &update); + } + + /* now do the seek */ + res = gst_qtdemux_perform_seek (qtdemux, &seeksegment); + + /* prepare for streaming again */ + if (flush) { + gst_pad_push_event (qtdemux->sinkpad, gst_event_new_flush_stop ()); + gst_qtdemux_push_event (qtdemux, gst_event_new_flush_stop ()); + } else if (qtdemux->segment_running) { + /* we are running the current segment and doing a non-flushing seek, + * close the segment first based on the last_stop. */ + GST_DEBUG_OBJECT (qtdemux, "closing running segment %" G_GINT64_FORMAT + " to %" G_GINT64_FORMAT, qtdemux->segment.start, + qtdemux->segment.last_stop); + + gst_qtdemux_push_event (qtdemux, + gst_event_new_new_segment (TRUE, + qtdemux->segment.rate, qtdemux->segment.format, + qtdemux->segment.start, qtdemux->segment.last_stop, + qtdemux->segment.time)); + } + + memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment)); + + if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) { + gst_element_post_message (GST_ELEMENT_CAST (qtdemux), + gst_message_new_segment_start (GST_OBJECT_CAST (qtdemux), + qtdemux->segment.format, qtdemux->segment.last_stop)); + } + + /* now send the newsegment */ + GST_DEBUG_OBJECT (qtdemux, "Sending newsegment from %" G_GINT64_FORMAT + " to %" G_GINT64_FORMAT, qtdemux->segment.start, stop); + + newsegment = + gst_event_new_new_segment (FALSE, qtdemux->segment.rate, + qtdemux->segment.format, qtdemux->segment.last_stop, + qtdemux->segment.stop, qtdemux->segment.time); + + gst_qtdemux_push_event (qtdemux, newsegment); + + qtdemux->segment_running = TRUE; + gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop, + qtdemux->sinkpad); + + /* and restart */ + GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad); + + return TRUE; + + /* ERRORS */ +no_format: + { + GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted."); + return FALSE; + } +} + +static gboolean +gst_qtdemux_handle_src_event (GstPad * pad, GstEvent * event) +{ + gboolean res = TRUE; + GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + res = gst_qtdemux_do_seek (qtdemux, pad, event); break; - } default: res = FALSE; break; @@ -595,6 +761,13 @@ gst_qtdemux_change_state (GstElement * element, GstStateChange transition) GstQTDemux *qtdemux = GST_QTDEMUX (element); GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE; + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + break; + default: + break; + } + result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); switch (transition) { @@ -620,6 +793,7 @@ gst_qtdemux_change_state (GstElement * element, GstStateChange transition) g_free (qtdemux->streams[n]); } qtdemux->n_streams = 0; + gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME); break; } default: @@ -786,6 +960,7 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux) } if (buf) { + buf = gst_buffer_make_metadata_writable (buf); /* hum... FIXME changing framerate breaks horribly, better set * an average framerate, or get rid of the framerate property. */ if (stream->subtype == GST_MAKE_FOURCC ('v', 'i', 'd', 'e')) { @@ -803,29 +978,36 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux) /* first buffer? */ if (qtdemux->last_ts == GST_CLOCK_TIME_NONE) { - gst_qtdemux_send_event (qtdemux, + gst_qtdemux_push_event (qtdemux, gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, GST_CLOCK_TIME_NONE, 0)); } /* timestamps of AMR aren't known... */ - if (stream->fourcc != GST_MAKE_FOURCC ('s', 'a', 'm', 'r')) { + if (stream->fourcc == GST_MAKE_FOURCC ('s', 'a', 'm', 'r')) { + if (stream->sample_index == 0) + GST_BUFFER_TIMESTAMP (buf) = 0; + } else { GST_BUFFER_TIMESTAMP (buf) = stream->samples[stream->sample_index].timestamp; qtdemux->last_ts = GST_BUFFER_TIMESTAMP (buf); GST_BUFFER_DURATION (buf) = gst_util_uint64_scale_int (stream->samples[stream->sample_index].duration, GST_SECOND, stream->timescale); - } else { - if (stream->sample_index == 0) { - GST_BUFFER_TIMESTAMP (buf) = 0; - } + } + gst_segment_set_last_stop (&qtdemux->segment, GST_FORMAT_TIME, + qtdemux->last_ts); + + if (!(stream->all_keyframe + || stream->samples[stream->sample_index].keyframe)) { + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); } GST_LOG_OBJECT (qtdemux, "Pushing buffer with time %" GST_TIME_FORMAT " on pad %p", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), stream->pad); gst_buffer_set_caps (buf, stream->caps); + ret = gst_pad_push (stream->pad, buf); } @@ -862,9 +1044,10 @@ gst_qtdemux_loop (GstPad * pad) if ((ret != GST_FLOW_OK) && (ret != GST_FLOW_NOT_LINKED)) { GST_LOG_OBJECT (qtdemux, "pausing task, reason %s", gst_flow_get_name (ret)); + qtdemux->segment_running = FALSE; gst_pad_pause_task (qtdemux->sinkpad); if (GST_FLOW_IS_FATAL (ret)) { - gst_pad_event_default (qtdemux->sinkpad, gst_event_new_eos ()); + gst_qtdemux_push_event (qtdemux, gst_event_new_eos ()); GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED, (NULL), ("stream stopped, reason %s", gst_flow_get_name (ret))); } @@ -1086,7 +1269,7 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf) /* first buffer? */ /* FIXME : this should be handled in sink_event */ if (demux->last_ts == GST_CLOCK_TIME_NONE) { - gst_qtdemux_send_event (demux, + gst_qtdemux_push_event (demux, gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, GST_CLOCK_TIME_NONE, 0)); } @@ -1165,8 +1348,10 @@ qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active) if (active) { /* if we have a scheduler we can start the task */ demux->pullbased = TRUE; + demux->segment_running = TRUE; gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop, sinkpad); } else { + demux->segment_running = FALSE; gst_pad_stop_task (sinkpad); } @@ -1248,17 +1433,17 @@ gst_qtdemux_add_stream (GstQTDemux * qtdemux, GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream; qtdemux->streams[qtdemux->n_streams] = stream; qtdemux->n_streams++; - GST_DEBUG ("n_streams is now %d", qtdemux->n_streams); + GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d", qtdemux->n_streams); gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event); gst_pad_set_query_type_function (stream->pad, gst_qtdemux_get_src_query_types); gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query); - GST_DEBUG ("setting caps %" GST_PTR_FORMAT, stream->caps); + GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT, stream->caps); gst_pad_set_caps (stream->pad, stream->caps); - GST_DEBUG ("adding pad %s %p to qtdemux %p", + GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p", GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux); gst_element_add_pad (GST_ELEMENT (qtdemux), stream->pad); if (list) { @@ -1661,9 +1846,9 @@ qtdemux_parse (GstQTDemux * qtdemux, GNode * node, void *buffer, int length) guint32 version; int tlen; - GST_DEBUG ("parsing in mp4v"); + GST_DEBUG_OBJECT (qtdemux, "parsing in mp4v"); version = QTDEMUX_GUINT32_GET (buffer + 16); - GST_DEBUG ("version %08x", version); + GST_DEBUG_OBJECT (qtdemux, "version %08x", version); if (1 || version == 0x00000000) { buf = buffer + 0x32; @@ -1673,9 +1858,9 @@ qtdemux_parse (GstQTDemux * qtdemux, GNode * node, void *buffer, int length) * the iso format uses C strings. Check the file * type before attempting to parse the string here. */ tlen = QTDEMUX_GUINT8_GET (buf); - GST_DEBUG ("tlen = %d", tlen); + GST_DEBUG_OBJECT (qtdemux, "tlen = %d", tlen); buf++; - GST_DEBUG ("string = %.*s", tlen, (char *) buf); + GST_DEBUG_OBJECT (qtdemux, "string = %.*s", tlen, (char *) buf); /* the string has a reserved space of 32 bytes so skip * the remaining 31 */ buf += 31; @@ -1747,16 +1932,16 @@ qtdemux_parse (GstQTDemux * qtdemux, GNode * node, void *buffer, int length) buf = buffer + 12; end = buffer + length; version = QTDEMUX_GUINT32_GET (buffer + 16); - GST_DEBUG ("version %08x", version); + GST_DEBUG_OBJECT (qtdemux, "version %08x", version); if (1 || version == 0x00000000) { buf = buffer + 0x32; end = buffer + length; tlen = QTDEMUX_GUINT8_GET (buf); - GST_DEBUG ("tlen = %d", tlen); + GST_DEBUG_OBJECT (qtdemux, "tlen = %d", tlen); buf++; - GST_DEBUG ("string = %.*s", tlen, (char *) buf); + GST_DEBUG_OBJECT (qtdemux, "string = %.*s", tlen, (char *) buf); buf += tlen; buf += 23; @@ -2343,7 +2528,8 @@ qtdemux_parse_tree (GstQTDemux * qtdemux) qtdemux_parse_udta (qtdemux, udta); if (qtdemux->tag_list) { - GST_DEBUG ("calling gst_element_found_tags with %" GST_PTR_FORMAT, + GST_DEBUG_OBJECT (qtdemux, + "calling gst_element_found_tags with %" GST_PTR_FORMAT, qtdemux->tag_list); gst_element_found_tags (GST_ELEMENT (qtdemux), qtdemux->tag_list); qtdemux->tag_list = NULL; @@ -2365,6 +2551,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) GNode *stbl; GNode *stsd; GNode *stsc; + GNode *stss; GNode *stsz; GNode *stco; GNode *co64; @@ -2696,6 +2883,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) /* sample time */ stts = qtdemux_tree_get_child_by_type (stbl, FOURCC_stts); g_assert (stts); + /* sample sync, can be NULL */ + stss = qtdemux_tree_get_child_by_type (stbl, FOURCC_stss); sample_size = QTDEMUX_GUINT32_GET (stsz->data + 12); if (sample_size == 0) { @@ -2709,6 +2898,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) for (i = 0; i < n_samples; i++) { samples[i].size = QTDEMUX_GUINT32_GET (stsz->data + i * 4 + 20); GST_LOG_OBJECT (qtdemux, "sample %d has size %d", i, samples[i].size); + /* init other fields to defaults for this sample */ + samples[i].keyframe = FALSE; } n_samples_per_chunk = QTDEMUX_GUINT32_GET (stsc->data + 12); index = 0; @@ -2767,6 +2958,26 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) index++; } } + if (stss) { + /* mark keyframes */ + guint32 n_sample_syncs; + + n_sample_syncs = QTDEMUX_GUINT32_GET (stss->data + 12); + if (n_sample_syncs == 0) { + stream->all_keyframe = TRUE; + } else { + offset = 16; + for (i = 0; i < n_sample_syncs; i++) { + /* not that the first sample is index 1, not 0 */ + index = QTDEMUX_GUINT32_GET (stss->data + offset); + samples[index - 1].keyframe = TRUE; + offset += 4; + } + } + } else { + /* no stss, all samples are keyframes */ + stream->all_keyframe = TRUE; + } } else { guint64 timestamp = 0; @@ -2835,6 +3046,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) samples[j].duration = samples_per_chunk * stream->timescale / (stream->rate / 2); samples[j].timestamp = timestamp; + samples[j].keyframe = TRUE; if (stream->rate > 0) { timestamp += gst_util_uint64_scale_int (samples_per_chunk, @@ -2851,31 +3063,6 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) sample_index += samples_per_chunk; } } -#if 0 - done2: - n_sample_times = QTDEMUX_GUINT32_GET (stts->data + 12); - GST_LOG ("n_sample_times = %d", n_sample_times); - timestamp = 0; - index = 0; - sample_index = 0; - for (i = 0; i < n_sample_times; i++) { - int duration; - guint64 time; - - sample_index += QTDEMUX_GUINT32_GET (stts->data + 16 + 8 * i); - duration = QTDEMUX_GUINT32_GET (stts->data + 16 + 8 * i + 4); - for (; index < n_samples && samples[index].sample_index < sample_index; - index++) { - int size; - - samples[index].timestamp = timestamp; - size = samples[index + 1].sample_index - samples[index].sample_index; - time = GST_SECOND / stream->rate; //(GST_SECOND * duration * samples[index].size)/stream->timescale ; - timestamp += time; - samples[index].duration = time; - } - } -#endif } done2: #if 0 @@ -2909,7 +3096,7 @@ qtdemux_parse_udta (GstQTDemux * qtdemux, GNode * udta) return; } - GST_DEBUG ("new tag list"); + GST_DEBUG_OBJECT (qtdemux, "new tag list"); qtdemux->tag_list = gst_tag_list_new (); node = qtdemux_tree_get_child_by_type (ilst, FOURCC__nam); @@ -2980,7 +3167,7 @@ qtdemux_tag_add_str (GstQTDemux * qtdemux, const char *tag, GNode * node) type = QTDEMUX_GUINT32_GET (data->data + 8); if (type == 0x00000001) { s = g_strndup ((char *) data->data + 16, len - 16); - GST_DEBUG ("adding tag %s", s); + GST_DEBUG_OBJECT (qtdemux, "adding tag %s", s); gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag, s, NULL); g_free (s); } @@ -3003,7 +3190,7 @@ qtdemux_tag_add_num (GstQTDemux * qtdemux, const char *tag1, if (type == 0x00000000 && len >= 22) { n1 = GST_READ_UINT16_BE (data->data + 18); n2 = GST_READ_UINT16_BE (data->data + 20); - GST_DEBUG ("adding tag %d/%d", n1, n2); + GST_DEBUG_OBJECT (qtdemux, "adding tag %d/%d", n1, n2); gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag1, n1, tag2, n2, NULL); } @@ -3056,7 +3243,7 @@ qtdemux_tag_add_gnre (GstQTDemux * qtdemux, const char *tag, GNode * node) if (type == 0x00000000 && len >= 18) { n = GST_READ_UINT16_BE (data->data + 16); if (n > 0 && n < sizeof (genres) / sizeof (char *)) { - GST_DEBUG ("adding %d [%s]", n, genres[n]); + GST_DEBUG_OBJECT (qtdemux, "adding %d [%s]", n, genres[n]); gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag, genres[n], NULL); } @@ -3097,38 +3284,44 @@ gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream, gst_util_dump_mem (ptr, len); ptr += 8; - GST_DEBUG ("version/flags = %08x", QTDEMUX_GUINT32_GET (ptr)); + GST_DEBUG_OBJECT (qtdemux, "version/flags = %08x", QTDEMUX_GUINT32_GET (ptr)); ptr += 4; while (ptr < end) { tag = QTDEMUX_GUINT8_GET (ptr); - GST_DEBUG ("tag = %02x", tag); + GST_DEBUG_OBJECT (qtdemux, "tag = %02x", tag); ptr++; len = get_size (ptr, &ptr); - GST_DEBUG ("len = %d", len); + GST_DEBUG_OBJECT (qtdemux, "len = %d", len); switch (tag) { case 0x03: - GST_DEBUG ("ID %04x", QTDEMUX_GUINT16_GET (ptr)); - GST_DEBUG ("priority %04x", QTDEMUX_GUINT8_GET (ptr + 2)); + GST_DEBUG_OBJECT (qtdemux, "ID %04x", QTDEMUX_GUINT16_GET (ptr)); + GST_DEBUG_OBJECT (qtdemux, "priority %04x", + QTDEMUX_GUINT8_GET (ptr + 2)); ptr += 3; break; case 0x04: - GST_DEBUG ("object_type_id %02x", QTDEMUX_GUINT8_GET (ptr)); - GST_DEBUG ("stream_type %02x", QTDEMUX_GUINT8_GET (ptr + 1)); - GST_DEBUG ("buffer_size_db %02x", QTDEMUX_GUINT24_GET (ptr + 2)); - GST_DEBUG ("max bitrate %d", QTDEMUX_GUINT32_GET (ptr + 5)); - GST_DEBUG ("avg bitrate %d", QTDEMUX_GUINT32_GET (ptr + 9)); + GST_DEBUG_OBJECT (qtdemux, "object_type_id %02x", + QTDEMUX_GUINT8_GET (ptr)); + GST_DEBUG_OBJECT (qtdemux, "stream_type %02x", + QTDEMUX_GUINT8_GET (ptr + 1)); + GST_DEBUG_OBJECT (qtdemux, "buffer_size_db %02x", + QTDEMUX_GUINT24_GET (ptr + 2)); + GST_DEBUG_OBJECT (qtdemux, "max bitrate %d", + QTDEMUX_GUINT32_GET (ptr + 5)); + GST_DEBUG_OBJECT (qtdemux, "avg bitrate %d", + QTDEMUX_GUINT32_GET (ptr + 9)); ptr += 13; break; case 0x05: - GST_DEBUG ("data:"); + GST_DEBUG_OBJECT (qtdemux, "data:"); gst_util_dump_mem (ptr, len); data_ptr = ptr; data_len = len; ptr += len; break; case 0x06: - GST_DEBUG ("data %02x", QTDEMUX_GUINT8_GET (ptr)); + GST_DEBUG_OBJECT (qtdemux, "data %02x", QTDEMUX_GUINT8_GET (ptr)); ptr += 1; break; default: diff --git a/gst/qtdemux/qtdemux.h b/gst/qtdemux/qtdemux.h index 97b907f9..3a5d5c77 100644 --- a/gst/qtdemux/qtdemux.h +++ b/gst/qtdemux/qtdemux.h @@ -50,9 +50,9 @@ struct _GstQTDemux { GstPad *sinkpad; QtDemuxStream *streams[GST_QTDEMUX_MAX_STREAMS]; - int n_streams; - int n_video_streams; - int n_audio_streams; + gint n_streams; + gint n_video_streams; + gint n_audio_streams; GNode *moov_node; GNode *moov_node_compressed; @@ -60,7 +60,7 @@ struct _GstQTDemux { guint32 timescale; guint32 duration; - int state; + gint state; gboolean pullbased; @@ -81,7 +81,9 @@ struct _GstQTDemux { guint64 last_ts; + /* configured playback region */ GstSegment segment; + gboolean segment_running; }; struct _GstQTDemuxClass { |