summaryrefslogtreecommitdiff
path: root/gst
diff options
context:
space:
mode:
authorArun Raghavan <arun.raghavan@collabora.co.uk>2010-03-25 11:22:58 +0000
committerSebastian Dröge <sebastian.droege@collabora.co.uk>2010-03-25 16:57:36 +0100
commit88c1e425676745ef32cb5ccc5e9db62083e29f1f (patch)
tree9c207f11ece318d2668d890153d43adcc923428e /gst
parentd380140038608678411ffefdc427df946f7c9f1d (diff)
downloadgstreamer-plugins-bad-88c1e425676745ef32cb5ccc5e9db62083e29f1f.tar.gz
audioparsers: Add bitrate calculation to baseparse
This makes baseparse keep a running average of the stream bitrate, as well as the minimum and maximum bitrates. Subclasses can override a vfunc to make sure that per-frame overhead from the container is not accounted for in the bitrate calculation. We take care not to override the bitrate, minimum-bitrate, and maximum-bitrate tags if they have been posted upstream. We also rate-limit the emission of bitrate so that it is only triggered by a change of >10 kbps.
Diffstat (limited to 'gst')
-rw-r--r--gst/audioparsers/gstbaseparse.c130
-rw-r--r--gst/audioparsers/gstbaseparse.h9
2 files changed, 139 insertions, 0 deletions
diff --git a/gst/audioparsers/gstbaseparse.c b/gst/audioparsers/gstbaseparse.c
index 4c9468c4b..3416bf6fc 100644
--- a/gst/audioparsers/gstbaseparse.c
+++ b/gst/audioparsers/gstbaseparse.c
@@ -228,8 +228,16 @@ struct _GstBaseParsePrivate
guint64 framecount;
guint64 bytecount;
+ guint64 data_bytecount;
guint64 acc_duration;
+ gboolean post_min_bitrate;
+ gboolean post_avg_bitrate;
+ gboolean post_max_bitrate;
+ guint min_bitrate;
+ guint avg_bitrate;
+ guint max_bitrate;
+
GList *pending_events;
GstBuffer *cache;
@@ -281,6 +289,7 @@ static gboolean gst_base_parse_sink_activate_pull (GstPad * pad,
gboolean active);
static gboolean gst_base_parse_handle_seek (GstBaseParse * parse,
GstEvent * event);
+static void gst_base_parse_handle_tag (GstBaseParse * parse, GstEvent * event);
static gboolean gst_base_parse_src_event (GstPad * pad, GstEvent * event);
static gboolean gst_base_parse_sink_event (GstPad * pad, GstEvent * event);
@@ -545,6 +554,11 @@ gst_base_parse_sink_event (GstPad * pad, GstEvent * event)
&& GST_EVENT_TYPE (event) != GST_EVENT_NEWSEGMENT
&& GST_EVENT_TYPE (event) != GST_EVENT_FLUSH_START
&& GST_EVENT_TYPE (event) != GST_EVENT_FLUSH_STOP) {
+
+ if (GST_EVENT_TYPE (event) == GST_EVENT_TAG)
+ /* See if any bitrate tags were posted */
+ gst_base_parse_handle_tag (parse, event);
+
parse->priv->pending_events =
g_list_append (parse->priv->pending_events, event);
ret = TRUE;
@@ -871,6 +885,91 @@ gst_base_parse_update_duration (GstBaseParse * aacparse)
}
/**
+ * gst_base_parse_update_bitrates:
+ * @parse: #GstBaseParse.
+ * @buffer: Current frame as a #GstBuffer
+ *
+ * Keeps track of the minimum and maximum bitrates, and also maintains a
+ * running average bitrate of the stream so far.
+ */
+static void
+gst_base_parse_update_bitrates (GstBaseParse * parse, GstBuffer * buffer)
+{
+ /* Only update the tag on a 10 kbps delta */
+ static const gint update_threshold = 10000;
+
+ GstBaseParseClass *klass;
+ guint64 data_len, frame_dur;
+ gint overhead = 0, frame_bitrate, old_avg_bitrate = parse->priv->avg_bitrate;
+ gboolean update_min = FALSE, update_avg = FALSE, update_max = FALSE;
+
+ klass = GST_BASE_PARSE_GET_CLASS (parse);
+
+ if (klass->get_frame_overhead) {
+ overhead = klass->get_frame_overhead (parse, buffer);
+ if (overhead == -1)
+ return;
+ }
+
+ data_len = GST_BUFFER_SIZE (buffer) - overhead;
+ parse->priv->data_bytecount += data_len;
+
+ if (parse->priv->fps_num) {
+ /* Calculate duration of a frame from frame properties */
+ frame_dur = (GST_SECOND * parse->priv->fps_den) / parse->priv->fps_num;
+ parse->priv->avg_bitrate = (8 * parse->priv->data_bytecount * GST_SECOND) /
+ (parse->priv->framecount * frame_dur);
+
+ } else if (GST_BUFFER_DURATION_IS_VALID (buffer)) {
+ /* Calculate duration of a frame from buffer properties */
+ frame_dur = GST_BUFFER_DURATION (buffer);
+ parse->priv->avg_bitrate = (8 * parse->priv->data_bytecount * GST_SECOND) /
+ parse->priv->acc_duration;
+
+ } else {
+ /* No way to figure out frame duration (is this even possible?) */
+ return;
+ }
+
+ frame_bitrate = (8 * data_len * GST_SECOND) / frame_dur;
+
+ if (frame_bitrate < parse->priv->min_bitrate) {
+ parse->priv->min_bitrate = frame_bitrate;
+ update_min = parse->priv->post_min_bitrate;
+ }
+
+ if (frame_bitrate > parse->priv->max_bitrate) {
+ parse->priv->max_bitrate = frame_bitrate;
+ update_max = parse->priv->post_max_bitrate;
+ }
+
+ if (old_avg_bitrate / update_threshold !=
+ parse->priv->avg_bitrate / update_threshold)
+ update_avg = parse->priv->post_avg_bitrate;
+
+ if (update_min || update_avg || update_max) {
+ GstTagList *taglist = gst_tag_list_new ();
+
+ if (update_min)
+ gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_MINIMUM_BITRATE,
+ parse->priv->min_bitrate, NULL);
+ if (update_avg)
+ gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
+ parse->priv->avg_bitrate, NULL);
+ if (update_max)
+ gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_MAXIMUM_BITRATE,
+ parse->priv->max_bitrate, NULL);
+
+ GST_DEBUG_OBJECT (parse, "Updated bitrates. Min: %u, Avg: %u, Max: %u",
+ parse->priv->min_bitrate, parse->priv->avg_bitrate,
+ parse->priv->max_bitrate);
+
+ gst_element_found_tags_for_pad (GST_ELEMENT (parse), parse->srcpad,
+ taglist);
+ }
+}
+
+/**
* gst_base_parse_handle_and_push_buffer:
* @parse: #GstBaseParse.
* @klass: #GstBaseParseClass.
@@ -966,6 +1065,8 @@ gst_base_parse_push_buffer (GstBaseParse * parse, GstBuffer * buffer)
(parse->priv->framecount % parse->priv->update_interval) == 0)
gst_base_parse_update_duration (parse);
+ gst_base_parse_update_bitrates (parse, buffer);
+
if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
last_stop = GST_BUFFER_TIMESTAMP (buffer);
if (last_stop != GST_CLOCK_TIME_NONE && GST_BUFFER_DURATION_IS_VALID (buffer))
@@ -1493,6 +1594,12 @@ gst_base_parse_activate (GstBaseParse * parse, gboolean active)
parse->priv->estimated_duration = -1;
parse->priv->next_ts = 0;
parse->priv->passthrough = FALSE;
+ parse->priv->post_min_bitrate = TRUE;
+ parse->priv->post_avg_bitrate = TRUE;
+ parse->priv->post_max_bitrate = TRUE;
+ parse->priv->min_bitrate = G_MAXUINT;
+ parse->priv->max_bitrate = 0;
+ parse->priv->max_bitrate = 0;
if (parse->pending_segment)
gst_event_unref (parse->pending_segment);
@@ -2109,6 +2216,29 @@ wrong_type:
}
}
+/**
+ * gst_base_parse_handle_tag:
+ * @parse: #GstBaseParse.
+ * @event: #GstEvent.
+ *
+ * Checks if bitrates are available from upstream tags so that we don't
+ * override them later
+ */
+static void
+gst_base_parse_handle_tag (GstBaseParse * parse, GstEvent * event)
+{
+ GstTagList *taglist = NULL;
+ guint tmp;
+
+ gst_event_parse_tag (event, &taglist);
+
+ if (gst_tag_list_get_uint (taglist, GST_TAG_MINIMUM_BITRATE, &tmp))
+ parse->priv->post_min_bitrate = FALSE;
+ if (gst_tag_list_get_uint (taglist, GST_TAG_BITRATE, &tmp))
+ parse->priv->post_avg_bitrate = FALSE;
+ if (gst_tag_list_get_uint (taglist, GST_TAG_MAXIMUM_BITRATE, &tmp))
+ parse->priv->post_max_bitrate = FALSE;
+}
/**
* gst_base_parse_sink_setcaps:
diff --git a/gst/audioparsers/gstbaseparse.h b/gst/audioparsers/gstbaseparse.h
index a6a5f54f2..08620b48f 100644
--- a/gst/audioparsers/gstbaseparse.h
+++ b/gst/audioparsers/gstbaseparse.h
@@ -184,6 +184,12 @@ struct _GstBaseParse {
* seekability of the stream. Otherwise the element assumes
* that stream is always seekable.
*
+ * @get_frame_overhead: Finds the metadata overhead for the given frame. This
+ * is used to enable more accurate bitrate computations.
+ * If NULL, the per-frame overhead is assumed to be 0. If
+ * this returns -1, it is assumed that this frame should
+ * be skipped in bitrate calculation.
+ *
* Subclasses can override any of the available virtual methods or not, as
* needed. At minimum @check_valid_frame and @parse_frame needs to be
* overridden.
@@ -228,6 +234,9 @@ struct _GstBaseParseClass {
gboolean (*is_seekable) (GstBaseParse *parse);
+ gint (*get_frame_overhead) (GstBaseParse *parse,
+ GstBuffer *buf);
+
/*< private >*/
gpointer _gst_reserved[GST_PADDING_LARGE];
GstBaseParseClassPrivate *priv;