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