[MSE][Mac] In SourceBufferPrivateAVFObjC::abort(), support reseting parser to the...
authorjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 28 Sep 2016 14:52:48 +0000 (14:52 +0000)
committerjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 28 Sep 2016 14:52:48 +0000 (14:52 +0000)
https://bugs.webkit.org/show_bug.cgi?id=135164

Reviewed by Eric Carlson.

Source/WebCore:

Test: media/media-source/media-source-abort-resets-parser.html

Use the -[AVStreamDataParser appendStreamData:withFlags:] to implement "resetting" the parser. In this case,
the parser isn't explicitly reset during resetParserState(), but rather a flag is set so that the next append
signals a data discontinuity, and the parser is reset at that point.

Because a previous append operation may be in-flight during this abort(), care must be taken to invalidate any
operations which may have already started on a background thread. So SourceBufferPrivateAVFObjC will use a
separate WeakPtrFactory for its append operations, will invalidate any outstanding WeakPtrs during an abort(),
and will block until the previous append() operation completes.

This will require the WebAVStreamDataParserListener object to occasionally have it's WeakPtr pointing back to the
SourceBufferPrivateAVFObjC to be reset after an abort(), so make that ivar an @property. Rather than passing a
RetainPtr to itself in all the callbacks it handles, the WebAVStreamDataParserListener can just pass in a copy
of its own WeakPtr (which may be invalidated during an abort()).

Break the distinct operations of "abort()" and "resetParserState()" into their own methods in SourceBufferPrivate
and all its subclasses.

* Modules/mediasource/SourceBuffer.cpp:
(WebCore::SourceBuffer::resetParserState):
(WebCore::SourceBuffer::abortIfUpdating):
* platform/graphics/SourceBufferPrivate.h:
* platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.h:
* platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm:
(-[WebAVStreamDataParserListener streamDataParser:didParseStreamDataAsAsset:]):
(-[WebAVStreamDataParserListener streamDataParser:didParseStreamDataAsAsset:withDiscontinuity:]):
(-[WebAVStreamDataParserListener streamDataParser:didFailToParseStreamDataWithError:]):
(-[WebAVStreamDataParserListener streamDataParser:didProvideMediaData:forTrackID:mediaType:flags:]):
(-[WebAVStreamDataParserListener streamDataParser:didReachEndOfTrackWithTrackID:mediaType:]):
(-[WebAVStreamDataParserListener streamDataParserWillProvideContentKeyRequestInitializationData:forTrackID:]):
(-[WebAVStreamDataParserListener streamDataParser:didProvideContentKeyRequestInitializationData:forTrackID:]):
(WebCore::SourceBufferPrivateAVFObjC::SourceBufferPrivateAVFObjC):
(WebCore::SourceBufferPrivateAVFObjC::append):
(WebCore::SourceBufferPrivateAVFObjC::abort):
(WebCore::SourceBufferPrivateAVFObjC::resetParserState):
(-[WebAVStreamDataParserListener initWithParser:parent:]): Deleted.
* platform/graphics/gstreamer/SourceBufferPrivateGStreamer.cpp:
(WebCore::SourceBufferPrivateGStreamer::resetParserState):
* platform/graphics/gstreamer/SourceBufferPrivateGStreamer.h:
* platform/mock/mediasource/MockSourceBufferPrivate.cpp:
(WebCore::MockSourceBufferPrivate::resetParserState):
* platform/mock/mediasource/MockSourceBufferPrivate.h:
* platform/spi/mac/AVFoundationSPI.h:

LayoutTests:

* media/media-source/media-source-abort-resets-parser-expected.txt: Added.
* media/media-source/media-source-abort-resets-parser.html: Added.

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

