MediaRecorder stopRecorder() returns empty Blob after first use
[WebKit-https.git] / Source / WebKit / WebProcess / GPU / webrtc / MediaRecorderPrivate.cpp
1 /*
2  * Copyright (C) 2020 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "MediaRecorderPrivate.h"
28
29 #if PLATFORM(COCOA) && ENABLE(GPU_PROCESS) && ENABLE(MEDIA_STREAM) && HAVE(AVASSETWRITERDELEGATE)
30
31 #include "DataReference.h"
32 #include "GPUProcessConnection.h"
33 #include "RemoteMediaRecorderManagerMessages.h"
34 #include "RemoteMediaRecorderMessages.h"
35 #include "WebProcess.h"
36 #include <WebCore/CARingBuffer.h>
37 #include <WebCore/MediaStreamPrivate.h>
38 #include <WebCore/MediaStreamTrackPrivate.h>
39 #include <WebCore/RemoteVideoSample.h>
40 #include <WebCore/SharedBuffer.h>
41 #include <WebCore/WebAudioBufferList.h>
42
43 namespace WebKit {
44 using namespace WebCore;
45
46 MediaRecorderPrivate::MediaRecorderPrivate(MediaStreamPrivate& stream)
47     : m_identifier(MediaRecorderIdentifier::generate())
48     , m_stream(makeRef(stream))
49     , m_connection(WebProcess::singleton().ensureGPUProcessConnection().connection())
50 {
51 }
52
53 void MediaRecorderPrivate::startRecording(ErrorCallback&& errorCallback)
54 {
55     // FIXME: we will need to implement support for multiple audio/video tracks
56     // Currently we only choose the first track as the recorded track.
57
58     auto selectedTracks = MediaRecorderPrivate::selectTracks(m_stream);
59     if (selectedTracks.audioTrack) {
60         m_ringBuffer = makeUnique<CARingBuffer>(makeUniqueRef<SharedRingBufferStorage>(this));
61         m_recordedAudioTrackID = selectedTracks.audioTrack->id();
62     }
63
64     int width = 0;
65     int height = 0;
66     if (selectedTracks.videoTrack) {
67         m_recordedVideoTrackID = selectedTracks.videoTrack->id();
68         height = selectedTracks.videoTrack->settings().height();
69         width = selectedTracks.videoTrack->settings().width();
70     }
71
72     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 {
73         if (!weakThis) {
74             errorCallback({ });
75             return;
76         }
77         if (exception) {
78             errorCallback(Exception { exception->code, WTFMove(exception->message) });
79             return;
80         }
81         if (audioTrack)
82             setAudioSource(&audioTrack->source());
83         if (videoTrack)
84             setVideoSource(&videoTrack->source());
85         errorCallback({ });
86     }, 0);
87 }
88
89 MediaRecorderPrivate::~MediaRecorderPrivate()
90 {
91     setAudioSource(nullptr);
92     setVideoSource(nullptr);
93     m_connection->send(Messages::RemoteMediaRecorderManager::ReleaseRecorder { m_identifier }, 0);
94 }
95
96 void MediaRecorderPrivate::videoSampleAvailable(MediaSample& sample)
97 {
98     if (auto remoteSample = RemoteVideoSample::create(sample))
99         m_connection->send(Messages::RemoteMediaRecorder::VideoSampleAvailable { WTFMove(*remoteSample) }, m_identifier);
100 }
101
102 void MediaRecorderPrivate::audioSamplesAvailable(const MediaTime& time, const PlatformAudioData& audioData, const AudioStreamDescription& description, size_t numberOfFrames)
103 {
104     if (m_description != description) {
105         ASSERT(description.platformDescription().type == PlatformDescription::CAAudioStreamBasicType);
106         m_description = *WTF::get<const AudioStreamBasicDescription*>(description.platformDescription().description);
107
108         // Allocate a ring buffer large enough to contain 2 seconds of audio.
109         m_numberOfFrames = m_description.sampleRate() * 2;
110         m_ringBuffer->allocate(m_description.streamDescription(), m_numberOfFrames);
111     }
112
113     ASSERT(is<WebAudioBufferList>(audioData));
114     m_ringBuffer->store(downcast<WebAudioBufferList>(audioData).list(), numberOfFrames, time.timeValue());
115     uint64_t startFrame;
116     uint64_t endFrame;
117     m_ringBuffer->getCurrentFrameBounds(startFrame, endFrame);
118     m_connection->send(Messages::RemoteMediaRecorder::AudioSamplesAvailable { time, numberOfFrames, startFrame, endFrame }, m_identifier);
119 }
120
121 void MediaRecorderPrivate::storageChanged(SharedMemory* storage)
122 {
123     SharedMemory::Handle handle;
124     if (storage)
125         storage->createHandle(handle, SharedMemory::Protection::ReadOnly);
126     m_connection->send(Messages::RemoteMediaRecorder::AudioSamplesStorageChanged { handle, m_description, static_cast<uint64_t>(m_numberOfFrames) }, m_identifier);
127 }
128
129 void MediaRecorderPrivate::fetchData(CompletionHandler<void(RefPtr<WebCore::SharedBuffer>&&, const String& mimeType)>&& completionHandler)
130 {
131     m_connection->sendWithAsyncReply(Messages::RemoteMediaRecorder::FetchData { }, [completionHandler = WTFMove(completionHandler)](auto&& data, auto&& mimeType) mutable {
132         RefPtr<SharedBuffer> buffer;
133         if (data.size())
134             buffer = SharedBuffer::create(data.data(), data.size());
135         completionHandler(WTFMove(buffer), mimeType);
136     }, m_identifier);
137 }
138
139 void MediaRecorderPrivate::stopRecording()
140 {
141     setAudioSource(nullptr);
142     setVideoSource(nullptr);
143     m_connection->send(Messages::RemoteMediaRecorder::StopRecording { }, m_identifier);
144 }
145
146 }
147
148 #endif // PLATFORM(COCOA) && ENABLE(GPU_PROCESS) && ENABLE(MEDIA_STREAM) && HAVE(AVASSETWRITERDELEGATE)