From f89caeff5c1820caecfd2e1d9b93ffeb0a477a17 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Tue, 25 May 2010 01:04:43 +0530 Subject: [PATCH] qtdemux: guess bitrate if only one stream's bitrate is unknown If the bitrates for all but one audio/video streams are known, and the total stream size and duration can be determined, this calculates the unkown bitrate as (stream size / duration) - (sum of known bitrates). While this is not guaranteed to be very accurate, it should be good enough for most purposes. For example, this is useful for H.263 + AAC streams where no 'btrt' atom is available for the video portion. https://bugzilla.gnome.org/show_bug.cgi?id=619548 --- gst/isomp4/qtdemux.c | 105 ++++++++++++++++++++++++++++++++++++++++++- gst/isomp4/qtdemux.h | 2 +- 2 files changed, 105 insertions(+), 2 deletions(-) diff --git a/gst/isomp4/qtdemux.c b/gst/isomp4/qtdemux.c index bafcf96721..5f88db6ebd 100644 --- a/gst/isomp4/qtdemux.c +++ b/gst/isomp4/qtdemux.c @@ -427,7 +427,6 @@ static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n); static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux); - static void gst_qtdemux_base_init (gpointer klass) { @@ -1835,6 +1834,7 @@ gst_qtdemux_change_state (GstElement * element, GstStateChange transition) qtdemux->posted_redirect = FALSE; qtdemux->offset = 0; qtdemux->first_mdat = -1; + qtdemux->header_size = 0; qtdemux->got_moov = FALSE; qtdemux->mdatoffset = GST_CLOCK_TIME_NONE; if (qtdemux->mdatbuffer) @@ -1888,6 +1888,9 @@ qtdemux_post_global_tags (GstQTDemux * qtdemux) static void qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length) { + /* counts as header data */ + qtdemux->header_size += length; + /* only consider at least a sufficiently complete ftyp atom */ if (length >= 20) { GstBuffer *buf; @@ -1928,6 +1931,9 @@ qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length) }; guint offset; + /* counts as header data */ + qtdemux->header_size += length; + offset = (QT_UINT32 (buffer) == 0) ? 16 : 8; if (length <= offset + 16) { @@ -4450,6 +4456,9 @@ qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer, guint length) qtdemux->moov_node = g_node_new ((guint8 *) buffer); + /* counts as header data */ + qtdemux->header_size += length; + GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom"); qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length); @@ -7409,6 +7418,98 @@ too_many_streams: } } +/* If we can estimate the overall bitrate, and don't have information about the + * stream bitrate for exactly one stream, this guesses the stream bitrate as + * the overall bitrate minus the sum of the bitrates of all other streams. This + * should be useful for the common case where we have one audio and one video + * stream and can estimate the bitrate of one, but not the other. */ +static void +gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux) +{ + GstFormat format = GST_FORMAT_BYTES; + QtDemuxStream *stream = NULL; + gint64 size, duration, sys_bitrate, sum_bitrate = 0; + gint i; + guint bitrate; + + if (qtdemux->fragmented) + return; + + GST_DEBUG_OBJECT (qtdemux, "Looking for streams with unknown bitrate"); + + if (!gst_pad_query_peer_duration (qtdemux->sinkpad, &format, &size) || + format != GST_FORMAT_BYTES) { + GST_DEBUG_OBJECT (qtdemux, + "Size in bytes of the stream not known - bailing"); + return; + } + + /* Subtract the header size */ + GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u", + size, qtdemux->header_size); + g_assert (size > qtdemux->header_size); + size = size - qtdemux->header_size; + + if (!gst_qtdemux_get_duration (qtdemux, &duration) || + duration == GST_CLOCK_TIME_NONE) { + GST_DEBUG_OBJECT (qtdemux, "Stream duration not known - bailing"); + return; + } + + for (i = 0; i < qtdemux->n_streams; i++) { + switch (qtdemux->streams[i]->subtype) { + case FOURCC_soun: + case FOURCC_vide: + /* retrieve bitrate, prefer avg then max */ + if (qtdemux->streams[i]->pending_tags) { + gst_tag_list_get_uint (qtdemux->streams[i]->pending_tags, + GST_TAG_MAXIMUM_BITRATE, &bitrate); + gst_tag_list_get_uint (qtdemux->streams[i]->pending_tags, + GST_TAG_BITRATE, &bitrate); + } + if (bitrate) + sum_bitrate += bitrate; + else { + if (stream) { + GST_DEBUG_OBJECT (qtdemux, + ">1 stream with unknown bitrate - bailing"); + return; + } else + stream = qtdemux->streams[i]; + } + + default: + /* For other subtypes, we assume no significant impact on bitrate */ + break; + } + } + + if (!stream) { + GST_DEBUG_OBJECT (qtdemux, "All stream bitrates are known"); + return; + } + + sys_bitrate = gst_util_uint64_scale (size, GST_SECOND * 8, duration); + + if (sys_bitrate < sum_bitrate) { + /* This can happen, since sum_bitrate might be derived from maximum + * bitrates and not average bitrates */ + GST_DEBUG_OBJECT (qtdemux, + "System bitrate less than sum bitrate - bailing"); + return; + } + + bitrate = sys_bitrate - sum_bitrate; + GST_DEBUG_OBJECT (qtdemux, "System bitrate = %" G_GINT64_FORMAT + ", Stream bitrate = %u", sys_bitrate, bitrate); + + if (!stream->pending_tags) + stream->pending_tags = gst_tag_list_new (); + + gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE, + GST_TAG_BITRATE, bitrate, NULL); +} + static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux) { @@ -7484,6 +7585,8 @@ qtdemux_expose_streams (GstQTDemux * qtdemux) gst_qtdemux_add_stream (qtdemux, stream, list); } + gst_qtdemux_guess_bitrate (qtdemux); + gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux)); /* check if we should post a redirect in case there is a single trak diff --git a/gst/isomp4/qtdemux.h b/gst/isomp4/qtdemux.h index 069fa72ebe..d2b43bd504 100644 --- a/gst/isomp4/qtdemux.h +++ b/gst/isomp4/qtdemux.h @@ -89,12 +89,12 @@ struct _GstQTDemux { GstBuffer *mdatbuffer; guint64 mdatleft; - /* offset of the media data (i.e.: Size of header) */ guint64 offset; /* offset of the mdat atom */ guint64 mdatoffset; guint64 first_mdat; gboolean got_moov; + guint header_size; GstTagList *tag_list;