13 files changed:
LayoutTests/ChangeLog
LayoutTests/media/media-source/media-source-abort-resets-parser-expected.txt [new file with mode: 0644]
LayoutTests/media/media-source/media-source-abort-resets-parser.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/Modules/mediasource/SourceBuffer.cpp
Source/WebCore/platform/graphics/SourceBufferPrivate.h
Source/WebCore/platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.h
Source/WebCore/platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm
Source/WebCore/platform/graphics/gstreamer/SourceBufferPrivateGStreamer.cpp
Source/WebCore/platform/graphics/gstreamer/SourceBufferPrivateGStreamer.h
Source/WebCore/platform/mock/mediasource/MockSourceBufferPrivate.cpp
Source/WebCore/platform/mock/mediasource/MockSourceBufferPrivate.h
Source/WebCore/platform/spi/mac/AVFoundationSPI.h

index e885f0e..94653d5 100644 (file)
@@ -1,3 +1,13 @@
+2016-09-28  Jer Noble  <jer.noble@apple.com>
+
+        [MSE][Mac] In SourceBufferPrivateAVFObjC::abort(), support reseting parser to the last appended initialization segment.
+        https://bugs.webkit.org/show_bug.cgi?id=135164
+
+        Reviewed by Eric Carlson.
+
+        * media/media-source/media-source-abort-resets-parser-expected.txt: Added.
+        * media/media-source/media-source-abort-resets-parser.html: Added.
+
 2016-09-28  Alejandro G. Castro  <alex@igalia.com>
 
         Add WebIDL special operation support: serializer
