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