summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/graphics/gtk
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/platform/graphics/gtk')
-rw-r--r--WebCore/platform/graphics/gtk/FontGtk.cpp2
-rw-r--r--WebCore/platform/graphics/gtk/IconGtk.cpp26
-rw-r--r--WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp330
-rw-r--r--WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h6
-rw-r--r--WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp2
-rw-r--r--WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp84
6 files changed, 296 insertions, 154 deletions
diff --git a/WebCore/platform/graphics/gtk/FontGtk.cpp b/WebCore/platform/graphics/gtk/FontGtk.cpp
index ee86f96..5c320e0 100644
--- a/WebCore/platform/graphics/gtk/FontGtk.cpp
+++ b/WebCore/platform/graphics/gtk/FontGtk.cpp
@@ -259,7 +259,7 @@ void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const F
// Re-enable the platform shadow we disabled earlier
if (hasShadow)
- context->setShadow(shadowSize, shadowBlur, shadowColor);
+ context->setShadow(shadowSize, shadowBlur, shadowColor, DeviceColorSpace);
// Pango sometimes leaves behind paths we don't want
cairo_new_path(cr);
diff --git a/WebCore/platform/graphics/gtk/IconGtk.cpp b/WebCore/platform/graphics/gtk/IconGtk.cpp
index e08c1ab..3563a59 100644
--- a/WebCore/platform/graphics/gtk/IconGtk.cpp
+++ b/WebCore/platform/graphics/gtk/IconGtk.cpp
@@ -87,23 +87,25 @@ static String lookupIconName(String MIMEType)
return GTK_STOCK_FILE;
}
-PassRefPtr<Icon> Icon::createIconForFile(const String& filename)
+PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames)
{
- if (!g_path_skip_root(filename.utf8().data()))
+ if (filenames.isEmpty())
return 0;
- String MIMEType = MIMETypeRegistry::getMIMETypeForPath(filename);
- String iconName = lookupIconName(MIMEType);
+ if (filenames.size() == 1) {
+ if (!g_path_skip_root(filenames[0].utf8().data()))
+ return 0;
- RefPtr<Icon> icon = adoptRef(new Icon);
- icon->m_icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), iconName.utf8().data(), 16, GTK_ICON_LOOKUP_USE_BUILTIN, NULL);
- if (!icon->m_icon)
- return 0;
- return icon.release();
-}
+ String MIMEType = MIMETypeRegistry::getMIMETypeForPath(filenames[0]);
+ String iconName = lookupIconName(MIMEType);
+
+ RefPtr<Icon> icon = adoptRef(new Icon);
+ icon->m_icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), iconName.utf8().data(), 16, GTK_ICON_LOOKUP_USE_BUILTIN, 0);
+ if (!icon->m_icon)
+ return 0;
+ return icon.release();
+ }
-PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames)
-{
//FIXME: Implement this
return 0;
}
diff --git a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp
index 8d1d261..a023dae 100644
--- a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp
+++ b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp
@@ -46,7 +46,7 @@
#include <gst/video/video.h>
#include <limits>
#include <math.h>
-#include <wtf/GOwnPtr.h>
+#include <wtf/gtk/GOwnPtr.h>
using namespace std;
@@ -66,11 +66,15 @@ gboolean mediaPlayerPrivateMessageCallback(GstBus* bus, GstMessage* message, gpo
LOG_VERBOSE(Media, "Error: %d, %s", err->code, err->message);
error = MediaPlayer::Empty;
- if (err->domain == GST_CORE_ERROR || err->domain == GST_LIBRARY_ERROR)
- error = MediaPlayer::DecodeError;
- else if (err->domain == GST_RESOURCE_ERROR)
+ if (err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND
+ || err->code == GST_STREAM_ERROR_WRONG_TYPE
+ || err->code == GST_STREAM_ERROR_FAILED
+ || err->code == GST_CORE_ERROR_MISSING_PLUGIN
+ || err->code == GST_RESOURCE_ERROR_NOT_FOUND)
error = MediaPlayer::FormatError;
else if (err->domain == GST_STREAM_ERROR)
+ error = MediaPlayer::DecodeError;
+ else if (err->domain == GST_RESOURCE_ERROR)
error = MediaPlayer::NetworkError;
if (mp)
@@ -95,6 +99,33 @@ gboolean mediaPlayerPrivateMessageCallback(GstBus* bus, GstMessage* message, gpo
return true;
}
+static float playbackPosition(GstElement* playbin)
+{
+
+ float ret = 0.0;
+
+ GstQuery* query = gst_query_new_position(GST_FORMAT_TIME);
+ if (!gst_element_query(playbin, query)) {
+ LOG_VERBOSE(Media, "Position query failed...");
+ gst_query_unref(query);
+ return ret;
+ }
+
+ gint64 position;
+ gst_query_parse_position(query, 0, &position);
+
+ // Position is available only if the pipeline is not in NULL or
+ // READY state.
+ if (position != static_cast<gint64>(GST_CLOCK_TIME_NONE))
+ ret = static_cast<float>(position) / static_cast<float>(GST_SECOND);
+
+ LOG_VERBOSE(Media, "Position %" GST_TIME_FORMAT, GST_TIME_ARGS(position));
+
+ gst_query_unref(query);
+
+ return ret;
+}
+
void mediaPlayerPrivateRepaintCallback(WebKitVideoSink*, GstBuffer *buffer, MediaPlayerPrivate* playerPrivate)
{
g_return_if_fail(GST_IS_BUFFER(buffer));
@@ -115,16 +146,34 @@ void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar)
static bool gstInitialized = false;
-static void do_gst_init()
+static bool do_gst_init()
{
// FIXME: We should pass the arguments from the command line
if (!gstInitialized) {
- gst_init(0, 0);
- gstInitialized = true;
- gst_element_register(0, "webkitmediasrc", GST_RANK_PRIMARY,
- WEBKIT_TYPE_DATA_SRC);
+ GOwnPtr<GError> error;
+ gstInitialized = gst_init_check(0, 0, &error.outPtr());
+ if (!gstInitialized)
+ LOG_VERBOSE(Media, "Could not initialize GStreamer: %s",
+ error ? error->message : "unknown error occurred");
+ else
+ gst_element_register(0, "webkitmediasrc", GST_RANK_PRIMARY,
+ WEBKIT_TYPE_DATA_SRC);
}
+ return gstInitialized;
+}
+
+bool MediaPlayerPrivate::isAvailable()
+{
+ if (!do_gst_init())
+ return false;
+
+ GstElementFactory* factory = gst_element_factory_find("playbin2");
+ if (factory) {
+ gst_object_unref(GST_OBJECT(factory));
+ return true;
+ }
+ return false;
}
MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player)
@@ -132,6 +181,8 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player)
, m_playBin(0)
, m_videoSink(0)
, m_source(0)
+ , m_seekTime(0)
+ , m_changingRate(false)
, m_endTime(numeric_limits<float>::infinity())
, m_networkState(MediaPlayer::Empty)
, m_readyState(MediaPlayer::HaveNothing)
@@ -181,14 +232,26 @@ void MediaPlayerPrivate::load(const String& url)
void MediaPlayerPrivate::play()
{
- LOG_VERBOSE(Media, "Play");
- gst_element_set_state(m_playBin, GST_STATE_PLAYING);
+ GstState state;
+ GstState pending;
+
+ gst_element_get_state(m_playBin, &state, &pending, 0);
+ if (state != GST_STATE_PLAYING && pending != GST_STATE_PLAYING) {
+ LOG_VERBOSE(Media, "Play");
+ gst_element_set_state(m_playBin, GST_STATE_PLAYING);
+ }
}
void MediaPlayerPrivate::pause()
{
- LOG_VERBOSE(Media, "Pause");
- gst_element_set_state(m_playBin, GST_STATE_PAUSED);
+ GstState state;
+ GstState pending;
+
+ gst_element_get_state(m_playBin, &state, &pending, 0);
+ if (state != GST_STATE_PAUSED && pending != GST_STATE_PAUSED) {
+ LOG_VERBOSE(Media, "Pause");
+ gst_element_set_state(m_playBin, GST_STATE_PAUSED);
+ }
}
float MediaPlayerPrivate::duration() const
@@ -202,7 +265,7 @@ float MediaPlayerPrivate::duration() const
GstFormat timeFormat = GST_FORMAT_TIME;
gint64 timeLength = 0;
- if (!gst_element_query_duration(m_playBin, &timeFormat, &timeLength) || timeFormat != GST_FORMAT_TIME || timeLength == GST_CLOCK_TIME_NONE) {
+ if (!gst_element_query_duration(m_playBin, &timeFormat, &timeLength) || timeFormat != GST_FORMAT_TIME || static_cast<guint64>(timeLength) == GST_CLOCK_TIME_NONE) {
LOG_VERBOSE(Media, "Time duration query failed.");
return numeric_limits<float>::infinity();
}
@@ -221,23 +284,11 @@ float MediaPlayerPrivate::currentTime() const
if (m_errorOccured)
return 0;
- float ret = 0.0;
+ if (m_seeking)
+ return m_seekTime;
- GstQuery* query = gst_query_new_position(GST_FORMAT_TIME);
- if (!gst_element_query(m_playBin, query)) {
- LOG_VERBOSE(Media, "Position query failed...");
- gst_query_unref(query);
- return ret;
- }
+ return playbackPosition(m_playBin);
- gint64 position;
- gst_query_parse_position(query, 0, &position);
- ret = (float) (position / 1000000000.0);
- LOG_VERBOSE(Media, "Position %" GST_TIME_FORMAT, GST_TIME_ARGS(position));
-
- gst_query_unref(query);
-
- return ret;
}
void MediaPlayerPrivate::seek(float time)
@@ -260,8 +311,10 @@ void MediaPlayerPrivate::seek(float time)
GST_SEEK_TYPE_SET, sec,
GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
LOG_VERBOSE(Media, "Seek to %f failed", time);
- else
+ else {
m_seeking = true;
+ m_seekTime = sec;
+ }
}
void MediaPlayerPrivate::setEndTime(float time)
@@ -310,10 +363,10 @@ IntSize MediaPlayerPrivate::naturalSize() const
gfloat pixelAspectRatio;
gint pixelAspectRatioNumerator, pixelAspectRatioDenominator;
- if (!GST_IS_CAPS(caps) || !gst_caps_is_fixed(caps) ||
- !gst_video_format_parse_caps(caps, NULL, &width, &height) ||
- !gst_video_parse_caps_pixel_aspect_ratio(caps, &pixelAspectRatioNumerator,
- &pixelAspectRatioDenominator)) {
+ if (!GST_IS_CAPS(caps) || !gst_caps_is_fixed(caps)
+ || !gst_video_format_parse_caps(caps, 0, &width, &height)
+ || !gst_video_parse_caps_pixel_aspect_ratio(caps, &pixelAspectRatioNumerator,
+ &pixelAspectRatioDenominator)) {
gst_object_unref(GST_OBJECT(pad));
return IntSize();
}
@@ -353,16 +406,50 @@ void MediaPlayerPrivate::setVolume(float volume)
void MediaPlayerPrivate::setRate(float rate)
{
- if (rate == 0.0) {
- gst_element_set_state(m_playBin, GST_STATE_PAUSED);
+ GstState state;
+ GstState pending;
+
+ gst_element_get_state(m_playBin, &state, &pending, 0);
+ if ((state != GST_STATE_PLAYING && state != GST_STATE_PAUSED)
+ || (pending == GST_STATE_PAUSED))
return;
- }
if (m_isStreaming)
return;
+ m_changingRate = true;
+ float currentPosition = playbackPosition(m_playBin) * GST_SECOND;
+ GstSeekFlags flags = (GstSeekFlags)(GST_SEEK_FLAG_FLUSH);
+ gint64 start, end;
+ bool mute = false;
+
LOG_VERBOSE(Media, "Set Rate to %f", rate);
- seek(currentTime());
+ if (rate >= 0) {
+ // Mute the sound if the playback rate is too extreme.
+ // TODO: in other cases we should perform pitch adjustments.
+ mute = (bool) (rate < 0.8 || rate > 2);
+ start = currentPosition;
+ end = GST_CLOCK_TIME_NONE;
+ } else {
+ start = 0;
+ mute = true;
+
+ // If we are at beginning of media, start from the end to
+ // avoid immediate EOS.
+ if (currentPosition <= 0)
+ end = duration() * GST_SECOND;
+ else
+ end = currentPosition;
+ }
+
+ LOG_VERBOSE(Media, "Need to mute audio: %d", (int) mute);
+
+ if (!gst_element_seek(m_playBin, rate, GST_FORMAT_TIME, flags,
+ GST_SEEK_TYPE_SET, start,
+ GST_SEEK_TYPE_SET, end))
+ LOG_VERBOSE(Media, "Set rate to %f failed", rate);
+ else
+ g_object_set(m_playBin, "mute", mute, NULL);
}
int MediaPlayerPrivate::dataRate() const
@@ -497,6 +584,11 @@ void MediaPlayerPrivate::updateStates()
} else
m_paused = true;
+ if (m_changingRate) {
+ m_player->rateChanged();
+ m_changingRate = false;
+ }
+
if (m_seeking) {
shouldUpdateAfterSeek = true;
m_seeking = false;
@@ -560,11 +652,6 @@ void MediaPlayerPrivate::loadStateChanged()
updateStates();
}
-void MediaPlayerPrivate::rateChanged()
-{
- updateStates();
-}
-
void MediaPlayerPrivate::sizeChanged()
{
notImplemented();
@@ -624,54 +711,36 @@ void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& rect)
return;
int width = 0, height = 0;
- int pixelAspectRatioNumerator = 0;
- int pixelAspectRatioDenominator = 0;
- double doublePixelAspectRatioNumerator = 0;
- double doublePixelAspectRatioDenominator = 0;
- double displayWidth;
- double displayHeight;
- double scale, gapHeight, gapWidth;
-
GstCaps *caps = gst_buffer_get_caps(m_buffer);
+ GstVideoFormat format;
- if (!gst_video_format_parse_caps(caps, NULL, &width, &height) ||
- !gst_video_parse_caps_pixel_aspect_ratio(caps, &pixelAspectRatioNumerator, &pixelAspectRatioDenominator)) {
+ if (!gst_video_format_parse_caps(caps, &format, &width, &height)) {
gst_caps_unref(caps);
return;
}
- displayWidth = width;
- displayHeight = height;
- doublePixelAspectRatioNumerator = pixelAspectRatioNumerator;
- doublePixelAspectRatioDenominator = pixelAspectRatioDenominator;
+ cairo_format_t cairoFormat;
+ if (format == GST_VIDEO_FORMAT_ARGB || format == GST_VIDEO_FORMAT_BGRA)
+ cairoFormat = CAIRO_FORMAT_ARGB32;
+ else
+ cairoFormat = CAIRO_FORMAT_RGB24;
cairo_t* cr = context->platformContext();
cairo_surface_t* src = cairo_image_surface_create_for_data(GST_BUFFER_DATA(m_buffer),
- CAIRO_FORMAT_RGB24,
+ cairoFormat,
width, height,
4 * width);
cairo_save(cr);
- cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
-
- displayWidth *= doublePixelAspectRatioNumerator / doublePixelAspectRatioDenominator;
- displayHeight *= doublePixelAspectRatioDenominator / doublePixelAspectRatioNumerator;
- scale = MIN (rect.width () / displayWidth, rect.height () / displayHeight);
- displayWidth *= scale;
- displayHeight *= scale;
+ // translate and scale the context to correct size
+ cairo_translate(cr, rect.x(), rect.y());
+ cairo_scale(cr, static_cast<double>(rect.width()) / width, static_cast<double>(rect.height()) / height);
- // Calculate gap between border an picture
- gapWidth = (rect.width() - displayWidth) / 2.0;
- gapHeight = (rect.height() - displayHeight) / 2.0;
-
- // paint the rectangle on the context and draw the surface inside.
- cairo_translate(cr, rect.x() + gapWidth, rect.y() + gapHeight);
- cairo_rectangle(cr, 0, 0, rect.width(), rect.height());
- cairo_scale(cr, doublePixelAspectRatioNumerator / doublePixelAspectRatioDenominator,
- doublePixelAspectRatioDenominator / doublePixelAspectRatioNumerator);
- cairo_scale(cr, scale, scale);
+ // And paint it.
cairo_set_source_surface(cr, src, 0, 0);
+ cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_PAD);
+ cairo_rectangle(cr, 0, 0, width, height);
cairo_fill(cr);
cairo_restore(cr);
@@ -688,76 +757,91 @@ static HashSet<String> mimeTypeCache()
static bool typeListInitialized = false;
if (!typeListInitialized) {
- // These subtypes are already beeing supported by WebKit itself
- HashSet<String> ignoredApplicationSubtypes;
- ignoredApplicationSubtypes.add(String("javascript"));
- ignoredApplicationSubtypes.add(String("ecmascript"));
- ignoredApplicationSubtypes.add(String("x-javascript"));
- ignoredApplicationSubtypes.add(String("xml"));
- ignoredApplicationSubtypes.add(String("xhtml+xml"));
- ignoredApplicationSubtypes.add(String("rss+xml"));
- ignoredApplicationSubtypes.add(String("atom+xml"));
- ignoredApplicationSubtypes.add(String("x-ftp-directory"));
- ignoredApplicationSubtypes.add(String("x-java-applet"));
- ignoredApplicationSubtypes.add(String("x-java-bean"));
- ignoredApplicationSubtypes.add(String("x-java-vm"));
- ignoredApplicationSubtypes.add(String("x-shockwave-flash"));
+ // Build a whitelist of mime-types known to be supported by
+ // GStreamer.
+ HashSet<String> handledApplicationSubtypes;
+ handledApplicationSubtypes.add(String("ogg"));
+ handledApplicationSubtypes.add(String("x-3gp"));
+ handledApplicationSubtypes.add(String("vnd.rn-realmedia"));
+ handledApplicationSubtypes.add(String("x-pn-realaudio"));
GList* factories = gst_type_find_factory_get_list();
for (GList* iterator = factories; iterator; iterator = iterator->next) {
GstTypeFindFactory* factory = GST_TYPE_FIND_FACTORY(iterator->data);
GstCaps* caps = gst_type_find_factory_get_caps(factory);
- // Splitting the capability by comma and taking the first part
- // as capability can be something like "audio/x-wavpack, framed=(boolean)false"
- GOwnPtr<gchar> capabilityString(gst_caps_to_string(caps));
- gchar** capability = g_strsplit(capabilityString.get(), ",", 2);
- gchar** mimetype = g_strsplit(capability[0], "/", 2);
-
- // GStreamer plugins can be capable of supporting types which WebKit supports
- // by default. In that case, we should not consider these types supportable by GStreamer.
- // Examples of what GStreamer can support but should not be added:
- // text/plain, text/html, image/jpeg, application/xml
- if (g_str_equal(mimetype[0], "audio") ||
- g_str_equal(mimetype[0], "video") ||
- (g_str_equal(mimetype[0], "application") &&
- !ignoredApplicationSubtypes.contains(String(mimetype[1])))) {
- cache.add(String(capability[0]));
-
- // These formats are supported by GStreamer, but not correctly advertised
- if (g_str_equal(capability[0], "video/x-h264") ||
- g_str_equal(capability[0], "audio/x-m4a")) {
+ if (!caps)
+ continue;
+
+ for (guint structureIndex = 0; structureIndex < gst_caps_get_size(caps); structureIndex++) {
+ GstStructure* structure = gst_caps_get_structure(caps, structureIndex);
+ const gchar* name = gst_structure_get_name(structure);
+ bool cached = false;
+
+ // These formats are supported by GStreamer, but not
+ // correctly advertised.
+ if (g_str_equal(name, "video/x-h264")
+ || g_str_equal(name, "audio/x-m4a")) {
cache.add(String("video/mp4"));
cache.add(String("audio/aac"));
+ cached = true;
}
- if (g_str_equal(capability[0], "video/x-theora"))
+ if (g_str_equal(name, "video/x-theora")) {
cache.add(String("video/ogg"));
+ cached = true;
+ }
- if (g_str_equal(capability[0], "audio/x-wav"))
- cache.add(String("audio/wav"));
+ if (g_str_equal(name, "audio/x-vorbis")) {
+ cache.add(String("audio/ogg"));
+ cached = true;
+ }
- if (g_str_equal(capability[0], "audio/mpeg")) {
- // This is what we are handling: mpegversion=(int)1, layer=(int)[ 1, 3 ]
- gchar** versionAndLayer = g_strsplit(capability[1], ",", 2);
+ if (g_str_equal(name, "audio/x-wav")) {
+ cache.add(String("audio/wav"));
+ cached = true;
+ }
- if (g_str_has_suffix (versionAndLayer[0], "(int)1")) {
- for (int i = 0; versionAndLayer[1][i] != '\0'; i++) {
- if (versionAndLayer[1][i] == '1')
+ if (g_str_equal(name, "audio/mpeg")) {
+ cache.add(String(name));
+ cached = true;
+
+ // This is what we are handling:
+ // mpegversion=(int)1, layer=(int)[ 1, 3 ]
+ gint mpegVersion = 0;
+ if (gst_structure_get_int(structure, "mpegversion", &mpegVersion) && (mpegVersion == 1)) {
+ const GValue* layer = gst_structure_get_value(structure, "layer");
+ if (G_VALUE_TYPE(layer) == GST_TYPE_INT_RANGE) {
+ gint minLayer = gst_value_get_int_range_min(layer);
+ gint maxLayer = gst_value_get_int_range_max(layer);
+ if (minLayer <= 1 <= maxLayer)
cache.add(String("audio/mp1"));
- else if (versionAndLayer[1][i] == '2')
+ if (minLayer <= 2 <= maxLayer)
cache.add(String("audio/mp2"));
- else if (versionAndLayer[1][i] == '3')
+ if (minLayer <= 3 <= maxLayer)
cache.add(String("audio/mp3"));
}
}
+ }
- g_strfreev(versionAndLayer);
+ if (!cached) {
+ // GStreamer plugins can be capable of supporting
+ // types which WebKit supports by default. In that
+ // case, we should not consider these types
+ // supportable by GStreamer. Examples of what
+ // GStreamer can support but should not be added:
+ // text/plain, text/html, image/jpeg,
+ // application/xml
+ gchar** mimetype = g_strsplit(name, "/", 2);
+ if (g_str_equal(mimetype[0], "audio")
+ || g_str_equal(mimetype[0], "video")
+ || (g_str_equal(mimetype[0], "application")
+ && handledApplicationSubtypes.contains(String(mimetype[1]))))
+ cache.add(String(name));
+
+ g_strfreev(mimetype);
}
}
-
- g_strfreev(capability);
- g_strfreev(mimetype);
}
gst_plugin_feature_list_free(factories);
diff --git a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h
index 54da420..6ab8edb 100644
--- a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h
+++ b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h
@@ -29,6 +29,7 @@
#include <cairo.h>
#include <glib.h>
+#include <gst/gst.h>
typedef struct _WebKitVideoSink WebKitVideoSink;
typedef struct _GstBuffer GstBuffer;
@@ -89,7 +90,6 @@ namespace WebCore {
void setSize(const IntSize&);
void loadStateChanged();
- void rateChanged();
void sizeChanged();
void timeChanged();
void volumeChanged();
@@ -109,7 +109,7 @@ namespace WebCore {
static void getSupportedTypes(HashSet<String>&);
static MediaPlayer::SupportsType supportsType(const String& type, const String& codecs);
- static bool isAvailable() { return true; }
+ static bool isAvailable();
void updateStates();
void cancelSeek();
@@ -124,6 +124,8 @@ namespace WebCore {
GstElement* m_playBin;
GstElement* m_videoSink;
GstElement* m_source;
+ GstClockTime m_seekTime;
+ bool m_changingRate;
float m_endTime;
bool m_isEndReached;
MediaPlayer::NetworkState m_networkState;
diff --git a/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp b/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp
index 9a616f4..df25393 100644
--- a/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp
+++ b/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp
@@ -39,8 +39,6 @@
#include "FontDescription.h"
#include "GlyphBuffer.h"
#include <cairo.h>
-#include <unicode/uchar.h>
-#include <unicode/unorm.h>
#include <wtf/MathExtras.h>
namespace WebCore {
diff --git a/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp b/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp
index b5e1a8b..5e0f8e2 100644
--- a/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp
+++ b/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp
@@ -37,21 +37,15 @@ static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE("sink",
GST_PAD_SINK, GST_PAD_ALWAYS,
// CAIRO_FORMAT_RGB24 used to render the video buffers is little/big endian dependant.
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
- GST_STATIC_CAPS(GST_VIDEO_CAPS_BGRx)
+ GST_STATIC_CAPS(GST_VIDEO_CAPS_BGRx ";" GST_VIDEO_CAPS_BGRA)
#else
- GST_STATIC_CAPS(GST_VIDEO_CAPS_xRGB)
+ GST_STATIC_CAPS(GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_ARGB)
#endif
);
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((gchar*) "WebKit video sink",
- (gchar*) "Sink/Video",
- (gchar*) "Sends video data from a GStreamer pipeline to a Cairo surface",
- (gchar*) "Alp Toker <alp@atoker.com>");
-
enum {
REPAINT_REQUESTED,
LAST_SIGNAL
@@ -98,7 +92,9 @@ 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);
+ gst_element_class_set_details_simple(element_class, "WebKit video sink",
+ "Sink/Video", "Sends video data from a GStreamer pipeline to a Cairo surface",
+ "Alp Toker <alp@atoker.com>");
}
static void
@@ -129,11 +125,6 @@ webkit_video_sink_timeout_func(gpointer data)
return FALSE;
}
- if (G_UNLIKELY(!GST_BUFFER_CAPS(buffer))) {
- buffer = gst_buffer_make_metadata_writable(buffer);
- gst_buffer_set_caps(buffer, GST_PAD_CAPS(GST_BASE_SINK_PAD(sink)));
- }
-
g_signal_emit(sink, webkit_video_sink_signals[REPAINT_REQUESTED], 0, buffer);
gst_buffer_unref(buffer);
g_cond_signal(priv->data_cond);
@@ -157,6 +148,71 @@ webkit_video_sink_render(GstBaseSink* bsink, GstBuffer* buffer)
priv->buffer = gst_buffer_ref(buffer);
+ // For the unlikely case where the buffer has no caps, the caps
+ // are implicitely the caps of the pad. This shouldn't happen.
+ if (G_UNLIKELY(!GST_BUFFER_CAPS(buffer))) {
+ buffer = priv->buffer = gst_buffer_make_metadata_writable(priv->buffer);
+ gst_buffer_set_caps(priv->buffer, GST_PAD_CAPS(GST_BASE_SINK_PAD(bsink)));
+ }
+
+ GstCaps *caps = GST_BUFFER_CAPS(buffer);
+ GstVideoFormat format;
+ int width, height;
+ if (G_UNLIKELY(!gst_video_format_parse_caps(caps, &format, &width, &height))) {
+ gst_buffer_unref(buffer);
+ g_mutex_unlock(priv->buffer_mutex);
+ return GST_FLOW_ERROR;
+ }
+
+ // Cairo's ARGB has pre-multiplied alpha while GStreamer's doesn't.
+ // Here we convert to Cairo's ARGB.
+ if (format == GST_VIDEO_FORMAT_ARGB || format == GST_VIDEO_FORMAT_BGRA) {
+ // Because GstBaseSink::render() only owns the buffer reference in the
+ // method scope we can't use gst_buffer_make_writable() here. Also
+ // The buffer content should not be changed here because the same buffer
+ // could be passed multiple times to this method (in theory)
+ GstBuffer *newBuffer = gst_buffer_try_new_and_alloc(GST_BUFFER_SIZE(buffer));
+
+ // Check if allocation failed
+ if (G_UNLIKELY(!newBuffer)) {
+ gst_buffer_unref(buffer);
+ g_mutex_unlock(priv->buffer_mutex);
+ return GST_FLOW_ERROR;
+ }
+
+ gst_buffer_copy_metadata(newBuffer, buffer, (GstBufferCopyFlags) GST_BUFFER_COPY_ALL);
+
+ // We don't use Color::premultipliedARGBFromColor() here because
+ // one function call per video pixel is just too expensive:
+ // For 720p/PAL for example this means 1280*720*25=23040000
+ // function calls per second!
+ unsigned short alpha;
+ const guint8 *source = GST_BUFFER_DATA(buffer);
+ guint8 *destination = GST_BUFFER_DATA(newBuffer);
+
+ for (int x = 0; x < height; x++) {
+ for (int y = 0; y < width; y++) {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ alpha = source[3];
+ destination[0] = (source[0] * alpha + 128) / 255;
+ destination[1] = (source[1] * alpha + 128) / 255;
+ destination[2] = (source[2] * alpha + 128) / 255;
+ destination[3] = alpha;
+#else
+ alpha = source[0];
+ destination[0] = alpha;
+ destination[1] = (source[1] * alpha + 128) / 255;
+ destination[2] = (source[2] * alpha + 128) / 255;
+ destination[3] = (source[3] * alpha + 128) / 255;
+#endif
+ source += 4;
+ destination += 4;
+ }
+ }
+ gst_buffer_unref(buffer);
+ buffer = priv->buffer = newBuffer;
+ }
+
// Use HIGH_IDLE+20 priority, like Gtk+ for redrawing operations.
priv->timeout_id = g_timeout_add_full(G_PRIORITY_HIGH_IDLE + 20, 0,
webkit_video_sink_timeout_func,