qtdemux: guess bitrate if only one stream's bitrate is unknown
authorArun Raghavan <arun.raghavan@collabora.co.uk>
Mon, 24 May 2010 19:34:43 +0000 (01:04 +0530)
committerMark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
Mon, 16 May 2011 11:29:56 +0000 (13:29 +0200)
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
gst/isomp4/qtdemux.h

index bafcf96..5f88db6 100644 (file)
@@ -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
index 069fa72..d2b43bd 100644 (file)
@@ -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;