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;