mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-20 07:16:55 +00:00
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.
This commit is contained in:
parent
8a2141f001
commit
102ee0cf41
2 changed files with 139 additions and 0 deletions
|
@ -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;
|
||||
|
@ -870,6 +884,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.
|
||||
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue