Unreviewed, rolling out r218365.
[WebKit-https.git] / Source / WebCore / Modules / mediastream / MediaStreamTrack.cpp
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  * Copyright (C) 2011, 2015 Ericsson AB. All rights reserved.
4  * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
5  * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1.  Redistributions of source code must retain the above copyright
11  *     notice, this list of conditions and the following disclaimer.
12  * 2.  Redistributions in binary form must reproduce the above copyright
13  *     notice, this list of conditions and the following disclaimer in the
14  *     documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "config.h"
29 #include "MediaStreamTrack.h"
30
31 #if ENABLE(MEDIA_STREAM)
32
33 #include "Event.h"
34 #include "EventNames.h"
35 #include "JSOverconstrainedError.h"
36 #include "MediaConstraints.h"
37 #include "MediaStream.h"
38 #include "MediaStreamPrivate.h"
39 #include "NotImplemented.h"
40 #include "OverconstrainedError.h"
41 #include "ScriptExecutionContext.h"
42 #include <wtf/NeverDestroyed.h>
43
44 namespace WebCore {
45
46 Ref<MediaStreamTrack> MediaStreamTrack::create(ScriptExecutionContext& context, Ref<MediaStreamTrackPrivate>&& privateTrack)
47 {
48     return adoptRef(*new MediaStreamTrack(context, WTFMove(privateTrack)));
49 }
50
51 MediaStreamTrack::MediaStreamTrack(ScriptExecutionContext& context, Ref<MediaStreamTrackPrivate>&& privateTrack)
52     : ActiveDOMObject(&context)
53     , m_private(WTFMove(privateTrack))
54     , m_weakPtrFactory(this)
55 {
56     suspendIfNeeded();
57
58     m_private->addObserver(*this);
59 }
60
61 MediaStreamTrack::~MediaStreamTrack()
62 {
63     m_private->removeObserver(*this);
64 }
65
66 const AtomicString& MediaStreamTrack::kind() const
67 {
68     static NeverDestroyed<AtomicString> audioKind("audio", AtomicString::ConstructFromLiteral);
69     static NeverDestroyed<AtomicString> videoKind("video", AtomicString::ConstructFromLiteral);
70
71     if (m_private->type() == RealtimeMediaSource::Type::Audio)
72         return audioKind;
73     return videoKind;
74 }
75
76 const String& MediaStreamTrack::id() const
77 {
78     return m_private->id();
79 }
80
81 const String& MediaStreamTrack::label() const
82 {
83     return m_private->label();
84 }
85
86 bool MediaStreamTrack::enabled() const
87 {
88     return m_private->enabled();
89 }
90
91 void MediaStreamTrack::setEnabled(bool enabled)
92 {
93     m_private->setEnabled(enabled);
94 }
95
96 bool MediaStreamTrack::muted() const
97 {
98     return m_private->muted();
99 }
100
101 auto MediaStreamTrack::readyState() const -> State
102 {
103     return ended() ? State::Ended : State::Live;
104 }
105
106 bool MediaStreamTrack::ended() const
107 {
108     return m_ended || m_private->ended();
109 }
110
111 Ref<MediaStreamTrack> MediaStreamTrack::clone()
112 {
113     return MediaStreamTrack::create(*scriptExecutionContext(), m_private->clone());
114 }
115
116 void MediaStreamTrack::stopTrack(StopMode mode)
117 {
118     // NOTE: this method is called when the "stop" method is called from JS, using the "ImplementedAs" IDL attribute.
119     // This is done because ActiveDOMObject requires a "stop" method.
120
121     if (ended())
122         return;
123
124     // An 'ended' event is not posted if m_ended is true when trackEnded is called, so set it now if we are
125     // not supposed to post the event.
126     if (mode == StopMode::Silently)
127         m_ended = true;
128
129     m_private->endTrack();
130     m_ended = true;
131 }
132
133 MediaStreamTrack::TrackSettings MediaStreamTrack::getSettings() const
134 {
135     auto& settings = m_private->settings();
136     TrackSettings result;
137     if (settings.supportsWidth())
138         result.width = settings.width();
139     if (settings.supportsHeight())
140         result.height = settings.height();
141     if (settings.supportsAspectRatio() && settings.aspectRatio()) // FIXME: Why the check for zero here?
142         result.aspectRatio = settings.aspectRatio();
143     if (settings.supportsFrameRate())
144         result.frameRate = settings.frameRate();
145     if (settings.supportsFacingMode())
146         result.facingMode = RealtimeMediaSourceSettings::facingMode(settings.facingMode());
147     if (settings.supportsVolume())
148         result.volume = settings.volume();
149     if (settings.supportsSampleRate())
150         result.sampleRate = settings.sampleRate();
151     if (settings.supportsSampleSize())
152         result.sampleSize = settings.sampleSize();
153     if (settings.supportsEchoCancellation())
154         result.echoCancellation = settings.echoCancellation();
155     if (settings.supportsDeviceId())
156         result.deviceId = settings.deviceId();
157     if (settings.supportsGroupId())
158         result.groupId = settings.groupId();
159     return result;
160 }
161
162 static DoubleRange capabilityDoubleRange(const CapabilityValueOrRange& value)
163 {
164     DoubleRange range;
165     switch (value.type()) {
166     case CapabilityValueOrRange::Double:
167         range.min = value.value().asDouble;
168         range.max = range.min;
169         break;
170     case CapabilityValueOrRange::DoubleRange:
171         range.min = value.rangeMin().asDouble;
172         range.max = value.rangeMax().asDouble;
173         break;
174     case CapabilityValueOrRange::Undefined:
175     case CapabilityValueOrRange::ULong:
176     case CapabilityValueOrRange::ULongRange:
177         ASSERT_NOT_REACHED();
178     }
179     return range;
180 }
181
182 static LongRange capabilityIntRange(const CapabilityValueOrRange& value)
183 {
184     LongRange range;
185     switch (value.type()) {
186     case CapabilityValueOrRange::ULong:
187         range.min = value.value().asInt;
188         range.max = range.min;
189         break;
190     case CapabilityValueOrRange::ULongRange:
191         range.min = value.rangeMin().asInt;
192         range.max = value.rangeMax().asInt;
193         break;
194     case CapabilityValueOrRange::Undefined:
195     case CapabilityValueOrRange::Double:
196     case CapabilityValueOrRange::DoubleRange:
197         ASSERT_NOT_REACHED();
198     }
199     return range;
200 }
201
202 static Vector<String> capabilityStringVector(const Vector<RealtimeMediaSourceSettings::VideoFacingMode>& modes)
203 {
204     Vector<String> result;
205     result.reserveCapacity(modes.size());
206     for (auto& mode : modes)
207         result.uncheckedAppend(RealtimeMediaSourceSettings::facingMode(mode));
208     return result;
209 }
210
211 static Vector<bool> capabilityBooleanVector(RealtimeMediaSourceCapabilities::EchoCancellation cancellation)
212 {
213     Vector<bool> result;
214     result.reserveCapacity(2);
215     result.uncheckedAppend(true);
216     result.uncheckedAppend(cancellation == RealtimeMediaSourceCapabilities::EchoCancellation::ReadWrite);
217     return result;
218 }
219
220 MediaStreamTrack::TrackCapabilities MediaStreamTrack::getCapabilities() const
221 {
222     auto capabilities = m_private->capabilities();
223     TrackCapabilities result;
224     if (capabilities.supportsWidth())
225         result.width = capabilityIntRange(capabilities.width());
226     if (capabilities.supportsHeight())
227         result.height = capabilityIntRange(capabilities.height());
228     if (capabilities.supportsAspectRatio())
229         result.aspectRatio = capabilityDoubleRange(capabilities.aspectRatio());
230     if (capabilities.supportsFrameRate())
231         result.frameRate = capabilityDoubleRange(capabilities.frameRate());
232     if (capabilities.supportsFacingMode())
233         result.facingMode = capabilityStringVector(capabilities.facingMode());
234     if (capabilities.supportsVolume())
235         result.volume = capabilityDoubleRange(capabilities.volume());
236     if (capabilities.supportsSampleRate())
237         result.sampleRate = capabilityIntRange(capabilities.sampleRate());
238     if (capabilities.supportsSampleSize())
239         result.sampleSize = capabilityIntRange(capabilities.sampleSize());
240     if (capabilities.supportsEchoCancellation())
241         result.echoCancellation = capabilityBooleanVector(capabilities.echoCancellation());
242     if (capabilities.supportsDeviceId())
243         result.deviceId = capabilities.deviceId();
244     if (capabilities.supportsGroupId())
245         result.groupId = capabilities.groupId();
246     return result;
247 }
248
249 static MediaConstraints createMediaConstraints(const std::optional<MediaTrackConstraints>& constraints)
250 {
251     if (!constraints) {
252         MediaConstraints validConstraints;
253         validConstraints.isValid = true;
254         return validConstraints;
255     }
256     return createMediaConstraints(constraints.value());
257 }
258
259 void MediaStreamTrack::applyConstraints(const std::optional<MediaTrackConstraints>& constraints, DOMPromiseDeferred<void>&& promise)
260 {
261     m_promise = WTFMove(promise);
262
263     auto weakThis = createWeakPtr();
264     auto failureHandler = [weakThis] (const String& failedConstraint, const String& message) {
265         if (!weakThis || !weakThis->m_promise)
266             return;
267         weakThis->m_promise->rejectType<IDLInterface<OverconstrainedError>>(OverconstrainedError::create(failedConstraint, message).get());
268     };
269     auto successHandler = [weakThis, constraints] () {
270         if (!weakThis || !weakThis->m_promise)
271             return;
272         weakThis->m_promise->resolve();
273         weakThis->m_constraints = constraints.value_or(MediaTrackConstraints { });
274     };
275     m_private->applyConstraints(createMediaConstraints(constraints), WTFMove(successHandler), WTFMove(failureHandler));
276 }
277
278 void MediaStreamTrack::addObserver(Observer& observer)
279 {
280     m_observers.append(&observer);
281 }
282
283 void MediaStreamTrack::removeObserver(Observer& observer)
284 {
285     m_observers.removeFirst(&observer);
286 }
287
288 void MediaStreamTrack::trackEnded(MediaStreamTrackPrivate&)
289 {
290     // http://w3c.github.io/mediacapture-main/#life-cycle
291     // When a MediaStreamTrack track ends for any reason other than the stop() method being invoked, the User Agent must queue a task that runs the following steps:
292     // 1. If the track's readyState attribute has the value ended already, then abort these steps.
293     if (m_ended)
294         return;
295
296     // 2. Set track's readyState attribute to ended.
297     m_ended = true;
298
299     if (scriptExecutionContext()->activeDOMObjectsAreSuspended() || scriptExecutionContext()->activeDOMObjectsAreStopped())
300         return;
301
302     // 3. Notify track's source that track is ended so that the source may be stopped, unless other MediaStreamTrack objects depend on it.
303     // 4. Fire a simple event named ended at the object.
304     dispatchEvent(Event::create(eventNames().endedEvent, false, false));
305
306     for (auto& observer : m_observers)
307         observer->trackDidEnd();
308
309     configureTrackRendering();
310 }
311     
312 void MediaStreamTrack::trackMutedChanged(MediaStreamTrackPrivate&)
313 {
314     if (scriptExecutionContext()->activeDOMObjectsAreSuspended() || scriptExecutionContext()->activeDOMObjectsAreStopped() || m_ended)
315         return;
316
317     AtomicString eventType = muted() ? eventNames().muteEvent : eventNames().unmuteEvent;
318     dispatchEvent(Event::create(eventType, false, false));
319
320     configureTrackRendering();
321 }
322
323 void MediaStreamTrack::trackSettingsChanged(MediaStreamTrackPrivate&)
324 {
325     configureTrackRendering();
326 }
327
328 void MediaStreamTrack::trackEnabledChanged(MediaStreamTrackPrivate&)
329 {
330     configureTrackRendering();
331 }
332
333 void MediaStreamTrack::configureTrackRendering()
334 {
335     // 4.3.1
336     // ... media from the source only flows when a MediaStreamTrack object is both unmuted and enabled
337 }
338
339 void MediaStreamTrack::stop()
340 {
341     stopTrack();
342 }
343
344 const char* MediaStreamTrack::activeDOMObjectName() const
345 {
346     return "MediaStreamTrack";
347 }
348
349 bool MediaStreamTrack::canSuspendForDocumentSuspension() const
350 {
351     return !hasPendingActivity();
352 }
353
354 bool MediaStreamTrack::hasPendingActivity() const
355 {
356     return !m_ended;
357 }
358
359 AudioSourceProvider* MediaStreamTrack::audioSourceProvider()
360 {
361     return m_private->audioSourceProvider();
362 }
363
364 } // namespace WebCore
365
366 #endif // ENABLE(MEDIA_STREAM)