mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-18 14:26:43 +00:00
libgstvideo: add a new API to handle QoS events and dropping logic
https://bugzilla.gnome.org/show_bug.cgi?id=658241
This commit is contained in:
parent
4a58223e4c
commit
eb03323fb6
4 changed files with 266 additions and 0 deletions
|
@ -2305,6 +2305,12 @@ gst_video_event_parse_still_frame
|
||||||
<SUBSECTION Standard>
|
<SUBSECTION Standard>
|
||||||
gst_video_format_get_type
|
gst_video_format_get_type
|
||||||
GST_TYPE_VIDEO_FORMAT
|
GST_TYPE_VIDEO_FORMAT
|
||||||
|
GstVideoQoSTracker
|
||||||
|
gst_video_qos_tracker_init
|
||||||
|
gst_video_qos_tracker_reset
|
||||||
|
gst_video_qos_tracker_update
|
||||||
|
gst_video_qos_tracker_process_frame
|
||||||
|
gst_video_qos_tracker_clear
|
||||||
</SECTION>
|
</SECTION>
|
||||||
|
|
||||||
<SECTION>
|
<SECTION>
|
||||||
|
|
|
@ -2330,3 +2330,220 @@ gst_video_parse_caps_palette (GstCaps * caps)
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_video_qos_tracker_init:
|
||||||
|
* @qt: The #GstVideoQoSTracker to initialize
|
||||||
|
* @element: The element the tracker belongs to, which will be sending the QoS
|
||||||
|
* messages when appropriate.
|
||||||
|
*
|
||||||
|
* Initialize a #GstVideoQoSTracker. Call gst_video_qos_tracker_clear when done.
|
||||||
|
* Includes its own locking, so it's safe to call the gst_video_qos_tracker_ API
|
||||||
|
* from multiple threads (except _init and _clear, of course).
|
||||||
|
*
|
||||||
|
* The element is not referenced, so must have a lifetime that encompasses that
|
||||||
|
* of the GstVideoQoSTracker. This is implicit in the typical case where the
|
||||||
|
* GstVideoQoSTracker is a member of the owning element.
|
||||||
|
*
|
||||||
|
* Since: 0.10.36
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
gst_video_qos_tracker_init (GstVideoQoSTracker * qt, GstElement * element)
|
||||||
|
{
|
||||||
|
qt->lock = g_mutex_new ();
|
||||||
|
qt->element = element;
|
||||||
|
gst_video_qos_tracker_reset (qt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_video_qos_tracker_reset:
|
||||||
|
* @qt: The #GstVideoQoSTracker to reset
|
||||||
|
*
|
||||||
|
* Reset a #GstVideoQoSTracker.
|
||||||
|
* Use when restarting an element.
|
||||||
|
*
|
||||||
|
* Since: 0.10.36
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
gst_video_qos_tracker_reset (GstVideoQoSTracker * qt)
|
||||||
|
{
|
||||||
|
g_return_if_fail (qt && qt->lock);
|
||||||
|
g_mutex_lock (qt->lock);
|
||||||
|
qt->proportion = 1.0;
|
||||||
|
qt->timestamp = GST_CLOCK_TIME_NONE;
|
||||||
|
qt->diff = 0;
|
||||||
|
qt->earliest_time = GST_CLOCK_TIME_NONE;
|
||||||
|
qt->processed = 0;
|
||||||
|
qt->dropped = 0;
|
||||||
|
g_mutex_unlock (qt->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_video_qos_tracker_update:
|
||||||
|
* @qt: The #GstVideoQoSTracker to update
|
||||||
|
* @event: The event to update from, must a QOS event
|
||||||
|
* @frame_duration: the duration of a frame, if known,
|
||||||
|
* may be GST_CLOCK_TIME_NONE is unknown
|
||||||
|
* @method: the algorithm to use to determine the time at
|
||||||
|
* which to start accepting frames again when we're late
|
||||||
|
*
|
||||||
|
* Update a #GstVideoQoSTracker from an incoming QOS event.
|
||||||
|
* This allows the QoS tracker to know whether the sink is
|
||||||
|
* late or not.
|
||||||
|
* An element using a GstVideoQoSTracker should pass all
|
||||||
|
* QOS events to this function.
|
||||||
|
*
|
||||||
|
* Since: 0.10.36
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
gst_video_qos_tracker_update (GstVideoQoSTracker * qt, GstEvent * event,
|
||||||
|
GstClockTime frame_duration, GstVideoQoSTrackerMethod method)
|
||||||
|
{
|
||||||
|
gdouble proportion;
|
||||||
|
GstClockTime timestamp;
|
||||||
|
GstClockTimeDiff diff;
|
||||||
|
|
||||||
|
g_return_if_fail (qt && qt->lock);
|
||||||
|
g_return_if_fail (event && GST_IS_EVENT (event));
|
||||||
|
g_return_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_QOS);
|
||||||
|
|
||||||
|
gst_event_parse_qos (event, &proportion, &diff, ×tamp);
|
||||||
|
|
||||||
|
g_mutex_lock (qt->lock);
|
||||||
|
|
||||||
|
qt->proportion = proportion;
|
||||||
|
qt->diff = diff;
|
||||||
|
|
||||||
|
if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (timestamp))) {
|
||||||
|
qt->timestamp = timestamp;
|
||||||
|
if (G_UNLIKELY (diff > 0)) {
|
||||||
|
switch (method) {
|
||||||
|
case GST_VIDEO_QOS_TRACKER_DIFF:
|
||||||
|
qt->earliest_time = qt->timestamp + diff;
|
||||||
|
break;
|
||||||
|
case GST_VIDEO_QOS_TRACKER_TWICE_DIFF:
|
||||||
|
qt->earliest_time = qt->timestamp + 2 * diff;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
qt->earliest_time = qt->timestamp + diff;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (GST_CLOCK_TIME_IS_VALID (frame_duration)) {
|
||||||
|
qt->earliest_time += frame_duration;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qt->earliest_time = qt->timestamp + qt->diff;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qt->timestamp = GST_CLOCK_TIME_NONE;
|
||||||
|
qt->earliest_time = GST_CLOCK_TIME_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (qt->element,
|
||||||
|
"got QoS %" GST_TIME_FORMAT ", %" G_GINT64_FORMAT,
|
||||||
|
GST_TIME_ARGS (qt->timestamp), qt->diff);
|
||||||
|
|
||||||
|
g_mutex_unlock (qt->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_video_qos_tracker_process_frame:
|
||||||
|
* @qt: The #GstVideoQoSTracker to use
|
||||||
|
* @segment: The segment to use to determine running times
|
||||||
|
* @timestamp: the timestamp of the buffer to consider.
|
||||||
|
* May be GST_CLOCK_TIME_NONE if unknown.
|
||||||
|
* @duration: the duration of the buffer to consider.
|
||||||
|
* May be GST_CLOCK_TIME_NONE if unknown.
|
||||||
|
*
|
||||||
|
* Decides if a frame should be dropped or not based on the known
|
||||||
|
* timings from previously received QOS events.
|
||||||
|
* Frames with unknown timestamps will never be dropped.
|
||||||
|
* If a frame is to be dropped, an appropriate QoS message will
|
||||||
|
* be sent on behalf of the owning element, and the element should
|
||||||
|
* not send that buffer downstream.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE if the buffer should be dropped, %FALSE otherwise.
|
||||||
|
*
|
||||||
|
* Since: 0.10.36
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
gst_video_qos_tracker_process_frame (GstVideoQoSTracker * qt,
|
||||||
|
const GstSegment * segment, GstClockTime timestamp, GstClockTime duration)
|
||||||
|
{
|
||||||
|
GstClockTime running_time;
|
||||||
|
gboolean skip;
|
||||||
|
GstClockTime earliest_time;
|
||||||
|
|
||||||
|
g_return_val_if_fail (qt && qt->lock, FALSE);
|
||||||
|
g_return_val_if_fail (segment, FALSE);
|
||||||
|
|
||||||
|
if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp)))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
g_mutex_lock (qt->lock);
|
||||||
|
|
||||||
|
earliest_time = qt->earliest_time;
|
||||||
|
|
||||||
|
/* qos needs to be done on running time */
|
||||||
|
running_time =
|
||||||
|
gst_segment_to_running_time ((GstSegment *) segment, GST_FORMAT_TIME,
|
||||||
|
timestamp);
|
||||||
|
skip = GST_CLOCK_TIME_IS_VALID (earliest_time)
|
||||||
|
&& running_time <= earliest_time;
|
||||||
|
|
||||||
|
if (skip) {
|
||||||
|
GstMessage *qos_msg;
|
||||||
|
guint64 stream_time;
|
||||||
|
gint64 jitter;
|
||||||
|
guint64 dropped, processed;
|
||||||
|
gdouble proportion;
|
||||||
|
|
||||||
|
qt->dropped++;
|
||||||
|
|
||||||
|
proportion = qt->proportion;
|
||||||
|
processed = qt->processed;
|
||||||
|
dropped = qt->dropped;
|
||||||
|
|
||||||
|
g_mutex_unlock (qt->lock);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (qt->element, "skipping decoding: qostime %"
|
||||||
|
GST_TIME_FORMAT " <= %" GST_TIME_FORMAT,
|
||||||
|
GST_TIME_ARGS (running_time), GST_TIME_ARGS (earliest_time));
|
||||||
|
|
||||||
|
stream_time =
|
||||||
|
gst_segment_to_stream_time ((GstSegment *) segment, GST_FORMAT_TIME,
|
||||||
|
timestamp);
|
||||||
|
jitter = GST_CLOCK_DIFF (running_time, earliest_time);
|
||||||
|
|
||||||
|
qos_msg =
|
||||||
|
gst_message_new_qos (GST_OBJECT_CAST (qt->element), FALSE, running_time,
|
||||||
|
stream_time, timestamp, duration);
|
||||||
|
gst_message_set_qos_values (qos_msg, jitter, proportion, 1000000);
|
||||||
|
gst_message_set_qos_stats (qos_msg, GST_FORMAT_BUFFERS, processed, dropped);
|
||||||
|
gst_element_post_message (qt->element, qos_msg);
|
||||||
|
} else {
|
||||||
|
qt->processed++;
|
||||||
|
g_mutex_unlock (qt->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return skip;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_video_qos_tracker_clear:
|
||||||
|
* @qt: The #GstVideoQoSTracker to clear
|
||||||
|
*
|
||||||
|
* Clears a previously initialized GstVideoQoSTracker.
|
||||||
|
* That GstVideoQoSTracker may be be used after this call, unless
|
||||||
|
* it is initialized again.
|
||||||
|
*
|
||||||
|
* Since: 0.10.36
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
gst_video_qos_tracker_clear (GstVideoQoSTracker * qt)
|
||||||
|
{
|
||||||
|
g_return_if_fail (qt && qt->lock);
|
||||||
|
g_mutex_free (qt->lock);
|
||||||
|
qt->lock = NULL;
|
||||||
|
}
|
||||||
|
|
|
@ -435,6 +435,34 @@ typedef enum {
|
||||||
*/
|
*/
|
||||||
#define GST_VIDEO_BUFFER_PROGRESSIVE GST_BUFFER_FLAG_MEDIA4
|
#define GST_VIDEO_BUFFER_PROGRESSIVE GST_BUFFER_FLAG_MEDIA4
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GstVideoQoSTrackerMethod:
|
||||||
|
* @GST_VIDEO_QOS_TRACKER_2DURATION:
|
||||||
|
*
|
||||||
|
* Enum value describing the algorithm to use to determine when to drop a frame.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
GST_VIDEO_QOS_TRACKER_DIFF,
|
||||||
|
GST_VIDEO_QOS_TRACKER_TWICE_DIFF,
|
||||||
|
} GstVideoQoSTrackerMethod;
|
||||||
|
|
||||||
|
typedef struct _GstVideoQoSTracker GstVideoQoSTracker;
|
||||||
|
struct _GstVideoQoSTracker {
|
||||||
|
gdouble proportion;
|
||||||
|
GstClockTime timestamp;
|
||||||
|
GstClockTimeDiff diff;
|
||||||
|
GstClockTime earliest_time;
|
||||||
|
guint64 processed;
|
||||||
|
guint64 dropped;
|
||||||
|
GMutex *lock; /* protects the above */
|
||||||
|
GstElement *element;
|
||||||
|
|
||||||
|
/*< private >*/
|
||||||
|
union {
|
||||||
|
gpointer _gst_reserved[GST_PADDING];
|
||||||
|
} abidata;
|
||||||
|
};
|
||||||
|
|
||||||
/* functions */
|
/* functions */
|
||||||
|
|
||||||
const GValue * gst_video_frame_rate (GstPad * pad);
|
const GValue * gst_video_frame_rate (GstPad * pad);
|
||||||
|
@ -569,6 +597,16 @@ GstBuffer * gst_video_convert_frame (GstBuffer * buf,
|
||||||
GstClockTime timeout,
|
GstClockTime timeout,
|
||||||
GError ** error);
|
GError ** error);
|
||||||
|
|
||||||
|
/* QoS */
|
||||||
|
void gst_video_qos_tracker_init (GstVideoQoSTracker * qt, GstElement *element);
|
||||||
|
void gst_video_qos_tracker_reset (GstVideoQoSTracker * qt);
|
||||||
|
void gst_video_qos_tracker_update (GstVideoQoSTracker * qt, GstEvent* event,
|
||||||
|
GstClockTime frame_duration,
|
||||||
|
GstVideoQoSTrackerMethod method);
|
||||||
|
gboolean gst_video_qos_tracker_process_frame (GstVideoQoSTracker * qt, const GstSegment *segment,
|
||||||
|
GstClockTime timestamp, GstClockTime duration);
|
||||||
|
void gst_video_qos_tracker_clear (GstVideoQoSTracker * qt);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __GST_VIDEO_H__ */
|
#endif /* __GST_VIDEO_H__ */
|
||||||
|
|
|
@ -35,3 +35,8 @@ EXPORTS
|
||||||
gst_video_parse_caps_pixel_aspect_ratio
|
gst_video_parse_caps_pixel_aspect_ratio
|
||||||
gst_video_sink_center_rect
|
gst_video_sink_center_rect
|
||||||
gst_video_sink_get_type
|
gst_video_sink_get_type
|
||||||
|
gst_video_qos_tracker_init
|
||||||
|
gst_video_qos_tracker_reset
|
||||||
|
gst_video_qos_tracker_update
|
||||||
|
gst_video_qos_tracker_process_frame
|
||||||
|
gst_video_qos_tracker_clear
|
||||||
|
|
Loading…
Reference in a new issue