Index: Source/core/html/HTMLMediaElement.cpp |
diff --git a/Source/core/html/HTMLMediaElement.cpp b/Source/core/html/HTMLMediaElement.cpp |
index d950813509f57be4ced709d0b6c95b819abd7b1e..ba07c0e0234aa84ca8e8590202484f47ab69df1b 100644 |
--- a/Source/core/html/HTMLMediaElement.cpp |
+++ b/Source/core/html/HTMLMediaElement.cpp |
@@ -53,8 +53,8 @@ |
#include "core/html/track/AudioTrack.h" |
#include "core/html/track/AudioTrackList.h" |
#include "core/html/track/AutomaticTrackSelection.h" |
+#include "core/html/track/CueTimeline.h" |
#include "core/html/track/InbandTextTrack.h" |
-#include "core/html/track/TextTrackCueList.h" |
#include "core/html/track/TextTrackList.h" |
#include "core/html/track/VideoTrack.h" |
#include "core/html/track/VideoTrackList.h" |
@@ -75,7 +75,6 @@ |
#include "public/platform/WebInbandTextTrack.h" |
#include "wtf/CurrentTime.h" |
#include "wtf/MathExtras.h" |
-#include "wtf/NonCopyingSort.h" |
#include "wtf/text/CString.h" |
#include <limits> |
@@ -149,24 +148,6 @@ static void removeElementFromDocumentMap(HTMLMediaElement* element, Document* do |
map.add(document, set); |
} |
-class TrackDisplayUpdateScope { |
- STACK_ALLOCATED(); |
-public: |
- TrackDisplayUpdateScope(HTMLMediaElement* mediaElement) |
- { |
- m_mediaElement = mediaElement; |
- m_mediaElement->beginIgnoringTrackDisplayUpdateRequests(); |
- } |
- ~TrackDisplayUpdateScope() |
- { |
- ASSERT(m_mediaElement); |
- m_mediaElement->endIgnoringTrackDisplayUpdateRequests(); |
- } |
- |
-private: |
- RawPtrWillBeMember<HTMLMediaElement> m_mediaElement; |
-}; |
- |
class AudioSourceProviderClientLockScope { |
STACK_ALLOCATED(); |
public: |
@@ -375,13 +356,11 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& docum |
, m_isFinalizing(false) |
, m_closeMediaSourceWhenFinalizing(false) |
#endif |
- , m_lastTextTrackUpdateTime(-1) |
, m_initialPlayWithoutUserGestures(false) |
, m_autoplayMediaCounted(false) |
, m_audioTracks(AudioTrackList::create(*this)) |
, m_videoTracks(VideoTrackList::create(*this)) |
, m_textTracks(nullptr) |
- , m_ignoreTrackDisplayUpdate(0) |
#if ENABLE(WEB_AUDIO) |
, m_audioSourceNode(nullptr) |
#endif |
@@ -818,7 +797,7 @@ void HTMLMediaElement::prepareForLoad() |
updateMediaController(); |
- updateActiveTextTrackCues(0); |
+ cueTimeline().updateActiveCues(0); |
} |
// 5 - Set the playbackRate attribute to the value of the defaultPlaybackRate attribute. |
@@ -1140,262 +1119,6 @@ WebMediaPlayer::LoadType HTMLMediaElement::loadType() const |
return WebMediaPlayer::LoadTypeURL; |
} |
-static bool trackIndexCompare(TextTrack* a, |
- TextTrack* b) |
-{ |
- return a->trackIndex() - b->trackIndex() < 0; |
-} |
- |
-static bool eventTimeCueCompare(const std::pair<double, TextTrackCue*>& a, |
- const std::pair<double, TextTrackCue*>& b) |
-{ |
- // 12 - Sort the tasks in events in ascending time order (tasks with earlier |
- // times first). |
- if (a.first != b.first) |
- return a.first - b.first < 0; |
- |
- // If the cues belong to different text tracks, it doesn't make sense to |
- // compare the two tracks by the relative cue order, so return the relative |
- // track order. |
- if (a.second->track() != b.second->track()) |
- return trackIndexCompare(a.second->track(), b.second->track()); |
- |
- // 12 - Further sort tasks in events that have the same time by the |
- // relative text track cue order of the text track cues associated |
- // with these tasks. |
- return a.second->cueIndex() - b.second->cueIndex() < 0; |
-} |
- |
- |
-void HTMLMediaElement::updateActiveTextTrackCues(double movieTime) |
-{ |
- // 4.8.10.8 Playing the media resource |
- |
- // If the current playback position changes while the steps are running, |
- // then the user agent must wait for the steps to complete, and then must |
- // immediately rerun the steps. |
- if (ignoreTrackDisplayUpdateRequests()) |
- return; |
- |
- // 1 - Let current cues be a list of cues, initialized to contain all the |
- // cues of all the hidden, showing, or showing by default text tracks of the |
- // media element (not the disabled ones) whose start times are less than or |
- // equal to the current playback position and whose end times are greater |
- // than the current playback position. |
- CueList currentCues; |
- |
- // The user agent must synchronously unset [the text track cue active] flag |
- // whenever ... the media element's readyState is changed back to HAVE_NOTHING. |
- if (m_readyState != HAVE_NOTHING && m_player) |
- currentCues = m_cueTree.allOverlaps(m_cueTree.createInterval(movieTime, movieTime)); |
- |
- CueList previousCues; |
- CueList missedCues; |
- |
- // 2 - Let other cues be a list of cues, initialized to contain all the cues |
- // of hidden, showing, and showing by default text tracks of the media |
- // element that are not present in current cues. |
- previousCues = m_currentlyActiveCues; |
- |
- // 3 - Let last time be the current playback position at the time this |
- // algorithm was last run for this media element, if this is not the first |
- // time it has run. |
- double lastTime = m_lastTextTrackUpdateTime; |
- |
- // 4 - If the current playback position has, since the last time this |
- // algorithm was run, only changed through its usual monotonic increase |
- // during normal playback, then let missed cues be the list of cues in other |
- // cues whose start times are greater than or equal to last time and whose |
- // end times are less than or equal to the current playback position. |
- // Otherwise, let missed cues be an empty list. |
- if (lastTime >= 0 && m_lastSeekTime < movieTime) { |
- CueList potentiallySkippedCues = |
- m_cueTree.allOverlaps(m_cueTree.createInterval(lastTime, movieTime)); |
- |
- for (CueInterval cue : potentiallySkippedCues) { |
- // Consider cues that may have been missed since the last seek time. |
- if (cue.low() > std::max(m_lastSeekTime, lastTime) && cue.high() < movieTime) |
- missedCues.append(cue); |
- } |
- } |
- |
- m_lastTextTrackUpdateTime = movieTime; |
- |
- // 5 - If the time was reached through the usual monotonic increase of the |
- // current playback position during normal playback, and if the user agent |
- // has not fired a timeupdate event at the element in the past 15 to 250ms |
- // and is not still running event handlers for such an event, then the user |
- // agent must queue a task to fire a simple event named timeupdate at the |
- // element. (In the other cases, such as explicit seeks, relevant events get |
- // fired as part of the overall process of changing the current playback |
- // position.) |
- if (!m_seeking && m_lastSeekTime < lastTime) |
- scheduleTimeupdateEvent(true); |
- |
- // Explicitly cache vector sizes, as their content is constant from here. |
- size_t currentCuesSize = currentCues.size(); |
- size_t missedCuesSize = missedCues.size(); |
- size_t previousCuesSize = previousCues.size(); |
- |
- // 6 - If all of the cues in current cues have their text track cue active |
- // flag set, none of the cues in other cues have their text track cue active |
- // flag set, and missed cues is empty, then abort these steps. |
- bool activeSetChanged = missedCuesSize; |
- |
- for (size_t i = 0; !activeSetChanged && i < previousCuesSize; ++i) { |
- if (!currentCues.contains(previousCues[i]) && previousCues[i].data()->isActive()) |
- activeSetChanged = true; |
- } |
- |
- for (CueInterval currentCue : currentCues) { |
- currentCue.data()->updateDisplayTree(movieTime); |
- |
- if (!currentCue.data()->isActive()) |
- activeSetChanged = true; |
- } |
- |
- if (!activeSetChanged) |
- return; |
- |
- // 7 - If the time was reached through the usual monotonic increase of the |
- // current playback position during normal playback, and there are cues in |
- // other cues that have their text track cue pause-on-exi flag set and that |
- // either have their text track cue active flag set or are also in missed |
- // cues, then immediately pause the media element. |
- for (size_t i = 0; !m_paused && i < previousCuesSize; ++i) { |
- if (previousCues[i].data()->pauseOnExit() |
- && previousCues[i].data()->isActive() |
- && !currentCues.contains(previousCues[i])) |
- pause(); |
- } |
- |
- for (size_t i = 0; !m_paused && i < missedCuesSize; ++i) { |
- if (missedCues[i].data()->pauseOnExit()) |
- pause(); |
- } |
- |
- // 8 - Let events be a list of tasks, initially empty. Each task in this |
- // list will be associated with a text track, a text track cue, and a time, |
- // which are used to sort the list before the tasks are queued. |
- WillBeHeapVector<std::pair<double, RawPtrWillBeMember<TextTrackCue>>> eventTasks; |
- |
- // 8 - Let affected tracks be a list of text tracks, initially empty. |
- WillBeHeapVector<RawPtrWillBeMember<TextTrack>> affectedTracks; |
- |
- for (size_t i = 0; i < missedCuesSize; ++i) { |
- // 9 - For each text track cue in missed cues, prepare an event named enter |
- // for the TextTrackCue object with the text track cue start time. |
- eventTasks.append(std::make_pair(missedCues[i].data()->startTime(), |
- missedCues[i].data())); |
- |
- // 10 - For each text track [...] in missed cues, prepare an event |
- // named exit for the TextTrackCue object with the with the later of |
- // the text track cue end time and the text track cue start time. |
- |
- // Note: An explicit task is added only if the cue is NOT a zero or |
- // negative length cue. Otherwise, the need for an exit event is |
- // checked when these tasks are actually queued below. This doesn't |
- // affect sorting events before dispatch either, because the exit |
- // event has the same time as the enter event. |
- if (missedCues[i].data()->startTime() < missedCues[i].data()->endTime()) |
- eventTasks.append(std::make_pair(missedCues[i].data()->endTime(), |
- missedCues[i].data())); |
- } |
- |
- for (size_t i = 0; i < previousCuesSize; ++i) { |
- // 10 - For each text track cue in other cues that has its text |
- // track cue active flag set prepare an event named exit for the |
- // TextTrackCue object with the text track cue end time. |
- if (!currentCues.contains(previousCues[i])) |
- eventTasks.append(std::make_pair(previousCues[i].data()->endTime(), |
- previousCues[i].data())); |
- } |
- |
- for (size_t i = 0; i < currentCuesSize; ++i) { |
- // 11 - For each text track cue in current cues that does not have its |
- // text track cue active flag set, prepare an event named enter for the |
- // TextTrackCue object with the text track cue start time. |
- if (!previousCues.contains(currentCues[i])) |
- eventTasks.append(std::make_pair(currentCues[i].data()->startTime(), |
- currentCues[i].data())); |
- } |
- |
- // 12 - Sort the tasks in events in ascending time order (tasks with earlier |
- // times first). |
- nonCopyingSort(eventTasks.begin(), eventTasks.end(), eventTimeCueCompare); |
- |
- for (size_t i = 0; i < eventTasks.size(); ++i) { |
- if (!affectedTracks.contains(eventTasks[i].second->track())) |
- affectedTracks.append(eventTasks[i].second->track()); |
- |
- // 13 - Queue each task in events, in list order. |
- RefPtrWillBeRawPtr<Event> event = nullptr; |
- |
- // Each event in eventTasks may be either an enterEvent or an exitEvent, |
- // depending on the time that is associated with the event. This |
- // correctly identifies the type of the event, if the startTime is |
- // less than the endTime in the cue. |
- if (eventTasks[i].second->startTime() >= eventTasks[i].second->endTime()) { |
- event = Event::create(EventTypeNames::enter); |
- event->setTarget(eventTasks[i].second); |
- m_asyncEventQueue->enqueueEvent(event.release()); |
- |
- event = Event::create(EventTypeNames::exit); |
- event->setTarget(eventTasks[i].second); |
- m_asyncEventQueue->enqueueEvent(event.release()); |
- } else { |
- if (eventTasks[i].first == eventTasks[i].second->startTime()) |
- event = Event::create(EventTypeNames::enter); |
- else |
- event = Event::create(EventTypeNames::exit); |
- |
- event->setTarget(eventTasks[i].second); |
- m_asyncEventQueue->enqueueEvent(event.release()); |
- } |
- } |
- |
- // 14 - Sort affected tracks in the same order as the text tracks appear in |
- // the media element's list of text tracks, and remove duplicates. |
- nonCopyingSort(affectedTracks.begin(), affectedTracks.end(), trackIndexCompare); |
- |
- // 15 - For each text track in affected tracks, in the list order, queue a |
- // task to fire a simple event named cuechange at the TextTrack object, and, ... |
- for (size_t i = 0; i < affectedTracks.size(); ++i) { |
- RefPtrWillBeRawPtr<Event> event = Event::create(EventTypeNames::cuechange); |
- event->setTarget(affectedTracks[i]); |
- |
- m_asyncEventQueue->enqueueEvent(event.release()); |
- |
- // ... if the text track has a corresponding track element, to then fire a |
- // simple event named cuechange at the track element as well. |
- if (affectedTracks[i]->trackType() == TextTrack::TrackElement) { |
- RefPtrWillBeRawPtr<Event> event = Event::create(EventTypeNames::cuechange); |
- HTMLTrackElement* trackElement = static_cast<LoadableTextTrack*>(affectedTracks[i].get())->trackElement(); |
- ASSERT(trackElement); |
- event->setTarget(trackElement); |
- |
- m_asyncEventQueue->enqueueEvent(event.release()); |
- } |
- } |
- |
- // 16 - Set the text track cue active flag of all the cues in the current |
- // cues, and unset the text track cue active flag of all the cues in the |
- // other cues. |
- for (size_t i = 0; i < currentCuesSize; ++i) |
- currentCues[i].data()->setIsActive(true); |
- |
- for (size_t i = 0; i < previousCuesSize; ++i) { |
- if (!currentCues.contains(previousCues[i])) |
- previousCues[i].data()->setIsActive(false); |
- } |
- |
- // Update the current active cues. |
- m_currentlyActiveCues = currentCues; |
- |
- if (activeSetChanged) |
- updateTextTrackDisplay(); |
-} |
- |
bool HTMLMediaElement::textTracksAreReady() const |
{ |
// 4.8.10.12.1 Text track model |
@@ -1443,7 +1166,7 @@ void HTMLMediaElement::textTrackModeChanged(TextTrack* track) |
track->setHasBeenConfigured(true); |
if (track->mode() != TextTrack::disabledKeyword()) { |
if (trackElement->readyState() == HTMLTrackElement::LOADED) |
- textTrackAddCues(track, track->cues()); |
+ cueTimeline().addCues(track, track->cues()); |
// If this is the first added track, create the list of text tracks. |
if (!m_textTracks) |
@@ -1452,7 +1175,7 @@ void HTMLMediaElement::textTrackModeChanged(TextTrack* track) |
break; |
} |
} else if (track->trackType() == TextTrack::AddTrack && track->mode() != TextTrack::disabledKeyword()) { |
- textTrackAddCues(track, track->cues()); |
+ cueTimeline().addCues(track, track->cues()); |
} |
configureTextTrackDisplay(AssumeVisibleChange); |
@@ -1467,78 +1190,6 @@ void HTMLMediaElement::textTrackKindChanged(TextTrack* track) |
track->setMode(TextTrack::hiddenKeyword()); |
} |
-void HTMLMediaElement::beginIgnoringTrackDisplayUpdateRequests() |
-{ |
- ++m_ignoreTrackDisplayUpdate; |
-} |
- |
-void HTMLMediaElement::endIgnoringTrackDisplayUpdateRequests() |
-{ |
- ASSERT(m_ignoreTrackDisplayUpdate); |
- --m_ignoreTrackDisplayUpdate; |
- if (!m_ignoreTrackDisplayUpdate && inActiveDocument()) |
- updateActiveTextTrackCues(currentTime()); |
-} |
- |
-void HTMLMediaElement::textTrackAddCues(TextTrack* track, const TextTrackCueList* cues) |
-{ |
- WTF_LOG(Media, "HTMLMediaElement::textTrackAddCues(%p)", this); |
- ASSERT(track->mode() != TextTrack::disabledKeyword()); |
- |
- TrackDisplayUpdateScope scope(this); |
- for (size_t i = 0; i < cues->length(); ++i) |
- textTrackAddCue(cues->item(i)->track(), cues->item(i)); |
-} |
- |
-void HTMLMediaElement::textTrackRemoveCues(TextTrack*, const TextTrackCueList* cues) |
-{ |
- WTF_LOG(Media, "HTMLMediaElement::textTrackRemoveCues(%p)", this); |
- |
- TrackDisplayUpdateScope scope(this); |
- for (size_t i = 0; i < cues->length(); ++i) |
- textTrackRemoveCue(cues->item(i)->track(), cues->item(i)); |
-} |
- |
-void HTMLMediaElement::textTrackAddCue(TextTrack* track, PassRefPtrWillBeRawPtr<TextTrackCue> cue) |
-{ |
- ASSERT(track->mode() != TextTrack::disabledKeyword()); |
- |
- // Negative duration cues need be treated in the interval tree as |
- // zero-length cues. |
- double endTime = std::max(cue->startTime(), cue->endTime()); |
- |
- CueInterval interval = m_cueTree.createInterval(cue->startTime(), endTime, cue.get()); |
- if (!m_cueTree.contains(interval)) |
- m_cueTree.add(interval); |
- updateActiveTextTrackCues(currentTime()); |
-} |
- |
-void HTMLMediaElement::textTrackRemoveCue(TextTrack*, PassRefPtrWillBeRawPtr<TextTrackCue> cue) |
-{ |
- // Negative duration cues need to be treated in the interval tree as |
- // zero-length cues. |
- double endTime = std::max(cue->startTime(), cue->endTime()); |
- |
- CueInterval interval = m_cueTree.createInterval(cue->startTime(), endTime, cue.get()); |
- m_cueTree.remove(interval); |
- |
- // Since the cue will be removed from the media element and likely the |
- // TextTrack might also be destructed, notifying the region of the cue |
- // removal shouldn't be done. |
- cue->notifyRegionWhenRemovingDisplayTree(false); |
- |
- size_t index = m_currentlyActiveCues.find(interval); |
- if (index != kNotFound) { |
- m_currentlyActiveCues.remove(index); |
- cue->setIsActive(false); |
- } |
- cue->removeDisplayTree(); |
- updateActiveTextTrackCues(currentTime()); |
- |
- cue->notifyRegionWhenRemovingDisplayTree(true); |
-} |
- |
- |
bool HTMLMediaElement::isSafeToLoadURL(const KURL& url, InvalidURLAction actionIfInvalid) |
{ |
if (!url.isValid()) { |
@@ -1929,7 +1580,7 @@ void HTMLMediaElement::setReadyState(ReadyState state) |
updatePlayState(); |
updateMediaController(); |
- updateActiveTextTrackCues(currentTime()); |
+ cueTimeline().updateActiveCues(currentTime()); |
} |
void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*) |
@@ -2518,7 +2169,7 @@ void HTMLMediaElement::playbackProgressTimerFired(Timer<HTMLMediaElement>*) |
if (!m_paused && hasMediaControls()) |
mediaControls()->playbackProgressed(); |
- updateActiveTextTrackCues(currentTime()); |
+ cueTimeline().updateActiveCues(currentTime()); |
} |
void HTMLMediaElement::scheduleTimeupdateEvent(bool periodicEvent) |
@@ -2728,7 +2379,7 @@ void HTMLMediaElement::addTextTrack(TextTrack* track) |
void HTMLMediaElement::removeTextTrack(TextTrack* track) |
{ |
- TrackDisplayUpdateScope scope(this); |
+ TrackDisplayUpdateScope scope(this->cueTimeline()); |
m_textTracks->remove(track); |
textTracksChanged(); |
@@ -2740,7 +2391,7 @@ void HTMLMediaElement::forgetResourceSpecificTracks() |
// The order is explicitly specified as text, then audio, and finally video. Also |
// 'removetrack' events should not be fired. |
if (m_textTracks) { |
- TrackDisplayUpdateScope scope(this); |
+ TrackDisplayUpdateScope scope(this->cueTimeline()); |
m_textTracks->removeAllInbandTracks(); |
textTracksChanged(); |
} |
@@ -3045,7 +2696,7 @@ void HTMLMediaElement::mediaPlayerTimeChanged() |
{ |
WTF_LOG(Media, "HTMLMediaElement::mediaPlayerTimeChanged(%p)", this); |
- updateActiveTextTrackCues(currentTime()); |
+ cueTimeline().updateActiveCues(currentTime()); |
invalidateCachedTime(); |
@@ -3389,7 +3040,7 @@ void HTMLMediaElement::userCancelledLoad() |
m_readyState = HAVE_NOTHING; |
invalidateCachedTime(); |
updateMediaController(); |
- updateActiveTextTrackCues(0); |
+ cueTimeline().updateActiveCues(0); |
} |
void HTMLMediaElement::clearMediaPlayerAndAudioSourceProviderClientWithoutLocking() |
@@ -3688,6 +3339,13 @@ void HTMLMediaElement::configureMediaControls() |
mediaControls()->hide(); |
} |
+CueTimeline& HTMLMediaElement::cueTimeline() |
+{ |
+ if (!m_cueTimeline) |
+ m_cueTimeline = adoptPtrWillBeNoop(new CueTimeline(*this)); |
+ return *m_cueTimeline; |
+} |
+ |
void HTMLMediaElement::configureTextTrackDisplay(VisibilityChangeAssumption assumption) |
{ |
ASSERT(m_textTracks); |
@@ -3706,7 +3364,7 @@ void HTMLMediaElement::configureTextTrackDisplay(VisibilityChangeAssumption assu |
if (assumption == AssumeNoVisibleChange |
&& m_haveVisibleTextTrack == haveVisibleTextTrack) { |
- updateActiveTextTrackCues(currentTime()); |
+ cueTimeline().updateActiveCues(currentTime()); |
return; |
} |
m_haveVisibleTextTrack = haveVisibleTextTrack; |
@@ -3718,7 +3376,7 @@ void HTMLMediaElement::configureTextTrackDisplay(VisibilityChangeAssumption assu |
ensureMediaControls(); |
mediaControls()->changedClosedCaptionsVisibility(); |
- updateActiveTextTrackCues(currentTime()); |
+ cueTimeline().updateActiveCues(currentTime()); |
updateTextTrackDisplay(); |
} |
@@ -3928,6 +3586,7 @@ void HTMLMediaElement::trace(Visitor* visitor) |
visitor->trace(m_mediaSource); |
visitor->trace(m_audioTracks); |
visitor->trace(m_videoTracks); |
+ visitor->trace(m_cueTimeline); |
visitor->trace(m_textTracks); |
visitor->trace(m_textTracksWhenResourceSelectionBegan); |
visitor->trace(m_mediaController); |