2007-12-04 Pierre-Luc Beaudoin <pierre-luc.beaudoin@collabora.co.uk>
authoralp@webkit.org <alp@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 4 Dec 2007 16:50:38 +0000 (16:50 +0000)
committeralp@webkit.org <alp@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 4 Dec 2007 16:50:38 +0000 (16:50 +0000)
        Reviewed by Alp Toker.

        http://bugs.webkit.org/show_bug.cgi?id=16145
        [gtk] Implement media support in GTK backend

        This implements the media tags of HTML5 on the GTK+ port based on the
        Mac port. Media tests pass although some tests required a small modifications:
        As per the HTML5 spec, the loading of the clip should start only after
        all script are done.  But in the case of the tests, the loading starts
        before some callbacks get registered.  This has been discussed with
        Antti Koivisto, and the tests should be updated.

        There is only one known issue: one time in 10 tries, loading a buffered
        clip will never end buffering.  The buffering will stall at 97% for no
        apparent reasons.  Reloading the page usually works around the problem.
        On the GStreamer side, some codecs don't return correct values, therefore
        they make the tests fail.  For instance H.264 will make the video-end
        test fail.  This should be fixed in GStreamer 0.10.15.

        This version displays video in a pop up window.  A place holder is drawn
        on the page where the video should appear.

        By default, it is turned off in WebCore.pro until GStreamer/X
        detection issues are sorted out.

        * WebCore.pro:
        Disable video for now
        * platform/Logging.cpp:
        (WebCore::):
        * platform/Logging.h:
        Added a logging level for Media stuff
        * platform/graphics/Movie.cpp:
        * platform/graphics/gtk/MoviePrivateGStreamer.cpp: Added.
        (WebCore::moviePrivateErrorCallback):
        (WebCore::moviePrivateEOSCallback):
        (WebCore::moviePrivateStateCallback):
        (WebCore::moviePrivateBufferingCallback):
        (WebCore::moviePrivateWindowIDCallback):
        (WebCore::MoviePrivate::MoviePrivate):
        (WebCore::MoviePrivate::~MoviePrivate):
        (WebCore::MoviePrivate::load):
        (WebCore::MoviePrivate::play):
        (WebCore::MoviePrivate::pause):
        (WebCore::MoviePrivate::duration):
        (WebCore::MoviePrivate::currentTime):
        (WebCore::MoviePrivate::seek):
        (WebCore::MoviePrivate::setEndTime):
        (WebCore::MoviePrivate::addCuePoint):
        (WebCore::MoviePrivate::removeCuePoint):
        (WebCore::MoviePrivate::clearCuePoints):
        (WebCore::MoviePrivate::startCuePointTimerIfNeeded):
        (WebCore::MoviePrivate::cancelSeek):
        (WebCore::MoviePrivate::cuePointTimerFired):
        (WebCore::MoviePrivate::paused):
        (WebCore::MoviePrivate::seeking):
        (WebCore::MoviePrivate::naturalSize):
        (WebCore::MoviePrivate::hasVideo):
        (WebCore::MoviePrivate::setVolume):
        (WebCore::MoviePrivate::setMuted):
        (WebCore::MoviePrivate::setRate):
        (WebCore::MoviePrivate::dataRate):
        (WebCore::MoviePrivate::networkState):
        (WebCore::MoviePrivate::readyState):
        (WebCore::MoviePrivate::maxTimeBuffered):
        (WebCore::MoviePrivate::maxTimeSeekable):
        (WebCore::MoviePrivate::maxTimeLoaded):
        (WebCore::MoviePrivate::bytesLoaded):
        (WebCore::MoviePrivate::totalBytesKnown):
        (WebCore::MoviePrivate::totalBytes):
        (WebCore::MoviePrivate::cancelLoad):
        (WebCore::MoviePrivate::updateStates):
        (WebCore::MoviePrivate::loadStateChanged):
        (WebCore::MoviePrivate::rateChanged):
        (WebCore::MoviePrivate::sizeChanged):
        (WebCore::MoviePrivate::timeChanged):
        (WebCore::MoviePrivate::volumeChanged):
        (WebCore::MoviePrivate::didEnd):
        (WebCore::MoviePrivate::loadingFailed):
        (WebCore::MoviePrivate::setRect):
        (WebCore::MoviePrivate::setVisible):
        (WebCore::MoviePrivate::paint):
        (WebCore::MoviePrivate::getSupportedTypes):
        (WebCore::MoviePrivate::createGSTPlayBin):
        * platform/graphics/gtk/MoviePrivateGStreamer.h: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@28389 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebCore/ChangeLog
WebCore/WebCore.pro
WebCore/platform/Logging.cpp
WebCore/platform/Logging.h
WebCore/platform/graphics/Movie.cpp
WebCore/platform/graphics/gtk/MoviePrivateGStreamer.cpp [new file with mode: 0644]
WebCore/platform/graphics/gtk/MoviePrivateGStreamer.h [new file with mode: 0644]

