blob: 0fa7c24473e25dc053b411fa38d4438d0a7b89a8 [file] [log] [blame]
jer.noble@apple.combe39c542011-11-14 17:55:16 +00001/*
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
28#if ENABLE(VIDEO)
29#include "MediaController.h"
30
31#include "Clock.h"
32#include "ExceptionCode.h"
33#include "HTMLMediaElement.h"
34#include "TimeRanges.h"
jamesr@google.comc38e6fb2012-08-11 00:32:19 +000035#include <wtf/CurrentTime.h>
jer.noble@apple.combe39c542011-11-14 17:55:16 +000036#include <wtf/StdLibExtras.h>
37#include <wtf/text/AtomicString.h>
38
39using namespace WebCore;
jer.noble@apple.combe39c542011-11-14 17:55:16 +000040
weinig@apple.coma9481f02013-10-31 17:26:35 +000041PassRefPtr<MediaController> MediaController::create(ScriptExecutionContext& context)
jer.noble@apple.combe39c542011-11-14 17:55:16 +000042{
43 return adoptRef(new MediaController(context));
44}
45
weinig@apple.coma9481f02013-10-31 17:26:35 +000046MediaController::MediaController(ScriptExecutionContext& context)
jer.noble@apple.combe39c542011-11-14 17:55:16 +000047 : m_paused(false)
48 , m_defaultPlaybackRate(1)
49 , m_volume(1)
jer.noble@apple.comee9d8ae2012-07-23 21:39:19 +000050 , m_position(MediaPlayer::invalidTime())
jer.noble@apple.combe39c542011-11-14 17:55:16 +000051 , m_muted(false)
52 , m_readyState(HAVE_NOTHING)
53 , m_playbackState(WAITING)
54 , m_asyncEventTimer(this, &MediaController::asyncEventTimerFired)
jer.noble@apple.comee9d8ae2012-07-23 21:39:19 +000055 , m_clearPositionTimer(this, &MediaController::clearPositionTimerFired)
jer.noble@apple.combe39c542011-11-14 17:55:16 +000056 , m_closedCaptionsVisible(false)
57 , m_clock(Clock::create())
58 , m_scriptExecutionContext(context)
jer.noble@apple.com29d33362012-08-10 23:50:46 +000059 , m_timeupdateTimer(this, &MediaController::timeupdateTimerFired)
60 , m_previousTimeupdateTime(0)
jer.noble@apple.combe39c542011-11-14 17:55:16 +000061{
62}
63
64MediaController::~MediaController()
65{
66}
67
68void MediaController::addMediaElement(HTMLMediaElement* element)
69{
70 ASSERT(element);
71 ASSERT(!m_mediaElements.contains(element));
72
73 m_mediaElements.append(element);
74 bringElementUpToSpeed(element);
75}
76
77void MediaController::removeMediaElement(HTMLMediaElement* element)
78{
79 ASSERT(element);
80 ASSERT(m_mediaElements.contains(element));
81 m_mediaElements.remove(m_mediaElements.find(element));
82}
83
84bool MediaController::containsMediaElement(HTMLMediaElement* element) const
85{
86 return m_mediaElements.contains(element);
87}
88
89PassRefPtr<TimeRanges> MediaController::buffered() const
90{
91 if (m_mediaElements.isEmpty())
92 return TimeRanges::create();
93
94 // The buffered attribute must return a new static normalized TimeRanges object that represents
95 // the intersection of the ranges of the media resources of the slaved media elements that the
96 // user agent has buffered, at the time the attribute is evaluated.
97 RefPtr<TimeRanges> bufferedRanges = m_mediaElements.first()->buffered();
98 for (size_t index = 1; index < m_mediaElements.size(); ++index)
99 bufferedRanges->intersectWith(m_mediaElements[index]->buffered().get());
100 return bufferedRanges;
101}
102
103PassRefPtr<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 object 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 RefPtr<TimeRanges> seekableRanges = m_mediaElements.first()->seekable();
112 for (size_t index = 1; index < m_mediaElements.size(); ++index)
113 seekableRanges->intersectWith(m_mediaElements[index]->seekable().get());
114 return seekableRanges;
115}
116
117PassRefPtr<TimeRanges> MediaController::played()
118{
119 if (m_mediaElements.isEmpty())
120 return TimeRanges::create();
121
122 // The played attribute must return a new static normalized TimeRanges object that represents
123 // the union of the ranges of the media resources of the slaved media elements that the
124 // user agent has so far rendered, at the time the attribute is evaluated.
125 RefPtr<TimeRanges> playedRanges = m_mediaElements.first()->played();
126 for (size_t index = 1; index < m_mediaElements.size(); ++index)
127 playedRanges->unionWith(m_mediaElements[index]->played().get());
128 return playedRanges;
129}
130
eric.carlson@apple.comffd4ecb2013-04-10 16:25:14 +0000131double MediaController::duration() const
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000132{
133 // FIXME: Investigate caching the maximum duration and only updating the cached value
134 // when the slaved media elements' durations change.
eric.carlson@apple.comffd4ecb2013-04-10 16:25:14 +0000135 double maxDuration = 0;
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000136 for (size_t index = 0; index < m_mediaElements.size(); ++index) {
eric.carlson@apple.comffd4ecb2013-04-10 16:25:14 +0000137 double duration = m_mediaElements[index]->duration();
zandobersek@gmail.com9182d472013-02-13 23:01:21 +0000138 if (std::isnan(duration))
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000139 continue;
andersca@apple.comff9adb82013-10-25 01:15:36 +0000140 maxDuration = std::max(maxDuration, duration);
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000141 }
142 return maxDuration;
143}
144
eric.carlson@apple.comffd4ecb2013-04-10 16:25:14 +0000145double MediaController::currentTime() const
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000146{
147 if (m_mediaElements.isEmpty())
148 return 0;
jer.noble@apple.com5022a392012-06-07 18:18:37 +0000149
jer.noble@apple.comee9d8ae2012-07-23 21:39:19 +0000150 if (m_position == MediaPlayer::invalidTime()) {
151 // Some clocks may return times outside the range of [0..duration].
andersca@apple.comff9adb82013-10-25 01:15:36 +0000152 m_position = std::max<double>(0, std::min(duration(), m_clock->currentTime()));
jer.noble@apple.comee9d8ae2012-07-23 21:39:19 +0000153 m_clearPositionTimer.startOneShot(0);
154 }
155
156 return m_position;
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000157}
158
eric.carlson@apple.comffd4ecb2013-04-10 16:25:14 +0000159void MediaController::setCurrentTime(double time, ExceptionCode& code)
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000160{
161 // When the user agent is to seek the media controller to a particular new playback position,
162 // it must follow these steps:
163 // If the new playback position is less than zero, then set it to zero.
andersca@apple.comff9adb82013-10-25 01:15:36 +0000164 time = std::max(0.0, time);
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000165
166 // If the new playback position is greater than the media controller duration, then set it
167 // to the media controller duration.
andersca@apple.comff9adb82013-10-25 01:15:36 +0000168 time = std::min(time, duration());
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000169
170 // Set the media controller position to the new playback position.
171 m_clock->setCurrentTime(time);
172
173 // Seek each slaved media element to the new playback position relative to the media element timeline.
174 for (size_t index = 0; index < m_mediaElements.size(); ++index)
175 m_mediaElements[index]->seek(time, code);
jer.noble@apple.com29d33362012-08-10 23:50:46 +0000176
177 scheduleTimeupdateEvent();
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000178}
179
jer.noble@apple.com53ef8c92012-12-01 00:13:34 +0000180void MediaController::unpause()
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000181{
jer.noble@apple.com53ef8c92012-12-01 00:13:34 +0000182 // When the unpause() method is invoked, if the MediaController is a paused media controller,
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000183 if (!m_paused)
184 return;
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000185 // the user agent must change the MediaController into a playing media controller,
186 m_paused = false;
187 // queue a task to fire a simple event named play at the MediaController,
188 scheduleEvent(eventNames().playEvent);
189 // and then report the controller state of the MediaController.
190 reportControllerState();
191}
192
jer.noble@apple.com53ef8c92012-12-01 00:13:34 +0000193void MediaController::play()
194{
195 // When the play() method is invoked, the user agent must invoke the play method of each
196 // slaved media element in turn,
197 for (size_t index = 0; index < m_mediaElements.size(); ++index)
198 m_mediaElements[index]->play();
199
200 // and then invoke the unpause method of the MediaController.
201 unpause();
202}
203
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000204void MediaController::pause()
205{
206 // When the pause() method is invoked, if the MediaController is a playing media controller,
207 if (m_paused)
208 return;
209
210 // then the user agent must change the MediaController into a paused media controller,
211 m_paused = true;
212 // queue a task to fire a simple event named pause at the MediaController,
213 scheduleEvent(eventNames().pauseEvent);
214 // and then report the controller state of the MediaController.
215 reportControllerState();
216}
217
eric.carlson@apple.comffd4ecb2013-04-10 16:25:14 +0000218void MediaController::setDefaultPlaybackRate(double rate)
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000219{
220 if (m_defaultPlaybackRate == rate)
221 return;
222
223 // The defaultPlaybackRate attribute, on setting, must set the MediaController's media controller
224 // default playback rate to the new value,
225 m_defaultPlaybackRate = rate;
226
227 // then queue a task to fire a simple event named ratechange at the MediaController.
228 scheduleEvent(eventNames().ratechangeEvent);
229}
230
eric.carlson@apple.comffd4ecb2013-04-10 16:25:14 +0000231double MediaController::playbackRate() const
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000232{
233 return m_clock->playRate();
234}
235
eric.carlson@apple.comffd4ecb2013-04-10 16:25:14 +0000236void MediaController::setPlaybackRate(double rate)
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000237{
238 if (m_clock->playRate() == rate)
239 return;
240
241 // The playbackRate attribute, on setting, must set the MediaController's media controller
242 // playback rate to the new value,
243 m_clock->setPlayRate(rate);
244
245 for (size_t index = 0; index < m_mediaElements.size(); ++index)
246 m_mediaElements[index]->updatePlaybackRate();
247
248 // then queue a task to fire a simple event named ratechange at the MediaController.
249 scheduleEvent(eventNames().ratechangeEvent);
250}
251
eric.carlson@apple.comffd4ecb2013-04-10 16:25:14 +0000252void MediaController::setVolume(double level, ExceptionCode& code)
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000253{
254 if (m_volume == level)
255 return;
256
257 // If the new value is outside the range 0.0 to 1.0 inclusive, then, on setting, an
258 // IndexSizeError exception must be raised instead.
259 if (level < 0 || level > 1) {
260 code = INDEX_SIZE_ERR;
261 return;
262 }
263
264 // The volume attribute, on setting, if the new value is in the range 0.0 to 1.0 inclusive,
265 // must set the MediaController's media controller volume multiplier to the new value
266 m_volume = level;
267
268 // and queue a task to fire a simple event named volumechange at the MediaController.
269 scheduleEvent(eventNames().volumechangeEvent);
270
271 for (size_t index = 0; index < m_mediaElements.size(); ++index)
272 m_mediaElements[index]->updateVolume();
273}
274
275void MediaController::setMuted(bool flag)
276{
277 if (m_muted == flag)
278 return;
279
280 // The muted attribute, on setting, must set the MediaController's media controller mute override
281 // to the new value
282 m_muted = flag;
283
284 // and queue a task to fire a simple event named volumechange at the MediaController.
285 scheduleEvent(eventNames().volumechangeEvent);
286
287 for (size_t index = 0; index < m_mediaElements.size(); ++index)
288 m_mediaElements[index]->updateVolume();
289}
290
jer.noble@apple.com53ef8c92012-12-01 00:13:34 +0000291static const AtomicString& playbackStateWaiting()
292{
293 DEFINE_STATIC_LOCAL(AtomicString, waiting, ("waiting", AtomicString::ConstructFromLiteral));
294 return waiting;
295}
296
297static const AtomicString& playbackStatePlaying()
298{
299 DEFINE_STATIC_LOCAL(AtomicString, playing, ("playing", AtomicString::ConstructFromLiteral));
300 return playing;
301}
302
303static const AtomicString& playbackStateEnded()
304{
305 DEFINE_STATIC_LOCAL(AtomicString, ended, ("ended", AtomicString::ConstructFromLiteral));
306 return ended;
307}
308
309const AtomicString& MediaController::playbackState() const
310{
311 switch (m_playbackState) {
312 case WAITING:
313 return playbackStateWaiting();
314 case PLAYING:
315 return playbackStatePlaying();
316 case ENDED:
317 return playbackStateEnded();
jer.noble@apple.comcbc42ae2012-12-01 00:43:08 +0000318 default:
319 ASSERT_NOT_REACHED();
320 return nullAtom;
jer.noble@apple.com53ef8c92012-12-01 00:13:34 +0000321 }
322}
323
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000324void MediaController::reportControllerState()
325{
326 updateReadyState();
327 updatePlaybackState();
328}
329
330static AtomicString eventNameForReadyState(MediaControllerInterface::ReadyState state)
331{
332 switch (state) {
333 case MediaControllerInterface::HAVE_NOTHING:
334 return eventNames().emptiedEvent;
335 case MediaControllerInterface::HAVE_METADATA:
336 return eventNames().loadedmetadataEvent;
337 case MediaControllerInterface::HAVE_CURRENT_DATA:
338 return eventNames().loadeddataEvent;
339 case MediaControllerInterface::HAVE_FUTURE_DATA:
340 return eventNames().canplayEvent;
341 case MediaControllerInterface::HAVE_ENOUGH_DATA:
342 return eventNames().canplaythroughEvent;
343 default:
344 ASSERT_NOT_REACHED();
345 return nullAtom;
346 }
347}
348
349void MediaController::updateReadyState()
350{
351 ReadyState oldReadyState = m_readyState;
352 ReadyState newReadyState;
353
354 if (m_mediaElements.isEmpty()) {
355 // If the MediaController has no slaved media elements, let new readiness state be 0.
356 newReadyState = HAVE_NOTHING;
357 } else {
358 // Otherwise, let it have the lowest value of the readyState IDL attributes of all of its
359 // slaved media elements.
360 newReadyState = m_mediaElements.first()->readyState();
361 for (size_t index = 1; index < m_mediaElements.size(); ++index)
andersca@apple.comff9adb82013-10-25 01:15:36 +0000362 newReadyState = std::min(newReadyState, m_mediaElements[index]->readyState());
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000363 }
364
365 if (newReadyState == oldReadyState)
366 return;
367
368 // If the MediaController's most recently reported readiness state is greater than new readiness
369 // state then queue a task to fire a simple event at the MediaController object, whose name is the
370 // event name corresponding to the value of new readiness state given in the table below. [omitted]
371 if (oldReadyState > newReadyState) {
372 scheduleEvent(eventNameForReadyState(newReadyState));
373 return;
374 }
375
376 // If the MediaController's most recently reported readiness state is less than the new readiness
377 // state, then run these substeps:
378 // 1. Let next state be the MediaController's most recently reported readiness state.
379 ReadyState nextState = oldReadyState;
380 do {
381 // 2. Loop: Increment next state by one.
382 nextState = static_cast<ReadyState>(nextState + 1);
383 // 3. Queue a task to fire a simple event at the MediaController object, whose name is the
384 // event name corresponding to the value of next state given in the table below. [omitted]
385 scheduleEvent(eventNameForReadyState(nextState));
386 // If next state is less than new readiness state, then return to the step labeled loop
387 } while (nextState < newReadyState);
388
389 // Let the MediaController's most recently reported readiness state be new readiness state.
390 m_readyState = newReadyState;
391}
392
393void MediaController::updatePlaybackState()
394{
395 PlaybackState oldPlaybackState = m_playbackState;
396 PlaybackState newPlaybackState;
397
398 // Initialize new playback state by setting it to the state given for the first matching
399 // condition from the following list:
400 if (m_mediaElements.isEmpty()) {
401 // If the MediaController has no slaved media elements
402 // Let new playback state be waiting.
403 newPlaybackState = WAITING;
404 } else if (hasEnded()) {
405 // If all of the MediaController's slaved media elements have ended playback and the media
406 // controller playback rate is positive or zero
407 // Let new playback state be ended.
408 newPlaybackState = ENDED;
409 } else if (isBlocked()) {
410 // If the MediaController is a blocked media controller
411 // Let new playback state be waiting.
412 newPlaybackState = WAITING;
413 } else {
414 // Otherwise
415 // Let new playback state be playing.
416 newPlaybackState = PLAYING;
417 }
418
419 // If the MediaController's most recently reported playback state is not equal to new playback state
420 if (newPlaybackState == oldPlaybackState)
421 return;
422
423 // and the new playback state is ended,
424 if (newPlaybackState == ENDED) {
425 // then queue a task that, if the MediaController object is a playing media controller, and
426 // all of the MediaController's slaved media elements have still ended playback, and the
427 // media controller playback rate is still positive or zero,
428 if (!m_paused && hasEnded()) {
429 // changes the MediaController object to a paused media controller
430 m_paused = true;
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000431
432 // and then fires a simple event named pause at the MediaController object.
433 scheduleEvent(eventNames().pauseEvent);
434 }
435 }
436
437 // If the MediaController's most recently reported playback state is not equal to new playback state
438 // then queue a task to fire a simple event at the MediaController object, whose name is playing
439 // if new playback state is playing, ended if new playback state is ended, and waiting otherwise.
440 AtomicString eventName;
441 switch (newPlaybackState) {
442 case WAITING:
443 eventName = eventNames().waitingEvent;
jer.noble@apple.comd0d77172011-12-19 19:15:47 +0000444 m_clock->stop();
jer.noble@apple.com29d33362012-08-10 23:50:46 +0000445 m_timeupdateTimer.stop();
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000446 break;
447 case ENDED:
448 eventName = eventNames().endedEvent;
jer.noble@apple.comd0d77172011-12-19 19:15:47 +0000449 m_clock->stop();
jer.noble@apple.com29d33362012-08-10 23:50:46 +0000450 m_timeupdateTimer.stop();
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000451 break;
452 case PLAYING:
453 eventName = eventNames().playingEvent;
454 m_clock->start();
jer.noble@apple.com29d33362012-08-10 23:50:46 +0000455 startTimeupdateTimer();
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000456 break;
457 default:
458 ASSERT_NOT_REACHED();
459 }
460 scheduleEvent(eventName);
461
462 // Let the MediaController's most recently reported playback state be new playback state.
463 m_playbackState = newPlaybackState;
464
465 updateMediaElements();
466}
467
468void MediaController::updateMediaElements()
469{
470 for (size_t index = 0; index < m_mediaElements.size(); ++index)
471 m_mediaElements[index]->updatePlayState();
472}
473
474void MediaController::bringElementUpToSpeed(HTMLMediaElement* element)
475{
476 ASSERT(element);
477 ASSERT(m_mediaElements.contains(element));
478
479 // When the user agent is to bring a media element up to speed with its new media controller,
480 // it must seek that media element to the MediaController's media controller position relative
481 // to the media element's timeline.
mkwst@chromium.org33cdf432013-02-08 14:21:48 +0000482 element->seek(currentTime(), IGNORE_EXCEPTION);
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000483}
484
485bool MediaController::isBlocked() const
486{
487 // A MediaController is a blocked media controller if the MediaController is a paused media
488 // controller,
489 if (m_paused)
490 return true;
491
492 if (m_mediaElements.isEmpty())
493 return false;
494
495 bool allPaused = true;
496 for (size_t index = 0; index < m_mediaElements.size(); ++index) {
497 HTMLMediaElement* element = m_mediaElements[index];
498 // or if any of its slaved media elements are blocked media elements,
499 if (element->isBlocked())
500 return true;
501
502 // or if any of its slaved media elements whose autoplaying flag is true still have their
503 // paused attribute set to true,
504 if (element->isAutoplaying() && element->paused())
505 return true;
506
507 if (!element->paused())
508 allPaused = false;
509 }
510
511 // or if all of its slaved media elements have their paused attribute set to true.
512 return allPaused;
513}
514
515bool MediaController::hasEnded() const
516{
517 // If the ... media controller playback rate is positive or zero
518 if (m_clock->playRate() < 0)
519 return false;
520
521 // [and] all of the MediaController's slaved media elements have ended playback ... let new
522 // playback state be ended.
523 if (m_mediaElements.isEmpty())
524 return false;
525
526 bool allHaveEnded = true;
527 for (size_t index = 0; index < m_mediaElements.size(); ++index) {
528 if (!m_mediaElements[index]->ended())
529 allHaveEnded = false;
530 }
531 return allHaveEnded;
532}
533
534void MediaController::scheduleEvent(const AtomicString& eventName)
535{
536 m_pendingEvents.append(Event::create(eventName, false, true));
537 if (!m_asyncEventTimer.isActive())
538 m_asyncEventTimer.startOneShot(0);
539}
540
541void MediaController::asyncEventTimerFired(Timer<MediaController>*)
542{
andersca@apple.comc3523f82013-10-18 23:41:24 +0000543 Vector<RefPtr<Event>> pendingEvents;
mkwst@chromium.org33cdf432013-02-08 14:21:48 +0000544
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000545 m_pendingEvents.swap(pendingEvents);
546 size_t count = pendingEvents.size();
547 for (size_t index = 0; index < count; ++index)
mkwst@chromium.org33cdf432013-02-08 14:21:48 +0000548 dispatchEvent(pendingEvents[index].release(), IGNORE_EXCEPTION);
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000549}
550
jer.noble@apple.comee9d8ae2012-07-23 21:39:19 +0000551void MediaController::clearPositionTimerFired(Timer<MediaController>*)
552{
553 m_position = MediaPlayer::invalidTime();
554}
555
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000556bool MediaController::hasAudio() const
557{
558 for (size_t index = 0; index < m_mediaElements.size(); ++index) {
559 if (m_mediaElements[index]->hasAudio())
560 return true;
561 }
562 return false;
563}
564
565bool MediaController::hasVideo() const
566{
567 for (size_t index = 0; index < m_mediaElements.size(); ++index) {
568 if (m_mediaElements[index]->hasVideo())
569 return true;
570 }
571 return false;
572}
573
574bool MediaController::hasClosedCaptions() const
575{
576 for (size_t index = 0; index < m_mediaElements.size(); ++index) {
577 if (m_mediaElements[index]->hasClosedCaptions())
578 return true;
579 }
580 return false;
581}
582
583void MediaController::setClosedCaptionsVisible(bool visible)
584{
585 m_closedCaptionsVisible = visible;
586 for (size_t index = 0; index < m_mediaElements.size(); ++index)
587 m_mediaElements[index]->setClosedCaptionsVisible(visible);
588}
589
590bool MediaController::supportsScanning() const
591{
592 for (size_t index = 0; index < m_mediaElements.size(); ++index) {
593 if (!m_mediaElements[index]->supportsScanning())
594 return false;
595 }
596 return true;
597}
598
599void MediaController::beginScrubbing()
600{
601 for (size_t index = 0; index < m_mediaElements.size(); ++index)
602 m_mediaElements[index]->beginScrubbing();
jer.noble@apple.comd0d77172011-12-19 19:15:47 +0000603 if (m_playbackState == PLAYING)
604 m_clock->stop();
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000605}
606
607void MediaController::endScrubbing()
608{
609 for (size_t index = 0; index < m_mediaElements.size(); ++index)
610 m_mediaElements[index]->endScrubbing();
jer.noble@apple.comd0d77172011-12-19 19:15:47 +0000611 if (m_playbackState == PLAYING)
612 m_clock->start();
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000613}
614
615bool MediaController::canPlay() const
616{
jer.noble@apple.comd0d77172011-12-19 19:15:47 +0000617 if (m_paused)
618 return true;
619
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000620 for (size_t index = 0; index < m_mediaElements.size(); ++index) {
621 if (!m_mediaElements[index]->canPlay())
622 return false;
623 }
624 return true;
625}
626
627bool MediaController::isLiveStream() const
628{
629 for (size_t index = 0; index < m_mediaElements.size(); ++index) {
630 if (!m_mediaElements[index]->isLiveStream())
631 return false;
632 }
633 return true;
634}
635
636bool MediaController::hasCurrentSrc() const
637{
638 for (size_t index = 0; index < m_mediaElements.size(); ++index) {
639 if (!m_mediaElements[index]->hasCurrentSrc())
640 return false;
641 }
642 return true;
643}
644
645void MediaController::returnToRealtime()
646{
647 for (size_t index = 0; index < m_mediaElements.size(); ++index)
648 m_mediaElements[index]->returnToRealtime();
649}
650
jer.noble@apple.com29d33362012-08-10 23:50:46 +0000651// The spec says to fire periodic timeupdate events (those sent while playing) every
652// "15 to 250ms", we choose the slowest frequency
653static const double maxTimeupdateEventFrequency = 0.25;
654
655void MediaController::startTimeupdateTimer()
656{
657 if (m_timeupdateTimer.isActive())
658 return;
659
660 m_timeupdateTimer.startRepeating(maxTimeupdateEventFrequency);
661}
662
663void MediaController::timeupdateTimerFired(Timer<MediaController>*)
664{
665 scheduleTimeupdateEvent();
666}
667
668void MediaController::scheduleTimeupdateEvent()
669{
commit-queue@webkit.orga5f446a2013-08-16 19:33:59 +0000670 double now = monotonicallyIncreasingTime();
jer.noble@apple.com29d33362012-08-10 23:50:46 +0000671 double timedelta = now - m_previousTimeupdateTime;
672
673 if (timedelta < maxTimeupdateEventFrequency)
674 return;
675
676 scheduleEvent(eventNames().timeupdateEvent);
677 m_previousTimeupdateTime = now;
678}
679
jer.noble@apple.combe39c542011-11-14 17:55:16 +0000680#endif