mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-09-08 21:28:45 +00:00
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
This commit is contained in:
parent
6e2774af55
commit
f89caeff5c
2 changed files with 105 additions and 2 deletions
|
@ -427,7 +427,6 @@ static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux,
|
||||||
QtDemuxStream * stream, guint32 n);
|
QtDemuxStream * stream, guint32 n);
|
||||||
static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux);
|
static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux);
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_qtdemux_base_init (gpointer klass)
|
gst_qtdemux_base_init (gpointer klass)
|
||||||
{
|
{
|
||||||
|
@ -1835,6 +1834,7 @@ gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
|
||||||
qtdemux->posted_redirect = FALSE;
|
qtdemux->posted_redirect = FALSE;
|
||||||
qtdemux->offset = 0;
|
qtdemux->offset = 0;
|
||||||
qtdemux->first_mdat = -1;
|
qtdemux->first_mdat = -1;
|
||||||
|
qtdemux->header_size = 0;
|
||||||
qtdemux->got_moov = FALSE;
|
qtdemux->got_moov = FALSE;
|
||||||
qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
|
qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
|
||||||
if (qtdemux->mdatbuffer)
|
if (qtdemux->mdatbuffer)
|
||||||
|
@ -1888,6 +1888,9 @@ qtdemux_post_global_tags (GstQTDemux * qtdemux)
|
||||||
static void
|
static void
|
||||||
qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
|
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 */
|
/* only consider at least a sufficiently complete ftyp atom */
|
||||||
if (length >= 20) {
|
if (length >= 20) {
|
||||||
GstBuffer *buf;
|
GstBuffer *buf;
|
||||||
|
@ -1928,6 +1931,9 @@ qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
|
||||||
};
|
};
|
||||||
guint offset;
|
guint offset;
|
||||||
|
|
||||||
|
/* counts as header data */
|
||||||
|
qtdemux->header_size += length;
|
||||||
|
|
||||||
offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
|
offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
|
||||||
|
|
||||||
if (length <= offset + 16) {
|
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);
|
qtdemux->moov_node = g_node_new ((guint8 *) buffer);
|
||||||
|
|
||||||
|
/* counts as header data */
|
||||||
|
qtdemux->header_size += length;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom");
|
GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom");
|
||||||
qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length);
|
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
|
static GstFlowReturn
|
||||||
qtdemux_expose_streams (GstQTDemux * qtdemux)
|
qtdemux_expose_streams (GstQTDemux * qtdemux)
|
||||||
{
|
{
|
||||||
|
@ -7484,6 +7585,8 @@ qtdemux_expose_streams (GstQTDemux * qtdemux)
|
||||||
gst_qtdemux_add_stream (qtdemux, stream, list);
|
gst_qtdemux_add_stream (qtdemux, stream, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gst_qtdemux_guess_bitrate (qtdemux);
|
||||||
|
|
||||||
gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
|
gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
|
||||||
|
|
||||||
/* check if we should post a redirect in case there is a single trak
|
/* check if we should post a redirect in case there is a single trak
|
||||||
|
|
|
@ -89,12 +89,12 @@ struct _GstQTDemux {
|
||||||
GstBuffer *mdatbuffer;
|
GstBuffer *mdatbuffer;
|
||||||
guint64 mdatleft;
|
guint64 mdatleft;
|
||||||
|
|
||||||
/* offset of the media data (i.e.: Size of header) */
|
|
||||||
guint64 offset;
|
guint64 offset;
|
||||||
/* offset of the mdat atom */
|
/* offset of the mdat atom */
|
||||||
guint64 mdatoffset;
|
guint64 mdatoffset;
|
||||||
guint64 first_mdat;
|
guint64 first_mdat;
|
||||||
gboolean got_moov;
|
gboolean got_moov;
|
||||||
|
guint header_size;
|
||||||
|
|
||||||
GstTagList *tag_list;
|
GstTagList *tag_list;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue