136ddce5eee0956543ba93b98f7a138aa99eb815
[WebKit-https.git] / Source / WebCore / platform / cocoa / WebPlaybackSessionModelMediaElement.mm
1 /*
2  * Copyright (C) 2016 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. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "WebPlaybackSessionModelMediaElement.h"
28
29 #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
30
31 #import "DOMEventInternal.h"
32 #import "Logging.h"
33 #import "MediaControlsHost.h"
34 #import "WebVideoFullscreenInterface.h"
35 #import <QuartzCore/CoreAnimation.h>
36 #import <WebCore/AudioTrackList.h>
37 #import <WebCore/DOMEventListener.h>
38 #import <WebCore/Event.h>
39 #import <WebCore/EventListener.h>
40 #import <WebCore/EventNames.h>
41 #import <WebCore/HTMLElement.h>
42 #import <WebCore/HTMLMediaElement.h>
43 #import <WebCore/Page.h>
44 #import <WebCore/PageGroup.h>
45 #import <WebCore/SoftLinking.h>
46 #import <WebCore/TextTrackList.h>
47 #import <WebCore/TimeRanges.h>
48 #import <wtf/NeverDestroyed.h>
49 #import <wtf/RetainPtr.h>
50
51 using namespace WebCore;
52
53 WebPlaybackSessionModelMediaElement::WebPlaybackSessionModelMediaElement()
54     : EventListener(EventListener::CPPEventListenerType)
55 {
56 }
57
58 WebPlaybackSessionModelMediaElement::~WebPlaybackSessionModelMediaElement()
59 {
60 }
61
62 void WebPlaybackSessionModelMediaElement::setWebPlaybackSessionInterface(WebPlaybackSessionInterface* interface)
63 {
64     if (interface == m_playbackSessionInterface)
65         return;
66
67     m_playbackSessionInterface = interface;
68
69     if (!m_playbackSessionInterface)
70         return;
71
72     m_playbackSessionInterface->resetMediaState();
73     m_playbackSessionInterface->setDuration(duration());
74     m_playbackSessionInterface->setCurrentTime(currentTime(), [[NSProcessInfo processInfo] systemUptime]);
75     m_playbackSessionInterface->setBufferedTime(bufferedTime());
76     m_playbackSessionInterface->setRate(isPlaying(), playbackRate());
77     m_playbackSessionInterface->setSeekableRanges(seekableRanges());
78     m_playbackSessionInterface->setCanPlayFastReverse(canPlayFastReverse());
79     m_playbackSessionInterface->setWirelessVideoPlaybackDisabled(wirelessVideoPlaybackDisabled());
80     updateLegibleOptions();
81 }
82
83 void WebPlaybackSessionModelMediaElement::setMediaElement(HTMLMediaElement* mediaElement)
84 {
85     if (m_mediaElement == mediaElement)
86         return;
87
88     if (m_playbackSessionInterface)
89         m_playbackSessionInterface->resetMediaState();
90
91     if (m_mediaElement && m_isListening) {
92         for (auto& eventName : observedEventNames())
93             m_mediaElement->removeEventListener(eventName, this, false);
94     }
95     m_isListening = false;
96
97     m_mediaElement = mediaElement;
98
99     if (!m_mediaElement)
100         return;
101
102     for (auto& eventName : observedEventNames())
103         m_mediaElement->addEventListener(eventName, this, false);
104     m_isListening = true;
105
106     updateForEventName(eventNameAll());
107
108     if (m_playbackSessionInterface)
109         m_playbackSessionInterface->setWirelessVideoPlaybackDisabled(m_mediaElement->mediaSession().wirelessVideoPlaybackDisabled(*m_mediaElement));
110 }
111
112 void WebPlaybackSessionModelMediaElement::handleEvent(WebCore::ScriptExecutionContext*, WebCore::Event* event)
113 {
114     updateForEventName(event->type());
115 }
116
117 void WebPlaybackSessionModelMediaElement::updateForEventName(const WTF::AtomicString& eventName)
118 {
119     if (!m_mediaElement || !m_playbackSessionInterface)
120         return;
121
122     bool all = eventName == eventNameAll();
123
124     if (all
125         || eventName == eventNames().durationchangeEvent) {
126         m_playbackSessionInterface->setDuration(m_mediaElement->duration());
127         // These is no standard event for minFastReverseRateChange; duration change is a reasonable proxy for it.
128         // It happens every time a new item becomes ready to play.
129         m_playbackSessionInterface->setCanPlayFastReverse(m_mediaElement->minFastReverseRate() < 0.0);
130     }
131
132     if (all
133         || eventName == eventNames().pauseEvent
134         || eventName == eventNames().playEvent
135         || eventName == eventNames().ratechangeEvent)
136         m_playbackSessionInterface->setRate(!m_mediaElement->paused(), m_mediaElement->playbackRate());
137
138     if (all
139         || eventName == eventNames().timeupdateEvent) {
140         m_playbackSessionInterface->setCurrentTime(m_mediaElement->currentTime(), [[NSProcessInfo processInfo] systemUptime]);
141         m_playbackSessionInterface->setBufferedTime(m_mediaElement->maxBufferedTime());
142         // FIXME: 130788 - find a better event to update seekable ranges from.
143         m_playbackSessionInterface->setSeekableRanges(m_mediaElement->seekable());
144     }
145
146     if (all
147         || eventName == eventNames().addtrackEvent
148         || eventName == eventNames().removetrackEvent)
149         updateLegibleOptions();
150
151     if (all
152         || eventName == eventNames().webkitcurrentplaybacktargetiswirelesschangedEvent) {
153         bool enabled = m_mediaElement->webkitCurrentPlaybackTargetIsWireless();
154         WebPlaybackSessionInterface::ExternalPlaybackTargetType targetType = WebPlaybackSessionInterface::TargetTypeNone;
155         String localizedDeviceName;
156
157         if (m_mediaElement->mediaControlsHost()) {
158             auto type = m_mediaElement->mediaControlsHost()->externalDeviceType();
159             if (type == MediaControlsHost::DeviceType::Airplay)
160                 targetType = WebPlaybackSessionInterface::TargetTypeAirPlay;
161             else if (type == MediaControlsHost::DeviceType::Tvout)
162                 targetType = WebPlaybackSessionInterface::TargetTypeTVOut;
163             localizedDeviceName = m_mediaElement->mediaControlsHost()->externalDeviceDisplayName();
164         }
165         m_playbackSessionInterface->setExternalPlayback(enabled, targetType, localizedDeviceName);
166         m_playbackSessionInterface->setWirelessVideoPlaybackDisabled(m_mediaElement->mediaSession().wirelessVideoPlaybackDisabled(*m_mediaElement));
167     }
168 }
169
170 void WebPlaybackSessionModelMediaElement::play()
171 {
172     if (m_mediaElement)
173         m_mediaElement->play();
174 }
175
176 void WebPlaybackSessionModelMediaElement::pause()
177 {
178     if (m_mediaElement)
179         m_mediaElement->pause();
180 }
181
182 void WebPlaybackSessionModelMediaElement::togglePlayState()
183 {
184     if (m_mediaElement)
185         m_mediaElement->togglePlayState();
186 }
187
188 void WebPlaybackSessionModelMediaElement::beginScrubbing()
189 {
190     if (m_mediaElement)
191         m_mediaElement->beginScrubbing();
192 }
193
194 void WebPlaybackSessionModelMediaElement::endScrubbing()
195 {
196     if (m_mediaElement)
197         m_mediaElement->endScrubbing();
198 }
199
200 void WebPlaybackSessionModelMediaElement::seekToTime(double time)
201 {
202     if (m_mediaElement)
203         m_mediaElement->setCurrentTime(time);
204 }
205
206 void WebPlaybackSessionModelMediaElement::fastSeek(double time)
207 {
208     if (m_mediaElement)
209         m_mediaElement->fastSeek(time);
210 }
211
212 void WebPlaybackSessionModelMediaElement::beginScanningForward()
213 {
214     if (m_mediaElement)
215         m_mediaElement->beginScanning(MediaControllerInterface::Forward);
216 }
217
218 void WebPlaybackSessionModelMediaElement::beginScanningBackward()
219 {
220     if (m_mediaElement)
221         m_mediaElement->beginScanning(MediaControllerInterface::Backward);
222 }
223
224 void WebPlaybackSessionModelMediaElement::endScanning()
225 {
226     if (m_mediaElement)
227         m_mediaElement->endScanning();
228 }
229
230 void WebPlaybackSessionModelMediaElement::selectAudioMediaOption(uint64_t selectedAudioIndex)
231 {
232     ASSERT(selectedAudioIndex < std::numeric_limits<size_t>::max());
233     AudioTrack* selectedAudioTrack = nullptr;
234
235     for (size_t index = 0; index < m_audioTracksForMenu.size(); ++index) {
236         auto& audioTrack = m_audioTracksForMenu[index];
237         audioTrack->setEnabled(index == static_cast<size_t>(selectedAudioIndex));
238         if (audioTrack->enabled())
239             selectedAudioTrack = audioTrack.get();
240     }
241
242     m_mediaElement->audioTrackEnabledChanged(selectedAudioTrack);
243 }
244
245 void WebPlaybackSessionModelMediaElement::selectLegibleMediaOption(uint64_t index)
246 {
247     ASSERT(index < std::numeric_limits<size_t>::max());
248     TextTrack* textTrack = nullptr;
249
250     if (index < m_legibleTracksForMenu.size())
251         textTrack = m_legibleTracksForMenu[static_cast<size_t>(index)].get();
252     else
253         textTrack = TextTrack::captionMenuOffItem();
254
255     m_mediaElement->setSelectedTextTrack(textTrack);
256 }
257
258 void WebPlaybackSessionModelMediaElement::updateLegibleOptions()
259 {
260     if (!m_mediaElement)
261         return;
262
263     if (!m_mediaElement->document().page())
264         return;
265
266     auto& captionPreferences = m_mediaElement->document().page()->group().captionPreferences();
267     auto& textTracks = m_mediaElement->textTracks();
268     if (textTracks.length())
269         m_legibleTracksForMenu = captionPreferences.sortedTrackListForMenu(&textTracks);
270     else
271         m_legibleTracksForMenu.clear();
272
273     auto& audioTracks = m_mediaElement->audioTracks();
274     if (audioTracks.length() > 1)
275         m_audioTracksForMenu = captionPreferences.sortedTrackListForMenu(&audioTracks);
276     else
277         m_audioTracksForMenu.clear();
278
279     m_playbackSessionInterface->setAudioMediaSelectionOptions(audioMediaSelectionOptions(), audioMediaSelectedIndex());
280     m_playbackSessionInterface->setLegibleMediaSelectionOptions(legibleMediaSelectionOptions(), legibleMediaSelectedIndex());
281 }
282
283 const Vector<AtomicString>&  WebPlaybackSessionModelMediaElement::observedEventNames()
284 {
285     static NeverDestroyed<Vector<AtomicString>> sEventNames;
286
287     if (!sEventNames.get().size()) {
288         sEventNames.get().append(eventNames().durationchangeEvent);
289         sEventNames.get().append(eventNames().pauseEvent);
290         sEventNames.get().append(eventNames().playEvent);
291         sEventNames.get().append(eventNames().ratechangeEvent);
292         sEventNames.get().append(eventNames().timeupdateEvent);
293         sEventNames.get().append(eventNames().addtrackEvent);
294         sEventNames.get().append(eventNames().removetrackEvent);
295         sEventNames.get().append(eventNames().webkitcurrentplaybacktargetiswirelesschangedEvent);
296     }
297     return sEventNames.get();
298 }
299
300 const AtomicString&  WebPlaybackSessionModelMediaElement::eventNameAll()
301 {
302     static NeverDestroyed<AtomicString> sEventNameAll = "allEvents";
303     return sEventNameAll;
304 }
305
306 double WebPlaybackSessionModelMediaElement::duration() const
307 {
308     return m_mediaElement ? m_mediaElement->duration() : 0;
309 }
310
311 double WebPlaybackSessionModelMediaElement::currentTime() const
312 {
313     return m_mediaElement ? m_mediaElement->currentTime() : 0;
314 }
315
316 double WebPlaybackSessionModelMediaElement::bufferedTime() const
317 {
318     return m_mediaElement ? m_mediaElement->maxBufferedTime() : 0;
319 }
320
321 bool WebPlaybackSessionModelMediaElement::isPlaying() const
322 {
323     return m_mediaElement ? !m_mediaElement->paused() : false;
324 }
325
326 float WebPlaybackSessionModelMediaElement::playbackRate() const
327 {
328     return m_mediaElement ? m_mediaElement->playbackRate() : 0;
329 }
330
331 Ref<TimeRanges> WebPlaybackSessionModelMediaElement::seekableRanges() const
332 {
333     return m_mediaElement ? m_mediaElement->seekable() : TimeRanges::create();
334 }
335
336 bool WebPlaybackSessionModelMediaElement::canPlayFastReverse() const
337 {
338     return m_mediaElement ? m_mediaElement->minFastReverseRate() < 0.0 : false;
339 }
340
341 Vector<WTF::String> WebPlaybackSessionModelMediaElement::audioMediaSelectionOptions() const
342 {
343     Vector<String> audioTrackDisplayNames;
344
345     if (!m_mediaElement || !m_mediaElement->document().page())
346         return audioTrackDisplayNames;
347
348     auto& captionPreferences = m_mediaElement->document().page()->group().captionPreferences();
349
350     for (auto& audioTrack : m_audioTracksForMenu)
351         audioTrackDisplayNames.append(captionPreferences.displayNameForTrack(audioTrack.get()));
352
353     return audioTrackDisplayNames;
354 }
355
356 uint64_t WebPlaybackSessionModelMediaElement::audioMediaSelectedIndex() const
357 {
358     for (size_t index = 0; index < m_audioTracksForMenu.size(); ++index) {
359         if (m_audioTracksForMenu[index]->enabled())
360             return index;
361     }
362     return 0;
363 }
364
365 Vector<WTF::String> WebPlaybackSessionModelMediaElement::legibleMediaSelectionOptions() const
366 {
367     Vector<String> trackDisplayNames;
368
369     if (!m_mediaElement || !m_mediaElement->document().page())
370         return trackDisplayNames;
371
372     auto& captionPreferences = m_mediaElement->document().page()->group().captionPreferences();
373
374     for (auto& track : m_legibleTracksForMenu)
375         trackDisplayNames.append(captionPreferences.displayNameForTrack(track.get()));
376
377     return trackDisplayNames;
378 }
379
380 uint64_t WebPlaybackSessionModelMediaElement::legibleMediaSelectedIndex() const
381 {
382     uint64_t selectedIndex = 0;
383     uint64_t offIndex = 0;
384     bool trackMenuItemSelected = false;
385
386     if (!m_mediaElement || !m_mediaElement->mediaControlsHost())
387         return selectedIndex;
388
389     AtomicString displayMode = m_mediaElement->mediaControlsHost()->captionDisplayMode();
390     TextTrack* offItem = m_mediaElement->mediaControlsHost()->captionMenuOffItem();
391     TextTrack* automaticItem = m_mediaElement->mediaControlsHost()->captionMenuAutomaticItem();
392
393     for (size_t index = 0; index < m_legibleTracksForMenu.size(); index++) {
394         auto& track = m_legibleTracksForMenu[index];
395         if (track == offItem)
396             offIndex = index;
397
398         if (track == automaticItem && displayMode == MediaControlsHost::automaticKeyword()) {
399             selectedIndex = index;
400             trackMenuItemSelected = true;
401         }
402
403         if (displayMode != MediaControlsHost::automaticKeyword() && track->mode() == TextTrack::Mode::Showing) {
404             selectedIndex = index;
405             trackMenuItemSelected = true;
406         }
407     }
408
409     if (offIndex && !trackMenuItemSelected && displayMode == MediaControlsHost::forcedOnlyKeyword())
410         selectedIndex = offIndex;
411
412     return selectedIndex;
413 }
414
415 bool WebPlaybackSessionModelMediaElement::externalPlaybackEnabled() const
416 {
417     return m_mediaElement ? m_mediaElement->webkitCurrentPlaybackTargetIsWireless() : false;
418 }
419
420 bool WebPlaybackSessionModelMediaElement::wirelessVideoPlaybackDisabled() const
421 {
422     return m_mediaElement ? m_mediaElement->mediaSession().wirelessVideoPlaybackDisabled(*m_mediaElement) : false;
423 }
424
425 #endif