timecodestamper: Add seeking support

The approach is quite simple and doesn't take all use cases into account,
it only implements support when we are using the internal timecode we
create ourself.

Also the way we compute the sought frame count is naive, but it works
for simple cases.
This commit is contained in:
Thibault Saunier 2020-02-21 13:12:39 -03:00
parent 905988c63f
commit a0423ee20f
2 changed files with 81 additions and 5 deletions

View file

@ -119,6 +119,8 @@ static void gst_timecodestamper_get_property (GObject * object, guint prop_id,
static void gst_timecodestamper_dispose (GObject * object);
static gboolean gst_timecodestamper_sink_event (GstBaseTransform * trans,
GstEvent * event);
static gboolean gst_timecodestamper_src_event (GstBaseTransform * trans,
GstEvent * event);
static GstFlowReturn gst_timecodestamper_transform_ip (GstBaseTransform *
vfilter, GstBuffer * buffer);
static gboolean gst_timecodestamper_stop (GstBaseTransform * trans);
@ -320,6 +322,7 @@ gst_timecodestamper_class_init (GstTimeCodeStamperClass * klass)
GST_DEBUG_FUNCPTR (gst_timecodestamper_release_pad);
trans_class->sink_event = GST_DEBUG_FUNCPTR (gst_timecodestamper_sink_event);
trans_class->src_event = GST_DEBUG_FUNCPTR (gst_timecodestamper_src_event);
#if HAVE_LTC
trans_class->query = GST_DEBUG_FUNCPTR (gst_timecodestamper_query);
#endif
@ -355,6 +358,8 @@ gst_timecodestamper_init (GstTimeCodeStamper * timecodestamper)
timecodestamper->last_tc_running_time = GST_CLOCK_TIME_NONE;
timecodestamper->rtc_tc = NULL;
timecodestamper->seeked_frames = -1;
#if HAVE_LTC
g_mutex_init (&timecodestamper->mutex);
g_cond_init (&timecodestamper->ltc_cond_video);
@ -842,6 +847,15 @@ gst_timecodestamper_sink_event (GstBaseTransform * trans, GstEvent * event)
gst_event_unref (event);
return FALSE;
}
GST_OBJECT_LOCK (timecodestamper);
if (timecodestamper->tc_source == GST_TIME_CODE_STAMPER_SOURCE_INTERNAL
&& GST_EVENT_SEQNUM (event) == timecodestamper->prev_seek_seqnum) {
timecodestamper->reset_internal_tc_from_seek = TRUE;
timecodestamper->prev_seek_seqnum = GST_SEQNUM_INVALID;
}
GST_OBJECT_UNLOCK (timecodestamper);
break;
}
case GST_EVENT_CAPS:
@ -907,6 +921,53 @@ gst_timecodestamper_sink_event (GstBaseTransform * trans, GstEvent * event)
return ret;
}
static gboolean
gst_timecodestamper_src_event (GstBaseTransform * trans, GstEvent * event)
{
GstTimeCodeStamper *timecodestamper = GST_TIME_CODE_STAMPER (trans);
GST_DEBUG_OBJECT (trans, "received event %" GST_PTR_FORMAT, event);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
{
gdouble rate;
GstSeekType start_type;
gint64 start;
GstFormat format;
gst_event_parse_seek (event, &rate, &format, NULL, &start_type, &start,
NULL, NULL);
if (rate < 0) {
GST_ERROR_OBJECT (timecodestamper, "Reverse playback is not supported");
return FALSE;
}
if (format != GST_FORMAT_TIME) {
GST_ERROR_OBJECT (timecodestamper,
"Seeking is only supported in TIME format");
return FALSE;
}
GST_OBJECT_LOCK (timecodestamper);
if (timecodestamper->vinfo.fps_d && timecodestamper->vinfo.fps_n) {
timecodestamper->prev_seek_seqnum = GST_EVENT_SEQNUM (event);
timecodestamper->seeked_frames = gst_util_uint64_scale (start,
timecodestamper->vinfo.fps_n,
timecodestamper->vinfo.fps_d * GST_SECOND);
}
GST_OBJECT_UNLOCK (timecodestamper);
break;
}
default:
break;
}
return
GST_BASE_TRANSFORM_CLASS (gst_timecodestamper_parent_class)->src_event
(trans, event);
}
#if HAVE_LTC
static gboolean
gst_timecodestamper_query (GstBaseTransform * trans,
@ -1082,24 +1143,34 @@ gst_timecodestamper_transform_ip (GstBaseTransform * vfilter,
/* If we don't have an internal timecode yet then either a new one was just
* set via the property or we just started. Initialize it here, otherwise
* increment it by one */
if (!timecodestamper->internal_tc) {
if (!timecodestamper->internal_tc
|| timecodestamper->reset_internal_tc_from_seek) {
gchar *tc_str;
if (timecodestamper->set_internal_tc)
timecodestamper->reset_internal_tc_from_seek = FALSE;
if (timecodestamper->set_internal_tc) {
timecodestamper->internal_tc =
gst_video_time_code_new (timecodestamper->vinfo.fps_n,
timecodestamper->vinfo.fps_d,
timecodestamper->set_internal_tc->config.latest_daily_jam,
tc_flags,
timecodestamper->set_internal_tc->config.latest_daily_jam, tc_flags,
timecodestamper->set_internal_tc->hours,
timecodestamper->set_internal_tc->minutes,
timecodestamper->set_internal_tc->seconds,
timecodestamper->set_internal_tc->frames,
timecodestamper->set_internal_tc->field_count);
else
} else {
timecodestamper->internal_tc =
gst_video_time_code_new (timecodestamper->vinfo.fps_n,
timecodestamper->vinfo.fps_d, dt_frame, tc_flags, 0, 0, 0, 0, 0);
if (timecodestamper->seeked_frames > 0) {
GST_DEBUG_OBJECT (timecodestamper,
"Adding %" G_GINT64_FORMAT " frames that were seeked",
timecodestamper->seeked_frames);
gst_video_time_code_add_frames (timecodestamper->internal_tc,
timecodestamper->seeked_frames);
timecodestamper->seeked_frames = -1;
}
}
tc_str = gst_video_time_code_to_string (timecodestamper->internal_tc);
GST_DEBUG_OBJECT (timecodestamper, "Initialized internal timecode to %s",

View file

@ -99,6 +99,11 @@ struct _GstTimeCodeStamper
/* Internal state */
GstVideoInfo vinfo; /* protected by object lock, changed only from video streaming thread */
/* Seek handling, protected by the object lock */
guint32 prev_seek_seqnum;
gboolean reset_internal_tc_from_seek;
gint64 seeked_frames;
/* LTC specific fields */
#if HAVE_LTC
GMutex mutex;