<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[285586] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/285586">285586</a></dd>
<dt>Author</dt> <dd>eocanha@igalia.com</dd>
<dt>Date</dt> <dd>2021-11-10 09:42:35 -0800 (Wed, 10 Nov 2021)</dd>
</dl>

<h3>Log Message</h3>
<pre>[GTK] Layout Test media/video-seek-with-negative-playback.html timeouts on the release bot.
https://bugs.webkit.org/show_bug.cgi?id=135086

Reviewed by Xabier Rodriguez-Calvar.

Source/WebCore:

In some specific cases, an EOS GstEvent can happen right before a seek. The event is translated
by playbin as an EOS GstMessage and posted to the bus, waiting to be forwarded to the main thread.
The EOS message (now irrelevant after the seek) is received and processed right after the seek,
causing the termination of the media at the player private and upper levels. This can even happen
after the seek has completed (m_isSeeking already false).

This patch detects that condition by ensuring that the playback is coherent with the EOS message,
that is, if we're still playing somewhere inside the playable ranges, there should be no EOS at
all. If that's the case, it's considered to be one of those spureous EOS and is ignored.

Live streams (infinite duration) are special and we still have to detect legitimate EOS there, so
this message bailout isn't done in those cases.

Also refactored the code that queries the position to the sinks.

* platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp: Ignore EOS message if the playback position is inside the playback limits when they're finite. Refactored sink position query code as gstreamerPositionFromSinks().
* platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h: Added gstreamerPositionFromSinks().

LayoutTests:

