Title: [277424] trunk/Source/WebCore
Revision
277424
Author
[email protected]
Date
2021-05-12 22:37:17 -0700 (Wed, 12 May 2021)

Log Message

[iPad] SourceBufferPrivateAVFObjC should not report an error to the web page when the video playback is interrupted
https://bugs.webkit.org/show_bug.cgi?id=225620

Reviewed by Jer Noble.

If `SourceBufferPrivateAVFObjC` reports an error to a web page when
`AVSampleBufferDisplayLayer` reports `AVErrorOperationInterrupted` (the playback
was interrupted), the web page will likely destroy the video player (and teardown
the video element). That behavior will lead to an empty picture-in-picture window
if the video was playing in picture-in-picture.

With this patch, `SourceBufferPrivateAVFObjC` will not report an error to the
web page in case of playback interruption. Instead, it takes a note that
the playback was interrupted, so that when we try to resume the playback later,
it will flush the `AVSampleBufferDisplayLayer` to recover the playback state.

In addition, we need to enqueue an IDR frame first before we enqueue more
samples after flushing the `AVSampleBufferDisplayLayer`. That is guaranteed
by `SourceBufferPrivate::reenqueSamples()`.

* platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm:
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::playInternal):
* platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.h:
* platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.mm:
(WebCore::MediaSourcePrivateAVFObjC::flushActiveSourceBuffersIfNeeded):
* platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.h:
* platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm:
(WebCore::SourceBufferPrivateAVFObjC::flushIfNeeded):
(WebCore::SourceBufferPrivateAVFObjC::layerDidReceiveError):
(WebCore::SourceBufferPrivateAVFObjC::isReadyForMoreSamples):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (277423 => 277424)


--- trunk/Source/WebCore/ChangeLog	2021-05-13 05:36:57 UTC (rev 277423)
+++ trunk/Source/WebCore/ChangeLog	2021-05-13 05:37:17 UTC (rev 277424)
@@ -1,3 +1,36 @@
+2021-05-12  Peng Liu  <[email protected]>
+
+        [iPad] SourceBufferPrivateAVFObjC should not report an error to the web page when the video playback is interrupted
+        https://bugs.webkit.org/show_bug.cgi?id=225620
+
+        Reviewed by Jer Noble.
+
+        If `SourceBufferPrivateAVFObjC` reports an error to a web page when
+        `AVSampleBufferDisplayLayer` reports `AVErrorOperationInterrupted` (the playback
+        was interrupted), the web page will likely destroy the video player (and teardown
+        the video element). That behavior will lead to an empty picture-in-picture window
+        if the video was playing in picture-in-picture.
+
+        With this patch, `SourceBufferPrivateAVFObjC` will not report an error to the
+        web page in case of playback interruption. Instead, it takes a note that
+        the playback was interrupted, so that when we try to resume the playback later,
+        it will flush the `AVSampleBufferDisplayLayer` to recover the playback state.
+
+        In addition, we need to enqueue an IDR frame first before we enqueue more
+        samples after flushing the `AVSampleBufferDisplayLayer`. That is guaranteed
+        by `SourceBufferPrivate::reenqueSamples()`.
+
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm:
+        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::playInternal):
+        * platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.h:
+        * platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.mm:
+        (WebCore::MediaSourcePrivateAVFObjC::flushActiveSourceBuffersIfNeeded):
+        * platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.h:
+        * platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm:
+        (WebCore::SourceBufferPrivateAVFObjC::flushIfNeeded):
+        (WebCore::SourceBufferPrivateAVFObjC::layerDidReceiveError):
+        (WebCore::SourceBufferPrivateAVFObjC::isReadyForMoreSamples):
+
 2021-05-12  Chris Dumez  <[email protected]>
 
         Notification.requestPermission() should return a Promise

Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm (277423 => 277424)


--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm	2021-05-13 05:36:57 UTC (rev 277423)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm	2021-05-13 05:37:17 UTC (rev 277424)
@@ -322,6 +322,9 @@
     }
 
     ALWAYS_LOG(LOGIDENTIFIER);
+#if PLATFORM(IOS_FAMILY)
+    m_mediaSourcePrivate->flushActiveSourceBuffersIfNeeded();
+#endif
     m_playing = true;
     if (shouldBePlaying())
         [m_synchronizer setRate:m_rate];

Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.h (277423 => 277424)


--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.h	2021-05-13 05:36:57 UTC (rev 277423)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.h	2021-05-13 05:37:17 UTC (rev 277424)
@@ -93,6 +93,10 @@
     void setVideoLayer(AVSampleBufferDisplayLayer*);
     void setDecompressionSession(WebCoreDecompressionSession*);
 
