mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +00:00
videotimecode: Added support for SMPTE time code metadata
Can be attached as GstMeta into a video frame. https://bugzilla.gnome.org/show_bug.cgi?id=766419
This commit is contained in:
parent
f66e6839c5
commit
ab35d7394e
6 changed files with 1179 additions and 2 deletions
|
@ -47,7 +47,8 @@ libgstvideo_@GST_API_VERSION@_la_SOURCES = \
|
||||||
video-resampler.c \
|
video-resampler.c \
|
||||||
video-blend.c \
|
video-blend.c \
|
||||||
video-overlay-composition.c \
|
video-overlay-composition.c \
|
||||||
video-multiview.c
|
video-multiview.c \
|
||||||
|
gstvideotimecode.c
|
||||||
|
|
||||||
nodist_libgstvideo_@GST_API_VERSION@_la_SOURCES = $(BUILT_SOURCES)
|
nodist_libgstvideo_@GST_API_VERSION@_la_SOURCES = $(BUILT_SOURCES)
|
||||||
|
|
||||||
|
@ -80,7 +81,8 @@ libgstvideo_@GST_API_VERSION@include_HEADERS = \
|
||||||
video-resampler.h \
|
video-resampler.h \
|
||||||
video-blend.h \
|
video-blend.h \
|
||||||
video-overlay-composition.h \
|
video-overlay-composition.h \
|
||||||
video-multiview.h
|
video-multiview.h \
|
||||||
|
gstvideotimecode.h
|
||||||
|
|
||||||
nodist_libgstvideo_@GST_API_VERSION@include_HEADERS = $(built_headers)
|
nodist_libgstvideo_@GST_API_VERSION@include_HEADERS = $(built_headers)
|
||||||
noinst_HEADERS = gstvideoutilsprivate.h
|
noinst_HEADERS = gstvideoutilsprivate.h
|
||||||
|
|
611
gst-libs/gst/video/gstvideotimecode.c
Normal file
611
gst-libs/gst/video/gstvideotimecode.c
Normal file
|
@ -0,0 +1,611 @@
|
||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) <2016> Vivia Nikolaidou <vivia@toolsonair.com>
|
||||||
|
*
|
||||||
|
* 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., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gstvideotimecode.h"
|
||||||
|
|
||||||
|
G_DEFINE_BOXED_TYPE (GstVideoTimeCode, gst_video_time_code,
|
||||||
|
(GBoxedCopyFunc) gst_video_time_code_copy,
|
||||||
|
(GBoxedFreeFunc) gst_video_time_code_free);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_video_time_code_is_valid:
|
||||||
|
* @tc: #GstVideoTimeCode to check
|
||||||
|
*
|
||||||
|
* Returns: whether @tc is a valid timecode (supported frame rate,
|
||||||
|
* hours/minutes/seconds/frames not overflowing)
|
||||||
|
*
|
||||||
|
* Since: 1.10
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
gst_video_time_code_is_valid (const GstVideoTimeCode * tc)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (tc != NULL, FALSE);
|
||||||
|
|
||||||
|
if (tc->hours > 24)
|
||||||
|
return FALSE;
|
||||||
|
if (tc->minutes >= 60)
|
||||||
|
return FALSE;
|
||||||
|
if (tc->seconds >= 60)
|
||||||
|
return FALSE;
|
||||||
|
if (tc->config.fps_d == 0)
|
||||||
|
return FALSE;
|
||||||
|
if ((tc->frames > tc->config.fps_n / tc->config.fps_d)
|
||||||
|
&& (tc->config.fps_n != 0 || tc->config.fps_d != 1))
|
||||||
|
return FALSE;
|
||||||
|
if (tc->config.fps_d == 1001) {
|
||||||
|
if (tc->config.fps_n != 30000 && tc->config.fps_n != 60000)
|
||||||
|
return FALSE;
|
||||||
|
} else if (tc->config.fps_n % tc->config.fps_d != 0) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_video_time_code_to_string:
|
||||||
|
* @tc: #GstVideoTimeCode to convert
|
||||||
|
*
|
||||||
|
* Returns: the SMPTE ST 2059-1:2015 string representation of @tc. That will
|
||||||
|
* take the form hh:mm:ss:ff . The last separator (between seconds and frames)
|
||||||
|
* may vary:
|
||||||
|
*
|
||||||
|
* ';' for drop-frame, non-interlaced content and for drop-frame interlaced
|
||||||
|
* field 2
|
||||||
|
* ',' for drop-frame interlaced field 1
|
||||||
|
* ':' for non-drop-frame, non-interlaced content and for non-drop-frame
|
||||||
|
* interlaced field 2
|
||||||
|
* '.' for non-drop-frame interlaced field 1
|
||||||
|
*
|
||||||
|
* Since: 1.10
|
||||||
|
*/
|
||||||
|
gchar *
|
||||||
|
gst_video_time_code_to_string (const GstVideoTimeCode * tc)
|
||||||
|
{
|
||||||
|
gchar *ret;
|
||||||
|
gboolean top_dot_present;
|
||||||
|
gchar sep;
|
||||||
|
|
||||||
|
g_return_val_if_fail (gst_video_time_code_is_valid (tc), NULL);
|
||||||
|
|
||||||
|
/* Top dot is present for non-interlaced content, and for field 2 in
|
||||||
|
* interlaced content */
|
||||||
|
top_dot_present =
|
||||||
|
!((tc->config.flags & GST_VIDEO_TIME_CODE_FLAGS_INTERLACED) == 1
|
||||||
|
&& tc->field_count == 1);
|
||||||
|
|
||||||
|
if (tc->config.flags & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME)
|
||||||
|
sep = top_dot_present ? ';' : ',';
|
||||||
|
else
|
||||||
|
sep = top_dot_present ? ':' : '.';
|
||||||
|
|
||||||
|
ret =
|
||||||
|
g_strdup_printf ("%02d:%02d:%02d%c%02d", tc->hours, tc->minutes,
|
||||||
|
tc->seconds, sep, tc->frames);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_video_time_code_to_date_time:
|
||||||
|
* @tc: #GstVideoTimeCode to convert
|
||||||
|
*
|
||||||
|
* The @tc.config->latest_daily_jam is required to be non-NULL.
|
||||||
|
*
|
||||||
|
* Returns: the #GDateTime representation of @tc.
|
||||||
|
*
|
||||||
|
* Since: 1.10
|
||||||
|
*/
|
||||||
|
GDateTime *
|
||||||
|
gst_video_time_code_to_date_time (const GstVideoTimeCode * tc)
|
||||||
|
{
|
||||||
|
GDateTime *ret;
|
||||||
|
GDateTime *ret2;
|
||||||
|
gdouble add_us;
|
||||||
|
|
||||||
|
g_return_val_if_fail (gst_video_time_code_is_valid (tc), NULL);
|
||||||
|
g_return_val_if_fail (tc->config.latest_daily_jam != NULL, NULL);
|
||||||
|
|
||||||
|
ret = g_date_time_ref (tc->config.latest_daily_jam);
|
||||||
|
|
||||||
|
if (ret == NULL) {
|
||||||
|
gchar *tc_str = gst_video_time_code_to_string (tc);
|
||||||
|
GST_WARNING
|
||||||
|
("Asked to convert time code %s to GDateTime, but its latest daily jam is NULL",
|
||||||
|
tc_str);
|
||||||
|
g_free (tc_str);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tc->config.fps_n == 0 && tc->config.fps_d == 1) {
|
||||||
|
gchar *tc_str = gst_video_time_code_to_string (tc);
|
||||||
|
GST_WARNING
|
||||||
|
("Asked to convert time code %s to GDateTime, but its framerate is unknown",
|
||||||
|
tc_str);
|
||||||
|
g_free (tc_str);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_util_fraction_to_double (tc->frames * tc->config.fps_d, tc->config.fps_n,
|
||||||
|
&add_us);
|
||||||
|
if ((tc->config.flags & GST_VIDEO_TIME_CODE_FLAGS_INTERLACED)
|
||||||
|
&& tc->field_count == 1) {
|
||||||
|
gdouble sub_us;
|
||||||
|
|
||||||
|
gst_util_fraction_to_double (tc->config.fps_d, 2 * tc->config.fps_n,
|
||||||
|
&sub_us);
|
||||||
|
add_us -= sub_us;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret2 = g_date_time_add_seconds (ret, add_us + tc->seconds);
|
||||||
|
g_date_time_unref (ret);
|
||||||
|
ret = g_date_time_add_minutes (ret2, tc->minutes);
|
||||||
|
g_date_time_unref (ret2);
|
||||||
|
ret2 = g_date_time_add_hours (ret, tc->hours);
|
||||||
|
g_date_time_unref (ret);
|
||||||
|
|
||||||
|
return ret2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_video_time_code_nsec_since_daily_jam:
|
||||||
|
* @tc: a #GstVideoTimeCode
|
||||||
|
*
|
||||||
|
* Returns: how many nsec have passed since the daily jam of @tc .
|
||||||
|
*
|
||||||
|
* Since: 1.10
|
||||||
|
*/
|
||||||
|
guint64
|
||||||
|
gst_video_time_code_nsec_since_daily_jam (const GstVideoTimeCode * tc)
|
||||||
|
{
|
||||||
|
gdouble nsec;
|
||||||
|
|
||||||
|
g_return_val_if_fail (gst_video_time_code_is_valid (tc), -1);
|
||||||
|
|
||||||
|
if (tc->config.fps_n == 0 && tc->config.fps_d == 1) {
|
||||||
|
gchar *tc_str = gst_video_time_code_to_string (tc);
|
||||||
|
GST_WARNING
|
||||||
|
("Asked to calculate nsec since daily jam of time code %s, but its framerate is unknown",
|
||||||
|
tc_str);
|
||||||
|
g_free (tc_str);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((tc->config.flags & GST_VIDEO_TIME_CODE_FLAGS_INTERLACED)
|
||||||
|
&& tc->field_count == 1)
|
||||||
|
nsec =
|
||||||
|
gst_util_uint64_scale (GST_SECOND * tc->frames - 500 * GST_MSECOND,
|
||||||
|
tc->config.fps_d, tc->config.fps_n);
|
||||||
|
else
|
||||||
|
nsec =
|
||||||
|
gst_util_uint64_scale (GST_SECOND * tc->frames, tc->config.fps_d,
|
||||||
|
tc->config.fps_n);
|
||||||
|
|
||||||
|
/* hours <= 24 (daily jam required)
|
||||||
|
* minutes < 60
|
||||||
|
* seconds < 60
|
||||||
|
* this can't overflow */
|
||||||
|
nsec += GST_SECOND * (tc->seconds + (60 * (tc->minutes + 60 * tc->hours)));
|
||||||
|
|
||||||
|
return nsec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_video_time_code_frames_since_daily_jam:
|
||||||
|
* @tc: a #GstVideoTimeCode
|
||||||
|
*
|
||||||
|
* Returns: how many frames have passed since the daily jam of @tc .
|
||||||
|
*
|
||||||
|
* Since: 1.10
|
||||||
|
*/
|
||||||
|
guint64
|
||||||
|
gst_video_time_code_frames_since_daily_jam (const GstVideoTimeCode * tc)
|
||||||
|
{
|
||||||
|
guint ff_nom;
|
||||||
|
gdouble ff;
|
||||||
|
|
||||||
|
g_return_val_if_fail (gst_video_time_code_is_valid (tc), -1);
|
||||||
|
|
||||||
|
gst_util_fraction_to_double (tc->config.fps_n, tc->config.fps_d, &ff);
|
||||||
|
if (tc->config.fps_d == 1001) {
|
||||||
|
ff_nom = tc->config.fps_n / 1000;
|
||||||
|
} else {
|
||||||
|
ff_nom = ff;
|
||||||
|
}
|
||||||
|
if (tc->config.flags & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME) {
|
||||||
|
/* these need to be truncated to integer: side effect, code looks cleaner
|
||||||
|
* */
|
||||||
|
guint ff_minutes = 60 * ff;
|
||||||
|
guint ff_hours = 3600 * ff;
|
||||||
|
/* for 30000/1001 we drop the first 2 frames per minute, for 60000/1001 we
|
||||||
|
* drop the first 4 : so we use this number */
|
||||||
|
guint dropframe_multiplier;
|
||||||
|
|
||||||
|
if (tc->config.fps_n == 30000) {
|
||||||
|
dropframe_multiplier = 2;
|
||||||
|
} else if (tc->config.fps_n == 60000) {
|
||||||
|
dropframe_multiplier = 4;
|
||||||
|
} else {
|
||||||
|
GST_ERROR ("Unsupported drop frame rate %u/%u", tc->config.fps_n,
|
||||||
|
tc->config.fps_d);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tc->frames + (ff_nom * tc->seconds) +
|
||||||
|
(ff_minutes * tc->minutes) +
|
||||||
|
dropframe_multiplier * ((gint) (tc->minutes / 10)) +
|
||||||
|
(ff_hours * tc->hours);
|
||||||
|
} else {
|
||||||
|
return tc->frames + (ff_nom * (tc->seconds + (60 * (tc->minutes +
|
||||||
|
(60 * tc->hours)))));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_video_time_code_increment_frame:
|
||||||
|
* @tc: a #GstVideoTimeCode
|
||||||
|
*
|
||||||
|
* Adds one frame to @tc .
|
||||||
|
*
|
||||||
|
* Since: 1.10
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
gst_video_time_code_increment_frame (GstVideoTimeCode * tc)
|
||||||
|
{
|
||||||
|
return gst_video_time_code_add_frames (tc, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_video_time_code_add_frames:
|
||||||
|
* @tc: a #GstVideoTimeCode
|
||||||
|
* @frames: How many frames to add or subtract
|
||||||
|
*
|
||||||
|
* Adds or subtracts @frames amount of frames to @tc .
|
||||||
|
*
|
||||||
|
* Since: 1.10
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
gst_video_time_code_add_frames (GstVideoTimeCode * tc, gint64 frames)
|
||||||
|
{
|
||||||
|
guint64 framecount;
|
||||||
|
guint64 h_notmod24;
|
||||||
|
guint64 h_new, min_new, sec_new, frames_new;
|
||||||
|
gdouble ff;
|
||||||
|
guint ff_nom;
|
||||||
|
/* formulas found in SMPTE ST 2059-1:2015 section 9.4.3
|
||||||
|
* and adapted for 60/1.001 as well as 30/1.001 */
|
||||||
|
|
||||||
|
g_return_if_fail (gst_video_time_code_is_valid (tc));
|
||||||
|
|
||||||
|
gst_util_fraction_to_double (tc->config.fps_n, tc->config.fps_d, &ff);
|
||||||
|
if (tc->config.fps_d == 1001) {
|
||||||
|
ff_nom = tc->config.fps_n / 1000;
|
||||||
|
} else {
|
||||||
|
ff_nom = ff;
|
||||||
|
if (tc->config.fps_d != 1)
|
||||||
|
GST_WARNING ("Unsupported frame rate %u/%u, results may be wrong",
|
||||||
|
tc->config.fps_n, tc->config.fps_d);
|
||||||
|
}
|
||||||
|
if (tc->config.flags & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME) {
|
||||||
|
/* these need to be truncated to integer: side effect, code looks cleaner
|
||||||
|
* */
|
||||||
|
guint ff_minutes = 60 * ff;
|
||||||
|
guint ff_hours = 3600 * ff;
|
||||||
|
/* a bunch of intermediate variables, to avoid monster code with possible
|
||||||
|
* integer overflows */
|
||||||
|
guint64 min_new_tmp1, min_new_tmp2, min_new_tmp3, min_new_denom;
|
||||||
|
/* for 30000/1001 we drop the first 2 frames per minute, for 60000/1001 we
|
||||||
|
* drop the first 4 : so we use this number */
|
||||||
|
guint dropframe_multiplier;
|
||||||
|
|
||||||
|
if (tc->config.fps_n == 30000)
|
||||||
|
dropframe_multiplier = 2;
|
||||||
|
else if (tc->config.fps_n == 60000)
|
||||||
|
dropframe_multiplier = 4;
|
||||||
|
else {
|
||||||
|
GST_ERROR ("Unsupported drop frame rate %u/%u", tc->config.fps_n,
|
||||||
|
tc->config.fps_d);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
framecount =
|
||||||
|
frames + tc->frames + (ff_nom * tc->seconds) +
|
||||||
|
(ff_minutes * tc->minutes) +
|
||||||
|
dropframe_multiplier * ((gint) (tc->minutes / 10)) +
|
||||||
|
(ff_hours * tc->hours);
|
||||||
|
h_notmod24 = gst_util_uint64_scale_int (framecount, 1, ff_hours);
|
||||||
|
|
||||||
|
min_new_denom = 60 * ff_nom;
|
||||||
|
min_new_tmp1 = (framecount - (h_notmod24 * ff_hours)) / min_new_denom;
|
||||||
|
min_new_tmp2 = framecount + dropframe_multiplier * min_new_tmp1;
|
||||||
|
min_new_tmp1 = (framecount - (h_notmod24 * ff_hours)) / (60 * 10 * ff_nom);
|
||||||
|
min_new_tmp3 =
|
||||||
|
dropframe_multiplier * min_new_tmp1 + (h_notmod24 * ff_hours);
|
||||||
|
min_new =
|
||||||
|
gst_util_uint64_scale_int (min_new_tmp2 - min_new_tmp3, 1,
|
||||||
|
min_new_denom);
|
||||||
|
|
||||||
|
sec_new =
|
||||||
|
(guint64) ((framecount - (ff_minutes * min_new) -
|
||||||
|
dropframe_multiplier * ((gint) (min_new / 10)) -
|
||||||
|
(ff_hours * h_notmod24)) / ff_nom);
|
||||||
|
|
||||||
|
frames_new =
|
||||||
|
framecount - (ff_nom * sec_new) - (ff_minutes * min_new) -
|
||||||
|
(dropframe_multiplier * ((gint) (min_new / 10))) -
|
||||||
|
(ff_hours * h_notmod24);
|
||||||
|
} else {
|
||||||
|
framecount =
|
||||||
|
frames + tc->frames + (ff_nom * (tc->seconds + (60 * (tc->minutes +
|
||||||
|
(60 * tc->hours)))));
|
||||||
|
h_notmod24 = gst_util_uint64_scale_int (framecount, 1, ff_nom * 3600);
|
||||||
|
min_new =
|
||||||
|
gst_util_uint64_scale_int ((framecount - (ff_nom * 3600 * h_notmod24)),
|
||||||
|
1, (ff_nom * 60));
|
||||||
|
sec_new =
|
||||||
|
gst_util_uint64_scale_int ((framecount - (ff_nom * 60 * (min_new +
|
||||||
|
(60 * h_notmod24)))), 1, ff_nom);
|
||||||
|
frames_new =
|
||||||
|
framecount - (ff_nom * (sec_new + 60 * (min_new + (60 * h_notmod24))));
|
||||||
|
if (frames_new > ff_nom)
|
||||||
|
frames_new = 0;
|
||||||
|
}
|
||||||
|
h_new = h_notmod24 % 24;
|
||||||
|
|
||||||
|
g_assert (min_new < 60);
|
||||||
|
g_assert (sec_new < 60);
|
||||||
|
g_assert (frames_new < ff_nom);
|
||||||
|
tc->hours = h_new;
|
||||||
|
tc->minutes = min_new;
|
||||||
|
tc->seconds = sec_new;
|
||||||
|
tc->frames = frames_new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_video_time_code_compare:
|
||||||
|
* @tc1: a #GstVideoTimeCode
|
||||||
|
* @tc2: another #GstVideoTimeCode
|
||||||
|
*
|
||||||
|
* Compares @tc1 and @tc2 . If both have latest daily jam information, it is
|
||||||
|
* taken into account. Otherwise, it is assumed that the daily jam of both
|
||||||
|
* @tc1 and @tc2 was at the same time.
|
||||||
|
*
|
||||||
|
* Returns: 1 if @tc1 is after @tc2, -1 if @tc1 is before @tc2, 0 otherwise.
|
||||||
|
*
|
||||||
|
* Since: 1.10
|
||||||
|
*/
|
||||||
|
gint
|
||||||
|
gst_video_time_code_compare (const GstVideoTimeCode * tc1,
|
||||||
|
const GstVideoTimeCode * tc2)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (gst_video_time_code_is_valid (tc1), -1);
|
||||||
|
g_return_val_if_fail (gst_video_time_code_is_valid (tc2), -1);
|
||||||
|
|
||||||
|
if (tc1->config.latest_daily_jam == NULL
|
||||||
|
|| tc2->config.latest_daily_jam == NULL) {
|
||||||
|
guint64 nsec1, nsec2;
|
||||||
|
#ifndef GST_DISABLE_GST_DEBUG
|
||||||
|
gchar *str1, *str2;
|
||||||
|
|
||||||
|
str1 = gst_video_time_code_to_string (tc1);
|
||||||
|
str2 = gst_video_time_code_to_string (tc2);
|
||||||
|
GST_INFO
|
||||||
|
("Comparing time codes %s and %s, but at least one of them has no "
|
||||||
|
"latest daily jam information. Assuming they started together",
|
||||||
|
str1, str2);
|
||||||
|
g_free (str1);
|
||||||
|
g_free (str2);
|
||||||
|
#endif
|
||||||
|
if (tc1->hours > tc2->hours) {
|
||||||
|
return 1;
|
||||||
|
} else if (tc1->hours < tc2->hours) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (tc1->minutes > tc2->minutes) {
|
||||||
|
return 1;
|
||||||
|
} else if (tc1->minutes < tc2->minutes) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (tc1->seconds > tc2->seconds) {
|
||||||
|
return 1;
|
||||||
|
} else if (tc1->seconds < tc2->seconds) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsec1 =
|
||||||
|
gst_util_uint64_scale (GST_SECOND,
|
||||||
|
tc1->frames * tc1->config.fps_n, tc1->config.fps_d);
|
||||||
|
nsec2 =
|
||||||
|
gst_util_uint64_scale (GST_SECOND,
|
||||||
|
tc2->frames * tc2->config.fps_n, tc2->config.fps_d);
|
||||||
|
if (nsec1 > nsec2) {
|
||||||
|
return 1;
|
||||||
|
} else if (nsec1 < nsec2) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (tc1->config.flags & GST_VIDEO_TIME_CODE_FLAGS_INTERLACED) {
|
||||||
|
if (tc1->field_count > tc2->field_count)
|
||||||
|
return 1;
|
||||||
|
else if (tc1->field_count < tc2->field_count)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
GDateTime *dt1, *dt2;
|
||||||
|
gint ret;
|
||||||
|
|
||||||
|
dt1 = gst_video_time_code_to_date_time (tc1);
|
||||||
|
dt2 = gst_video_time_code_to_date_time (tc2);
|
||||||
|
|
||||||
|
ret = g_date_time_compare (dt1, dt2);
|
||||||
|
|
||||||
|
g_date_time_unref (dt1);
|
||||||
|
g_date_time_unref (dt2);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_video_time_code_new:
|
||||||
|
* @fps_n: Numerator of the frame rate
|
||||||
|
* @fps_d: Denominator of the frame rate
|
||||||
|
* @latest_daily_jam: The latest daily jam of the #GstVideoTimeCode
|
||||||
|
* @flags: #GstVideoTimeCodeFlags
|
||||||
|
* @hours: the hours field of #GstVideoTimeCode
|
||||||
|
* @minutes: the minutes field of #GstVideoTimeCode
|
||||||
|
* @seconds: the seconds field of #GstVideoTimeCode
|
||||||
|
* @frames: the frames field of #GstVideoTimeCode
|
||||||
|
* @field_count: Interlaced video field count
|
||||||
|
*
|
||||||
|
* @field_count is 0 for progressive, 1 or 2 for interlaced.
|
||||||
|
* @latest_daiy_jam reference is stolen from caller.
|
||||||
|
*
|
||||||
|
* Returns: a new #GstVideoTimeCode with the given values.
|
||||||
|
*
|
||||||
|
* Since: 1.10
|
||||||
|
*/
|
||||||
|
GstVideoTimeCode *
|
||||||
|
gst_video_time_code_new (guint fps_n, guint fps_d, GDateTime * latest_daily_jam,
|
||||||
|
GstVideoTimeCodeFlags flags, guint hours, guint minutes, guint seconds,
|
||||||
|
guint frames, guint field_count)
|
||||||
|
{
|
||||||
|
GstVideoTimeCode *tc;
|
||||||
|
|
||||||
|
tc = g_new0 (GstVideoTimeCode, 1);
|
||||||
|
gst_video_time_code_init (tc, fps_n, fps_d, latest_daily_jam, flags, hours,
|
||||||
|
minutes, seconds, frames, field_count);
|
||||||
|
return tc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_video_time_code_new_empty:
|
||||||
|
*
|
||||||
|
* Returns: a new empty #GstVideoTimeCode
|
||||||
|
*
|
||||||
|
* Since: 1.10
|
||||||
|
*/
|
||||||
|
GstVideoTimeCode *
|
||||||
|
gst_video_time_code_new_empty (void)
|
||||||
|
{
|
||||||
|
GstVideoTimeCode *tc;
|
||||||
|
|
||||||
|
tc = g_new0 (GstVideoTimeCode, 1);
|
||||||
|
gst_video_time_code_clear (tc);
|
||||||
|
return tc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_video_time_code_init:
|
||||||
|
* @tc: a #GstVideoTimeCode
|
||||||
|
* @fps_n: Numerator of the frame rate
|
||||||
|
* @fps_d: Denominator of the frame rate
|
||||||
|
* @latest_daily_jam: The latest daily jam of the #GstVideoTimeCode
|
||||||
|
* @flags: #GstVideoTimeCodeFlags
|
||||||
|
* @hours: the hours field of #GstVideoTimeCode
|
||||||
|
* @minutes: the minutes field of #GstVideoTimeCode
|
||||||
|
* @seconds: the seconds field of #GstVideoTimeCode
|
||||||
|
* @frames: the frames field of #GstVideoTimeCode
|
||||||
|
* @field_count: Interlaced video field count
|
||||||
|
*
|
||||||
|
* @field_count is 0 for progressive, 1 or 2 for interlaced.
|
||||||
|
* @latest_daiy_jam reference is stolen from caller.
|
||||||
|
*
|
||||||
|
* Initializes @tc with the given values.
|
||||||
|
*
|
||||||
|
* Since: 1.10
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
gst_video_time_code_init (GstVideoTimeCode * tc, guint fps_n, guint fps_d,
|
||||||
|
GDateTime * latest_daily_jam, GstVideoTimeCodeFlags flags, guint hours,
|
||||||
|
guint minutes, guint seconds, guint frames, guint field_count)
|
||||||
|
{
|
||||||
|
tc->hours = hours;
|
||||||
|
tc->minutes = minutes;
|
||||||
|
tc->seconds = seconds;
|
||||||
|
tc->frames = frames;
|
||||||
|
tc->field_count = field_count;
|
||||||
|
tc->config.fps_n = fps_n;
|
||||||
|
tc->config.fps_d = fps_d;
|
||||||
|
if (latest_daily_jam != NULL)
|
||||||
|
tc->config.latest_daily_jam = g_date_time_ref (latest_daily_jam);
|
||||||
|
else
|
||||||
|
tc->config.latest_daily_jam = NULL;
|
||||||
|
tc->config.flags = flags;
|
||||||
|
|
||||||
|
g_return_if_fail (gst_video_time_code_is_valid (tc));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_video_time_code_clear:
|
||||||
|
* @tc: a #GstVideoTimeCode
|
||||||
|
*
|
||||||
|
* Initializes @tc with empty/zero/NULL values.
|
||||||
|
*
|
||||||
|
* Since: 1.10
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
gst_video_time_code_clear (GstVideoTimeCode * tc)
|
||||||
|
{
|
||||||
|
tc->hours = 0;
|
||||||
|
tc->minutes = 0;
|
||||||
|
tc->seconds = 0;
|
||||||
|
tc->frames = 0;
|
||||||
|
tc->field_count = 0;
|
||||||
|
tc->config.fps_n = 0;
|
||||||
|
tc->config.fps_d = 1;
|
||||||
|
if (tc->config.latest_daily_jam != NULL)
|
||||||
|
g_date_time_unref (tc->config.latest_daily_jam);
|
||||||
|
tc->config.latest_daily_jam = NULL;
|
||||||
|
tc->config.flags = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_video_time_code_copy:
|
||||||
|
* @tc: a #GstVideoTimeCode
|
||||||
|
*
|
||||||
|
* Returns: a new #GstVideoTimeCode with the same values as @tc .
|
||||||
|
*
|
||||||
|
* Since: 1.10
|
||||||
|
*/
|
||||||
|
GstVideoTimeCode *
|
||||||
|
gst_video_time_code_copy (const GstVideoTimeCode * tc)
|
||||||
|
{
|
||||||
|
return gst_video_time_code_new (tc->config.fps_n, tc->config.fps_d,
|
||||||
|
tc->config.latest_daily_jam, tc->config.flags, tc->hours, tc->minutes,
|
||||||
|
tc->seconds, tc->frames, tc->field_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_video_time_code_free:
|
||||||
|
* @tc: a #GstVideoTimeCode
|
||||||
|
*
|
||||||
|
* Frees @tc .
|
||||||
|
*
|
||||||
|
* Since: 1.10
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
gst_video_time_code_free (GstVideoTimeCode * tc)
|
||||||
|
{
|
||||||
|
if (tc->config.latest_daily_jam != NULL)
|
||||||
|
g_date_time_unref (tc->config.latest_daily_jam);
|
||||||
|
|
||||||
|
g_free (tc);
|
||||||
|
}
|
151
gst-libs/gst/video/gstvideotimecode.h
Normal file
151
gst-libs/gst/video/gstvideotimecode.h
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) <2016> Vivia Nikolaidou <vivia@toolsonair.com>
|
||||||
|
*
|
||||||
|
* 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., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __GST_VIDEO_TIME_CODE_H__
|
||||||
|
#define __GST_VIDEO_TIME_CODE_H__
|
||||||
|
|
||||||
|
#include <gst/gst.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
typedef struct _GstVideoTimeCodeConfig GstVideoTimeCodeConfig;
|
||||||
|
typedef struct _GstVideoTimeCode GstVideoTimeCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GstVideoTimeCodeFlags:
|
||||||
|
* @GST_VIDEO_TIME_CODE_FLAGS_NONE: No flags
|
||||||
|
* @GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME: Whether we have drop frame rate
|
||||||
|
* @GST_VIDEO_TIME_CODE_FLAGS_INTERLACED: Whether we have interlaced video
|
||||||
|
*
|
||||||
|
* Flags related to the time code information.
|
||||||
|
* For drop frame, only 30000/1001 and 60000/1001 frame rates are supported.
|
||||||
|
*
|
||||||
|
* Since: 1.10
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
GST_VIDEO_TIME_CODE_FLAGS_NONE = 0,
|
||||||
|
GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME = (1<<0),
|
||||||
|
GST_VIDEO_TIME_CODE_FLAGS_INTERLACED = (1<<1)
|
||||||
|
/* Not supported yet:
|
||||||
|
* GST_VIDEO_TIME_CODE_ALLOW_MORE_THAN_24H = (1<<2)
|
||||||
|
* GST_VIDEO_TIME_CODE_ALLOW_NEGATIVE = (1<<3)
|
||||||
|
*/
|
||||||
|
} GstVideoTimeCodeFlags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GstVideoTimeCodeConfig:
|
||||||
|
* @fps_n: Numerator of the frame rate
|
||||||
|
* @fps_d: Denominator of the frame rate
|
||||||
|
* @flags: the corresponding #GstVideoTimeCodeFlags
|
||||||
|
* @latest_daily_jam: The latest daily jam information, if present, or NULL
|
||||||
|
*
|
||||||
|
* Supported frame rates: 30000/1001, 60000/1001 (both with and without drop
|
||||||
|
* frame), and integer frame rates e.g. 25/1, 30/1, 50/1, 60/1.
|
||||||
|
*
|
||||||
|
* The configuration of the time code.
|
||||||
|
*
|
||||||
|
* Since: 1.10
|
||||||
|
*/
|
||||||
|
struct _GstVideoTimeCodeConfig {
|
||||||
|
guint fps_n;
|
||||||
|
guint fps_d;
|
||||||
|
GstVideoTimeCodeFlags flags;
|
||||||
|
GDateTime *latest_daily_jam;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GstVideoTimeCode:
|
||||||
|
* @hours: the hours field of #GstVideoTimeCode
|
||||||
|
* @minutes: the minutes field of #GstVideoTimeCode
|
||||||
|
* @seconds: the seconds field of #GstVideoTimeCode
|
||||||
|
* @frames: the frames field of #GstVideoTimeCode
|
||||||
|
* @field_count: Interlaced video field count
|
||||||
|
* @config: the corresponding #GstVideoTimeCodeConfig
|
||||||
|
*
|
||||||
|
* @field_count must be 0 for progressive video and 1 or 2 for interlaced.
|
||||||
|
*
|
||||||
|
* A representation of a SMPTE time code.
|
||||||
|
*
|
||||||
|
* @hours must be positive and less than 24. Will wrap around otherwise.
|
||||||
|
* @minutes and @seconds must be positive and less than 60.
|
||||||
|
* @frames must be less than or equal to @config.fps_n / @config.fps_d
|
||||||
|
* These values are *NOT* automatically normalized.
|
||||||
|
*
|
||||||
|
* Since: 1.10
|
||||||
|
*/
|
||||||
|
struct _GstVideoTimeCode {
|
||||||
|
GstVideoTimeCodeConfig config;
|
||||||
|
|
||||||
|
guint hours;
|
||||||
|
guint minutes;
|
||||||
|
guint seconds;
|
||||||
|
guint frames;
|
||||||
|
guint field_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define GST_TYPE_VIDEO_TIME_CODE (gst_video_time_code_get_type())
|
||||||
|
GType gst_video_time_code_get_type (void);
|
||||||
|
|
||||||
|
GstVideoTimeCode * gst_video_time_code_new (guint fps_n,
|
||||||
|
guint fps_d,
|
||||||
|
GDateTime * latest_daily_jam,
|
||||||
|
GstVideoTimeCodeFlags flags,
|
||||||
|
guint hours,
|
||||||
|
guint minutes,
|
||||||
|
guint seconds,
|
||||||
|
guint frames,
|
||||||
|
guint field_count);
|
||||||
|
GstVideoTimeCode * gst_video_time_code_new_empty (void);
|
||||||
|
void gst_video_time_code_free (GstVideoTimeCode * tc);
|
||||||
|
|
||||||
|
GstVideoTimeCode * gst_video_time_code_copy (const GstVideoTimeCode * tc);
|
||||||
|
|
||||||
|
void gst_video_time_code_init (GstVideoTimeCode * tc,
|
||||||
|
guint fps_n,
|
||||||
|
guint fps_d,
|
||||||
|
GDateTime * latest_daily_jam,
|
||||||
|
GstVideoTimeCodeFlags flags,
|
||||||
|
guint hours,
|
||||||
|
guint minutes,
|
||||||
|
guint seconds,
|
||||||
|
guint frames,
|
||||||
|
guint field_count);
|
||||||
|
void gst_video_time_code_clear (GstVideoTimeCode * tc);
|
||||||
|
|
||||||
|
gboolean gst_video_time_code_is_valid (const GstVideoTimeCode * tc);
|
||||||
|
|
||||||
|
gint gst_video_time_code_compare (const GstVideoTimeCode * tc1,
|
||||||
|
const GstVideoTimeCode * tc2);
|
||||||
|
|
||||||
|
void gst_video_time_code_increment_frame (GstVideoTimeCode * tc);
|
||||||
|
void gst_video_time_code_add_frames (GstVideoTimeCode * tc,
|
||||||
|
gint64 frames);
|
||||||
|
|
||||||
|
gchar *gst_video_time_code_to_string (const GstVideoTimeCode * tc);
|
||||||
|
|
||||||
|
GDateTime *gst_video_time_code_to_date_time (const GstVideoTimeCode * tc);
|
||||||
|
|
||||||
|
guint64 gst_video_time_code_nsec_since_daily_jam (const GstVideoTimeCode * tc);
|
||||||
|
|
||||||
|
guint64 gst_video_time_code_frames_since_daily_jam (const GstVideoTimeCode * tc);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __GST_VIDEO_TIME_CODE_H__ */
|
|
@ -140,5 +140,6 @@ G_END_DECLS
|
||||||
#include <gst/video/videoorientation.h>
|
#include <gst/video/videoorientation.h>
|
||||||
#include <gst/video/video-overlay-composition.h>
|
#include <gst/video/video-overlay-composition.h>
|
||||||
#include <gst/video/videooverlay.h>
|
#include <gst/video/videooverlay.h>
|
||||||
|
#include <gst/video/gstvideotimecode.h>
|
||||||
|
|
||||||
#endif /* __GST_VIDEO_H__ */
|
#endif /* __GST_VIDEO_H__ */
|
||||||
|
|
|
@ -217,6 +217,7 @@ check_PROGRAMS = \
|
||||||
libs/video \
|
libs/video \
|
||||||
libs/videodecoder \
|
libs/videodecoder \
|
||||||
libs/videoencoder \
|
libs/videoencoder \
|
||||||
|
libs/videotimecode \
|
||||||
libs/xmpwriter \
|
libs/xmpwriter \
|
||||||
pipelines/simple-launch-lines \
|
pipelines/simple-launch-lines \
|
||||||
pipelines/basetime \
|
pipelines/basetime \
|
||||||
|
@ -621,6 +622,16 @@ libs_videoencoder_LDADD = \
|
||||||
$(GST_BASE_LIBS) \
|
$(GST_BASE_LIBS) \
|
||||||
$(LDADD)
|
$(LDADD)
|
||||||
|
|
||||||
|
libs_videotimecode_CFLAGS = \
|
||||||
|
$(GST_PLUGINS_BASE_CFLAGS) \
|
||||||
|
$(GST_BASE_CFLAGS) \
|
||||||
|
$(AM_CFLAGS)
|
||||||
|
|
||||||
|
libs_videotimecode_LDADD = \
|
||||||
|
$(top_builddir)/gst-libs/gst/video/libgstvideo-@GST_API_VERSION@.la \
|
||||||
|
$(GST_BASE_LIBS) \
|
||||||
|
$(LDADD)
|
||||||
|
|
||||||
elements_multisocketsink_CFLAGS = $(GIO_CFLAGS) $(AM_CFLAGS)
|
elements_multisocketsink_CFLAGS = $(GIO_CFLAGS) $(AM_CFLAGS)
|
||||||
elements_multisocketsink_LDADD = $(GIO_LIBS) $(LDADD)
|
elements_multisocketsink_LDADD = $(GIO_LIBS) $(LDADD)
|
||||||
|
|
||||||
|
|
401
tests/check/libs/videotimecode.c
Normal file
401
tests/check/libs/videotimecode.c
Normal file
|
@ -0,0 +1,401 @@
|
||||||
|
/* GStreamer
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Vivia Nikolaidou <vivia@toolsonair.com>
|
||||||
|
*
|
||||||
|
* 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., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#include <gst/check/gstcheck.h>
|
||||||
|
#include <gst/video/video.h>
|
||||||
|
|
||||||
|
|
||||||
|
GST_START_TEST (videotimecode_compare_equal)
|
||||||
|
{
|
||||||
|
GstVideoTimeCode *tc1, *tc2;
|
||||||
|
|
||||||
|
tc2 = gst_video_time_code_new (50, 1, NULL, 0, 10, 10, 10, 10, 0);
|
||||||
|
tc1 = gst_video_time_code_new (50, 1, NULL, 0, 10, 10, 10, 10, 0);
|
||||||
|
fail_unless (gst_video_time_code_compare (tc1, tc2) == 0);
|
||||||
|
gst_video_time_code_free (tc1);
|
||||||
|
gst_video_time_code_free (tc2);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (videotimecode_compare_fps_n)
|
||||||
|
{
|
||||||
|
GstVideoTimeCode *tc1, *tc2;
|
||||||
|
|
||||||
|
tc1 = gst_video_time_code_new (50, 1, NULL, 0, 10, 10, 10, 10, 0);
|
||||||
|
tc2 = gst_video_time_code_new (25, 1, NULL, 0, 10, 10, 10, 10, 0);
|
||||||
|
fail_unless (gst_video_time_code_compare (tc1, tc2) == 1);
|
||||||
|
fail_unless (gst_video_time_code_compare (tc2, tc1) == -1);
|
||||||
|
gst_video_time_code_free (tc1);
|
||||||
|
gst_video_time_code_free (tc2);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (videotimecode_compare_fps_d)
|
||||||
|
{
|
||||||
|
GstVideoTimeCode *tc1, *tc2;
|
||||||
|
|
||||||
|
tc1 = gst_video_time_code_new (50, 1, NULL, 0, 10, 10, 10, 10, 0);
|
||||||
|
tc2 = gst_video_time_code_new (50, 2, NULL, 0, 10, 10, 10, 10, 0);
|
||||||
|
fail_unless (gst_video_time_code_compare (tc1, tc2) == 1);
|
||||||
|
fail_unless (gst_video_time_code_compare (tc2, tc1) == -1);
|
||||||
|
gst_video_time_code_free (tc1);
|
||||||
|
gst_video_time_code_free (tc2);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (videotimecode_compare_frames)
|
||||||
|
{
|
||||||
|
GstVideoTimeCode *tc1, *tc2;
|
||||||
|
|
||||||
|
tc1 = gst_video_time_code_new (50, 1, NULL, 0, 10, 10, 10, 10, 0);
|
||||||
|
tc2 = gst_video_time_code_new (50, 1, NULL, 0, 10, 10, 10, 9, 0);
|
||||||
|
fail_unless (gst_video_time_code_compare (tc1, tc2) == 1);
|
||||||
|
fail_unless (gst_video_time_code_compare (tc2, tc1) == -1);
|
||||||
|
gst_video_time_code_free (tc1);
|
||||||
|
gst_video_time_code_free (tc2);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (videotimecode_compare_seconds)
|
||||||
|
{
|
||||||
|
GstVideoTimeCode *tc1, *tc2;
|
||||||
|
|
||||||
|
tc1 = gst_video_time_code_new (50, 1, NULL, 0, 10, 10, 10, 10, 0);
|
||||||
|
tc2 = gst_video_time_code_new (50, 1, NULL, 0, 10, 10, 9, 10, 0);
|
||||||
|
fail_unless (gst_video_time_code_compare (tc1, tc2) == 1);
|
||||||
|
fail_unless (gst_video_time_code_compare (tc2, tc1) == -1);
|
||||||
|
gst_video_time_code_free (tc1);
|
||||||
|
gst_video_time_code_free (tc2);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (videotimecode_compare_minutes)
|
||||||
|
{
|
||||||
|
GstVideoTimeCode *tc1, *tc2;
|
||||||
|
|
||||||
|
tc1 = gst_video_time_code_new (50, 1, NULL, 0, 10, 10, 10, 10, 0);
|
||||||
|
tc2 = gst_video_time_code_new (50, 1, NULL, 0, 10, 9, 10, 10, 0);
|
||||||
|
fail_unless (gst_video_time_code_compare (tc1, tc2) == 1);
|
||||||
|
fail_unless (gst_video_time_code_compare (tc2, tc1) == -1);
|
||||||
|
gst_video_time_code_free (tc1);
|
||||||
|
gst_video_time_code_free (tc2);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (videotimecode_compare_hours)
|
||||||
|
{
|
||||||
|
GstVideoTimeCode *tc1, *tc2;
|
||||||
|
|
||||||
|
tc1 = gst_video_time_code_new (50, 1, NULL, 0, 10, 10, 10, 10, 0);
|
||||||
|
tc2 = gst_video_time_code_new (50, 1, NULL, 0, 9, 10, 10, 10, 0);
|
||||||
|
fail_unless (gst_video_time_code_compare (tc1, tc2) == 1);
|
||||||
|
fail_unless (gst_video_time_code_compare (tc2, tc1) == -1);
|
||||||
|
gst_video_time_code_free (tc1);
|
||||||
|
gst_video_time_code_free (tc2);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (videotimecode_compare_fieldcounts)
|
||||||
|
{
|
||||||
|
GstVideoTimeCode *tc1, *tc2;
|
||||||
|
|
||||||
|
tc1 =
|
||||||
|
gst_video_time_code_new (50, 1, NULL,
|
||||||
|
GST_VIDEO_TIME_CODE_FLAGS_INTERLACED, 10, 10, 10, 10, 2);
|
||||||
|
tc2 =
|
||||||
|
gst_video_time_code_new (50, 1, NULL,
|
||||||
|
GST_VIDEO_TIME_CODE_FLAGS_INTERLACED, 10, 10, 10, 10, 1);
|
||||||
|
fail_unless (gst_video_time_code_compare (tc1, tc2) == 1);
|
||||||
|
fail_unless (gst_video_time_code_compare (tc2, tc1) == -1);
|
||||||
|
gst_video_time_code_free (tc1);
|
||||||
|
gst_video_time_code_free (tc2);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (videotimecode_addframe_10)
|
||||||
|
{
|
||||||
|
GstVideoTimeCode *tc1;
|
||||||
|
|
||||||
|
tc1 = gst_video_time_code_new (50, 1, NULL, 0, 10, 10, 10, 10, 0);
|
||||||
|
gst_video_time_code_increment_frame (tc1);
|
||||||
|
fail_unless (tc1->hours == 10);
|
||||||
|
fail_unless (tc1->minutes == 10);
|
||||||
|
fail_unless (tc1->seconds == 10);
|
||||||
|
fail_unless (tc1->frames == 11);
|
||||||
|
gst_video_time_code_free (tc1);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (videotimecode_addframe_0)
|
||||||
|
{
|
||||||
|
GstVideoTimeCode *tc1;
|
||||||
|
|
||||||
|
tc1 = gst_video_time_code_new (50, 1, NULL, 0, 0, 0, 0, 0, 0);
|
||||||
|
gst_video_time_code_increment_frame (tc1);
|
||||||
|
fail_unless (tc1->hours == 0);
|
||||||
|
fail_unless (tc1->minutes == 0);
|
||||||
|
fail_unless (tc1->seconds == 0);
|
||||||
|
fail_unless (tc1->frames == 1);
|
||||||
|
gst_video_time_code_free (tc1);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (videotimecode_addframe_high)
|
||||||
|
{
|
||||||
|
GstVideoTimeCode *tc1;
|
||||||
|
/* Make sure nothing overflows */
|
||||||
|
|
||||||
|
tc1 = gst_video_time_code_new (60, 1, NULL, 0, 23, 59, 59, 58, 0);
|
||||||
|
gst_video_time_code_increment_frame (tc1);
|
||||||
|
fail_unless (tc1->hours == 23);
|
||||||
|
fail_unless (tc1->minutes == 59);
|
||||||
|
fail_unless (tc1->seconds == 59);
|
||||||
|
fail_unless (tc1->frames == 59);
|
||||||
|
gst_video_time_code_free (tc1);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (videotimecode_addframe_dropframe)
|
||||||
|
{
|
||||||
|
GstVideoTimeCode *tc1;
|
||||||
|
|
||||||
|
tc1 =
|
||||||
|
gst_video_time_code_new (30000, 1001, NULL,
|
||||||
|
GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME, 10, 10, 10, 10, 0);
|
||||||
|
gst_video_time_code_increment_frame (tc1);
|
||||||
|
fail_unless (tc1->hours == 10);
|
||||||
|
fail_unless (tc1->minutes == 10);
|
||||||
|
fail_unless (tc1->seconds == 10);
|
||||||
|
fail_unless (tc1->frames == 11);
|
||||||
|
gst_video_time_code_free (tc1);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (videotimecode_addframe_framedropped)
|
||||||
|
{
|
||||||
|
GstVideoTimeCode *tc1;
|
||||||
|
|
||||||
|
tc1 =
|
||||||
|
gst_video_time_code_new (30000, 1001, NULL,
|
||||||
|
GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME, 10, 10, 59, 29, 0);
|
||||||
|
gst_video_time_code_increment_frame (tc1);
|
||||||
|
fail_unless (tc1->hours == 10);
|
||||||
|
fail_unless (tc1->minutes == 11);
|
||||||
|
fail_unless (tc1->seconds == 0);
|
||||||
|
fail_unless (tc1->frames == 2);
|
||||||
|
gst_video_time_code_free (tc1);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (videotimecode_addframe_wrapover)
|
||||||
|
{
|
||||||
|
GstVideoTimeCode *tc1;
|
||||||
|
|
||||||
|
tc1 =
|
||||||
|
gst_video_time_code_new (30000, 1001, NULL,
|
||||||
|
GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME, 23, 59, 59, 29, 0);
|
||||||
|
gst_video_time_code_increment_frame (tc1);
|
||||||
|
fail_unless (tc1->hours == 0);
|
||||||
|
fail_unless (tc1->minutes == 0);
|
||||||
|
fail_unless (tc1->seconds == 0);
|
||||||
|
fail_unless (tc1->frames == 0);
|
||||||
|
gst_video_time_code_free (tc1);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (videotimecode_addframe_60drop_dropframe)
|
||||||
|
{
|
||||||
|
GstVideoTimeCode *tc1;
|
||||||
|
|
||||||
|
tc1 =
|
||||||
|
gst_video_time_code_new (60000, 1001, NULL,
|
||||||
|
GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME, 10, 10, 10, 10, 0);
|
||||||
|
gst_video_time_code_increment_frame (tc1);
|
||||||
|
fail_unless (tc1->hours == 10);
|
||||||
|
fail_unless (tc1->minutes == 10);
|
||||||
|
fail_unless (tc1->seconds == 10);
|
||||||
|
fail_unless (tc1->frames == 11);
|
||||||
|
gst_video_time_code_free (tc1);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (videotimecode_addframe_60drop_framedropped)
|
||||||
|
{
|
||||||
|
GstVideoTimeCode *tc1;
|
||||||
|
|
||||||
|
tc1 =
|
||||||
|
gst_video_time_code_new (60000, 1001, NULL,
|
||||||
|
GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME, 10, 10, 59, 59, 0);
|
||||||
|
gst_video_time_code_increment_frame (tc1);
|
||||||
|
fail_unless (tc1->hours == 10);
|
||||||
|
fail_unless (tc1->minutes == 11);
|
||||||
|
fail_unless (tc1->seconds == 0);
|
||||||
|
fail_unless (tc1->frames == 4);
|
||||||
|
gst_video_time_code_free (tc1);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (videotimecode_addframe_60drop_wrapover)
|
||||||
|
{
|
||||||
|
GstVideoTimeCode *tc1;
|
||||||
|
/* Make sure nothing overflows here either */
|
||||||
|
|
||||||
|
tc1 =
|
||||||
|
gst_video_time_code_new (60000, 1001, NULL,
|
||||||
|
GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME, 23, 59, 59, 59, 0);
|
||||||
|
gst_video_time_code_increment_frame (tc1);
|
||||||
|
fail_unless (tc1->hours == 0);
|
||||||
|
fail_unless (tc1->minutes == 0);
|
||||||
|
fail_unless (tc1->seconds == 0);
|
||||||
|
fail_unless (tc1->frames == 0);
|
||||||
|
gst_video_time_code_free (tc1);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (videotimecode_addframe_loop)
|
||||||
|
{
|
||||||
|
GstVideoTimeCode *tc1;
|
||||||
|
guint64 i;
|
||||||
|
/* Loop for an hour and a bit, make sure no assertions explode */
|
||||||
|
|
||||||
|
tc1 =
|
||||||
|
gst_video_time_code_new (60000, 1001, NULL,
|
||||||
|
GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME, 12, 12, 12, 12, 0);
|
||||||
|
for (i = 0; i < 220000; i++)
|
||||||
|
gst_video_time_code_increment_frame (tc1);
|
||||||
|
gst_video_time_code_init (tc1, 60, 1, NULL, 0, 12, 12, 12, 12, 0);
|
||||||
|
for (i = 0; i < 220000; i++)
|
||||||
|
gst_video_time_code_increment_frame (tc1);
|
||||||
|
gst_video_time_code_free (tc1);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (videotimecode_dailyjam_todatetime)
|
||||||
|
{
|
||||||
|
GstVideoTimeCode *tc1;
|
||||||
|
GDateTime *dt1, *dt2;
|
||||||
|
|
||||||
|
dt1 = g_date_time_new_utc (2016, 7, 29, 10, 32, 50);
|
||||||
|
|
||||||
|
tc1 =
|
||||||
|
gst_video_time_code_new (50, 1, dt1,
|
||||||
|
GST_VIDEO_TIME_CODE_FLAGS_NONE, 0, 0, 0, 0, 0);
|
||||||
|
/* 1 hour, 4 minutes, 3 seconds, and 2 frames */
|
||||||
|
gst_video_time_code_add_frames (tc1, 192152);
|
||||||
|
fail_unless (tc1->hours == 1);
|
||||||
|
fail_unless (tc1->minutes == 4);
|
||||||
|
fail_unless (tc1->seconds == 3);
|
||||||
|
fail_unless (tc1->frames == 2);
|
||||||
|
|
||||||
|
dt2 = gst_video_time_code_to_date_time (tc1);
|
||||||
|
fail_unless (g_date_time_get_year (dt2) == 2016);
|
||||||
|
fail_unless (g_date_time_get_month (dt2) == 7);
|
||||||
|
fail_unless (g_date_time_get_day_of_month (dt2) == 29);
|
||||||
|
fail_unless (g_date_time_get_hour (dt2) == 11);
|
||||||
|
fail_unless (g_date_time_get_minute (dt2) == 36);
|
||||||
|
fail_unless (g_date_time_get_seconds (dt2) == 53.04);
|
||||||
|
|
||||||
|
gst_video_time_code_free (tc1);
|
||||||
|
g_date_time_unref (dt2);
|
||||||
|
g_date_time_unref (dt1);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (videotimecode_dailyjam_compare)
|
||||||
|
{
|
||||||
|
GstVideoTimeCode *tc1, *tc2;
|
||||||
|
GDateTime *dt1;
|
||||||
|
|
||||||
|
dt1 = g_date_time_new_utc (2016, 7, 29, 10, 32, 50);
|
||||||
|
|
||||||
|
tc1 =
|
||||||
|
gst_video_time_code_new (50, 1, dt1,
|
||||||
|
GST_VIDEO_TIME_CODE_FLAGS_NONE, 0, 0, 0, 0, 0);
|
||||||
|
tc2 = gst_video_time_code_copy (tc1);
|
||||||
|
fail_unless (gst_video_time_code_compare (tc1, tc2) == 0);
|
||||||
|
gst_video_time_code_increment_frame (tc1);
|
||||||
|
fail_unless (gst_video_time_code_compare (tc1, tc2) == 1);
|
||||||
|
gst_video_time_code_add_frames (tc2, 2);
|
||||||
|
fail_unless (gst_video_time_code_compare (tc1, tc2) == -1);
|
||||||
|
|
||||||
|
gst_video_time_code_free (tc1);
|
||||||
|
gst_video_time_code_free (tc2);
|
||||||
|
g_date_time_unref (dt1);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
static Suite *
|
||||||
|
gst_videotimecode_suite (void)
|
||||||
|
{
|
||||||
|
Suite *s = suite_create ("GstVideoTimeCode");
|
||||||
|
TCase *tc = tcase_create ("general");
|
||||||
|
|
||||||
|
suite_add_tcase (s, tc);
|
||||||
|
|
||||||
|
tcase_add_test (tc, videotimecode_compare_equal);
|
||||||
|
tcase_add_test (tc, videotimecode_compare_fps_n);
|
||||||
|
tcase_add_test (tc, videotimecode_compare_fps_d);
|
||||||
|
tcase_add_test (tc, videotimecode_compare_frames);
|
||||||
|
tcase_add_test (tc, videotimecode_compare_seconds);
|
||||||
|
tcase_add_test (tc, videotimecode_compare_minutes);
|
||||||
|
tcase_add_test (tc, videotimecode_compare_hours);
|
||||||
|
tcase_add_test (tc, videotimecode_compare_fieldcounts);
|
||||||
|
|
||||||
|
tcase_add_test (tc, videotimecode_addframe_10);
|
||||||
|
tcase_add_test (tc, videotimecode_addframe_0);
|
||||||
|
tcase_add_test (tc, videotimecode_addframe_high);
|
||||||
|
tcase_add_test (tc, videotimecode_addframe_dropframe);
|
||||||
|
tcase_add_test (tc, videotimecode_addframe_framedropped);
|
||||||
|
tcase_add_test (tc, videotimecode_addframe_wrapover);
|
||||||
|
tcase_add_test (tc, videotimecode_addframe_60drop_dropframe);
|
||||||
|
tcase_add_test (tc, videotimecode_addframe_60drop_framedropped);
|
||||||
|
tcase_add_test (tc, videotimecode_addframe_60drop_wrapover);
|
||||||
|
tcase_add_test (tc, videotimecode_addframe_loop);
|
||||||
|
|
||||||
|
tcase_add_test (tc, videotimecode_dailyjam_todatetime);
|
||||||
|
tcase_add_test (tc, videotimecode_dailyjam_compare);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_CHECK_MAIN (gst_videotimecode);
|
Loading…
Reference in a new issue