/* * Copyright (C) 2009 Igalia S.L * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include "DataSourceGStreamer.h" #include #include #include #include static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY); GST_DEBUG_CATEGORY_STATIC(webkit_data_src_debug); #define GST_CAT_DEFAULT webkit_data_src_debug static void webkit_data_src_uri_handler_init(gpointer g_iface, gpointer iface_data); static void webkit_data_src_finalize(WebkitDataSrc* src); static GstStateChangeReturn webkit_data_src_change_state(GstElement* element, GstStateChange transition); static const GInterfaceInfo urihandler_info = { webkit_data_src_uri_handler_init, 0, 0 }; static void _do_init(GType datasrc_type) { GST_DEBUG_CATEGORY_INIT(webkit_data_src_debug, "webkit_data_src", 0, "datasrc element"); g_type_add_interface_static(datasrc_type, GST_TYPE_URI_HANDLER, &urihandler_info); } GST_BOILERPLATE_FULL(WebkitDataSrc, webkit_data_src, GstBin, GST_TYPE_BIN, _do_init); static void webkit_data_src_base_init(gpointer klass) { GstElementClass* element_class = GST_ELEMENT_CLASS(klass); gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&src_template)); gst_element_class_set_details_simple(element_class, (gchar*) "WebKit data source element", (gchar*) "Source", (gchar*) "Handles data: uris", (gchar*) "Philippe Normand "); } static void webkit_data_src_class_init(WebkitDataSrcClass* klass) { GObjectClass* oklass = G_OBJECT_CLASS(klass); GstElementClass* eklass = GST_ELEMENT_CLASS(klass); oklass->finalize = (GObjectFinalizeFunc) webkit_data_src_finalize; eklass->change_state = webkit_data_src_change_state; } static gboolean webkit_data_src_reset(WebkitDataSrc* src) { GstPad* targetpad; if (src->kid) { gst_element_set_state(src->kid, GST_STATE_NULL); gst_bin_remove(GST_BIN(src), src->kid); } src->kid = gst_element_factory_make("giostreamsrc", "streamsrc"); if (!src->kid) { GST_ERROR_OBJECT(src, "Failed to create giostreamsrc"); return FALSE; } gst_bin_add(GST_BIN(src), src->kid); targetpad = gst_element_get_static_pad(src->kid, "src"); gst_ghost_pad_set_target(GST_GHOST_PAD(src->pad), targetpad); gst_object_unref(targetpad); return TRUE; } static void webkit_data_src_init(WebkitDataSrc* src, WebkitDataSrcClass* g_class) { GstPadTemplate* pad_template = gst_static_pad_template_get(&src_template); src->pad = gst_ghost_pad_new_no_target_from_template("src", pad_template); gst_element_add_pad(GST_ELEMENT(src), src->pad); webkit_data_src_reset(src); } static void webkit_data_src_finalize(WebkitDataSrc* src) { g_free(src->uri); if (src->kid) { GST_DEBUG_OBJECT(src, "Removing giostreamsrc element"); gst_element_set_state(src->kid, GST_STATE_NULL); gst_bin_remove(GST_BIN(src), src->kid); src->kid = 0; } GST_CALL_PARENT(G_OBJECT_CLASS, finalize, ((GObject* )(src))); } static GstStateChangeReturn webkit_data_src_change_state(GstElement* element, GstStateChange transition) { GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; WebkitDataSrc* src = WEBKIT_DATA_SRC(element); switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: if (!src->kid) { gst_element_post_message(element, gst_missing_element_message_new(element, "giostreamsrc")); GST_ELEMENT_ERROR(src, CORE, MISSING_PLUGIN, (0), ("no giostreamsrc")); return GST_STATE_CHANGE_FAILURE; } break; default: break; } ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition); if (G_UNLIKELY(ret == GST_STATE_CHANGE_FAILURE)) return ret; // Downwards state change code should be here, after chaining up // to the parent class. return ret; } /*** GSTURIHANDLER INTERFACE *************************************************/ static GstURIType webkit_data_src_uri_get_type(void) { return GST_URI_SRC; } static gchar** webkit_data_src_uri_get_protocols(void) { static gchar* protocols[] = {(gchar*) "data", 0 }; return protocols; } static const gchar* webkit_data_src_uri_get_uri(GstURIHandler* handler) { WebkitDataSrc* src = WEBKIT_DATA_SRC(handler); return src->uri; } static gboolean webkit_data_src_uri_set_uri(GstURIHandler* handler, const gchar* uri) { WebkitDataSrc* src = WEBKIT_DATA_SRC(handler); // URI as defined in RFC2397: // "data:" [ mediatype ] [ ";base64" ] "," data // we parse URIs like this one: // data:audio/3gpp;base64,AA... gchar** scheme_and_remains = g_strsplit(uri, ":", 2); gchar** mime_type_and_options = g_strsplit(scheme_and_remains[1], ";", 0); gint options_size = g_strv_length(mime_type_and_options); gchar* data = 0; gchar* mime_type = 0; gint ret = FALSE; // we require uris with a specified mime-type and base64-encoded // data. It doesn't make much sense anyway to play plain/text data // with very few allowed characters (as per the RFC). if (GST_STATE(src) >= GST_STATE_PAUSED) { GST_ERROR_OBJECT(src, "Element already configured. Reset it and retry"); } else if (!options_size) GST_ERROR_OBJECT(src, "A mime-type is needed in %s", uri); else { mime_type = mime_type_and_options[0]; data = mime_type_and_options[options_size-1]; guchar* decoded_data = 0; gsize decoded_size; if (!g_str_has_prefix(data, "base64")) GST_ERROR_OBJECT(src, "Data has to be base64-encoded in %s", uri); else { decoded_data = g_base64_decode(data+7, &decoded_size); GInputStream* stream = g_memory_input_stream_new_from_data(decoded_data, decoded_size, g_free); g_object_set(src->kid, "stream", stream, NULL); g_object_unref(stream); if (src->uri) { g_free(src->uri); src->uri = 0; } src->uri = g_strdup(uri); ret = TRUE; } } g_strfreev(scheme_and_remains); g_strfreev(mime_type_and_options); return ret; } static void webkit_data_src_uri_handler_init(gpointer g_iface, gpointer iface_data) { GstURIHandlerInterface* iface = (GstURIHandlerInterface *) g_iface; iface->get_type = webkit_data_src_uri_get_type; iface->get_protocols = webkit_data_src_uri_get_protocols; iface->get_uri = webkit_data_src_uri_get_uri; iface->set_uri = webkit_data_src_uri_set_uri; }