diff options
author | Upstream <upstream-import@none> | 1970-01-12 13:46:40 +0000 |
---|---|---|
committer | Upstream <upstream-import@none> | 1970-01-12 13:46:40 +0000 |
commit | d8543bb6618c17b12da906afa77d216f58cf4058 (patch) | |
tree | c58dc05ed86825bd0ef8d305d58c8205106b540f /WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp | |
download | external_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.cpp | 314 |
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); +} |