* platform/glib/TestExpectations: Unskipped test.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsplatformglibTestExpectations">trunk/LayoutTests/platform/glib/TestExpectations</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsgstreamerMediaPlayerPrivateGStreamercpp">trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsgstreamerMediaPlayerPrivateGStreamerh">trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (285585 => 285586)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog      2021-11-10 17:41:49 UTC (rev 285585)
+++ trunk/LayoutTests/ChangeLog 2021-11-10 17:42:35 UTC (rev 285586)
</span><span class="lines">@@ -1,3 +1,12 @@
</span><ins>+2021-11-10  Enrique Ocaña González  <eocanha@igalia.com>
+
+        [GTK] Layout Test media/video-seek-with-negative-playback.html timeouts on the release bot.
+        https://bugs.webkit.org/show_bug.cgi?id=135086
+
+        Reviewed by Xabier Rodriguez-Calvar.
+
+        * platform/glib/TestExpectations: Unskipped test.
+
</ins><span class="cx"> 2021-11-10  Rob Buis  <rbuis@igalia.com>
</span><span class="cx"> 
</span><span class="cx">         [css-contain] Support contain:paint
</span></span></pre></div>
<a id="trunkLayoutTestsplatformglibTestExpectations"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/platform/glib/TestExpectations (285585 => 285586)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/glib/TestExpectations 2021-11-10 17:41:49 UTC (rev 285585)
+++ trunk/LayoutTests/platform/glib/TestExpectations    2021-11-10 17:42:35 UTC (rev 285586)
</span><span class="lines">@@ -2341,7 +2341,6 @@
</span><span class="cx"> 
</span><span class="cx"> webkit.org/b/108925 http/tests/media/video-play-stall.html [ Failure Timeout ]
</span><span class="cx"> 
</span><del>-webkit.org/b/135086 media/video-seek-with-negative-playback.html [ Timeout Pass ]
</del><span class="cx"> webkit.org/b/137698 media/video-controls-drag.html [ Timeout ]
</span><span class="cx"> webkit.org/b/141959 http/tests/media/clearkey/clear-key-hls-aes128.html [ Crash Timeout ]
</span><span class="cx"> webkit.org/b/142489 http/tests/media/video-play-waiting.html [ Timeout ]
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (285585 => 285586)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog   2021-11-10 17:41:49 UTC (rev 285585)
+++ trunk/Source/WebCore/ChangeLog      2021-11-10 17:42:35 UTC (rev 285586)
</span><span class="lines">@@ -1,3 +1,28 @@
</span><ins>+2021-11-10  Enrique Ocaña González  <eocanha@igalia.com>
+
+        [GTK] Layout Test media/video-seek-with-negative-playback.html timeouts on the release bot.
+        https://bugs.webkit.org/show_bug.cgi?id=135086
+
+        Reviewed by Xabier Rodriguez-Calvar.
+
+        In some specific cases, an EOS GstEvent can happen right before a seek. The event is translated
+        by playbin as an EOS GstMessage and posted to the bus, waiting to be forwarded to the main thread.
+        The EOS message (now irrelevant after the seek) is received and processed right after the seek,
+        causing the termination of the media at the player private and upper levels. This can even happen
+        after the seek has completed (m_isSeeking already false).
+
+        This patch detects that condition by ensuring that the playback is coherent with the EOS message,
+        that is, if we're still playing somewhere inside the playable ranges, there should be no EOS at
+        all. If that's the case, it's considered to be one of those spureous EOS and is ignored.
+
+        Live streams (infinite duration) are special and we still have to detect legitimate EOS there, so
+        this message bailout isn't done in those cases.
+
+        Also refactored the code that queries the position to the sinks.
+
+        * platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp: Ignore EOS message if the playback position is inside the playback limits when they're finite. Refactored sink position query code as gstreamerPositionFromSinks().
+        * platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h: Added gstreamerPositionFromSinks().
+
</ins><span class="cx"> 2021-11-10  Jer Noble  <jer.noble@apple.com>
</span><span class="cx"> 
</span><span class="cx">         [iOS] Adopt -[AVAudioSession setAuditTokensForProcessAssertion:]
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsgstreamerMediaPlayerPrivateGStreamercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp (285585 => 285586)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp 2021-11-10 17:41:49 UTC (rev 285585)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp    2021-11-10 17:42:35 UTC (rev 285586)
</span><span class="lines">@@ -1276,6 +1276,31 @@
</span><span class="cx">     return sink;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+GstClockTime MediaPlayerPrivateGStreamer::gstreamerPositionFromSinks() const
+{
+    gint64 gstreamerPosition = GST_CLOCK_TIME_NONE;
+    // Asking directly to the sinks and choosing the highest value is faster than asking to the pipeline.
+    GRefPtr<GstQuery> query = adoptGRef(gst_query_new_position(GST_FORMAT_TIME));
+    if (m_audioSink && gst_element_query(m_audioSink.get(), query.get())) {
+        gint64 audioPosition = GST_CLOCK_TIME_NONE;
+        gst_query_parse_position(query.get(), 0, &audioPosition);
+        if (GST_CLOCK_TIME_IS_VALID(audioPosition))
+            gstreamerPosition = audioPosition;
+        GST_TRACE_OBJECT(pipeline(), "Audio position %" GST_TIME_FORMAT, GST_TIME_ARGS(audioPosition));
+        query = adoptGRef(gst_query_new_position(GST_FORMAT_TIME));
+    }
+    if (m_videoSink && gst_element_query(m_videoSink.get(), query.get())) {
+        gint64 videoPosition = GST_CLOCK_TIME_NONE;
+        gst_query_parse_position(query.get(), 0, &videoPosition);
+        GST_TRACE_OBJECT(pipeline(), "Video position %" GST_TIME_FORMAT, GST_TIME_ARGS(videoPosition));
+        if (GST_CLOCK_TIME_IS_VALID(videoPosition) && (!GST_CLOCK_TIME_IS_VALID(gstreamerPosition)
+            || (m_playbackRate >= 0 && videoPosition > gstreamerPosition)
+            || (m_playbackRate < 0 && videoPosition < gstreamerPosition)))
+            gstreamerPosition = videoPosition;
+    }
+    return static_cast<GstClockTime>(gstreamerPosition);
+}
+
</ins><span class="cx"> MediaTime MediaPlayerPrivateGStreamer::playbackPosition() const
</span><span class="cx"> {
</span><span class="cx">     GST_TRACE_OBJECT(pipeline(), "isEndReached: %s, seeking: %s, seekTime: %s", boolForPrinting(m_isEndReached), boolForPrinting(m_isSeeking), m_seekTime.toString().utf8().data());
</span><span class="lines">@@ -1295,28 +1320,7 @@
</span><span class="cx">         return m_cachedPosition.value();
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    // Position is only available if no async state change is going on and the state is either paused or playing.
-    gint64 position = GST_CLOCK_TIME_NONE;
-
-    // Asking directly to the sinks and choosing the highest value is faster than asking to the pipeline.
-    GRefPtr<GstQuery> query = adoptGRef(gst_query_new_position(GST_FORMAT_TIME));
-    if (m_audioSink && gst_element_query(m_audioSink.get(), query.get())) {
-        gint64 audioPosition = GST_CLOCK_TIME_NONE;
-        gst_query_parse_position(query.get(), 0, &audioPosition);
-        if (GST_CLOCK_TIME_IS_VALID(audioPosition))
-            position = audioPosition;
-        query = adoptGRef(gst_query_new_position(GST_FORMAT_TIME));
-    }
-    if (m_videoSink && gst_element_query(m_videoSink.get(), query.get())) {
-        gint64 videoPosition = GST_CLOCK_TIME_NONE;
-        gst_query_parse_position(query.get(), 0, &videoPosition);
-        if (GST_CLOCK_TIME_IS_VALID(videoPosition) && (!GST_CLOCK_TIME_IS_VALID(position)
-            || (m_playbackRate >= 0 && videoPosition > position)
-            || (m_playbackRate < 0 && videoPosition < position)))
-            position = videoPosition;
-    }
-
-    GstClockTime gstreamerPosition = static_cast<GstClockTime>(position);
</del><ins>+    GstClockTime gstreamerPosition = gstreamerPositionFromSinks();
</ins><span class="cx">     GST_TRACE_OBJECT(pipeline(), "Position %" GST_TIME_FORMAT ", canFallBackToLastFinishedSeekPosition: %s", GST_TIME_ARGS(gstreamerPosition), boolForPrinting(m_canFallBackToLastFinishedSeekPosition));
</span><span class="cx"> 
</span><span class="cx">     MediaTime playbackPosition = MediaTime::zeroTime();
</span><span class="lines">@@ -1762,9 +1766,32 @@
</span><span class="cx">             }
</span><span class="cx">         }
</span><span class="cx">         break;
</span><del>-    case GST_MESSAGE_EOS:
</del><ins>+    case GST_MESSAGE_EOS: {
+        // In some specific cases, an EOS GstEvent can happen right before a seek. The event is translated
+        // by playbin as an EOS GstMessage and posted to the bus, waiting to be forwarded to the main thread.
+        // The EOS message (now irrelevant after the seek) is received and processed right after the seek,
+        // causing the termination of the media at the player private and upper levels. This can even happen
+        // after the seek has completed (m_isSeeking already false).
+        // The code below detects that condition by ensuring that the playback is coherent with the EOS message,
+        // that is, if we're still playing somewhere inside the playable ranges, there should be no EOS at
+        // all. If that's the case, it's considered to be one of those spureous EOS and is ignored.
+        // Live streams (infinite duration) are special and we still have to detect legitimate EOS there, so
+        // this message bailout isn't done in those cases.
+        MediaTime playbackPosition = MediaTime::invalidTime();
+        MediaTime duration = durationMediaTime();
+        GstClockTime gstreamerPosition = gstreamerPositionFromSinks();
+        if (GST_CLOCK_TIME_IS_VALID(gstreamerPosition))
+            playbackPosition = MediaTime(gstreamerPosition, GST_SECOND);
+        if (playbackPosition.isValid() && duration.isValid()
+            && ((m_playbackRate >= 0 && playbackPosition < duration && duration.isFinite())
+            || (m_playbackRate < 0 && playbackPosition > MediaTime::zeroTime()))) {
+            GST_DEBUG_OBJECT(pipeline(), "EOS received but position %s is still in the finite playable limits [%s, %s], ignoring it",
+                playbackPosition.toString().utf8().data(), MediaTime::zeroTime().toString().utf8().data(), duration.toString().utf8().data());
+            break;
+        }
</ins><span class="cx">         didEnd();
</span><span class="cx">         break;
</span><ins>+    }
</ins><span class="cx">     case GST_MESSAGE_ASYNC_DONE:
</span><span class="cx">         if (!messageSourceIsPlaybin || m_isDelayingLoad)
</span><span class="cx">             break;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsgstreamerMediaPlayerPrivateGStreamerh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h (285585 => 285586)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h   2021-11-10 17:41:49 UTC (rev 285585)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h      2021-11-10 17:42:35 UTC (rev 285586)
</span><span class="lines">@@ -438,6 +438,7 @@
</span><span class="cx"> 
</span><span class="cx">     void syncOnClock(bool sync);
</span><span class="cx"> 
</span><ins>+    GstClockTime gstreamerPositionFromSinks() const;
</ins><span class="cx">     MediaTime playbackPosition() const;
</span><span class="cx"> 
</span><span class="cx">     virtual void updateStates();
</span></span></pre>
</div>
</div>

</body>
</html>