mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 12:11:13 +00:00
videorate: Add fixed rate property
https://bugzilla.gnome.org/show_bug.cgi?id=699077
This commit is contained in:
parent
9121131f31
commit
658ee6f0db
3 changed files with 388 additions and 22 deletions
|
@ -52,6 +52,10 @@
|
||||||
* Note that property notification will happen from the streaming thread, so
|
* Note that property notification will happen from the streaming thread, so
|
||||||
* applications should be prepared for this.
|
* applications should be prepared for this.
|
||||||
*
|
*
|
||||||
|
* The property #GstVideoRate:rate allows the modification of video speed by a
|
||||||
|
* certain factor. It must not be confused with framerate. Think of rate as
|
||||||
|
* speed and framerate as flow.
|
||||||
|
*
|
||||||
* <refsect2>
|
* <refsect2>
|
||||||
* <title>Example pipelines</title>
|
* <title>Example pipelines</title>
|
||||||
* |[
|
* |[
|
||||||
|
@ -91,6 +95,7 @@ enum
|
||||||
#define DEFAULT_DROP_ONLY FALSE
|
#define DEFAULT_DROP_ONLY FALSE
|
||||||
#define DEFAULT_AVERAGE_PERIOD 0
|
#define DEFAULT_AVERAGE_PERIOD 0
|
||||||
#define DEFAULT_MAX_RATE G_MAXINT
|
#define DEFAULT_MAX_RATE G_MAXINT
|
||||||
|
#define DEFAULT_RATE 1.0
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
@ -104,7 +109,8 @@ enum
|
||||||
PROP_SKIP_TO_FIRST,
|
PROP_SKIP_TO_FIRST,
|
||||||
PROP_DROP_ONLY,
|
PROP_DROP_ONLY,
|
||||||
PROP_AVERAGE_PERIOD,
|
PROP_AVERAGE_PERIOD,
|
||||||
PROP_MAX_RATE
|
PROP_MAX_RATE,
|
||||||
|
PROP_RATE
|
||||||
};
|
};
|
||||||
|
|
||||||
static GstStaticPadTemplate gst_video_rate_src_template =
|
static GstStaticPadTemplate gst_video_rate_src_template =
|
||||||
|
@ -127,6 +133,8 @@ static void gst_video_rate_swap_prev (GstVideoRate * videorate,
|
||||||
GstBuffer * buffer, gint64 time);
|
GstBuffer * buffer, gint64 time);
|
||||||
static gboolean gst_video_rate_sink_event (GstBaseTransform * trans,
|
static gboolean gst_video_rate_sink_event (GstBaseTransform * trans,
|
||||||
GstEvent * event);
|
GstEvent * event);
|
||||||
|
static gboolean gst_video_rate_src_event (GstBaseTransform * trans,
|
||||||
|
GstEvent * event);
|
||||||
static gboolean gst_video_rate_query (GstBaseTransform * trans,
|
static gboolean gst_video_rate_query (GstBaseTransform * trans,
|
||||||
GstPadDirection direction, GstQuery * query);
|
GstPadDirection direction, GstQuery * query);
|
||||||
|
|
||||||
|
@ -175,6 +183,7 @@ gst_video_rate_class_init (GstVideoRateClass * klass)
|
||||||
GST_DEBUG_FUNCPTR (gst_video_rate_transform_caps);
|
GST_DEBUG_FUNCPTR (gst_video_rate_transform_caps);
|
||||||
base_class->transform_ip = GST_DEBUG_FUNCPTR (gst_video_rate_transform_ip);
|
base_class->transform_ip = GST_DEBUG_FUNCPTR (gst_video_rate_transform_ip);
|
||||||
base_class->sink_event = GST_DEBUG_FUNCPTR (gst_video_rate_sink_event);
|
base_class->sink_event = GST_DEBUG_FUNCPTR (gst_video_rate_sink_event);
|
||||||
|
base_class->src_event = GST_DEBUG_FUNCPTR (gst_video_rate_src_event);
|
||||||
base_class->start = GST_DEBUG_FUNCPTR (gst_video_rate_start);
|
base_class->start = GST_DEBUG_FUNCPTR (gst_video_rate_start);
|
||||||
base_class->stop = GST_DEBUG_FUNCPTR (gst_video_rate_stop);
|
base_class->stop = GST_DEBUG_FUNCPTR (gst_video_rate_stop);
|
||||||
base_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_video_rate_fixate_caps);
|
base_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_video_rate_fixate_caps);
|
||||||
|
@ -250,6 +259,19 @@ gst_video_rate_class_init (GstVideoRateClass * klass)
|
||||||
1, G_MAXINT, DEFAULT_MAX_RATE,
|
1, G_MAXINT, DEFAULT_MAX_RATE,
|
||||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GstVideoRate:rate:
|
||||||
|
*
|
||||||
|
* Factor of speed for frame displaying
|
||||||
|
*
|
||||||
|
* Since: 1.12
|
||||||
|
*/
|
||||||
|
g_object_class_install_property (object_class, PROP_RATE,
|
||||||
|
g_param_spec_double ("rate", "Rate",
|
||||||
|
"Factor of speed for frame displaying", 0.0, G_MAXDOUBLE,
|
||||||
|
DEFAULT_RATE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
|
||||||
|
GST_PARAM_MUTABLE_READY));
|
||||||
|
|
||||||
gst_element_class_set_static_metadata (element_class,
|
gst_element_class_set_static_metadata (element_class,
|
||||||
"Video rate adjuster", "Filter/Effect/Video",
|
"Video rate adjuster", "Filter/Effect/Video",
|
||||||
"Drops/duplicates/adjusts timestamps on video frames to make a perfect stream",
|
"Drops/duplicates/adjusts timestamps on video frames to make a perfect stream",
|
||||||
|
@ -588,6 +610,7 @@ gst_video_rate_init (GstVideoRate * videorate)
|
||||||
videorate->average_period = DEFAULT_AVERAGE_PERIOD;
|
videorate->average_period = DEFAULT_AVERAGE_PERIOD;
|
||||||
videorate->average_period_set = DEFAULT_AVERAGE_PERIOD;
|
videorate->average_period_set = DEFAULT_AVERAGE_PERIOD;
|
||||||
videorate->max_rate = DEFAULT_MAX_RATE;
|
videorate->max_rate = DEFAULT_MAX_RATE;
|
||||||
|
videorate->rate = DEFAULT_RATE;
|
||||||
|
|
||||||
videorate->from_rate_numerator = 0;
|
videorate->from_rate_numerator = 0;
|
||||||
videorate->from_rate_denominator = 0;
|
videorate->from_rate_denominator = 0;
|
||||||
|
@ -727,11 +750,11 @@ gst_video_rate_sink_event (GstBaseTransform * trans, GstEvent * event)
|
||||||
switch (GST_EVENT_TYPE (event)) {
|
switch (GST_EVENT_TYPE (event)) {
|
||||||
case GST_EVENT_SEGMENT:
|
case GST_EVENT_SEGMENT:
|
||||||
{
|
{
|
||||||
const GstSegment *segment;
|
GstSegment segment;
|
||||||
|
gint seqnum;
|
||||||
|
|
||||||
gst_event_parse_segment (event, &segment);
|
gst_event_copy_segment (event, &segment);
|
||||||
|
if (segment.format != GST_FORMAT_TIME)
|
||||||
if (segment->format != GST_FORMAT_TIME)
|
|
||||||
goto format_error;
|
goto format_error;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (videorate, "handle NEWSEGMENT");
|
GST_DEBUG_OBJECT (videorate, "handle NEWSEGMENT");
|
||||||
|
@ -776,10 +799,23 @@ gst_video_rate_sink_event (GstBaseTransform * trans, GstEvent * event)
|
||||||
videorate->next_ts = GST_CLOCK_TIME_NONE;
|
videorate->next_ts = GST_CLOCK_TIME_NONE;
|
||||||
|
|
||||||
/* We just want to update the accumulated stream_time */
|
/* We just want to update the accumulated stream_time */
|
||||||
gst_segment_copy_into (segment, &videorate->segment);
|
|
||||||
|
|
||||||
|
segment.start = (gint64) (segment.start / videorate->rate);
|
||||||
|
segment.position = (gint64) (segment.position / videorate->rate);
|
||||||
|
if (GST_CLOCK_TIME_IS_VALID (segment.stop))
|
||||||
|
segment.stop = (gint64) (segment.stop / videorate->rate);
|
||||||
|
segment.time = (gint64) (segment.time / videorate->rate);
|
||||||
|
|
||||||
|
gst_segment_copy_into (&segment, &videorate->segment);
|
||||||
GST_DEBUG_OBJECT (videorate, "updated segment: %" GST_SEGMENT_FORMAT,
|
GST_DEBUG_OBJECT (videorate, "updated segment: %" GST_SEGMENT_FORMAT,
|
||||||
&videorate->segment);
|
&videorate->segment);
|
||||||
|
|
||||||
|
|
||||||
|
seqnum = gst_event_get_seqnum (event);
|
||||||
|
gst_event_unref (event);
|
||||||
|
event = gst_event_new_segment (&segment);
|
||||||
|
gst_event_set_seqnum (event, seqnum);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GST_EVENT_EOS:{
|
case GST_EVENT_EOS:{
|
||||||
|
@ -871,6 +907,47 @@ format_error:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_video_rate_src_event (GstBaseTransform * trans, GstEvent * event)
|
||||||
|
{
|
||||||
|
GstVideoRate *videorate;
|
||||||
|
GstPad *sinkpad;
|
||||||
|
gboolean res = FALSE;
|
||||||
|
|
||||||
|
videorate = GST_VIDEO_RATE (trans);
|
||||||
|
sinkpad = GST_BASE_TRANSFORM_SINK_PAD (trans);
|
||||||
|
switch (GST_EVENT_TYPE (event)) {
|
||||||
|
case GST_EVENT_SEEK:
|
||||||
|
{
|
||||||
|
gdouble srate;
|
||||||
|
GstSeekFlags flags;
|
||||||
|
GstSeekType start_type, stop_type;
|
||||||
|
gint64 start, stop;
|
||||||
|
gint seqnum = gst_event_get_seqnum (event);
|
||||||
|
|
||||||
|
gst_event_parse_seek (event, &srate, NULL, &flags, &start_type, &start,
|
||||||
|
&stop_type, &stop);
|
||||||
|
|
||||||
|
start = (gint64) (start * videorate->rate);
|
||||||
|
if (GST_CLOCK_TIME_IS_VALID (stop)) {
|
||||||
|
stop = (gint64) (stop * videorate->rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_event_unref (event);
|
||||||
|
event = gst_event_new_seek (srate, GST_FORMAT_TIME,
|
||||||
|
flags, start_type, start, stop_type, stop);
|
||||||
|
gst_event_set_seqnum (event, seqnum);
|
||||||
|
|
||||||
|
res = gst_pad_push_event (sinkpad, event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
res = gst_pad_push_event (sinkpad, event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_video_rate_query (GstBaseTransform * trans, GstPadDirection direction,
|
gst_video_rate_query (GstBaseTransform * trans, GstPadDirection direction,
|
||||||
GstQuery * query)
|
GstQuery * query)
|
||||||
|
@ -937,6 +1014,49 @@ gst_video_rate_query (GstBaseTransform * trans, GstPadDirection direction,
|
||||||
/* Simple fallthrough if we don't have a latency or not a peer that we
|
/* Simple fallthrough if we don't have a latency or not a peer that we
|
||||||
* can't ask about its latency yet.. */
|
* can't ask about its latency yet.. */
|
||||||
}
|
}
|
||||||
|
case GST_QUERY_DURATION:
|
||||||
|
{
|
||||||
|
GstFormat format;
|
||||||
|
gint64 duration;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
gst_query_parse_duration (query, &format, &duration);
|
||||||
|
|
||||||
|
if (format != GST_FORMAT_TIME) {
|
||||||
|
GST_DEBUG_OBJECT (videorate, "not TIME format");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
GST_LOG_OBJECT (videorate, "upstream duration: %" G_GINT64_FORMAT,
|
||||||
|
duration);
|
||||||
|
if (GST_CLOCK_TIME_IS_VALID (duration)) {
|
||||||
|
duration = (gint64) (duration / videorate->rate);
|
||||||
|
}
|
||||||
|
GST_LOG_OBJECT (videorate, "our duration: %" G_GINT64_FORMAT, duration);
|
||||||
|
gst_query_set_duration (query, format, duration);
|
||||||
|
res = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GST_QUERY_POSITION:
|
||||||
|
{
|
||||||
|
GstFormat dst_format;
|
||||||
|
gint64 dst_value;
|
||||||
|
|
||||||
|
gst_query_parse_position (query, &dst_format, &dst_value);
|
||||||
|
|
||||||
|
if (dst_format != GST_FORMAT_TIME) {
|
||||||
|
GST_DEBUG_OBJECT (videorate, "not TIME format");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dst_value =
|
||||||
|
(gint64) (gst_segment_to_stream_time (&videorate->segment,
|
||||||
|
GST_FORMAT_TIME, videorate->last_ts / videorate->rate));
|
||||||
|
GST_LOG_OBJECT (videorate, "our position: %" GST_TIME_FORMAT,
|
||||||
|
GST_TIME_ARGS (dst_value));
|
||||||
|
gst_query_set_position (query, dst_format, dst_value);
|
||||||
|
res = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
res =
|
res =
|
||||||
GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
|
GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
|
||||||
|
@ -1272,23 +1392,24 @@ gst_video_rate_transform_ip (GstBaseTransform * trans, GstBuffer * buffer)
|
||||||
|
|
||||||
/* got 2 buffers, see which one is the best */
|
/* got 2 buffers, see which one is the best */
|
||||||
do {
|
do {
|
||||||
|
GstClockTime next_ts = videorate->next_ts * videorate->rate;
|
||||||
|
|
||||||
/* take absolute values, beware: abs and ABS don't work for gint64 */
|
/* take absolute values, beware: abs and ABS don't work for gint64 */
|
||||||
if (prevtime > videorate->next_ts)
|
if (prevtime > next_ts)
|
||||||
diff1 = prevtime - videorate->next_ts;
|
diff1 = prevtime - next_ts;
|
||||||
else
|
else
|
||||||
diff1 = videorate->next_ts - prevtime;
|
diff1 = next_ts - prevtime;
|
||||||
|
|
||||||
if (intime > videorate->next_ts)
|
if (intime > next_ts)
|
||||||
diff2 = intime - videorate->next_ts;
|
diff2 = intime - next_ts;
|
||||||
else
|
else
|
||||||
diff2 = videorate->next_ts - intime;
|
diff2 = next_ts - intime;
|
||||||
|
|
||||||
GST_LOG_OBJECT (videorate,
|
GST_LOG_OBJECT (videorate,
|
||||||
"diff with prev %" GST_TIME_FORMAT " diff with new %"
|
"diff with prev %" GST_TIME_FORMAT " diff with new %"
|
||||||
GST_TIME_FORMAT " outgoing ts %" GST_TIME_FORMAT,
|
GST_TIME_FORMAT " outgoing ts %" GST_TIME_FORMAT,
|
||||||
GST_TIME_ARGS (diff1), GST_TIME_ARGS (diff2),
|
GST_TIME_ARGS (diff1), GST_TIME_ARGS (diff2),
|
||||||
GST_TIME_ARGS (videorate->next_ts));
|
GST_TIME_ARGS (next_ts));
|
||||||
|
|
||||||
if (videorate->segment.rate < 0.0) {
|
if (videorate->segment.rate < 0.0) {
|
||||||
/* Make sure that we have a duration for this buffer. The previous
|
/* Make sure that we have a duration for this buffer. The previous
|
||||||
|
@ -1384,6 +1505,15 @@ gst_video_rate_stop (GstBaseTransform * trans)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_videorate_update_duration (GstVideoRate * videorate)
|
||||||
|
{
|
||||||
|
GstMessage *m;
|
||||||
|
|
||||||
|
m = gst_message_new_duration_changed (GST_OBJECT (videorate));
|
||||||
|
gst_element_post_message (GST_ELEMENT (videorate), m);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_video_rate_set_property (GObject * object,
|
gst_video_rate_set_property (GObject * object,
|
||||||
guint prop_id, const GValue * value, GParamSpec * pspec)
|
guint prop_id, const GValue * value, GParamSpec * pspec)
|
||||||
|
@ -1416,11 +1546,18 @@ gst_video_rate_set_property (GObject * object,
|
||||||
case PROP_MAX_RATE:
|
case PROP_MAX_RATE:
|
||||||
g_atomic_int_set (&videorate->max_rate, g_value_get_int (value));
|
g_atomic_int_set (&videorate->max_rate, g_value_get_int (value));
|
||||||
goto reconfigure;
|
goto reconfigure;
|
||||||
|
case PROP_RATE:
|
||||||
|
videorate->rate = g_value_get_double (value);
|
||||||
|
GST_OBJECT_UNLOCK (videorate);
|
||||||
|
|
||||||
|
gst_videorate_update_duration (videorate);
|
||||||
|
return;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
GST_OBJECT_UNLOCK (videorate);
|
GST_OBJECT_UNLOCK (videorate);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
reconfigure:
|
reconfigure:
|
||||||
|
@ -1471,6 +1608,9 @@ gst_video_rate_get_property (GObject * object,
|
||||||
case PROP_MAX_RATE:
|
case PROP_MAX_RATE:
|
||||||
g_value_set_int (value, g_atomic_int_get (&videorate->max_rate));
|
g_value_set_int (value, g_atomic_int_get (&videorate->max_rate));
|
||||||
break;
|
break;
|
||||||
|
case PROP_RATE:
|
||||||
|
g_value_set_double (value, videorate->rate);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
#include <gst/base/gstbasetransform.h>
|
#include <gst/base/gstbasetransform.h>
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
#define GST_TYPE_VIDEO_RATE \
|
#define GST_TYPE_VIDEO_RATE \
|
||||||
(gst_video_rate_get_type())
|
(gst_video_rate_get_type())
|
||||||
#define GST_VIDEO_RATE(obj) \
|
#define GST_VIDEO_RATE(obj) \
|
||||||
|
@ -35,7 +34,6 @@ G_BEGIN_DECLS
|
||||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_RATE))
|
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_RATE))
|
||||||
#define GST_IS_VIDEO_RATE_CLASS(klass) \
|
#define GST_IS_VIDEO_RATE_CLASS(klass) \
|
||||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_RATE))
|
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_RATE))
|
||||||
|
|
||||||
typedef struct _GstVideoRate GstVideoRate;
|
typedef struct _GstVideoRate GstVideoRate;
|
||||||
typedef struct _GstVideoRateClass GstVideoRateClass;
|
typedef struct _GstVideoRateClass GstVideoRateClass;
|
||||||
|
|
||||||
|
@ -80,6 +78,7 @@ struct _GstVideoRate
|
||||||
guint64 average_period_set;
|
guint64 average_period_set;
|
||||||
|
|
||||||
volatile int max_rate;
|
volatile int max_rate;
|
||||||
|
gdouble rate;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstVideoRateClass
|
struct _GstVideoRateClass
|
||||||
|
@ -90,5 +89,4 @@ struct _GstVideoRateClass
|
||||||
GType gst_video_rate_get_type (void);
|
GType gst_video_rate_get_type (void);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __GST_VIDEO_RATE_H__ */
|
#endif /* __GST_VIDEO_RATE_H__ */
|
||||||
|
|
|
@ -999,8 +999,7 @@ check_caps_identical (GstCaps * a, GstCaps * b, const char *name)
|
||||||
fail:
|
fail:
|
||||||
caps_str_a = gst_caps_to_string (a);
|
caps_str_a = gst_caps_to_string (a);
|
||||||
caps_str_b = gst_caps_to_string (b);
|
caps_str_b = gst_caps_to_string (b);
|
||||||
fail ("%s caps (%s) is not equal to caps (%s)",
|
fail ("%s caps (%s) is not equal to caps (%s)", name, caps_str_a, caps_str_b);
|
||||||
name, caps_str_a, caps_str_b);
|
|
||||||
g_free (caps_str_a);
|
g_free (caps_str_a);
|
||||||
g_free (caps_str_b);
|
g_free (caps_str_b);
|
||||||
}
|
}
|
||||||
|
@ -1168,6 +1167,231 @@ GST_START_TEST (test_variable_framerate_renegotiation)
|
||||||
|
|
||||||
GST_END_TEST;
|
GST_END_TEST;
|
||||||
|
|
||||||
|
/* Rate tests info */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
gdouble rate;
|
||||||
|
guint64 expected_in, expected_out, expected_drop, expected_dup;
|
||||||
|
gint current_buf;
|
||||||
|
GstClockTime expected_ts;
|
||||||
|
} RateInfo;
|
||||||
|
|
||||||
|
static RateInfo rate_tests[] = {
|
||||||
|
{
|
||||||
|
.rate = 1.0,
|
||||||
|
.expected_in = 34,
|
||||||
|
.expected_out = 25,
|
||||||
|
.expected_drop = 8,
|
||||||
|
.expected_dup = 0,
|
||||||
|
.current_buf = 0,
|
||||||
|
.expected_ts = 0},
|
||||||
|
{
|
||||||
|
.rate = 0.5,
|
||||||
|
.expected_in = 34,
|
||||||
|
.expected_out = 50,
|
||||||
|
.expected_drop = 0,
|
||||||
|
.expected_dup = 17,
|
||||||
|
.current_buf = 0,
|
||||||
|
.expected_ts = 0},
|
||||||
|
{
|
||||||
|
.rate = 2.0,
|
||||||
|
.expected_in = 34,
|
||||||
|
.expected_out = 13,
|
||||||
|
.expected_drop = 20,
|
||||||
|
.expected_dup = 0,
|
||||||
|
.current_buf = 0,
|
||||||
|
.expected_ts = 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
static GstPadProbeReturn
|
||||||
|
listen_outbuffer_ts (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
|
||||||
|
{
|
||||||
|
GstBuffer *buffer;
|
||||||
|
guint64 buf_ts;
|
||||||
|
RateInfo *test = (RateInfo *) user_data;
|
||||||
|
|
||||||
|
buffer = GST_PAD_PROBE_INFO_BUFFER (info);
|
||||||
|
buf_ts = GST_BUFFER_TIMESTAMP (buffer);
|
||||||
|
|
||||||
|
GST_DEBUG ("Probed %d outbuf. ts : %" GST_TIME_FORMAT
|
||||||
|
", expected : %" GST_TIME_FORMAT, test->current_buf,
|
||||||
|
GST_TIME_ARGS (buf_ts), GST_TIME_ARGS (test->expected_ts));
|
||||||
|
fail_unless_equals_uint64 (buf_ts, test->expected_ts);
|
||||||
|
|
||||||
|
/* Next expected timestamp with fps 25/1 */
|
||||||
|
test->expected_ts += 40000000;
|
||||||
|
test->current_buf += 1;
|
||||||
|
|
||||||
|
fail_if (test->current_buf > test->expected_out);
|
||||||
|
|
||||||
|
return GST_PAD_PROBE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_START_TEST (test_rate)
|
||||||
|
{
|
||||||
|
GstElement *videorate;
|
||||||
|
RateInfo *test = &rate_tests[__i__];
|
||||||
|
GstClockTime ts;
|
||||||
|
GstBuffer *buf;
|
||||||
|
GstCaps *caps;
|
||||||
|
gulong probe;
|
||||||
|
|
||||||
|
videorate = setup_videorate ();
|
||||||
|
fail_unless (gst_element_set_state (videorate,
|
||||||
|
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
|
||||||
|
"could not set to playing");
|
||||||
|
probe = gst_pad_add_probe (mysinkpad, GST_PAD_PROBE_TYPE_BUFFER,
|
||||||
|
(GstPadProbeCallback) listen_outbuffer_ts, test, NULL);
|
||||||
|
|
||||||
|
buf = gst_buffer_new_and_alloc (4);
|
||||||
|
gst_buffer_memset (buf, 0, 0, 4);
|
||||||
|
caps = gst_caps_from_string (VIDEO_CAPS_STRING);
|
||||||
|
gst_check_setup_events (mysrcpad, videorate, caps, GST_FORMAT_TIME);
|
||||||
|
gst_caps_unref (caps);
|
||||||
|
ASSERT_BUFFER_REFCOUNT (buf, "inbuffer", 1);
|
||||||
|
|
||||||
|
/* Setting rate */
|
||||||
|
g_object_set (videorate, "rate", test->rate, NULL);
|
||||||
|
|
||||||
|
/* Push 1 second of buffers */
|
||||||
|
for (ts = 0; ts < 1 * GST_SECOND; ts += GST_SECOND / 33) {
|
||||||
|
GstBuffer *inbuf;
|
||||||
|
|
||||||
|
inbuf = gst_buffer_copy (buf);
|
||||||
|
GST_BUFFER_TIMESTAMP (inbuf) = ts;
|
||||||
|
|
||||||
|
fail_unless_equals_int (gst_pad_push (mysrcpad, inbuf), GST_FLOW_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
fail_unless_equals_int (g_list_length (buffers), test->expected_out);
|
||||||
|
assert_videorate_stats (videorate, "last buffer", test->expected_in,
|
||||||
|
test->expected_out, test->expected_drop, test->expected_dup);
|
||||||
|
|
||||||
|
/* cleanup */
|
||||||
|
gst_pad_remove_probe (mysinkpad, probe);
|
||||||
|
cleanup_videorate (videorate);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
/* Probing the pad to force a fake upstream duration */
|
||||||
|
static GstPadProbeReturn
|
||||||
|
listen_sink_query_duration (GstPad * pad, GstPadProbeInfo * info,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GstQuery *query;
|
||||||
|
gint64 *duration = (gint64 *) user_data;
|
||||||
|
|
||||||
|
query = gst_pad_probe_info_get_query (info);
|
||||||
|
|
||||||
|
if (GST_QUERY_TYPE (query) == GST_QUERY_DURATION) {
|
||||||
|
gst_query_set_duration (query, GST_FORMAT_TIME, *duration);
|
||||||
|
}
|
||||||
|
return GST_PAD_PROBE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_START_TEST (test_query_duration)
|
||||||
|
{
|
||||||
|
GstElement *videorate;
|
||||||
|
gulong probe_sink;
|
||||||
|
gint64 duration;
|
||||||
|
GstQuery *query;
|
||||||
|
|
||||||
|
videorate = setup_videorate ();
|
||||||
|
fail_unless (gst_element_set_state (videorate,
|
||||||
|
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
|
||||||
|
"could not set to playing");
|
||||||
|
probe_sink =
|
||||||
|
gst_pad_add_probe (mysrcpad,
|
||||||
|
GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM | GST_PAD_PROBE_TYPE_PUSH,
|
||||||
|
(GstPadProbeCallback) listen_sink_query_duration, &duration, NULL);
|
||||||
|
|
||||||
|
query = gst_query_new_duration (GST_FORMAT_TIME);
|
||||||
|
duration = GST_CLOCK_TIME_NONE;
|
||||||
|
gst_pad_peer_query (mysrcpad, query);
|
||||||
|
gst_query_parse_duration (query, NULL, &duration);
|
||||||
|
fail_unless_equals_uint64 (duration, GST_CLOCK_TIME_NONE);
|
||||||
|
|
||||||
|
/* Setting fake upstream duration to 1 second */
|
||||||
|
duration = GST_SECOND;
|
||||||
|
|
||||||
|
/* Setting rate to 2.0 */
|
||||||
|
g_object_set (videorate, "rate", 2.0, NULL);
|
||||||
|
|
||||||
|
gst_pad_peer_query (mysrcpad, query);
|
||||||
|
gst_query_parse_duration (query, NULL, &duration);
|
||||||
|
fail_unless_equals_uint64 (duration, 0.5 * GST_SECOND);
|
||||||
|
|
||||||
|
/* cleanup */
|
||||||
|
gst_query_unref (query);
|
||||||
|
gst_pad_remove_probe (mysrcpad, probe_sink);
|
||||||
|
cleanup_videorate (videorate);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
/* Position tests info */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
gdouble rate;
|
||||||
|
} PositionInfo;
|
||||||
|
|
||||||
|
static PositionInfo position_tests[] = {
|
||||||
|
{
|
||||||
|
.rate = 1.0},
|
||||||
|
{
|
||||||
|
.rate = 0.5},
|
||||||
|
{
|
||||||
|
.rate = 2.0},
|
||||||
|
{
|
||||||
|
.rate = 1.7},
|
||||||
|
};
|
||||||
|
|
||||||
|
GST_START_TEST (test_query_position)
|
||||||
|
{
|
||||||
|
GstElement *videorate;
|
||||||
|
PositionInfo *test = &position_tests[__i__];
|
||||||
|
GstClockTime ts;
|
||||||
|
GstBuffer *buf;
|
||||||
|
GstCaps *caps;
|
||||||
|
gint64 position, expected_position = 0;
|
||||||
|
|
||||||
|
videorate = setup_videorate ();
|
||||||
|
fail_unless (gst_element_set_state (videorate,
|
||||||
|
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
|
||||||
|
"could not set to playing");
|
||||||
|
|
||||||
|
buf = gst_buffer_new_and_alloc (4);
|
||||||
|
gst_buffer_memset (buf, 0, 0, 4);
|
||||||
|
caps = gst_caps_from_string (VIDEO_CAPS_STRING);
|
||||||
|
gst_check_setup_events (mysrcpad, videorate, caps, GST_FORMAT_TIME);
|
||||||
|
gst_caps_unref (caps);
|
||||||
|
ASSERT_BUFFER_REFCOUNT (buf, "inbuffer", 1);
|
||||||
|
|
||||||
|
/* Push a few buffers */
|
||||||
|
g_object_set (videorate, "rate", test->rate, NULL);
|
||||||
|
for (ts = 0; ts < GST_SECOND; ts += GST_SECOND / 20) {
|
||||||
|
GstBuffer *inbuf;
|
||||||
|
|
||||||
|
inbuf = gst_buffer_copy (buf);
|
||||||
|
GST_BUFFER_TIMESTAMP (inbuf) = ts;
|
||||||
|
|
||||||
|
fail_unless_equals_int (gst_pad_push (mysrcpad, inbuf), GST_FLOW_OK);
|
||||||
|
|
||||||
|
expected_position = ts / test->rate;
|
||||||
|
gst_element_query_position (videorate, GST_FORMAT_TIME, &position);
|
||||||
|
GST_DEBUG_OBJECT (NULL,
|
||||||
|
"pushed buffer %" GST_TIME_FORMAT ", queried pos: %" GST_TIME_FORMAT
|
||||||
|
", expected pos: %" GST_TIME_FORMAT, GST_TIME_ARGS (ts),
|
||||||
|
GST_TIME_ARGS (position), GST_TIME_ARGS (expected_position));
|
||||||
|
fail_unless_equals_uint64 (position, expected_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* cleanup */
|
||||||
|
cleanup_videorate (videorate);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
static Suite *
|
static Suite *
|
||||||
videorate_suite (void)
|
videorate_suite (void)
|
||||||
|
@ -1189,6 +1413,10 @@ videorate_suite (void)
|
||||||
0, G_N_ELEMENTS (caps_negotiation_tests));
|
0, G_N_ELEMENTS (caps_negotiation_tests));
|
||||||
tcase_add_test (tc_chain, test_fixed_framerate);
|
tcase_add_test (tc_chain, test_fixed_framerate);
|
||||||
tcase_add_test (tc_chain, test_variable_framerate_renegotiation);
|
tcase_add_test (tc_chain, test_variable_framerate_renegotiation);
|
||||||
|
tcase_add_loop_test (tc_chain, test_rate, 0, G_N_ELEMENTS (rate_tests));
|
||||||
|
tcase_add_test (tc_chain, test_query_duration);
|
||||||
|
tcase_add_loop_test (tc_chain, test_query_position, 0,
|
||||||
|
G_N_ELEMENTS (position_tests));
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue