2010-02-15 Philippe Normand <pnormand@igalia.com>
authorphiln@webkit.org <philn@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 17 Feb 2010 08:37:38 +0000 (08:37 +0000)
committerphiln@webkit.org <philn@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 17 Feb 2010 08:37:38 +0000 (08:37 +0000)
        Reviewed by Gustavo Noronha Silva.

        [GStreamer] Should handle BUFFERING messages
        https://bugs.webkit.org/show_bug.cgi?id=30004

        * configure.ac: Bump gstreamer -core/-plugins-base requirements to
        0.10.25 which is the minimum required version for on-disk buffering.

2010-01-07  Philippe Normand  <pnormand@igalia.com>

        Reviewed by Gustavo Noronha Silva.

        [GStreamer] Should handle BUFFERING messages
        https://bugs.webkit.org/show_bug.cgi?id=30004

        Draw the buffering status in the media controls. The timebar is
        now 2 pixels shorter so dragging it at same absolute position than
        before produces a seek at a new position in the media, this
        explains the rebaselining of the controls-drag-timebar test.

        * platform/gtk/media/controls-after-reload-expected.txt:
        * platform/gtk/media/controls-drag-timebar-expected.txt:
        * platform/gtk/media/controls-strict-expected.txt:
        * platform/gtk/media/controls-styling-expected.txt:
        * platform/gtk/media/video-controls-rendering-expected.txt:
        Re-baselined due to 1px left/right border added in controls timeline.

2010-01-07  Philippe Normand  <pnormand@igalia.com>

        Reviewed by Gustavo Noronha Silva.

        [GStreamer] Should handle BUFFERING messages
        https://bugs.webkit.org/show_bug.cgi?id=30004

        Initial support for on-disk buffering of videos. This works only
        for Quicktime and flv though.

        * css/mediaControlsGtk.css:
        * platform/gtk/RenderThemeGtk.cpp:
        (WebCore::RenderThemeGtk::paintMediaSliderTrack): Draw the
        buffering status in the media controls.
        * platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp:
        (WebCore::mediaPlayerPrivateMessageCallback): Defer buffering
        messages handling to processBufferingStats().
        (WebCore::bufferingTimeoutCallback): Closure called periodically
        during the on-disk buffering process.
        (WebCore::MediaPlayerPrivate::MediaPlayerPrivate): New instance
        variables and create playbin2 here instead of doing it in load().
        (WebCore::MediaPlayerPrivate::~MediaPlayerPrivate): New instance
        variables.
        (WebCore::MediaPlayerPrivate::load): Simply set uri on playbin2
        instead of creating the pipeline and setting uri all together.
        (WebCore::MediaPlayerPrivate::processBufferingStats): Start a new
        timeout source if the player is starting on-disk buffering.
        (WebCore::MediaPlayerPrivate::queryBufferingStats): Method called
        200ms during on-disk buffering to update the maxTimeLoaded and few
        other private variables.
        (WebCore::MediaPlayerPrivate::maxTimeSeekable):
        (WebCore::MediaPlayerPrivate::maxTimeLoaded):
        (WebCore::MediaPlayerPrivate::bytesLoaded): Fixed implementations
        regarding buffering.
        (WebCore::MediaPlayerPrivate::totalBytes): Improved logging.
        (WebCore::MediaPlayerPrivate::updateStates): Start playback if it
        was internally paused at beginning of on-disk buffering and set
        ready/network states depending on the state of the on-disk
        buffering process.
        (WebCore::MediaPlayerPrivate::didEnd): Emit durationChanged.
        (WebCore::MediaPlayerPrivate::setAutobuffer): Edit playbin2 flags
        property depending on autoBuffer value.
        (WebCore::MediaPlayerPrivate::createGSTPlayBin): Don't set uri
        there, it is now done in load().
        * platform/graphics/gtk/MediaPlayerPrivateGStreamer.h: New methods
        and instance variables.

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

