/* GStreamer * Copyright (C) <1999> Erik Walthinsen * <2002> Wim Taymans * * 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "gstcdxaparse.h" #include "gst/riff/riff-ids.h" #include "gst/riff/riff-media.h" static void gst_cdxaparse_base_init (gpointer g_class); static void gst_cdxaparse_class_init (GstCDXAParseClass * klass); static void gst_cdxaparse_init (GstCDXAParse * cdxaparse); static GstElementStateReturn gst_cdxaparse_change_state (GstElement * element); static void gst_cdxaparse_loop (GstElement * element); static GstStaticPadTemplate sink_template_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS ("video/x-cdxa") ); static GstStaticPadTemplate src_template_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS ("video/mpeg, " "systemstream = (boolean) TRUE") ); /* CDXAParse signals and args */ enum { /* FILL ME */ LAST_SIGNAL }; enum { ARG_0, /* FILL ME */ }; static GstElementClass *parent_class = NULL; GType gst_cdxaparse_get_type (void) { static GType cdxaparse_type = 0; if (!cdxaparse_type) { static const GTypeInfo cdxaparse_info = { sizeof (GstCDXAParseClass), gst_cdxaparse_base_init, NULL, (GClassInitFunc) gst_cdxaparse_class_init, NULL, NULL, sizeof (GstCDXAParse), 0, (GInstanceInitFunc) gst_cdxaparse_init, }; cdxaparse_type = g_type_register_static (GST_TYPE_RIFF_READ, "GstCDXAParse", &cdxaparse_info, 0); } return cdxaparse_type; } static void gst_cdxaparse_base_init (gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); static GstElementDetails gst_cdxaparse_details = GST_ELEMENT_DETAILS (".dat parser", "Codec/Parser", "Parse a .dat file (VCD) into raw mpeg1", "Wim Taymans "); gst_element_class_set_details (element_class, &gst_cdxaparse_details); /* register src pads */ gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&sink_template_factory)); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&src_template_factory)); } static void gst_cdxaparse_class_init (GstCDXAParseClass * klass) { GstElementClass *gstelement_class; GObjectClass *object_class; gstelement_class = (GstElementClass *) klass; object_class = (GObjectClass *) klass; parent_class = g_type_class_ref (GST_TYPE_RIFF_READ); gstelement_class->change_state = gst_cdxaparse_change_state; } static void gst_cdxaparse_init (GstCDXAParse * cdxaparse) { /* sink */ cdxaparse->sinkpad = gst_pad_new_from_template (gst_static_pad_template_get (&sink_template_factory), "sink"); gst_element_add_pad (GST_ELEMENT (cdxaparse), cdxaparse->sinkpad); GST_RIFF_READ (cdxaparse)->sinkpad = cdxaparse->sinkpad; gst_element_set_loop_function (GST_ELEMENT (cdxaparse), gst_cdxaparse_loop); cdxaparse->state = GST_CDXAPARSE_START; cdxaparse->srcpad = gst_pad_new_from_template (gst_static_pad_template_get (&src_template_factory), "src"); gst_element_add_pad (GST_ELEMENT (cdxaparse), cdxaparse->srcpad); cdxaparse->seek_pending = FALSE; cdxaparse->seek_offset = 0; } static gboolean gst_cdxaparse_stream_init (GstCDXAParse * cdxa) { GstRiffRead *riff = GST_RIFF_READ (cdxa); guint32 doctype; if (!gst_riff_read_header (riff, &doctype)) return FALSE; if (doctype != GST_RIFF_RIFF_CDXA) { GST_ELEMENT_ERROR (cdxa, STREAM, WRONG_TYPE, (NULL), (NULL)); return FALSE; } return TRUE; } /* Read 'fmt ' header */ static gboolean gst_cdxaparse_fmt (GstCDXAParse * cdxa) { GstRiffRead *riff = GST_RIFF_READ (cdxa); gst_riff_strf_auds *header; if (!gst_riff_read_strf_auds (riff, &header)) { g_warning ("Not fmt"); return FALSE; } /* As we don't know what is in this fmt field, we do nothing */ return TRUE; } static gboolean gst_cdxaparse_other (GstCDXAParse * cdxa) { GstRiffRead *riff = GST_RIFF_READ (cdxa); guint32 tag, length; if (!gst_riff_peek_head (riff, &tag, &length, NULL)) { return FALSE; } switch (tag) { case GST_RIFF_TAG_data: if (!gst_bytestream_flush (riff->bs, 8)) return FALSE; cdxa->state = GST_CDXAPARSE_DATA; cdxa->dataleft = cdxa->datasize = (guint64) length; cdxa->datastart = gst_bytestream_tell (riff->bs); break; default: if (!gst_riff_read_skip (riff)) return FALSE; break; } return TRUE; } /* A sector is 2352 bytes long and is composed of: ! sync ! header ! subheader ! data ... ! edc ! ! 12 bytes ! 4 bytes ! 8 bytes ! 2324 bytes ! 4 bytes ! !-------------------------------------------------------! We parse the data out of it and send it to the srcpad. sync : 00 FF FF FF FF FF FF FF FF FF FF 00 header : hour minute second mode sub-header : track channel sub_mode coding repeat (4 bytes) edc : checksum */ static void gst_cdxaparse_loop (GstElement * element) { GstCDXAParse *cdxa = GST_CDXAPARSE (element); GstRiffRead *riff = GST_RIFF_READ (cdxa); if (cdxa->state == GST_CDXAPARSE_DATA) { if (cdxa->dataleft > 0) { guint32 got_bytes, desired; GstBuffer *buf = NULL; GstBuffer *outbuf = NULL; desired = GST_CDXA_SECTOR_SIZE; if (!(buf = gst_riff_read_element_data (riff, desired, &got_bytes))) return; /* Skip CDXA headers, only keep data */ outbuf = gst_buffer_create_sub (buf, GST_CDXA_HEADER_SIZE, GST_CDXA_DATA_SIZE); gst_buffer_unref (buf); gst_pad_push (cdxa->srcpad, GST_DATA (outbuf)); cdxa->byteoffset += got_bytes; if (got_bytes < cdxa->dataleft) { cdxa->dataleft -= got_bytes; return; } else { cdxa->dataleft = 0; cdxa->state = GST_CDXAPARSE_OTHER; } } else { cdxa->state = GST_CDXAPARSE_OTHER; } } switch (cdxa->state) { case GST_CDXAPARSE_START: if (!gst_cdxaparse_stream_init (cdxa)) { return; } cdxa->state = GST_CDXAPARSE_FMT; /* fall-through */ case GST_CDXAPARSE_FMT: if (!gst_cdxaparse_fmt (cdxa)) { return; } cdxa->state = GST_CDXAPARSE_OTHER; /* fall-through */ case GST_CDXAPARSE_OTHER: if (!gst_cdxaparse_other (cdxa)) { return; } break; case GST_CDXAPARSE_DATA: default: g_assert_not_reached (); } } static GstElementStateReturn gst_cdxaparse_change_state (GstElement * element) { GstCDXAParse *cdxa = GST_CDXAPARSE (element); switch (GST_STATE_TRANSITION (element)) { case GST_STATE_NULL_TO_READY: break; case GST_STATE_READY_TO_PAUSED: cdxa->state = GST_CDXAPARSE_START; break; case GST_STATE_PAUSED_TO_PLAYING: break; case GST_STATE_PLAYING_TO_PAUSED: break; case GST_STATE_PAUSED_TO_READY: cdxa->state = GST_CDXAPARSE_START; cdxa->seek_pending = FALSE; cdxa->seek_offset = 0; break; case GST_STATE_READY_TO_NULL: break; } if (GST_ELEMENT_CLASS (parent_class)->change_state) return GST_ELEMENT_CLASS (parent_class)->change_state (element); return GST_STATE_SUCCESS; } static gboolean plugin_init (GstPlugin * plugin) { if (!gst_library_load ("riff")) { return FALSE; } return gst_element_register (plugin, "cdxaparse", GST_RANK_PRIMARY, GST_TYPE_CDXAPARSE); } GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, "cdxaparse", "Parse a .dat file (VCD) into raw mpeg1", plugin_init, VERSION, "LGPL", GST_PACKAGE, GST_ORIGIN)