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