dcb3249f0a58b2f5fa9901aa5cd047c58e1456f2
[WebKit-https.git] / Source / WebCore / html / MediaController.cpp
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 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 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 "HTMLMediaElement.h"
33 #include "TimeRanges.h"
34 #include <wtf/CurrentTime.h>
35 #include <wtf/StdLibExtras.h>
36 #include <wtf/text/AtomicString.h>
37
38 using namespace WebCore;
39
40 Ref<MediaController> MediaController::create(ScriptExecutionContext& context)
41 {
42     return adoptRef(*new MediaController(context));
43 }
44
45 MediaController::MediaController(ScriptExecutionContext& context)
46     : m_paused(false)
47     , m_defaultPlaybackRate(1)
48     , m_volume(1)
49     , m_position(MediaPlayer::invalidTime())
50     , m_muted(false)
51     , m_readyState(HAVE_NOTHING)
52     , m_playbackState(WAITING)
53     , m_asyncEventTimer(*this, &MediaController::asyncEventTimerFired)
54     , m_clearPositionTimer(*this, &MediaController::clearPositionTimerFired)
55     , m_closedCaptionsVisible(false)
56     , m_clock(Clock::create())
57     , m_scriptExecutionContext(context)
58     , m_timeupdateTimer(*this, &MediaController::scheduleTimeupdateEvent)
59     , m_previousTimeupdateTime(0)
60 {
61 }
62
63 MediaController::~MediaController()
64 {
65 }
66
67 void MediaController::addMediaElement(HTMLMediaElement* element)
68 {
69     ASSERT(element);
70     ASSERT(!m_mediaElements.contains(element));
71
72     m_mediaElements.append(element);
73     bringElementUpToSpeed(element);
74 }
75
76 void MediaController::removeMediaElement(HTMLMediaElement* element)
77 {
78     ASSERT(element);
79     ASSERT(m_mediaElements.contains(element));
80     m_mediaElements.remove(m_mediaElements.find(element));
81 }
82
83 bool MediaController::containsMediaElement(HTMLMediaElement* element) const
84 {
85     return m_mediaElements.contains(element);
86 }
87
88 PassRefPtr<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 object 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     RefPtr<TimeRanges> bufferedRanges = m_mediaElements.first()->buffered();
97     for (size_t index = 1; index < m_mediaElements.size(); ++index)
98         bufferedRanges->intersectWith(*m_mediaElements[index]->buffered().get());
99     return bufferedRanges;
100 }
101
102 PassRefPtr<TimeRanges> MediaController::seekable() const
103 {
104     if (m_mediaElements.isEmpty())
105         return TimeRanges::create();
106
107     // The seekable attribute must return a new static normalized TimeRanges object that represents
108     // the intersection of the ranges of the media resources of the slaved media elements that the
109     // user agent is able to seek to, at the time the attribute is evaluated.
110     RefPtr<TimeRanges> seekableRanges = m_mediaElements.first()->seekable();
111     for (size_t index = 1; index < m_mediaElements.size(); ++index)
112         seekableRanges->intersectWith(*m_mediaElements[index]->seekable().get());
113     return seekableRanges;
114 }
115
116 PassRefPtr<TimeRanges> MediaController::played()
117 {
118     if (m_mediaElements.isEmpty())
119         return TimeRanges::create();
120
121     // The played attribute must return a new static normalized TimeRanges object that represents 
122     // the union of the ranges of the media resources of the slaved media elements that the 
123     // user agent has so far rendered, at the time the attribute is evaluated.
124     RefPtr<TimeRanges> playedRanges = m_mediaElements.first()->played();
125     for (size_t index = 1; index < m_mediaElements.size(); ++index)
126         playedRanges->unionWith(*m_mediaElements[index]->played().get());
127     return playedRanges;
128 }
129
130 double MediaController::duration() const
131 {
132     // FIXME: Investigate caching the maximum duration and only updating the cached value
133     // when the slaved media elements' durations change.
134     double maxDuration = 0;
135     for (size_t index = 0; index < m_mediaElements.size(); ++index) {
136         double duration = m_mediaElements[index]->duration();
137         if (std::isnan(duration))
138             continue;
139         maxDuration = std::max(maxDuration, duration);
140     }
141     return maxDuration;
142 }
143
144 double MediaController::currentTime() const
145 {
146     if (m_mediaElements.isEmpty())
147         return 0;
148
149     if (m_position == MediaPlayer::invalidTime()) {
150         // Some clocks may return times outside the range of [0..duration].
151         m_position = std::max<double>(0, std::min(duration(), m_clock->currentTime()));
152         m_clearPositionTimer.startOneShot(0);
153     }
154
155     return m_position;
156 }
157
158 void MediaController::setCurrentTime(double time)
159 {
160     // When the user agent is to seek the media controller to a particular new playback position, 
161     // it must follow these steps:
162     // If the new playback position is less than zero, then set it to zero.
163     time = std::max(0.0, time);
164     
165     // If the new playback position is greater than the media controller duration, then set it 
166     // to the media controller duration.
167     time = std::min(time, duration());
168     
169     // Set the media controller position to the new playback position.
170     m_clock->setCurrentTime(time);
171     
172     // Seek each slaved media element to the new playback position relative to the media element timeline.
173     for (size_t index = 0; index < m_mediaElements.size(); ++index)
174         m_mediaElements[index]->seek(MediaTime::createWithDouble(time));
175
176     scheduleTimeupdateEvent();
177     m_resetCurrentTimeInNextPlay = false;
178 }
179
180 void MediaController::unpause()
181 {
182     // When the unpause() method is invoked, if the MediaController is a paused media controller,
183     if (!m_paused)
184         return;
185     // 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
193 void 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
204 void 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
218 void MediaController::setDefaultPlaybackRate(double rate)
219 {
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
231 double MediaController::playbackRate() const
232 {
233     return m_clock->playRate();
234 }
235
236 void MediaController::setPlaybackRate(double rate)
237 {
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
252 void MediaController::setVolume(double level, ExceptionCode& code)
253 {
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
275 void 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
291 static const AtomicString& playbackStateWaiting()
292 {
293     DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, waiting, ("waiting", AtomicString::ConstructFromLiteral));
294     return waiting;
295 }
296
297 static const AtomicString& playbackStatePlaying()
298 {
299     DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, playing, ("playing", AtomicString::ConstructFromLiteral));
300     return playing;
301 }
302
303 static const AtomicString& playbackStateEnded()
304 {
305     DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, ended, ("ended", AtomicString::ConstructFromLiteral));
306     return ended;
307 }
308
309 const 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();
318     default:
319         ASSERT_NOT_REACHED();
320         return nullAtom;
321     }
322 }
323
324 void MediaController::reportControllerState()
325 {
326     updateReadyState();
327     updatePlaybackState();
328 }
329
330 static 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
349 void 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)
362             newReadyState = std::min(newReadyState, m_mediaElements[index]->readyState());
363     }
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
393 void 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;
431
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;
444         m_clock->stop();
445         m_timeupdateTimer.stop();
446         break;
447     case ENDED:
448         eventName = eventNames().endedEvent;
449         m_resetCurrentTimeInNextPlay = true;
450         m_clock->stop();
451         m_timeupdateTimer.stop();
452         break;
453     case PLAYING:
454         if (m_resetCurrentTimeInNextPlay) {
455             m_resetCurrentTimeInNextPlay = false;
456             m_clock->setCurrentTime(0);
457         }
458         eventName = eventNames().playingEvent;
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 playback state.
468     m_playbackState = newPlaybackState;
469
470     updateMediaElements();
471 }
472
473 void MediaController::updateMediaElements()
474 {
475     for (size_t index = 0; index < m_mediaElements.size(); ++index)
476         m_mediaElements[index]->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->seekInternal(MediaTime::createWithDouble(currentTime()));
488 }
489
490 bool MediaController::isBlocked() const
491 {
492     // A MediaController is a blocked media controller if the MediaController is a paused media 
493     // controller,
494     if (m_paused)
495         return true;
496     
497     if (m_mediaElements.isEmpty())
498         return false;
499     
500     bool allPaused = true;
501     for (size_t index = 0; index < m_mediaElements.size(); ++index) {
502         HTMLMediaElement* element = m_mediaElements[index];
503         //  or if any of its slaved media elements are blocked media elements,
504         if (element->isBlocked())
505             return true;
506         
507         // or if any of its slaved media elements whose autoplaying flag is true still have their 
508         // paused attribute set to true,
509         if (element->isAutoplaying() && element->paused())
510             return true;
511         
512         if (!element->paused())
513             allPaused = false;
514     }
515     
516     // or if all of its slaved media elements have their paused attribute set to true.
517     return allPaused;
518 }
519
520 bool MediaController::hasEnded() const
521 {
522     // If the ... media controller playback rate is positive or zero
523     if (m_clock->playRate() < 0)
524         return false;
525
526     // [and] all of the MediaController's slaved media elements have ended playback ... let new
527     // playback state be ended.
528     if (m_mediaElements.isEmpty())
529         return false;
530     
531     bool allHaveEnded = true;
532     for (size_t index = 0; index < m_mediaElements.size(); ++index) {
533         if (!m_mediaElements[index]->ended())
534             allHaveEnded = false;
535     }
536     return allHaveEnded;
537 }
538
539 void MediaController::scheduleEvent(const AtomicString& eventName)
540 {
541     m_pendingEvents.append(Event::create(eventName, false, true));
542     if (!m_asyncEventTimer.isActive())
543         m_asyncEventTimer.startOneShot(0);
544 }
545
546 void MediaController::asyncEventTimerFired()
547 {
548     Vector<RefPtr<Event>> pendingEvents;
549
550     m_pendingEvents.swap(pendingEvents);
551     size_t count = pendingEvents.size();
552     for (size_t index = 0; index < count; ++index)
553         dispatchEvent(pendingEvents[index].release(), IGNORE_EXCEPTION);
554 }
555
556 void MediaController::clearPositionTimerFired()
557 {
558     m_position = MediaPlayer::invalidTime();
559 }
560
561 bool MediaController::hasAudio() const
562 {
563     for (size_t index = 0; index < m_mediaElements.size(); ++index) {
564         if (m_mediaElements[index]->hasAudio())
565             return true;
566     }
567     return false;
568 }
569
570 bool MediaController::hasVideo() const
571 {
572     for (size_t index = 0; index < m_mediaElements.size(); ++index) {
573         if (m_mediaElements[index]->hasVideo())
574             return true;
575     }
576     return false;
577 }
578
579 bool MediaController::hasClosedCaptions() const
580 {
581     for (size_t index = 0; index < m_mediaElements.size(); ++index) {
582         if (m_mediaElements[index]->hasClosedCaptions())
583             return true;
584     }
585     return false;
586 }
587
588 void MediaController::setClosedCaptionsVisible(bool visible)
589 {
590     m_closedCaptionsVisible = visible;
591     for (size_t index = 0; index < m_mediaElements.size(); ++index)
592         m_mediaElements[index]->setClosedCaptionsVisible(visible);
593 }
594
595 bool MediaController::supportsScanning() const
596 {
597     for (size_t index = 0; index < m_mediaElements.size(); ++index) {
598         if (!m_mediaElements[index]->supportsScanning())
599             return false;
600     }
601     return true;
602 }
603
604 void MediaController::beginScrubbing()
605 {
606     for (size_t index = 0; index < m_mediaElements.size(); ++index)
607         m_mediaElements[index]->beginScrubbing();
608     if (m_playbackState == PLAYING)
609         m_clock->stop();
610 }
611
612 void MediaController::endScrubbing()
613 {
614     for (size_t index = 0; index < m_mediaElements.size(); ++index)
615         m_mediaElements[index]->endScrubbing();
616     if (m_playbackState == PLAYING)
617         m_clock->start();
618 }
619
620 void MediaController::beginScanning(ScanDirection direction)
621 {
622     for (auto& mediaElement : m_mediaElements)
623         mediaElement->beginScanning(direction);
624 }
625
626 void MediaController::endScanning()
627 {
628     for (auto& mediaElement : m_mediaElements)
629         mediaElement->endScanning();
630 }
631
632 bool MediaController::canPlay() const
633 {
634     if (m_paused)
635         return true;
636
637     for (size_t index = 0; index < m_mediaElements.size(); ++index) {
638         if (!m_mediaElements[index]->canPlay())
639             return false;
640     }
641     return true;
642 }
643
644 bool MediaController::isLiveStream() const
645 {
646     for (size_t index = 0; index < m_mediaElements.size(); ++index) {
647         if (!m_mediaElements[index]->isLiveStream())
648             return false;
649     }
650     return true;
651 }
652
653 bool MediaController::hasCurrentSrc() const
654 {
655     for (size_t index = 0; index < m_mediaElements.size(); ++index) {
656         if (!m_mediaElements[index]->hasCurrentSrc())
657             return false;
658     }
659     return true;
660 }
661
662 void MediaController::returnToRealtime()
663 {
664     for (size_t index = 0; index < m_mediaElements.size(); ++index)
665         m_mediaElements[index]->returnToRealtime();
666 }
667
668 // The spec says to fire periodic timeupdate events (those sent while playing) every
669 // "15 to 250ms", we choose the slowest frequency
670 static const double maxTimeupdateEventFrequency = 0.25;
671
672 void MediaController::startTimeupdateTimer()
673 {
674     if (m_timeupdateTimer.isActive())
675         return;
676
677     m_timeupdateTimer.startRepeating(maxTimeupdateEventFrequency);
678 }
679
680 void MediaController::scheduleTimeupdateEvent()
681 {
682     double now = monotonicallyIncreasingTime();
683     double timedelta = now - m_previousTimeupdateTime;
684
685     if (timedelta < maxTimeupdateEventFrequency)
686         return;
687
688     scheduleEvent(eventNames().timeupdateEvent);
689     m_previousTimeupdateTime = now;
690 }
691
692 #endif