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;
}