summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp
diff options
context:
space:
mode:
authorUpstream <upstream-import@none>1970-01-12 13:46:40 +0000
committerUpstream <upstream-import@none>1970-01-12 13:46:40 +0000
commitd8543bb6618c17b12da906afa77d216f58cf4058 (patch)
treec58dc05ed86825bd0ef8d305d58c8205106b540f /WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp
downloadexternal_webkit-d8543bb6618c17b12da906afa77d216f58cf4058.zip
external_webkit-d8543bb6618c17b12da906afa77d216f58cf4058.tar.gz
external_webkit-d8543bb6618c17b12da906afa77d216f58cf4058.tar.bz2
external/webkit r30707
Diffstat (limited to 'WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp')
-rw-r--r--WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp314
1 files changed, 314 insertions, 0 deletions
diff --git a/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp b/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp
new file mode 100644
index 0000000..7e97688
--- /dev/null
+++ b/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2007 OpenedHand
+ * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ *
+ * 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
+ */
+
+/**
+ * SECTION:webkit-video-sink
+ * @short_description: GStreamer video sink
+ *
+ * #WebKitVideoSink is a GStreamer sink element that sends
+ * data to a #cairo_surface_t.
+ */
+
+#include "config.h"
+#include "VideoSinkGStreamer.h"
+
+#include <glib.h>
+#include <gst/gst.h>
+#include <gst/video/video.h>
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE("sink",
+ GST_PAD_SINK, GST_PAD_ALWAYS,
+ GST_STATIC_CAPS(GST_VIDEO_CAPS_RGBx ";" GST_VIDEO_CAPS_BGRx));
+
+GST_DEBUG_CATEGORY_STATIC(webkit_video_sink_debug);
+#define GST_CAT_DEFAULT webkit_video_sink_debug
+
+static GstElementDetails webkit_video_sink_details =
+ GST_ELEMENT_DETAILS("WebKit video sink",
+ "Sink/Video",
+ "Sends video data from a GStreamer pipeline to a Cairo surface",
+ "Alp Toker <alp@atoker.com>");
+
+enum {
+ PROP_0,
+ PROP_SURFACE
+};
+
+struct _WebKitVideoSinkPrivate {
+ cairo_surface_t* surface;
+ GAsyncQueue* async_queue;
+ gboolean rgb_ordering;
+ int width;
+ int height;
+ int fps_n;
+ int fps_d;
+ int par_n;
+ int par_d;
+};
+
+#define _do_init(bla) \
+ GST_DEBUG_CATEGORY_INIT (webkit_video_sink_debug, \
+ "webkitsink", \
+ 0, \
+ "webkit video sink")
+
+GST_BOILERPLATE_FULL(WebKitVideoSink,
+ webkit_video_sink,
+ GstBaseSink,
+ GST_TYPE_BASE_SINK,
+ _do_init);
+
+static void
+webkit_video_sink_base_init(gpointer g_class)
+{
+ GstElementClass* element_class = GST_ELEMENT_CLASS(g_class);
+
+ gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&sinktemplate));
+ gst_element_class_set_details(element_class, &webkit_video_sink_details);
+}
+
+static void
+webkit_video_sink_init(WebKitVideoSink* sink, WebKitVideoSinkClass* klass)
+{
+ WebKitVideoSinkPrivate* priv;
+
+ sink->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE(sink, WEBKIT_TYPE_VIDEO_SINK, WebKitVideoSinkPrivate);
+ priv->async_queue = g_async_queue_new();
+}
+
+static gboolean
+webkit_video_sink_idle_func(gpointer data)
+{
+ WebKitVideoSinkPrivate* priv;
+ GstBuffer* buffer;
+
+ priv = (WebKitVideoSinkPrivate*)data;
+
+ if (!priv->async_queue)
+ return FALSE;
+
+ buffer = (GstBuffer*)g_async_queue_try_pop(priv->async_queue);
+ if (buffer == NULL || G_UNLIKELY(!GST_IS_BUFFER(buffer)))
+ return FALSE;
+
+ // TODO: consider priv->rgb_ordering?
+ cairo_surface_t* src = cairo_image_surface_create_for_data(GST_BUFFER_DATA(buffer), CAIRO_FORMAT_RGB24, priv->width, priv->height, (4 * priv->width + 3) & ~ 3);
+
+ // TODO: We copy the data twice right now. This could be easily improved.
+ cairo_t* cr = cairo_create(priv->surface);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_surface(cr, src, 0, 0);
+ cairo_surface_destroy(src);
+ cairo_rectangle(cr, 0, 0, priv->width, priv->height);
+ cairo_fill(cr);
+ cairo_destroy(cr);
+
+ gst_buffer_unref(buffer);
+
+ return FALSE;
+}
+
+static GstFlowReturn
+webkit_video_sink_render(GstBaseSink* bsink, GstBuffer* buffer)
+{
+ WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(bsink);
+ WebKitVideoSinkPrivate* priv = sink->priv;
+
+ g_async_queue_push(priv->async_queue, gst_buffer_ref(buffer));
+ g_idle_add_full(G_PRIORITY_HIGH_IDLE, webkit_video_sink_idle_func, priv, NULL);
+
+ return GST_FLOW_OK;
+}
+
+static gboolean
+webkit_video_sink_set_caps(GstBaseSink* bsink, GstCaps* caps)
+{
+ WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(bsink);
+ WebKitVideoSinkPrivate* priv = sink->priv;
+ GstStructure* structure;
+ gboolean ret;
+ const GValue* fps;
+ const GValue* par;
+ gint width, height;
+ int red_mask;
+
+ GstCaps* intersection = gst_caps_intersect(gst_static_pad_template_get_caps(&sinktemplate), caps);
+
+ if (gst_caps_is_empty(intersection))
+ return FALSE;
+
+ gst_caps_unref(intersection);
+
+ structure = gst_caps_get_structure(caps, 0);
+
+ ret = gst_structure_get_int(structure, "width", &width);
+ ret &= gst_structure_get_int(structure, "height", &height);
+ fps = gst_structure_get_value(structure, "framerate");
+ ret &= (fps != NULL);
+
+ par = gst_structure_get_value(structure, "pixel-aspect-ratio");
+
+ if (!ret)
+ return FALSE;
+
+ priv->width = width;
+ priv->height = height;
+
+ /* We dont yet use fps or pixel aspect into but handy to have */
+ priv->fps_n = gst_value_get_fraction_numerator(fps);
+ priv->fps_d = gst_value_get_fraction_denominator(fps);
+
+ if (par) {
+ priv->par_n = gst_value_get_fraction_numerator(par);
+ priv->par_d = gst_value_get_fraction_denominator(par);
+ } else
+ priv->par_n = priv->par_d = 1;
+
+ gst_structure_get_int(structure, "red_mask", &red_mask);
+ priv->rgb_ordering = (red_mask == 0xff000000);
+
+ return TRUE;
+}
+
+static void
+webkit_video_sink_dispose(GObject* object)
+{
+ WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(object);
+ WebKitVideoSinkPrivate* priv = sink->priv;
+
+ if (priv->surface) {
+ cairo_surface_destroy(priv->surface);
+ priv->surface = NULL;
+ }
+
+ if (priv->async_queue) {
+ g_async_queue_unref(priv->async_queue);
+ priv->async_queue = NULL;
+ }
+
+ G_OBJECT_CLASS(parent_class)->dispose(object);
+}
+
+static void
+webkit_video_sink_finalize(GObject* object)
+{
+ WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(object);
+
+ G_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+static void
+webkit_video_sink_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec)
+{
+ WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(object);
+ WebKitVideoSinkPrivate* priv = sink->priv;
+
+ switch (prop_id) {
+ case PROP_SURFACE:
+ if (priv->surface)
+ cairo_surface_destroy(priv->surface);
+ priv->surface = cairo_surface_reference((cairo_surface_t*)g_value_get_pointer(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+webkit_video_sink_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec)
+{
+ WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(object);
+
+ switch (prop_id) {
+ case PROP_SURFACE:
+ g_value_set_pointer(value, sink->priv->surface);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+webkit_video_sink_stop(GstBaseSink* base_sink)
+{
+ WebKitVideoSinkPrivate* priv = WEBKIT_VIDEO_SINK(base_sink)->priv;
+
+ g_async_queue_lock(priv->async_queue);
+
+ /* Remove all remaining objects from the queue */
+ while(GstBuffer* buffer = (GstBuffer*)g_async_queue_try_pop_unlocked(priv->async_queue))
+ gst_buffer_unref(buffer);
+
+ g_async_queue_unlock(priv->async_queue);
+
+ return TRUE;
+}
+
+static void
+webkit_video_sink_class_init(WebKitVideoSinkClass* klass)
+{
+ GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
+ GstBaseSinkClass* gstbase_sink_class = GST_BASE_SINK_CLASS(klass);
+
+ g_type_class_add_private(klass, sizeof(WebKitVideoSinkPrivate));
+
+ gobject_class->set_property = webkit_video_sink_set_property;
+ gobject_class->get_property = webkit_video_sink_get_property;
+
+ gobject_class->dispose = webkit_video_sink_dispose;
+ gobject_class->finalize = webkit_video_sink_finalize;
+
+ gstbase_sink_class->render = webkit_video_sink_render;
+ gstbase_sink_class->preroll = webkit_video_sink_render;
+ gstbase_sink_class->stop = webkit_video_sink_stop;
+ gstbase_sink_class->set_caps = webkit_video_sink_set_caps;
+
+ g_object_class_install_property(
+ gobject_class, PROP_SURFACE,
+ g_param_spec_pointer("surface", "surface", "Target cairo_surface_t*",
+ (GParamFlags)(G_PARAM_READWRITE)));
+}
+
+/**
+ * webkit_video_sink_new:
+ * @surface: a #cairo_surface_t
+ *
+ * Creates a new GStreamer video sink which uses @surface as the target
+ * for sinking a video stream from GStreamer.
+ *
+ * Return value: a #GstElement for the newly created video sink
+ */
+GstElement*
+webkit_video_sink_new(cairo_surface_t* surface)
+{
+ return (GstElement*)g_object_new(WEBKIT_TYPE_VIDEO_SINK, "surface", surface, NULL);
+}
+
+void
+webkit_video_sink_set_surface(WebKitVideoSink* sink, cairo_surface_t* surface)
+{
+ WebKitVideoSinkPrivate* priv;
+
+ sink->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE(sink, WEBKIT_TYPE_VIDEO_SINK, WebKitVideoSinkPrivate);
+ if (priv->surface)
+ cairo_surface_destroy(priv->surface);
+ priv->surface = cairo_surface_reference(surface);
+}