Home | History | Annotate | Download | only in gtk
      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