1/*
2 * Copyright (C) 2011 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27
28#if ENABLE(VIDEO)
29#include "MediaController.h"
30
31#include "Clock.h"
32#include "ExceptionCode.h"
33#include "HTMLMediaElement.h"
34#include "TimeRanges.h"
35#include <wtf/CurrentTime.h>
36#include <wtf/StdLibExtras.h>
37#include <wtf/text/AtomicString.h>
38
39using namespace WebCore;
40using namespace std;
41
42PassRefPtr<MediaController> MediaController::create(ScriptExecutionContext* context)
43{
44    return adoptRef(new MediaController(context));
45}
46
47MediaController::MediaController(ScriptExecutionContext* context)
48    : m_paused(false)
49    , m_defaultPlaybackRate(1)
50    , m_volume(1)
51    , m_position(MediaPlayer::invalidTime())
52    , m_muted(false)
53    , m_readyState(HAVE_NOTHING)
54    , m_playbackState(WAITING)
55    , m_asyncEventTimer(this, &MediaController::asyncEventTimerFired)
56    , m_clearPositionTimer(this, &MediaController::clearPositionTimerFired)
57    , m_closedCaptionsVisible(false)
58    , m_clock(Clock::create())
59    , m_scriptExecutionContext(context)
60    , m_timeupdateTimer(this, &MediaController::timeupdateTimerFired)
61    , m_previousTimeupdateTime(0)
62{
63}
64
65MediaController::~MediaController()
66{
67}
68
69void MediaController::addMediaElement(HTMLMediaElement* element)
70{
71    ASSERT(element);
72    ASSERT(!m_mediaElements.contains(element));
73
74    m_mediaElements.append(element);
75    bringElementUpToSpeed(element);
76}
77
78void MediaController::removeMediaElement(HTMLMediaElement* element)
79{
80    ASSERT(element);
81    ASSERT(m_mediaElements.contains(element));
82    m_mediaElements.remove(m_mediaElements.find(element));
83}
84
85bool MediaController::containsMediaElement(HTMLMediaElement* element) const
86{
87    return m_mediaElements.contains(element);
88}
89
90PassRefPtr<TimeRanges> MediaController::buffered() const
91{
92    if (m_mediaElements.isEmpty())
93        return TimeRanges::create();
94
95    // The buffered attribute must return a new static normalized TimeRanges object that represents
96    // the intersection of the ranges of the media resources of the slaved media elements that the
97    // user agent has buffered, at the time the attribute is evaluated.
98    RefPtr<TimeRanges> bufferedRanges = m_mediaElements.first()->buffered();
99    for (size_t index = 1; index < m_mediaElements.size(); ++index)
100        bufferedRanges->intersectWith(m_mediaElements[index]->buffered().get());
101    return bufferedRanges;
102}
103
104PassRefPtr<TimeRanges> MediaController::seekable() const
105{
106    if (m_mediaElements.isEmpty())
107        return TimeRanges::create();
108
109    // The seekable attribute must return a new static normalized TimeRanges object that represents
110    // the intersection of the ranges of the media resources of the slaved media elements that the
111    // user agent is able to seek to, at the time the attribute is evaluated.
112    RefPtr<TimeRanges> seekableRanges = m_mediaElements.first()->seekable();
113    for (size_t index = 1; index < m_mediaElements.size(); ++index)
114        seekableRanges->intersectWith(m_mediaElements[index]->seekable().get());
115    return seekableRanges;
116}
117
118PassRefPtr<TimeRanges> MediaController::played()
119{
120    if (m_mediaElements.isEmpty())
121        return TimeRanges::create();
122
123    // The played attribute must return a new static normalized TimeRanges object that represents
124    // the union of the ranges of the media resources of the slaved media elements that the
125    // user agent has so far rendered, at the time the attribute is evaluated.
126    RefPtr<TimeRanges> playedRanges = m_mediaElements.first()->played();
127    for (size_t index = 1; index < m_mediaElements.size(); ++index)
128        playedRanges->unionWith(m_mediaElements[index]->played().get());
129    return playedRanges;
130}
131
132double MediaController::duration() const
133{
134    // FIXME: Investigate caching the maximum duration and only updating the cached value
135    // when the slaved media elements' durations change.
136    double maxDuration = 0;
137    for (size_t index = 0; index < m_mediaElements.size(); ++index) {
138        double duration = m_mediaElements[index]->duration();
139        if (std::isnan(duration))
140            continue;
141        maxDuration = max(maxDuration, duration);
142    }
143    return maxDuration;
144}
145
146double MediaController::currentTime() const
147{
148    if (m_mediaElements.isEmpty())
149        return 0;
150
151    if (m_position == MediaPlayer::invalidTime()) {
152        // Some clocks may return times outside the range of [0..duration].
153        m_position = max(0.0, min(duration(), m_clock->currentTime()));
154        m_clearPositionTimer.startOneShot(0);
155    }
156
157    return m_position;
158}
159
160void MediaController::setCurrentTime(double time, ExceptionCode& code)
161{
162    // When the user agent is to seek the media controller to a particular new playback position,
163    // it must follow these steps:
164    // If the new playback position is less than zero, then set it to zero.
165    time = max(0.0, time);
166
167    // If the new playback position is greater than the media controller duration, then set it
168    // to the media controller duration.
169    time = min(time, duration());
170
171    // Set the media controller position to the new playback position.
172    m_clock->setCurrentTime(time);
173
174    // Seek each slaved media element to the new playback position relative to the media element timeline.
175    for (size_t index = 0; index < m_mediaElements.size(); ++index)
176        m_mediaElements[index]->seek(time, code);
177
178    scheduleTimeupdateEvent();
179}
180
181void MediaController::unpause()
182{
183    // When the unpause() method is invoked, if the MediaController is a paused media controller,
184    if (!m_paused)
185        return;
186    // the user agent must change the MediaController into a playing media controller,
187    m_paused = false;
188    // queue a task to fire a simple event named play at the MediaController,
189    scheduleEvent(eventNames().playEvent);
190    // and then report the controller state of the MediaController.
191    reportControllerState();
192}
193
194void MediaController::play()
195{
196    // When the play() method is invoked, the user agent must invoke the play method of each
197    // slaved media element in turn,
198    for (size_t index = 0; index < m_mediaElements.size(); ++index)
199        m_mediaElements[index]->play();
200
201    // and then invoke the unpause method of the MediaController.
202    unpause();
203}
204
205void MediaController::pause()
206{
207    // When the pause() method is invoked, if the MediaController is a playing media controller,
208    if (m_paused)
209        return;
210
211    // then the user agent must change the MediaController into a paused media controller,
212    m_paused = true;
213    // queue a task to fire a simple event named pause at the MediaController,
214    scheduleEvent(eventNames().pauseEvent);
215    // and then report the controller state of the MediaController.
216    reportControllerState();
217}
218
219void MediaController::setDefaultPlaybackRate(double rate)
220{
221    if (m_defaultPlaybackRate == rate)
222        return;
223
224    // The defaultPlaybackRate attribute, on setting, must set the MediaController's media controller
225    // default playback rate to the new value,
226    m_defaultPlaybackRate = rate;
227
228    // then queue a task to fire a simple event named ratechange at the MediaController.
229    scheduleEvent(eventNames().ratechangeEvent);
230}
231
232double MediaController::playbackRate() const
233{
234    return m_clock->playRate();
235}
236
237void MediaController::setPlaybackRate(double rate)
238{
239    if (m_clock->playRate() == rate)
240        return;
241
242    // The playbackRate attribute, on setting, must set the MediaController's media controller
243    // playback rate to the new value,
244    m_clock->setPlayRate(rate);
245
246    for (size_t index = 0; index < m_mediaElements.size(); ++index)
247        m_mediaElements[index]->updatePlaybackRate();
248
249    // then queue a task to fire a simple event named ratechange at the MediaController.
250    scheduleEvent(eventNames().ratechangeEvent);
251}
252
253void MediaController::setVolume(double level, ExceptionCode& code)
254{
255    if (m_volume == level)
256        return;
257
258    // If the new value is outside the range 0.0 to 1.0 inclusive, then, on setting, an
259    // IndexSizeError exception must be raised instead.
260    if (level < 0 || level > 1) {
261        code = INDEX_SIZE_ERR;
262        return;
263    }
264
265    // The volume attribute, on setting, if the new value is in the range 0.0 to 1.0 inclusive,
266    // must set the MediaController's media controller volume multiplier to the new value
267    m_volume = level;
268
269    // and queue a task to fire a simple event named volumechange at the MediaController.
270    scheduleEvent(eventNames().volumechangeEvent);
271
272    for (size_t index = 0; index < m_mediaElements.size(); ++index)
273        m_mediaElements[index]->updateVolume();
274}
275
276void MediaController::setMuted(bool flag)
277{
278    if (m_muted == flag)
279        return;
280
281    // The muted attribute, on setting, must set the MediaController's media controller mute override
282    // to the new value
283    m_muted = flag;
284
285    // and queue a task to fire a simple event named volumechange at the MediaController.
286    scheduleEvent(eventNames().volumechangeEvent);
287
288    for (size_t index = 0; index < m_mediaElements.size(); ++index)
289        m_mediaElements[index]->updateVolume();
290}
291
292static const AtomicString& playbackStateWaiting()
293{
294    DEFINE_STATIC_LOCAL(AtomicString, waiting, ("waiting", AtomicString::ConstructFromLiteral));
295    return waiting;
296}
297
298static const AtomicString& playbackStatePlaying()
299{
300    DEFINE_STATIC_LOCAL(AtomicString, playing, ("playing", AtomicString::ConstructFromLiteral));
301    return playing;
302}
303
304static const AtomicString& playbackStateEnded()
305{
306    DEFINE_STATIC_LOCAL(AtomicString, ended, ("ended", AtomicString::ConstructFromLiteral));
307    return ended;
308}
309
310const AtomicString& MediaController::playbackState() const
311{
312    switch (m_playbackState) {
313    case WAITING:
314        return playbackStateWaiting();
315    case PLAYING:
316        return playbackStatePlaying();
317    case ENDED:
318        return playbackStateEnded();
319    default:
320        ASSERT_NOT_REACHED();
321        return nullAtom;
322    }
323}
324
325void MediaController::reportControllerState()
326{
327    updateReadyState();
328    updatePlaybackState();
329}
330
331static AtomicString eventNameForReadyState(MediaControllerInterface::ReadyState state)
332{
333    switch (state) {
334    case MediaControllerInterface::HAVE_NOTHING:
335        return eventNames().emptiedEvent;
336    case MediaControllerInterface::HAVE_METADATA:
337        return eventNames().loadedmetadataEvent;
338    case MediaControllerInterface::HAVE_CURRENT_DATA:
339        return eventNames().loadeddataEvent;
340    case MediaControllerInterface::HAVE_FUTURE_DATA:
341        return eventNames().canplayEvent;
342    case MediaControllerInterface::HAVE_ENOUGH_DATA:
343        return eventNames().canplaythroughEvent;
344    default:
345        ASSERT_NOT_REACHED();
346        return nullAtom;
347    }
348}
349
350void MediaController::updateReadyState()
351{
352    ReadyState oldReadyState = m_readyState;
353    ReadyState newReadyState;
354
355    if (m_mediaElements.isEmpty()) {
356        // If the MediaController has no slaved media elements, let new readiness state be 0.
357        newReadyState = HAVE_NOTHING;
358    } else {
359        // Otherwise, let it have the lowest value of the readyState IDL attributes of all of its
360        // slaved media elements.
361        newReadyState = m_mediaElements.first()->readyState();
362        for (size_t index = 1; index < m_mediaElements.size(); ++index)
363            newReadyState = min(newReadyState, m_mediaElements[index]->readyState());
364    }
365
366    if (newReadyState == oldReadyState)
367        return;
368
369    // If the MediaController's most recently reported readiness state is greater than new readiness
370    // state then queue a task to fire a simple event at the MediaController object, whose name is the
371    // event name corresponding to the value of new readiness state given in the table below. [omitted]
372    if (oldReadyState > newReadyState) {
373        scheduleEvent(eventNameForReadyState(newReadyState));
374        return;
375    }
376
377    // If the MediaController's most recently reported readiness state is less than the new readiness
378    // state, then run these substeps:
379    // 1. Let next state be the MediaController's most recently reported readiness state.
380    ReadyState nextState = oldReadyState;
381    do {
382        // 2. Loop: Increment next state by one.
383        nextState = static_cast<ReadyState>(nextState + 1);
384        // 3. Queue a task to fire a simple event at the MediaController object, whose name is the
385        // event name corresponding to the value of next state given in the table below. [omitted]
386        scheduleEvent(eventNameForReadyState(nextState));
387        // If next state is less than new readiness state, then return to the step labeled loop
388    } while (nextState < newReadyState);
389
390    // Let the MediaController's most recently reported readiness state be new readiness state.
391    m_readyState = newReadyState;
392}
393
394void MediaController::updatePlaybackState()
395{
396    PlaybackState oldPlaybackState = m_playbackState;
397    PlaybackState newPlaybackState;
398
399    // Initialize new playback state by setting it to the state given for the first matching
400    // condition from the following list:
401    if (m_mediaElements.isEmpty()) {
402        // If the MediaController has no slaved media elements
403        // Let new playback state be waiting.
404        newPlaybackState = WAITING;
405    } else if (hasEnded()) {
406        // If all of the MediaController's slaved media elements have ended playback and the media
407        // controller playback rate is positive or zero
408        // Let new playback state be ended.
409        newPlaybackState = ENDED;
410    } else if (isBlocked()) {
411        // If the MediaController is a blocked media controller
412        // Let new playback state be waiting.
413        newPlaybackState = WAITING;
414    } else {
415        // Otherwise
416        // Let new playback state be playing.
417        newPlaybackState = PLAYING;
418    }
419
420    // If the MediaController's most recently reported playback state is not equal to new playback state
421    if (newPlaybackState == oldPlaybackState)
422        return;
423
424    // and the new playback state is ended,
425    if (newPlaybackState == ENDED) {
426        // then queue a task that, if the MediaController object is a playing media controller, and
427        // all of the MediaController's slaved media elements have still ended playback, and the
428        // media controller playback rate is still positive or zero,
429        if (!m_paused && hasEnded()) {
430            // changes the MediaController object to a paused media controller
431            m_paused = true;
432
433            // and then fires a simple event named pause at the MediaController object.
434            scheduleEvent(eventNames().pauseEvent);
435        }
436    }
437
438    // If the MediaController's most recently reported playback state is not equal to new playback state
439    // then queue a task to fire a simple event at the MediaController object, whose name is playing
440    // if new playback state is playing, ended if new playback state is ended, and waiting otherwise.
441    AtomicString eventName;
442    switch (newPlaybackState) {
443    case WAITING:
444        eventName = eventNames().waitingEvent;
445        m_clock->stop();
446        m_timeupdateTimer.stop();
447        break;
448    case ENDED:
449        eventName = eventNames().endedEvent;
450        m_clock->stop();
451        m_timeupdateTimer.stop();
452        break;
453    case PLAYING:
454        eventName = eventNames().playingEvent;
455        m_clock->start();
456        startTimeupdateTimer();
457        break;
458    default:
459        ASSERT_NOT_REACHED();
460    }
461    scheduleEvent(eventName);
462
463    // Let the MediaController's most recently reported playback state be new playback state.
464    m_playbackState = newPlaybackState;
465
466    updateMediaElements();
467}
468
469void MediaController::updateMediaElements()
470{
471    for (size_t index = 0; index < m_mediaElements.size(); ++index)
472        m_mediaElements[index]->updatePlayState();
473}
474
475void MediaController::bringElementUpToSpeed(HTMLMediaElement* element)
476{
477    ASSERT(element);
478    ASSERT(m_mediaElements.contains(element));
479
480    // When the user agent is to bring a media element up to speed with its new media controller,
481    // it must seek that media element to the MediaController's media controller position relative
482    // to the media element's timeline.
483    element->seek(currentTime(), IGNORE_EXCEPTION);
484}
485
486bool MediaController::isBlocked() const
487{
488    // A MediaController is a blocked media controller if the MediaController is a paused media
489    // controller,
490    if (m_paused)
491        return true;
492
493    if (m_mediaElements.isEmpty())
494        return false;
495
496    bool allPaused = true;
497    for (size_t index = 0; index < m_mediaElements.size(); ++index) {
498        HTMLMediaElement* element = m_mediaElements[index];
499        //  or if any of its slaved media elements are blocked media elements,
500        if (element->isBlocked())
501            return true;
502
503        // or if any of its slaved media elements whose autoplaying flag is true still have their
504        // paused attribute set to true,
505        if (element->isAutoplaying() && element->paused())
506            return true;
507
508        if (!element->paused())
509            allPaused = false;
510    }
511
512    // or if all of its slaved media elements have their paused attribute set to true.
513    return allPaused;
514}
515
516bool MediaController::hasEnded() const
517{
518    // If the ... media controller playback rate is positive or zero
519    if (m_clock->playRate() < 0)
520        return false;
521
522    // [and] all of the MediaController's slaved media elements have ended playback ... let new
523    // playback state be ended.
524    if (m_mediaElements.isEmpty())
525        return false;
526
527    bool allHaveEnded = true;
528    for (size_t index = 0; index < m_mediaElements.size(); ++index) {
529        if (!m_mediaElements[index]->ended())
530            allHaveEnded = false;
531    }
532    return allHaveEnded;
533}
534
535void MediaController::scheduleEvent(const AtomicString& eventName)
536{
537    m_pendingEvents.append(Event::create(eventName, false, true));
538    if (!m_asyncEventTimer.isActive())
539        m_asyncEventTimer.startOneShot(0);
540}
541
542void MediaController::asyncEventTimerFired(Timer<MediaController>*)
543{
544    Vector<RefPtr<Event> > pendingEvents;
545
546    m_pendingEvents.swap(pendingEvents);
547    size_t count = pendingEvents.size();
548    for (size_t index = 0; index < count; ++index)
549        dispatchEvent(pendingEvents[index].release(), IGNORE_EXCEPTION);
550}
551
552void MediaController::clearPositionTimerFired(Timer<MediaController>*)
553{
554    m_position = MediaPlayer::invalidTime();
555}
556
557bool MediaController::hasAudio() const
558{
559    for (size_t index = 0; index < m_mediaElements.size(); ++index) {
560        if (m_mediaElements[index]->hasAudio())
561            return true;
562    }
563    return false;
564}
565
566bool MediaController::hasVideo() const
567{
568    for (size_t index = 0; index < m_mediaElements.size(); ++index) {
569        if (m_mediaElements[index]->hasVideo())
570            return true;
571    }
572    return false;
573}
574
575bool MediaController::hasClosedCaptions() const
576{
577    for (size_t index = 0; index < m_mediaElements.size(); ++index) {
578        if (m_mediaElements[index]->hasClosedCaptions())
579            return true;
580    }
581    return false;
582}
583
584void MediaController::setClosedCaptionsVisible(bool visible)
585{
586    m_closedCaptionsVisible = visible;
587    for (size_t index = 0; index < m_mediaElements.size(); ++index)
588        m_mediaElements[index]->setClosedCaptionsVisible(visible);
589}
590
591bool MediaController::supportsScanning() const
592{
593    for (size_t index = 0; index < m_mediaElements.size(); ++index) {
594        if (!m_mediaElements[index]->supportsScanning())
595            return false;
596    }
597    return true;
598}
599
600void MediaController::beginScrubbing()
601{
602    for (size_t index = 0; index < m_mediaElements.size(); ++index)
603        m_mediaElements[index]->beginScrubbing();
604    if (m_playbackState == PLAYING)
605        m_clock->stop();
606}
607
608void MediaController::endScrubbing()
609{
610    for (size_t index = 0; index < m_mediaElements.size(); ++index)
611        m_mediaElements[index]->endScrubbing();
612    if (m_playbackState == PLAYING)
613        m_clock->start();
614}
615
616bool MediaController::canPlay() const
617{
618    if (m_paused)
619        return true;
620
621    for (size_t index = 0; index < m_mediaElements.size(); ++index) {
622        if (!m_mediaElements[index]->canPlay())
623            return false;
624    }
625    return true;
626}
627
628bool MediaController::isLiveStream() const
629{
630    for (size_t index = 0; index < m_mediaElements.size(); ++index) {
631        if (!m_mediaElements[index]->isLiveStream())
632            return false;
633    }
634    return true;
635}
636
637bool MediaController::hasCurrentSrc() const
638{
639    for (size_t index = 0; index < m_mediaElements.size(); ++index) {
640        if (!m_mediaElements[index]->hasCurrentSrc())
641            return false;
642    }
643    return true;
644}
645
646void MediaController::returnToRealtime()
647{
648    for (size_t index = 0; index < m_mediaElements.size(); ++index)
649        m_mediaElements[index]->returnToRealtime();
650}
651
652const AtomicString& MediaController::interfaceName() const
653{
654    return eventNames().interfaceForMediaController;
655}
656
657// The spec says to fire periodic timeupdate events (those sent while playing) every
658// "15 to 250ms", we choose the slowest frequency
659static const double maxTimeupdateEventFrequency = 0.25;
660
661void MediaController::startTimeupdateTimer()
662{
663    if (m_timeupdateTimer.isActive())
664        return;
665
666    m_timeupdateTimer.startRepeating(maxTimeupdateEventFrequency);
667}
668
669void MediaController::timeupdateTimerFired(Timer<MediaController>*)
670{
671    scheduleTimeupdateEvent();
672}
673
674void MediaController::scheduleTimeupdateEvent()
675{
676    double now = WTF::currentTime();
677    double timedelta = now - m_previousTimeupdateTime;
678
679    if (timedelta < maxTimeupdateEventFrequency)
680        return;
681
682    scheduleEvent(eventNames().timeupdateEvent);
683    m_previousTimeupdateTime = now;
684}
685
686#endif
687