1/*
2 * Copyright (C) 2011 Google Inc. All rights reserved.
3 * Copyright (C) 2011, 2012 Ericsson AB. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
21 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "modules/mediastream/MediaStream.h"
28
29#include "bindings/v8/ExceptionState.h"
30#include "core/dom/Event.h"
31#include "core/dom/ExceptionCode.h"
32#include "core/platform/mediastream/MediaStreamCenter.h"
33#include "core/platform/mediastream/MediaStreamSource.h"
34#include "modules/mediastream/MediaStreamRegistry.h"
35#include "modules/mediastream/MediaStreamTrackEvent.h"
36
37namespace WebCore {
38
39static bool containsSource(MediaStreamSourceVector& sourceVector, MediaStreamSource* source)
40{
41    for (size_t i = 0; i < sourceVector.size(); ++i) {
42        if (source->id() == sourceVector[i]->id())
43            return true;
44    }
45    return false;
46}
47
48static void processTrack(MediaStreamTrack* track, MediaStreamSourceVector& sourceVector)
49{
50    if (track->ended())
51        return;
52
53    MediaStreamSource* source = track->component()->source();
54    if (!containsSource(sourceVector, source))
55        sourceVector.append(source);
56}
57
58static PassRefPtr<MediaStream> createFromSourceVectors(ScriptExecutionContext* context, const MediaStreamSourceVector& audioSources, const MediaStreamSourceVector& videoSources)
59{
60    RefPtr<MediaStreamDescriptor> descriptor = MediaStreamDescriptor::create(audioSources, videoSources);
61    MediaStreamCenter::instance().didCreateMediaStream(descriptor.get());
62
63    return MediaStream::create(context, descriptor.release());
64}
65
66PassRefPtr<MediaStream> MediaStream::create(ScriptExecutionContext* context)
67{
68    MediaStreamSourceVector audioSources;
69    MediaStreamSourceVector videoSources;
70
71    return createFromSourceVectors(context, audioSources, videoSources);
72}
73
74PassRefPtr<MediaStream> MediaStream::create(ScriptExecutionContext* context, PassRefPtr<MediaStream> stream)
75{
76    ASSERT(stream);
77
78    MediaStreamSourceVector audioSources;
79    MediaStreamSourceVector videoSources;
80
81    for (size_t i = 0; i < stream->m_audioTracks.size(); ++i)
82        processTrack(stream->m_audioTracks[i].get(), audioSources);
83
84    for (size_t i = 0; i < stream->m_videoTracks.size(); ++i)
85        processTrack(stream->m_videoTracks[i].get(), videoSources);
86
87    return createFromSourceVectors(context, audioSources, videoSources);
88}
89
90PassRefPtr<MediaStream> MediaStream::create(ScriptExecutionContext* context, const MediaStreamTrackVector& tracks)
91{
92    MediaStreamSourceVector audioSources;
93    MediaStreamSourceVector videoSources;
94
95    for (size_t i = 0; i < tracks.size(); ++i)
96        processTrack(tracks[i].get(), tracks[i]->kind() == "audio" ? audioSources : videoSources);
97
98    return createFromSourceVectors(context, audioSources, videoSources);
99}
100
101PassRefPtr<MediaStream> MediaStream::create(ScriptExecutionContext* context, PassRefPtr<MediaStreamDescriptor> streamDescriptor)
102{
103    return adoptRef(new MediaStream(context, streamDescriptor));
104}
105
106MediaStream::MediaStream(ScriptExecutionContext* context, PassRefPtr<MediaStreamDescriptor> streamDescriptor)
107    : ContextLifecycleObserver(context)
108    , m_stopped(false)
109    , m_descriptor(streamDescriptor)
110    , m_scheduledEventTimer(this, &MediaStream::scheduledEventTimerFired)
111{
112    ScriptWrappable::init(this);
113    m_descriptor->setClient(this);
114
115    size_t numberOfAudioTracks = m_descriptor->numberOfAudioComponents();
116    m_audioTracks.reserveCapacity(numberOfAudioTracks);
117    for (size_t i = 0; i < numberOfAudioTracks; i++)
118        m_audioTracks.append(MediaStreamTrack::create(context, m_descriptor->audioComponent(i)));
119
120    size_t numberOfVideoTracks = m_descriptor->numberOfVideoComponents();
121    m_videoTracks.reserveCapacity(numberOfVideoTracks);
122    for (size_t i = 0; i < numberOfVideoTracks; i++)
123        m_videoTracks.append(MediaStreamTrack::create(context, m_descriptor->videoComponent(i)));
124}
125
126MediaStream::~MediaStream()
127{
128    m_descriptor->setClient(0);
129}
130
131bool MediaStream::ended() const
132{
133    return m_stopped || m_descriptor->ended();
134}
135
136void MediaStream::addTrack(PassRefPtr<MediaStreamTrack> prpTrack, ExceptionState& es)
137{
138    if (ended()) {
139        es.throwDOMException(InvalidStateError);
140        return;
141    }
142
143    if (!prpTrack) {
144        es.throwDOMException(TypeMismatchError);
145        return;
146    }
147
148    RefPtr<MediaStreamTrack> track = prpTrack;
149
150    if (getTrackById(track->id()))
151        return;
152
153    RefPtr<MediaStreamComponent> component = MediaStreamComponent::create(m_descriptor.get(), track->component()->source());
154    RefPtr<MediaStreamTrack> newTrack = MediaStreamTrack::create(scriptExecutionContext(), component.get());
155
156    switch (component->source()->type()) {
157    case MediaStreamSource::TypeAudio:
158        m_audioTracks.append(newTrack);
159        break;
160    case MediaStreamSource::TypeVideo:
161        m_videoTracks.append(newTrack);
162        break;
163    }
164
165    m_descriptor->addComponent(component.release());
166
167    MediaStreamCenter::instance().didAddMediaStreamTrack(m_descriptor.get(), newTrack->component());
168}
169
170void MediaStream::removeTrack(PassRefPtr<MediaStreamTrack> prpTrack, ExceptionState& es)
171{
172    if (ended()) {
173        es.throwDOMException(InvalidStateError);
174        return;
175    }
176
177    if (!prpTrack) {
178        es.throwDOMException(TypeMismatchError);
179        return;
180    }
181
182    RefPtr<MediaStreamTrack> track = prpTrack;
183
184    size_t pos = notFound;
185    switch (track->component()->source()->type()) {
186    case MediaStreamSource::TypeAudio:
187        pos = m_audioTracks.find(track);
188        if (pos != notFound)
189            m_audioTracks.remove(pos);
190        break;
191    case MediaStreamSource::TypeVideo:
192        pos = m_videoTracks.find(track);
193        if (pos != notFound)
194            m_videoTracks.remove(pos);
195        break;
196    }
197
198    if (pos == notFound)
199        return;
200
201    m_descriptor->removeComponent(track->component());
202
203    if (!m_audioTracks.size() && !m_videoTracks.size())
204        m_descriptor->setEnded();
205
206    MediaStreamCenter::instance().didRemoveMediaStreamTrack(m_descriptor.get(), track->component());
207}
208
209MediaStreamTrack* MediaStream::getTrackById(String id)
210{
211    for (MediaStreamTrackVector::iterator iter = m_audioTracks.begin(); iter != m_audioTracks.end(); ++iter) {
212        if ((*iter)->id() == id)
213            return (*iter).get();
214    }
215
216    for (MediaStreamTrackVector::iterator iter = m_videoTracks.begin(); iter != m_videoTracks.end(); ++iter) {
217        if ((*iter)->id() == id)
218            return (*iter).get();
219    }
220
221    return 0;
222}
223
224void MediaStream::stop()
225{
226    if (ended())
227        return;
228
229    MediaStreamCenter::instance().didStopLocalMediaStream(descriptor());
230
231    streamEnded();
232}
233
234void MediaStream::trackEnded()
235{
236    for (size_t i = 0; i < m_audioTracks.size(); ++i)
237        if (!m_audioTracks[i]->ended())
238            return;
239
240    for (size_t i = 0; i < m_videoTracks.size(); ++i)
241        if (!m_videoTracks[i]->ended())
242            return;
243
244    streamEnded();
245}
246
247void MediaStream::streamEnded()
248{
249    if (ended())
250        return;
251
252    m_descriptor->setEnded();
253    scheduleDispatchEvent(Event::create(eventNames().endedEvent, false, false));
254}
255
256void MediaStream::contextDestroyed()
257{
258    ContextLifecycleObserver::contextDestroyed();
259    m_stopped = true;
260}
261
262const AtomicString& MediaStream::interfaceName() const
263{
264    return eventNames().interfaceForMediaStream;
265}
266
267ScriptExecutionContext* MediaStream::scriptExecutionContext() const
268{
269    return ContextLifecycleObserver::scriptExecutionContext();
270}
271
272EventTargetData* MediaStream::eventTargetData()
273{
274    return &m_eventTargetData;
275}
276
277EventTargetData* MediaStream::ensureEventTargetData()
278{
279    return &m_eventTargetData;
280}
281
282void MediaStream::addRemoteTrack(MediaStreamComponent* component)
283{
284    ASSERT(component && !component->stream());
285    if (ended())
286        return;
287
288    component->setStream(descriptor());
289
290    RefPtr<MediaStreamTrack> track = MediaStreamTrack::create(scriptExecutionContext(), component);
291    switch (component->source()->type()) {
292    case MediaStreamSource::TypeAudio:
293        m_audioTracks.append(track);
294        break;
295    case MediaStreamSource::TypeVideo:
296        m_videoTracks.append(track);
297        break;
298    }
299    m_descriptor->addComponent(component);
300
301    scheduleDispatchEvent(MediaStreamTrackEvent::create(eventNames().addtrackEvent, false, false, track));
302}
303
304void MediaStream::removeRemoteTrack(MediaStreamComponent* component)
305{
306    if (ended())
307        return;
308
309    MediaStreamTrackVector* tracks = 0;
310    switch (component->source()->type()) {
311    case MediaStreamSource::TypeAudio:
312        tracks = &m_audioTracks;
313        break;
314    case MediaStreamSource::TypeVideo:
315        tracks = &m_videoTracks;
316        break;
317    }
318
319    size_t index = notFound;
320    for (size_t i = 0; i < tracks->size(); ++i) {
321        if ((*tracks)[i]->component() == component) {
322            index = i;
323            break;
324        }
325    }
326    if (index == notFound)
327        return;
328
329    m_descriptor->removeComponent(component);
330
331    RefPtr<MediaStreamTrack> track = (*tracks)[index];
332    tracks->remove(index);
333    scheduleDispatchEvent(MediaStreamTrackEvent::create(eventNames().removetrackEvent, false, false, track));
334}
335
336void MediaStream::scheduleDispatchEvent(PassRefPtr<Event> event)
337{
338    m_scheduledEvents.append(event);
339
340    if (!m_scheduledEventTimer.isActive())
341        m_scheduledEventTimer.startOneShot(0);
342}
343
344void MediaStream::scheduledEventTimerFired(Timer<MediaStream>*)
345{
346    if (m_stopped)
347        return;
348
349    Vector<RefPtr<Event> > events;
350    events.swap(m_scheduledEvents);
351
352    Vector<RefPtr<Event> >::iterator it = events.begin();
353    for (; it != events.end(); ++it)
354        dispatchEvent((*it).release());
355
356    events.clear();
357}
358
359URLRegistry& MediaStream::registry() const
360{
361    return MediaStreamRegistry::registry();
362}
363
364} // namespace WebCore
365