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

Side by Side Diff: sky/engine/core/html/MediaController.cpp

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

Powered by Google App Engine
This is Rietveld 408576698