index 3b8f09c2d61031871f5c84056cf2de1d363bfadc..2b9bf6598d785c4f16096e7807f10b637fd7906f 100644 (file)
@@ -1,3 +1,90 @@
+2007-12-04  Pierre-Luc Beaudoin  <pierre-luc.beaudoin@collabora.co.uk>
+
+        Reviewed by Alp Toker.
+
+        http://bugs.webkit.org/show_bug.cgi?id=16145
+        [gtk] Implement media support in GTK backend
+
+        This implements the media tags of HTML5 on the GTK+ port based on the 
+        Mac port. Media tests pass although some tests required a small modifications: 
+        As per the HTML5 spec, the loading of the clip should start only after 
+        all script are done.  But in the case of the tests, the loading starts 
+        before some callbacks get registered.  This has been discussed with 
+        Antti Koivisto, and the tests should be updated.
+
+        There is only one known issue: one time in 10 tries, loading a buffered 
+        clip will never end buffering.  The buffering will stall at 97% for no 
+        apparent reasons.  Reloading the page usually works around the problem.
+        On the GStreamer side, some codecs don't return correct values, therefore
+        they make the tests fail.  For instance H.264 will make the video-end 
+        test fail.  This should be fixed in GStreamer 0.10.15.
+
+        This version displays video in a pop up window.  A place holder is drawn
+        on the page where the video should appear.
+
+        By default, it is turned off in WebCore.pro until GStreamer/X
+        detection issues are sorted out.
+
+        * WebCore.pro:
+        Disable video for now
+        * platform/Logging.cpp:
+        (WebCore::):
+        * platform/Logging.h:
+        Added a logging level for Media stuff
+        * platform/graphics/Movie.cpp:
+        * platform/graphics/gtk/MoviePrivateGStreamer.cpp: Added.
+        (WebCore::moviePrivateErrorCallback):
+        (WebCore::moviePrivateEOSCallback):
+        (WebCore::moviePrivateStateCallback):
+        (WebCore::moviePrivateBufferingCallback):
+        (WebCore::moviePrivateWindowIDCallback):
+        (WebCore::MoviePrivate::MoviePrivate):
+        (WebCore::MoviePrivate::~MoviePrivate):
+        (WebCore::MoviePrivate::load):
+        (WebCore::MoviePrivate::play):
+        (WebCore::MoviePrivate::pause):
+        (WebCore::MoviePrivate::duration):
+        (WebCore::MoviePrivate::currentTime):
+        (WebCore::MoviePrivate::seek):
+        (WebCore::MoviePrivate::setEndTime):
+        (WebCore::MoviePrivate::addCuePoint):
+        (WebCore::MoviePrivate::removeCuePoint):
+        (WebCore::MoviePrivate::clearCuePoints):
+        (WebCore::MoviePrivate::startCuePointTimerIfNeeded):
+        (WebCore::MoviePrivate::cancelSeek):
+        (WebCore::MoviePrivate::cuePointTimerFired):
+        (WebCore::MoviePrivate::paused):
+        (WebCore::MoviePrivate::seeking):
+        (WebCore::MoviePrivate::naturalSize):
+        (WebCore::MoviePrivate::hasVideo):
+        (WebCore::MoviePrivate::setVolume):
+        (WebCore::MoviePrivate::setMuted):
+        (WebCore::MoviePrivate::setRate):
+        (WebCore::MoviePrivate::dataRate):
+        (WebCore::MoviePrivate::networkState):
+        (WebCore::MoviePrivate::readyState):
+        (WebCore::MoviePrivate::maxTimeBuffered):
+        (WebCore::MoviePrivate::maxTimeSeekable):
+        (WebCore::MoviePrivate::maxTimeLoaded):
+        (WebCore::MoviePrivate::bytesLoaded):
+        (WebCore::MoviePrivate::totalBytesKnown):
+        (WebCore::MoviePrivate::totalBytes):
+        (WebCore::MoviePrivate::cancelLoad):
+        (WebCore::MoviePrivate::updateStates):
+        (WebCore::MoviePrivate::loadStateChanged):
+        (WebCore::MoviePrivate::rateChanged):
+        (WebCore::MoviePrivate::sizeChanged):
+        (WebCore::MoviePrivate::timeChanged):
+        (WebCore::MoviePrivate::volumeChanged):
+        (WebCore::MoviePrivate::didEnd):
+        (WebCore::MoviePrivate::loadingFailed):
+        (WebCore::MoviePrivate::setRect):
+        (WebCore::MoviePrivate::setVisible):
+        (WebCore::MoviePrivate::paint):
+        (WebCore::MoviePrivate::getSupportedTypes):
+        (WebCore::MoviePrivate::createGSTPlayBin):
+        * platform/graphics/gtk/MoviePrivateGStreamer.h: Added.
+
 2007-12-04  Holger Hans Peter Freyther <holger.freyther@trolltech.com>
 
         Reviewed by Simon.
