Title: [263891] trunk
Revision
263891
Author
[email protected]
Date
2020-07-03 05:01:41 -0700 (Fri, 03 Jul 2020)

Log Message

MediaRecorder stopRecorder() returns empty Blob after first use
https://bugs.webkit.org/show_bug.cgi?id=212274
<rdar://problem/63601298>

Reviewed by Eric Carlson.

Source/WebCore:

Refactor code to create/destroy MediaRecorderPrivate on MediaRecorder start/stop.
This allows reusing a MediaRecorder after a stop and restarting with a clean state.

We introduce MediaRecorderPrivate::startRecording to do the initialization,
which allows to fix a potential ref cycle as part of the error callback handling.

Make some improvements to the platform implementation, in particular add default initialization to all fields.
Align the code using AudioConverterRef to what is done in AudioSampleDataSource.
Also call VTCompressionSessionInvalidate when destroying the VideoSampleBufferCompressor.

Test: http/wpt/mediarecorder/MediaRecorder-multiple-start-stop.html

* Modules/mediarecorder/MediaRecorder.cpp:
(WebCore::MediaRecorder::create):
(WebCore::MediaRecorder::MediaRecorder):
(WebCore::MediaRecorder::startRecording):
(WebCore::MediaRecorder::stopRecording):
(WebCore::MediaRecorder::requestData):
* Modules/mediarecorder/MediaRecorder.h:
* platform/mediarecorder/MediaRecorderPrivateMock.cpp:
(WebCore::MediaRecorderPrivateMock::fetchData):
* platform/mediarecorder/MediaRecorderPrivate.h:
(WebCore::MediaRecorderPrivate::startRecording):
* platform/mediarecorder/cocoa/AudioSampleBufferCompressor.h:
* platform/mediarecorder/cocoa/AudioSampleBufferCompressor.mm:
(WebCore::AudioSampleBufferCompressor::~AudioSampleBufferCompressor):
(WebCore::AudioSampleBufferCompressor::initAudioConverterForSourceFormatDescription):
(WebCore::AudioSampleBufferCompressor::attachPrimingTrimsIfNeeded):
(WebCore::AudioSampleBufferCompressor::gradualDecoderRefreshCount):
(WebCore::AudioSampleBufferCompressor::sampleBufferWithNumPackets):
(WebCore::AudioSampleBufferCompressor::processSampleBuffersUntilLowWaterTime):
* platform/mediarecorder/cocoa/MediaRecorderPrivateWriterCocoa.h:
* platform/mediarecorder/cocoa/MediaRecorderPrivateWriterCocoa.mm:
(WebCore::MediaRecorderPrivateWriter::stopRecording):
* platform/mediarecorder/cocoa/VideoSampleBufferCompressor.mm:
(WebCore::VideoSampleBufferCompressor::~VideoSampleBufferCompressor):

Source/WebKit:

Update implementation to do initialization as part of startRecording.

* GPUProcess/webrtc/RemoteMediaRecorderManager.cpp:
(WebKit::RemoteMediaRecorderManager::releaseRecorder):
Remove ASSERT as recorder creation in WebProcess is always ok while creation in GPUProcess may fail and m_recorders may not be populated.
* WebProcess/GPU/webrtc/MediaRecorderPrivate.cpp:
(WebKit::MediaRecorderPrivate::MediaRecorderPrivate):
(WebKit::MediaRecorderPrivate::startRecording):
* WebProcess/GPU/webrtc/MediaRecorderPrivate.h:

LayoutTests:

* http/wpt/mediarecorder/MediaRecorder-multiple-start-stop-expected.txt: Added.
* http/wpt/mediarecorder/MediaRecorder-multiple-start-stop.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (263890 => 263891)


--- trunk/LayoutTests/ChangeLog	2020-07-03 09:35:48 UTC (rev 263890)
+++ trunk/LayoutTests/ChangeLog	2020-07-03 12:01:41 UTC (rev 263891)
@@ -1,3 +1,14 @@
+2020-07-03  Youenn Fablet  <[email protected]>
+
+        MediaRecorder stopRecorder() returns empty Blob after first use
+        https://bugs.webkit.org/show_bug.cgi?id=212274
+        <rdar://problem/63601298>
+
+        Reviewed by Eric Carlson.
+
+        * http/wpt/mediarecorder/MediaRecorder-multiple-start-stop-expected.txt: Added.
+        * http/wpt/mediarecorder/MediaRecorder-multiple-start-stop.html: Added.
+
 2020-07-03  Diego Pino Garcia  <[email protected]>
 
         Unreviewed test gardening. Update test expectations and baselines after r263858.

Added: trunk/LayoutTests/http/wpt/mediarecorder/MediaRecorder-multiple-start-stop-expected.txt (0 => 263891)


--- trunk/LayoutTests/http/wpt/mediarecorder/MediaRecorder-multiple-start-stop-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/mediarecorder/MediaRecorder-multiple-start-stop-expected.txt	2020-07-03 12:01:41 UTC (rev 263891)
@@ -0,0 +1,3 @@
+
+PASS Make sure that MediaRecorder can be used after stopping it once 
+

Added: trunk/LayoutTests/http/wpt/mediarecorder/MediaRecorder-multiple-start-stop.html (0 => 263891)


