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