index ca871c66915ea349ac06419319beb9e7c5fc0996..925fb5c7631f7b3438bb117e8d4a16e153580fa1 100644 (file)
@@ -74,6 +74,7 @@ gtk-port:!contains(DEFINES, ENABLE_XSLT=.): DEFINES += ENABLE_XSLT=1
 #!contains(DEFINES, ENABLE_XBL=.): DEFINES += ENABLE_XBL=1
 qt-port: !contains(DEFINES, ENABLE_SVG=.): DEFINES += ENABLE_SVG=1
 gtk-port:DEFINES += ENABLE_SVG=1
+DEFINES += ENABLE_VIDEO=0
 
 DEFINES += WTF_CHANGES=1
 
@@ -1080,6 +1081,38 @@ contains(DEFINES, ENABLE_ICONDATABASE=1) {
         loader/icon/IconDatabaseNone.cpp
 }
 
+contains(DEFINES, ENABLE_VIDEO=1) {
+    FEATURE_DEFINES_JAVASCRIPT += ENABLE_VIDEO=1
+
+    IDL_BINDINGS += \
+        html/HTMLAudioElement.idl \
+        html/HTMLMediaElement.idl \
+        html/HTMLSourceElement.idl \
+        html/HTMLVideoElement.idl \
+        html/MediaError.idl \
+        html/TimeRanges.idl \
+        html/VoidCallback.idl 
+
+    SOURCES += \
+        html/HTMLAudioElement.cpp \
+        html/HTMLMediaElement.cpp \
+        html/HTMLSourceElement.cpp \
+        html/HTMLVideoElement.cpp \
+        html/TimeRanges.cpp \
+        platform/graphics/Movie.cpp \
+        rendering/RenderVideo.cpp \
+        rendering/RenderMedia.cpp \
+        bindings/js/JSHTMLAudioElementConstructor.cpp 
+
+    gtk-port {
+        SOURCES += \
+            platform/graphics/gtk/MoviePrivateGStreamer.cpp
+
+        PKGCONFIG += gstreamer-0.10 gstreamer-plugins-base-0.10 gnome-vfs-2.0
+        LIBS += -lgstinterfaces-0.10 -lgstbase-0.10 -lgstvideo-0.10
+    }
+}
+
 contains(DEFINES, ENABLE_XPATH=1) {
     FEATURE_DEFINES_JAVASCRIPT += ENABLE_XPATH=1
 
index 5d756a0125d5c8d8f927bb3b128e94ec2530e5cf..2e57f9c8cfb774b400c9023459ff2cf1e16d971e 100644 (file)
@@ -54,4 +54,6 @@ WTFLogChannel LogFTP =               { 0x00200000, "WebCoreLogLevel", WTFLogChan
 WTFLogChannel LogThreading =         { 0x00400000, "WebCoreLogLevel", WTFLogChannelOff };
 WTFLogChannel LogStorageAPI =        { 0x00800000, "WebCoreLogLevel", WTFLogChannelOff };
 
+WTFLogChannel LogMedia =             { 0x01000000, "WebCoreLogLevel", WTFLogChannelOff };
+
 }
index 2a2dbcc7393ae9cab0eeeeda2f241c48d476a738..69e164801c27963df823c1815b4e51da1729da65 100644 (file)
@@ -52,6 +52,7 @@ namespace WebCore {
     extern WTFLogChannel LogFTP;
     extern WTFLogChannel LogThreading;
     extern WTFLogChannel LogStorageAPI;
+    extern WTFLogChannel LogMedia;
 
     void InitializeLoggingChannelsIfNecessary();
 }
index 3352f431a56cae0bc463504dc9dfe741a980d318..98cd7e3f774b33b39f547525730272d3db27d014 100644 (file)
@@ -33,6 +33,8 @@
 
 #if PLATFORM(MAC)
 #include "MoviePrivateQTKit.h"
+#elif PLATFORM(GTK)
+#include "MoviePrivateGStreamer.h"
 #endif
 
 namespace WebCore {
diff --git a/WebCore/platform/graphics/gtk/MoviePrivateGStreamer.cpp b/WebCore/platform/graphics/gtk/MoviePrivateGStreamer.cpp
new file mode 100644 (file)
index 0000000..915ee96
--- /dev/null
@@ -0,0 +1,667 @@
+/*
+ * Copyright (C) 2007 Apple Inc.  All rights reserved.
+ * Copyright (C) 2007 Collabora Ltd.  All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * aint with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#if ENABLE(VIDEO)
+
+#include "MoviePrivateGStreamer.h"
+
+#include "CString.h"
+#include "CString.h"
+#include "GraphicsContext.h"
+#include "IntRect.h"
+#include "KURL.h"
+#include "MIMETypeRegistry.h"
+#include "Movie.h"
+#include "NotImplemented.h"
+#include "ScrollView.h"
+#include "Widget.h"
+
+#include <gdk/gdkx.h>
+#include <gst/base/gstbasesrc.h>
+#include <gst/gst.h>
+#include <gst/interfaces/mixer.h>
+#include <gst/interfaces/xoverlay.h>
+#include <gst/video/video.h>
+#include <libgnomevfs/gnome-vfs.h>
+#include <limits>
+#include <math.h>
+
+namespace WebCore {
+
+// Local var, for strange reasons, a member var will make the app crash
+GdkWindow* m_window;
+
+gboolean moviePrivateErrorCallback(GstBus* bus, GstMessage* message, gpointer data)
+{
+    if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_ERROR)
+    {
+        GError* err;
+        gchar* debug;
+
+        gst_message_parse_error(message, &err, &debug);
+        if (err->code == 3) {
+            LOG_VERBOSE(Media, "File not found");
+            MoviePrivate* mp = reinterpret_cast<MoviePrivate*>(data);
+            if (mp)
+                mp->loadingFailed();
+        } else {
+            LOG_VERBOSE(Media, "Error: %d, %s", err->code,  err->message);
+            g_error_free(err);
+            g_free(debug);
+        }
+    }
+    return true;
+}
+
+gboolean moviePrivateEOSCallback(GstBus* bus, GstMessage* message, gpointer data)
+{
+    if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_EOS)
+    {
+        LOG_VERBOSE(Media, "END OF STREAM");
+        MoviePrivate* mp = reinterpret_cast<MoviePrivate*>(data);
+        mp->didEnd();
+    }
+    return true;
+}
+
+gboolean moviePrivateStateCallback(GstBus* bus, GstMessage* message, gpointer data)
+{
+    if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_STATE_CHANGED)
+    {
+        MoviePrivate* mp = reinterpret_cast<MoviePrivate*>(data);
+        mp->updateStates();
+    }
+    return true;
+}
+
+gboolean moviePrivateBufferingCallback(GstBus* bus, GstMessage* message, gpointer data)
+{
+    if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_BUFFERING)
+    {
+        gint percent = 0;
+        gst_message_parse_buffering(message, &percent);
+        LOG_VERBOSE(Media, "Buffering %d", percent);
+    }
+    return true;
+}
+
+// Setup the overlay when the message is received
+GstBusSyncReply moviePrivateWindowIDCallback(GstBus* bus, GstMessage* message, gpointer data)
+{
+    if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_ELEMENT)
+        return GST_BUS_PASS;
+
+    if (!gst_structure_has_name(message->structure, "prepare-xwindow-id"))
+        return GST_BUS_PASS;
+
+    // Disabled for now as it is annoying: the overlay if done for the whole web page
+    // not only the specified Rect...
+
+    //gst_x_overlay_set_xwindow_id(
+    //    GST_X_OVERLAY(GST_MESSAGE_SRC(message)),
+    //    GDK_WINDOW_XID(m_window));
+    return GST_BUS_DROP;
+}
+
+MoviePrivate::MoviePrivate(Movie* movie)
+    : m_movie(movie)
+    , m_playBin(0)
+    , m_video_sink(0)
+    , m_source(0)
+    , m_seekTo(-1)
+    , m_rate(1.0f)
+    , m_endTime(std::numeric_limits<float>::infinity())
+    , m_endReached(false)
+    , m_oldVolume(0.5f)
+    , m_previousTimeCueTimerFired(0)
+    , m_networkState(Movie::Empty)
+    , m_readyState(Movie::DataUnavailable)
+    , m_startedPlaying(false)
+    , m_isStreaming(false)
+{
+    //FIXME We should pass the arguments from the command line
+    gst_init(0, NULL);
+}
+
+MoviePrivate::~MoviePrivate()
+{
+    gst_element_set_state(m_playBin, GST_STATE_NULL);
+    gst_object_unref(GST_OBJECT(m_playBin));
+}
+
+void MoviePrivate::load(String url)
+{
+    LOG_VERBOSE(Media, "LOAD %s", url.utf8().data());
+    if (m_networkState != Movie::Loading) {
+        m_networkState = Movie::Loading;
+        m_movie->networkStateChanged();
+    }
+    if (m_readyState != Movie::DataUnavailable) {
+        m_readyState = Movie::DataUnavailable;
+        m_movie->readyStateChanged();
+    }
+
+    cancelSeek();
+    createGSTPlayBin(url);
+    pause();
+}
+
+void MoviePrivate::play()
+{
+    cancelSeek();
+    LOG_VERBOSE(Media, "PLAY");
+    // When end reached, rewind for Test video-seek-past-end-playing
+    if (m_endReached)
+        seek(0);
+    m_endReached = false;
+
+    gst_element_set_state(m_playBin, GST_STATE_PLAYING);
+    m_startedPlaying = true;
+}
+
+void MoviePrivate::pause()
+{
+    cancelSeek();
+    LOG_VERBOSE(Media, "PAUSE");
+    gst_element_set_state(m_playBin, GST_STATE_PAUSED);
+    m_startedPlaying = false;
+}
+
+float MoviePrivate::duration()
+{
+    if (!m_playBin)
+        return 0.0;
+
+    GstFormat fmt = GST_FORMAT_TIME;
+    gint64 len = 0;
+
+    if (gst_element_query_duration(m_playBin, &fmt, &len))
+        LOG_VERBOSE(Media, "duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(len));
+    else
+        LOG_VERBOSE(Media, "duration query failed ");
+
+    if ((GstClockTime)len == GST_CLOCK_TIME_NONE) {
+        m_isStreaming = true;
+        return std::numeric_limits<float>::infinity();
+    }
+    return (float) (len / 1000000000.0);
+    //FIXME: handle 3.14.9.5 properly
+}
+
+float MoviePrivate::currentTime() const
+{
+    if (!m_playBin)
+        return 0;
+    if (seeking())
+        return m_seekTo;
+    // Necessary as sometimes, gstreamer return 0:00 at the EOS
+    if (m_endReached)
+        return m_endTime;
+
+    float ret;
+    GstQuery* query;
+    gboolean res;
+
+    query = gst_query_new_position(GST_FORMAT_TIME);
+    res = gst_element_query(m_playBin, query);
+    if (res) {
+        gint64 position;
+        gst_query_parse_position(query, NULL, &position);
+        ret = (float) (position / 1000000000.0);
+        LOG_VERBOSE(Media, "currentTime %" GST_TIME_FORMAT, GST_TIME_ARGS(position));
+    } else {
+        LOG_VERBOSE(Media, "position query failed...");
+        ret = 0.0;
+    }
+    gst_query_unref(query);
+    return ret;
+}
+
+void MoviePrivate::seek(float time)
+{
+    cancelSeek();
+    GstClockTime sec = (GstClockTime)(time * GST_SECOND);
+
+    if (!m_playBin)
+        return;
+
+    if (m_isStreaming)
+        return;
+
+    LOG_VERBOSE(Media, "seek: %" GST_TIME_FORMAT, GST_TIME_ARGS(sec));
+    // FIXME: What happens when the seeked position is not available?
+    if (!gst_element_seek( m_playBin, m_rate,
+            GST_FORMAT_TIME,
+            (GstSeekFlags)(GST_SEEK_FLAG_FLUSH),
+            GST_SEEK_TYPE_SET, sec,
+            GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
+        LOG_VERBOSE(Media, "seek to %f failed", time);
+}
+
+void MoviePrivate::setEndTime(float time)
+{
+    if (!m_playBin)
+        return;
+    if (m_isStreaming)
+        return;
+    if (m_endTime != time) {
+        m_endTime = time;
+        GstClockTime start = (GstClockTime)(currentTime() * GST_SECOND);
+        GstClockTime end   = (GstClockTime)(time * GST_SECOND);
+        LOG_VERBOSE(Media, "setEndTime: %" GST_TIME_FORMAT, GST_TIME_ARGS(end));
+        // FIXME: What happens when the seeked position is not available?
+        if (!gst_element_seek( m_playBin, m_rate,
+                GST_FORMAT_TIME,
+                (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE),
+                GST_SEEK_TYPE_SET, start,
+                GST_SEEK_TYPE_SET, end ))
+            LOG_VERBOSE(Media, "seek to %f failed", time);
+    }
+}
+
+void MoviePrivate::addCuePoint(float time)
+{
+    notImplemented();
+}
+
+void MoviePrivate::removeCuePoint(float time)
+{
+    notImplemented();
+}
+
+void MoviePrivate::clearCuePoints()
+{
+    notImplemented();
+}
+
+void MoviePrivate::startCuePointTimerIfNeeded()
+{
+    notImplemented();
+}
+
+void MoviePrivate::cancelSeek()
+{
+    if (m_seekTo > -1)
+        m_seekTo = -1;
+}
+
+void MoviePrivate::cuePointTimerFired(Timer<MoviePrivate>*)
+{
+    notImplemented();
+}
+
+bool MoviePrivate::paused() const
+{
+    return !m_startedPlaying;
+}
+
+bool MoviePrivate::seeking() const
+{
+     if (!m_playBin)
+        return false;
+    return m_seekTo >= 0;
+}
+
+// Returns the size of the video
+IntSize MoviePrivate::naturalSize()
+{
+    int x = 0, y = 0;
+    if (hasVideo()) {
+        GstPad* pad = NULL;
+        pad = gst_element_get_pad(m_video_sink, "sink");
+        if (pad)
+            gst_video_get_size(GST_PAD(pad), &x, &y);
+    }
+    return IntSize(x, y);
+}
+
+bool MoviePrivate::hasVideo()
+{
+    gint currentVideo = -1;
+    if (m_playBin)
+        g_object_get(G_OBJECT(m_playBin), "current-video", &currentVideo, NULL);
+    return currentVideo > -1;
+}
+
+void MoviePrivate::setVolume(float volume)
+{
+    m_oldVolume = volume;
+    LOG_VERBOSE(Media, "Volume to %f", volume);
+    setMuted(false);
+}
+
+void MoviePrivate::setMuted(bool b)
+{
+    if (!m_playBin) 
+        return;
+
+    if (b) {
+        g_object_get(G_OBJECT(m_playBin), "volume", &m_oldVolume, NULL);
+        g_object_set(G_OBJECT(m_playBin), "volume", (double)0.0, NULL);
+    } else {
+        g_object_set(G_OBJECT(m_playBin), "volume", m_oldVolume, NULL);
+    }
+}
+
+void MoviePrivate::setRate(float rate)
+{
+    if (rate == 0.0) {
+        gst_element_set_state(m_playBin, GST_STATE_PAUSED);
+        return;
+    }
+    if (m_isStreaming)
+        return;
+
+    m_rate = rate;
+    LOG_VERBOSE(Media, "Set Rate to %f", rate);
+    if (!gst_element_seek(m_playBin, rate,
+            GST_FORMAT_TIME,
+            (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE),
+            GST_SEEK_TYPE_SET, (GstClockTime) (currentTime() * GST_SECOND),
+            GST_SEEK_TYPE_SET, (GstClockTime) (m_endTime * GST_SECOND)))
+        LOG_VERBOSE(Media, "Set Rate to %f failed", rate);
+}
+
+int MoviePrivate::dataRate() const
+{
+    notImplemented();
+    return 1;
+}
+
+Movie::NetworkState MoviePrivate::networkState()
+{
+    return m_networkState;
+}
+
+Movie::ReadyState MoviePrivate::readyState()
+{
+    return m_readyState;
+}
+
+float MoviePrivate::maxTimeBuffered()
+{
+    //TODO
+    LOG_VERBOSE(Media, "maxTimeBuffered");
+    // rtsp streams are not buffered
+    return m_isStreaming ? 0 : maxTimeLoaded();
+}
+
+float MoviePrivate::maxTimeSeekable()
+{
+    //TODO
+    LOG_VERBOSE(Media, "maxTimeSeekable");
+    if (m_isStreaming)
+        return std::numeric_limits<float>::infinity();
+    // infinite duration means live stream
+    return maxTimeLoaded();
+}
+
+float MoviePrivate::maxTimeLoaded()
+{
+    //TODO
+    LOG_VERBOSE(Media, "maxTimeLoaded");
+    notImplemented();
+    return duration();
+}
+
+unsigned MoviePrivate::bytesLoaded()
+{
+    LOG_VERBOSE(Media, "bytesLoaded");
+    /*if (!m_playBin)
+        return 0;
+    float dur = duration();
+    float maxTime = maxTimeLoaded();
+    if (!dur)
+        return 0;*/
+    return 1;//totalBytes() * maxTime / dur;
+}
+
+bool MoviePrivate::totalBytesKnown()
+{
+    LOG_VERBOSE(Media, "totalBytesKnown");
+    return totalBytes() > 0;
+}
+
+unsigned MoviePrivate::totalBytes()
+{
+
+    LOG_VERBOSE(Media, "totalBytes");
+    if (!m_playBin)
+        return 0;
+
+    if (!m_source)
+        return 0;
+    /*GstFormat fmt = GST_FORMAT_BYTES;
+
+
+    if (gst_element_query_duration(m_playBin, &fmt, &len)) 
+        LOG_VERBOSE(Media, "totalBytes: %d", (len));
+    else {
+        LOG_VERBOSE(Media, "totalBytes failed ");
+        return 0;
+    }*/
+    guint64 len;
+    if (G_OBJECT_TYPE_NAME(m_source) == "GstFileSrc") {
+        // FIXME: Get file size some other way, maybe with the fd property
+        len = 0;
+    } else if (G_OBJECT_TYPE_NAME(m_source) == "GstGnomeVFSSrc") {
+        /*
+        GnomeVFSHandle* handle;
+        g_object_get(m_source, "handle", handle, NULL);
+        GnomeVFSFileInfo info;
+        gnome_vfs_get_file_info_from_handle(handle, &info,
+            GNOME_VFS_FILE_INFO_DEFAULT);
+        len = info.size;
+        */
+    }
+    return 100;
+}
+
+void MoviePrivate::cancelLoad()
+{
+    notImplemented();
+}
+
+void MoviePrivate::updateStates()
+{
+    // There is no (known) way to get such level of information about
+    // the state of GStreamer, therefore, when in PAUSED state,
+    // we are sure we can display the first frame and go to play
+
+    Movie::NetworkState oldNetworkState = m_networkState;
+    Movie::ReadyState oldReadyState = m_readyState;
+    GstState state;
+    GstState pending;
+
+    if (!m_playBin)
+        return;
+
+    if (m_seekTo >= currentTime())
+        m_seekTo = -1;
+
+    GstStateChangeReturn ret = gst_element_get_state (m_playBin,
+        &state, &pending, 250 * GST_NSECOND);
+
+    switch(ret) {
+    case GST_STATE_CHANGE_SUCCESS:
+        LOG_VERBOSE(Media, "State: %s, pending: %s",
+            gst_element_state_get_name(state),
+            gst_element_state_get_name(pending));
+
+        if (state == GST_STATE_READY) {
+            m_readyState = Movie::CanPlayThrough;
+        } else if (state == GST_STATE_PAUSED) {
+            m_readyState = Movie::CanPlayThrough;
+        }
+        if (m_networkState < Movie::Loaded)
+            m_networkState = Movie::Loaded;
+
+        g_object_get(m_playBin, "source", &m_source, NULL);
+        if (!m_source)
+            LOG_VERBOSE(Media, "m_source is NULL");
+        break;
+    case GST_STATE_CHANGE_ASYNC:
+        LOG_VERBOSE(Media, "Async: State: %s, pending: %s",
+            gst_element_state_get_name(state),
+            gst_element_state_get_name(pending));
+        // Change in progress
+        return;
+        break;
+    case GST_STATE_CHANGE_NO_PREROLL:
+        LOG_VERBOSE(Media, "No preroll: State: %s, pending: %s",
+            gst_element_state_get_name(state),
+            gst_element_state_get_name(pending));
+        if (state == GST_STATE_READY) {
+            m_readyState = Movie::CanPlay;
+        } else if (state == GST_STATE_PAUSED) {
+            m_readyState = Movie::CanPlay;
+        }
+        if (m_networkState < Movie::LoadedMetaData)
+            m_networkState = Movie::LoadedMetaData;
+        break;
+    default:
+        LOG_VERBOSE(Media, "Else : %d", ret);
+        break;
+    }
+
+    if (seeking())
+        m_readyState = Movie::DataUnavailable;
+
+    if (m_networkState != oldNetworkState) {
+        LOG_VERBOSE(Media, "Network State Changed from %u to %u",
+            oldNetworkState, m_networkState);
+        m_movie->networkStateChanged();
+    }
+    if (m_readyState != oldReadyState) {
+        LOG_VERBOSE(Media, "Ready State Changed from %u to %u",
+            oldReadyState, m_readyState);
+        m_movie->readyStateChanged();
+    }
+}
+
+void MoviePrivate::loadStateChanged()
+{
+    updateStates();
+}
+
+void MoviePrivate::rateChanged()
+{
+    updateStates();
+}
+
+void MoviePrivate::sizeChanged()
+{
+    notImplemented();
+}
+
+void MoviePrivate::timeChanged()
+{
+    updateStates();
+    m_movie->timeChanged();
+}
+
+void MoviePrivate::volumeChanged()
+{
+    m_movie->volumeChanged();
+}
+
+void MoviePrivate::didEnd()
+{
+    m_endReached = true;
+    pause();
+    timeChanged();
+}
+
+void MoviePrivate::loadingFailed()
+{
+    if (m_networkState != Movie::LoadFailed) {
+        m_networkState = Movie::LoadFailed;
+        m_movie->networkStateChanged();
+    }
+    if (m_readyState != Movie::DataUnavailable) {
+        m_readyState = Movie::DataUnavailable;
+        m_movie->readyStateChanged();
+    }
+}
+
+void MoviePrivate::setRect(const IntRect& r)
+{
+    notImplemented();
+}
+
+void MoviePrivate::setVisible(bool b)
+{
+    notImplemented();
+}
+
+void MoviePrivate::paint(GraphicsContext* p, const IntRect& r)
+{
+    // FIXME do the real thing
+    if (p->paintingDisabled())
+        return;
+    // For now draw a placeholder rectangle
+    p->drawRect(r);
+    // This will be used to draw with XOverlay
+    m_window = p->gdkDrawable();
+}
+
+void MoviePrivate::getSupportedTypes(HashSet<String>& types)
+{
+    // FIXME do the real thing
+    notImplemented();
+    types.add(String("video/x-theora+ogg"));
+}
+
+void MoviePrivate::createGSTPlayBin(String url)
+{
+    GstElement* audio_sink;
+    GstBus* bus;
+
+    m_playBin = gst_element_factory_make("playbin", "play");
+
+    bus = gst_pipeline_get_bus(GST_PIPELINE(m_playBin));
+
+    gst_bus_set_sync_handler(bus, (GstBusSyncHandler) moviePrivateWindowIDCallback, m_playBin);
+    gst_bus_add_signal_watch(bus);
+
+    g_signal_connect(bus, "message::error", G_CALLBACK(moviePrivateErrorCallback), this);
+    g_signal_connect(bus, "message::eos", G_CALLBACK(moviePrivateEOSCallback), this);
+    g_signal_connect(bus, "message::prepare-xwindow-id", G_CALLBACK(moviePrivateWindowIDCallback), NULL);
+    g_signal_connect(bus, "message::state-changed", G_CALLBACK(moviePrivateStateCallback), this);
+    g_signal_connect(bus, "message::buffering", G_CALLBACK(moviePrivateBufferingCallback), this);
+
+    gst_object_unref(bus);
+
+    g_object_set(G_OBJECT(m_playBin), "uri", url.utf8().data(), NULL);
+    audio_sink = gst_element_factory_make("gconfaudiosink", NULL);
+    m_video_sink = gst_element_factory_make("gconfvideosink", NULL);
+
+    g_object_set(m_playBin, "audio-sink", audio_sink, NULL);
+    g_object_set(m_playBin, "video-sink", m_video_sink, NULL);
+
+    setVolume(m_oldVolume);
+}
+
+}
+
+#endif
+
diff --git a/WebCore/platform/graphics/gtk/MoviePrivateGStreamer.h b/WebCore/platform/graphics/gtk/MoviePrivateGStreamer.h
new file mode 100644 (file)
index 0000000..b1859fe
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2007 Apple Inc.  All rights reserved.
+ * Copyright (C) 2007 Collabora Ltd. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * aint with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef MoviePrivateGStreamer_h
+#define MoviePrivateGStreamer_h
+
+#if ENABLE(VIDEO)
+
+#include "Movie.h"
+#include "Timer.h"
+#include "wtf/Noncopyable.h"
+
+#include <gtk/gtk.h>
+
+typedef struct _GstElement GstElement;
+typedef struct _GstMessage GstMessage;
+typedef struct _GstBus GstBus;
+
+namespace WebCore {
+
+    class GraphicsContext;
+    class IntSize;
+    class IntRect;
+    class String;
+
+    gboolean moviePrivateErrorCallback(GstBus* bus, GstMessage* message, gpointer data);
+    gboolean moviePrivateEOSCallback(GstBus* bus, GstMessage* message, gpointer data);
+    gboolean moviePrivateStateCallback(GstBus* bus, GstMessage* message, gpointer data);
+
+    class MoviePrivate : Noncopyable
+    {
+    friend gboolean moviePrivateErrorCallback(GstBus* bus, GstMessage* message, gpointer data);
+    friend gboolean moviePrivateEOSCallback(GstBus* bus, GstMessage* message, gpointer data);
+    friend gboolean moviePrivateStateCallback(GstBus* bus, GstMessage* message, gpointer data);
+
+    public:
+        MoviePrivate(Movie* m);
+        ~MoviePrivate();
+
+        IntSize naturalSize();
+        bool hasVideo();
+
+        void load(String url);
+        void cancelLoad();
+
+        void play();
+        void pause();
+
+        bool paused() const;
+        bool seeking() const;
+
+        float duration();
+        float currentTime() const;
+        void seek(float time);
+        void setEndTime(float time);
+
+        void addCuePoint(float time);
+        void removeCuePoint(float time);
+        void clearCuePoints();
+
+        void setRate(float);
+        void setVolume(float);
+        void setMuted(bool);
+
+        int dataRate() const;
+
+        Movie::NetworkState networkState();
+        Movie::ReadyState readyState();
+
+        float maxTimeBuffered();
+        float maxTimeSeekable();
+        unsigned bytesLoaded();
+        bool totalBytesKnown();
+        unsigned totalBytes();
+
+        void setVisible(bool);
+        void setRect(const IntRect& r);
+
+        void loadStateChanged();
+        void rateChanged();
+        void sizeChanged();
+        void timeChanged();
+        void volumeChanged();
+        void didEnd();
+        void loadingFailed();
+
+        void paint(GraphicsContext* p, const IntRect& r);
+        static void getSupportedTypes(HashSet<String>& types);
+
+    private:
+
+        void updateStates();
+        void cancelSeek();
+        void cuePointTimerFired(Timer<MoviePrivate>*);
+        float maxTimeLoaded();
+        void startCuePointTimerIfNeeded();
+
+        void createGSTPlayBin(String url);
+
+    private:
+        Movie* m_movie;
+        GstElement* m_playBin;
+        GstElement* m_video_sink;
+        GstElement* m_source;
+        float m_seekTo;
+        float m_rate;
+        float m_endTime;
+        bool m_endReached;
+        double m_oldVolume;
+        float m_previousTimeCueTimerFired;
+        Movie::NetworkState m_networkState;
+        Movie::ReadyState m_readyState;
+        bool m_startedPlaying;
+        bool m_isStreaming;
+    };
+}
+
+#endif
+#endif