MediaRecorderPrivateAVFImpl should have a Ref<MediaRecorderPrivateWriter> as member
[WebKit-https.git] / Source / WebCore / Modules / mediarecorder / MediaRecorder.cpp
1 /*
2  * Copyright (C) 2018 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'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25
26 #include "config.h"
27 #include "MediaRecorder.h"
28
29 #if ENABLE(MEDIA_STREAM)
30
31 #include "Blob.h"
32 #include "BlobEvent.h"
33 #include "Document.h"
34 #include "EventNames.h"
35 #include "MediaRecorderErrorEvent.h"
36 #include "MediaRecorderPrivate.h"
37 #include "SharedBuffer.h"
38
39 #if PLATFORM(COCOA)
40 #include "MediaRecorderPrivateAVFImpl.h"
41 #endif
42
43 namespace WebCore {
44
45 creatorFunction MediaRecorder::m_customCreator = nullptr;
46
47 ExceptionOr<Ref<MediaRecorder>> MediaRecorder::create(Document& document, Ref<MediaStream>&& stream, Options&& options)
48 {
49     auto privateInstance = MediaRecorder::getPrivateImpl(stream->privateStream());
50     if (!privateInstance)
51         return Exception { NotSupportedError, "The MediaRecorder is unsupported on this platform"_s };
52     auto recorder = adoptRef(*new MediaRecorder(document, WTFMove(stream), WTFMove(privateInstance), WTFMove(options)));
53     recorder->suspendIfNeeded();
54     return WTFMove(recorder);
55 }
56
57 void MediaRecorder::setCustomPrivateRecorderCreator(creatorFunction creator)
58 {
59     m_customCreator = creator;
60 }
61
62 std::unique_ptr<MediaRecorderPrivate> MediaRecorder::getPrivateImpl(const MediaStreamPrivate& stream)
63 {
64     if (m_customCreator)
65         return m_customCreator();
66     
67 #if PLATFORM(COCOA)
68     return MediaRecorderPrivateAVFImpl::create(stream);
69 #endif
70     return nullptr;
71 }
72
73 MediaRecorder::MediaRecorder(Document& document, Ref<MediaStream>&& stream, std::unique_ptr<MediaRecorderPrivate>&& privateImpl, Options&& option)
74     : ActiveDOMObject(&document)
75     , m_options(WTFMove(option))
76     , m_stream(WTFMove(stream))
77     , m_private(WTFMove(privateImpl))
78 {
79     m_tracks = WTF::map(m_stream->getTracks(), [] (auto&& track) -> Ref<MediaStreamTrackPrivate> {
80         return track->privateTrack();
81     });
82     m_stream->addObserver(this);
83 }
84
85 MediaRecorder::~MediaRecorder()
86 {
87     m_stream->removeObserver(this);
88     stopRecordingInternal();
89 }
90
91 void MediaRecorder::stop()
92 {
93     m_isActive = false;
94     stopRecordingInternal();
95 }
96
97 const char* MediaRecorder::activeDOMObjectName() const
98 {
99     return "MediaRecorder";
100 }
101
102 bool MediaRecorder::canSuspendForDocumentSuspension() const
103 {
104     return false; // FIXME: We should do better here as this prevents entering PageCache.
105 }
106
107 ExceptionOr<void> MediaRecorder::startRecording(std::optional<int> timeslice)
108 {
109     UNUSED_PARAM(timeslice);
110     if (state() != RecordingState::Inactive)
111         return Exception { InvalidStateError, "The MediaRecorder's state must be inactive in order to start recording"_s };
112     
113     for (auto& track : m_tracks)
114         track->addObserver(*this);
115
116     m_state = RecordingState::Recording;
117     return { };
118 }
119
120 ExceptionOr<void> MediaRecorder::stopRecording()
121 {
122     if (state() == RecordingState::Inactive)
123         return Exception { InvalidStateError, "The MediaRecorder's state cannot be inactive"_s };
124     
125     scheduleDeferredTask([this] {
126         if (!m_isActive || state() == RecordingState::Inactive)
127             return;
128
129         stopRecordingInternal();
130         ASSERT(m_state == RecordingState::Inactive);
131         dispatchEvent(BlobEvent::create(eventNames().dataavailableEvent, Event::CanBubble::No, Event::IsCancelable::No, createRecordingDataBlob()));
132         if (!m_isActive)
133             return;
134         dispatchEvent(Event::create(eventNames().stopEvent, Event::CanBubble::No, Event::IsCancelable::No));
135     });
136     return { };
137 }
138
139 void MediaRecorder::stopRecordingInternal()
140 {
141     if (state() != RecordingState::Recording)
142         return;
143
144     for (auto& track : m_tracks)
145         track->removeObserver(*this);
146
147     m_state = RecordingState::Inactive;
148     m_private->stopRecording();
149 }
150
151 Ref<Blob> MediaRecorder::createRecordingDataBlob()
152 {
153     auto data = m_private->fetchData();
154     if (!data)
155         return Blob::create();
156     return Blob::create(*data, m_private->mimeType());
157 }
158
159 void MediaRecorder::didAddOrRemoveTrack()
160 {
161     scheduleDeferredTask([this] {
162         if (!m_isActive || state() == RecordingState::Inactive)
163             return;
164         stopRecordingInternal();
165         auto event = MediaRecorderErrorEvent::create(eventNames().errorEvent, Exception { UnknownError, "Track cannot be added to or removed from the MediaStream while recording is happening"_s });
166         dispatchEvent(WTFMove(event));
167     });
168 }
169
170 void MediaRecorder::trackEnded(MediaStreamTrackPrivate&)
171 {
172     auto position = m_tracks.findMatching([](auto& track) {
173         return !track->ended();
174     });
175     if (position != notFound)
176         return;
177
178     stopRecording();
179 }
180
181 void MediaRecorder::sampleBufferUpdated(MediaStreamTrackPrivate& track, MediaSample& mediaSample)
182 {
183     m_private->sampleBufferUpdated(track, mediaSample);
184 }
185
186 void MediaRecorder::audioSamplesAvailable(MediaStreamTrackPrivate& track, const MediaTime& mediaTime, const PlatformAudioData& audioData, const AudioStreamDescription& description, size_t sampleCount)
187 {
188     m_private->audioSamplesAvailable(track, mediaTime, audioData, description, sampleCount);
189 }
190
191 void MediaRecorder::scheduleDeferredTask(Function<void()>&& function)
192 {
193     ASSERT(function);
194     auto* scriptExecutionContext = this->scriptExecutionContext();
195     if (!scriptExecutionContext)
196         return;
197
198     scriptExecutionContext->postTask([protectedThis = makeRef(*this), function = WTFMove(function)] (auto&) {
199         function();
200     });
201 }
202
203 } // namespace WebCore
204
205 #endif // ENABLE(MEDIA_STREAM)