diff --git a/LayoutTests/media/media-source/media-source-abort-resets-parser-expected.txt b/LayoutTests/media/media-source/media-source-abort-resets-parser-expected.txt
new file mode 100644 (file)
index 0000000..c7a5393
--- /dev/null
@@ -0,0 +1,24 @@
+This tests the ability of the SourceBuffer to reset the parser after an abort(). A SourceBuffer in this state should be able to accept a new initialization segment or a new media segment.
+
+RUN(video.src = URL.createObjectURL(source))
+EVENT(sourceopen)
+RUN(source.duration = loader.duration())
+RUN(sourceBuffer = source.addSourceBuffer(loader.type()))
+RUN(sourceBuffer.appendBuffer(loader.initSegment()))
+EVENT(update)
+Append a partial media segment.
+RUN(sourceBuffer.appendBuffer(loader.mediaSegment(0).slice(0, loader.mediaSegment(0).byteLength / 2)))
+EVENT(update)
+Abort and append a new initialization segment.
+RUN(sourceBuffer.abort())
+RUN(sourceBuffer.appendBuffer(loader.initSegment()))
+EVENT(update)
+Append a partial media segment.
+RUN(sourceBuffer.appendBuffer(loader.mediaSegment(0).slice(0, loader.mediaSegment(0).byteLength / 2)))
+EVENT(update)
+Abort and append a new media segment.
+RUN(sourceBuffer.abort())
+RUN(sourceBuffer.appendBuffer(loader.mediaSegment(1)))
+EVENT(update)
+END OF TEST
+
diff --git a/LayoutTests/media/media-source/media-source-abort-resets-parser.html b/LayoutTests/media/media-source/media-source-abort-resets-parser.html
new file mode 100644 (file)
index 0000000..14f2d7b
--- /dev/null
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>media-source-abort-resets-parser</title>
+    <script src="media-source-loader.js"></script>
+    <script src="../video-test.js"></script>
+    <script>
+    var loader;
+    var source;
+    var sourceBuffer;
+
+    function runTest() {
+        findMediaElement();
+
+        loader = new MediaSourceLoader('content/test-fragmented-manifest.json');
+        loader.onload = mediaDataLoaded;
+        loader.onerror = mediaDataLoadingFailed;
+    }
+
+    function mediaDataLoadingFailed() {
+        failTest('Media data loading failed');
+    }
+
+    function mediaDataLoaded() {
+        source = new MediaSource();
+        waitForEvent('sourceopen', sourceOpen, false, false, source);
+        waitForEventAndFail('error');
+        run('video.src = URL.createObjectURL(source)');
+    }
+
+    function sourceOpen() {
+        run('source.duration = loader.duration()');
+        run('sourceBuffer = source.addSourceBuffer(loader.type())');
+        waitForEventOn(sourceBuffer, 'update', sourceInitialized, false, true);
+        run('sourceBuffer.appendBuffer(loader.initSegment())');
+    }
+
+    function sourceInitialized() {
+        waitForEventOn(sourceBuffer, 'update', partialMediaSegmentAppended1, false, true);
+        consoleWrite('Append a partial media segment.')
+        run('sourceBuffer.appendBuffer(loader.mediaSegment(0).slice(0, loader.mediaSegment(0).byteLength / 2))');
+    }
+
+    function partialMediaSegmentAppended1() {
+        consoleWrite('Abort and append a new initialization segment.')
+        run('sourceBuffer.abort()');
+        run('sourceBuffer.appendBuffer(loader.initSegment())');
+        waitForEventOn(sourceBuffer, 'update', initSegmentAppended, false, true);
+    }
+
+    function initSegmentAppended() {
+        waitForEventOn(sourceBuffer, 'update', partialMediaSegmentAppended2, false, true);
+        consoleWrite('Append a partial media segment.')
+        run('sourceBuffer.appendBuffer(loader.mediaSegment(0).slice(0, loader.mediaSegment(0).byteLength / 2))');
+    }
+
+    function partialMediaSegmentAppended2() {
+        consoleWrite('Abort and append a new media segment.')
+        run('sourceBuffer.abort()');
+        run('sourceBuffer.appendBuffer(loader.mediaSegment(1))');
+        waitForEventOn(sourceBuffer, 'update', endTest, false, true);
+    }
+    </script>
+</head>
+<body onload="runTest()">
+    <div>
+        This tests the ability of the SourceBuffer to reset the parser after an abort(). A SourceBuffer in this state should be able to accept
+        a new initialization segment or a new media segment.
+    </div>
+    <video controls></video>
+</body>
+</html>
\ No newline at end of file
index 9ea60aa..3a9bbe3 100644 (file)
@@ -1,3 +1,55 @@
+2016-09-28  Jer Noble  <jer.noble@apple.com>
+
+        [MSE][Mac] In SourceBufferPrivateAVFObjC::abort(), support reseting parser to the last appended initialization segment.
+        https://bugs.webkit.org/show_bug.cgi?id=135164
+
+        Reviewed by Eric Carlson.
+
+        Test: media/media-source/media-source-abort-resets-parser.html
+
+        Use the -[AVStreamDataParser appendStreamData:withFlags:] to implement "resetting" the parser. In this case,
+        the parser isn't explicitly reset during resetParserState(), but rather a flag is set so that the next append
+        signals a data discontinuity, and the parser is reset at that point.
+
+        Because a previous append operation may be in-flight during this abort(), care must be taken to invalidate any
+        operations which may have already started on a background thread. So SourceBufferPrivateAVFObjC will use a
+        separate WeakPtrFactory for its append operations, will invalidate any outstanding WeakPtrs during an abort(),
+        and will block until the previous append() operation completes.
+
+        This will require the WebAVStreamDataParserListener object to occasionally have it's WeakPtr pointing back to the
+        SourceBufferPrivateAVFObjC to be reset after an abort(), so make that ivar an @property. Rather than passing a
+        RetainPtr to itself in all the callbacks it handles, the WebAVStreamDataParserListener can just pass in a copy
+        of its own WeakPtr (which may be invalidated during an abort()).
+
+        Break the distinct operations of "abort()" and "resetParserState()" into their own methods in SourceBufferPrivate
+        and all its subclasses.
+
+        * Modules/mediasource/SourceBuffer.cpp:
+        (WebCore::SourceBuffer::resetParserState):
+        (WebCore::SourceBuffer::abortIfUpdating):
+        * platform/graphics/SourceBufferPrivate.h:
+        * platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.h:
+        * platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm:
+        (-[WebAVStreamDataParserListener streamDataParser:didParseStreamDataAsAsset:]):
+        (-[WebAVStreamDataParserListener streamDataParser:didParseStreamDataAsAsset:withDiscontinuity:]):
+        (-[WebAVStreamDataParserListener streamDataParser:didFailToParseStreamDataWithError:]):
+        (-[WebAVStreamDataParserListener streamDataParser:didProvideMediaData:forTrackID:mediaType:flags:]):
+        (-[WebAVStreamDataParserListener streamDataParser:didReachEndOfTrackWithTrackID:mediaType:]):
+        (-[WebAVStreamDataParserListener streamDataParserWillProvideContentKeyRequestInitializationData:forTrackID:]):
+        (-[WebAVStreamDataParserListener streamDataParser:didProvideContentKeyRequestInitializationData:forTrackID:]):
+        (WebCore::SourceBufferPrivateAVFObjC::SourceBufferPrivateAVFObjC):
+        (WebCore::SourceBufferPrivateAVFObjC::append):
+        (WebCore::SourceBufferPrivateAVFObjC::abort):
+        (WebCore::SourceBufferPrivateAVFObjC::resetParserState):
+        (-[WebAVStreamDataParserListener initWithParser:parent:]): Deleted.
+        * platform/graphics/gstreamer/SourceBufferPrivateGStreamer.cpp:
+        (WebCore::SourceBufferPrivateGStreamer::resetParserState):
+        * platform/graphics/gstreamer/SourceBufferPrivateGStreamer.h:
+        * platform/mock/mediasource/MockSourceBufferPrivate.cpp:
+        (WebCore::MockSourceBufferPrivate::resetParserState):
+        * platform/mock/mediasource/MockSourceBufferPrivate.h:
+        * platform/spi/mac/AVFoundationSPI.h:
+
 2016-09-28  Michael Catanzaro  <mcatanzaro@igalia.com>
 
         [GTK] User agent should always claim to be Intel
