/* 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" #define MAKE_FOUR_CC(a,b,c,d) ( ((guint32)a) | (((guint32)b)<< 8) | \ ((guint32)c)<<16 | (((guint32)d)<<24) ) /* RIFF types */ #define GST_RIFF_TAG_RIFF MAKE_FOUR_CC('R','I','F','F') #define GST_RIFF_RIFF_CDXA MAKE_FOUR_CC('C','D','X','A') #define GST_RIFF_TAG_fmt MAKE_FOUR_CC('f','m','t',' ') #define GST_RIFF_TAG_data MAKE_FOUR_CC('d','a','t','a') /* elementfactory information */ static GstElementDetails gst_cdxa_parse_details = { ".dat parser", "Codec/Parser", "LGPL", "Parse a .dat file (VCD) into raw mpeg1", VERSION, "Wim Taymans ", "(C) 2002", }; /* CDXAParse signals and args */ enum { /* FILL ME */ LAST_SIGNAL }; enum { ARG_0, /* FILL ME */ }; GST_PAD_TEMPLATE_FACTORY (sink_templ, "sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_CAPS_NEW ( "cdxaparse_sink", "video/x-cdxa", NULL ) ) GST_PAD_TEMPLATE_FACTORY (src_templ, "src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_CAPS_NEW ( "cdxaparse_src", "video/mpeg", "systemstream", GST_PROPS_BOOLEAN (TRUE) ) ) static void gst_cdxa_parse_class_init (GstCDXAParseClass *klass); static void gst_cdxa_parse_init (GstCDXAParse *cdxa_parse); static void gst_cdxa_parse_loop (GstElement *element); static GstElementStateReturn gst_cdxa_parse_change_state (GstElement *element); static GstElementClass *parent_class = NULL; /*static guint gst_cdxa_parse_signals[LAST_SIGNAL] = { 0 }; */ GType gst_cdxa_parse_get_type(void) { static GType cdxa_parse_type = 0; if (!cdxa_parse_type) { static const GTypeInfo cdxa_parse_info = { sizeof(GstCDXAParseClass), NULL, NULL, (GClassInitFunc)gst_cdxa_parse_class_init, NULL, NULL, sizeof(GstCDXAParse), 0, (GInstanceInitFunc)gst_cdxa_parse_init, }; cdxa_parse_type = g_type_register_static(GST_TYPE_ELEMENT, "GstCDXAParse", &cdxa_parse_info, 0); } return cdxa_parse_type; } static void gst_cdxa_parse_class_init (GstCDXAParseClass *klass) { GObjectClass *gobject_class; GstElementClass *gstelement_class; gobject_class = (GObjectClass*)klass; gstelement_class = (GstElementClass*)klass; parent_class = g_type_class_ref (GST_TYPE_ELEMENT); gstelement_class->change_state = gst_cdxa_parse_change_state; } static void gst_cdxa_parse_init (GstCDXAParse *cdxa_parse) { GST_FLAG_SET (cdxa_parse, GST_ELEMENT_EVENT_AWARE); cdxa_parse->sinkpad = gst_pad_new_from_template ( GST_PAD_TEMPLATE_GET (sink_templ), "sink"); gst_element_add_pad (GST_ELEMENT (cdxa_parse), cdxa_parse->sinkpad); cdxa_parse->srcpad = gst_pad_new_from_template ( GST_PAD_TEMPLATE_GET (src_templ), "src"); gst_element_add_pad (GST_ELEMENT (cdxa_parse), cdxa_parse->srcpad); gst_element_set_loop_function (GST_ELEMENT (cdxa_parse), gst_cdxa_parse_loop); } static gboolean gst_cdxa_parse_handle_event (GstCDXAParse *cdxa_parse) { guint32 remaining; GstEvent *event; GstEventType type; gst_bytestream_get_status (cdxa_parse->bs, &remaining, &event); type = event? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN; switch (type) { case GST_EVENT_EOS: gst_pad_event_default (cdxa_parse->sinkpad, event); break; case GST_EVENT_SEEK: g_warning ("seek event\n"); break; case GST_EVENT_FLUSH: g_warning ("flush event\n"); break; case GST_EVENT_DISCONTINUOUS: g_warning ("discont event\n"); break; default: g_warning ("unhandled event %d\n", type); break; } return TRUE; } /* CDXA starts with the following header: ! RIFF:4 ! size:4 ! "CDXA" ! "fmt " ! size:4 ! (size+1)&~1 bytes of crap ! ! "data" ! data_size:4 ! (data_size/2352) sectors... */ typedef struct { gchar RIFF_tag[4]; guint32 riff_size; gchar CDXA_tag[4]; gchar fmt_tag[4]; guint32 fmt_size; } CDXAParseHeader; /* A sectors 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. */ static void gst_cdxa_parse_loop (GstElement *element) { GstCDXAParse *cdxa_parse; CDXAParseHeader *header; guint8 *headerdata; g_return_if_fail (element != NULL); g_return_if_fail (GST_IS_CDXA_PARSE (element)); cdxa_parse = GST_CDXA_PARSE (element); if (cdxa_parse->state == CDXA_PARSE_HEADER) { guint32 fmt_size; guint8 *buf; guint32 got_bytes; got_bytes = gst_bytestream_peek_bytes (cdxa_parse->bs, &headerdata, 20); header = (CDXAParseHeader *) headerdata; if (got_bytes < 20) return; cdxa_parse->riff_size = GUINT32_FROM_LE (header->riff_size); fmt_size = (GUINT32_FROM_LE (header->fmt_size) + 1)&~1; /* flush the header + fmt_size bytes + 4 bytes "data" */ if (!gst_bytestream_flush (cdxa_parse->bs, 20 + fmt_size + 4)) return; /* get the data size */ got_bytes = gst_bytestream_peek_bytes (cdxa_parse->bs, (guint8**)&buf, 4); if (got_bytes < 4) return; cdxa_parse->data_size = GUINT32_FROM_LE (*((guint32 *)buf)); /* flush the data size */ if (!gst_bytestream_flush (cdxa_parse->bs, 4)) return; if (cdxa_parse->data_size % CDXA_SECTOR_SIZE) g_warning ("cdxa_parse: size not multiple of %d bytes", CDXA_SECTOR_SIZE); cdxa_parse->sectors = cdxa_parse->data_size / CDXA_SECTOR_SIZE; cdxa_parse->state = CDXA_PARSE_DATA; } else { GstBuffer *buf; GstBuffer *outbuf; guint32 got_bytes; got_bytes = gst_bytestream_read (cdxa_parse->bs, &buf, CDXA_SECTOR_SIZE); if (got_bytes < CDXA_SECTOR_SIZE) { gst_cdxa_parse_handle_event (cdxa_parse); return; } outbuf = gst_buffer_create_sub (buf, 24, CDXA_DATA_SIZE); gst_buffer_unref (buf); gst_pad_push (cdxa_parse->srcpad, GST_DATA (outbuf)); } } static GstElementStateReturn gst_cdxa_parse_change_state (GstElement *element) { GstCDXAParse *cdxa_parse = GST_CDXA_PARSE (element); switch (GST_STATE_TRANSITION (element)) { case GST_STATE_NULL_TO_READY: break; case GST_STATE_READY_TO_PAUSED: cdxa_parse->state = CDXA_PARSE_HEADER; cdxa_parse->bs = gst_bytestream_new (cdxa_parse->sinkpad); break; case GST_STATE_PAUSED_TO_PLAYING: break; case GST_STATE_PLAYING_TO_PAUSED: break; case GST_STATE_PAUSED_TO_READY: gst_bytestream_destroy (cdxa_parse->bs); break; case GST_STATE_READY_TO_NULL: break; default: break; } parent_class->change_state (element); return GST_STATE_SUCCESS; } static gboolean plugin_init (GModule *module, GstPlugin *plugin) { GstElementFactory *factory; if (!gst_library_load ("gstbytestream")) return FALSE; /* create an elementfactory for the cdxa_parse element */ factory = gst_element_factory_new ("cdxaparse", GST_TYPE_CDXA_PARSE, &gst_cdxa_parse_details); g_return_val_if_fail (factory != NULL, FALSE); gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (src_templ)); gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (sink_templ)); gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); return TRUE; } GstPluginDesc plugin_desc = { GST_VERSION_MAJOR, GST_VERSION_MINOR, "cdxaparse", plugin_init };