From 29173242bb589a1d51c05a5301aac43002d4b46c Mon Sep 17 00:00:00 2001 From: David Schleef Date: Sun, 13 Sep 2009 12:20:23 -0700 Subject: dvdemux: Add code to parse SMPTE time codes Code to convert time codes to/from timestamps and frame numbers. --- ext/dv/Makefile.am | 7 ++ ext/dv/gstsmptetimecode.c | 240 ++++++++++++++++++++++++++++++++++++++++++++++ ext/dv/gstsmptetimecode.h | 65 +++++++++++++ ext/dv/smpte_test.c | 81 ++++++++++++++++ 4 files changed, 393 insertions(+) create mode 100644 ext/dv/gstsmptetimecode.c create mode 100644 ext/dv/gstsmptetimecode.h create mode 100644 ext/dv/smpte_test.c (limited to 'ext') 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 + * + * 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 + * + * 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 + +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 + +#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; +} -- cgit