index 7ae67fb..cf20072 100644 (file)
@@ -278,7 +278,7 @@ void SourceBuffer::resetParserState()
     // 7. Set append state to WAITING_FOR_SEGMENT.
     m_appendState = WaitingForSegment;
 
-    m_private->abort();
+    m_private->resetParserState();
 }
 
 void SourceBuffer::abort(ExceptionCode& ec)
@@ -383,6 +383,7 @@ void SourceBuffer::abortIfUpdating()
     // 4.1. Abort the buffer append algorithm if it is running.
     m_appendBufferTimer.stop();
     m_pendingAppendData.clear();
+    m_private->abort();
 
     // 4.2. Set the updating attribute to false.
     m_updating = false;
index 0a18562..15b255b 100644 (file)
@@ -52,6 +52,7 @@ public:
 
     virtual void append(const unsigned char* data, unsigned length) = 0;
     virtual void abort() = 0;
+    virtual void resetParserState() = 0;
     virtual void removedFromMediaSource() = 0;
 
     virtual MediaPlayer::ReadyState readyState() const = 0;
index c2321c1..baf5bfd 100644 (file)
@@ -29,6 +29,7 @@
 #if ENABLE(MEDIA_SOURCE) && USE(AVFOUNDATION)
 
 #include "SourceBufferPrivate.h"
+#include <dispatch/group.h>
 #include <dispatch/semaphore.h>
 #include <wtf/Deque.h>
 #include <wtf/HashMap.h>
@@ -115,6 +116,7 @@ private:
     void setClient(SourceBufferPrivateClient*) override;
     void append(const unsigned char* data, unsigned length) override;
     void abort() override;
+    void resetParserState() override;
     void removedFromMediaSource() override;
     MediaPlayer::ReadyState readyState() const override;
     void setReadyState(MediaPlayer::ReadyState) override;
