Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1983)

Side by Side Diff: Source/core/html/track/CueTimeline.cpp

Issue 913133003: Move text track active list management to CueTimeline (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Rename to CueTimeline. Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « Source/core/html/track/CueTimeline.h ('k') | Source/core/html/track/LoadableTextTrack.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "config.h"
6 #include "core/html/track/CueTimeline.h"
7
8 #include "core/events/Event.h"
9 #include "core/html/HTMLMediaElement.h"
10 #include "core/html/HTMLTrackElement.h"
11 #include "core/html/track/LoadableTextTrack.h"
12 #include "core/html/track/TextTrack.h"
13 #include "core/html/track/TextTrackCue.h"
14 #include "core/html/track/TextTrackCueList.h"
15 #include "wtf/NonCopyingSort.h"
16
17 namespace blink {
18
19 CueTimeline::CueTimeline(HTMLMediaElement& mediaElement)
20 : m_mediaElement(&mediaElement)
21 , m_lastUpdateTime(-1)
22 , m_ignoreUpdate(0)
23 {
24 }
25
26 void CueTimeline::addCues(TextTrack* track, const TextTrackCueList* cues)
27 {
28 ASSERT(track->mode() != TextTrack::disabledKeyword());
29
30 TrackDisplayUpdateScope scope(*this);
31 for (size_t i = 0; i < cues->length(); ++i)
32 addCue(cues->item(i)->track(), cues->item(i));
33 }
34
35 void CueTimeline::addCue(TextTrack* track, PassRefPtrWillBeRawPtr<TextTrackCue> cue)
36 {
37 ASSERT(track->mode() != TextTrack::disabledKeyword());
38
39 // Negative duration cues need be treated in the interval tree as
40 // zero-length cues.
41 double endTime = std::max(cue->startTime(), cue->endTime());
42
43 CueInterval interval = m_cueTree.createInterval(cue->startTime(), endTime, c ue.get());
44 if (!m_cueTree.contains(interval))
45 m_cueTree.add(interval);
46 updateActiveCues(mediaElement().currentTime());
47 }
48
49 void CueTimeline::removeCues(TextTrack*, const TextTrackCueList* cues)
50 {
51 TrackDisplayUpdateScope scope(*this);
52 for (size_t i = 0; i < cues->length(); ++i)
53 removeCue(cues->item(i)->track(), cues->item(i));
54 }
55
56 void CueTimeline::removeCue(TextTrack*, PassRefPtrWillBeRawPtr<TextTrackCue> cue )
57 {
58 // Negative duration cues need to be treated in the interval tree as
59 // zero-length cues.
60 double endTime = std::max(cue->startTime(), cue->endTime());
61
62 CueInterval interval = m_cueTree.createInterval(cue->startTime(), endTime, c ue.get());
63 m_cueTree.remove(interval);
64
65 // Since the cue will be removed from the media element and likely the
66 // TextTrack might also be destructed, notifying the region of the cue
67 // removal shouldn't be done.
68 cue->notifyRegionWhenRemovingDisplayTree(false);
69
70 size_t index = m_currentlyActiveCues.find(interval);
71 if (index != kNotFound) {
72 m_currentlyActiveCues.remove(index);
73 cue->setIsActive(false);
74 }
75 cue->removeDisplayTree();
76 updateActiveCues(mediaElement().currentTime());
77
78 cue->notifyRegionWhenRemovingDisplayTree(true);
79 }
80
81 static bool trackIndexCompare(TextTrack* a, TextTrack* b)
82 {
83 return a->trackIndex() - b->trackIndex() < 0;
84 }
85
86 static bool eventTimeCueCompare(const std::pair<double, TextTrackCue*>& a, const std::pair<double, TextTrackCue*>& b)
87 {
88 // 12 - Sort the tasks in events in ascending time order (tasks with earlier
89 // times first).
90 if (a.first != b.first)
91 return a.first - b.first < 0;
92
93 // If the cues belong to different text tracks, it doesn't make sense to
94 // compare the two tracks by the relative cue order, so return the relative
95 // track order.
96 if (a.second->track() != b.second->track())
97 return trackIndexCompare(a.second->track(), b.second->track());
98
99 // 12 - Further sort tasks in events that have the same time by the
100 // relative text track cue order of the text track cues associated
101 // with these tasks.
102 return a.second->cueIndex() - b.second->cueIndex() < 0;
103 }
104
105 void CueTimeline::updateActiveCues(double movieTime)
106 {
107 // 4.8.10.8 Playing the media resource
108
109 // If the current playback position changes while the steps are running,
110 // then the user agent must wait for the steps to complete, and then must
111 // immediately rerun the steps.
112 if (ignoreUpdateRequests())
113 return;
114
115 HTMLMediaElement& mediaElement = this->mediaElement();
116
117 // 1 - Let current cues be a list of cues, initialized to contain all the
118 // cues of all the hidden, showing, or showing by default text tracks of the
119 // media element (not the disabled ones) whose start times are less than or
120 // equal to the current playback position and whose end times are greater
121 // than the current playback position.
122 CueList currentCues;
123
124 // The user agent must synchronously unset [the text track cue active] flag
125 // whenever ... the media element's readyState is changed back to HAVE_NOTHI NG.
126 if (mediaElement.readyState() != HTMLMediaElement::HAVE_NOTHING && mediaElem ent.webMediaPlayer())
127 currentCues = m_cueTree.allOverlaps(m_cueTree.createInterval(movieTime, movieTime));
128
129 CueList previousCues;
130 CueList missedCues;
131
132 // 2 - Let other cues be a list of cues, initialized to contain all the cues
133 // of hidden, showing, and showing by default text tracks of the media
134 // element that are not present in current cues.
135 previousCues = m_currentlyActiveCues;
136
137 // 3 - Let last time be the current playback position at the time this
138 // algorithm was last run for this media element, if this is not the first
139 // time it has run.
140 double lastTime = m_lastUpdateTime;
141 double lastSeekTime = mediaElement.lastSeekTime();
142
143 // 4 - If the current playback position has, since the last time this
144 // algorithm was run, only changed through its usual monotonic increase
145 // during normal playback, then let missed cues be the list of cues in other
146 // cues whose start times are greater than or equal to last time and whose
147 // end times are less than or equal to the current playback position.
148 // Otherwise, let missed cues be an empty list.
149 if (lastTime >= 0 && lastSeekTime < movieTime) {
150 CueList potentiallySkippedCues =
151 m_cueTree.allOverlaps(m_cueTree.createInterval(lastTime, movieTime)) ;
152
153 for (CueInterval cue : potentiallySkippedCues) {
154 // Consider cues that may have been missed since the last seek time.
155 if (cue.low() > std::max(lastSeekTime, lastTime) && cue.high() < mov ieTime)
156 missedCues.append(cue);
157 }
158 }
159
160 m_lastUpdateTime = movieTime;
161
162 // 5 - If the time was reached through the usual monotonic increase of the
163 // current playback position during normal playback, and if the user agent
164 // has not fired a timeupdate event at the element in the past 15 to 250ms
165 // and is not still running event handlers for such an event, then the user
166 // agent must queue a task to fire a simple event named timeupdate at the
167 // element. (In the other cases, such as explicit seeks, relevant events get
168 // fired as part of the overall process of changing the current playback
169 // position.)
170 if (!mediaElement.seeking() && lastSeekTime < lastTime)
171 mediaElement.scheduleTimeupdateEvent(true);
172
173 // Explicitly cache vector sizes, as their content is constant from here.
174 size_t currentCuesSize = currentCues.size();
175 size_t missedCuesSize = missedCues.size();
176 size_t previousCuesSize = previousCues.size();
177
178 // 6 - If all of the cues in current cues have their text track cue active
179 // flag set, none of the cues in other cues have their text track cue active
180 // flag set, and missed cues is empty, then abort these steps.
181 bool activeSetChanged = missedCuesSize;
182
183 for (size_t i = 0; !activeSetChanged && i < previousCuesSize; ++i) {
184 if (!currentCues.contains(previousCues[i]) && previousCues[i].data()->is Active())
185 activeSetChanged = true;
186 }
187
188 for (CueInterval currentCue : currentCues) {
189 currentCue.data()->updateDisplayTree(movieTime);
190
191 if (!currentCue.data()->isActive())
192 activeSetChanged = true;
193 }
194
195 if (!activeSetChanged)
196 return;
197
198 // 7 - If the time was reached through the usual monotonic increase of the
199 // current playback position during normal playback, and there are cues in
200 // other cues that have their text track cue pause-on-exi flag set and that
201 // either have their text track cue active flag set or are also in missed
202 // cues, then immediately pause the media element.
203 for (size_t i = 0; !mediaElement.paused() && i < previousCuesSize; ++i) {
204 if (previousCues[i].data()->pauseOnExit()
205 && previousCues[i].data()->isActive()
206 && !currentCues.contains(previousCues[i]))
207 mediaElement.pause();
208 }
209
210 for (size_t i = 0; !mediaElement.paused() && i < missedCuesSize; ++i) {
211 if (missedCues[i].data()->pauseOnExit())
212 mediaElement.pause();
213 }
214
215 // 8 - Let events be a list of tasks, initially empty. Each task in this
216 // list will be associated with a text track, a text track cue, and a time,
217 // which are used to sort the list before the tasks are queued.
218 WillBeHeapVector<std::pair<double, RawPtrWillBeMember<TextTrackCue>>> eventT asks;
219
220 // 8 - Let affected tracks be a list of text tracks, initially empty.
221 WillBeHeapVector<RawPtrWillBeMember<TextTrack>> affectedTracks;
222
223 for (size_t i = 0; i < missedCuesSize; ++i) {
224 // 9 - For each text track cue in missed cues, prepare an event named en ter
225 // for the TextTrackCue object with the text track cue start time.
226 eventTasks.append(std::make_pair(missedCues[i].data()->startTime(), miss edCues[i].data()));
227
228 // 10 - For each text track [...] in missed cues, prepare an event
229 // named exit for the TextTrackCue object with the with the later of
230 // the text track cue end time and the text track cue start time.
231
232 // Note: An explicit task is added only if the cue is NOT a zero or
233 // negative length cue. Otherwise, the need for an exit event is
234 // checked when these tasks are actually queued below. This doesn't
235 // affect sorting events before dispatch either, because the exit
236 // event has the same time as the enter event.
237 if (missedCues[i].data()->startTime() < missedCues[i].data()->endTime())
238 eventTasks.append(std::make_pair(missedCues[i].data()->endTime(), mi ssedCues[i].data()));
239 }
240
241 for (size_t i = 0; i < previousCuesSize; ++i) {
242 // 10 - For each text track cue in other cues that has its text
243 // track cue active flag set prepare an event named exit for the
244 // TextTrackCue object with the text track cue end time.
245 if (!currentCues.contains(previousCues[i]))
246 eventTasks.append(std::make_pair(previousCues[i].data()->endTime(), previousCues[i].data()));
247 }
248
249 for (size_t i = 0; i < currentCuesSize; ++i) {
250 // 11 - For each text track cue in current cues that does not have its
251 // text track cue active flag set, prepare an event named enter for the
252 // TextTrackCue object with the text track cue start time.
253 if (!previousCues.contains(currentCues[i]))
254 eventTasks.append(std::make_pair(currentCues[i].data()->startTime(), currentCues[i].data()));
255 }
256
257 // 12 - Sort the tasks in events in ascending time order (tasks with earlier
258 // times first).
259 nonCopyingSort(eventTasks.begin(), eventTasks.end(), eventTimeCueCompare);
260
261 for (size_t i = 0; i < eventTasks.size(); ++i) {
262 if (!affectedTracks.contains(eventTasks[i].second->track()))
263 affectedTracks.append(eventTasks[i].second->track());
264
265 // 13 - Queue each task in events, in list order.
266 RefPtrWillBeRawPtr<Event> event = nullptr;
267
268 // Each event in eventTasks may be either an enterEvent or an exitEvent,
269 // depending on the time that is associated with the event. This
270 // correctly identifies the type of the event, if the startTime is
271 // less than the endTime in the cue.
272 if (eventTasks[i].second->startTime() >= eventTasks[i].second->endTime() ) {
273 event = Event::create(EventTypeNames::enter);
274 event->setTarget(eventTasks[i].second);
275 mediaElement.scheduleEvent(event.release());
276
277 event = Event::create(EventTypeNames::exit);
278 event->setTarget(eventTasks[i].second);
279 mediaElement.scheduleEvent(event.release());
280 } else {
281 if (eventTasks[i].first == eventTasks[i].second->startTime())
282 event = Event::create(EventTypeNames::enter);
283 else
284 event = Event::create(EventTypeNames::exit);
285
286 event->setTarget(eventTasks[i].second);
287 mediaElement.scheduleEvent(event.release());
288 }
289 }
290
291 // 14 - Sort affected tracks in the same order as the text tracks appear in
292 // the media element's list of text tracks, and remove duplicates.
293 nonCopyingSort(affectedTracks.begin(), affectedTracks.end(), trackIndexCompa re);
294
295 // 15 - For each text track in affected tracks, in the list order, queue a
296 // task to fire a simple event named cuechange at the TextTrack object, and, ...
297 for (size_t i = 0; i < affectedTracks.size(); ++i) {
298 RefPtrWillBeRawPtr<Event> event = Event::create(EventTypeNames::cuechang e);
299 event->setTarget(affectedTracks[i]);
300
301 mediaElement.scheduleEvent(event.release());
302
303 // ... if the text track has a corresponding track element, to then fire a
304 // simple event named cuechange at the track element as well.
305 if (affectedTracks[i]->trackType() == TextTrack::TrackElement) {
306 RefPtrWillBeRawPtr<Event> event = Event::create(EventTypeNames::cuec hange);
brucedawson 2015/02/18 21:06:40 The declaration of 'event' shadows the declaration
307 HTMLTrackElement* trackElement = static_cast<LoadableTextTrack*>(aff ectedTracks[i].get())->trackElement();
308 ASSERT(trackElement);
309 event->setTarget(trackElement);
310
311 mediaElement.scheduleEvent(event.release());
312 }
313 }
314
315 // 16 - Set the text track cue active flag of all the cues in the current
316 // cues, and unset the text track cue active flag of all the cues in the
317 // other cues.
318 for (size_t i = 0; i < currentCuesSize; ++i)
319 currentCues[i].data()->setIsActive(true);
320
321 for (size_t i = 0; i < previousCuesSize; ++i) {
322 if (!currentCues.contains(previousCues[i]))
323 previousCues[i].data()->setIsActive(false);
324 }
325
326 // Update the current active cues.
327 m_currentlyActiveCues = currentCues;
328
329 if (activeSetChanged)
330 mediaElement.updateTextTrackDisplay();
331 }
332
333 void CueTimeline::beginIgnoringUpdateRequests()
334 {
335 ++m_ignoreUpdate;
336 }
337
338 void CueTimeline::endIgnoringUpdateRequests()
339 {
340 ASSERT(m_ignoreUpdate);
341 --m_ignoreUpdate;
342 if (!m_ignoreUpdate && mediaElement().inActiveDocument())
343 updateActiveCues(mediaElement().currentTime());
344 }
345
346 DEFINE_TRACE(CueTimeline)
347 {
348 visitor->trace(m_mediaElement);
349 }
350
351 } // namespace blink
OLDNEW
« no previous file with comments | « Source/core/html/track/CueTimeline.h ('k') | Source/core/html/track/LoadableTextTrack.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698