Home | History | Annotate | Download | only in html
      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 #include "core/html/MediaController.h"
     28 
     29 #include "bindings/v8/ExceptionMessages.h"
     30 #include "bindings/v8/ExceptionState.h"
     31 #include "bindings/v8/ExceptionStatePlaceholder.h"
     32 #include "core/dom/ExceptionCode.h"
     33 #include "core/dom/ExecutionContext.h"
     34 #include "core/events/Event.h"
     35 #include "core/events/GenericEventQueue.h"
     36 #include "core/html/HTMLMediaElement.h"
     37 #include "core/html/TimeRanges.h"
     38 #include "platform/Clock.h"
     39 #include "wtf/CurrentTime.h"
     40 #include "wtf/StdLibExtras.h"
     41 #include "wtf/text/AtomicString.h"
     42 
     43 namespace WebCore {
     44 
     45 PassRefPtrWillBeRawPtr<MediaController> MediaController::create(ExecutionContext* context)
     46 {
     47     return adoptRefWillBeRefCountedGarbageCollected(new MediaController(context));
     48 }
     49 
     50 MediaController::MediaController(ExecutionContext* context)
     51     : m_paused(false)
     52     , m_defaultPlaybackRate(1)
     53     , m_volume(1)
     54     , m_position(MediaPlayer::invalidTime())
     55     , m_muted(false)
     56     , m_readyState(HTMLMediaElement::HAVE_NOTHING)
     57     , m_playbackState(WAITING)
     58     , m_pendingEventsQueue(GenericEventQueue::create(this))
     59     , m_clearPositionTimer(this, &MediaController::clearPositionTimerFired)
     60     , m_clock(Clock::create())
     61     , m_executionContext(context)
     62     , m_timeupdateTimer(this, &MediaController::timeupdateTimerFired)
     63     , m_previousTimeupdateTime(0)
     64 {
     65     ScriptWrappable::init(this);
     66 }
     67 
     68 MediaController::~MediaController()
     69 {
     70 }
     71 
     72 void MediaController::addMediaElement(HTMLMediaElement* element)
     73 {
     74     ASSERT(element);
     75     ASSERT(!m_mediaElements.contains(element));
     76 
     77     m_mediaElements.add(element);
     78     bringElementUpToSpeed(element);
     79 }
     80 
     81 void MediaController::removeMediaElement(HTMLMediaElement* element)
     82 {
     83     ASSERT(element);
     84     ASSERT(m_mediaElements.contains(element));
     85     m_mediaElements.remove(m_mediaElements.find(element));
     86 }
     87 
     88 PassRefPtr<TimeRanges> MediaController::buffered() const
     89 {
     90     if (m_mediaElements.isEmpty())
     91         return TimeRanges::create();
     92 
     93     // The buffered attribute must return a new static normalized TimeRanges object that represents
     94     // the intersection of the ranges of the media resources of the slaved media elements that the
     95     // user agent has buffered, at the time the attribute is evaluated.
     96     MediaElementSequence::const_iterator it = m_mediaElements.begin();
     97     RefPtr<TimeRanges> bufferedRanges = (*it)->buffered();
     98     for (++it; it != m_mediaElements.end(); ++it)
     99         bufferedRanges->intersectWith((*it)->buffered().get());
    100     return bufferedRanges;
    101 }
    102 
    103 PassRefPtr<TimeRanges> MediaController::seekable() const
    104 {
    105     if (m_mediaElements.isEmpty())
    106         return TimeRanges::create();
    107 
    108     // The seekable attribute must return a new static normalized TimeRanges object that represents
    109     // the intersection of the ranges of the media resources of the slaved media elements that the
    110     // user agent is able to seek to, at the time the attribute is evaluated.
    111     MediaElementSequence::const_iterator it = m_mediaElements.begin();
    112     RefPtr<TimeRanges> seekableRanges = (*it)->seekable();
    113     for (++it; it != m_mediaElements.end(); ++it)
    114         seekableRanges->intersectWith((*it)->seekable().get());
    115     return seekableRanges;
    116 }
    117 
    118 PassRefPtr<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     MediaElementSequence::const_iterator it = m_mediaElements.begin();
    127     RefPtr<TimeRanges> playedRanges = (*it)->played();
    128     for (++it; it != m_mediaElements.end(); ++it)
    129         playedRanges->unionWith((*it)->played().get());
    130     return playedRanges;
    131 }
    132 
    133 double MediaController::duration() const
    134 {
    135     // FIXME: Investigate caching the maximum duration and only updating the cached value
    136     // when the slaved media elements' durations change.
    137     double maxDuration = 0;
    138     for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it) {
    139         double duration = (*it)->duration();
    140         if (std::isnan(duration))
    141             continue;
    142         maxDuration = std::max(maxDuration, duration);
    143     }
    144     return maxDuration;
    145 }
    146 
    147 double MediaController::currentTime() const
    148 {
    149     if (m_mediaElements.isEmpty())
    150         return 0;
    151 
    152     if (m_position == MediaPlayer::invalidTime()) {
    153         // Some clocks may return times outside the range of [0..duration].
    154         m_position = std::max(0.0, std::min(duration(), m_clock->currentTime()));
    155         m_clearPositionTimer.startOneShot(0, FROM_HERE);
    156     }
    157 
    158     return m_position;
    159 }
    160 
    161 void MediaController::setCurrentTime(double time, ExceptionState& exceptionState)
    162 {
    163     // When the user agent is to seek the media controller to a particular new playback position,
    164     // it must follow these steps:
    165     // If the new playback position is less than zero, then set it to zero.
    166     time = std::max(0.0, time);
    167 
    168     // If the new playback position is greater than the media controller duration, then set it
    169     // to the media controller duration.
    170     time = std::min(time, duration());
    171 
    172     // Set the media controller position to the new playback position.
    173     m_position = time;
    174     m_clock->setCurrentTime(time);
    175 
    176     // Seek each slaved media element to the new playback position relative to the media element timeline.
    177     for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it)
    178         (*it)->seek(time, exceptionState);
    179 
    180     scheduleTimeupdateEvent();
    181 }
    182 
    183 void MediaController::unpause()
    184 {
    185     // When the unpause() method is invoked, if the MediaController is a paused media controller,
    186     if (!m_paused)
    187         return;
    188 
    189     // the user agent must change the MediaController into a playing media controller,
    190     m_paused = false;
    191     // queue a task to fire a simple event named play at the MediaController,
    192     scheduleEvent(EventTypeNames::play);
    193     // and then report the controller state of the MediaController.
    194     reportControllerState();
    195 }
    196 
    197 void MediaController::play()
    198 {
    199     // When the play() method is invoked, the user agent must invoke the play method of each
    200     // slaved media element in turn,
    201     for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it)
    202         (*it)->play();
    203 
    204     // and then invoke the unpause method of the MediaController.
    205     unpause();
    206 }
    207 
    208 void MediaController::pause()
    209 {
    210     // When the pause() method is invoked, if the MediaController is a playing media controller,
    211     if (m_paused)
    212         return;
    213 
    214     // then the user agent must change the MediaController into a paused media controller,
    215     m_paused = true;
    216     // queue a task to fire a simple event named pause at the MediaController,
    217     scheduleEvent(EventTypeNames::pause);
    218     // and then report the controller state of the MediaController.
    219     reportControllerState();
    220 }
    221 
    222 void MediaController::setDefaultPlaybackRate(double rate)
    223 {
    224     if (m_defaultPlaybackRate == rate)
    225         return;
    226 
    227     // The defaultPlaybackRate attribute, on setting, must set the MediaController's media controller
    228     // default playback rate to the new value,
    229     m_defaultPlaybackRate = rate;
    230 
    231     // then queue a task to fire a simple event named ratechange at the MediaController.
    232     scheduleEvent(EventTypeNames::ratechange);
    233 }
    234 
    235 double MediaController::playbackRate() const
    236 {
    237     return m_clock->playRate();
    238 }
    239 
    240 void MediaController::setPlaybackRate(double rate)
    241 {
    242     if (m_clock->playRate() == rate)
    243         return;
    244 
    245     // The playbackRate attribute, on setting, must set the MediaController's media controller
    246     // playback rate to the new value,
    247     m_clock->setPlayRate(rate);
    248 
    249     for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it)
    250         (*it)->updatePlaybackRate();
    251 
    252     // then queue a task to fire a simple event named ratechange at the MediaController.
    253     scheduleEvent(EventTypeNames::ratechange);
    254 }
    255 
    256 void MediaController::setVolume(double level, ExceptionState& exceptionState)
    257 {
    258     if (m_volume == level)
    259         return;
    260 
    261     // If the new value is outside the range 0.0 to 1.0 inclusive, then, on setting, an
    262     // IndexSizeError exception must be raised instead.
    263     if (level < 0 || level > 1) {
    264         exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexOutsideRange("volume", level, 0.0, ExceptionMessages::InclusiveBound, 1.0, ExceptionMessages::InclusiveBound));
    265         return;
    266     }
    267 
    268     // The volume attribute, on setting, if the new value is in the range 0.0 to 1.0 inclusive,
    269     // must set the MediaController's media controller volume multiplier to the new value
    270     m_volume = level;
    271 
    272     // and queue a task to fire a simple event named volumechange at the MediaController.
    273     scheduleEvent(EventTypeNames::volumechange);
    274 
    275     for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it)
    276         (*it)->updateVolume();
    277 }
    278 
    279 void MediaController::setMuted(bool flag)
    280 {
    281     if (m_muted == flag)
    282         return;
    283 
    284     // The muted attribute, on setting, must set the MediaController's media controller mute override
    285     // to the new value
    286     m_muted = flag;
    287 
    288     // and queue a task to fire a simple event named volumechange at the MediaController.
    289     scheduleEvent(EventTypeNames::volumechange);
    290 
    291     for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it)
    292         (*it)->updateVolume();
    293 }
    294 
    295 static const AtomicString& playbackStateWaiting()
    296 {
    297     DEFINE_STATIC_LOCAL(AtomicString, waiting, ("waiting", AtomicString::ConstructFromLiteral));
    298     return waiting;
    299 }
    300 
    301 static const AtomicString& playbackStatePlaying()
    302 {
    303     DEFINE_STATIC_LOCAL(AtomicString, playing, ("playing", AtomicString::ConstructFromLiteral));
    304     return playing;
    305 }
    306 
    307 static const AtomicString& playbackStateEnded()
    308 {
    309     DEFINE_STATIC_LOCAL(AtomicString, ended, ("ended", AtomicString::ConstructFromLiteral));
    310     return ended;
    311 }
    312 
    313 const AtomicString& MediaController::playbackState() const
    314 {
    315     switch (m_playbackState) {
    316     case WAITING:
    317         return playbackStateWaiting();
    318     case PLAYING:
    319         return playbackStatePlaying();
    320     case ENDED:
    321         return playbackStateEnded();
    322     default:
    323         ASSERT_NOT_REACHED();
    324         return nullAtom;
    325     }
    326 }
    327 
    328 void MediaController::reportControllerState()
    329 {
    330     updateReadyState();
    331     updatePlaybackState();
    332 }
    333 
    334 static const AtomicString& eventNameForReadyState(HTMLMediaElement::ReadyState state)
    335 {
    336     switch (state) {
    337     case HTMLMediaElement::HAVE_NOTHING:
    338         return EventTypeNames::emptied;
    339     case HTMLMediaElement::HAVE_METADATA:
    340         return EventTypeNames::loadedmetadata;
    341     case HTMLMediaElement::HAVE_CURRENT_DATA:
    342         return EventTypeNames::loadeddata;
    343     case HTMLMediaElement::HAVE_FUTURE_DATA:
    344         return EventTypeNames::canplay;
    345     case HTMLMediaElement::HAVE_ENOUGH_DATA:
    346         return EventTypeNames::canplaythrough;
    347     default:
    348         ASSERT_NOT_REACHED();
    349         return nullAtom;
    350     }
    351 }
    352 
    353 void MediaController::updateReadyState()
    354 {
    355     ReadyState oldReadyState = m_readyState;
    356     ReadyState newReadyState;
    357 
    358     if (m_mediaElements.isEmpty()) {
    359         // If the MediaController has no slaved media elements, let new readiness state be 0.
    360         newReadyState = HTMLMediaElement::HAVE_NOTHING;
    361     } else {
    362         // Otherwise, let it have the lowest value of the readyState IDL attributes of all of its
    363         // slaved media elements.
    364         MediaElementSequence::const_iterator it = m_mediaElements.begin();
    365         newReadyState = (*it)->readyState();
    366         for (++it; it != m_mediaElements.end(); ++it)
    367             newReadyState = std::min(newReadyState, (*it)->readyState());
    368     }
    369 
    370     if (newReadyState == oldReadyState)
    371         return;
    372 
    373     // If the MediaController's most recently reported readiness state is greater than new readiness
    374     // state then queue a task to fire a simple event at the MediaController object, whose name is the
    375     // event name corresponding to the value of new readiness state given in the table below. [omitted]
    376     if (oldReadyState > newReadyState) {
    377         scheduleEvent(eventNameForReadyState(newReadyState));
    378         return;
    379     }
    380 
    381     // If the MediaController's most recently reported readiness state is less than the new readiness
    382     // state, then run these substeps:
    383     // 1. Let next state be the MediaController's most recently reported readiness state.
    384     ReadyState nextState = oldReadyState;
    385     do {
    386         // 2. Loop: Increment next state by one.
    387         nextState = static_cast<ReadyState>(nextState + 1);
    388         // 3. Queue a task to fire a simple event at the MediaController object, whose name is the
    389         // event name corresponding to the value of next state given in the table below. [omitted]
    390         scheduleEvent(eventNameForReadyState(nextState));
    391         // If next state is less than new readiness state, then return to the step labeled loop
    392     } while (nextState < newReadyState);
    393 
    394     // Let the MediaController's most recently reported readiness state be new readiness state.
    395     m_readyState = newReadyState;
    396 }
    397 
    398 void MediaController::updatePlaybackState()
    399 {
    400     PlaybackState oldPlaybackState = m_playbackState;
    401     PlaybackState newPlaybackState;
    402 
    403     // Initialize new playback state by setting it to the state given for the first matching
    404     // condition from the following list:
    405     if (m_mediaElements.isEmpty()) {
    406         // If the MediaController has no slaved media elements
    407         // Let new playback state be waiting.
    408         newPlaybackState = WAITING;
    409     } else if (hasEnded()) {
    410         // If all of the MediaController's slaved media elements have ended playback and the media
    411         // controller playback rate is positive or zero
    412         // Let new playback state be ended.
    413         newPlaybackState = ENDED;
    414     } else if (isBlocked()) {
    415         // If the MediaController is a blocked media controller
    416         // Let new playback state be waiting.
    417         newPlaybackState = WAITING;
    418     } else {
    419         // Otherwise
    420         // Let new playback state be playing.
    421         newPlaybackState = PLAYING;
    422     }
    423 
    424     // If the MediaController's most recently reported playback state is not equal to new playback state
    425     if (newPlaybackState == oldPlaybackState)
    426         return;
    427 
    428     // and the new playback state is ended,
    429     if (newPlaybackState == ENDED) {
    430         // then queue a task that, if the MediaController object is a playing media controller, and
    431         // all of the MediaController's slaved media elements have still ended playback, and the
    432         // media controller playback rate is still positive or zero,
    433         if (!m_paused && hasEnded()) {
    434             // changes the MediaController object to a paused media controller
    435             m_paused = true;
    436 
    437             // and then fires a simple event named pause at the MediaController object.
    438             scheduleEvent(EventTypeNames::pause);
    439         }
    440     }
    441 
    442     // If the MediaController's most recently reported playback state is not equal to new playback state
    443     // then queue a task to fire a simple event at the MediaController object, whose name is playing
    444     // if new playback state is playing, ended if new playback state is ended, and waiting otherwise.
    445     AtomicString eventName;
    446     switch (newPlaybackState) {
    447     case WAITING:
    448         eventName = EventTypeNames::waiting;
    449         m_clock->stop();
    450         m_timeupdateTimer.stop();
    451         break;
    452     case ENDED:
    453         eventName = EventTypeNames::ended;
    454         m_clock->stop();
    455         m_timeupdateTimer.stop();
    456         break;
    457     case PLAYING:
    458         eventName = EventTypeNames::playing;
    459         m_clock->start();
    460         startTimeupdateTimer();
    461         break;
    462     default:
    463         ASSERT_NOT_REACHED();
    464     }
    465     scheduleEvent(eventName);
    466 
    467     // Let the MediaController's most recently reported playback state be new playback state.
    468     m_playbackState = newPlaybackState;
    469 
    470     updateMediaElements();
    471 }
    472 
    473 void MediaController::updateMediaElements()
    474 {
    475     for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it)
    476         (*it)->updatePlayState();
    477 }
    478 
    479 void MediaController::bringElementUpToSpeed(HTMLMediaElement* element)
    480 {
    481     ASSERT(element);
    482     ASSERT(m_mediaElements.contains(element));
    483 
    484     // When the user agent is to bring a media element up to speed with its new media controller,
    485     // it must seek that media element to the MediaController's media controller position relative
    486     // to the media element's timeline.
    487     element->seek(currentTime(), IGNORE_EXCEPTION);
    488 }
    489 
    490 bool MediaController::isRestrained() const
    491 {
    492     ASSERT(!m_mediaElements.isEmpty());
    493 
    494     // A MediaController is a restrained media controller if the MediaController is a playing media
    495     // controller,
    496     if (m_paused)
    497         return false;
    498 
    499     bool anyAutoplayingAndPaused = false;
    500     bool allPaused = true;
    501     for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it) {
    502         HTMLMediaElement* element = *it;
    503 
    504         // and none of its slaved media elements are blocked media elements,
    505         if (element->isBlocked())
    506             return false;
    507 
    508         if (element->isAutoplaying() && element->paused())
    509             anyAutoplayingAndPaused = true;
    510 
    511         if (!element->paused())
    512             allPaused = false;
    513     }
    514 
    515     // but either at least one of its slaved media elements whose autoplaying flag is true still has
    516     // its paused attribute set to true, or, all of its slaved media elements have their paused
    517     // attribute set to true.
    518     return anyAutoplayingAndPaused || allPaused;
    519 }
    520 
    521 bool MediaController::isBlocked() const
    522 {
    523     ASSERT(!m_mediaElements.isEmpty());
    524 
    525     // A MediaController is a blocked media controller if the MediaController is a paused media
    526     // controller,
    527     if (m_paused)
    528         return true;
    529 
    530     bool allPaused = true;
    531     for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it) {
    532         HTMLMediaElement* element = *it;
    533 
    534         // or if any of its slaved media elements are blocked media elements,
    535         if (element->isBlocked())
    536             return true;
    537 
    538         // or if any of its slaved media elements whose autoplaying flag is true still have their
    539         // paused attribute set to true,
    540         if (element->isAutoplaying() && element->paused())
    541             return true;
    542 
    543         if (!element->paused())
    544             allPaused = false;
    545     }
    546 
    547     // or if all of its slaved media elements have their paused attribute set to true.
    548     return allPaused;
    549 }
    550 
    551 bool MediaController::hasEnded() const
    552 {
    553     // If the ... media controller playback rate is positive or zero
    554     if (m_clock->playRate() < 0)
    555         return false;
    556 
    557     // [and] all of the MediaController's slaved media elements have ended playback ... let new
    558     // playback state be ended.
    559     if (m_mediaElements.isEmpty())
    560         return false;
    561 
    562     bool allHaveEnded = true;
    563     for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it) {
    564         if (!(*it)->ended())
    565             allHaveEnded = false;
    566     }
    567     return allHaveEnded;
    568 }
    569 
    570 void MediaController::scheduleEvent(const AtomicString& eventName)
    571 {
    572     m_pendingEventsQueue->enqueueEvent(Event::createCancelable(eventName));
    573 }
    574 
    575 void MediaController::clearPositionTimerFired(Timer<MediaController>*)
    576 {
    577     m_position = MediaPlayer::invalidTime();
    578 }
    579 
    580 const AtomicString& MediaController::interfaceName() const
    581 {
    582     return EventTargetNames::MediaController;
    583 }
    584 
    585 // The spec says to fire periodic timeupdate events (those sent while playing) every
    586 // "15 to 250ms", we choose the slowest frequency
    587 static const double maxTimeupdateEventFrequency = 0.25;
    588 
    589 void MediaController::startTimeupdateTimer()
    590 {
    591     if (m_timeupdateTimer.isActive())
    592         return;
    593 
    594     m_timeupdateTimer.startRepeating(maxTimeupdateEventFrequency, FROM_HERE);
    595 }
    596 
    597 void MediaController::timeupdateTimerFired(Timer<MediaController>*)
    598 {
    599     scheduleTimeupdateEvent();
    600 }
    601 
    602 void MediaController::scheduleTimeupdateEvent()
    603 {
    604     double now = WTF::currentTime();
    605     double timedelta = now - m_previousTimeupdateTime;
    606 
    607     if (timedelta < maxTimeupdateEventFrequency)
    608         return;
    609 
    610     scheduleEvent(EventTypeNames::timeupdate);
    611     m_previousTimeupdateTime = now;
    612 }
    613 
    614 void MediaController::trace(Visitor* visitor)
    615 {
    616     visitor->trace(m_mediaElements);
    617     visitor->trace(m_pendingEventsQueue);
    618     visitor->trace(m_executionContext);
    619     EventTargetWithInlineData::trace(visitor);
    620 }
    621 
    622 }
    623