@@ -139,6 +141,7 @@ private:
     Vector<SourceBufferPrivateAVFObjCErrorClient*> m_errorClients;
 
     WeakPtrFactory<SourceBufferPrivateAVFObjC> m_weakFactory;
+    WeakPtrFactory<SourceBufferPrivateAVFObjC> m_appendWeakFactory;
 
     RetainPtr<AVStreamDataParser> m_parser;
     RetainPtr<AVAsset> m_asset;
@@ -148,6 +151,7 @@ private:
     RetainPtr<WebAVSampleBufferErrorListener> m_errorListener;
     RetainPtr<NSError> m_hdcpError;
     OSObjectPtr<dispatch_semaphore_t> m_hasSessionSemaphore;
+    OSObjectPtr<dispatch_group_t> m_isAppendingGroup;
 
     MediaSourcePrivateAVFObjC* m_mediaSource;
     SourceBufferPrivateClient* m_client;
@@ -156,6 +160,7 @@ private:
     FloatSize m_cachedSize;
     FloatSize m_currentSize;
     bool m_parsingSucceeded;
+    bool m_parserStateWasReset { false };
     int m_enabledVideoTrackID;
     int m_protectedTrackID;
 };
index a97f1a7..18bdc1b 100644 (file)
@@ -131,6 +131,7 @@ SOFT_LINK_CONSTANT(AVFoundation, AVSampleBufferDisplayLayerFailedToDecodeNotific
     WeakPtr<WebCore::SourceBufferPrivateAVFObjC> _parent;
     AVStreamDataParser* _parser;
 }
+@property (assign) WeakPtr<WebCore::SourceBufferPrivateAVFObjC> parent;
 - (id)initWithParser:(AVStreamDataParser*)parser parent:(WeakPtr<WebCore::SourceBufferPrivateAVFObjC>)parent;
 @end
 
@@ -148,6 +149,8 @@ SOFT_LINK_CONSTANT(AVFoundation, AVSampleBufferDisplayLayerFailedToDecodeNotific
     return self;
 }
 
