codecparser: mpeg4 type error
[profile/ivi/gst-plugins-bad.git] / gst / cdxaparse / gstcdxaparse.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *               <2002> Wim Taymans <wim.taymans@chello.be>
4  *               <2006> Tim-Philipp Müller <tim centricular net>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include <string.h>
26
27 #include "gstcdxaparse.h"
28 #include "gstvcdparse.h"
29
30 #include <gst/riff/riff-ids.h>
31 #include <gst/riff/riff-read.h>
32
33 GST_DEBUG_CATEGORY (vcdparse_debug);
34
35 GST_DEBUG_CATEGORY_STATIC (cdxaparse_debug);
36 #define GST_CAT_DEFAULT cdxaparse_debug
37
38 static gboolean gst_cdxa_parse_sink_activate (GstPad * sinkpad);
39 static void gst_cdxa_parse_loop (GstPad * sinkpad);
40 static gboolean gst_cdxa_parse_sink_activate_pull (GstPad * sinkpad,
41     gboolean active);
42 static GstStateChangeReturn gst_cdxa_parse_change_state (GstElement * element,
43     GstStateChange transition);
44 static gboolean gst_cdxa_parse_src_event (GstPad * srcpad, GstEvent * event);
45 static gboolean gst_cdxa_parse_src_query (GstPad * srcpad, GstQuery * query);
46
47 static GstStaticPadTemplate sink_template_factory =
48 GST_STATIC_PAD_TEMPLATE ("sink",
49     GST_PAD_SINK,
50     GST_PAD_ALWAYS,
51     GST_STATIC_CAPS ("video/x-cdxa")
52     );
53
54 static GstStaticPadTemplate src_template_factory =
55 GST_STATIC_PAD_TEMPLATE ("src",
56     GST_PAD_SRC,
57     GST_PAD_ALWAYS,
58     GST_STATIC_CAPS ("video/mpeg, " "systemstream = (boolean) TRUE")
59     );
60
61 GST_BOILERPLATE (GstCDXAParse, gst_cdxa_parse, GstElement, GST_TYPE_ELEMENT);
62
63 static void
64 gst_cdxa_parse_base_init (gpointer g_class)
65 {
66   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
67
68   gst_element_class_set_details_simple (element_class, "(S)VCD parser",
69       "Codec/Parser",
70       "Parse a .dat file from (S)VCD into raw MPEG-1",
71       "Wim Taymans <wim.taymans@tvd.be>");
72
73   /* register src pads */
74   gst_element_class_add_static_pad_template (element_class,
75       &sink_template_factory);
76   gst_element_class_add_static_pad_template (element_class,
77       &src_template_factory);
78 }
79
80 static void
81 gst_cdxa_parse_class_init (GstCDXAParseClass * klass)
82 {
83   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
84
85   gstelement_class->change_state =
86       GST_DEBUG_FUNCPTR (gst_cdxa_parse_change_state);
87 }
88
89 static void
90 gst_cdxa_parse_init (GstCDXAParse * cdxaparse, GstCDXAParseClass * klass)
91 {
92   GstCaps *caps;
93
94   cdxaparse->sinkpad =
95       gst_pad_new_from_static_template (&sink_template_factory, "sink");
96   gst_pad_set_activate_function (cdxaparse->sinkpad,
97       GST_DEBUG_FUNCPTR (gst_cdxa_parse_sink_activate));
98   gst_pad_set_activatepull_function (cdxaparse->sinkpad,
99       GST_DEBUG_FUNCPTR (gst_cdxa_parse_sink_activate_pull));
100
101   gst_element_add_pad (GST_ELEMENT (cdxaparse), cdxaparse->sinkpad);
102
103   cdxaparse->srcpad =
104       gst_pad_new_from_static_template (&src_template_factory, "src");
105
106   gst_pad_set_event_function (cdxaparse->srcpad,
107       GST_DEBUG_FUNCPTR (gst_cdxa_parse_src_event));
108   gst_pad_set_query_function (cdxaparse->srcpad,
109       GST_DEBUG_FUNCPTR (gst_cdxa_parse_src_query));
110
111   caps = gst_caps_new_simple ("video/mpeg",
112       "systemstream", G_TYPE_BOOLEAN, TRUE, NULL);
113   gst_pad_use_fixed_caps (cdxaparse->srcpad);
114   gst_pad_set_caps (cdxaparse->srcpad, caps);
115   gst_caps_unref (caps);
116   gst_element_add_pad (GST_ELEMENT (cdxaparse), cdxaparse->srcpad);
117
118
119   cdxaparse->state = GST_CDXA_PARSE_START;
120   cdxaparse->offset = 0;
121   cdxaparse->datasize = 0;
122   cdxaparse->datastart = -1;
123 }
124
125 #define HAVE_FOURCC(data,fourcc)  (GST_READ_UINT32_LE((data))==(fourcc))
126
127 static gboolean
128 gst_cdxa_parse_stream_init (GstCDXAParse * cdxa)
129 {
130   GstFlowReturn flow_ret;
131   GstBuffer *buf = NULL;
132   guint8 *data;
133
134   flow_ret = gst_pad_pull_range (cdxa->sinkpad, cdxa->offset, 12, &buf);
135   if (flow_ret != GST_FLOW_OK)
136     return flow_ret;
137
138   if (GST_BUFFER_SIZE (buf) < 12)
139     goto wrong_type;
140
141   data = GST_BUFFER_DATA (buf);
142   if (!HAVE_FOURCC (data, GST_RIFF_TAG_RIFF)) {
143     GST_ERROR_OBJECT (cdxa, "Not a RIFF file");
144     goto wrong_type;
145   }
146
147   if (!HAVE_FOURCC (data + 8, GST_RIFF_RIFF_CDXA)) {
148     GST_ERROR_OBJECT (cdxa, "RIFF file does not have CDXA content");
149     goto wrong_type;
150   }
151
152   cdxa->offset += 12;
153   gst_buffer_unref (buf);
154
155   return TRUE;
156
157 wrong_type:
158
159   GST_ELEMENT_ERROR (cdxa, STREAM, WRONG_TYPE, (NULL), (NULL));
160   gst_buffer_unref (buf);
161   return FALSE;
162 }
163
164 static gboolean
165 gst_cdxa_parse_sink_activate (GstPad * sinkpad)
166 {
167   GstCDXAParse *cdxa = GST_CDXA_PARSE (GST_PAD_PARENT (sinkpad));
168
169   if (!gst_pad_check_pull_range (sinkpad) ||
170       !gst_pad_activate_pull (sinkpad, TRUE)) {
171     GST_DEBUG_OBJECT (cdxa, "No pull mode");
172     return FALSE;
173   }
174
175   /* If we can activate pull_range upstream, then read the header
176    * and see if it's really a RIFF CDXA file. */
177   GST_DEBUG_OBJECT (cdxa, "Activated pull mode. Reading RIFF header");
178   if (!gst_cdxa_parse_stream_init (cdxa))
179     return FALSE;
180
181   return TRUE;
182 }
183
184 static gboolean
185 gst_cdxa_parse_sink_activate_pull (GstPad * sinkpad, gboolean active)
186 {
187   if (active) {
188     /* if we have a scheduler we can start the task */
189     gst_pad_start_task (sinkpad, (GstTaskFunction) gst_cdxa_parse_loop,
190         sinkpad);
191   } else {
192     gst_pad_stop_task (sinkpad);
193   }
194
195   return TRUE;
196 }
197
198 /*
199  * A sector is 2352 bytes long and is composed of:
200  * 
201  * !  sync    !  header ! subheader ! data ...   ! edc     !
202  * ! 12 bytes ! 4 bytes ! 8 bytes   ! 2324 bytes ! 4 bytes !
203  * !-------------------------------------------------------!
204  * 
205  * We strip the data out of it and send it to the srcpad.
206  * 
207  * sync : 00 FF FF FF FF FF FF FF FF FF FF 00
208  * header : hour minute second mode
209  * sub-header : track channel sub_mode coding repeat (4 bytes)
210  * edc : checksum
211  */
212
213 /* FIXME: use define from gstcdxastrip.h */
214 #define GST_CDXA_SECTOR_SIZE    2352
215 #define GST_CDXA_DATA_SIZE      2324
216 #define GST_CDXA_HEADER_SIZE    24
217
218 /* FIXME: use version from gstcdxastrip.c */
219 static GstBuffer *
220 gst_cdxa_parse_strip (GstBuffer * buf)
221 {
222   GstBuffer *sub;
223
224   g_assert (GST_BUFFER_SIZE (buf) >= GST_CDXA_SECTOR_SIZE);
225
226   /* Skip CDXA headers, only keep data.
227    * FIXME: check sync, resync, ... */
228   sub = gst_buffer_create_sub (buf, GST_CDXA_HEADER_SIZE, GST_CDXA_DATA_SIZE);
229   gst_buffer_unref (buf);
230
231   return sub;
232 }
233
234 /* -1 = no sync (discard buffer),
235  * otherwise offset indicates syncpoint in buffer.
236  */
237
238 static gint
239 gst_cdxa_parse_sync (GstBuffer * buf)
240 {
241   const guint8 sync_marker[12] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
242     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00
243   };
244   guint8 *data;
245   guint size;
246
247   size = GST_BUFFER_SIZE (buf);
248   data = GST_BUFFER_DATA (buf);
249
250   while (size >= 12) {
251     if (memcmp (data, sync_marker, 12) == 0) {
252       return (gint) (data - GST_BUFFER_DATA (buf));
253     }
254     --size;
255     ++data;
256   }
257   return -1;
258 }
259
260 static void
261 gst_cdxa_parse_loop (GstPad * sinkpad)
262 {
263   GstFlowReturn flow_ret;
264   GstCDXAParse *cdxa;
265   GstBuffer *buf = NULL;
266   gint sync_offset = -1;
267
268   cdxa = GST_CDXA_PARSE (GST_PAD_PARENT (sinkpad));
269
270   if (cdxa->datasize <= 0) {
271     GstFormat format = GST_FORMAT_BYTES;
272     GstPad *peer;
273
274     if ((peer = gst_pad_get_peer (sinkpad))) {
275       if (!gst_pad_query_duration (peer, &format, &cdxa->datasize)) {
276         GST_DEBUG_OBJECT (cdxa, "Failed to query upstream size!");
277         gst_object_unref (peer);
278         goto pause;
279       }
280       gst_object_unref (peer);
281     }
282     GST_DEBUG_OBJECT (cdxa, "Upstream size: %" G_GINT64_FORMAT, cdxa->datasize);
283   }
284
285   do {
286     guint req;
287
288     req = 8 + GST_CDXA_SECTOR_SIZE;     /* riff chunk header = 8 bytes */
289
290     flow_ret = gst_pad_pull_range (cdxa->sinkpad, cdxa->offset, req, &buf);
291
292     if (flow_ret != GST_FLOW_OK) {
293       GST_DEBUG_OBJECT (cdxa, "Pull flow: %s", gst_flow_get_name (flow_ret));
294       goto pause;
295     }
296
297     if (GST_BUFFER_SIZE (buf) < req) {
298       GST_DEBUG_OBJECT (cdxa, "Short read, only got %u/%u bytes",
299           GST_BUFFER_SIZE (buf), req);
300       goto eos;
301     }
302
303     sync_offset = gst_cdxa_parse_sync (buf);
304     gst_buffer_unref (buf);
305     buf = NULL;
306
307     if (sync_offset >= 0)
308       break;
309
310     cdxa->offset += req;
311     cdxa->bytes_skipped += req;
312   } while (1);
313
314   cdxa->offset += sync_offset;
315   cdxa->bytes_skipped += sync_offset;
316
317   /* first sync frame? */
318   if (cdxa->datastart < 0) {
319     GST_LOG_OBJECT (cdxa, "datastart=0x%" G_GINT64_MODIFIER "x", cdxa->offset);
320     cdxa->datastart = cdxa->offset;
321     cdxa->bytes_skipped = 0;
322     cdxa->bytes_sent = 0;
323   }
324
325   GST_DEBUG_OBJECT (cdxa, "pulling buffer at offset 0x%" G_GINT64_MODIFIER "x",
326       cdxa->offset);
327
328   flow_ret = gst_pad_pull_range (cdxa->sinkpad, cdxa->offset,
329       GST_CDXA_SECTOR_SIZE, &buf);
330
331   if (flow_ret != GST_FLOW_OK) {
332     GST_DEBUG_OBJECT (cdxa, "Flow: %s", gst_flow_get_name (flow_ret));
333     goto pause;
334   }
335
336   if (GST_BUFFER_SIZE (buf) < GST_CDXA_SECTOR_SIZE) {
337     GST_DEBUG_OBJECT (cdxa, "Short read, only got %u/%u bytes",
338         GST_BUFFER_SIZE (buf), GST_CDXA_SECTOR_SIZE);
339     goto eos;
340   }
341
342   buf = gst_cdxa_parse_strip (buf);
343
344   GST_DEBUG_OBJECT (cdxa, "pushing buffer %p", buf);
345   gst_buffer_set_caps (buf, GST_PAD_CAPS (cdxa->srcpad));
346
347   cdxa->offset += GST_BUFFER_SIZE (buf);
348   cdxa->bytes_sent += GST_BUFFER_SIZE (buf);
349
350   flow_ret = gst_pad_push (cdxa->srcpad, buf);
351   if (flow_ret != GST_FLOW_OK) {
352     GST_DEBUG_OBJECT (cdxa, "Push flow: %s", gst_flow_get_name (flow_ret));
353     goto pause;
354   }
355
356   return;
357
358 eos:
359   {
360     GST_DEBUG_OBJECT (cdxa, "Sending EOS");
361     if (buf)
362       gst_buffer_unref (buf);
363     buf = NULL;
364     gst_pad_push_event (cdxa->srcpad, gst_event_new_eos ());
365     /* fallthrough */
366   }
367 pause:
368   {
369     GST_DEBUG_OBJECT (cdxa, "Pausing");
370     gst_pad_pause_task (cdxa->sinkpad);
371     return;
372   }
373 }
374
375 static gint64
376 gst_cdxa_parse_convert_src_to_sink_offset (GstCDXAParse * cdxa, gint64 src)
377 {
378   gint64 sink;
379
380   sink = src + cdxa->datastart;
381   sink = gst_util_uint64_scale (sink, GST_CDXA_SECTOR_SIZE, GST_CDXA_DATA_SIZE);
382
383   /* FIXME: take into account skipped bytes */
384
385   GST_DEBUG_OBJECT (cdxa, "src offset=%" G_GINT64_FORMAT ", sink offset=%"
386       G_GINT64_FORMAT, src, sink);
387
388   return sink;
389 }
390
391 static gint64
392 gst_cdxa_parse_convert_sink_to_src_offset (GstCDXAParse * cdxa, gint64 sink)
393 {
394   gint64 src;
395
396   src = sink - cdxa->datastart;
397   src = gst_util_uint64_scale (src, GST_CDXA_DATA_SIZE, GST_CDXA_SECTOR_SIZE);
398
399   /* FIXME: take into account skipped bytes */
400
401   GST_DEBUG_OBJECT (cdxa, "sink offset=%" G_GINT64_FORMAT ", src offset=%"
402       G_GINT64_FORMAT, sink, src);
403
404   return src;
405 }
406
407 static gboolean
408 gst_cdxa_parse_do_seek (GstCDXAParse * cdxa, GstEvent * event)
409 {
410   GstSeekFlags flags;
411   GstSeekType start_type;
412   GstFormat format;
413   gint64 start, off, upstream_size;
414
415   gst_event_parse_seek (event, NULL, &format, &flags, &start_type, &start,
416       NULL, NULL);
417
418   if (format != GST_FORMAT_BYTES) {
419     GST_DEBUG_OBJECT (cdxa, "Can only handle seek in BYTES format");
420     return FALSE;
421   }
422
423   if (start_type != GST_SEEK_TYPE_SET) {
424     GST_DEBUG_OBJECT (cdxa, "Can only handle seek from start (SEEK_TYPE_SET)");
425     return FALSE;
426   }
427
428   GST_OBJECT_LOCK (cdxa);
429   off = gst_cdxa_parse_convert_src_to_sink_offset (cdxa, start);
430   upstream_size = cdxa->datasize;
431   GST_OBJECT_UNLOCK (cdxa);
432
433   if (off >= upstream_size) {
434     GST_DEBUG_OBJECT (cdxa, "Invalid target offset %" G_GINT64_FORMAT ", file "
435         "is only %" G_GINT64_FORMAT " bytes in size", off, upstream_size);
436     return FALSE;
437   }
438
439   /* unlock upstream pull_range */
440   gst_pad_push_event (cdxa->sinkpad, gst_event_new_flush_start ());
441
442   /* make sure our loop function exits */
443   gst_pad_push_event (cdxa->srcpad, gst_event_new_flush_start ());
444
445   /* wait for streaming to finish */
446   GST_PAD_STREAM_LOCK (cdxa->sinkpad);
447
448   /* prepare for streaming again */
449   gst_pad_push_event (cdxa->sinkpad, gst_event_new_flush_stop ());
450   gst_pad_push_event (cdxa->srcpad, gst_event_new_flush_stop ());
451
452   gst_pad_push_event (cdxa->srcpad,
453       gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES,
454           start, GST_CLOCK_TIME_NONE, 0));
455
456   GST_OBJECT_LOCK (cdxa);
457   cdxa->offset = off;
458   GST_OBJECT_UNLOCK (cdxa);
459
460   /* and restart */
461   gst_pad_start_task (cdxa->sinkpad,
462       (GstTaskFunction) gst_cdxa_parse_loop, cdxa->sinkpad);
463
464   GST_PAD_STREAM_UNLOCK (cdxa->sinkpad);
465   return TRUE;
466 }
467
468 static gboolean
469 gst_cdxa_parse_src_event (GstPad * srcpad, GstEvent * event)
470 {
471   GstCDXAParse *cdxa = GST_CDXA_PARSE (gst_pad_get_parent (srcpad));
472   gboolean res = FALSE;
473
474   GST_DEBUG_OBJECT (cdxa, "Handling %s event", GST_EVENT_TYPE_NAME (event));
475
476   switch (GST_EVENT_TYPE (event)) {
477     case GST_EVENT_SEEK:
478       res = gst_cdxa_parse_do_seek (cdxa, event);
479       break;
480     default:
481       res = gst_pad_event_default (srcpad, event);
482       break;
483   }
484
485   gst_object_unref (cdxa);
486   return res;
487 }
488
489 static gboolean
490 gst_cdxa_parse_src_query (GstPad * srcpad, GstQuery * query)
491 {
492   GstCDXAParse *cdxa = GST_CDXA_PARSE (gst_pad_get_parent (srcpad));
493   gboolean res = FALSE;
494
495   GST_DEBUG_OBJECT (cdxa, "Handling %s query",
496       gst_query_type_get_name (GST_QUERY_TYPE (query)));
497
498   res = gst_pad_query_default (srcpad, query);
499
500   if (res) {
501     GstFormat format;
502     gint64 val;
503
504     switch (GST_QUERY_TYPE (query)) {
505       case GST_QUERY_DURATION:
506         gst_query_parse_duration (query, &format, &val);
507         if (format == GST_FORMAT_BYTES) {
508           val = gst_cdxa_parse_convert_sink_to_src_offset (cdxa, val);
509           gst_query_set_duration (query, format, val);
510         }
511         break;
512       case GST_QUERY_POSITION:
513         gst_query_parse_position (query, &format, &val);
514         if (format == GST_FORMAT_BYTES) {
515           val = gst_cdxa_parse_convert_sink_to_src_offset (cdxa, val);
516           gst_query_set_position (query, format, val);
517         }
518         break;
519       default:
520         break;
521     }
522   }
523
524   gst_object_unref (cdxa);
525   return res;
526 }
527
528 static GstStateChangeReturn
529 gst_cdxa_parse_change_state (GstElement * element, GstStateChange transition)
530 {
531   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
532   GstCDXAParse *cdxa = GST_CDXA_PARSE (element);
533
534   switch (transition) {
535     case GST_STATE_CHANGE_READY_TO_PAUSED:
536       cdxa->state = GST_CDXA_PARSE_START;
537       break;
538     default:
539       break;
540   }
541
542   if (GST_ELEMENT_CLASS (parent_class)->change_state)
543     ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
544
545   switch (transition) {
546     case GST_STATE_CHANGE_PAUSED_TO_READY:
547       cdxa->state = GST_CDXA_PARSE_START;
548       cdxa->datasize = 0;
549       cdxa->datastart = -1;
550       break;
551     default:
552       break;
553   }
554
555   return ret;
556 }
557
558 static gboolean
559 plugin_init (GstPlugin * plugin)
560 {
561   GST_DEBUG_CATEGORY_INIT (cdxaparse_debug, "cdxaparse", 0, "CDXA Parser");
562   GST_DEBUG_CATEGORY_INIT (vcdparse_debug, "vcdparse", 0, "VCD Parser");
563
564   if (!gst_element_register (plugin, "cdxaparse", GST_RANK_PRIMARY,
565           GST_TYPE_CDXA_PARSE))
566     return FALSE;
567   if (!gst_element_register (plugin, "vcdparse", GST_RANK_PRIMARY,
568           GST_TYPE_VCD_PARSE))
569     return FALSE;
570
571   return TRUE;
572 }
573
574 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
575     GST_VERSION_MINOR,
576     "cdxaparse",
577     "Parse a .dat file (VCD) into raw mpeg1",
578     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)