dbb02ab9e949121946a00ebad73a1820c7f16b02
[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 (auto& mediaElement : m_mediaElements) {
136         double duration = mediaElement->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 (auto& mediaElement : m_mediaElements)
174         mediaElement->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 (auto& mediaElement : m_mediaElements)
198         mediaElement->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 (auto& mediaElement : m_mediaElements)
246         mediaElement->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 (auto& mediaElement : m_mediaElements)
272         mediaElement->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 (auto& mediaElement : m_mediaElements)
288         mediaElement->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 (auto& mediaElement : m_mediaElements)
476         mediaElement->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 (auto& element : m_mediaElements) {
502         //  or if any of its slaved media elements are blocked media elements,
503         if (element->isBlocked())
504             return true;
505         
506         // or if any of its slaved media elements whose autoplaying flag is true still have their 
507         // paused attribute set to true,
508         if (element->isAutoplaying() && element->paused())
509             return true;
510         
511         if (!element->paused())
512             allPaused = false;
513     }
514     
515     // or if all of its slaved media elements have their paused attribute set to true.
516     return allPaused;
517 }
518
519 bool MediaController::hasEnded() const
520 {
521     // If the ... media controller playback rate is positive or zero
522     if (m_clock->playRate() < 0)
523         return false;
524
525     // [and] all of the MediaController's slaved media elements have ended playback ... let new
526     // playback state be ended.
527     if (m_mediaElements.isEmpty())
528         return false;
529     
530     bool allHaveEnded = true;
531     for (auto& mediaElement : m_mediaElements) {
532         if (!mediaElement->ended())
533             allHaveEnded = false;
534     }
535     return allHaveEnded;
536 }
537
538 void MediaController::scheduleEvent(const AtomicString& eventName)
539 {
540     m_pendingEvents.append(Event::create(eventName, false, true));
541     if (!m_asyncEventTimer.isActive())
542         m_asyncEventTimer.startOneShot(0);
543 }
544
545 void MediaController::asyncEventTimerFired()
546 {
547     Vector<Ref<Event>> pendingEvents;
548
549     m_pendingEvents.swap(pendingEvents);
550     for (auto& pendingEvent : pendingEvents)
551         dispatchEvent(pendingEvent);
552 }
553
554 void MediaController::clearPositionTimerFired()
555 {
556     m_position = MediaPlayer::invalidTime();
557 }
558
559 bool MediaController::hasAudio() const
560 {
561     for (auto& mediaElement : m_mediaElements) {
562         if (mediaElement->hasAudio())
563             return true;
564     }
565     return false;
566 }
567
568 bool MediaController::hasVideo() const
569 {
570     for (auto& mediaElement : m_mediaElements) {
571         if (mediaElement->hasVideo())
572             return true;
573     }
574     return false;
575 }
576
577 bool MediaController::hasClosedCaptions() const
578 {
579     for (auto& mediaElement : m_mediaElements) {
580         if (mediaElement->hasClosedCaptions())
581             return true;
582     }
583     return false;
584 }
585
586 void MediaController::setClosedCaptionsVisible(bool visible)
587 {
588     m_closedCaptionsVisible = visible;
589     for (auto& mediaElement : m_mediaElements)
590         mediaElement->setClosedCaptionsVisible(visible);
591 }
592
593 bool MediaController::supportsScanning() const
594 {
595     for (auto& mediaElement : m_mediaElements) {
596         if (!mediaElement->supportsScanning())
597             return false;
598     }
599     return true;
600 }
601
602 void MediaController::beginScrubbing()
603 {
604     for (auto& mediaElement : m_mediaElements)
605         mediaElement->beginScrubbing();
606     if (m_playbackState == PLAYING)
607         m_clock->stop();
608 }
609
610 void MediaController::endScrubbing()
611 {
612     for (auto& mediaElement : m_mediaElements)
613         mediaElement->endScrubbing();
614     if (m_playbackState == PLAYING)
615         m_clock->start();
616 }
617
618 void MediaController::beginScanning(ScanDirection direction)
619 {
620     for (auto& mediaElement : m_mediaElements)
621         mediaElement->beginScanning(direction);
622 }
623
624 void MediaController::endScanning()
625 {
626     for (auto& mediaElement : m_mediaElements)
627         mediaElement->endScanning();
628 }
629
630 bool MediaController::canPlay() const
631 {
632     if (m_paused)
633         return true;
634
635     for (auto& mediaElement : m_mediaElements) {
636         if (!mediaElement->canPlay())
637             return false;
638     }
639     return true;
640 }
641
642 bool MediaController::isLiveStream() const
643 {
644     for (auto& mediaElement : m_mediaElements) {
645         if (!mediaElement->isLiveStream())
646             return false;
647     }
648     return true;
649 }
650
651 bool MediaController::hasCurrentSrc() const
652 {
653     for (auto& mediaElement : m_mediaElements) {
654         if (!mediaElement->hasCurrentSrc())
655             return false;
656     }
657     return true;
658 }
659
660 void MediaController::returnToRealtime()
661 {
662     for (auto& mediaElement : m_mediaElements)
663         mediaElement->returnToRealtime();
664 }
665
666 // The spec says to fire periodic timeupdate events (those sent while playing) every
667 // "15 to 250ms", we choose the slowest frequency
668 static const double maxTimeupdateEventFrequency = 0.25;
669
670 void MediaController::startTimeupdateTimer()
671 {
672     if (m_timeupdateTimer.isActive())
673         return;
674
675     m_timeupdateTimer.startRepeating(maxTimeupdateEventFrequency);
676 }
677
678 void MediaController::scheduleTimeupdateEvent()
679 {
680     double now = monotonicallyIncreasingTime();
681     double timedelta = now - m_previousTimeupdateTime;
682
683     if (timedelta < maxTimeupdateEventFrequency)
684         return;
685
686     scheduleEvent(eventNames().timeupdateEvent);
687     m_previousTimeupdateTime = now;
688 }
689
690 #endif