videotimecode: New GstVideoTimeCodeInterval type, ability to add to a GstVideoTimeCode
authorVivia Nikolaidou <vivia@toolsonair.com>
Thu, 29 Dec 2016 12:42:52 +0000 (14:42 +0200)
committerJan Schmidt <jan@centricular.com>
Wed, 11 Jan 2017 00:05:21 +0000 (11:05 +1100)
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

gst-libs/gst/video/gstvideotimecode.c
gst-libs/gst/video/gstvideotimecode.h

index c1aabf7..187c92b 100644 (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);
+}
index 1443a7b..7fc7d40 100644 (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__ */