+@synthesize parent=_parent;
+
 - (void)dealloc
 {
     [_parser setDelegate:nil];
@@ -166,12 +169,11 @@ SOFT_LINK_CONSTANT(AVFoundation, AVSampleBufferDisplayLayerFailedToDecodeNotific
     UNUSED_PARAM(streamDataParser);
 #endif
     ASSERT(streamDataParser == _parser);
-    RetainPtr<WebAVStreamDataParserListener> protectedSelf = self;
 
     RetainPtr<AVAsset*> protectedAsset = asset;
-    callOnMainThread([protectedSelf = WTFMove(protectedSelf), protectedAsset = WTFMove(protectedAsset)] {
-        if (protectedSelf->_parent)
-            protectedSelf->_parent->didParseStreamDataAsAsset(protectedAsset.get());
+    callOnMainThread([parent = _parent, protectedAsset = WTFMove(protectedAsset)] {
+        if (parent)
+            parent->didParseStreamDataAsAsset(protectedAsset.get());
     });
 }
 
@@ -182,12 +184,11 @@ SOFT_LINK_CONSTANT(AVFoundation, AVSampleBufferDisplayLayerFailedToDecodeNotific
     UNUSED_PARAM(streamDataParser);
 #endif
     ASSERT(streamDataParser == _parser);
-    RetainPtr<WebAVStreamDataParserListener> protectedSelf = self;
 
     RetainPtr<AVAsset*> protectedAsset = asset;
-    callOnMainThread([protectedSelf = WTFMove(protectedSelf), protectedAsset = WTFMove(protectedAsset)] {
-        if (protectedSelf->_parent)
-            protectedSelf->_parent->didParseStreamDataAsAsset(protectedAsset.get());
+    callOnMainThread([parent = _parent, protectedAsset = WTFMove(protectedAsset)] {
+        if (parent)
+            parent->didParseStreamDataAsAsset(protectedAsset.get());
     });
 }
 
@@ -197,12 +198,11 @@ SOFT_LINK_CONSTANT(AVFoundation, AVSampleBufferDisplayLayerFailedToDecodeNotific
     UNUSED_PARAM(streamDataParser);
 #endif
     ASSERT(streamDataParser == _parser);
-    RetainPtr<WebAVStreamDataParserListener> protectedSelf = self;
 
     RetainPtr<NSError> protectedError = error;
-    callOnMainThread([protectedSelf = WTFMove(protectedSelf), protectedError = WTFMove(protectedError)] {
-        if (protectedSelf->_parent)
-            protectedSelf->_parent->didFailToParseStreamDataWithError(protectedError.get());
+    callOnMainThread([parent = _parent, protectedError = WTFMove(protectedError)] {
+        if (parent)
+            parent->didFailToParseStreamDataWithError(protectedError.get());
     });
 }
 
@@ -212,13 +212,12 @@ SOFT_LINK_CONSTANT(AVFoundation, AVSampleBufferDisplayLayerFailedToDecodeNotific
     UNUSED_PARAM(streamDataParser);
 #endif
     ASSERT(streamDataParser == _parser);
-    RetainPtr<WebAVStreamDataParserListener> protectedSelf = self;
 
     RetainPtr<CMSampleBufferRef> protectedSample = sample;
     String mediaType = nsMediaType;
-    callOnMainThread([protectedSelf = WTFMove(protectedSelf), protectedSample = WTFMove(protectedSample), trackID, mediaType, flags] {
-        if (protectedSelf->_parent)
-            protectedSelf->_parent->didProvideMediaDataForTrackID(trackID, protectedSample.get(), mediaType, flags);
+    callOnMainThread([parent = _parent, protectedSample = WTFMove(protectedSample), trackID, mediaType, flags] {
+        if (parent)
+            parent->didProvideMediaDataForTrackID(trackID, protectedSample.get(), mediaType, flags);
     });
 }
 
@@ -228,12 +227,11 @@ SOFT_LINK_CONSTANT(AVFoundation, AVSampleBufferDisplayLayerFailedToDecodeNotific
     UNUSED_PARAM(streamDataParser);
 #endif
     ASSERT(streamDataParser == _parser);
-    RetainPtr<WebAVStreamDataParserListener> protectedSelf = self;
 
     String mediaType = nsMediaType;
-    callOnMainThread([protectedSelf = WTFMove(protectedSelf), trackID, mediaType] {
-        if (protectedSelf->_parent)
-            protectedSelf->_parent->didReachEndOfTrackWithTrackID(trackID, mediaType);
+    callOnMainThread([parent = _parent, trackID, mediaType] {
+        if (parent)
+            parent->didReachEndOfTrackWithTrackID(trackID, mediaType);
     });
 }
 
@@ -246,10 +244,9 @@ SOFT_LINK_CONSTANT(AVFoundation, AVSampleBufferDisplayLayerFailedToDecodeNotific
 
     // We must call synchronously to the main thread, as the AVStreamSession must be associated
     // with the streamDataParser before the delegate method returns.
-    RetainPtr<WebAVStreamDataParserListener> strongSelf = self;
-    dispatch_sync(dispatch_get_main_queue(), [strongSelf, trackID]() {
-        if (strongSelf->_parent)
-            strongSelf->_parent->willProvideContentKeyRequestInitializationDataForTrackID(trackID);
+    dispatch_sync(dispatch_get_main_queue(), [parent = _parent, trackID]() {
+        if (parent)
+            parent->willProvideContentKeyRequestInitializationDataForTrackID(trackID);
     });
 }
 
@@ -259,12 +256,11 @@ SOFT_LINK_CONSTANT(AVFoundation, AVSampleBufferDisplayLayerFailedToDecodeNotific
     UNUSED_PARAM(streamDataParser);
 #endif
     ASSERT(streamDataParser == _parser);
-    RetainPtr<WebAVStreamDataParserListener> protectedSelf = self;
 
     OSObjectPtr<dispatch_semaphore_t> hasSessionSemaphore = adoptOSObject(dispatch_semaphore_create(0));
-    callOnMainThread([protectedSelf = WTFMove(protectedSelf), protectedInitData = RetainPtr<NSData>(initData), trackID, hasSessionSemaphore] {
-        if (protectedSelf->_parent)
-            protectedSelf->_parent->didProvideContentKeyRequestInitializationDataForTrackID(protectedInitData.get(), trackID, hasSessionSemaphore);
+    callOnMainThread([parent = _parent, protectedInitData = RetainPtr<NSData>(initData), trackID, hasSessionSemaphore] {
+        if (parent)
+            parent->didProvideContentKeyRequestInitializationDataForTrackID(protectedInitData.get(), trackID, hasSessionSemaphore);
     });
     dispatch_semaphore_wait(hasSessionSemaphore.get(), DISPATCH_TIME_FOREVER);
 }
@@ -461,9 +457,11 @@ RefPtr<SourceBufferPrivateAVFObjC> SourceBufferPrivateAVFObjC::create(MediaSourc
 
 SourceBufferPrivateAVFObjC::SourceBufferPrivateAVFObjC(MediaSourcePrivateAVFObjC* parent)
     : m_weakFactory(this)
+    , m_appendWeakFactory(this)
     , m_parser(adoptNS([allocAVStreamDataParserInstance() init]))
     , m_delegate(adoptNS([[WebAVStreamDataParserListener alloc] initWithParser:m_parser.get() parent:createWeakPtr()]))
     , m_errorListener(adoptNS([[WebAVSampleBufferErrorListener alloc] initWithParent:this]))
+    , m_isAppendingGroup(adoptOSObject(dispatch_group_create()))
     , m_mediaSource(parent)
     , m_client(0)
     , m_parsingSucceeded(true)
@@ -641,21 +639,26 @@ void SourceBufferPrivateAVFObjC::append(const unsigned char* data, unsigned leng
     LOG(MediaSource, "SourceBufferPrivateAVFObjC::append(%p) - data:%p, length:%d", this, data, length);
 
     RetainPtr<NSData> nsData = adoptNS([[NSData alloc] initWithBytes:data length:length]);
-    WeakPtr<SourceBufferPrivateAVFObjC> weakThis = createWeakPtr();
+    WeakPtr<SourceBufferPrivateAVFObjC> weakThis = m_appendWeakFactory.createWeakPtr();
     RetainPtr<AVStreamDataParser> parser = m_parser;
     RetainPtr<WebAVStreamDataParserListener> delegate = m_delegate;
 
     m_parsingSucceeded = true;
+    dispatch_group_enter(m_isAppendingGroup.get());
 
-    dispatch_async(globalDataParserQueue(), [nsData, weakThis, parser, delegate] {
-
-        [parser appendStreamData:nsData.get()];
+    dispatch_async(globalDataParserQueue(), [nsData, weakThis, parser, delegate, isAppendingGroup = m_isAppendingGroup, parserStateWasReset = m_parserStateWasReset] {
+        if (parserStateWasReset)
+            [parser appendStreamData:nsData.get() withFlags:AVStreamDataParserStreamDataDiscontinuity];
+        else
+            [parser appendStreamData:nsData.get()];
 
         callOnMainThread([weakThis] {
             if (weakThis)
                 weakThis->appendCompleted();
         });
+        dispatch_group_leave(isAppendingGroup.get());
     });
+    m_parserStateWasReset = false;
 }
 
 void SourceBufferPrivateAVFObjC::appendCompleted()
@@ -669,12 +672,19 @@ void SourceBufferPrivateAVFObjC::appendCompleted()
 
 void SourceBufferPrivateAVFObjC::abort()
 {
-    // The parser does not have a mechanism for resetting to a clean state, so destroy and re-create it.
-    // FIXME(135164): Support resetting parser to the last appended initialization segment.
-    destroyParser();
+    // The parsing queue may be blocked waiting for the main thread to provide it a AVStreamSession. We
+    // were asked to abort, and that cancels all outstanding append operations. Without cancelling this
+    // semaphore, the m_isAppendingGroup wait operation will deadlock.
+    if (m_hasSessionSemaphore)
+        dispatch_semaphore_signal(m_hasSessionSemaphore.get());
+    dispatch_group_wait(m_isAppendingGroup.get(), DISPATCH_TIME_FOREVER);
+    m_appendWeakFactory.revokeAll();
+    m_delegate.get().parent = m_appendWeakFactory.createWeakPtr();
+}
 
-    m_parser = adoptNS([allocAVStreamDataParserInstance() init]);
-    m_delegate = adoptNS([[WebAVStreamDataParserListener alloc] initWithParser:m_parser.get() parent:createWeakPtr()]);
+void SourceBufferPrivateAVFObjC::resetParserState()
+{
+    m_parserStateWasReset = true;
 }
 
 void SourceBufferPrivateAVFObjC::destroyParser()
index ed004a3..c9dd7fd 100644 (file)
@@ -75,6 +75,11 @@ void SourceBufferPrivateGStreamer::abort()
     notImplemented();
 }
 
+void SourceBufferPrivateGStreamer::resetParserState()
+{
+    notImplemented();
+}
+
 void SourceBufferPrivateGStreamer::removedFromMediaSource()
 {
     m_client->removedFromMediaSource(this);
index d4ecede..282f86c 100644 (file)
@@ -50,6 +50,7 @@ public:
 
     virtual void append(const unsigned char* data, unsigned length);
     virtual void abort();
+    virtual void resetParserState();
     virtual void removedFromMediaSource();
 
     virtual MediaPlayer::ReadyState readyState() const;
index 623afb3..e42f3ca 100644 (file)
@@ -203,6 +203,10 @@ void MockSourceBufferPrivate::abort()
 {
 }
 
+void MockSourceBufferPrivate::resetParserState()
+{
+}
+
 void MockSourceBufferPrivate::removedFromMediaSource()
 {
     if (m_mediaSource)
index ae0c66c..8380961 100644 (file)
@@ -63,6 +63,7 @@ private:
     void setClient(SourceBufferPrivateClient*) override;
     void append(const unsigned char* data, unsigned length) override;
     void abort() override;
+    void resetParserState() override;
     void removedFromMediaSource() override;
     MediaPlayer::ReadyState readyState() const override;
     void setReadyState(MediaPlayer::ReadyState) override;
index 72e140d..eaba816 100644 (file)
@@ -116,9 +116,14 @@ typedef NS_ENUM(NSUInteger, AVStreamDataParserOutputMediaDataFlags) {
     AVStreamDataParserOutputMediaDataReserved = 1 << 0
 };
 
+typedef NS_ENUM(NSUInteger, AVStreamDataParserStreamDataFlags) {
+    AVStreamDataParserStreamDataDiscontinuity = 1 << 0,
+};
+
 @interface AVStreamDataParser : NSObject
 - (void)setDelegate:(nullable id<AVStreamDataParserOutputHandling>)delegate;
 - (void)appendStreamData:(NSData *)data;
+- (void)appendStreamData:(NSData *)data withFlags:(AVStreamDataParserStreamDataFlags)flags;
 - (void)setShouldProvideMediaData:(BOOL)shouldProvideMediaData forTrackID:(CMPersistentTrackID)trackID;
 - (BOOL)shouldProvideMediaDataForTrackID:(CMPersistentTrackID)trackID;
 - (void)providePendingMediaData;