1 /* 2 * Copyright (C) 2009 Igalia S.L 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 */ 18 19 #include "config.h" 20 #include "DataSourceGStreamer.h" 21 22 #include <gio/gio.h> 23 #include <glib.h> 24 #include <gst/gst.h> 25 #include <gst/pbutils/missing-plugins.h> 26 27 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE("src", 28 GST_PAD_SRC, 29 GST_PAD_ALWAYS, 30 GST_STATIC_CAPS_ANY); 31 32 GST_DEBUG_CATEGORY_STATIC(webkit_data_src_debug); 33 #define GST_CAT_DEFAULT webkit_data_src_debug 34 35 static void webkit_data_src_uri_handler_init(gpointer g_iface, 36 gpointer iface_data); 37 38 static void webkit_data_src_finalize(WebkitDataSrc* src); 39 static GstStateChangeReturn webkit_data_src_change_state(GstElement* element, 40 GstStateChange transition); 41 42 static const GInterfaceInfo urihandler_info = { 43 webkit_data_src_uri_handler_init, 44 0, 0 45 }; 46 47 48 static void _do_init(GType datasrc_type) 49 { 50 GST_DEBUG_CATEGORY_INIT(webkit_data_src_debug, "webkit_data_src", 0, "datasrc element"); 51 g_type_add_interface_static(datasrc_type, GST_TYPE_URI_HANDLER, 52 &urihandler_info); 53 } 54 55 GST_BOILERPLATE_FULL(WebkitDataSrc, webkit_data_src, GstBin, GST_TYPE_BIN, _do_init); 56 57 static void webkit_data_src_base_init(gpointer klass) 58 { 59 GstElementClass* element_class = GST_ELEMENT_CLASS(klass); 60 61 gst_element_class_add_pad_template(element_class, 62 gst_static_pad_template_get(&src_template)); 63 gst_element_class_set_details_simple(element_class, (gchar*) "WebKit data source element", 64 (gchar*) "Source", 65 (gchar*) "Handles data: uris", 66 (gchar*) "Philippe Normand <pnormand (at) igalia.com>"); 67 68 } 69 70 static void webkit_data_src_class_init(WebkitDataSrcClass* klass) 71 { 72 GObjectClass* oklass = G_OBJECT_CLASS(klass); 73 GstElementClass* eklass = GST_ELEMENT_CLASS(klass); 74 75 oklass->finalize = (GObjectFinalizeFunc) webkit_data_src_finalize; 76 eklass->change_state = webkit_data_src_change_state; 77 } 78 79 80 static gboolean webkit_data_src_reset(WebkitDataSrc* src) 81 { 82 GstPad* targetpad; 83 84 if (src->kid) { 85 gst_element_set_state(src->kid, GST_STATE_NULL); 86 gst_bin_remove(GST_BIN(src), src->kid); 87 } 88 89 src->kid = gst_element_factory_make("giostreamsrc", "streamsrc"); 90 if (!src->kid) { 91 GST_ERROR_OBJECT(src, "Failed to create giostreamsrc"); 92 return FALSE; 93 } 94 95 gst_bin_add(GST_BIN(src), src->kid); 96 97 targetpad = gst_element_get_static_pad(src->kid, "src"); 98 gst_ghost_pad_set_target(GST_GHOST_PAD(src->pad), targetpad); 99 gst_object_unref(targetpad); 100 101 return TRUE; 102 } 103 104 static void webkit_data_src_init(WebkitDataSrc* src, 105 WebkitDataSrcClass* g_class) 106 { 107 GstPadTemplate* pad_template = gst_static_pad_template_get(&src_template); 108 src->pad = gst_ghost_pad_new_no_target_from_template("src", 109 pad_template); 110 111 gst_element_add_pad(GST_ELEMENT(src), src->pad); 112 113 webkit_data_src_reset(src); 114 } 115 116 static void webkit_data_src_finalize(WebkitDataSrc* src) 117 { 118 g_free(src->uri); 119 120 if (src->kid) { 121 GST_DEBUG_OBJECT(src, "Removing giostreamsrc element"); 122 gst_element_set_state(src->kid, GST_STATE_NULL); 123 gst_bin_remove(GST_BIN(src), src->kid); 124 src->kid = 0; 125 } 126 127 GST_CALL_PARENT(G_OBJECT_CLASS, finalize, ((GObject* )(src))); 128 } 129 130 static GstStateChangeReturn webkit_data_src_change_state(GstElement* element, GstStateChange transition) 131 { 132 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; 133 WebkitDataSrc* src = WEBKIT_DATA_SRC(element); 134 135 switch (transition) { 136 case GST_STATE_CHANGE_NULL_TO_READY: 137 if (!src->kid) { 138 gst_element_post_message(element, 139 gst_missing_element_message_new(element, "giostreamsrc")); 140 GST_ELEMENT_ERROR(src, CORE, MISSING_PLUGIN, (0), ("no giostreamsrc")); 141 return GST_STATE_CHANGE_FAILURE; 142 } 143 break; 144 default: 145 break; 146 } 147 148 ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition); 149 if (G_UNLIKELY(ret == GST_STATE_CHANGE_FAILURE)) 150 return ret; 151 152 // Downwards state change code should be here, after chaining up 153 // to the parent class. 154 155 return ret; 156 } 157 158 /*** GSTURIHANDLER INTERFACE *************************************************/ 159 160 static GstURIType webkit_data_src_uri_get_type(void) 161 { 162 return GST_URI_SRC; 163 } 164 165 static gchar** webkit_data_src_uri_get_protocols(void) 166 { 167 static gchar* protocols[] = {(gchar*) "data", 0 }; 168 169 return protocols; 170 } 171 172 static const gchar* webkit_data_src_uri_get_uri(GstURIHandler* handler) 173 { 174 WebkitDataSrc* src = WEBKIT_DATA_SRC(handler); 175 176 return src->uri; 177 } 178 179 static gboolean webkit_data_src_uri_set_uri(GstURIHandler* handler, const gchar* uri) 180 { 181 WebkitDataSrc* src = WEBKIT_DATA_SRC(handler); 182 183 // URI as defined in RFC2397: 184 // "data:" [ mediatype ] [ ";base64" ] "," data 185 // we parse URIs like this one: 186 // data:audio/3gpp;base64,AA... 187 188 gchar** scheme_and_remains = g_strsplit(uri, ":", 2); 189 gchar** mime_type_and_options = g_strsplit(scheme_and_remains[1], ";", 0); 190 gint options_size = g_strv_length(mime_type_and_options); 191 gchar* data = 0; 192 gchar* mime_type = 0; 193 gint ret = FALSE; 194 195 // we require uris with a specified mime-type and base64-encoded 196 // data. It doesn't make much sense anyway to play plain/text data 197 // with very few allowed characters (as per the RFC). 198 199 if (GST_STATE(src) >= GST_STATE_PAUSED) { 200 GST_ERROR_OBJECT(src, "Element already configured. Reset it and retry"); 201 } else if (!options_size) 202 GST_ERROR_OBJECT(src, "A mime-type is needed in %s", uri); 203 else { 204 mime_type = mime_type_and_options[0]; 205 data = mime_type_and_options[options_size-1]; 206 207 guchar* decoded_data = 0; 208 gsize decoded_size; 209 210 if (!g_str_has_prefix(data, "base64")) 211 GST_ERROR_OBJECT(src, "Data has to be base64-encoded in %s", uri); 212 else { 213 decoded_data = g_base64_decode(data+7, &decoded_size); 214 GInputStream* stream = g_memory_input_stream_new_from_data(decoded_data, 215 decoded_size, 216 g_free); 217 g_object_set(src->kid, "stream", stream, NULL); 218 g_object_unref(stream); 219 220 if (src->uri) { 221 g_free(src->uri); 222 src->uri = 0; 223 } 224 225 src->uri = g_strdup(uri); 226 ret = TRUE; 227 } 228 } 229 230 g_strfreev(scheme_and_remains); 231 g_strfreev(mime_type_and_options); 232 return ret; 233 } 234 235 static void webkit_data_src_uri_handler_init(gpointer g_iface, gpointer iface_data) 236 { 237 GstURIHandlerInterface* iface = (GstURIHandlerInterface *) g_iface; 238 239 iface->get_type = webkit_data_src_uri_get_type; 240 iface->get_protocols = webkit_data_src_uri_get_protocols; 241 iface->get_uri = webkit_data_src_uri_get_uri; 242 iface->set_uri = webkit_data_src_uri_set_uri; 243 } 244