This source file includes following definitions.
- create
 
- m_previousTimeupdateTime
 
- addMediaElement
 
- removeMediaElement
 
- containsMediaElement
 
- buffered
 
- seekable
 
- played
 
- duration
 
- currentTime
 
- setCurrentTime
 
- unpause
 
- play
 
- pause
 
- setDefaultPlaybackRate
 
- playbackRate
 
- setPlaybackRate
 
- setVolume
 
- setMuted
 
- playbackStateWaiting
 
- playbackStatePlaying
 
- playbackStateEnded
 
- playbackState
 
- reportControllerState
 
- eventNameForReadyState
 
- updateReadyState
 
- updatePlaybackState
 
- updateMediaElements
 
- bringElementUpToSpeed
 
- isRestrained
 
- isBlocked
 
- hasEnded
 
- scheduleEvent
 
- asyncEventTimerFired
 
- clearPositionTimerFired
 
- interfaceName
 
- startTimeupdateTimer
 
- timeupdateTimerFired
 
- scheduleTimeupdateEvent
 
#include "config.h"
#include "core/html/MediaController.h"
#include "bindings/v8/ExceptionMessages.h"
#include "bindings/v8/ExceptionState.h"
#include "bindings/v8/ExceptionStatePlaceholder.h"
#include "core/dom/ExceptionCode.h"
#include "core/html/HTMLMediaElement.h"
#include "core/html/TimeRanges.h"
#include "platform/Clock.h"
#include "wtf/CurrentTime.h"
#include "wtf/StdLibExtras.h"
#include "wtf/text/AtomicString.h"
using namespace WebCore;
using namespace std;
PassRefPtr<MediaController> MediaController::create(ExecutionContext* context)
{
    return adoptRef(new MediaController(context));
}
MediaController::MediaController(ExecutionContext* context)
    : m_paused(false)
    , m_defaultPlaybackRate(1)
    , m_volume(1)
    , m_position(MediaPlayer::invalidTime())
    , m_muted(false)
    , m_readyState(HTMLMediaElement::HAVE_NOTHING)
    , m_playbackState(WAITING)
    , m_asyncEventTimer(this, &MediaController::asyncEventTimerFired)
    , m_clearPositionTimer(this, &MediaController::clearPositionTimerFired)
    , m_clock(Clock::create())
    , m_executionContext(context)
    , m_timeupdateTimer(this, &MediaController::timeupdateTimerFired)
    , m_previousTimeupdateTime(0)
{
    ScriptWrappable::init(this);
}
MediaController::~MediaController()
{
}
void MediaController::addMediaElement(HTMLMediaElement* element)
{
    ASSERT(element);
    ASSERT(!m_mediaElements.contains(element));
    m_mediaElements.append(element);
    bringElementUpToSpeed(element);
}
void MediaController::removeMediaElement(HTMLMediaElement* element)
{
    ASSERT(element);
    ASSERT(m_mediaElements.contains(element));
    m_mediaElements.remove(m_mediaElements.find(element));
}
bool MediaController::containsMediaElement(HTMLMediaElement* element) const
{
    return m_mediaElements.contains(element);
}
PassRefPtr<TimeRanges> MediaController::buffered() const
{
    if (m_mediaElements.isEmpty())
        return TimeRanges::create();
    
    
    
    RefPtr<TimeRanges> bufferedRanges = m_mediaElements.first()->buffered();
    for (size_t index = 1; index < m_mediaElements.size(); ++index)
        bufferedRanges->intersectWith(m_mediaElements[index]->buffered().get());
    return bufferedRanges;
}
PassRefPtr<TimeRanges> MediaController::seekable() const
{
    if (m_mediaElements.isEmpty())
        return TimeRanges::create();
    
    
    
    RefPtr<TimeRanges> seekableRanges = m_mediaElements.first()->seekable();
    for (size_t index = 1; index < m_mediaElements.size(); ++index)
        seekableRanges->intersectWith(m_mediaElements[index]->seekable().get());
    return seekableRanges;
}
PassRefPtr<TimeRanges> MediaController::played()
{
    if (m_mediaElements.isEmpty())
        return TimeRanges::create();
    
    
    
    RefPtr<TimeRanges> playedRanges = m_mediaElements.first()->played();
    for (size_t index = 1; index < m_mediaElements.size(); ++index)
        playedRanges->unionWith(m_mediaElements[index]->played().get());
    return playedRanges;
}
double MediaController::duration() const
{
    
    
    double maxDuration = 0;
    for (size_t index = 0; index < m_mediaElements.size(); ++index) {
        double duration = m_mediaElements[index]->duration();
        if (std::isnan(duration))
            continue;
        maxDuration = max(maxDuration, duration);
    }
    return maxDuration;
}
double MediaController::currentTime() const
{
    if (m_mediaElements.isEmpty())
        return 0;
    if (m_position == MediaPlayer::invalidTime()) {
        
        m_position = max(0.0, min(duration(), m_clock->currentTime()));
        m_clearPositionTimer.startOneShot(0, FROM_HERE);
    }
    return m_position;
}
void MediaController::setCurrentTime(double time, ExceptionState& exceptionState)
{
    
    if (!std::isfinite(time)) {
        exceptionState.throwTypeError(ExceptionMessages::notAFiniteNumber(time));
        return;
    }
    
    
    
    time = max(0.0, time);
    
    
    time = min(time, duration());
    
    m_position = time;
    m_clock->setCurrentTime(time);
    
    for (size_t index = 0; index < m_mediaElements.size(); ++index)
        m_mediaElements[index]->seek(time, exceptionState);
    scheduleTimeupdateEvent();
}
void MediaController::unpause()
{
    
    if (!m_paused)
        return;
    
    m_paused = false;
    
    scheduleEvent(EventTypeNames::play);
    
    reportControllerState();
}
void MediaController::play()
{
    
    
    for (size_t index = 0; index < m_mediaElements.size(); ++index)
        m_mediaElements[index]->play();
    
    unpause();
}
void MediaController::pause()
{
    
    if (m_paused)
        return;
    
    m_paused = true;
    
    scheduleEvent(EventTypeNames::pause);
    
    reportControllerState();
}
void MediaController::setDefaultPlaybackRate(double rate, ExceptionState& exceptionState)
{
    
    if (!std::isfinite(rate)) {
        exceptionState.throwTypeError(ExceptionMessages::notAFiniteNumber(rate));
        return;
    }
    if (m_defaultPlaybackRate == rate)
        return;
    
    
    m_defaultPlaybackRate = rate;
    
    scheduleEvent(EventTypeNames::ratechange);
}
double MediaController::playbackRate() const
{
    return m_clock->playRate();
}
void MediaController::setPlaybackRate(double rate, ExceptionState& exceptionState)
{
    
    if (!std::isfinite(rate)) {
        exceptionState.throwTypeError(ExceptionMessages::notAFiniteNumber(rate));
        return;
    }
    if (m_clock->playRate() == rate)
        return;
    
    
    m_clock->setPlayRate(rate);
    for (size_t index = 0; index < m_mediaElements.size(); ++index)
        m_mediaElements[index]->updatePlaybackRate();
    
    scheduleEvent(EventTypeNames::ratechange);
}
void MediaController::setVolume(double level, ExceptionState& exceptionState)
{
    
    if (!std::isfinite(level)) {
        exceptionState.throwTypeError(ExceptionMessages::notAFiniteNumber(level));
        return;
    }
    if (m_volume == level)
        return;
    
    
    if (level < 0 || level > 1) {
        exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexOutsideRange("volume", level, 0.0, ExceptionMessages::InclusiveBound, 1.0, ExceptionMessages::InclusiveBound));
        return;
    }
    
    
    m_volume = level;
    
    scheduleEvent(EventTypeNames::volumechange);
    for (size_t index = 0; index < m_mediaElements.size(); ++index)
        m_mediaElements[index]->updateVolume();
}
void MediaController::setMuted(bool flag)
{
    if (m_muted == flag)
        return;
    
    
    m_muted = flag;
    
    scheduleEvent(EventTypeNames::volumechange);
    for (size_t index = 0; index < m_mediaElements.size(); ++index)
        m_mediaElements[index]->updateVolume();
}
static const AtomicString& playbackStateWaiting()
{
    DEFINE_STATIC_LOCAL(AtomicString, waiting, ("waiting", AtomicString::ConstructFromLiteral));
    return waiting;
}
static const AtomicString& playbackStatePlaying()
{
    DEFINE_STATIC_LOCAL(AtomicString, playing, ("playing", AtomicString::ConstructFromLiteral));
    return playing;
}
static const AtomicString& playbackStateEnded()
{
    DEFINE_STATIC_LOCAL(AtomicString, ended, ("ended", AtomicString::ConstructFromLiteral));
    return ended;
}
const AtomicString& MediaController::playbackState() const
{
    switch (m_playbackState) {
    case WAITING:
        return playbackStateWaiting();
    case PLAYING:
        return playbackStatePlaying();
    case ENDED:
        return playbackStateEnded();
    default:
        ASSERT_NOT_REACHED();
        return nullAtom;
    }
}
void MediaController::reportControllerState()
{
    updateReadyState();
    updatePlaybackState();
}
static const AtomicString& eventNameForReadyState(HTMLMediaElement::ReadyState state)
{
    switch (state) {
    case HTMLMediaElement::HAVE_NOTHING:
        return EventTypeNames::emptied;
    case HTMLMediaElement::HAVE_METADATA:
        return EventTypeNames::loadedmetadata;
    case HTMLMediaElement::HAVE_CURRENT_DATA:
        return EventTypeNames::loadeddata;
    case HTMLMediaElement::HAVE_FUTURE_DATA:
        return EventTypeNames::canplay;
    case HTMLMediaElement::HAVE_ENOUGH_DATA:
        return EventTypeNames::canplaythrough;
    default:
        ASSERT_NOT_REACHED();
        return nullAtom;
    }
}
void MediaController::updateReadyState()
{
    ReadyState oldReadyState = m_readyState;
    ReadyState newReadyState;
    if (m_mediaElements.isEmpty()) {
        
        newReadyState = HTMLMediaElement::HAVE_NOTHING;
    } else {
        
        
        newReadyState = m_mediaElements.first()->readyState();
        for (size_t index = 1; index < m_mediaElements.size(); ++index)
            newReadyState = min(newReadyState, m_mediaElements[index]->readyState());
    }
    if (newReadyState == oldReadyState)
        return;
    
    
    
    if (oldReadyState > newReadyState) {
        scheduleEvent(eventNameForReadyState(newReadyState));
        return;
    }
    
    
    
    ReadyState nextState = oldReadyState;
    do {
        
        nextState = static_cast<ReadyState>(nextState + 1);
        
        
        scheduleEvent(eventNameForReadyState(nextState));
        
    } while (nextState < newReadyState);
    
    m_readyState = newReadyState;
}
void MediaController::updatePlaybackState()
{
    PlaybackState oldPlaybackState = m_playbackState;
    PlaybackState newPlaybackState;
    
    
    if (m_mediaElements.isEmpty()) {
        
        
        newPlaybackState = WAITING;
    } else if (hasEnded()) {
        
        
        
        newPlaybackState = ENDED;
    } else if (isBlocked()) {
        
        
        newPlaybackState = WAITING;
    } else {
        
        
        newPlaybackState = PLAYING;
    }
    
    if (newPlaybackState == oldPlaybackState)
        return;
    
    if (newPlaybackState == ENDED) {
        
        
        
        if (!m_paused && hasEnded()) {
            
            m_paused = true;
            
            scheduleEvent(EventTypeNames::pause);
        }
    }
    
    
    
    AtomicString eventName;
    switch (newPlaybackState) {
    case WAITING:
        eventName = EventTypeNames::waiting;
        m_clock->stop();
        m_timeupdateTimer.stop();
        break;
    case ENDED:
        eventName = EventTypeNames::ended;
        m_clock->stop();
        m_timeupdateTimer.stop();
        break;
    case PLAYING:
        eventName = EventTypeNames::playing;
        m_clock->start();
        startTimeupdateTimer();
        break;
    default:
        ASSERT_NOT_REACHED();
    }
    scheduleEvent(eventName);
    
    m_playbackState = newPlaybackState;
    updateMediaElements();
}
void MediaController::updateMediaElements()
{
    for (size_t index = 0; index < m_mediaElements.size(); ++index)
        m_mediaElements[index]->updatePlayState();
}
void MediaController::bringElementUpToSpeed(HTMLMediaElement* element)
{
    ASSERT(element);
    ASSERT(m_mediaElements.contains(element));
    
    
    
    element->seek(currentTime(), IGNORE_EXCEPTION);
}
bool MediaController::isRestrained() const
{
    ASSERT(!m_mediaElements.isEmpty());
    
    
    if (m_paused)
        return false;
    bool anyAutoplayingAndPaused = false;
    bool allPaused = true;
    for (size_t index = 0; index < m_mediaElements.size(); ++index) {
        HTMLMediaElement* element = m_mediaElements[index];
        
        if (element->isBlocked())
            return false;
        if (element->isAutoplaying() && element->paused())
            anyAutoplayingAndPaused = true;
        if (!element->paused())
            allPaused = false;
    }
    
    
    
    return anyAutoplayingAndPaused || allPaused;
}
bool MediaController::isBlocked() const
{
    ASSERT(!m_mediaElements.isEmpty());
    
    
    if (m_paused)
        return true;
    bool allPaused = true;
    for (size_t index = 0; index < m_mediaElements.size(); ++index) {
        HTMLMediaElement* element = m_mediaElements[index];
        
        if (element->isBlocked())
            return true;
        
        
        if (element->isAutoplaying() && element->paused())
            return true;
        if (!element->paused())
            allPaused = false;
    }
    
    return allPaused;
}
bool MediaController::hasEnded() const
{
    
    if (m_clock->playRate() < 0)
        return false;
    
    
    if (m_mediaElements.isEmpty())
        return false;
    bool allHaveEnded = true;
    for (size_t index = 0; index < m_mediaElements.size(); ++index) {
        if (!m_mediaElements[index]->ended())
            allHaveEnded = false;
    }
    return allHaveEnded;
}
void MediaController::scheduleEvent(const AtomicString& eventName)
{
    m_pendingEvents.append(Event::createCancelable(eventName));
    if (!m_asyncEventTimer.isActive())
        m_asyncEventTimer.startOneShot(0, FROM_HERE);
}
void MediaController::asyncEventTimerFired(Timer<MediaController>*)
{
    WillBeHeapVector<RefPtrWillBeMember<Event> > pendingEvents;
    m_pendingEvents.swap(pendingEvents);
    size_t count = pendingEvents.size();
    for (size_t index = 0; index < count; ++index)
        dispatchEvent(pendingEvents[index].release(), IGNORE_EXCEPTION);
}
void MediaController::clearPositionTimerFired(Timer<MediaController>*)
{
    m_position = MediaPlayer::invalidTime();
}
const AtomicString& MediaController::interfaceName() const
{
    return EventTargetNames::MediaController;
}
static const double maxTimeupdateEventFrequency = 0.25;
void MediaController::startTimeupdateTimer()
{
    if (m_timeupdateTimer.isActive())
        return;
    m_timeupdateTimer.startRepeating(maxTimeupdateEventFrequency, FROM_HERE);
}
void MediaController::timeupdateTimerFired(Timer<MediaController>*)
{
    scheduleTimeupdateEvent();
}
void MediaController::scheduleTimeupdateEvent()
{
    double now = WTF::currentTime();
    double timedelta = now - m_previousTimeupdateTime;
    if (timedelta < maxTimeupdateEventFrequency)
        return;
    scheduleEvent(EventTypeNames::timeupdate);
    m_previousTimeupdateTime = now;
}