summaryrefslogtreecommitdiffstats
path: root/ext
diff options
context:
space:
mode:
authorDavid Schleef <ds@schleef.org>2009-09-13 12:20:23 -0700
committerDavid Schleef <ds@schleef.org>2009-09-13 13:22:35 -0700
commit29173242bb589a1d51c05a5301aac43002d4b46c (patch)
treeeb7e80ef6bd91b3715bb89f661aac55826c06989 /ext
parent1b57f1950905c4e3432312108cab19dd1b8446a0 (diff)
dvdemux: Add code to parse SMPTE time codes
Code to convert time codes to/from timestamps and frame numbers.
Diffstat (limited to 'ext')
-rw-r--r--ext/dv/Makefile.am7
-rw-r--r--ext/dv/gstsmptetimecode.c240
-rw-r--r--ext/dv/gstsmptetimecode.h65
-rw-r--r--ext/dv/smpte_test.c81
4 files changed, 393 insertions, 0 deletions
diff --git a/ext/dv/Makefile.am b/ext/dv/Makefile.am
index 6eb2014d..7c0ef940 100644
--- a/ext/dv/Makefile.am
+++ b/ext/dv/Makefile.am
@@ -11,3 +11,10 @@ libgstdv_la_LIBTOOLFLAGS = --tag=disable-static
noinst_HEADERS = gstdvdemux.h gstdvdec.h
EXTRA_DIST = NOTES
+
+noinst_PROGRAMS = smpte_test
+
+smpte_test_SOURCES = smpte_test.c gstsmptetimecode.c
+smpte_test_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(LIBDV_CFLAGS)
+smpte_test_LDADD = $(GST_BASE_LIBS) $(GST_LIBS)
+
diff --git a/ext/dv/gstsmptetimecode.c b/ext/dv/gstsmptetimecode.c
new file mode 100644
index 00000000..40a36d3f
--- /dev/null
+++ b/ext/dv/gstsmptetimecode.c
@@ -0,0 +1,240 @@
+/* GStreamer
+ * Copyright (C) 2009 David A. Schleef <ds@schleef.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Utility functions for handing SMPTE Time Codes, as described in
+ * SMPTE Standard 12M-1999.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstsmptetimecode.h"
+
+#define NTSC_FRAMES_PER_10_MINS (10*60*30 - 10*2 + 2)
+#define NTSC_FRAMES_PER_HOUR (6*NTSC_FRAMES_PER_10_MINS)
+
+/**
+ * gst_smpte_time_code_from_frame_number:
+ * @system: SMPTE Time Code system
+ * @time_code: pointer to time code structure
+ * @frame_number: integer frame number
+ *
+ * Converts a frame number to a time code.
+ *
+ * Returns: TRUE if the conversion was successful
+ */
+gboolean
+gst_smpte_time_code_from_frame_number (GstSMPTETimeCodeSystem system,
+ GstSMPTETimeCode * time_code, int frame_number)
+{
+ int ten_mins;
+ int n;
+
+ g_return_val_if_fail (time_code != NULL, FALSE);
+ g_return_val_if_fail (GST_SMPTE_TIME_CODE_SYSTEM_IS_VALID (system), FALSE);
+
+ time_code->hours = 99;
+ time_code->minutes = 99;
+ time_code->seconds = 99;
+ time_code->frames = 99;
+
+ if (frame_number < 0)
+ return FALSE;
+
+ switch (system) {
+ case GST_SMPTE_TIME_CODE_SYSTEM_30:
+ if (frame_number >= 24 * NTSC_FRAMES_PER_HOUR)
+ return FALSE;
+
+ ten_mins = frame_number / NTSC_FRAMES_PER_10_MINS;
+ frame_number -= ten_mins * NTSC_FRAMES_PER_10_MINS;
+
+ time_code->hours = ten_mins / 6;
+ time_code->minutes = 10 * (ten_mins % 6);
+
+ if (frame_number < 2) {
+ /* treat the first two frames of each ten minutes specially */
+ time_code->seconds = 0;
+ time_code->frames = frame_number;
+ } else {
+ n = (frame_number - 2) / (60 * 30 - 2);
+ time_code->minutes += n;
+ frame_number -= n * (60 * 30 - 2);
+
+ time_code->seconds = frame_number / 30;
+ time_code->frames = frame_number % 30;
+ }
+ break;
+ case GST_SMPTE_TIME_CODE_SYSTEM_25:
+ if (frame_number >= 24 * 60 * 60 * 25)
+ return FALSE;
+
+ time_code->frames = frame_number % 25;
+ frame_number /= 25;
+ time_code->seconds = frame_number % 60;
+ frame_number /= 60;
+ time_code->minutes = frame_number % 60;
+ frame_number /= 60;
+ time_code->hours = frame_number;
+ break;
+ case GST_SMPTE_TIME_CODE_SYSTEM_24:
+ if (frame_number >= 24 * 60 * 60 * 24)
+ return FALSE;
+
+ time_code->frames = frame_number % 24;
+ frame_number /= 24;
+ time_code->seconds = frame_number % 60;
+ frame_number /= 60;
+ time_code->minutes = frame_number % 60;
+ frame_number /= 60;
+ time_code->hours = frame_number;
+ break;
+ }
+
+ return TRUE;
+}
+
+/**
+ * gst_smpte_time_code_is_valid:
+ * @system: SMPTE Time Code system
+ * @time_code: pointer to time code structure
+ *
+ * Checks that the time code represents a valid time code.
+ *
+ * Returns: TRUE if the time code is valid
+ */
+gboolean
+gst_smpte_time_code_is_valid (GstSMPTETimeCodeSystem system,
+ GstSMPTETimeCode * time_code)
+{
+ g_return_val_if_fail (time_code != NULL, FALSE);
+ g_return_val_if_fail (GST_SMPTE_TIME_CODE_SYSTEM_IS_VALID (system), FALSE);
+
+ if (time_code->hours < 0 || time_code->hours >= 24)
+ return FALSE;
+ if (time_code->minutes < 0 || time_code->minutes >= 60)
+ return FALSE;
+ if (time_code->seconds < 0 || time_code->seconds >= 60)
+ return FALSE;
+ if (time_code->frames < 0)
+ return FALSE;
+
+ switch (system) {
+ case GST_SMPTE_TIME_CODE_SYSTEM_30:
+ if (time_code->frames >= 30)
+ return FALSE;
+ if (time_code->frames >= 2 || time_code->seconds > 0)
+ return TRUE;
+ if (time_code->minutes % 10 != 0)
+ return FALSE;
+ break;
+ case GST_SMPTE_TIME_CODE_SYSTEM_25:
+ if (time_code->frames >= 25)
+ return FALSE;
+ break;
+ case GST_SMPTE_TIME_CODE_SYSTEM_24:
+ if (time_code->frames >= 24)
+ return FALSE;
+ break;
+ }
+ return TRUE;
+}
+
+/**
+ * gst_smpte_time_get_frame_number:
+ * @system: SMPTE Time Code system
+ * @frame_number: pointer to frame number
+ * @time_code: pointer to time code structure
+ *
+ * Converts the time code structure to a linear frame number.
+ *
+ * Returns: TRUE if the time code could be converted
+ */
+gboolean
+gst_smpte_time_code_get_frame_number (GstSMPTETimeCodeSystem system,
+ int *frame_number, GstSMPTETimeCode * time_code)
+{
+ int frame = 0;
+
+ g_return_val_if_fail (GST_SMPTE_TIME_CODE_SYSTEM_IS_VALID (system), FALSE);
+ g_return_val_if_fail (time_code != NULL, FALSE);
+
+ if (!gst_smpte_time_code_is_valid (system, time_code)) {
+ return FALSE;
+ }
+
+ switch (system) {
+ case GST_SMPTE_TIME_CODE_SYSTEM_30:
+ frame = time_code->hours * NTSC_FRAMES_PER_HOUR;
+ frame += (time_code->minutes / 10) * NTSC_FRAMES_PER_10_MINS;
+ frame += (time_code->minutes % 10) * (30 * 60 - 2);
+ frame += time_code->seconds * 30;
+ break;
+ case GST_SMPTE_TIME_CODE_SYSTEM_25:
+ time_code->frames =
+ 25 * ((time_code->hours * 60 + time_code->minutes) * 60 +
+ time_code->seconds);
+ break;
+ case GST_SMPTE_TIME_CODE_SYSTEM_24:
+ time_code->frames =
+ 24 * ((time_code->hours * 60 + time_code->minutes) * 60 +
+ time_code->seconds);
+ break;
+ }
+ frame += time_code->frames;
+
+ if (frame_number) {
+ *frame_number = frame;
+ }
+
+ return TRUE;
+}
+
+/**
+ * gst_smpte_time_get_timestamp:
+ * @system: SMPTE Time Code system
+ * @time_code: pointer to time code structure
+ *
+ * Converts the time code structure to a timestamp.
+ *
+ * Returns: Time stamp for time code, or GST_CLOCK_TIME_NONE if time
+ * code is invalid.
+ */
+GstClockTime
+gst_smpte_time_code_get_timestamp (GstSMPTETimeCodeSystem system,
+ GstSMPTETimeCode * time_code)
+{
+ int frame_number;
+
+ g_return_val_if_fail (GST_SMPTE_TIME_CODE_SYSTEM_IS_VALID (system),
+ GST_CLOCK_TIME_NONE);
+ g_return_val_if_fail (time_code != NULL, GST_CLOCK_TIME_NONE);
+
+ if (gst_smpte_time_code_get_frame_number (system, &frame_number, time_code)) {
+ static int framerate_n[3] = { 3000, 25, 24 };
+ static int framerate_d[3] = { 1001, 1, 1 };
+
+ return gst_util_uint64_scale (frame_number,
+ GST_SECOND * framerate_d[system], framerate_n[system]);
+ }
+
+ return GST_CLOCK_TIME_NONE;
+}
diff --git a/ext/dv/gstsmptetimecode.h b/ext/dv/gstsmptetimecode.h
new file mode 100644
index 00000000..2f08c924
--- /dev/null
+++ b/ext/dv/gstsmptetimecode.h
@@ -0,0 +1,65 @@
+/* GStreamer
+ * Copyright (C) 2009 David A. Schleef <ds@schleef.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _GST_SMPTE_TIME_CODE_H_
+#define _GST_SMPTE_TIME_CODE_H_
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstSMPTETimeCode GstSMPTETimeCode;
+
+/**
+ * GstSMPTETimeCode:
+ * @GST_SMPTE_TIME_CODE_SYSTEM_30: 29.97 frame per second system (NTSC)
+ * @GST_SMPTE_TIME_CODE_SYSTEM_25: 25 frame per second system (PAL)
+ * @GST_SMPTE_TIME_CODE_SYSTEM_24: 24 frame per second system
+ *
+ * Enum value representing SMPTE Time Code system.
+ */
+typedef enum {
+ GST_SMPTE_TIME_CODE_SYSTEM_30 = 0,
+ GST_SMPTE_TIME_CODE_SYSTEM_25,
+ GST_SMPTE_TIME_CODE_SYSTEM_24
+} GstSMPTETimeCodeSystem;
+
+struct _GstSMPTETimeCode {
+ int hours;
+ int minutes;
+ int seconds;
+ int frames;
+};
+
+#define GST_SMPTE_TIME_CODE_SYSTEM_IS_VALID(x) \
+ ((x) >= GST_SMPTE_TIME_CODE_SYSTEM_30 && (x) <= GST_SMPTE_TIME_CODE_SYSTEM_24)
+
+gboolean gst_smpte_time_code_is_valid (GstSMPTETimeCodeSystem system,
+ GstSMPTETimeCode *time_code);
+gboolean gst_smpte_time_code_from_frame_number (GstSMPTETimeCodeSystem system,
+ GstSMPTETimeCode *time_code, int frame_number);
+gboolean gst_smpte_time_code_get_frame_number (GstSMPTETimeCodeSystem system,
+ int *frame_number, GstSMPTETimeCode *time_code);
+GstClockTime gst_smpte_time_code_get_timestamp (GstSMPTETimeCodeSystem system,
+ GstSMPTETimeCode *time_code);
+
+G_END_DECLS
+
+#endif
+
diff --git a/ext/dv/smpte_test.c b/ext/dv/smpte_test.c
new file mode 100644
index 00000000..f18113cd
--- /dev/null
+++ b/ext/dv/smpte_test.c
@@ -0,0 +1,81 @@
+
+#include "config.h"
+
+#include "gstsmptetimecode.h"
+
+#include <glib.h>
+
+#define NTSC_FRAMES_PER_10_MINS (10*60*30 - 10*2 + 2)
+#define NTSC_FRAMES_PER_HOUR (6*NTSC_FRAMES_PER_10_MINS)
+
+
+int
+main (int argc, char *argv[])
+{
+ GstSMPTETimeCode tc;
+ int i;
+ int min;
+
+ for (min = 0; min < 3; min++) {
+ g_print ("--- minute %d ---\n", min);
+ for (i = min * 60 * 30 - 5; i <= min * 60 * 30 + 5; i++) {
+ gst_smpte_time_code_from_frame_number (GST_SMPTE_TIME_CODE_SYSTEM_30, &tc,
+ i);
+ g_print ("%d %02d:%02d:%02d:%02d\n", i, tc.hours, tc.minutes, tc.seconds,
+ tc.frames);
+ }
+ }
+
+ for (min = 9; min < 12; min++) {
+ g_print ("--- minute %d ---\n", min);
+ for (i = min * 60 * 30 - 5 - 18; i <= min * 60 * 30 + 5 - 18; i++) {
+ gst_smpte_time_code_from_frame_number (GST_SMPTE_TIME_CODE_SYSTEM_30, &tc,
+ i);
+ g_print ("%d %02d:%02d:%02d:%02d\n", i, tc.hours, tc.minutes, tc.seconds,
+ tc.frames);
+ }
+ }
+
+ for (min = -1; min < 2; min++) {
+ int offset = NTSC_FRAMES_PER_HOUR;
+
+ g_print ("--- minute %d ---\n", min);
+ for (i = offset + min * 60 * 30 - 5; i <= offset + min * 60 * 30 + 5; i++) {
+ gst_smpte_time_code_from_frame_number (GST_SMPTE_TIME_CODE_SYSTEM_30, &tc,
+ i);
+ g_print ("%d %02d:%02d:%02d:%02d\n", i, tc.hours, tc.minutes, tc.seconds,
+ tc.frames);
+ }
+ }
+
+ for (min = 0; min < 1; min++) {
+ int offset = NTSC_FRAMES_PER_HOUR;
+
+ g_print ("--- minute %d ---\n", min);
+ for (i = 24 * offset - 5; i <= 24 * offset + 5; i++) {
+ gst_smpte_time_code_from_frame_number (GST_SMPTE_TIME_CODE_SYSTEM_30, &tc,
+ i);
+ g_print ("%d %02d:%02d:%02d:%02d\n", i, tc.hours, tc.minutes, tc.seconds,
+ tc.frames);
+ }
+ }
+
+ for (i = 0; i < 24 * NTSC_FRAMES_PER_HOUR; i++) {
+ int fn;
+ int ret;
+
+ gst_smpte_time_code_from_frame_number (GST_SMPTE_TIME_CODE_SYSTEM_30, &tc,
+ i);
+
+ ret = gst_smpte_time_code_get_frame_number (GST_SMPTE_TIME_CODE_SYSTEM_30,
+ &fn, &tc);
+ if (!ret) {
+ g_print ("bad valid at %d\n", i);
+ }
+ if (fn != i) {
+ g_print ("index mismatch %d != %d\n", fn, i);
+ }
+ }
+
+ return 0;
+}