Tizen 2.0 Release
[framework/multimedia/gst-plugins-good0.10.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 <stdio.h>
28 #include <string.h>
29
30 typedef struct
31 {
32   GList *clips;
33   gpointer user_data;
34 } GstCmmlTrack;
35
36 GstClockTime
37 gst_cmml_clock_time_from_npt (const gchar * time)
38 {
39   GstClockTime res;
40   gint fields;
41   gint hours = 0;
42   gint minutes = 0;
43   gint seconds = 0;
44   gint mseconds = 0;
45   GstClockTime hours_t = 0, seconds_t = 0;
46
47   if (!strncmp (time, "npt:", 4))
48     time += 4;
49
50   /* parse npt-hhmmss */
51   fields = sscanf (time, "%d:%d:%d.%d", &hours, &minutes, &seconds, &mseconds);
52   if (fields == 4) {
53     if (hours < 0 || (guint) minutes > 59 || (guint) seconds > 59)
54       goto bad_input;
55
56     hours_t = gst_util_uint64_scale (hours, GST_SECOND * 3600, 1);
57     if (hours_t == G_MAXUINT64)
58       goto overflow;
59
60     seconds_t = seconds * GST_SECOND;
61   } else {
62     guint64 u64seconds;
63
64     /* parse npt-sec */
65     hours_t = 0;
66     minutes = 0;
67     fields = sscanf (time, "%" G_GUINT64_FORMAT ".%d", &u64seconds, &mseconds);
68     if (seconds < 0)
69       goto bad_input;
70
71     seconds_t = gst_util_uint64_scale_int (u64seconds, GST_SECOND, 1);
72     if (seconds_t == G_MAXUINT64)
73       goto overflow;
74   }
75
76   if ((guint) mseconds > 999)
77     goto bad_input;
78
79   res = (minutes * 60) * GST_SECOND + mseconds * GST_MSECOND;
80   if (G_MAXUINT64 - hours_t - seconds_t < res)
81     goto overflow;
82
83   res += hours_t + seconds_t;
84
85   return res;
86
87 bad_input:
88 overflow:
89   return GST_CLOCK_TIME_NONE;
90 }
91
92 GstClockTime
93 gst_cmml_clock_time_from_smpte (const gchar * time)
94 {
95   GstClockTime res;
96   GstClockTime hours_t;
97   gint hours, minutes, seconds;
98   gdouble framerate;
99   gfloat frames;
100   gint fields;
101
102   if (!strncmp (time, "smpte-24:", 9)) {
103     framerate = 24.0;
104     time += 9;
105   } else if (!strncmp (time, "smpte-24-drop:", 14)) {
106     framerate = 23.976;
107     time += 14;
108   } else if (!strncmp (time, "smpte-25:", 9)) {
109     framerate = 25.0;
110     time += 9;
111   } else if (!strncmp (time, "smpte-30:", 9)) {
112     framerate = 30.0;
113     time += 9;
114   } else if (!strncmp (time, "smpte-30-drop:", 14)) {
115     framerate = 29.976;
116     time += 14;
117   } else if (!strncmp (time, "smpte-50:", 9)) {
118     framerate = 50.0;
119     time += 9;
120   } else if (!strncmp (time, "smpte-60:", 9)) {
121     framerate = 60.0;
122     time += 9;
123   } else if (!strncmp (time, "smpte-60-drop:", 14)) {
124     framerate = 59.94;
125     time += 14;
126   } else {
127     return GST_CLOCK_TIME_NONE;
128   }
129
130   fields = sscanf (time, "%d:%d:%d:%f", &hours, &minutes, &seconds, &frames);
131   if (fields == 4) {
132     if (hours < 0 || (guint) minutes > 59 || (guint) seconds > 59 ||
133         frames < 0 || frames > ceil (framerate)) {
134       res = GST_CLOCK_TIME_NONE;
135     } else {
136       hours_t = gst_util_uint64_scale (hours, GST_SECOND * 3600, 1);
137       if (hours_t == G_MAXUINT64)
138         goto overflow;
139
140       res = ((minutes * 60) + seconds + (frames / framerate))
141           * GST_SECOND;
142       if (G_MAXUINT64 - hours_t < res)
143         goto overflow;
144
145       res = hours_t + res;
146     }
147   } else {
148     res = GST_CLOCK_TIME_NONE;
149   }
150
151   return res;
152 overflow:
153   return GST_CLOCK_TIME_NONE;
154 }
155
156 gchar *
157 gst_cmml_clock_time_to_npt (const GstClockTime time)
158 {
159   guint seconds, hours, minutes, mseconds;
160   gchar *res;
161
162   g_return_val_if_fail (time != GST_CLOCK_TIME_NONE, NULL);
163
164   hours = time / (GST_SECOND * 3600);
165   minutes = (time / ((GST_SECOND * 60)) % 60);
166   seconds = (time / GST_SECOND) % 60;
167   mseconds = (time % GST_SECOND) / GST_MSECOND;
168
169   if (mseconds < 100)
170     mseconds *= 10;
171
172   res = g_strdup_printf ("%u:%02u:%02u.%03u",
173       hours, minutes, seconds, mseconds);
174
175   return res;
176 }
177
178 gint64
179 gst_cmml_clock_time_to_granule (GstClockTime prev_time,
180     GstClockTime current_time, gint64 granulerate_n, gint64 granulerate_d,
181     guint8 granuleshift)
182 {
183   guint64 keyindex, keyoffset, granulepos, maxoffset;
184   gint64 granulerate;
185
186   g_return_val_if_fail (granulerate_d != 0, -1);
187   g_return_val_if_fail (granuleshift > 0, -1);
188   g_return_val_if_fail (granuleshift <= 64, -1);
189
190   if (prev_time == GST_CLOCK_TIME_NONE)
191     prev_time = 0;
192
193   if (prev_time > current_time)
194     return -1;
195
196   /* GST_SECOND / (granulerate_n / granulerate_d) */
197   granulerate = gst_util_uint64_scale (GST_SECOND,
198       granulerate_d, granulerate_n);
199
200   prev_time = prev_time / granulerate;
201
202   /* granuleshift == 64 should be a << 0 shift, which is defined */
203   maxoffset = ((guint64) 1 << (64 - granuleshift)) - 1;
204   if (prev_time > maxoffset)
205     /* we need more than 64 - granuleshift bits to encode prev_time */
206     goto overflow;
207
208   keyindex = prev_time << granuleshift;
209
210   keyoffset = (current_time / granulerate) - prev_time;
211   /* make sure we don't shift to the limits of the types as this is undefined. */
212   if (granuleshift == 64)
213     maxoffset = G_MAXUINT64;
214   else
215     maxoffset = ((guint64) 1 << granuleshift) - 1;
216
217   if (keyoffset > maxoffset)
218     /* we need more than granuleshift bits to encode prev_time - current_time */
219     goto overflow;
220
221   granulepos = keyindex + keyoffset;
222
223   return granulepos;
224
225 overflow:
226   return -1;
227 }
228
229 /* track list */
230 GHashTable *
231 gst_cmml_track_list_new (void)
232 {
233   return g_hash_table_new (g_str_hash, g_str_equal);
234 }
235
236 static gboolean
237 gst_cmml_track_list_destroy_track (gchar * key,
238     GstCmmlTrack * track, gpointer user_data)
239 {
240   GList *walk;
241
242   for (walk = track->clips; walk; walk = g_list_next (walk))
243     g_object_unref (G_OBJECT (walk->data));
244
245   g_free (key);
246   g_list_free (track->clips);
247   g_free (track);
248
249   return TRUE;
250 }
251
252 void
253 gst_cmml_track_list_destroy (GHashTable * tracks)
254 {
255   g_hash_table_foreach_remove (tracks,
256       (GHRFunc) gst_cmml_track_list_destroy_track, NULL);
257   g_hash_table_destroy (tracks);
258 }
259
260 static gint
261 gst_cmml_track_list_compare_clips (GstCmmlTagClip * a, GstCmmlTagClip * b)
262 {
263   if (a->start_time < b->start_time)
264     return -1;
265
266   return 1;
267 }
268
269 void
270 gst_cmml_track_list_add_clip (GHashTable * tracks, GstCmmlTagClip * clip)
271 {
272   gpointer key, value;
273   GstCmmlTrack *track;
274   gchar *track_name;
275
276   g_return_if_fail (clip->track != NULL);
277
278   if (g_hash_table_lookup_extended (tracks, clip->track, &key, &value)) {
279     track_name = (gchar *) key;
280     track = (GstCmmlTrack *) value;
281   } else {
282     track_name = g_strdup ((gchar *) clip->track);
283     track = g_new0 (GstCmmlTrack, 1);
284     g_hash_table_insert (tracks, track_name, track);
285   }
286
287   /* add clip to the tracklist */
288   track->clips = g_list_insert_sorted (track->clips, g_object_ref (clip),
289       (GCompareFunc) gst_cmml_track_list_compare_clips);
290 }
291
292 gboolean
293 gst_cmml_track_list_del_clip (GHashTable * tracks, GstCmmlTagClip * clip)
294 {
295   GstCmmlTrack *track;
296   GList *link;
297   gboolean res = FALSE;
298
299   g_return_val_if_fail (clip->track != NULL, FALSE);
300
301   track = g_hash_table_lookup (tracks, clip->track);
302   if (track) {
303     link = g_list_find (track->clips, clip);
304     if (link) {
305       g_object_unref (G_OBJECT (link->data));
306       track->clips = g_list_delete_link (track->clips, link);
307       res = TRUE;
308     }
309   }
310
311   return res;
312 }
313
314 gboolean
315 gst_cmml_track_list_has_clip (GHashTable * tracks, GstCmmlTagClip * clip)
316 {
317   GstCmmlTrack *track;
318   GList *walk;
319   GstCmmlTagClip *tmp;
320   gboolean res = FALSE;
321
322   track = g_hash_table_lookup (tracks, (gchar *) clip->track);
323   if (track) {
324     for (walk = track->clips; walk; walk = g_list_next (walk)) {
325       tmp = GST_CMML_TAG_CLIP (walk->data);
326       if (tmp->start_time == clip->start_time) {
327         res = TRUE;
328         break;
329       }
330     }
331   }
332
333   return res;
334 }
335
336 static gboolean
337 gst_cmml_track_list_merge_track (gchar * track_name,
338     GstCmmlTrack * track, GList ** list)
339 {
340   GList *walk;
341   GstCmmlTagClip *cur;
342
343   for (walk = track->clips; walk; walk = g_list_next (walk)) {
344     cur = GST_CMML_TAG_CLIP (walk->data);
345     *list = g_list_insert_sorted (*list, cur,
346         (GCompareFunc) gst_cmml_track_list_compare_clips);
347   }
348
349   return TRUE;
350 }
351
352 GList *
353 gst_cmml_track_list_get_track_clips (GHashTable * tracks,
354     const gchar * track_name)
355 {
356   GstCmmlTrack *track;
357
358   g_return_val_if_fail (track_name != NULL, NULL);
359
360   track = g_hash_table_lookup (tracks, track_name);
361   return track ? track->clips : NULL;
362 }
363
364 GList *
365 gst_cmml_track_list_get_clips (GHashTable * tracks)
366 {
367   GList *list = NULL;
368
369   g_hash_table_foreach (tracks,
370       (GHFunc) gst_cmml_track_list_merge_track, &list);
371   return list;
372 }
373
374 GstCmmlTagClip *
375 gst_cmml_track_list_get_track_last_clip (GHashTable * tracks,
376     const gchar * track_name)
377 {
378   GstCmmlTrack *track;
379   GList *res = NULL;
380
381   g_return_val_if_fail (track_name != NULL, NULL);
382
383   track = g_hash_table_lookup (tracks, track_name);
384   if (track && track->clips)
385     res = g_list_last (track->clips);
386
387   return res ? GST_CMML_TAG_CLIP (res->data) : NULL;
388 }