1/*
2 * Copyright (C) 2011 Google Inc. All rights reserved.
3 * Copyright (C) 2011 Ericsson AB. All rights reserved.
4 * Copyright (C) 2013 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 "AllAudioCapabilities.h"
34#include "AllVideoCapabilities.h"
35#include "AudioStreamTrack.h"
36#include "Dictionary.h"
37#include "Event.h"
38#include "ExceptionCode.h"
39#include "ExceptionCodePlaceholder.h"
40#include "MediaConstraintsImpl.h"
41#include "MediaSourceStates.h"
42#include "MediaStream.h"
43#include "MediaStreamCenter.h"
44#include "MediaStreamPrivate.h"
45#include "MediaStreamTrackSourcesCallback.h"
46#include "MediaStreamTrackSourcesRequest.h"
47#include "MediaTrackConstraints.h"
48#include "NotImplemented.h"
49#include "VideoStreamTrack.h"
50#include <wtf/Functional.h>
51#include <wtf/NeverDestroyed.h>
52
53namespace WebCore {
54
55MediaStreamTrack::MediaStreamTrack(ScriptExecutionContext& context, MediaStreamTrackPrivate& privateTrack, const Dictionary* constraints)
56    : RefCounted()
57    , ActiveDOMObject(&context)
58    , m_privateTrack(privateTrack)
59    , m_eventDispatchScheduled(false)
60    , m_stoppingTrack(false)
61{
62    suspendIfNeeded();
63
64    m_privateTrack->setClient(this);
65
66    if (constraints)
67        applyConstraints(*constraints);
68}
69
70MediaStreamTrack::MediaStreamTrack(MediaStreamTrack& other)
71    : RefCounted()
72    , ActiveDOMObject(other.scriptExecutionContext())
73    , m_privateTrack(*other.privateTrack().clone())
74    , m_eventDispatchScheduled(false)
75    , m_stoppingTrack(false)
76{
77    suspendIfNeeded();
78
79    m_privateTrack->setClient(this);
80}
81
82MediaStreamTrack::~MediaStreamTrack()
83{
84    m_privateTrack->setClient(nullptr);
85}
86
87void MediaStreamTrack::setSource(PassRefPtr<MediaStreamSource> newSource)
88{
89    m_privateTrack->setSource(newSource);
90}
91
92const String& MediaStreamTrack::id() const
93{
94    return m_privateTrack->id();
95}
96
97const String& MediaStreamTrack::label() const
98{
99    return m_privateTrack->label();
100}
101
102bool MediaStreamTrack::enabled() const
103{
104    return m_privateTrack->enabled();
105}
106
107void MediaStreamTrack::setEnabled(bool enabled)
108{
109    m_privateTrack->setEnabled(enabled);
110}
111
112bool MediaStreamTrack::stopped() const
113{
114    return m_privateTrack->stopped();
115}
116
117bool MediaStreamTrack::muted() const
118{
119    return m_privateTrack->muted();
120}
121
122bool MediaStreamTrack::readonly() const
123{
124    return m_privateTrack->readonly();
125}
126
127bool MediaStreamTrack::remote() const
128{
129    return m_privateTrack->remote();
130}
131
132const AtomicString& MediaStreamTrack::readyState() const
133{
134    static NeverDestroyed<AtomicString> ended("ended", AtomicString::ConstructFromLiteral);
135    static NeverDestroyed<AtomicString> live("live", AtomicString::ConstructFromLiteral);
136    static NeverDestroyed<AtomicString> newState("new", AtomicString::ConstructFromLiteral);
137
138    switch (m_privateTrack->readyState()) {
139    case MediaStreamSource::Live:
140        return live;
141    case MediaStreamSource::New:
142        return newState;
143    case MediaStreamSource::Ended:
144        return ended;
145    }
146
147    ASSERT_NOT_REACHED();
148    return emptyAtom;
149}
150
151void MediaStreamTrack::getSources(ScriptExecutionContext* context, PassRefPtr<MediaStreamTrackSourcesCallback> callback, ExceptionCode& ec)
152{
153    RefPtr<MediaStreamTrackSourcesRequest> request = MediaStreamTrackSourcesRequest::create(context, callback);
154    if (!MediaStreamCenter::shared().getMediaStreamTrackSources(request.release()))
155        ec = NOT_SUPPORTED_ERR;
156}
157
158RefPtr<MediaTrackConstraints> MediaStreamTrack::getConstraints() const
159{
160    // FIXME: https://bugs.webkit.org/show_bug.cgi?id=122428
161    notImplemented();
162    return 0;
163}
164
165RefPtr<MediaSourceStates> MediaStreamTrack::states() const
166{
167    return MediaSourceStates::create(m_privateTrack->states());
168}
169
170RefPtr<MediaStreamCapabilities> MediaStreamTrack::getCapabilities() const
171{
172    // The source may be shared by multiple tracks, so its states is not necessarily
173    // in sync with the track state. A track that is new or has ended always has a source
174    // type of "none".
175    RefPtr<MediaStreamSourceCapabilities> sourceCapabilities = m_privateTrack->capabilities();
176    MediaStreamSource::ReadyState readyState = m_privateTrack->readyState();
177    if (readyState == MediaStreamSource::New || readyState == MediaStreamSource::Ended)
178        sourceCapabilities->setSourceType(MediaStreamSourceStates::None);
179
180    return MediaStreamCapabilities::create(sourceCapabilities.release());
181}
182
183void MediaStreamTrack::applyConstraints(const Dictionary& constraints)
184{
185    m_constraints->initialize(constraints);
186    m_privateTrack->applyConstraints(m_constraints);
187}
188
189void MediaStreamTrack::applyConstraints(PassRefPtr<MediaConstraints>)
190{
191    // FIXME: apply the new constraints to the track
192    // https://bugs.webkit.org/show_bug.cgi?id=122428
193}
194
195RefPtr<MediaStreamTrack> MediaStreamTrack::clone()
196{
197    if (m_privateTrack->type() == MediaStreamSource::Audio)
198        return AudioStreamTrack::create(*this);
199
200    return VideoStreamTrack::create(*this);
201}
202
203void MediaStreamTrack::stopProducingData()
204{
205    // NOTE: this method is called when the "stop" method is called from JS, using
206    // the "ImplementedAs" IDL attribute. This is done because ActiveDOMObject requires
207    // a "stop" method.
208
209    // The stop method should "Permanently stop the generation of data for track's source", but it
210    // should not post an 'ended' event.
211    m_stoppingTrack = true;
212    m_privateTrack->stop(MediaStreamTrackPrivate::StopTrackAndStopSource);
213    m_stoppingTrack = false;
214}
215
216bool MediaStreamTrack::ended() const
217{
218    return m_privateTrack->ended();
219}
220
221void MediaStreamTrack::addObserver(MediaStreamTrack::Observer* observer)
222{
223    m_observers.append(observer);
224}
225
226void MediaStreamTrack::removeObserver(MediaStreamTrack::Observer* observer)
227{
228    size_t pos = m_observers.find(observer);
229    if (pos != notFound)
230        m_observers.remove(pos);
231}
232
233void MediaStreamTrack::trackReadyStateChanged()
234{
235    if (stopped())
236        return;
237
238    MediaStreamSource::ReadyState readyState = m_privateTrack->readyState();
239    if (readyState == MediaStreamSource::Live)
240        scheduleEventDispatch(Event::create(eventNames().startedEvent, false, false));
241    else if (readyState == MediaStreamSource::Ended && !m_stoppingTrack)
242        scheduleEventDispatch(Event::create(eventNames().endedEvent, false, false));
243
244    configureTrackRendering();
245}
246
247void MediaStreamTrack::trackMutedChanged()
248{
249    if (stopped())
250        return;
251
252    if (muted())
253        scheduleEventDispatch(Event::create(eventNames().muteEvent, false, false));
254    else
255        scheduleEventDispatch(Event::create(eventNames().unmuteEvent, false, false));
256
257    configureTrackRendering();
258}
259
260void MediaStreamTrack::trackEnabledChanged()
261{
262    if (stopped())
263        return;
264
265    setEnabled(m_privateTrack->enabled());
266    configureTrackRendering();
267}
268
269void MediaStreamTrack::configureTrackRendering()
270{
271    if (stopped())
272        return;
273
274    // 4.3.1
275    // ... media from the source only flows when a MediaStreamTrack object is both unmuted and enabled
276}
277
278void MediaStreamTrack::trackDidEnd()
279{
280    m_privateTrack->setReadyState(MediaStreamSource::Ended);
281
282    for (Vector<Observer*>::iterator i = m_observers.begin(); i != m_observers.end(); ++i)
283        (*i)->trackDidEnd();
284}
285
286void MediaStreamTrack::stop()
287{
288    m_privateTrack->stop(MediaStreamTrackPrivate::StopTrackOnly);
289}
290
291void MediaStreamTrack::scheduleEventDispatch(PassRefPtr<Event> event)
292{
293    {
294        MutexLocker locker(m_mutex);
295        m_scheduledEvents.append(event);
296        if (m_eventDispatchScheduled)
297            return;
298        m_eventDispatchScheduled = true;
299    }
300
301    callOnMainThread(bind(&MediaStreamTrack::dispatchQueuedEvents, this));
302}
303
304void MediaStreamTrack::dispatchQueuedEvents()
305{
306    Vector<RefPtr<Event>> events;
307    {
308        MutexLocker locker(m_mutex);
309        m_eventDispatchScheduled = false;
310        events.swap(m_scheduledEvents);
311    }
312    if (!scriptExecutionContext())
313        return;
314
315    for (auto it = events.begin(); it != events.end(); ++it)
316        dispatchEvent((*it).release());
317
318    events.clear();
319}
320
321} // namespace WebCore
322
323#endif // ENABLE(MEDIA_STREAM)
324