--- trunk/LayoutTests/http/wpt/mediarecorder/MediaRecorder-multiple-start-stop.html	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/mediarecorder/MediaRecorder-multiple-start-stop.html	2020-07-03 12:01:41 UTC (rev 263891)
@@ -0,0 +1,49 @@
+<!doctype html>
+<html>
+<head>
+    <title>MediaRecorder Dataavailable</title>
+    <link rel="help" href=""
+    <script src=""
+    <script src=""
+</head>
+<body>
+<canvas id="canvas" width="200" height="200">
+</canvas>
+<script>
+    promise_test(async t => {
+        const video = await navigator.mediaDevices.getUserMedia({ audio : true, video : true });
+        const recorder = new MediaRecorder(video);
+
+        assert_equals(recorder.stream, video);
+
+        let promise = new Promise(resolve => {
+            recorder._ondataavailable_ = t.step_func(blobEvent => {
+                if (blobEvent.data.size)
+                    resolve();
+            });
+        });
+
+        recorder.start();
+        let timer = setInterval(() => recorder.requestData(), 10);
+        await promise;
+        clearInterval(timer);
+
+        recorder.stop();
+
+        promise = new Promise(resolve => {
+            recorder._ondataavailable_ = t.step_func(blobEvent => {
+                if (blobEvent.data.size)
+                    resolve();
+            });
+        });
+
+        recorder.start();
+        timer = setInterval(() => recorder.requestData(), 10);
+        await promise;
+        clearInterval(timer);
+
+        recorder.stop();
+    }, 'Make sure that MediaRecorder can be used after stopping it once');
+</script>
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (263890 => 263891)


--- trunk/Source/WebCore/ChangeLog	2020-07-03 09:35:48 UTC (rev 263890)
+++ trunk/Source/WebCore/ChangeLog	2020-07-03 12:01:41 UTC (rev 263891)
@@ -1,3 +1,48 @@
+2020-07-03  Youenn Fablet  <[email protected]>
+
+        MediaRecorder stopRecorder() returns empty Blob after first use
+        https://bugs.webkit.org/show_bug.cgi?id=212274
+        <rdar://problem/63601298>
+
+        Reviewed by Eric Carlson.
+
+        Refactor code to create/destroy MediaRecorderPrivate on MediaRecorder start/stop.
+        This allows reusing a MediaRecorder after a stop and restarting with a clean state.
+
+        We introduce MediaRecorderPrivate::startRecording to do the initialization,
+        which allows to fix a potential ref cycle as part of the error callback handling.
+
+        Make some improvements to the platform implementation, in particular add default initialization to all fields.
+        Align the code using AudioConverterRef to what is done in AudioSampleDataSource.
+        Also call VTCompressionSessionInvalidate when destroying the VideoSampleBufferCompressor.
+
+        Test: http/wpt/mediarecorder/MediaRecorder-multiple-start-stop.html
+
+        * Modules/mediarecorder/MediaRecorder.cpp:
+        (WebCore::MediaRecorder::create):
+        (WebCore::MediaRecorder::MediaRecorder):
+        (WebCore::MediaRecorder::startRecording):
+        (WebCore::MediaRecorder::stopRecording):
+        (WebCore::MediaRecorder::requestData):
+        * Modules/mediarecorder/MediaRecorder.h:
+        * platform/mediarecorder/MediaRecorderPrivateMock.cpp:
+        (WebCore::MediaRecorderPrivateMock::fetchData):
+        * platform/mediarecorder/MediaRecorderPrivate.h:
+        (WebCore::MediaRecorderPrivate::startRecording):
+        * platform/mediarecorder/cocoa/AudioSampleBufferCompressor.h:
+        * platform/mediarecorder/cocoa/AudioSampleBufferCompressor.mm:
+        (WebCore::AudioSampleBufferCompressor::~AudioSampleBufferCompressor):
+        (WebCore::AudioSampleBufferCompressor::initAudioConverterForSourceFormatDescription):
+        (WebCore::AudioSampleBufferCompressor::attachPrimingTrimsIfNeeded):
+        (WebCore::AudioSampleBufferCompressor::gradualDecoderRefreshCount):
+        (WebCore::AudioSampleBufferCompressor::sampleBufferWithNumPackets):
+        (WebCore::AudioSampleBufferCompressor::processSampleBuffersUntilLowWaterTime):
+        * platform/mediarecorder/cocoa/MediaRecorderPrivateWriterCocoa.h:
+        * platform/mediarecorder/cocoa/MediaRecorderPrivateWriterCocoa.mm:
+        (WebCore::MediaRecorderPrivateWriter::stopRecording):
+        * platform/mediarecorder/cocoa/VideoSampleBufferCompressor.mm:
+        (WebCore::VideoSampleBufferCompressor::~VideoSampleBufferCompressor):
+
 2020-07-03  Said Abou-Hallawa  <[email protected]>
 
         Change the names of MIMETypeRegistry methods to comply with webkit naming style

Modified: trunk/Source/WebCore/Modules/mediarecorder/MediaRecorder.cpp (263890 => 263891)


