d230694452480847d59f769bcefaa8aa6d17fbe1
[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 "Document.h"
34 #include "Event.h"
35 #include "EventNames.h"
36 #include "JSOverconstrainedError.h"
37 #include "MediaConstraints.h"
38 #include "MediaStream.h"
39 #include "MediaStreamPrivate.h"
40 #include "NotImplemented.h"
41 #include "OverconstrainedError.h"
42 #include "Page.h"
43 #include "ScriptExecutionContext.h"
44 #include <wtf/NeverDestroyed.h>
45
46 namespace WebCore {
47
48 Ref<MediaStreamTrack> MediaStreamTrack::create(ScriptExecutionContext& context, Ref<MediaStreamTrackPrivate>&& privateTrack)
49 {
50     return adoptRef(*new MediaStreamTrack(context, WTFMove(privateTrack)));
51 }
52
53 MediaStreamTrack::MediaStreamTrack(ScriptExecutionContext& context, Ref<MediaStreamTrackPrivate>&& privateTrack)
54     : ActiveDOMObject(&context)
55     , m_private(WTFMove(privateTrack))
56     , m_weakPtrFactory(this)
57     , m_taskQueue(context)
58 {
59     suspendIfNeeded();
60
61     m_private->addObserver(*this);
62
63     if (auto document = this->document())
64         document->addAudioProducer(this);
65 }
66
67 MediaStreamTrack::~MediaStreamTrack()
68 {
69     m_private->removeObserver(*this);
70
71     if (auto document = this->document())
72         document->removeAudioProducer(this);
73 }
74
75 const AtomicString& MediaStreamTrack::kind() const
76 {
77     static NeverDestroyed<AtomicString> audioKind("audio", AtomicString::ConstructFromLiteral);
78     static NeverDestroyed<AtomicString> videoKind("video", AtomicString::ConstructFromLiteral);
79
80     if (m_private->type() == RealtimeMediaSource::Type::Audio)
81         return audioKind;
82     return videoKind;
83 }
84
85 const String& MediaStreamTrack::id() const
86 {
87     return m_private->id();
88 }
89
90 const String& MediaStreamTrack::label() const
91 {
92     return m_private->label();
93 }
94
95 bool MediaStreamTrack::enabled() const
96 {
97     return m_private->enabled();
98 }
99
100 void MediaStreamTrack::setEnabled(bool enabled)
101 {
102     m_private->setEnabled(enabled);
103 }
104
105 bool MediaStreamTrack::muted() const
106 {
107     return m_private->muted();
108 }
109
110 auto MediaStreamTrack::readyState() const -> State
111 {
112     return ended() ? State::Ended : State::Live;
113 }
114
115 bool MediaStreamTrack::ended() const
116 {
117     return m_ended || m_private->ended();
118 }
119
120 Ref<MediaStreamTrack> MediaStreamTrack::clone()
121 {
122     return MediaStreamTrack::create(*scriptExecutionContext(), m_private->clone());
123 }
124
125 void MediaStreamTrack::stopTrack(StopMode mode)
126 {
127     // NOTE: this method is called when the "stop" method is called from JS, using the "ImplementedAs" IDL attribute.
128     // This is done because ActiveDOMObject requires a "stop" method.
129
130     if (ended())
131         return;
132
133     // An 'ended' event is not posted if m_ended is true when trackEnded is called, so set it now if we are
134     // not supposed to post the event.
135     if (mode == StopMode::Silently)
136         m_ended = true;
137
138     m_private->endTrack();
139     m_ended = true;
140 }
141
142 MediaStreamTrack::TrackSettings MediaStreamTrack::getSettings() const
143 {
144     auto& settings = m_private->settings();
145     TrackSettings result;
146     if (settings.supportsWidth())
147         result.width = settings.width();
148     if (settings.supportsHeight())
149         result.height = settings.height();
150     if (settings.supportsAspectRatio() && settings.aspectRatio()) // FIXME: Why the check for zero here?
151         result.aspectRatio = settings.aspectRatio();
152     if (settings.supportsFrameRate())
153         result.frameRate = settings.frameRate();
154     if (settings.supportsFacingMode())
155         result.facingMode = RealtimeMediaSourceSettings::facingMode(settings.facingMode());
156     if (settings.supportsVolume())
157         result.volume = settings.volume();
158     if (settings.supportsSampleRate())
159         result.sampleRate = settings.sampleRate();
160     if (settings.supportsSampleSize())
161         result.sampleSize = settings.sampleSize();
162     if (settings.supportsEchoCancellation())
163         result.echoCancellation = settings.echoCancellation();
164     if (settings.supportsDeviceId())
165         result.deviceId = settings.deviceId();
166     if (settings.supportsGroupId())
167         result.groupId = settings.groupId();
168     return result;
169 }
170
171 static DoubleRange capabilityDoubleRange(const CapabilityValueOrRange& value)
172 {
173     DoubleRange range;
174     switch (value.type()) {
175     case CapabilityValueOrRange::Double:
176         range.min = value.value().asDouble;
177         range.max = range.min;
178         break;
179     case CapabilityValueOrRange::DoubleRange:
180         range.min = value.rangeMin().asDouble;
181         range.max = value.rangeMax().asDouble;
182         break;
183     case CapabilityValueOrRange::Undefined:
184     case CapabilityValueOrRange::ULong:
185     case CapabilityValueOrRange::ULongRange:
186         ASSERT_NOT_REACHED();
187     }
188     return range;
189 }
190
191 static LongRange capabilityIntRange(const CapabilityValueOrRange& value)
192 {
193     LongRange range;
194     switch (value.type()) {
195     case CapabilityValueOrRange::ULong:
196         range.min = value.value().asInt;
197         range.max = range.min;
198         break;
199     case CapabilityValueOrRange::ULongRange:
200         range.min = value.rangeMin().asInt;
201         range.max = value.rangeMax().asInt;
202         break;
203     case CapabilityValueOrRange::Undefined:
204     case CapabilityValueOrRange::Double:
205     case CapabilityValueOrRange::DoubleRange:
206         ASSERT_NOT_REACHED();
207     }
208     return range;
209 }
210
211 static Vector<String> capabilityStringVector(const Vector<RealtimeMediaSourceSettings::VideoFacingMode>& modes)
212 {
213     Vector<String> result;
214     result.reserveCapacity(modes.size());
215     for (auto& mode : modes)
216         result.uncheckedAppend(RealtimeMediaSourceSettings::facingMode(mode));
217     return result;
218 }
219
220 static Vector<bool> capabilityBooleanVector(RealtimeMediaSourceCapabilities::EchoCancellation cancellation)
221 {
222     Vector<bool> result;
223     result.reserveCapacity(2);
224     result.uncheckedAppend(true);
225     result.uncheckedAppend(cancellation == RealtimeMediaSourceCapabilities::EchoCancellation::ReadWrite);
226     return result;
227 }
228
229 MediaStreamTrack::TrackCapabilities MediaStreamTrack::getCapabilities() const
230 {
231     auto capabilities = m_private->capabilities();
232     TrackCapabilities result;
233     if (capabilities.supportsWidth())
234         result.width = capabilityIntRange(capabilities.width());
235     if (capabilities.supportsHeight())
236         result.height = capabilityIntRange(capabilities.height());
237     if (capabilities.supportsAspectRatio())
238         result.aspectRatio = capabilityDoubleRange(capabilities.aspectRatio());
239     if (capabilities.supportsFrameRate())
240         result.frameRate = capabilityDoubleRange(capabilities.frameRate());
241     if (capabilities.supportsFacingMode())
242         result.facingMode = capabilityStringVector(capabilities.facingMode());
243     if (capabilities.supportsVolume())
244         result.volume = capabilityDoubleRange(capabilities.volume());
245     if (capabilities.supportsSampleRate())
246         result.sampleRate = capabilityIntRange(capabilities.sampleRate());
247     if (capabilities.supportsSampleSize())
248         result.sampleSize = capabilityIntRange(capabilities.sampleSize());
249     if (capabilities.supportsEchoCancellation())
250         result.echoCancellation = capabilityBooleanVector(capabilities.echoCancellation());
251     if (capabilities.supportsDeviceId())
252         result.deviceId = capabilities.deviceId();
253     if (capabilities.supportsGroupId())
254         result.groupId = capabilities.groupId();
255     return result;
256 }
257
258 static MediaConstraints createMediaConstraints(const std::optional<MediaTrackConstraints>& constraints)
259 {
260     if (!constraints) {
261         MediaConstraints validConstraints;
262         validConstraints.isValid = true;
263         return validConstraints;
264     }
265     return createMediaConstraints(constraints.value());
266 }
267
268 void MediaStreamTrack::applyConstraints(const std::optional<MediaTrackConstraints>& constraints, DOMPromiseDeferred<void>&& promise)
269 {
270     m_promise = WTFMove(promise);
271
272     auto weakThis = createWeakPtr();
273     auto failureHandler = [weakThis] (const String& failedConstraint, const String& message) {
274         if (!weakThis || !weakThis->m_promise)
275             return;
276         weakThis->m_promise->rejectType<IDLInterface<OverconstrainedError>>(OverconstrainedError::create(failedConstraint, message).get());
277     };
278     auto successHandler = [weakThis, constraints] () {
279         if (!weakThis || !weakThis->m_promise)
280             return;
281         weakThis->m_promise->resolve();
282         weakThis->m_constraints = constraints.value_or(MediaTrackConstraints { });
283     };
284     m_private->applyConstraints(createMediaConstraints(constraints), WTFMove(successHandler), WTFMove(failureHandler));
285 }
286
287 void MediaStreamTrack::addObserver(Observer& observer)
288 {
289     m_observers.append(&observer);
290 }
291
292 void MediaStreamTrack::removeObserver(Observer& observer)
293 {
294     m_observers.removeFirst(&observer);
295 }
296
297 void MediaStreamTrack::pageMutedStateDidChange()
298 {
299     if (m_ended || !isCaptureTrack())
300         return;
301
302     Document* document = this->document();
303     if (!document || !document->page())
304         return;
305
306     m_private->setMuted(document->page()->isMediaCaptureMuted());
307 }
308
309 MediaProducer::MediaStateFlags MediaStreamTrack::mediaState() const
310 {
311     if (m_ended || !isCaptureTrack())
312         return IsNotPlaying;
313
314     Document* document = this->document();
315     if (!document || !document->page())
316         return IsNotPlaying;
317
318     bool pageCaptureMuted = document->page()->isMediaCaptureMuted();
319
320     if (source().type() == RealtimeMediaSource::Type::Audio) {
321         if (source().interrupted() && !pageCaptureMuted)
322             return HasInterruptedAudioCaptureDevice;
323         if (muted())
324             return HasMutedAudioCaptureDevice;
325         if (m_private->isProducingData())
326             return HasActiveAudioCaptureDevice;
327     } else {
328         if (source().interrupted() && !pageCaptureMuted)
329             return HasInterruptedVideoCaptureDevice;
330         if (muted())
331             return HasMutedVideoCaptureDevice;
332         if (m_private->isProducingData())
333             return HasActiveVideoCaptureDevice;
334     }
335
336     return IsNotPlaying;
337 }
338
339 void MediaStreamTrack::trackStarted(MediaStreamTrackPrivate&)
340 {
341     configureTrackRendering();
342 }
343
344 void MediaStreamTrack::trackEnded(MediaStreamTrackPrivate&)
345 {
346     // http://w3c.github.io/mediacapture-main/#life-cycle
347     // 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:
348     // 1. If the track's readyState attribute has the value ended already, then abort these steps.
349     if (m_ended)
350         return;
351
352     // 2. Set track's readyState attribute to ended.
353     m_ended = true;
354
355     if (scriptExecutionContext()->activeDOMObjectsAreSuspended() || scriptExecutionContext()->activeDOMObjectsAreStopped())
356         return;
357
358     // 3. Notify track's source that track is ended so that the source may be stopped, unless other MediaStreamTrack objects depend on it.
359     // 4. Fire a simple event named ended at the object.
360     dispatchEvent(Event::create(eventNames().endedEvent, false, false));
361
362     for (auto& observer : m_observers)
363         observer->trackDidEnd();
364
365     configureTrackRendering();
366 }
367     
368 void MediaStreamTrack::trackMutedChanged(MediaStreamTrackPrivate&)
369 {
370     if (scriptExecutionContext()->activeDOMObjectsAreSuspended() || scriptExecutionContext()->activeDOMObjectsAreStopped() || m_ended)
371         return;
372
373     AtomicString eventType = muted() ? eventNames().muteEvent : eventNames().unmuteEvent;
374     dispatchEvent(Event::create(eventType, false, false));
375
376     configureTrackRendering();
377 }
378
379 void MediaStreamTrack::trackSettingsChanged(MediaStreamTrackPrivate&)
380 {
381     configureTrackRendering();
382 }
383
384 void MediaStreamTrack::trackEnabledChanged(MediaStreamTrackPrivate&)
385 {
386     configureTrackRendering();
387 }
388
389 void MediaStreamTrack::configureTrackRendering()
390 {
391     m_taskQueue.enqueueTask([this] {
392         if (auto document = this->document())
393             document->updateIsPlayingMedia();
394     });
395
396     // 4.3.1
397     // ... media from the source only flows when a MediaStreamTrack object is both unmuted and enabled
398 }
399
400 void MediaStreamTrack::stop()
401 {
402     stopTrack();
403     m_taskQueue.close();
404 }
405
406 const char* MediaStreamTrack::activeDOMObjectName() const
407 {
408     return "MediaStreamTrack";
409 }
410
411 bool MediaStreamTrack::canSuspendForDocumentSuspension() const
412 {
413     return !hasPendingActivity();
414 }
415
416 bool MediaStreamTrack::hasPendingActivity() const
417 {
418     return !m_ended;
419 }
420
421 AudioSourceProvider* MediaStreamTrack::audioSourceProvider()
422 {
423     return m_private->audioSourceProvider();
424 }
425
426 Document* MediaStreamTrack::document() const
427 {
428     return downcast<Document>(scriptExecutionContext());
429 }
430
431 } // namespace WebCore
432
433 #endif // ENABLE(MEDIA_STREAM)