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>
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.
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.
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.
27 #include "gstcdxaparse.h"
28 #include "gstvcdparse.h"
30 #include <gst/riff/riff-ids.h>
31 #include <gst/riff/riff-read.h>
33 GST_DEBUG_CATEGORY (vcdparse_debug);
35 GST_DEBUG_CATEGORY_STATIC (cdxaparse_debug);
36 #define GST_CAT_DEFAULT cdxaparse_debug
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,
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);
47 static GstStaticPadTemplate sink_template_factory =
48 GST_STATIC_PAD_TEMPLATE ("sink",
51 GST_STATIC_CAPS ("video/x-cdxa")
54 static GstStaticPadTemplate src_template_factory =
55 GST_STATIC_PAD_TEMPLATE ("src",
58 GST_STATIC_CAPS ("video/mpeg, " "systemstream = (boolean) TRUE")
61 GST_BOILERPLATE (GstCDXAParse, gst_cdxa_parse, GstElement, GST_TYPE_ELEMENT);
64 gst_cdxa_parse_base_init (gpointer g_class)
66 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
68 gst_element_class_set_details_simple (element_class, "(S)VCD parser",
70 "Parse a .dat file from (S)VCD into raw MPEG-1",
71 "Wim Taymans <wim.taymans@tvd.be>");
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);
81 gst_cdxa_parse_class_init (GstCDXAParseClass * klass)
83 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
85 gstelement_class->change_state =
86 GST_DEBUG_FUNCPTR (gst_cdxa_parse_change_state);
90 gst_cdxa_parse_init (GstCDXAParse * cdxaparse, GstCDXAParseClass * klass)
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));
101 gst_element_add_pad (GST_ELEMENT (cdxaparse), cdxaparse->sinkpad);
104 gst_pad_new_from_static_template (&src_template_factory, "src");
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));
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);
119 cdxaparse->state = GST_CDXA_PARSE_START;
120 cdxaparse->offset = 0;
121 cdxaparse->datasize = 0;
122 cdxaparse->datastart = -1;
125 #define HAVE_FOURCC(data,fourcc) (GST_READ_UINT32_LE((data))==(fourcc))
128 gst_cdxa_parse_stream_init (GstCDXAParse * cdxa)
130 GstFlowReturn flow_ret;
131 GstBuffer *buf = NULL;
134 flow_ret = gst_pad_pull_range (cdxa->sinkpad, cdxa->offset, 12, &buf);
135 if (flow_ret != GST_FLOW_OK)
138 if (GST_BUFFER_SIZE (buf) < 12)
141 data = GST_BUFFER_DATA (buf);
142 if (!HAVE_FOURCC (data, GST_RIFF_TAG_RIFF)) {
143 GST_ERROR_OBJECT (cdxa, "Not a RIFF file");
147 if (!HAVE_FOURCC (data + 8, GST_RIFF_RIFF_CDXA)) {
148 GST_ERROR_OBJECT (cdxa, "RIFF file does not have CDXA content");
153 gst_buffer_unref (buf);
159 GST_ELEMENT_ERROR (cdxa, STREAM, WRONG_TYPE, (NULL), (NULL));
160 gst_buffer_unref (buf);
165 gst_cdxa_parse_sink_activate (GstPad * sinkpad)
167 GstCDXAParse *cdxa = GST_CDXA_PARSE (GST_PAD_PARENT (sinkpad));
169 if (!gst_pad_check_pull_range (sinkpad) ||
170 !gst_pad_activate_pull (sinkpad, TRUE)) {
171 GST_DEBUG_OBJECT (cdxa, "No pull mode");
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))
185 gst_cdxa_parse_sink_activate_pull (GstPad * sinkpad, gboolean active)
188 /* if we have a scheduler we can start the task */
189 gst_pad_start_task (sinkpad, (GstTaskFunction) gst_cdxa_parse_loop,
192 gst_pad_stop_task (sinkpad);
199 * A sector is 2352 bytes long and is composed of:
201 * ! sync ! header ! subheader ! data ... ! edc !
202 * ! 12 bytes ! 4 bytes ! 8 bytes ! 2324 bytes ! 4 bytes !
203 * !-------------------------------------------------------!
205 * We strip the data out of it and send it to the srcpad.
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)
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
218 /* FIXME: use version from gstcdxastrip.c */
220 gst_cdxa_parse_strip (GstBuffer * buf)
224 g_assert (GST_BUFFER_SIZE (buf) >= GST_CDXA_SECTOR_SIZE);
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);
234 /* -1 = no sync (discard buffer),
235 * otherwise offset indicates syncpoint in buffer.
239 gst_cdxa_parse_sync (GstBuffer * buf)
241 const guint8 sync_marker[12] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
242 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00
247 size = GST_BUFFER_SIZE (buf);
248 data = GST_BUFFER_DATA (buf);
251 if (memcmp (data, sync_marker, 12) == 0) {
252 return (gint) (data - GST_BUFFER_DATA (buf));
261 gst_cdxa_parse_loop (GstPad * sinkpad)
263 GstFlowReturn flow_ret;
265 GstBuffer *buf = NULL;
266 gint sync_offset = -1;
268 cdxa = GST_CDXA_PARSE (GST_PAD_PARENT (sinkpad));
270 if (cdxa->datasize <= 0) {
271 GstFormat format = GST_FORMAT_BYTES;
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);
280 gst_object_unref (peer);
282 GST_DEBUG_OBJECT (cdxa, "Upstream size: %" G_GINT64_FORMAT, cdxa->datasize);
288 req = 8 + GST_CDXA_SECTOR_SIZE; /* riff chunk header = 8 bytes */
290 flow_ret = gst_pad_pull_range (cdxa->sinkpad, cdxa->offset, req, &buf);
292 if (flow_ret != GST_FLOW_OK) {
293 GST_DEBUG_OBJECT (cdxa, "Pull flow: %s", gst_flow_get_name (flow_ret));
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);
303 sync_offset = gst_cdxa_parse_sync (buf);
304 gst_buffer_unref (buf);
307 if (sync_offset >= 0)
311 cdxa->bytes_skipped += req;
314 cdxa->offset += sync_offset;
315 cdxa->bytes_skipped += sync_offset;
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;
325 GST_DEBUG_OBJECT (cdxa, "pulling buffer at offset 0x%" G_GINT64_MODIFIER "x",
328 flow_ret = gst_pad_pull_range (cdxa->sinkpad, cdxa->offset,
329 GST_CDXA_SECTOR_SIZE, &buf);
331 if (flow_ret != GST_FLOW_OK) {
332 GST_DEBUG_OBJECT (cdxa, "Flow: %s", gst_flow_get_name (flow_ret));
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);
342 buf = gst_cdxa_parse_strip (buf);
344 GST_DEBUG_OBJECT (cdxa, "pushing buffer %p", buf);
345 gst_buffer_set_caps (buf, GST_PAD_CAPS (cdxa->srcpad));
347 cdxa->offset += GST_BUFFER_SIZE (buf);
348 cdxa->bytes_sent += GST_BUFFER_SIZE (buf);
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));
360 GST_DEBUG_OBJECT (cdxa, "Sending EOS");
362 gst_buffer_unref (buf);
364 gst_pad_push_event (cdxa->srcpad, gst_event_new_eos ());
369 GST_DEBUG_OBJECT (cdxa, "Pausing");
370 gst_pad_pause_task (cdxa->sinkpad);
376 gst_cdxa_parse_convert_src_to_sink_offset (GstCDXAParse * cdxa, gint64 src)
380 sink = src + cdxa->datastart;
381 sink = gst_util_uint64_scale (sink, GST_CDXA_SECTOR_SIZE, GST_CDXA_DATA_SIZE);
383 /* FIXME: take into account skipped bytes */
385 GST_DEBUG_OBJECT (cdxa, "src offset=%" G_GINT64_FORMAT ", sink offset=%"
386 G_GINT64_FORMAT, src, sink);
392 gst_cdxa_parse_convert_sink_to_src_offset (GstCDXAParse * cdxa, gint64 sink)
396 src = sink - cdxa->datastart;
397 src = gst_util_uint64_scale (src, GST_CDXA_DATA_SIZE, GST_CDXA_SECTOR_SIZE);
399 /* FIXME: take into account skipped bytes */
401 GST_DEBUG_OBJECT (cdxa, "sink offset=%" G_GINT64_FORMAT ", src offset=%"
402 G_GINT64_FORMAT, sink, src);
408 gst_cdxa_parse_do_seek (GstCDXAParse * cdxa, GstEvent * event)
411 GstSeekType start_type;
413 gint64 start, off, upstream_size;
415 gst_event_parse_seek (event, NULL, &format, &flags, &start_type, &start,
418 if (format != GST_FORMAT_BYTES) {
419 GST_DEBUG_OBJECT (cdxa, "Can only handle seek in BYTES format");
423 if (start_type != GST_SEEK_TYPE_SET) {
424 GST_DEBUG_OBJECT (cdxa, "Can only handle seek from start (SEEK_TYPE_SET)");
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);
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);
439 /* unlock upstream pull_range */
440 gst_pad_push_event (cdxa->sinkpad, gst_event_new_flush_start ());
442 /* make sure our loop function exits */
443 gst_pad_push_event (cdxa->srcpad, gst_event_new_flush_start ());
445 /* wait for streaming to finish */
446 GST_PAD_STREAM_LOCK (cdxa->sinkpad);
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 ());
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));
456 GST_OBJECT_LOCK (cdxa);
458 GST_OBJECT_UNLOCK (cdxa);
461 gst_pad_start_task (cdxa->sinkpad,
462 (GstTaskFunction) gst_cdxa_parse_loop, cdxa->sinkpad);
464 GST_PAD_STREAM_UNLOCK (cdxa->sinkpad);
469 gst_cdxa_parse_src_event (GstPad * srcpad, GstEvent * event)
471 GstCDXAParse *cdxa = GST_CDXA_PARSE (gst_pad_get_parent (srcpad));
472 gboolean res = FALSE;
474 GST_DEBUG_OBJECT (cdxa, "Handling %s event", GST_EVENT_TYPE_NAME (event));
476 switch (GST_EVENT_TYPE (event)) {
478 res = gst_cdxa_parse_do_seek (cdxa, event);
481 res = gst_pad_event_default (srcpad, event);
485 gst_object_unref (cdxa);
490 gst_cdxa_parse_src_query (GstPad * srcpad, GstQuery * query)
492 GstCDXAParse *cdxa = GST_CDXA_PARSE (gst_pad_get_parent (srcpad));
493 gboolean res = FALSE;
495 GST_DEBUG_OBJECT (cdxa, "Handling %s query",
496 gst_query_type_get_name (GST_QUERY_TYPE (query)));
498 res = gst_pad_query_default (srcpad, query);
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);
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);
524 gst_object_unref (cdxa);
528 static GstStateChangeReturn
529 gst_cdxa_parse_change_state (GstElement * element, GstStateChange transition)
531 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
532 GstCDXAParse *cdxa = GST_CDXA_PARSE (element);
534 switch (transition) {
535 case GST_STATE_CHANGE_READY_TO_PAUSED:
536 cdxa->state = GST_CDXA_PARSE_START;
542 if (GST_ELEMENT_CLASS (parent_class)->change_state)
543 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
545 switch (transition) {
546 case GST_STATE_CHANGE_PAUSED_TO_READY:
547 cdxa->state = GST_CDXA_PARSE_START;
549 cdxa->datastart = -1;
559 plugin_init (GstPlugin * plugin)
561 GST_DEBUG_CATEGORY_INIT (cdxaparse_debug, "cdxaparse", 0, "CDXA Parser");
562 GST_DEBUG_CATEGORY_INIT (vcdparse_debug, "vcdparse", 0, "VCD Parser");
564 if (!gst_element_register (plugin, "cdxaparse", GST_RANK_PRIMARY,
565 GST_TYPE_CDXA_PARSE))
567 if (!gst_element_register (plugin, "vcdparse", GST_RANK_PRIMARY,
574 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
577 "Parse a .dat file (VCD) into raw mpeg1",
578 plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)