/* GStreamer * Copyright (C) 2004 Benjamin Otte * * 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. */ #include #include "gstxine.h" #include #include #include #define GST_TYPE_XINE_INPUT \ (gst_xine_input_get_type()) #define GST_XINE_INPUT(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_XINE_INPUT,GstXineInput)) #define GST_XINE_INPUT_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_XINE_INPUT, GstXineInputClass)) #define GST_XINE_INPUT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_XINE_INPUT,GstXineInputClass)) #define GST_IS_XINE_INPUT(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_XINE_INPUT)) #define GST_IS_XINE_INPUT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_XINE_INPUT)) GType gst_xine_input_get_type (void); typedef struct _GstXineInput GstXineInput; typedef struct _GstXineInputClass GstXineInputClass; struct _GstXineInput { GstXine parent; GstPad *srcpad; input_plugin_t *input; gchar *location; guint blocksize; }; struct _GstXineInputClass { GstXineClass parent_class; plugin_node_t *plugin_node; }; /** GstXineInput ***********************************************************/ enum { ARG_0, ARG_LOCATION }; GST_BOILERPLATE (GstXineInput, gst_xine_input, GstXine, GST_TYPE_XINE); static void gst_xine_input_dispose (GObject * object); static void gst_xine_input_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_xine_input_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static GstStateChangeReturn gst_xine_input_change_state (GstElement * element, GstStateChange transition); static void gst_xine_input_base_init (gpointer g_class) { } static void gst_xine_input_class_init (GstXineInputClass * klass) { GstElementClass *element = GST_ELEMENT_CLASS (klass); GObjectClass *object = G_OBJECT_CLASS (klass); element->change_state = gst_xine_input_change_state; object->set_property = gst_xine_input_set_property; object->get_property = gst_xine_input_get_property; object->dispose = gst_xine_input_dispose; g_object_class_install_property (object, ARG_LOCATION, g_param_spec_string ("location", "location", "location", NULL, G_PARAM_READWRITE)); } static void gst_xine_input_init (GstXineInput * xine, GstXineInputClass * g_class) { } static void gst_xine_input_dispose (GObject * object) { GstXineInput *xine = GST_XINE_INPUT (object); g_free (xine->location); xine->location = NULL; GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object)); } static void gst_xine_input_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstXineInput *xine = GST_XINE_INPUT (object); switch (prop_id) { case ARG_LOCATION: if (gst_element_get_state (GST_ELEMENT (xine)) != GST_STATE_NULL) return; if (xine->location) g_free (xine->location); xine->location = g_strdup (g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); return; } } static void gst_xine_input_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstXineInput *xine = GST_XINE_INPUT (object); switch (prop_id) { case ARG_LOCATION: g_value_set_string (value, xine->location); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); return; } } #define BUFFER_SIZE 4096 /* FIXME: what size? */ static GstData * gst_xine_input_get (GstPad * pad) { GstXineInput *xine = GST_XINE_INPUT (gst_object_get_parent (GST_OBJECT (pad))); GstBuffer *buf; gint real_size, position; /* FIXME: how does xine figure out EOS? */ position = xine->input->get_current_pos (xine->input); if (position > 0 && position == xine->input->get_length (xine->input)) { gst_element_set_eos (GST_ELEMENT (xine)); return GST_DATA (gst_event_new (GST_EVENT_EOS)); } buf = gst_pad_alloc_buffer_and_set_caps (xine->srcpad, GST_BUFFER_OFFSET_NONE, xine->blocksize); GST_BUFFER_OFFSET (buf) = position; real_size = xine->input->read (xine->input, GST_BUFFER_DATA (buf), GST_BUFFER_MAXSIZE (buf)); GST_BUFFER_SIZE (buf) = real_size; if (real_size < 0) { GST_ELEMENT_ERROR (xine, RESOURCE, READ, (NULL), ("error %d reading data", real_size)); gst_data_unref (GST_DATA (buf)); return NULL; } else if (real_size == 0) { buf_element_t *element; if (xine->input->get_capabilities (xine->input) & INPUT_CAP_BLOCK) element = xine->input->read_block (xine->input, gst_xine_get_stream (GST_XINE (xine))->audio_fifo, xine->blocksize); if (element == NULL) { /* FIXME: is this EOS? */ gst_element_set_eos (GST_ELEMENT (xine)); return GST_DATA (gst_event_new (GST_EVENT_EOS)); } else { GST_BUFFER_SIZE (buf) = element->size; /* FIXME: put buf_element_t data in buffer */ memcpy (GST_BUFFER_DATA (buf), element->mem, element->size); element->free_buffer (element); } } GST_BUFFER_OFFSET_END (buf) = xine->input->get_current_pos (xine->input); return GST_DATA (buf); } static GstStateChangeReturn gst_xine_input_change_state (GstElement * element, GstStateChange transition) { GstXineInput *xine = GST_XINE_INPUT (element); input_class_t *input = (input_class_t *) GST_XINE_INPUT_GET_CLASS (xine)->plugin_node-> plugin_class; switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: xine->input = input->get_instance (input, gst_xine_get_stream (GST_XINE (xine)), xine->location); if (!xine->input) return GST_STATE_CHANGE_FAILURE; if (xine->input->open (xine->input) == 0) return GST_STATE_CHANGE_FAILURE; xine->blocksize = xine->input->get_blocksize (xine->input); if (xine->blocksize == 0) xine->blocksize = BUFFER_SIZE; break; case GST_STATE_CHANGE_READY_TO_PAUSED: break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: break; case GST_STATE_CHANGE_PLAYING_TO_PAUSED: break; case GST_STATE_CHANGE_PAUSED_TO_READY: /* FIXME: reset stream */ break; case GST_STATE_CHANGE_READY_TO_NULL: xine->input->dispose (xine->input); xine->input = NULL; break; default: GST_ERROR_OBJECT (element, "invalid state change"); break; } return GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, change_state, (element), GST_STATE_CHANGE_SUCCESS); } /** GstXineInput subclasses ************************************************/ static GstStaticPadTemplate any_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY); static GstStaticPadTemplate cdda_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS ("audio/x-raw-int, " "endianness = (int) LITTLE_ENDIAN, " "signed = (boolean) true, " "width = (int) 16, " "depth = (int) 16, " "rate = (int) 44100, " "channels = (int) 2") ); static void gst_xine_input_subclass_init (gpointer g_class, gpointer class_data) { GstXineInputClass *xine_class = GST_XINE_INPUT_CLASS (g_class); GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); GstElementDetails details = GST_ELEMENT_DETAILS (NULL, "Source", NULL, "Benjamin Otte "); input_class_t *input; xine_class->plugin_node = class_data; input = (input_class_t *) xine_class->plugin_node->plugin_class; details.longname = g_strdup_printf ("%s xine input", input->get_identifier (input)); details.description = g_strdup_printf ("%s", input->get_description (input)); gst_element_class_set_details (element_class, &details); g_free (details.longname); g_free (details.description); /* FIXME: this is pretty hackish, anyone knows a better idea (xine doesn't) */ if (strcmp (input->get_identifier (input), "cdda") == 0) { gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&cdda_template)); } else { gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&any_template)); } } static void gst_xine_input_sub_init (GTypeInstance * instance, gpointer g_class) { GstElementClass *klass = GST_ELEMENT_GET_CLASS (instance); GstXineInput *xine = GST_XINE_INPUT (instance); xine->srcpad = gst_pad_new_from_template (gst_element_class_get_pad_template (klass, "src"), "src"); gst_pad_set_get_function (xine->srcpad, gst_xine_input_get); gst_element_add_pad (GST_ELEMENT (xine), xine->srcpad); } gboolean gst_xine_input_init_plugin (GstPlugin * plugin) { GTypeInfo plugin_info = { sizeof (GstXineInputClass), NULL, NULL, gst_xine_input_subclass_init, NULL, NULL, sizeof (GstXineInput), 0, gst_xine_input_sub_init, }; plugin_node_t *node; GstXineClass *klass; klass = g_type_class_ref (GST_TYPE_XINE); node = xine_list_first_content (klass->xine->plugin_catalog->input); while (node) { gchar *plugin_name = g_strdup_printf ("xinesrc_%s", node->info->id); gchar *type_name = g_strdup_printf ("GstXineInput%s", node->info->id); GType type; plugin_info.class_data = node; type = g_type_register_static (GST_TYPE_XINE_INPUT, type_name, &plugin_info, 0); g_free (type_name); if (!gst_element_register (plugin, plugin_name, GST_RANK_MARGINAL, type)) { g_free (plugin_name); return FALSE; } g_free (plugin_name); node = xine_list_next_content (klass->xine->plugin_catalog->input); } g_type_class_unref (klass); return TRUE; }