--- trunk/Source/WebCore/Modules/mediarecorder/MediaRecorder.cpp	2020-07-03 09:35:48 UTC (rev 263890)
+++ trunk/Source/WebCore/Modules/mediarecorder/MediaRecorder.cpp	2020-07-03 12:01:41 UTC (rev 263891)
@@ -51,11 +51,8 @@
     auto privateInstance = MediaRecorder::createMediaRecorderPrivate(document, stream->privateStream());
     if (!privateInstance)
         return Exception { NotSupportedError, "The MediaRecorder is unsupported on this platform"_s };
-    auto recorder = adoptRef(*new MediaRecorder(document, WTFMove(stream), WTFMove(privateInstance), WTFMove(options)));
+    auto recorder = adoptRef(*new MediaRecorder(document, WTFMove(stream), WTFMove(options)));
     recorder->suspendIfNeeded();
-    recorder->m_private->setErrorCallback([recorder](auto&& exception) mutable {
-        recorder->dispatchError(WTFMove(*exception));
-    });
     return recorder;
 }
 
@@ -82,11 +79,10 @@
 #endif
 }
 
-MediaRecorder::MediaRecorder(Document& document, Ref<MediaStream>&& stream, std::unique_ptr<MediaRecorderPrivate>&& privateImpl, Options&& option)
+MediaRecorder::MediaRecorder(Document& document, Ref<MediaStream>&& stream, Options&& option)
     : ActiveDOMObject(document)
     , m_options(WTFMove(option))
     , m_stream(WTFMove(stream))