+#if PLATFORM(IOS_FAMILY)
+    void flushActiveSourceBuffersIfNeeded();
+#endif
+
 #if ENABLE(ENCRYPTED_MEDIA)
     void cdmInstanceAttached(CDMInstance&);
     void cdmInstanceDetached(CDMInstance&);

Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.mm (277423 => 277424)


--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.mm	2021-05-13 05:36:57 UTC (rev 277423)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.mm	2021-05-13 05:37:17 UTC (rev 277424)
@@ -282,6 +282,14 @@
         m_sourceBufferWithSelectedVideo->setDecompressionSession(decompressionSession);
 }
 
+#if PLATFORM(IOS_FAMILY)
+void MediaSourcePrivateAVFObjC::flushActiveSourceBuffersIfNeeded()
+{
+    for (auto* sourceBuffer : m_activeSourceBuffers)
+        sourceBuffer->flushIfNeeded();
+}
+#endif
+
 #if ENABLE(ENCRYPTED_MEDIA)
 void MediaSourcePrivateAVFObjC::cdmInstanceAttached(CDMInstance& instance)
 {

Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.h (277423 => 277424)


--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.h	2021-05-13 05:36:57 UTC (rev 277423)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.h	2021-05-13 05:37:17 UTC (rev 277424)
@@ -109,6 +109,9 @@
     bool waitingForKey() const { return m_waitingForKey; }
 
     void flush();
+#if PLATFORM(IOS_FAMILY)
+    void flushIfNeeded();
+#endif
 
     void registerForErrorNotifications(SourceBufferPrivateAVFObjCErrorClient*);
     void unregisterForErrorNotifications(SourceBufferPrivateAVFObjCErrorClient*);
@@ -197,6 +200,9 @@
     HashMap<uint64_t, RetainPtr<AVSampleBufferAudioRenderer>, DefaultHash<uint64_t>, WTF::UnsignedWithZeroKeyHashTraits<uint64_t>> m_audioRenderers;
     ALLOW_NEW_API_WITHOUT_GUARDS_END
     RetainPtr<WebAVSampleBufferErrorListener> m_errorListener;
+#if PLATFORM(IOS_FAMILY)
+    bool m_displayLayerWasInterrupted { false };
+#endif
     RetainPtr<NSError> m_hdcpError;
     Box<BinarySemaphore> m_hasSessionSemaphore;
     Box<Semaphore> m_abortSemaphore;

Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm (277423 => 277424)


--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm	2021-05-13 05:36:57 UTC (rev 277423)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm	2021-05-13 05:37:17 UTC (rev 277424)
@@ -928,6 +928,27 @@
         flushAudio(renderer.get());
 }
 
+#if PLATFORM(IOS_FAMILY)
+void SourceBufferPrivateAVFObjC::flushIfNeeded()
+{
+    if (!m_displayLayerWasInterrupted)
+        return;
+
+    m_displayLayerWasInterrupted = false;
+    if (m_videoTracks.size())
+        flushVideo();
+
+    // We initiatively enqueue samples instead of waiting for the
+    // media data requests from m_decompressionSession and m_displayLayer.
+    // In addition, we need to enqueue a sync sample (IDR video frame) first.
+    if (m_decompressionSession)
+        m_decompressionSession->stopRequestingMediaData();
+    [m_displayLayer stopRequestingMediaData];
+
+    reenqueSamples(AtomString::number(m_enabledVideoTrackID));
+}
+#endif
+
 void SourceBufferPrivateAVFObjC::registerForErrorNotifications(SourceBufferPrivateAVFObjCErrorClient* client)
 {
     ASSERT(!m_errorClients.contains(client));
@@ -944,6 +965,13 @@
 {
     ERROR_LOG(LOGIDENTIFIER, [[error description] UTF8String]);
 
+#if PLATFORM(IOS_FAMILY)
+    if ([layer status] == AVQueuedSampleBufferRenderingStatusFailed && [[error domain] isEqualToString:@"AVFoundationErrorDomain"] && [error code] == AVErrorOperationInterrupted) {
+        m_displayLayerWasInterrupted = true;
+        return;
+    }
+#endif
+
     // FIXME(142246): Remove the following once <rdar://problem/20027434> is resolved.
     bool anyIgnored = false;
     for (auto& client : m_errorClients) {
@@ -1174,6 +1202,11 @@
 {
     auto trackID = parseIntegerAllowingTrailingJunk<uint64_t>(trackIDString).valueOr(0);
     if (trackID == m_enabledVideoTrackID) {
+#if PLATFORM(IOS_FAMILY)
+        if (m_displayLayerWasInterrupted)
+            return false;
+#endif
+
         if (m_decompressionSession)
             return m_decompressionSession->isReadyForMoreMediaData();
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to