mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-18 07:47:17 +00:00
dvdemux: Add code to parse SMPTE time codes
Code to convert time codes to/from timestamps and frame numbers.
This commit is contained in:
parent
1b57f19509
commit
29173242bb
4 changed files with 393 additions and 0 deletions
|
@ -11,3 +11,10 @@ libgstdv_la_LIBTOOLFLAGS = --tag=disable-static
|
||||||
noinst_HEADERS = gstdvdemux.h gstdvdec.h
|
noinst_HEADERS = gstdvdemux.h gstdvdec.h
|
||||||
|
|
||||||
EXTRA_DIST = NOTES
|
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)
|
||||||
|
|
||||||
|
|
240
ext/dv/gstsmptetimecode.c
Normal file
240
ext/dv/gstsmptetimecode.c
Normal file
|
@ -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;
|
||||||
|
}
|
65
ext/dv/gstsmptetimecode.h
Normal file
65
ext/dv/gstsmptetimecode.h
Normal file
|
@ -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
|
||||||
|
|
81
ext/dv/smpte_test.c
Normal file
81
ext/dv/smpte_test.c
Normal file
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in a new issue