13 files changed:
ChangeLog
LayoutTests/ChangeLog
LayoutTests/platform/gtk/media/controls-after-reload-expected.txt
LayoutTests/platform/gtk/media/controls-drag-timebar-expected.txt
LayoutTests/platform/gtk/media/controls-strict-expected.txt
LayoutTests/platform/gtk/media/controls-styling-expected.txt
LayoutTests/platform/gtk/media/video-controls-rendering-expected.txt
WebCore/ChangeLog
WebCore/css/mediaControlsGtk.css
WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp
WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h
WebCore/platform/gtk/RenderThemeGtk.cpp
configure.ac

index 9a70337..669f9c6 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2010-02-15  Philippe Normand  <pnormand@igalia.com>
+
+        Reviewed by Gustavo Noronha Silva.
+
+        [GStreamer] Should handle BUFFERING messages
+        https://bugs.webkit.org/show_bug.cgi?id=30004
+
+        * configure.ac: Bump gstreamer -core/-plugins-base requirements to
+        0.10.25 which is the minimum required version for on-disk buffering.
+
 2010-02-16  Xan Lopez  <xlopez@igalia.com>
 
         Reviewed by Gustavo Noronha.
index 2350b6b..0f36537 100644 (file)
@@ -1,3 +1,22 @@
+2010-01-07  Philippe Normand  <pnormand@igalia.com>
+
+        Reviewed by Gustavo Noronha Silva.
+
+        [GStreamer] Should handle BUFFERING messages
+        https://bugs.webkit.org/show_bug.cgi?id=30004
+
+        Draw the buffering status in the media controls. The timebar is
+        now 2 pixels shorter so dragging it at same absolute position than
+        before produces a seek at a new position in the media, this
+        explains the rebaselining of the controls-drag-timebar test.
+
+        * platform/gtk/media/controls-after-reload-expected.txt:
+        * platform/gtk/media/controls-drag-timebar-expected.txt:
+        * platform/gtk/media/controls-strict-expected.txt:
+        * platform/gtk/media/controls-styling-expected.txt:
+        * platform/gtk/media/video-controls-rendering-expected.txt:
+        Re-baselined due to 1px left/right border added in controls timeline.
+
 2010-02-17  Xan Lopez  <xlopez@igalia.com>
 
         Skip tests requiring new results.
index 08d9c92..98b8804 100644 (file)
@@ -17,8 +17,8 @@ layer at (8,44) size 320x240
 layer at (8,264) size 320x20
   RenderFlexibleBox (positioned) {DIV} at (0,220) size 320x20
     RenderButton {INPUT} at (0,0) size 20x20
