/* * 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 <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; }