videotimecode: New GstVideoTimeCodeInterval type, ability to add to a GstVideoTimeCode

Sometimes there is a human-oriented timecode that represents an
interval between two other timecodes. It corresponds to the human
perception of "add X hours" or "add X seconds" to a specific timecode,
taking drop-frame oddities into account. This interval-representing
timecode is now a GstVideoTimeCodeInterval. Also added function to add it to
a GstVideoTimeCode.

https://bugzilla.gnome.org/show_bug.cgi?id=776447
This commit is contained in:
Vivia Nikolaidou 2016-12-29 14:42:52 +02:00 committed by Jan Schmidt
parent 7bd7b2209a
commit 629e8229a7
2 changed files with 237 additions and 0 deletions

View file

@ -716,3 +716,198 @@ gst_video_time_code_free (GstVideoTimeCode * tc)
g_free (tc);
}
/**
* gst_video_time_code_add_interval:
* @tc: The #GstVideoTimeCode where the diff should be added
* @tc_inter: The #GstVideoTimeCodeInterval to add to @tc
*
* This makes a component-wise addition of @tc_inter to @tc. For example,
* adding ("01:02:03:04", "00:01:00:00") will return "01:03:03:04".
* When it comes to drop-frame timecodes,
* adding ("00:00:00;00", "00:01:00:00") will return "00:01:00;02"
* because of drop-frame oddities. However,
* adding ("00:09:00;02", "00:01:00:00") will return "00:10:00;00"
* because this time we can have an exact minute.
*
* Returns: A new #GstVideoTimeCode with @tc_inter added.
*
* Since: 1.12
*/
GstVideoTimeCode *
gst_video_time_code_add_interval (const GstVideoTimeCode * tc,
const GstVideoTimeCodeInterval * tc_inter)
{
GstVideoTimeCode *ret =
gst_video_time_code_new (tc->config.fps_n, tc->config.fps_d,
tc->config.latest_daily_jam, tc->config.flags, tc_inter->hours,
tc_inter->minutes, tc_inter->seconds, tc_inter->frames, 0);
guint frames_to_add = gst_video_time_code_frames_since_daily_jam (tc);
gboolean check_again = FALSE;
/* Drop-frame compensation: 00:01:00;00 is falsely interpreted as
* 00:00:59;28 */
if (tc->config.flags & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME &&
tc_inter->frames == 0 && tc_inter->seconds == 0
&& (tc_inter->minutes % 10 != 0)) {
/* User wants us to split at invalid timecodes */
check_again = TRUE;
if (tc->minutes % 10 == 0) {
/* Apply compensation every 10th minute: before adding the frames,
* but only if we are before the "invalid frame" mark */
if (tc->config.fps_n == 30000 && tc->frames <= 2) {
frames_to_add += 2;
check_again = FALSE;
} else if (tc->config.fps_n == 60000 && tc->frames <= 4) {
frames_to_add += 4;
check_again = FALSE;
}
}
}
gst_video_time_code_add_frames (ret, frames_to_add);
if (check_again && ret->minutes % 10 == 0) {
if (ret->config.fps_n == 30000 && tc->frames > 2) {
frames_to_add = 2;
} else if (ret->config.fps_n == 60000 && tc->frames > 4) {
frames_to_add = 4;
} else {
frames_to_add = 0;
}
gst_video_time_code_add_frames (ret, frames_to_add);
}
return ret;
}
G_DEFINE_BOXED_TYPE (GstVideoTimeCodeInterval, gst_video_time_code_interval,
(GBoxedCopyFunc) gst_video_time_code_interval_copy,
(GBoxedFreeFunc) gst_video_time_code_interval_free);
/**
* gst_video_time_code_interval_new:
* @hours: the hours field of #GstVideoTimeCodeInterval
* @minutes: the minutes field of #GstVideoTimeCodeInterval
* @seconds: the seconds field of #GstVideoTimeCodeInterval
* @frames: the frames field of #GstVideoTimeCodeInterval
*
* Returns: a new #GstVideoTimeCodeInterval with the given values.
*
* Since: 1.12
*/
GstVideoTimeCodeInterval *
gst_video_time_code_interval_new (guint hours, guint minutes, guint seconds,
guint frames)
{
GstVideoTimeCodeInterval *tc;
tc = g_new0 (GstVideoTimeCodeInterval, 1);
gst_video_time_code_interval_init (tc, hours, minutes, seconds, frames);
return tc;
}
/**
* gst_video_time_code_interval_new_from_string:
* @tc_inter_str: The string that represents the #GstVideoTimeCodeInterval
*
* @tc_inter_str must only have ":" as separators.
*
* Returns: a new #GstVideoTimeCodeInterval from the given string
*
* Since: 1.12
*/
GstVideoTimeCodeInterval *
gst_video_time_code_interval_new_from_string (const gchar * tc_inter_str)
{
GstVideoTimeCodeInterval *tc;
guint hours, minutes, seconds, frames;
if (sscanf (tc_inter_str, "%02u:%02u:%02u:%02u", &hours, &minutes, &seconds,
&frames)
== 4
|| sscanf (tc_inter_str, "%02u:%02u:%02u;%02u", &hours, &minutes,
&seconds, &frames)
== 4
|| sscanf (tc_inter_str, "%02u:%02u:%02u.%02u", &hours, &minutes,
&seconds, &frames)
== 4
|| sscanf (tc_inter_str, "%02u:%02u:%02u,%02u", &hours, &minutes,
&seconds, &frames)
== 4) {
tc = gst_video_time_code_interval_new (hours, minutes, seconds, frames);
return tc;
} else {
GST_ERROR ("Warning: Could not parse timecode %s. "
"Please input a timecode in the form 00:00:00:00", tc_inter_str);
return NULL;
}
}
/**
* gst_video_time_code_interval_init:
* @tc: a #GstVideoTimeCodeInterval
* @hours: the hours field of #GstVideoTimeCodeInterval
* @minutes: the minutes field of #GstVideoTimeCodeInterval
* @seconds: the seconds field of #GstVideoTimeCodeInterval
* @frames: the frames field of #GstVideoTimeCodeInterval
*
* Initializes @tc with the given values.
*
* Since: 1.12
*/
void
gst_video_time_code_interval_init (GstVideoTimeCodeInterval * tc, guint hours,
guint minutes, guint seconds, guint frames)
{
tc->hours = hours;
tc->minutes = minutes;
tc->seconds = seconds;
tc->frames = frames;
}
/**
* gst_video_time_code_interval_clear:
* @tc: a #GstVideoTimeCodeInterval
*
* Initializes @tc with empty/zero/NULL values.
*
* Since: 1.12
*/
void
gst_video_time_code_interval_clear (GstVideoTimeCodeInterval * tc)
{
tc->hours = 0;
tc->minutes = 0;
tc->seconds = 0;
tc->frames = 0;
}
/**
* gst_video_time_code_interval_copy:
* @tc: a #GstVideoTimeCodeInterval
*
* Returns: a new #GstVideoTimeCodeInterval with the same values as @tc .
*
* Since: 1.12
*/
GstVideoTimeCodeInterval *
gst_video_time_code_interval_copy (const GstVideoTimeCodeInterval * tc)
{
return gst_video_time_code_interval_new (tc->hours, tc->minutes,
tc->seconds, tc->frames);
}
/**
* gst_video_time_code_interval_free:
* @tc: a #GstVideoTimeCodeInterval
*
* Frees @tc .
*
* Since: 1.12
*/
void
gst_video_time_code_interval_free (GstVideoTimeCodeInterval * tc)
{
g_free (tc);
}

