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 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" 35#include <wtf/CurrentTime.h> 36#include <wtf/StdLibExtras.h> 37#include <wtf/text/AtomicString.h> 38 39using namespace WebCore; 40using namespace std; 41 42PassRefPtr<MediaController> MediaController::create(ScriptExecutionContext* context) 43{ 44 return adoptRef(new MediaController(context)); 45} 46 47MediaController::MediaController(ScriptExecutionContext* context) 48 : m_paused(false) 49 , m_defaultPlaybackRate(1) 50 , m_volume(1) 51 , m_position(MediaPlayer::invalidTime()) 52 , m_muted(false) 53 , m_readyState(HAVE_NOTHING) 54 , m_playbackState(WAITING) 55 , m_asyncEventTimer(this, &MediaController::asyncEventTimerFired) 56 , m_clearPositionTimer(this, &MediaController::clearPositionTimerFired) 57 , m_closedCaptionsVisible(false) 58 , m_clock(Clock::create()) 59 , m_scriptExecutionContext(context) 60 , m_timeupdateTimer(this, &MediaController::timeupdateTimerFired) 61 , m_previousTimeupdateTime(0) 62{ 63} 64 65MediaController::~MediaController() 66{ 67} 68 69void MediaController::addMediaElement(HTMLMediaElement* element) 70{ 71 ASSERT(element); 72 ASSERT(!m_mediaElements.contains(element)); 73 74 m_mediaElements.append(element); 75 bringElementUpToSpeed(element); 76} 77 78void MediaController::removeMediaElement(HTMLMediaElement* element) 79{ 80 ASSERT(element); 81 ASSERT(m_mediaElements.contains(element)); 82 m_mediaElements.remove(m_mediaElements.find(element)); 83} 84 85bool MediaController::containsMediaElement(HTMLMediaElement* element) const 86{ 87 return m_mediaElements.contains(element); 88} 89 90PassRefPtr<TimeRanges> MediaController::buffered() const 91{ 92 if (m_mediaElements.isEmpty()) 93 return TimeRanges::create(); 94 95 // The buffered attribute must return a new static normalized TimeRanges object that represents 96 // the intersection of the ranges of the media resources of the slaved media elements that the 97 // user agent has buffered, at the time the attribute is evaluated. 98 RefPtr<TimeRanges> bufferedRanges = m_mediaElements.first()->buffered(); 99 for (size_t index = 1; index < m_mediaElements.size(); ++index) 100 bufferedRanges->intersectWith(m_mediaElements[index]->buffered().get()); 101 return bufferedRanges; 102} 103 104PassRefPtr<TimeRanges> MediaController::seekable() const 105{ 106 if (m_mediaElements.isEmpty()) 107 return TimeRanges::create(); 108 109 // The seekable attribute must return a new static normalized TimeRanges object that represents 110 // the intersection of the ranges of the media resources of the slaved media elements that the 111 // user agent is able to seek to, at the time the attribute is evaluated. 112 RefPtr<TimeRanges> seekableRanges = m_mediaElements.first()->seekable(); 113 for (size_t index = 1; index < m_mediaElements.size(); ++index) 114 seekableRanges->intersectWith(m_mediaElements[index]->seekable().get()); 115 return seekableRanges; 116} 117 118PassRefPtr<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 object that represents 124 // the union of the ranges of the media resources of the slaved media elements that the 125 // user agent has so far rendered, at the time the attribute is evaluated. 126 RefPtr<TimeRanges> playedRanges = m_mediaElements.first()->played(); 127 for (size_t index = 1; index < m_mediaElements.size(); ++index) 128 playedRanges->unionWith(m_mediaElements[index]->played().get()); 129 return playedRanges; 130} 131 132double MediaController::duration() const 133{ 134 // FIXME: Investigate caching the maximum duration and only updating the cached value 135 // when the slaved media elements' durations change. 136 double maxDuration = 0; 137 for (size_t index = 0; index < m_mediaElements.size(); ++index) { 138 double duration = m_mediaElements[index]->duration(); 139 if (std::isnan(duration)) 140 continue; 141 maxDuration = max(maxDuration, duration); 142 } 143 return maxDuration; 144} 145 146double MediaController::currentTime() const 147{ 148 if (m_mediaElements.isEmpty()) 149 return 0; 150 151 if (m_position == MediaPlayer::invalidTime()) { 152 // Some clocks may return times outside the range of [0..duration]. 153 m_position = max(0.0, min(duration(), m_clock->currentTime())); 154 m_clearPositionTimer.startOneShot(0); 155 } 156 157 return m_position; 158} 159 160void MediaController::setCurrentTime(double time, ExceptionCode& code) 161{ 162 // When the user agent is to seek the media controller to a particular new playback position, 163 // it must follow these steps: 164 // If the new playback position is less than zero, then set it to zero. 165 time = max(0.0, time); 166 167 // If the new playback position is greater than the media controller duration, then set it 168 // to the media controller duration. 169 time = min(time, duration()); 170 171 // Set the media controller position to the new playback position. 172 m_clock->setCurrentTime(time); 173 174 // Seek each slaved media element to the new playback position relative to the media element timeline. 175 for (size_t index = 0; index < m_mediaElements.size(); ++index) 176 m_mediaElements[index]->seek(time, code); 177 178 scheduleTimeupdateEvent(); 179} 180 181void MediaController::unpause() 182{ 183 // When the unpause() method is invoked, if the MediaController is a paused media controller, 184 if (!m_paused) 185 return; 186 // the user agent must change the MediaController into a playing media controller, 187 m_paused = false; 188 // queue a task to fire a simple event named play at the MediaController, 189 scheduleEvent(eventNames().playEvent); 190 // and then report the controller state of the MediaController. 191 reportControllerState(); 192} 193 194void MediaController::play() 195{ 196 // When the play() method is invoked, the user agent must invoke the play method of each 197 // slaved media element in turn, 198 for (size_t index = 0; index < m_mediaElements.size(); ++index) 199 m_mediaElements[index]->play(); 200 201 // and then invoke the unpause method of the MediaController. 202 unpause(); 203} 204 205void MediaController::pause() 206{ 207 // When the pause() method is invoked, if the MediaController is a playing media controller, 208 if (m_paused) 209 return; 210 211 // then the user agent must change the MediaController into a paused media controller, 212 m_paused = true; 213 // queue a task to fire a simple event named pause at the MediaController, 214 scheduleEvent(eventNames().pauseEvent); 215 // and then report the controller state of the MediaController. 216 reportControllerState(); 217} 218 219void MediaController::setDefaultPlaybackRate(double rate) 220{ 221 if (m_defaultPlaybackRate == rate) 222 return; 223 224 // The defaultPlaybackRate attribute, on setting, must set the MediaController's media controller 225 // default playback rate to the new value, 226 m_defaultPlaybackRate = rate; 227 228 // then queue a task to fire a simple event named ratechange at the MediaController. 229 scheduleEvent(eventNames().ratechangeEvent); 230} 231 232double MediaController::playbackRate() const 233{ 234 return m_clock->playRate(); 235} 236 237void MediaController::setPlaybackRate(double rate) 238{ 239 if (m_clock->playRate() == rate) 240 return; 241 242 // The playbackRate attribute, on setting, must set the MediaController's media controller 243 // playback rate to the new value, 244 m_clock->setPlayRate(rate); 245 246 for (size_t index = 0; index < m_mediaElements.size(); ++index) 247 m_mediaElements[index]->updatePlaybackRate(); 248 249 // then queue a task to fire a simple event named ratechange at the MediaController. 250 scheduleEvent(eventNames().ratechangeEvent); 251} 252 253void MediaController::setVolume(double level, ExceptionCode& code) 254{ 255 if (m_volume == level) 256 return; 257 258 // If the new value is outside the range 0.0 to 1.0 inclusive, then, on setting, an 259 // IndexSizeError exception must be raised instead. 260 if (level < 0 || level > 1) { 261 code = INDEX_SIZE_ERR; 262 return; 263 } 264 265 // The volume attribute, on setting, if the new value is in the range 0.0 to 1.0 inclusive, 266 // must set the MediaController's media controller volume multiplier to the new value 267 m_volume = level; 268 269 // and queue a task to fire a simple event named volumechange at the MediaController. 270 scheduleEvent(eventNames().volumechangeEvent); 271 272 for (size_t index = 0; index < m_mediaElements.size(); ++index) 273 m_mediaElements[index]->updateVolume(); 274} 275 276void MediaController::setMuted(bool flag) 277{ 278 if (m_muted == flag) 279 return; 280 281 // The muted attribute, on setting, must set the MediaController's media controller mute override 282 // to the new value 283 m_muted = flag; 284 285 // and queue a task to fire a simple event named volumechange at the MediaController. 286 scheduleEvent(eventNames().volumechangeEvent); 287 288 for (size_t index = 0; index < m_mediaElements.size(); ++index) 289 m_mediaElements[index]->updateVolume(); 290} 291 292static const AtomicString& playbackStateWaiting() 293{ 294 DEFINE_STATIC_LOCAL(AtomicString, waiting, ("waiting", AtomicString::ConstructFromLiteral)); 295 return waiting; 296} 297 298static const AtomicString& playbackStatePlaying() 299{ 300 DEFINE_STATIC_LOCAL(AtomicString, playing, ("playing", AtomicString::ConstructFromLiteral)); 301 return playing; 302} 303 304static const AtomicString& playbackStateEnded() 305{ 306 DEFINE_STATIC_LOCAL(AtomicString, ended, ("ended", AtomicString::ConstructFromLiteral)); 307 return ended; 308} 309 310const AtomicString& MediaController::playbackState() const 311{ 312 switch (m_playbackState) { 313 case WAITING: 314 return playbackStateWaiting(); 315 case PLAYING: 316 return playbackStatePlaying(); 317 case ENDED: 318 return playbackStateEnded(); 319 default: 320 ASSERT_NOT_REACHED(); 321 return nullAtom; 322 } 323} 324 325void MediaController::reportControllerState() 326{ 327 updateReadyState(); 328 updatePlaybackState(); 329} 330 331static AtomicString eventNameForReadyState(MediaControllerInterface::ReadyState state) 332{ 333 switch (state) { 334 case MediaControllerInterface::HAVE_NOTHING: 335 return eventNames().emptiedEvent; 336 case MediaControllerInterface::HAVE_METADATA: 337 return eventNames().loadedmetadataEvent; 338 case MediaControllerInterface::HAVE_CURRENT_DATA: 339 return eventNames().loadeddataEvent; 340 case MediaControllerInterface::HAVE_FUTURE_DATA: 341 return eventNames().canplayEvent; 342 case MediaControllerInterface::HAVE_ENOUGH_DATA: 343 return eventNames().canplaythroughEvent; 344 default: 345 ASSERT_NOT_REACHED(); 346 return nullAtom; 347 } 348} 349 350void MediaController::updateReadyState() 351{ 352 ReadyState oldReadyState = m_readyState; 353 ReadyState newReadyState; 354 355 if (m_mediaElements.isEmpty()) { 356 // If the MediaController has no slaved media elements, let new readiness state be 0. 357 newReadyState = HAVE_NOTHING; 358 } else { 359 // Otherwise, let it have the lowest value of the readyState IDL attributes of all of its 360 // slaved media elements. 361 newReadyState = m_mediaElements.first()->readyState(); 362 for (size_t index = 1; index < m_mediaElements.size(); ++index) 363 newReadyState = min(newReadyState, m_mediaElements[index]->readyState()); 364 } 365 366 if (newReadyState == oldReadyState) 367 return; 368 369 // If the MediaController's most recently reported readiness state is greater than new readiness 370 // state then queue a task to fire a simple event at the MediaController object, whose name is the 371 // event name corresponding to the value of new readiness state given in the table below. [omitted] 372 if (oldReadyState > newReadyState) { 373 scheduleEvent(eventNameForReadyState(newReadyState)); 374 return; 375 } 376 377 // If the MediaController's most recently reported readiness state is less than the new readiness 378 // state, then run these substeps: 379 // 1. Let next state be the MediaController's most recently reported readiness state. 380 ReadyState nextState = oldReadyState; 381 do { 382 // 2. Loop: Increment next state by one. 383 nextState = static_cast<ReadyState>(nextState + 1); 384 // 3. Queue a task to fire a simple event at the MediaController object, whose name is the 385 // event name corresponding to the value of next state given in the table below. [omitted] 386 scheduleEvent(eventNameForReadyState(nextState)); 387 // If next state is less than new readiness state, then return to the step labeled loop 388 } while (nextState < newReadyState); 389 390 // Let the MediaController's most recently reported readiness state be new readiness state. 391 m_readyState = newReadyState; 392} 393 394void MediaController::updatePlaybackState() 395{ 396 PlaybackState oldPlaybackState = m_playbackState; 397 PlaybackState newPlaybackState; 398 399 // Initialize new playback state by setting it to the state given for the first matching 400 // condition from the following list: 401 if (m_mediaElements.isEmpty()) { 402 // If the MediaController has no slaved media elements 403 // Let new playback state be waiting. 404 newPlaybackState = WAITING; 405 } else if (hasEnded()) { 406 // If all of the MediaController's slaved media elements have ended playback and the media 407 // controller playback rate is positive or zero 408 // Let new playback state be ended. 409 newPlaybackState = ENDED; 410 } else if (isBlocked()) { 411 // If the MediaController is a blocked media controller 412 // Let new playback state be waiting. 413 newPlaybackState = WAITING; 414 } else { 415 // Otherwise 416 // Let new playback state be playing. 417 newPlaybackState = PLAYING; 418 } 419 420 // If the MediaController's most recently reported playback state is not equal to new playback state 421 if (newPlaybackState == oldPlaybackState) 422 return; 423 424 // and the new playback state is ended, 425 if (newPlaybackState == ENDED) { 426 // then queue a task that, if the MediaController object is a playing media controller, and 427 // all of the MediaController's slaved media elements have still ended playback, and the 428 // media controller playback rate is still positive or zero, 429 if (!m_paused && hasEnded()) { 430 // changes the MediaController object to a paused media controller 431 m_paused = true; 432 433 // and then fires a simple event named pause at the MediaController object. 434 scheduleEvent(eventNames().pauseEvent); 435 } 436 } 437 438 // If the MediaController's most recently reported playback state is not equal to new playback state 439 // then queue a task to fire a simple event at the MediaController object, whose name is playing 440 // if new playback state is playing, ended if new playback state is ended, and waiting otherwise. 441 AtomicString eventName; 442 switch (newPlaybackState) { 443 case WAITING: 444 eventName = eventNames().waitingEvent; 445 m_clock->stop(); 446 m_timeupdateTimer.stop(); 447 break; 448 case ENDED: 449 eventName = eventNames().endedEvent; 450 m_clock->stop(); 451 m_timeupdateTimer.stop(); 452 break; 453 case PLAYING: 454 eventName = eventNames().playingEvent; 455 m_clock->start(); 456 startTimeupdateTimer(); 457 break; 458 default: 459 ASSERT_NOT_REACHED(); 460 } 461 scheduleEvent(eventName); 462 463 // Let the MediaController's most recently reported playback state be new playback state. 464 m_playbackState = newPlaybackState; 465 466 updateMediaElements(); 467} 468 469void MediaController::updateMediaElements() 470{ 471 for (size_t index = 0; index < m_mediaElements.size(); ++index) 472 m_mediaElements[index]->updatePlayState(); 473} 474 475void MediaController::bringElementUpToSpeed(HTMLMediaElement* element) 476{ 477 ASSERT(element); 478 ASSERT(m_mediaElements.contains(element)); 479 480 // When the user agent is to bring a media element up to speed with its new media controller, 481 // it must seek that media element to the MediaController's media controller position relative 482 // to the media element's timeline. 483 element->seek(currentTime(), IGNORE_EXCEPTION); 484} 485 486bool MediaController::isBlocked() const 487{ 488 // A MediaController is a blocked media controller if the MediaController is a paused media 489 // controller, 490 if (m_paused) 491 return true; 492 493 if (m_mediaElements.isEmpty()) 494 return false; 495 496 bool allPaused = true; 497 for (size_t index = 0; index < m_mediaElements.size(); ++index) { 498 HTMLMediaElement* element = m_mediaElements[index]; 499 // or if any of its slaved media elements are blocked media elements, 500 if (element->isBlocked()) 501 return true; 502 503 // or if any of its slaved media elements whose autoplaying flag is true still have their 504 // paused attribute set to true, 505 if (element->isAutoplaying() && element->paused()) 506 return true; 507 508 if (!element->paused()) 509 allPaused = false; 510 } 511 512 // or if all of its slaved media elements have their paused attribute set to true. 513 return allPaused; 514} 515 516bool MediaController::hasEnded() const 517{ 518 // If the ... media controller playback rate is positive or zero 519 if (m_clock->playRate() < 0) 520 return false; 521 522 // [and] all of the MediaController's slaved media elements have ended playback ... let new 523 // playback state be ended. 524 if (m_mediaElements.isEmpty()) 525 return false; 526 527 bool allHaveEnded = true; 528 for (size_t index = 0; index < m_mediaElements.size(); ++index) { 529 if (!m_mediaElements[index]->ended()) 530 allHaveEnded = false; 531 } 532 return allHaveEnded; 533} 534 535void MediaController::scheduleEvent(const AtomicString& eventName) 536{ 537 m_pendingEvents.append(Event::create(eventName, false, true)); 538 if (!m_asyncEventTimer.isActive()) 539 m_asyncEventTimer.startOneShot(0); 540} 541 542void MediaController::asyncEventTimerFired(Timer<MediaController>*) 543{ 544 Vector<RefPtr<Event> > pendingEvents; 545 546 m_pendingEvents.swap(pendingEvents); 547 size_t count = pendingEvents.size(); 548 for (size_t index = 0; index < count; ++index) 549 dispatchEvent(pendingEvents[index].release(), IGNORE_EXCEPTION); 550} 551 552void MediaController::clearPositionTimerFired(Timer<MediaController>*) 553{ 554 m_position = MediaPlayer::invalidTime(); 555} 556 557bool MediaController::hasAudio() const 558{ 559 for (size_t index = 0; index < m_mediaElements.size(); ++index) { 560 if (m_mediaElements[index]->hasAudio()) 561 return true; 562 } 563 return false; 564} 565 566bool MediaController::hasVideo() const 567{ 568 for (size_t index = 0; index < m_mediaElements.size(); ++index) { 569 if (m_mediaElements[index]->hasVideo()) 570 return true; 571 } 572 return false; 573} 574 575bool MediaController::hasClosedCaptions() const 576{ 577 for (size_t index = 0; index < m_mediaElements.size(); ++index) { 578 if (m_mediaElements[index]->hasClosedCaptions()) 579 return true; 580 } 581 return false; 582} 583 584void MediaController::setClosedCaptionsVisible(bool visible) 585{ 586 m_closedCaptionsVisible = visible; 587 for (size_t index = 0; index < m_mediaElements.size(); ++index) 588 m_mediaElements[index]->setClosedCaptionsVisible(visible); 589} 590 591bool MediaController::supportsScanning() const 592{ 593 for (size_t index = 0; index < m_mediaElements.size(); ++index) { 594 if (!m_mediaElements[index]->supportsScanning()) 595 return false; 596 } 597 return true; 598} 599 600void MediaController::beginScrubbing() 601{ 602 for (size_t index = 0; index < m_mediaElements.size(); ++index) 603 m_mediaElements[index]->beginScrubbing(); 604 if (m_playbackState == PLAYING) 605 m_clock->stop(); 606} 607 608void MediaController::endScrubbing() 609{ 610 for (size_t index = 0; index < m_mediaElements.size(); ++index) 611 m_mediaElements[index]->endScrubbing(); 612 if (m_playbackState == PLAYING) 613 m_clock->start(); 614} 615 616bool MediaController::canPlay() const 617{ 618 if (m_paused) 619 return true; 620 621 for (size_t index = 0; index < m_mediaElements.size(); ++index) { 622 if (!m_mediaElements[index]->canPlay()) 623 return false; 624 } 625 return true; 626} 627 628bool MediaController::isLiveStream() const 629{ 630 for (size_t index = 0; index < m_mediaElements.size(); ++index) { 631 if (!m_mediaElements[index]->isLiveStream()) 632 return false; 633 } 634 return true; 635} 636 637bool MediaController::hasCurrentSrc() const 638{ 639 for (size_t index = 0; index < m_mediaElements.size(); ++index) { 640 if (!m_mediaElements[index]->hasCurrentSrc()) 641 return false; 642 } 643 return true; 644} 645 646void MediaController::returnToRealtime() 647{ 648 for (size_t index = 0; index < m_mediaElements.size(); ++index) 649 m_mediaElements[index]->returnToRealtime(); 650} 651 652const AtomicString& MediaController::interfaceName() const 653{ 654 return eventNames().interfaceForMediaController; 655} 656 657// The spec says to fire periodic timeupdate events (those sent while playing) every 658// "15 to 250ms", we choose the slowest frequency 659static const double maxTimeupdateEventFrequency = 0.25; 660 661void MediaController::startTimeupdateTimer() 662{ 663 if (m_timeupdateTimer.isActive()) 664 return; 665 666 m_timeupdateTimer.startRepeating(maxTimeupdateEventFrequency); 667} 668 669void MediaController::timeupdateTimerFired(Timer<MediaController>*) 670{ 671 scheduleTimeupdateEvent(); 672} 673 674void MediaController::scheduleTimeupdateEvent() 675{ 676 double now = WTF::currentTime(); 677 double timedelta = now - m_previousTimeupdateTime; 678 679 if (timedelta < maxTimeupdateEventFrequency) 680 return; 681 682 scheduleEvent(eventNames().timeupdateEvent); 683 m_previousTimeupdateTime = now; 684} 685 686#endif 687