mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 16:50:47 +00:00
412 lines
9.8 KiB
C
412 lines
9.8 KiB
C
/*
|
|
* gstcmmlutils.c - GStreamer CMML utility functions
|
|
* Copyright (C) 2005 Alessandro Decina
|
|
*
|
|
* Authors:
|
|
* Alessandro Decina <alessandro@nnva.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.
|
|
*/
|
|
|
|
#include "gstcmmlutils.h"
|
|
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
typedef struct
|
|
{
|
|
GList *clips;
|
|
gpointer user_data;
|
|
} GstCmmlTrack;
|
|
|
|
GstClockTime
|
|
gst_cmml_clock_time_from_npt (const gchar * time)
|
|
{
|
|
GstClockTime res;
|
|
gint fields;
|
|
gint hours = 0;
|
|
gint minutes = 0;
|
|
gint seconds = 0;
|
|
gint mseconds = 0;
|
|
GstClockTime hours_t = 0, seconds_t = 0;
|
|
|
|
if (!strncmp (time, "npt:", 4))
|
|
time += 4;
|
|
|
|
/* parse npt-hhmmss */
|
|
fields = sscanf (time, "%d:%d:%d.%d", &hours, &minutes, &seconds, &mseconds);
|
|
if (fields == 4) {
|
|
if (hours < 0 || (guint) minutes > 59 || (guint) seconds > 59)
|
|
goto bad_input;
|
|
|
|
hours_t = gst_util_uint64_scale (hours, GST_SECOND * 3600, 1);
|
|
if (hours_t == G_MAXUINT64)
|
|
goto overflow;
|
|
|
|
seconds_t = seconds * GST_SECOND;
|
|
} else {
|
|
guint64 u64seconds;
|
|
|
|
/* parse npt-sec */
|
|
hours_t = 0;
|
|
minutes = 0;
|
|
fields = sscanf (time, "%" G_GUINT64_FORMAT ".%d", &u64seconds, &mseconds);
|
|
if (seconds < 0)
|
|
goto bad_input;
|
|
|
|
seconds_t = gst_util_uint64_scale_int (u64seconds, GST_SECOND, 1);
|
|
if (seconds_t == G_MAXUINT64)
|
|
goto overflow;
|
|
}
|
|
|
|
if ((guint) mseconds > 999)
|
|
goto bad_input;
|
|
|
|
res = (minutes * 60) * GST_SECOND + mseconds * GST_MSECOND;
|
|
if (G_MAXUINT64 - hours_t - seconds_t < res)
|
|
goto overflow;
|
|
|
|
res += hours_t + seconds_t;
|
|
|
|
return res;
|
|
|
|
bad_input:
|
|
overflow:
|
|
return GST_CLOCK_TIME_NONE;
|
|
}
|
|
|
|
GstClockTime
|
|
gst_cmml_clock_time_from_smpte (const gchar * time)
|
|
{
|
|
GstClockTime res;
|
|
GstClockTime hours_t;
|
|
gint hours, minutes, seconds;
|
|
gdouble framerate;
|
|
gfloat frames;
|
|
gint fields;
|
|
|
|
if (!strncmp (time, "smpte-24:", 9)) {
|
|
framerate = 24.0;
|
|
time += 9;
|
|
} else if (!strncmp (time, "smpte-24-drop:", 14)) {
|
|
framerate = 23.976;
|
|
time += 14;
|
|
} else if (!strncmp (time, "smpte-25:", 9)) {
|
|
framerate = 25.0;
|
|
time += 9;
|
|
} else if (!strncmp (time, "smpte-30:", 9)) {
|
|
framerate = 30.0;
|
|
time += 9;
|
|
} else if (!strncmp (time, "smpte-30-drop:", 14)) {
|
|
framerate = 29.976;
|
|
time += 14;
|
|
} else if (!strncmp (time, "smpte-50:", 9)) {
|
|
framerate = 50.0;
|
|
time += 9;
|
|
} else if (!strncmp (time, "smpte-60:", 9)) {
|
|
framerate = 60.0;
|
|
time += 9;
|
|
} else if (!strncmp (time, "smpte-60-drop:", 14)) {
|
|
framerate = 59.94;
|
|
time += 14;
|
|
} else {
|
|
return GST_CLOCK_TIME_NONE;
|
|
}
|
|
|
|
fields = sscanf (time, "%d:%d:%d:%f", &hours, &minutes, &seconds, &frames);
|
|
if (fields == 4) {
|
|
if (hours < 0 || (guint) minutes > 59 || (guint) seconds > 59 ||
|
|
frames < 0 || frames > ceil (framerate)) {
|
|
res = GST_CLOCK_TIME_NONE;
|
|
} else {
|
|
hours_t = gst_util_uint64_scale (hours, GST_SECOND * 3600, 1);
|
|
if (hours_t == G_MAXUINT64)
|
|
goto overflow;
|
|
|
|
res = ((minutes * 60) + seconds + (frames / framerate))
|
|
* GST_SECOND;
|
|
if (G_MAXUINT64 - hours_t < res)
|
|
goto overflow;
|
|
|
|
res = hours_t + res;
|
|
}
|
|
} else {
|
|
res = GST_CLOCK_TIME_NONE;
|
|
}
|
|
|
|
return res;
|
|
overflow:
|
|
return GST_CLOCK_TIME_NONE;
|
|
}
|
|
|
|
gchar *
|
|
gst_cmml_clock_time_to_npt (const GstClockTime time)
|
|
{
|
|
guint seconds, hours, minutes, mseconds;
|
|
gchar *res;
|
|
|
|
g_return_val_if_fail (time != GST_CLOCK_TIME_NONE, NULL);
|
|
|
|
hours = time / (GST_SECOND * 3600);
|
|
minutes = (time / ((GST_SECOND * 60)) % 60);
|
|
seconds = (time / GST_SECOND) % 60;
|
|
mseconds = (time % GST_SECOND) / GST_MSECOND;
|
|
|
|
if (mseconds < 100)
|
|
mseconds *= 10;
|
|
|
|
res = g_strdup_printf ("%u:%02u:%02u.%03u",
|
|
hours, minutes, seconds, mseconds);
|
|
|
|
return res;
|
|
}
|
|
|
|
gint64
|
|
gst_cmml_clock_time_to_granule (GstClockTime prev_time,
|
|
GstClockTime current_time, gint64 granulerate_n, gint64 granulerate_d,
|
|
guint8 granuleshift)
|
|
{
|
|
guint64 keyindex, keyoffset, granulepos, maxoffset;
|
|
gint64 granulerate;
|
|
|
|
g_return_val_if_fail (granulerate_d != 0, -1);
|
|
g_return_val_if_fail (granuleshift > 0, -1);
|
|
g_return_val_if_fail (granuleshift <= 64, -1);
|
|
|
|
if (prev_time == GST_CLOCK_TIME_NONE)
|
|
prev_time = 0;
|
|
|
|
if (prev_time > current_time)
|
|
return -1;
|
|
|
|
/* GST_SECOND / (granulerate_n / granulerate_d) */
|
|
granulerate = gst_util_uint64_scale (GST_SECOND,
|
|
granulerate_d, granulerate_n);
|
|
|
|
prev_time = prev_time / granulerate;
|
|
|
|
/* granuleshift == 64 should be a << 0 shift, which is defined */
|
|
maxoffset = ((guint64) 1 << (64 - granuleshift)) - 1;
|
|
if (prev_time > maxoffset)
|
|
/* we need more than 64 - granuleshift bits to encode prev_time */
|
|
goto overflow;
|
|
|
|
keyindex = prev_time << granuleshift;
|
|
|
|
keyoffset = (current_time / granulerate) - prev_time;
|
|
/* make sure we don't shift to the limits of the types as this is undefined. */
|
|
if (granuleshift == 64)
|
|
maxoffset = G_MAXUINT64;
|
|
else
|
|
maxoffset = ((guint64) 1 << granuleshift) - 1;
|
|
|
|
if (keyoffset > maxoffset)
|
|
/* we need more than granuleshift bits to encode prev_time - current_time */
|
|
goto overflow;
|
|
|
|
granulepos = keyindex + keyoffset;
|
|
|
|
return granulepos;
|
|
|
|
overflow:
|
|
return -1;
|
|
}
|
|
|
|
/* track list */
|
|
GHashTable *
|
|
gst_cmml_track_list_new ()
|
|
{
|
|
return g_hash_table_new (g_str_hash, g_str_equal);
|
|
}
|
|
|
|
static gboolean
|
|
gst_cmml_track_list_destroy_track (gchar * key,
|
|
GstCmmlTrack * track, gpointer user_data)
|
|
{
|
|
GList *walk;
|
|
|
|
for (walk = track->clips; walk; walk = g_list_next (walk))
|
|
g_object_unref (G_OBJECT (walk->data));
|
|
|
|
g_free (key);
|
|
g_list_free (track->clips);
|
|
g_free (track);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
gst_cmml_track_list_destroy (GHashTable * tracks)
|
|
{
|
|
g_hash_table_foreach_remove (tracks,
|
|
(GHRFunc) gst_cmml_track_list_destroy_track, NULL);
|
|
g_hash_table_destroy (tracks);
|
|
}
|
|
|
|
static gint
|
|
gst_cmml_track_list_compare_clips (GstCmmlTagClip * a, GstCmmlTagClip * b)
|
|
{
|
|
if (a->start_time < b->start_time)
|
|
return -1;
|
|
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
gst_cmml_track_list_add_clip (GHashTable * tracks, GstCmmlTagClip * clip)
|
|
{
|
|
gpointer key, value;
|
|
GstCmmlTrack *track;
|
|
gchar *track_name;
|
|
|
|
g_return_if_fail (clip->track != NULL);
|
|
|
|
if (g_hash_table_lookup_extended (tracks, clip->track, &key, &value)) {
|
|
track_name = (gchar *) key;
|
|
track = (GstCmmlTrack *) value;
|
|
} else {
|
|
track_name = g_strdup ((gchar *) clip->track);
|
|
track = g_new0 (GstCmmlTrack, 1);
|
|
g_hash_table_insert (tracks, track_name, track);
|
|
}
|
|
|
|
/* add clip to the tracklist */
|
|
track->clips = g_list_insert_sorted (track->clips, g_object_ref (clip),
|
|
(GCompareFunc) gst_cmml_track_list_compare_clips);
|
|
}
|
|
|
|
gboolean
|
|
gst_cmml_track_list_del_clip (GHashTable * tracks, GstCmmlTagClip * clip)
|
|
{
|
|
GstCmmlTrack *track;
|
|
GList *link;
|
|
gboolean res = FALSE;
|
|
|
|
g_return_val_if_fail (clip->track != NULL, FALSE);
|
|
|
|
track = g_hash_table_lookup (tracks, clip->track);
|
|
if (track) {
|
|
link = g_list_find (track->clips, clip);
|
|
if (link) {
|
|
g_object_unref (G_OBJECT (link->data));
|
|
track->clips = g_list_delete_link (track->clips, link);
|
|
res = TRUE;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
gboolean
|
|
gst_cmml_track_list_has_clip (GHashTable * tracks, GstCmmlTagClip * clip)
|
|
{
|
|
GstCmmlTrack *track;
|
|
GList *walk;
|
|
GstCmmlTagClip *tmp;
|
|
gboolean res = FALSE;
|
|
|
|
track = g_hash_table_lookup (tracks, (gchar *) clip->track);
|
|
if (track) {
|
|
for (walk = track->clips; walk; walk = g_list_next (walk)) {
|
|
tmp = GST_CMML_TAG_CLIP (walk->data);
|
|
if (tmp->start_time == clip->start_time) {
|
|
res = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static gboolean
|
|
gst_cmml_track_list_merge_track (gchar * track_name,
|
|
GstCmmlTrack * track, GList ** list)
|
|
{
|
|
GList *walk;
|
|
GstCmmlTagClip *cur;
|
|
|
|
for (walk = track->clips; walk; walk = g_list_next (walk)) {
|
|
cur = GST_CMML_TAG_CLIP (walk->data);
|
|
*list = g_list_insert_sorted (*list, cur,
|
|
(GCompareFunc) gst_cmml_track_list_compare_clips);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GList *
|
|
gst_cmml_track_list_get_track_clips (GHashTable * tracks,
|
|
const gchar * track_name)
|
|
{
|
|
GstCmmlTrack *track;
|
|
|
|
g_return_val_if_fail (track_name != NULL, NULL);
|
|
|
|
track = g_hash_table_lookup (tracks, track_name);
|
|
return track ? track->clips : NULL;
|
|
}
|
|
|
|
GList *
|
|
gst_cmml_track_list_get_clips (GHashTable * tracks)
|
|
{
|
|
GList *list = NULL;
|
|
|
|
g_hash_table_foreach (tracks,
|
|
(GHFunc) gst_cmml_track_list_merge_track, &list);
|
|
return list;
|
|
}
|
|
|
|
GstCmmlTagClip *
|
|
gst_cmml_track_list_get_track_last_clip (GHashTable * tracks,
|
|
const gchar * track_name)
|
|
{
|
|
GstCmmlTrack *track;
|
|
GList *res = NULL;
|
|
|
|
g_return_val_if_fail (track_name != NULL, NULL);
|
|
|
|
track = g_hash_table_lookup (tracks, track_name);
|
|
if (track && track->clips)
|
|
res = g_list_last (track->clips);
|
|
|
|
return res ? GST_CMML_TAG_CLIP (res->data) : NULL;
|
|
}
|
|
|
|
void
|
|
gst_cmml_track_list_set_data (GHashTable * tracks,
|
|
const gchar * track_name, gpointer data)
|
|
{
|
|
GstCmmlTrack *track;
|
|
|
|
g_return_if_fail (track_name != NULL);
|
|
|
|
track = g_hash_table_lookup (tracks, track_name);
|
|
if (track)
|
|
track->user_data = data;
|
|
}
|
|
|
|
gpointer
|
|
gst_cmml_track_get_data (GHashTable * tracks, const gchar * track_name)
|
|
{
|
|
GstCmmlTrack *track;
|
|
|
|
g_return_val_if_fail (track_name != NULL, NULL);
|
|
|
|
track = g_hash_table_lookup (tracks, track_name);
|
|
return track ? track->user_data : NULL;
|
|
}
|