-    , m_private(WTFMove(privateImpl))
 {
     m_tracks = WTF::map(m_stream->getTracks(), [] (auto&& track) -> Ref<MediaStreamTrackPrivate> {
         return track->privateTrack();
@@ -132,9 +128,26 @@
 ExceptionOr<void> MediaRecorder::startRecording(Optional<int> timeslice)
 {
     UNUSED_PARAM(timeslice);
+    if (!m_isActive)
+        return Exception { InvalidStateError, "The MediaRecorder is not active"_s };
+
     if (state() != RecordingState::Inactive)
         return Exception { InvalidStateError, "The MediaRecorder's state must be inactive in order to start recording"_s };
-    
+
+    ASSERT(!m_private);
+    m_private = createMediaRecorderPrivate(*document(), m_stream->privateStream());
+
+    if (!m_private)
+        return Exception { NotSupportedError, "The MediaRecorder is unsupported on this platform"_s };
+
+    m_private->startRecording([this, pendingActivity = makePendingActivity(*this)](auto&& exception) mutable {
+        if (!m_isActive || !exception)
+            return;
+
+        stopRecordingInternal();
+        dispatchError(WTFMove(*exception));
+    });
+
     for (auto& track : m_tracks)
         track->addObserver(*this);
 
@@ -146,22 +159,17 @@
 {
     if (state() == RecordingState::Inactive)
         return Exception { InvalidStateError, "The MediaRecorder's state cannot be inactive"_s };
-    
-    queueTaskKeepingObjectAlive(*this, TaskSource::Networking, [this] {
-        if (!m_isActive || state() == RecordingState::Inactive)
-            return;
 
-        stopRecordingInternal();
-        ASSERT(m_state == RecordingState::Inactive);
-        m_private->fetchData([this, pendingActivity = makePendingActivity(*this)](auto&& buffer, auto& mimeType) {
+    stopRecordingInternal();
+    auto& privateRecorder = *m_private;
+    privateRecorder.fetchData([this, pendingActivity = makePendingActivity(*this), privateRecorder = WTFMove(m_private)](auto&& buffer, auto& mimeType) {
+        queueTaskKeepingObjectAlive(*this, TaskSource::Networking, [this, buffer = WTFMove(buffer), mimeType]() mutable {
             if (!m_isActive)
                 return;
-    
             dispatchEvent(BlobEvent::create(eventNames().dataavailableEvent, Event::CanBubble::No, Event::IsCancelable::No, buffer ? Blob::create(buffer.releaseNonNull(), mimeType) : Blob::create()));
 
             if (!m_isActive)
                 return;
-
             dispatchEvent(Event::create(eventNames().stopEvent, Event::CanBubble::No, Event::IsCancelable::No));
         });
     });
@@ -174,10 +182,12 @@
         return Exception { InvalidStateError, "The MediaRecorder's state cannot be inactive"_s };
 
     m_private->fetchData([this, pendingActivity = makePendingActivity(*this)](auto&& buffer, auto& mimeType) {
-        if (!m_isActive)
-            return;
+        queueTaskKeepingObjectAlive(*this, TaskSource::Networking, [this, buffer = WTFMove(buffer), mimeType]() mutable {
+            if (!m_isActive)
+                return;
 
-        dispatchEvent(BlobEvent::create(eventNames().dataavailableEvent, Event::CanBubble::No, Event::IsCancelable::No, buffer ? Blob::create(buffer.releaseNonNull(), mimeType) : Blob::create()));
+            dispatchEvent(BlobEvent::create(eventNames().dataavailableEvent, Event::CanBubble::No, Event::IsCancelable::No, buffer ? Blob::create(buffer.releaseNonNull(), mimeType) : Blob::create()));
+        });
     });
     return { };
 }

Modified: trunk/Source/WebCore/Modules/mediarecorder/MediaRecorder.h (263890 => 263891)


--- trunk/Source/WebCore/Modules/mediarecorder/MediaRecorder.h	2020-07-03 09:35:48 UTC (rev 263890)
+++ trunk/Source/WebCore/Modules/mediarecorder/MediaRecorder.h	2020-07-03 12:01:41 UTC (rev 263891)
@@ -75,7 +75,7 @@
     MediaStream& stream() { return m_stream.get(); }
 
 private:
-    MediaRecorder(Document&, Ref<MediaStream>&&, std::unique_ptr<MediaRecorderPrivate>&&, Options&& = { });
+    MediaRecorder(Document&, Ref<MediaStream>&&, Options&& = { });
 
     static std::unique_ptr<MediaRecorderPrivate> createMediaRecorderPrivate(Document&, MediaStreamPrivate&);
     

Modified: trunk/Source/WebCore/platform/mediarecorder/MediaRecorderPrivate.h (263890 => 263891)


--- trunk/Source/WebCore/platform/mediarecorder/MediaRecorderPrivate.h	2020-07-03 09:35:48 UTC (rev 263890)
+++ trunk/Source/WebCore/platform/mediarecorder/MediaRecorderPrivate.h	2020-07-03 12:01:41 UTC (rev 263891)
@@ -60,16 +60,13 @@
     virtual void fetchData(FetchDataCallback&&) = 0;
     virtual void stopRecording() = 0;
 
-    using ErrorCallback = Function<void(Optional<Exception>&&)>;
-    void setErrorCallback(ErrorCallback&& errorCallback) { m_errorCallback = WTFMove(errorCallback); }
+    using ErrorCallback = CompletionHandler<void(Optional<Exception>&&)>;
+    virtual void startRecording(ErrorCallback&& callback) { callback({ }); }
 
 protected:
     void setAudioSource(RefPtr<RealtimeMediaSource>&&);
     void setVideoSource(RefPtr<RealtimeMediaSource>&&);
 
-protected:
-    ErrorCallback m_errorCallback;
-
 private:
     RefPtr<RealtimeMediaSource> m_audioSource;
     RefPtr<RealtimeMediaSource> m_videoSource;

Modified: trunk/Source/WebCore/platform/mediarecorder/MediaRecorderPrivateMock.cpp (263890 => 263891)


--- trunk/Source/WebCore/platform/mediarecorder/MediaRecorderPrivateMock.cpp	2020-07-03 09:35:48 UTC (rev 263890)
+++ trunk/Source/WebCore/platform/mediarecorder/MediaRecorderPrivateMock.cpp	2020-07-03 12:01:41 UTC (rev 263891)
@@ -83,11 +83,15 @@
 
 void MediaRecorderPrivateMock::fetchData(FetchDataCallback&& completionHandler)
 {
-    auto locker = holdLock(m_bufferLock);
-    Vector<uint8_t> value(m_buffer.length());
-    memcpy(value.data(), m_buffer.characters8(), m_buffer.length());
-    m_buffer.clear();
-    completionHandler(SharedBuffer::create(WTFMove(value)), mimeType());
+    RefPtr<SharedBuffer> buffer;
+    {
+        auto locker = holdLock(m_bufferLock);
+        Vector<uint8_t> value(m_buffer.length());
+        memcpy(value.data(), m_buffer.characters8(), m_buffer.length());
+        m_buffer.clear();
+        buffer = SharedBuffer::create(WTFMove(value));
+    }
+    completionHandler(WTFMove(buffer), mimeType());
 }
 
 const String& MediaRecorderPrivateMock::mimeType()

Modified: trunk/Source/WebCore/platform/mediarecorder/cocoa/AudioSampleBufferCompressor.h (263890 => 263891)


--- trunk/Source/WebCore/platform/mediarecorder/cocoa/AudioSampleBufferCompressor.h	2020-07-03 09:35:48 UTC (rev 263890)
+++ trunk/Source/WebCore/platform/mediarecorder/cocoa/AudioSampleBufferCompressor.h	2020-07-03 12:01:41 UTC (rev 263891)
@@ -60,13 +60,13 @@
     OSStatus provideSourceDataNumOutputPackets(UInt32*, AudioBufferList*, AudioStreamPacketDescription**);
 
     dispatch_queue_t m_serialDispatchQueue;
-    CMTime m_lowWaterTime;
+    CMTime m_lowWaterTime { kCMTimeInvalid };
 
     RetainPtr<CMBufferQueueRef> m_outputBufferQueue;
     RetainPtr<CMBufferQueueRef> m_inputBufferQueue;
     bool m_isEncoding { false };
 
-    RetainPtr<AudioConverterRef> m_converter;
+    AudioConverterRef m_converter { nullptr };
     AudioStreamBasicDescription m_sourceFormat;
     AudioStreamBasicDescription m_destinationFormat;
     RetainPtr<CMFormatDescriptionRef> m_destinationFormatDescription;
@@ -74,9 +74,9 @@
     UInt32 m_maxOutputPacketSize { 0 };
     Vector<AudioStreamPacketDescription> m_destinationPacketDescriptions;
 
-    CMTime m_currentNativePresentationTimeStamp;
-    CMTime m_currentOutputPresentationTimeStamp;
-    CMTime m_remainingPrimeDuration;
+    CMTime m_currentNativePresentationTimeStamp { kCMTimeInvalid };
+    CMTime m_currentOutputPresentationTimeStamp { kCMTimeInvalid };
+    CMTime m_remainingPrimeDuration { kCMTimeInvalid };
 
     Vector<char> m_sourceBuffer;
     Vector<char> m_destinationBuffer;

Modified: trunk/Source/WebCore/platform/mediarecorder/cocoa/AudioSampleBufferCompressor.mm (263890 => 263891)


--- trunk/Source/WebCore/platform/mediarecorder/cocoa/AudioSampleBufferCompressor.mm	2020-07-03 09:35:48 UTC (rev 263890)
+++ trunk/Source/WebCore/platform/mediarecorder/cocoa/AudioSampleBufferCompressor.mm	2020-07-03 12:01:41 UTC (rev 263891)
@@ -58,6 +58,10 @@
 AudioSampleBufferCompressor::~AudioSampleBufferCompressor()
 {
     dispatch_release(m_serialDispatchQueue);
+    if (m_converter) {
+        AudioConverterDispose(m_converter);
+        m_converter = nullptr;
+    }
 }
 
 bool AudioSampleBufferCompressor::initialize(CMBufferQueueTriggerCallback callback, void* callbackObject)
@@ -112,12 +116,12 @@
         RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor AudioConverterNew failed with %d", error);
         return false;
     }
-    m_converter = adoptCF(converter);
+    m_converter = converter;
 
     size_t cookieSize = 0;
     const void *cookie = CMAudioFormatDescriptionGetMagicCookie(formatDescription, &cookieSize);
     if (cookieSize) {
-        if (auto error = AudioConverterSetProperty(m_converter.get(), kAudioConverterDecompressionMagicCookie, (UInt32)cookieSize, cookie)) {
+        if (auto error = AudioConverterSetProperty(m_converter, kAudioConverterDecompressionMagicCookie, (UInt32)cookieSize, cookie)) {
             RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor setting kAudioConverterDecompressionMagicCookie failed with %d", error);
             return false;
         }
@@ -124,7 +128,7 @@
     }
 
     size = sizeof(m_sourceFormat);
-    if (auto error = AudioConverterGetProperty(m_converter.get(), kAudioConverterCurrentInputStreamDescription, &size, &m_sourceFormat)) {
+    if (auto error = AudioConverterGetProperty(m_converter, kAudioConverterCurrentInputStreamDescription, &size, &m_sourceFormat)) {
         RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor getting kAudioConverterCurrentInputStreamDescription failed with %d", error);
         return false;
     }
@@ -135,7 +139,7 @@
     }
 
     size = sizeof(m_destinationFormat);
-    if (auto error = AudioConverterGetProperty(m_converter.get(), kAudioConverterCurrentOutputStreamDescription, &size, &m_destinationFormat)) {
+    if (auto error = AudioConverterGetProperty(m_converter, kAudioConverterCurrentOutputStreamDescription, &size, &m_destinationFormat)) {
         RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor getting kAudioConverterCurrentOutputStreamDescription failed with %d", error);
         return false;
     }
@@ -149,7 +153,7 @@
             outputBitRate = 32000;
 
         size = sizeof(outputBitRate);
-        if (auto error = AudioConverterSetProperty(m_converter.get(), kAudioConverterEncodeBitRate, size, &outputBitRate)) {
+        if (auto error = AudioConverterSetProperty(m_converter, kAudioConverterEncodeBitRate, size, &outputBitRate)) {
             RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor setting kAudioConverterEncodeBitRate failed with %d", error);
             return false;
         }
@@ -159,7 +163,7 @@
         // If the destination format is VBR, we need to get max size per packet from the converter.
         size = sizeof(m_maxOutputPacketSize);
 
-        if (auto error = AudioConverterGetProperty(m_converter.get(), kAudioConverterPropertyMaximumOutputPacketSize, &size, &m_maxOutputPacketSize)) {
+        if (auto error = AudioConverterGetProperty(m_converter, kAudioConverterPropertyMaximumOutputPacketSize, &size, &m_maxOutputPacketSize)) {
             RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor getting kAudioConverterPropertyMaximumOutputPacketSize failed with %d", error);
             return false;
         }
@@ -189,7 +193,7 @@
         AudioConverterPrimeInfo primeInfo { 0, 0 };
         UInt32 size = sizeof(primeInfo);
 
-        if (auto error = AudioConverterGetProperty(m_converter.get(), kAudioConverterPrimeInfo, &size, &primeInfo)) {
+        if (auto error = AudioConverterGetProperty(m_converter, kAudioConverterPrimeInfo, &size, &primeInfo)) {
             RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor getting kAudioConverterPrimeInfo failed with %d", error);
             return;
         }
@@ -211,13 +215,13 @@
 {
     UInt32 delaySize = sizeof(uint32_t);
     uint32_t originalDelayMode = 0;
-    if (auto error = AudioConverterGetProperty(m_converter.get(), kAudioCodecPropertyDelayMode, &delaySize, &originalDelayMode)) {
+    if (auto error = AudioConverterGetProperty(m_converter, kAudioCodecPropertyDelayMode, &delaySize, &originalDelayMode)) {
         RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor getting kAudioCodecPropertyDelayMode failed with %d", error);
         return nil;
     }
 
     uint32_t optimalDelayMode = kAudioCodecDelayMode_Optimal;
-    if (auto error = AudioConverterSetProperty(m_converter.get(), kAudioCodecPropertyDelayMode, delaySize, &optimalDelayMode)) {
+    if (auto error = AudioConverterSetProperty(m_converter, kAudioCodecPropertyDelayMode, delaySize, &optimalDelayMode)) {
         RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor setting kAudioCodecPropertyDelayMode failed with %d", error);
         return nil;
     }
@@ -224,12 +228,12 @@
 
     UInt32 primeSize = sizeof(AudioCodecPrimeInfo);
     AudioCodecPrimeInfo primeInfo { 0, 0 };
-    if (auto error = AudioConverterGetProperty(m_converter.get(), kAudioCodecPropertyPrimeInfo, &primeSize, &primeInfo)) {
+    if (auto error = AudioConverterGetProperty(m_converter, kAudioCodecPropertyPrimeInfo, &primeSize, &primeInfo)) {
         RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor getting kAudioCodecPropertyPrimeInfo failed with %d", error);
         return nil;
     }
 
-    if (auto error = AudioConverterSetProperty(m_converter.get(), kAudioCodecPropertyDelayMode, delaySize, &originalDelayMode)) {
+    if (auto error = AudioConverterSetProperty(m_converter, kAudioCodecPropertyDelayMode, delaySize, &originalDelayMode)) {
         RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor setting kAudioCodecPropertyDelayMode failed with %d", error);
         return nil;
     }
@@ -242,11 +246,11 @@
     if (!m_destinationFormatDescription) {
         UInt32 cookieSize = 0;
 
-        auto error = AudioConverterGetPropertyInfo(m_converter.get(), kAudioConverterCompressionMagicCookie, &cookieSize, NULL);
+        auto error = AudioConverterGetPropertyInfo(m_converter, kAudioConverterCompressionMagicCookie, &cookieSize, NULL);
         if ((error == noErr) && !!cookieSize) {
             cookie.resize(cookieSize);
 
-            if (auto error = AudioConverterGetProperty(m_converter.get(), kAudioConverterCompressionMagicCookie, &cookieSize, cookie.data())) {
+            if (auto error = AudioConverterGetProperty(m_converter, kAudioConverterCompressionMagicCookie, &cookieSize, cookie.data())) {
                 RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor getting kAudioConverterCompressionMagicCookie failed with %d", error);
                 return nil;
             }
@@ -439,7 +443,7 @@
         UInt32 outputPacketSize = m_destinationFormat.mBytesPerPacket ? m_destinationFormat.mBytesPerPacket : m_maxOutputPacketSize;
         UInt32 numOutputPackets = (UInt32)m_destinationBuffer.capacity() / outputPacketSize;
 
-        auto error = AudioConverterFillComplexBuffer(m_converter.get(), audioConverterComplexInputDataProc, this, &numOutputPackets, &fillBufferList, m_destinationPacketDescriptions.data());
+        auto error = AudioConverterFillComplexBuffer(m_converter, audioConverterComplexInputDataProc, this, &numOutputPackets, &fillBufferList, m_destinationPacketDescriptions.data());
         if (error) {
             RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor AudioConverterFillComplexBuffer failed with %d", error);
             return;

Modified: trunk/Source/WebCore/platform/mediarecorder/cocoa/MediaRecorderPrivateWriterCocoa.h (263890 => 263891)


--- trunk/Source/WebCore/platform/mediarecorder/cocoa/MediaRecorderPrivateWriterCocoa.h	2020-07-03 09:35:48 UTC (rev 263890)
+++ trunk/Source/WebCore/platform/mediarecorder/cocoa/MediaRecorderPrivateWriterCocoa.h	2020-07-03 12:01:41 UTC (rev 263891)
@@ -114,8 +114,8 @@
     RetainPtr<CMFormatDescriptionRef> m_videoFormatDescription;
     std::unique_ptr<VideoSampleBufferCompressor> m_videoCompressor;
     RetainPtr<AVAssetWriterInput> m_videoAssetWriterInput;
-    CMTime m_lastVideoPresentationTime;
-    CMTime m_lastVideoDecodingTime;
+    CMTime m_lastVideoPresentationTime { kCMTimeInvalid };
+    CMTime m_lastVideoDecodingTime { kCMTimeInvalid };
     bool m_hasEncodedVideoSamples { false };
 
     RetainPtr<WebAVAssetWriterDelegate> m_writerDelegate;

Modified: trunk/Source/WebCore/platform/mediarecorder/cocoa/MediaRecorderPrivateWriterCocoa.mm (263890 => 263891)


--- trunk/Source/WebCore/platform/mediarecorder/cocoa/MediaRecorderPrivateWriterCocoa.mm	2020-07-03 09:35:48 UTC (rev 263890)
+++ trunk/Source/WebCore/platform/mediarecorder/cocoa/MediaRecorderPrivateWriterCocoa.mm	2020-07-03 12:01:41 UTC (rev 263891)
@@ -435,16 +435,21 @@
     m_isStopping = true;
     // We hop to the main thread since finishing the video compressor might trigger starting the writer asynchronously.
     callOnMainThread([this, weakThis = makeWeakPtr(this)]() mutable {
-        auto whenFinished = [this] {
+        if (!weakThis)
+            return;
+
+        auto whenFinished = [this, weakThis] {
+            if (!weakThis)
+                return;
+
             m_isStopping = false;
-            if (m_fetchDataCompletionHandler) {
-                auto buffer = WTFMove(m_data);
-                m_fetchDataCompletionHandler(WTFMove(buffer));
-            }
-
             m_isStopped = false;
             m_hasStartedWriting = false;
-            clear();
+
+            if (m_writer)
+                m_writer.clear();
+            if (m_fetchDataCompletionHandler)
+                m_fetchDataCompletionHandler(std::exchange(m_data, nullptr));
         };
 
         if (!m_hasStartedWriting) {
@@ -461,12 +466,8 @@
             [m_writer flush];
             ALLOW_DEPRECATED_DECLARATIONS_END
 
-            [m_writer finishWritingWithCompletionHandler:[weakThis = WTFMove(weakThis), whenFinished = WTFMove(whenFinished)]() mutable {
-                callOnMainThread([weakThis = WTFMove(weakThis), whenFinished = WTFMove(whenFinished)]() mutable {
-                    if (!weakThis)
-                        return;
-                    whenFinished();
-                });
+            [m_writer finishWritingWithCompletionHandler:[whenFinished = WTFMove(whenFinished)]() mutable {
+                callOnMainThread(WTFMove(whenFinished));
             }];
         });
     });

Modified: trunk/Source/WebCore/platform/mediarecorder/cocoa/VideoSampleBufferCompressor.mm (263890 => 263891)


--- trunk/Source/WebCore/platform/mediarecorder/cocoa/VideoSampleBufferCompressor.mm	2020-07-03 09:35:48 UTC (rev 263890)
+++ trunk/Source/WebCore/platform/mediarecorder/cocoa/VideoSampleBufferCompressor.mm	2020-07-03 12:01:41 UTC (rev 263891)
@@ -56,6 +56,10 @@
 VideoSampleBufferCompressor::~VideoSampleBufferCompressor()
 {
     dispatch_release(m_serialDispatchQueue);
+    if (m_vtSession) {
+        VTCompressionSessionInvalidate(m_vtSession.get());
+        m_vtSession = nullptr;
+    }
 }
 
 bool VideoSampleBufferCompressor::initialize(CMBufferQueueTriggerCallback callback, void* callbackObject)

Modified: trunk/Source/WebCore/platform/mediastream/MediaStreamPrivate.h (263890 => 263891)


--- trunk/Source/WebCore/platform/mediastream/MediaStreamPrivate.h	2020-07-03 09:35:48 UTC (rev 263890)
+++ trunk/Source/WebCore/platform/mediastream/MediaStreamPrivate.h	2020-07-03 12:01:41 UTC (rev 263891)
@@ -73,7 +73,7 @@
     static Ref<MediaStreamPrivate> create(Ref<const Logger>&&, RefPtr<RealtimeMediaSource>&& audioSource, RefPtr<RealtimeMediaSource>&& videoSource);
     static Ref<MediaStreamPrivate> create(Ref<const Logger>&& logger, const MediaStreamTrackPrivateVector& tracks, String&& id = createCanonicalUUIDString()) { return adoptRef(*new MediaStreamPrivate(WTFMove(logger), tracks, WTFMove(id))); }
 
-    virtual ~MediaStreamPrivate();
+    WEBCORE_EXPORT virtual ~MediaStreamPrivate();
 
     void addObserver(Observer&);
     void removeObserver(Observer&);

Modified: trunk/Source/WebKit/ChangeLog (263890 => 263891)


--- trunk/Source/WebKit/ChangeLog	2020-07-03 09:35:48 UTC (rev 263890)
+++ trunk/Source/WebKit/ChangeLog	2020-07-03 12:01:41 UTC (rev 263891)
@@ -1,3 +1,21 @@
+2020-07-03  Youenn Fablet  <[email protected]>
+
+        MediaRecorder stopRecorder() returns empty Blob after first use
+        https://bugs.webkit.org/show_bug.cgi?id=212274
+        <rdar://problem/63601298>
+
+        Reviewed by Eric Carlson.
+
+        Update implementation to do initialization as part of startRecording.
+
+        * GPUProcess/webrtc/RemoteMediaRecorderManager.cpp:
+        (WebKit::RemoteMediaRecorderManager::releaseRecorder):
+        Remove ASSERT as recorder creation in WebProcess is always ok while creation in GPUProcess may fail and m_recorders may not be populated.
+        * WebProcess/GPU/webrtc/MediaRecorderPrivate.cpp:
+        (WebKit::MediaRecorderPrivate::MediaRecorderPrivate):
+        (WebKit::MediaRecorderPrivate::startRecording):
+        * WebProcess/GPU/webrtc/MediaRecorderPrivate.h:
+
 2020-07-03  Said Abou-Hallawa  <[email protected]>
 
         Change the names of MIMETypeRegistry methods to comply with webkit naming style

Modified: trunk/Source/WebKit/GPUProcess/webrtc/RemoteMediaRecorderManager.cpp (263890 => 263891)


--- trunk/Source/WebKit/GPUProcess/webrtc/RemoteMediaRecorderManager.cpp	2020-07-03 09:35:48 UTC (rev 263890)
+++ trunk/Source/WebKit/GPUProcess/webrtc/RemoteMediaRecorderManager.cpp	2020-07-03 12:01:41 UTC (rev 263891)
@@ -64,7 +64,6 @@
 
 void RemoteMediaRecorderManager::releaseRecorder(MediaRecorderIdentifier identifier)
 {
-    ASSERT(m_recorders.contains(identifier));
     m_recorders.remove(identifier);
 }
 

Modified: trunk/Source/WebKit/WebProcess/GPU/webrtc/MediaRecorderPrivate.cpp (263890 => 263891)


--- trunk/Source/WebKit/WebProcess/GPU/webrtc/MediaRecorderPrivate.cpp	2020-07-03 09:35:48 UTC (rev 263890)
+++ trunk/Source/WebKit/WebProcess/GPU/webrtc/MediaRecorderPrivate.cpp	2020-07-03 12:01:41 UTC (rev 263891)
@@ -45,12 +45,17 @@
 
 MediaRecorderPrivate::MediaRecorderPrivate(MediaStreamPrivate& stream)
     : m_identifier(MediaRecorderIdentifier::generate())
+    , m_stream(makeRef(stream))
     , m_connection(WebProcess::singleton().ensureGPUProcessConnection().connection())
 {
+}
+
+void MediaRecorderPrivate::startRecording(ErrorCallback&& errorCallback)
+{
     // FIXME: we will need to implement support for multiple audio/video tracks
     // Currently we only choose the first track as the recorded track.
 
-    auto selectedTracks = MediaRecorderPrivate::selectTracks(stream);
+    auto selectedTracks = MediaRecorderPrivate::selectTracks(m_stream);
     if (selectedTracks.audioTrack) {
         m_ringBuffer = makeUnique<CARingBuffer>(makeUniqueRef<SharedRingBufferStorage>(this));
         m_recordedAudioTrackID = selectedTracks.audioTrack->id();
@@ -64,15 +69,20 @@
         width = selectedTracks.videoTrack->settings().width();
     }
 
-    m_connection->sendWithAsyncReply(Messages::RemoteMediaRecorderManager::CreateRecorder { m_identifier, !!selectedTracks.audioTrack, width, height }, [this, weakThis = makeWeakPtr(this), audioTrack = makeRefPtr(selectedTracks.audioTrack), videoTrack = makeRefPtr(selectedTracks.videoTrack)](auto&& exception) {
-        if (!weakThis)
+    m_connection->sendWithAsyncReply(Messages::RemoteMediaRecorderManager::CreateRecorder { m_identifier, !!selectedTracks.audioTrack, width, height }, [this, weakThis = makeWeakPtr(this), audioTrack = makeRefPtr(selectedTracks.audioTrack), videoTrack = makeRefPtr(selectedTracks.videoTrack), errorCallback = WTFMove(errorCallback)](auto&& exception) mutable {
+        if (!weakThis) {
+            errorCallback({ });
             return;
-        if (exception)
-            return m_errorCallback(Exception { exception->code, WTFMove(exception->message) });
+        }
+        if (exception) {
+            errorCallback(Exception { exception->code, WTFMove(exception->message) });
+            return;
+        }
         if (audioTrack)
             setAudioSource(&audioTrack->source());
         if (videoTrack)
             setVideoSource(&videoTrack->source());
+        errorCallback({ });
     }, 0);
 }
 

Modified: trunk/Source/WebKit/WebProcess/GPU/webrtc/MediaRecorderPrivate.h (263890 => 263891)


--- trunk/Source/WebKit/WebProcess/GPU/webrtc/MediaRecorderPrivate.h	2020-07-03 09:35:48 UTC (rev 263890)
+++ trunk/Source/WebKit/WebProcess/GPU/webrtc/MediaRecorderPrivate.h	2020-07-03 12:01:41 UTC (rev 263891)
@@ -58,6 +58,7 @@
     void videoSampleAvailable(WebCore::MediaSample&) final;
     void fetchData(CompletionHandler<void(RefPtr<WebCore::SharedBuffer>&&, const String& mimeType)>&&) final;
     void stopRecording() final;
+    void startRecording(ErrorCallback&&) final;
     void audioSamplesAvailable(const WTF::MediaTime&, const WebCore::PlatformAudioData&, const WebCore::AudioStreamDescription&, size_t) final;
 
     // SharedRingBufferStorage::Client
@@ -64,8 +65,9 @@
     void storageChanged(SharedMemory*);
 
     MediaRecorderIdentifier m_identifier;
+    Ref<MediaStreamPrivate> m_stream;
+    Ref<IPC::Connection> m_connection;
 
-    Ref<IPC::Connection> m_connection;
     String m_recordedAudioTrackID;
     String m_recordedVideoTrackID;
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to