-    RenderFlexibleBox {DIV} at (20,0) size 240x20
-      RenderSlider {INPUT} at (0,0) size 240x20
+    RenderFlexibleBox {DIV} at (20,0) size 240x20 [border: (1px solid #FFFFFF33) none (1px solid #FFFFFF33)]
+      RenderSlider {INPUT} at (1,0) size 238x20
         RenderBlock {DIV} at (2,4) size 12x12
     RenderButton {INPUT} at (260,0) size 20x20
     RenderButton {INPUT} at (280,0) size 20x20
index 688b449..6936be4 100644 (file)
@@ -4,7 +4,7 @@ RUN(video.autoplay = true)
 RUN(video.src = 'content/test.mp4')
 EVENT(playing)
 EVENT(seeked)
-Time: 2.2
+Time: 2.1
 EVENT(seeked)
 Time: 2.7
 EVENT(seeked)
index 2508c1c..f04023e 100644 (file)
@@ -17,8 +17,8 @@ layer at (8,52) size 320x240
 layer at (8,272) size 320x20
   RenderFlexibleBox (positioned) {DIV} at (0,220) size 320x20
     RenderButton {INPUT} at (0,0) size 20x20
-    RenderFlexibleBox {DIV} at (20,0) size 240x20
-      RenderSlider {INPUT} at (0,0) size 240x20
+    RenderFlexibleBox {DIV} at (20,0) size 240x20 [border: (1px solid #FFFFFF33) none (1px solid #FFFFFF33)]
+      RenderSlider {INPUT} at (1,0) size 238x20
         RenderBlock {DIV} at (2,4) size 12x12
     RenderButton {INPUT} at (260,0) size 20x20
     RenderButton {INPUT} at (280,0) size 20x20
index 7b97183..f9d170c 100644 (file)
@@ -21,8 +21,8 @@ layer at (18,44) size 320x240
 layer at (18,264) size 320x20
   RenderFlexibleBox (positioned) {DIV} at (0,220) size 320x20
     RenderButton {INPUT} at (0,0) size 20x20
-    RenderFlexibleBox {DIV} at (20,0) size 240x20
-      RenderSlider {INPUT} at (0,0) size 240x20
+    RenderFlexibleBox {DIV} at (20,0) size 240x20 [border: (1px solid #FFFFFF33) none (1px solid #FFFFFF33)]
+      RenderSlider {INPUT} at (1,0) size 238x20
         RenderBlock {DIV} at (2,4) size 12x12
     RenderButton {INPUT} at (260,0) size 20x20
     RenderButton {INPUT} at (280,0) size 20x20
@@ -32,8 +32,8 @@ layer at (8,284) size 320x240
 layer at (8,504) size 320x20
   RenderFlexibleBox (positioned) {DIV} at (0,220) size 320x20
     RenderButton {INPUT} at (0,0) size 20x20
-    RenderFlexibleBox {DIV} at (20,0) size 240x20
-      RenderSlider {INPUT} at (0,0) size 240x20
+    RenderFlexibleBox {DIV} at (20,0) size 240x20 [border: (1px solid #FFFFFF33) none (1px solid #FFFFFF33)]
+      RenderSlider {INPUT} at (1,0) size 238x20
         RenderBlock {DIV} at (2,4) size 12x12
     RenderButton {INPUT} at (260,0) size 20x20
     RenderButton {INPUT} at (280,0) size 20x20
index add29fe..ec8c14f 100644 (file)
@@ -20,8 +20,8 @@ layer at (8,44) size 320x240
 layer at (8,264) size 320x20
   RenderFlexibleBox (positioned) {DIV} at (0,220) size 320x20
     RenderButton {INPUT} at (0,0) size 20x20
-    RenderFlexibleBox {DIV} at (20,0) size 240x20
-      RenderSlider {INPUT} at (0,0) size 240x20
+    RenderFlexibleBox {DIV} at (20,0) size 240x20 [border: (1px solid #FFFFFF33) none (1px solid #FFFFFF33)]
+      RenderSlider {INPUT} at (1,0) size 238x20
         RenderBlock {DIV} at (2,4) size 12x12
     RenderButton {INPUT} at (260,0) size 20x20
     RenderButton {INPUT} at (280,0) size 20x20
@@ -31,8 +31,8 @@ layer at (8,284) size 320x240
 layer at (8,504) size 320x20
   RenderFlexibleBox (positioned) {DIV} at (0,220) size 320x20
     RenderButton {INPUT} at (0,0) size 20x20
-    RenderFlexibleBox {DIV} at (20,0) size 240x20
-      RenderSlider {INPUT} at (0,0) size 240x20
+    RenderFlexibleBox {DIV} at (20,0) size 240x20 [border: (1px solid #FFFFFF33) none (1px solid #FFFFFF33)]
+      RenderSlider {INPUT} at (1,0) size 238x20
         RenderBlock {DIV} at (2,4) size 12x12
     RenderButton {INPUT} at (260,0) size 20x20
     RenderButton {INPUT} at (280,0) size 20x20
@@ -44,8 +44,8 @@ layer at (8,524) size 320x240
 layer at (8,744) size 320x20
   RenderFlexibleBox (positioned) {DIV} at (0,220) size 320x20
     RenderButton {INPUT} at (0,0) size 20x20
-    RenderFlexibleBox {DIV} at (20,0) size 240x20
-      RenderSlider {INPUT} at (0,0) size 240x20
+    RenderFlexibleBox {DIV} at (20,0) size 240x20 [border: (1px solid #FFFFFF33) none (1px solid #FFFFFF33)]
+      RenderSlider {INPUT} at (1,0) size 238x20
         RenderBlock {DIV} at (2,4) size 12x12
     RenderButton {INPUT} at (260,0) size 20x20
     RenderButton {INPUT} at (280,0) size 20x20
index 4c6be7a..db89770 100644 (file)
@@ -1,3 +1,50 @@
+2010-01-07  Philippe Normand  <pnormand@igalia.com>
+
+        Reviewed by Gustavo Noronha Silva.
+
+        [GStreamer] Should handle BUFFERING messages
+        https://bugs.webkit.org/show_bug.cgi?id=30004
+
+        Initial support for on-disk buffering of videos. This works only
+        for Quicktime and flv though.
+
+        * css/mediaControlsGtk.css:
+        * platform/gtk/RenderThemeGtk.cpp:
+        (WebCore::RenderThemeGtk::paintMediaSliderTrack): Draw the
+        buffering status in the media controls.
+        * platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp:
+        (WebCore::mediaPlayerPrivateMessageCallback): Defer buffering
+        messages handling to processBufferingStats().
+        (WebCore::bufferingTimeoutCallback): Closure called periodically
+        during the on-disk buffering process.
+        (WebCore::MediaPlayerPrivate::MediaPlayerPrivate): New instance
+        variables and create playbin2 here instead of doing it in load().
+        (WebCore::MediaPlayerPrivate::~MediaPlayerPrivate): New instance
+        variables.
+        (WebCore::MediaPlayerPrivate::load): Simply set uri on playbin2
+        instead of creating the pipeline and setting uri all together.
+        (WebCore::MediaPlayerPrivate::processBufferingStats): Start a new
+        timeout source if the player is starting on-disk buffering.
+        (WebCore::MediaPlayerPrivate::queryBufferingStats): Method called
+        200ms during on-disk buffering to update the maxTimeLoaded and few
+        other private variables.
+        (WebCore::MediaPlayerPrivate::maxTimeSeekable):
+        (WebCore::MediaPlayerPrivate::maxTimeLoaded):
+        (WebCore::MediaPlayerPrivate::bytesLoaded): Fixed implementations
+        regarding buffering.
+        (WebCore::MediaPlayerPrivate::totalBytes): Improved logging.
+        (WebCore::MediaPlayerPrivate::updateStates): Start playback if it
+        was internally paused at beginning of on-disk buffering and set
+        ready/network states depending on the state of the on-disk
+        buffering process.
+        (WebCore::MediaPlayerPrivate::didEnd): Emit durationChanged.
+        (WebCore::MediaPlayerPrivate::setAutobuffer): Edit playbin2 flags
+        property depending on autoBuffer value.
+        (WebCore::MediaPlayerPrivate::createGSTPlayBin): Don't set uri
+        there, it is now done in load().
+        * platform/graphics/gtk/MediaPlayerPrivateGStreamer.h: New methods
+        and instance variables.
+
 2010-02-16  Chris Evans  <cevans@chromium.org>
 
         Reviewed by Adam Barth.
index 8e98ab1..cc6da14 100644 (file)
@@ -41,6 +41,8 @@ audio::-webkit-media-controls-play-button, video::-webkit-media-controls-play-bu
 
 audio::-webkit-media-controls-timeline-container, video::-webkit-media-controls-timeline-container {
     height: 20px;
+    border-left: 1px solid rgba(255, 255, 255, 0.2);
+    border-right: 1px solid rgba(255, 255, 255, 0.2);
 }
 
 audio::-webkit-media-controls-timeline, video::-webkit-media-controls-timeline {
index 55fa180..cbc64a1 100644 (file)
@@ -3,6 +3,7 @@
  * Copyright (C) 2007 Collabora Ltd.  All rights reserved.
  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
  * Copyright (C) 2009 Gustavo Noronha Silva <gns@gnome.org>
+ * Copyright (C) 2009, 2010 Igalia S.L
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
 #include <webkit/webkitwebview.h>
 #include <wtf/gtk/GOwnPtr.h>
 
+// GstPlayFlags flags from playbin2. It is the policy of GStreamer to
+// not publicly expose element-specific enums. That's why this
+// GstPlayFlags enum has been copied here.
+typedef enum {
+    GST_PLAY_FLAG_VIDEO         = 0x00000001,
+    GST_PLAY_FLAG_AUDIO         = 0x00000002,
+    GST_PLAY_FLAG_TEXT          = 0x00000004,
+    GST_PLAY_FLAG_VIS           = 0x00000008,
+    GST_PLAY_FLAG_SOFT_VOLUME   = 0x00000010,
+    GST_PLAY_FLAG_NATIVE_AUDIO  = 0x00000020,
+    GST_PLAY_FLAG_NATIVE_VIDEO  = 0x00000040,
+    GST_PLAY_FLAG_DOWNLOAD      = 0x00000080,
+    GST_PLAY_FLAG_BUFFERING     = 0x000000100
+} GstPlayFlags;
+
 using namespace std;
 
 namespace WebCore {
@@ -76,7 +92,6 @@ gboolean mediaPlayerPrivateMessageCallback(GstBus* bus, GstMessage* message, gpo
     GOwnPtr<gchar> debug;
     MediaPlayer::NetworkState error;
     MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data);
-    gint percent = 0;
     bool issueError = true;
     bool attemptNextLocation = false;
 
@@ -126,8 +141,7 @@ gboolean mediaPlayerPrivateMessageCallback(GstBus* bus, GstMessage* message, gpo
         mp->updateStates();
         break;
     case GST_MESSAGE_BUFFERING:
-        gst_message_parse_buffering(message, &percent);
-        LOG_VERBOSE(Media, "Buffering %d", percent);
+        mp->processBufferingStats(message);
         break;
     case GST_MESSAGE_DURATION:
         LOG_VERBOSE(Media, "Duration changed");
@@ -185,6 +199,12 @@ gboolean notifyMuteIdleCallback(gpointer data)
     return FALSE;
 }
 
+gboolean bufferingTimeoutCallback(gpointer data)
+{
+    MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data);
+    return mp->queryBufferingStats();
+}
+
 static float playbackPosition(GstElement* playbin)
 {
 
@@ -289,14 +309,24 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player)
     , m_playbackRate(1)
     , m_errorOccured(false)
     , m_volumeIdleId(0)
-    , m_mediaDuration(0.0)
+    , m_mediaDuration(0)
     , m_muteIdleId(0)
+    , m_startedBuffering(false)
+    , m_fillTimeoutId(0)
+    , m_maxTimeLoaded(0)
+    , m_fillStatus(0)
 {
-    doGstInit();
+    if (doGstInit())
+        createGSTPlayBin();
 }
 
 MediaPlayerPrivate::~MediaPlayerPrivate()
 {
+    if (m_fillTimeoutId) {
+        g_source_remove(m_fillTimeoutId);
+        m_fillTimeoutId = 0;
+    }
+
     if (m_volumeIdleId) {
         g_source_remove(m_volumeIdleId);
         m_volumeIdleId = 0;
@@ -349,7 +379,7 @@ void MediaPlayerPrivate::load(const String& url)
         m_player->readyStateChanged();
     }
 
-    createGSTPlayBin(url);
+    g_object_set(m_playBin, "uri", url.utf8().data(), NULL);
     pause();
 }
 
@@ -651,16 +681,79 @@ PassRefPtr<TimeRanges> MediaPlayerPrivate::buffered() const
     return timeRanges.release();
 }
 
+void MediaPlayerPrivate::processBufferingStats(GstMessage* message)
+{
+    GstBufferingMode mode;
+
+    gst_message_parse_buffering_stats(message, &mode, 0, 0, 0);
+    if (mode != GST_BUFFERING_DOWNLOAD)
+        return;
+
+    if (!m_startedBuffering) {
+        m_startedBuffering = true;
+
+        if (m_fillTimeoutId > 0)
+            g_source_remove(m_fillTimeoutId);
+
+        m_fillTimeoutId = g_timeout_add(200, (GSourceFunc) bufferingTimeoutCallback, this);
+    }
+}
+
+bool MediaPlayerPrivate::queryBufferingStats()
+{
+    GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT);
+
+    if (!gst_element_query(m_playBin, query)) {
+        gst_query_unref(query);
+        return TRUE;
+    }
+
+    gint64 start, stop;
+
+    gst_query_parse_buffering_range(query, 0, &start, &stop, 0);
+    gst_query_unref(query);
+
+    if (stop != -1)
+        m_fillStatus = 100.0 * stop / GST_FORMAT_PERCENT_MAX;
+    else
+        m_fillStatus = 100.0;
+
+    LOG_VERBOSE(Media, "Download buffer filled up to %f%%", m_fillStatus);
+
+    if (!m_mediaDuration)
+        durationChanged();
+
+    // Update maxTimeLoaded only if the media duration is
+    // available. Otherwise we can't compute it.
+    if (m_mediaDuration) {
+        m_maxTimeLoaded = static_cast<float>((m_fillStatus * m_mediaDuration) / 100.0);
+        LOG_VERBOSE(Media, "Updated maxTimeLoaded: %f", m_maxTimeLoaded);
+    }
+
+    if (m_fillStatus != 100.0) {
+        updateStates();
+        return TRUE;
+    }
+
+    // Media is now fully loaded. It will play even if network
+    // connection is cut. Buffering is done, remove the fill source
+    // from the main loop.
+    m_fillTimeoutId = 0;
+    m_startedBuffering = false;
+    updateStates();
+    return FALSE;
+}
+
 float MediaPlayerPrivate::maxTimeSeekable() const
 {
     if (m_errorOccured)
         return 0.0;
 
-    // TODO
     LOG_VERBOSE(Media, "maxTimeSeekable");
-    if (m_isStreaming)
-        return numeric_limits<float>::infinity();
     // infinite duration means live stream
+    if (isinf(duration()))
+        return 0.0;
+
     return maxTimeLoaded();
 }
 
@@ -669,29 +762,28 @@ float MediaPlayerPrivate::maxTimeLoaded() const
     if (m_errorOccured)
         return 0.0;
 
-    // TODO
-    LOG_VERBOSE(Media, "maxTimeLoaded");
-    notImplemented();
-    return duration();
+    float loaded = m_maxTimeLoaded;
+    if (!loaded && !m_fillTimeoutId)
+        loaded = duration();
+    LOG_VERBOSE(Media, "maxTimeLoaded: %f", loaded);
+    return loaded;
 }
 
 unsigned MediaPlayerPrivate::bytesLoaded() const
 {
-    notImplemented();
-    LOG_VERBOSE(Media, "bytesLoaded");
-    /*if (!m_playBin)
+    if (!m_playBin)
+        return 0;
+
+    if (!m_mediaDuration)
         return 0;
-    float dur = duration();
-    float maxTime = maxTimeLoaded();
-    if (!dur)
-        return 0;*/
 
-    return 1; // totalBytes() * maxTime / dur;
+    unsigned loaded = totalBytes() * maxTimeLoaded() / m_mediaDuration;
+    LOG_VERBOSE(Media, "bytesLoaded: %d", loaded);
+    return loaded;
 }
 
 unsigned MediaPlayerPrivate::totalBytes() const
 {
-    LOG_VERBOSE(Media, "totalBytes");
     if (!m_source)
         return 0;
 
@@ -701,6 +793,7 @@ unsigned MediaPlayerPrivate::totalBytes() const
     GstFormat fmt = GST_FORMAT_BYTES;
     gint64 length = 0;
     gst_element_query_duration(m_source, &fmt, &length);
+    LOG_VERBOSE(Media, "totalBytes %" G_GINT64_FORMAT, length);
 
     return length;
 }
@@ -751,6 +844,7 @@ void MediaPlayerPrivate::updateStates()
         if (state == GST_STATE_PLAYING) {
             m_readyState = MediaPlayer::HaveEnoughData;
             m_paused = false;
+            m_startedPlaying = true;
             if (!m_mediaDuration) {
                 float newDuration = duration();
                 if (!isinf(newDuration))
@@ -759,6 +853,28 @@ void MediaPlayerPrivate::updateStates()
         } else
             m_paused = true;
 
+        // Is on-disk buffering in progress?
+        if (m_fillTimeoutId) {
+            m_networkState = MediaPlayer::Loading;
+            // Buffering has just started, we should now have enough
+            // data to restart playback if it was internally paused by
+            // GStreamer.
+            if (m_paused && !m_startedPlaying)
+                gst_element_set_state(m_playBin, GST_STATE_PLAYING);
+        }
+
+        if (maxTimeLoaded() == duration()) {
+            m_networkState = MediaPlayer::Loaded;
+            if (state == GST_STATE_READY)
+                m_readyState = MediaPlayer::HaveNothing;
+            else if (state == GST_STATE_PAUSED)
+                m_readyState = MediaPlayer::HaveEnoughData;
+        } else
+            if (state == GST_STATE_READY)
+                m_readyState = MediaPlayer::HaveNothing;
+            else if (m_paused)
+                m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData;
+
         if (m_changingRate) {
             m_player->rateChanged();
             m_changingRate = false;
@@ -769,7 +885,6 @@ void MediaPlayerPrivate::updateStates()
             m_seeking = false;
         }
 
-        m_networkState = MediaPlayer::Loaded;
         break;
     case GST_STATE_CHANGE_ASYNC:
         LOG_VERBOSE(Media, "Async: State: %s, pending: %s",
@@ -939,8 +1054,11 @@ void MediaPlayerPrivate::didEnd()
     // not always 0. So to not confuse the HTMLMediaElement we
     // synchronize position and duration values.
     float now = currentTime();
-    if (now > 0)
+    if (now > 0) {
         m_mediaDuration = now;
+        m_player->durationChanged();
+    }
+
     gst_element_set_state(m_playBin, GST_STATE_PAUSED);
 
     timeChanged();
@@ -1191,7 +1309,19 @@ bool MediaPlayerPrivate::supportsFullscreen() const
     return true;
 }
 
-void MediaPlayerPrivate::createGSTPlayBin(String url)
+void MediaPlayerPrivate::setAutobuffer(bool autoBuffer)
+{
+    ASSERT(m_playBin);
+
+    GstPlayFlags flags;
+    g_object_get(m_playBin, "flags", &flags, NULL);
+    if (autoBuffer)
+        g_object_set(m_playBin, "flags", flags | GST_PLAY_FLAG_DOWNLOAD, NULL);
+    else
+        g_object_set(m_playBin, "flags", flags & ~GST_PLAY_FLAG_DOWNLOAD, NULL);
+}
+
+void MediaPlayerPrivate::createGSTPlayBin()
 {
     ASSERT(!m_playBin);
     m_playBin = gst_element_factory_make("playbin2", "play");
@@ -1201,8 +1331,6 @@ void MediaPlayerPrivate::createGSTPlayBin(String url)
     g_signal_connect(bus, "message", G_CALLBACK(mediaPlayerPrivateMessageCallback), this);
     gst_object_unref(bus);
 
-    g_object_set(m_playBin, "uri", url.utf8().data(), NULL);
-
     g_signal_connect(m_playBin, "notify::volume", G_CALLBACK(mediaPlayerPrivateVolumeChangedCallback), this);
     g_signal_connect(m_playBin, "notify::source", G_CALLBACK(mediaPlayerPrivateSourceChangedCallback), this);
     g_signal_connect(m_playBin, "notify::mute", G_CALLBACK(mediaPlayerPrivateMuteChangedCallback), this);
@@ -1221,7 +1349,7 @@ void MediaPlayerPrivate::createGSTPlayBin(String url)
         } else {
             m_fpsSink = 0;
             g_object_set(m_playBin, "video-sink", m_videoSink, NULL);
-            LOG(Media, "Can't display FPS statistics, you need gst-plugins-bad >= 0.10.18");
+            LOG_VERBOSE(Media, "Can't display FPS statistics, you need gst-plugins-bad >= 0.10.18");
         }
     } else
         g_object_set(m_playBin, "video-sink", m_videoSink, NULL);
index 34257ca..e19b686 100644 (file)
@@ -2,6 +2,7 @@
  * Copyright (C) 2007, 2009 Apple Inc.  All rights reserved.
  * Copyright (C) 2007 Collabora Ltd. All rights reserved.
  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ * Copyright (C) 2009, 2010 Igalia S.L
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -86,6 +87,9 @@ class MediaPlayerPrivate : public MediaPlayerPrivateInterface {
             void muteChanged();
             void muteChangedCallback();
 
+            void setAutobuffer(bool);
+            bool queryBufferingStats();
+
             MediaPlayer::NetworkState networkState() const;
             MediaPlayer::ReadyState readyState() const;
 
@@ -128,9 +132,11 @@ class MediaPlayerPrivate : public MediaPlayerPrivateInterface {
             float maxTimeLoaded() const;
             void startEndPointTimerIfNeeded();
 
-            void createGSTPlayBin(String url);
+            void createGSTPlayBin();
             bool changePipelineState(GstState state);
 
+            void processBufferingStats(GstMessage* message);
+
         private:
             MediaPlayer* m_player;
             GstElement* m_playBin;
@@ -157,6 +163,10 @@ class MediaPlayerPrivate : public MediaPlayerPrivateInterface {
             guint m_volumeIdleId;
             gfloat m_mediaDuration;
             guint m_muteIdleId;
+            bool m_startedBuffering;
+            guint m_fillTimeoutId;
+            float m_maxTimeLoaded;
+            gdouble m_fillStatus;
     };
 }
 
index 727788a..e19e2fa 100644 (file)
 #include "AffineTransform.h"
 #include "CString.h"
 #include "GOwnPtr.h"
+#include "Gradient.h"
 #include "GraphicsContext.h"
 #include "HTMLMediaElement.h"
 #include "HTMLNames.h"
+#include "MediaControlElements.h"
 #include "NotImplemented.h"
 #include "RenderBox.h"
 #include "RenderObject.h"
@@ -686,9 +688,46 @@ bool RenderThemeGtk::paintMediaSeekForwardButton(RenderObject* o, const RenderOb
 
 bool RenderThemeGtk::paintMediaSliderTrack(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
 {
-    paintInfo.context->fillRect(FloatRect(r), m_panelColor, DeviceColorSpace);
-    paintInfo.context->fillRect(FloatRect(IntRect(r.x(), r.y() + (r.height() - m_mediaSliderHeight) / 2,
-                                                  r.width(), m_mediaSliderHeight)), m_sliderColor, DeviceColorSpace);
+    GraphicsContext* context = paintInfo.context;
+
+    context->fillRect(FloatRect(r), m_panelColor, DeviceColorSpace);
+    context->fillRect(FloatRect(IntRect(r.x(), r.y() + (r.height() - m_mediaSliderHeight) / 2,
+                                        r.width(), m_mediaSliderHeight)), m_sliderColor, DeviceColorSpace);
+
+    RenderStyle* style = o->style();
+    HTMLMediaElement* mediaElement = toParentMediaElement(o);
+
+    if (!mediaElement)
+        return false;
+
+    // Draw the buffered ranges. This code is highly inspired from
+    // Chrome.
+    // FIXME: Draw multiple ranges if there are multiple buffered
+    // ranges. The current implementation of the player is always
+    // buffering a single range anyway.
+    IntRect bufferedRect = r;
+    bufferedRect.inflate(-style->borderLeftWidth());
+    bufferedRect.setWidth((bufferedRect.width() * mediaElement->percentLoaded()));
+
+    // Don't bother drawing an empty area.
+    if (bufferedRect.isEmpty())
+        return false;
+
+    IntPoint sliderTopLeft = bufferedRect.location();
+    IntPoint sliderTopRight = sliderTopLeft;
+    sliderTopRight.move(0, bufferedRect.height());
+
+    RefPtr<Gradient> gradient = Gradient::create(sliderTopLeft, sliderTopRight);
+    Color startColor = m_panelColor;
+    gradient->addColorStop(0.0, startColor);
+    gradient->addColorStop(1.0, Color(startColor.red() / 2, startColor.green() / 2, startColor.blue() / 2, startColor.alpha()));
+
+    context->save();
+    context->setStrokeStyle(NoStroke);
+    context->setFillGradient(gradient);
+    context->fillRect(bufferedRect);
+    context->restore();
+
     return false;
 }
 
index 3b1ddd3..6e01ce4 100644 (file)
@@ -211,7 +211,7 @@ GTK_REQUIRED_VERSION=2.10
 LIBXSLT_REQUIRED_VERSION=1.1.7
 SQLITE_REQUIRED_VERSION=3.0
 GSTREAMER_REQUIRED_VERSION=0.10
-GSTREAMER_PLUGINS_BASE_REQUIRED_VERSION=0.10.23
+GSTREAMER_PLUGINS_BASE_REQUIRED_VERSION=0.10.25
 ENCHANT_REQUIRED_VERSION=0.22
 GAIL_REQUIRED_VERSION=1.8