6dfe967b62655b4eed4979bbbf7ba66022fda4c0
[platform/upstream/gstreamer.git] / ext / annodex / gstcmmlutils.c
1 /*
2  * gstcmmlutils.c - GStreamer CMML utility functions
3  * Copyright (C) 2005 Alessandro Decina
4  * 
5  * Authors:
6  *   Alessandro Decina <alessandro@nnva.org>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #include "gstcmmlutils.h"
25
26 #include <math.h>
27 #include <string.h>
28
29 typedef struct
30 {
31   GList *clips;
32   gpointer user_data;
33 } GstCmmlTrack;
34
35 GstClockTime
36 gst_cmml_clock_time_from_npt (const gchar * time)
37 {
38   GstClockTime res;
39   gint fields;
40   gint hours = 0;
41   gint minutes = 0;
42   gint seconds = 0;
43   gint mseconds = 0;
44   GstClockTime hours_t = 0, seconds_t = 0;
45
46   if (!strncmp (time, "npt:", 4))
47     time += 4;
48
49   /* parse npt-hhmmss */
50   fields = sscanf (time, "%d:%d:%d.%d", &hours, &minutes, &seconds, &mseconds);
51   if (fields == 4) {
52     if (hours < 0 || (guint) minutes > 59 || (guint) seconds > 59)
53       goto bad_input;
54
55     hours_t = gst_util_uint64_scale (hours, GST_SECOND * 3600, 1);
56     if (hours_t == G_MAXUINT64)
57       goto overflow;
58
59     seconds_t = seconds * GST_SECOND;
60   } else {
61     guint64 u64seconds;
62
63     /* parse npt-sec */
64     hours_t = 0;
65     minutes = 0;
66     fields = sscanf (time, "%" G_GUINT64_FORMAT ".%d", &u64seconds, &mseconds);
67     if (seconds < 0)
68       goto bad_input;
69
70     seconds_t = gst_util_uint64_scale_int (u64seconds, GST_SECOND, 1);
71     if (seconds_t == G_MAXUINT64)
72       goto overflow;
73   }
74
75   if ((guint) mseconds > 999)
76     goto bad_input;
77
78   res = (minutes * 60) * GST_SECOND + mseconds * GST_MSECOND;
79   if (G_MAXUINT64 - hours_t - seconds_t < res)
80     goto overflow;
81
82   res += hours_t + seconds_t;
83
84   return res;
85
86 bad_input:
87 overflow:
88   return GST_CLOCK_TIME_NONE;
89 }
90
91 GstClockTime
92 gst_cmml_clock_time_from_smpte (const gchar * time)
93 {
94   GstClockTime res;
95   GstClockTime hours_t;
96   gint hours, minutes, seconds;
97   gdouble framerate;
98   gfloat frames;
99   gint fields;
100
101   if (!strncmp (time, "smpte-24:", 9)) {
102     framerate = 24.0;
103     time += 9;
104   } else if (!strncmp (time, "smpte-24-drop:", 14)) {
105     framerate = 23.976;
106     time += 14;
107   } else if (!strncmp (time, "smpte-25:", 9)) {
108     framerate = 25.0;
109     time += 9;
110   } else if (!strncmp (time, "smpte-30:", 9)) {
111     framerate = 30.0;
112     time += 9;
113   } else if (!strncmp (time, "smpte-30-drop:", 14)) {
114     framerate = 29.976;
115     time += 14;
116   } else if (!strncmp (time, "smpte-50:", 9)) {
117     framerate = 50.0;
118     time += 9;
119   } else if (!strncmp (time, "smpte-60:", 9)) {
120     framerate = 60.0;
121     time += 9;
122   } else if (!strncmp (time, "smpte-60-drop:", 14)) {
123     framerate = 59.94;
124     time += 14;
125   } else {
126     return GST_CLOCK_TIME_NONE;
127   }
128
129   fields = sscanf (time, "%d:%d:%d:%f", &hours, &minutes, &seconds, &frames);
130   if (fields == 4) {
131     if (hours < 0 || (guint) minutes > 59 || (guint) seconds > 59 ||
132         frames < 0 || frames > ceil (framerate)) {
133       res = GST_CLOCK_TIME_NONE;
134     } else {
135       hours_t = gst_util_uint64_scale (hours, GST_SECOND * 3600, 1);
136       if (hours_t == G_MAXUINT64)
137         goto overflow;
138
139       res = ((minutes * 60) + seconds + (frames / framerate))
140           * GST_SECOND;
141       if (G_MAXUINT64 - hours_t < res)
142         goto overflow;
143
144       res = hours_t + res;
145     }
146   } else {
147     res = GST_CLOCK_TIME_NONE;
148   }
149
150   return res;
151 overflow:
152   return GST_CLOCK_TIME_NONE;
153 }
154
155 gchar *
156 gst_cmml_clock_time_to_npt (const GstClockTime time)
157 {
158   guint seconds, hours, minutes, mseconds;
159   gchar *res;
160
161   g_return_val_if_fail (time != GST_CLOCK_TIME_NONE, NULL);
162
163   hours = time / (GST_SECOND * 3600);
164   minutes = (time / ((GST_SECOND * 60)) % 60);
165   seconds = (time / GST_SECOND) % 60;
166   mseconds = (time % GST_SECOND) / GST_MSECOND;
167
168   if (mseconds < 100)
169     mseconds *= 10;
170
171   res = g_strdup_printf ("%u:%02u:%02u.%03u",
172       hours, minutes, seconds, mseconds);
173
174   return res;
175 }
176
177 gint64
178 gst_cmml_clock_time_to_granule (GstClockTime prev_time,
179     GstClockTime current_time, gint64 granulerate_n, gint64 granulerate_d,
180     guint8 granuleshift)
181 {
182   guint64 keyindex, keyoffset, granulepos, maxoffset;
183   gint64 granulerate;
184
185   g_return_val_if_fail (granulerate_d != 0, -1);
186   g_return_val_if_fail (granuleshift > 0, -1);
187   g_return_val_if_fail (granuleshift <= 64, -1);
188
189   if (prev_time == GST_CLOCK_TIME_NONE)
190     prev_time = 0;
191
192   if (prev_time > current_time)
193     return -1;
194
195   /* GST_SECOND / (granulerate_n / granulerate_d) */
196   granulerate = gst_util_uint64_scale (GST_SECOND,
197       granulerate_d, granulerate_n);
198
199   prev_time = prev_time / granulerate;
200
201   /* granuleshift == 64 should be a << 0 shift, which is defined */
202   maxoffset = ((guint64) 1 << (64 - granuleshift)) - 1;
203   if (prev_time > maxoffset)
204     /* we need more than 64 - granuleshift bits to encode prev_time */
205     goto overflow;
206
207   keyindex = prev_time << granuleshift;
208
209   keyoffset = (current_time / granulerate) - prev_time;
210   /* make sure we don't shift to the limits of the types as this is undefined. */
211   if (granuleshift == 64)
212     maxoffset = G_MAXUINT64;
213   else
214     maxoffset = ((guint64) 1 << granuleshift) - 1;
215
216   if (keyoffset > maxoffset)
217     /* we need more than granuleshift bits to encode prev_time - current_time */
218     goto overflow;
219
220   granulepos = keyindex + keyoffset;
221
222   return granulepos;
223
224 overflow:
225   return -1;
226 }
227
228 /* track list */
229 GHashTable *
230 gst_cmml_track_list_new ()
231 {
232   return g_hash_table_new (g_str_hash, g_str_equal);
233 }
234
235 static gboolean
236 gst_cmml_track_list_destroy_track (gchar * key,
237     GstCmmlTrack * track, gpointer user_data)
238 {
239   GList *walk;
240
241   for (walk = track->clips; walk; walk = g_list_next (walk))
242     g_object_unref (G_OBJECT (walk->data));
243
244   g_free (key);
245   g_list_free (track->clips);
246   g_free (track);
247
248   return TRUE;
249 }
250
251 void
252 gst_cmml_track_list_destroy (GHashTable * tracks)
253 {
254   g_hash_table_foreach_remove (tracks,
255       (GHRFunc) gst_cmml_track_list_destroy_track, NULL);
256   g_hash_table_destroy (tracks);
257 }
258
259 static gint
260 gst_cmml_track_list_compare_clips (GstCmmlTagClip * a, GstCmmlTagClip * b)
261 {
262   if (a->start_time < b->start_time)
263     return -1;
264
265   return 1;
266 }
267
268 void
269 gst_cmml_track_list_add_clip (GHashTable * tracks, GstCmmlTagClip * clip)
270 {
271   gpointer key, value;
272   GstCmmlTrack *track;
273   gchar *track_name;
274
275   g_return_if_fail (clip->track != NULL);
276
277   if (g_hash_table_lookup_extended (tracks, clip->track, &key, &value)) {
278     track_name = (gchar *) key;
279     track = (GstCmmlTrack *) value;
280   } else {
281     track_name = g_strdup ((gchar *) clip->track);
282     track = g_new0 (GstCmmlTrack, 1);
283     g_hash_table_insert (tracks, track_name, track);
284   }
285
286   /* add clip to the tracklist */
287   track->clips = g_list_insert_sorted (track->clips, g_object_ref (clip),
288       (GCompareFunc) gst_cmml_track_list_compare_clips);
289 }
290
291 gboolean
292 gst_cmml_track_list_del_clip (GHashTable * tracks, GstCmmlTagClip * clip)
293 {
294   GstCmmlTrack *track;
295   GList *link;
296   gboolean res = FALSE;
297
298   g_return_val_if_fail (clip->track != NULL, FALSE);
299
300   track = g_hash_table_lookup (tracks, clip->track);
301   if (track) {
302     link = g_list_find (track->clips, clip);
303     if (link) {
304       g_object_unref (G_OBJECT (link->data));
305       track->clips = g_list_delete_link (track->clips, link);
306       res = TRUE;
307     }
308   }
309
310   return res;
311 }
312
313 gboolean
314 gst_cmml_track_list_has_clip (GHashTable * tracks, GstCmmlTagClip * clip)
315 {
316   GstCmmlTrack *track;
317   GList *walk;
318   GstCmmlTagClip *tmp;
319   gboolean res = FALSE;
320
321   track = g_hash_table_lookup (tracks, (gchar *) clip->track);
322   if (track) {
323     for (walk = track->clips; walk; walk = g_list_next (walk)) {
324       tmp = GST_CMML_TAG_CLIP (walk->data);
325       if (tmp->start_time == clip->start_time) {
326         res = TRUE;
327         break;
328       }
329     }
330   }
331
332   return res;
333 }
334
335 static gboolean
336 gst_cmml_track_list_merge_track (gchar * track_name,
337     GstCmmlTrack * track, GList ** list)
338 {
339   GList *walk;
340   GstCmmlTagClip *cur;
341
342   for (walk = track->clips; walk; walk = g_list_next (walk)) {
343     cur = GST_CMML_TAG_CLIP (walk->data);
344     *list = g_list_insert_sorted (*list, cur,
345         (GCompareFunc) gst_cmml_track_list_compare_clips);
346   }
347
348   return TRUE;
349 }
350
351 GList *
352 gst_cmml_track_list_get_track_clips (GHashTable * tracks,
353     const gchar * track_name)
354 {
355   GstCmmlTrack *track;
356
357   g_return_val_if_fail (track_name != NULL, NULL);
358
359   track = g_hash_table_lookup (tracks, track_name);
360   return track ? track->clips : NULL;
361 }
362
363 GList *
364 gst_cmml_track_list_get_clips (GHashTable * tracks)
365 {
366   GList *list = NULL;
367
368   g_hash_table_foreach (tracks,
369       (GHFunc) gst_cmml_track_list_merge_track, &list);
370   return list;
371 }
372
373 GstCmmlTagClip *
374 gst_cmml_track_list_get_track_last_clip (GHashTable * tracks,
375     const gchar * track_name)
376 {
377   GstCmmlTrack *track;
378   GList *res = NULL;
379
380   g_return_val_if_fail (track_name != NULL, NULL);
381
382   track = g_hash_table_lookup (tracks, track_name);
383   if (track && track->clips)
384     res = g_list_last (track->clips);
385
386   return res ? GST_CMML_TAG_CLIP (res->data) : NULL;
387 }
388
389 void
390 gst_cmml_track_list_set_data (GHashTable * tracks,
391     const gchar * track_name, gpointer data)
392 {
393   GstCmmlTrack *track;
394
395   g_return_if_fail (track_name != NULL);
396
397   track = g_hash_table_lookup (tracks, track_name);
398   if (track)
399     track->user_data = data;
400 }
401
402 gpointer
403 gst_cmml_track_get_data (GHashTable * tracks, const gchar * track_name)
404 {
405   GstCmmlTrack *track;
406
407   g_return_val_if_fail (track_name != NULL, NULL);
408
409   track = g_hash_table_lookup (tracks, track_name);
410   return track ? track->user_data : NULL;
411 }