View file

@ -26,6 +26,7 @@ G_BEGIN_DECLS
typedef struct _GstVideoTimeCodeConfig GstVideoTimeCodeConfig;
typedef struct _GstVideoTimeCode GstVideoTimeCode;
typedef struct _GstVideoTimeCodeInterval GstVideoTimeCodeInterval;
/**
* GstVideoTimeCodeFlags:
@ -100,6 +101,26 @@ struct _GstVideoTimeCode {
guint field_count;
};
/**
* GstVideoTimeCodeInterval:
* @hours: the hours field of #GstVideoTimeCodeInterval
* @minutes: the minutes field of #GstVideoTimeCodeInterval
* @seconds: the seconds field of #GstVideoTimeCodeInterval
* @frames: the frames field of #GstVideoTimeCodeInterval
*
* A representation of a difference between two #GstVideoTimeCode instances.
* Will not necessarily correspond to a real timecode (e.g. 00:00:10;00)
*
* Since: 1.12
*/
struct _GstVideoTimeCodeInterval {
guint hours;
guint minutes;
guint seconds;
guint frames;
};
#define GST_TYPE_VIDEO_TIME_CODE (gst_video_time_code_get_type())
GType gst_video_time_code_get_type (void);
@ -147,6 +168,27 @@ guint64 gst_video_time_code_nsec_since_daily_jam (const GstVideoTimeCode * tc
guint64 gst_video_time_code_frames_since_daily_jam (const GstVideoTimeCode * tc);
GstVideoTimeCode * gst_video_time_code_add_interval (const GstVideoTimeCode * tc, const GstVideoTimeCodeInterval * tc_inter);
#define GST_TYPE_VIDEO_TIME_CODE_INTERVAL (gst_video_time_code_interval_get_type())
GType gst_video_time_code_interval_get_type (void);
GstVideoTimeCodeInterval * gst_video_time_code_interval_new (guint hours,
guint minutes,
guint seconds,
guint frames);
GstVideoTimeCodeInterval * gst_video_time_code_interval_new_from_string (const gchar * tc_inter_str);
void gst_video_time_code_interval_free (GstVideoTimeCodeInterval * tc);
GstVideoTimeCodeInterval * gst_video_time_code_interval_copy (const GstVideoTimeCodeInterval * tc);
void gst_video_time_code_interval_init (GstVideoTimeCodeInterval * tc,
guint hours,
guint minutes,
guint seconds,
guint frames);
void gst_video_time_code_interval_clear (GstVideoTimeCodeInterval * tc);
G_END_DECLS
#endif /* __GST_VIDEO_TIME_CODE_H__ */