Media elements are allowed to continue to load media data after navigation
[WebKit-https.git] / Source / WebCore / platform / audio / PlatformMediaSession.cpp
1 /*
2  * Copyright (C) 2014-2015 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 #include "config.h"
27 #include "PlatformMediaSession.h"
28
29 #if ENABLE(VIDEO) || ENABLE(WEB_AUDIO)
30 #include "HTMLMediaElement.h"
31 #include "Logging.h"
32 #include "MediaPlayer.h"
33 #include "PlatformMediaSessionManager.h"
34
35 namespace WebCore {
36
37 static const Seconds clientDataBufferingTimerThrottleDelay { 100_ms };
38
39 #if !LOG_DISABLED
40 static const char* stateName(PlatformMediaSession::State state)
41 {
42 #define STATE_CASE(state) case PlatformMediaSession::state: return #state
43     switch (state) {
44     STATE_CASE(Idle);
45     STATE_CASE(Autoplaying);
46     STATE_CASE(Playing);
47     STATE_CASE(Paused);
48     STATE_CASE(Interrupted);
49     }
50
51     ASSERT_NOT_REACHED();
52     return "";
53 }
54
55 static const char* interruptionName(PlatformMediaSession::InterruptionType type)
56 {
57 #define INTERRUPTION_CASE(type) case PlatformMediaSession::type: return #type
58     switch (type) {
59     INTERRUPTION_CASE(NoInterruption);
60     INTERRUPTION_CASE(SystemSleep);
61     INTERRUPTION_CASE(EnteringBackground);
62     INTERRUPTION_CASE(SystemInterruption);
63     INTERRUPTION_CASE(SuspendedUnderLock);
64     INTERRUPTION_CASE(InvisibleAutoplay);
65     }
66     
67     ASSERT_NOT_REACHED();
68     return "";
69 }
70 #endif
71
72 std::unique_ptr<PlatformMediaSession> PlatformMediaSession::create(PlatformMediaSessionClient& client)
73 {
74     return std::make_unique<PlatformMediaSession>(client);
75 }
76
77 PlatformMediaSession::PlatformMediaSession(PlatformMediaSessionClient& client)
78     : m_client(client)
79     , m_clientDataBufferingTimer(*this, &PlatformMediaSession::clientDataBufferingTimerFired)
80     , m_state(Idle)
81     , m_stateToRestore(Idle)
82     , m_notifyingClient(false)
83 {
84     ASSERT(m_client.mediaType() >= None && m_client.mediaType() <= MediaStreamCapturingAudio);
85     PlatformMediaSessionManager::sharedManager().addSession(*this);
86 }
87
88 PlatformMediaSession::~PlatformMediaSession()
89 {
90     PlatformMediaSessionManager::sharedManager().removeSession(*this);
91 }
92
93 void PlatformMediaSession::setState(State state)
94 {
95     LOG(Media, "PlatformMediaSession::setState(%p) - %s", this, stateName(state));
96     m_state = state;
97 }
98
99 void PlatformMediaSession::beginInterruption(InterruptionType type)
100 {
101     LOG(Media, "PlatformMediaSession::beginInterruption(%p), state = %s, interruption type = %s, interruption count = %i", this, stateName(m_state), interruptionName(type), m_interruptionCount);
102
103     // When interruptions are overridden, m_interruptionType doesn't get set.
104     // Give nested interruptions a chance when the previous interruptions were overridden.
105     if (++m_interruptionCount > 1 && m_interruptionType != NoInterruption)
106         return;
107
108     if (client().shouldOverrideBackgroundPlaybackRestriction(type)) {
109         LOG(Media, "PlatformMediaSession::beginInterruption(%p), returning early because client says to override interruption", this);
110         return;
111     }
112
113     m_stateToRestore = state();
114     m_notifyingClient = true;
115     setState(Interrupted);
116     m_interruptionType = type;
117     client().suspendPlayback();
118     m_notifyingClient = false;
119 }
120
121 void PlatformMediaSession::endInterruption(EndInterruptionFlags flags)
122 {
123     LOG(Media, "PlatformMediaSession::endInterruption(%p) - flags = %i, stateToRestore = %s, interruption count = %i", this, (int)flags, stateName(m_stateToRestore), m_interruptionCount);
124
125     if (!m_interruptionCount) {
126         LOG(Media, "PlatformMediaSession::endInterruption(%p) - !! ignoring spurious interruption end !!", this);
127         return;
128     }
129
130     if (--m_interruptionCount)
131         return;
132
133     State stateToRestore = m_stateToRestore;
134     m_stateToRestore = Idle;
135     m_interruptionType = NoInterruption;
136     setState(stateToRestore);
137
138     if (stateToRestore == Autoplaying)
139         client().resumeAutoplaying();
140
141     bool shouldResume = flags & MayResumePlaying && stateToRestore == Playing;
142     client().mayResumePlayback(shouldResume);
143 }
144
145 void PlatformMediaSession::clientWillBeginAutoplaying()
146 {
147     if (m_notifyingClient)
148         return;
149
150     LOG(Media, "PlatformMediaSession::clientWillBeginAutoplaying(%p)- state = %s", this, stateName(m_state));
151     if (state() == Interrupted) {
152         m_stateToRestore = Autoplaying;
153         LOG(Media, "      setting stateToRestore to \"Autoplaying\"");
154         return;
155     }
156
157     setState(Autoplaying);
158     updateClientDataBuffering();
159 }
160
161 bool PlatformMediaSession::clientWillBeginPlayback()
162 {
163     if (m_notifyingClient)
164         return true;
165
166     if (!PlatformMediaSessionManager::sharedManager().sessionWillBeginPlayback(*this)) {
167         if (state() == Interrupted)
168             m_stateToRestore = Playing;
169         return false;
170     }
171
172     setState(Playing);
173     updateClientDataBuffering();
174     return true;
175 }
176
177 bool PlatformMediaSession::clientWillPausePlayback()
178 {
179     if (m_notifyingClient)
180         return true;
181
182     LOG(Media, "PlatformMediaSession::clientWillPausePlayback(%p)- state = %s", this, stateName(m_state));
183     if (state() == Interrupted) {
184         m_stateToRestore = Paused;
185         LOG(Media, "      setting stateToRestore to \"Paused\"");
186         return false;
187     }
188     
189     setState(Paused);
190     PlatformMediaSessionManager::sharedManager().sessionWillEndPlayback(*this);
191     scheduleClientDataBufferingCheck();
192     return true;
193 }
194
195 void PlatformMediaSession::pauseSession()
196 {
197     LOG(Media, "PlatformMediaSession::pauseSession(%p)", this);
198     m_client.suspendPlayback();
199 }
200
201 void PlatformMediaSession::stopSession()
202 {
203     LOG(Media, "PlatformMediaSession::stopSession(%p)", this);
204     m_client.suspendPlayback();
205     PlatformMediaSessionManager::sharedManager().removeSession(*this);
206 }
207
208 PlatformMediaSession::MediaType PlatformMediaSession::mediaType() const
209 {
210     return m_client.mediaType();
211 }
212
213 PlatformMediaSession::MediaType PlatformMediaSession::presentationType() const
214 {
215     return m_client.presentationType();
216 }
217
218 PlatformMediaSession::CharacteristicsFlags PlatformMediaSession::characteristics() const
219 {
220     return m_client.characteristics();
221 }
222
223 #if ENABLE(VIDEO)
224 String PlatformMediaSession::title() const
225 {
226     return m_client.mediaSessionTitle();
227 }
228
229 double PlatformMediaSession::duration() const
230 {
231     return m_client.mediaSessionDuration();
232 }
233
234 double PlatformMediaSession::currentTime() const
235 {
236     return m_client.mediaSessionCurrentTime();
237 }
238 #endif
239     
240 bool PlatformMediaSession::canReceiveRemoteControlCommands() const
241 {
242     return m_client.canReceiveRemoteControlCommands();
243 }
244
245 void PlatformMediaSession::didReceiveRemoteControlCommand(RemoteControlCommandType command, const PlatformMediaSession::RemoteCommandArgument* argument)
246 {
247     m_client.didReceiveRemoteControlCommand(command, argument);
248 }
249
250 bool PlatformMediaSession::supportsSeeking() const
251 {
252     return m_client.supportsSeeking();
253 }
254
255 void PlatformMediaSession::visibilityChanged()
256 {
257     scheduleClientDataBufferingCheck();
258 }
259
260 void PlatformMediaSession::scheduleClientDataBufferingCheck()
261 {
262     if (!m_clientDataBufferingTimer.isActive())
263         m_clientDataBufferingTimer.startOneShot(clientDataBufferingTimerThrottleDelay);
264 }
265
266 void PlatformMediaSession::clientDataBufferingTimerFired()
267 {
268     LOG(Media, "PlatformMediaSession::clientDataBufferingTimerFired(%p)- visible = %s", this, m_client.elementIsHidden() ? "false" : "true");
269
270     updateClientDataBuffering();
271
272 #if PLATFORM(IOS)
273     PlatformMediaSessionManager::sharedManager().configureWireLessTargetMonitoring();
274 #endif
275
276     if (m_state != Playing || !m_client.elementIsHidden())
277         return;
278
279     PlatformMediaSessionManager::SessionRestrictions restrictions = PlatformMediaSessionManager::sharedManager().restrictions(mediaType());
280     if ((restrictions & PlatformMediaSessionManager::BackgroundTabPlaybackRestricted) == PlatformMediaSessionManager::BackgroundTabPlaybackRestricted)
281         pauseSession();
282 }
283
284 void PlatformMediaSession::updateClientDataBuffering()
285 {
286     if (m_clientDataBufferingTimer.isActive())
287         m_clientDataBufferingTimer.stop();
288
289     m_client.setShouldBufferData(PlatformMediaSessionManager::sharedManager().sessionCanLoadMedia(*this));
290 }
291
292 String PlatformMediaSession::sourceApplicationIdentifier() const
293 {
294     return m_client.sourceApplicationIdentifier();
295 }
296
297 bool PlatformMediaSession::isHidden() const
298 {
299     return m_client.elementIsHidden();
300 }
301
302 bool PlatformMediaSession::isSuspended() const
303 {
304     return m_client.isSuspended();
305 }
306
307 bool PlatformMediaSession::shouldOverrideBackgroundLoadingRestriction() const
308 {
309     return m_client.shouldOverrideBackgroundLoadingRestriction();
310 }
311
312 void PlatformMediaSession::isPlayingToWirelessPlaybackTargetChanged(bool isWireless)
313 {
314     if (isWireless == m_isPlayingToWirelessPlaybackTarget)
315         return;
316
317     m_isPlayingToWirelessPlaybackTarget = isWireless;
318
319     // Save and restore the interruption count so it doesn't get out of sync if beginInterruption is called because
320     // if we in the background.
321     int interruptionCount = m_interruptionCount;
322     PlatformMediaSessionManager::sharedManager().sessionIsPlayingToWirelessPlaybackTargetChanged(*this);
323     m_interruptionCount = interruptionCount;
324 }
325
326 PlatformMediaSession::DisplayType PlatformMediaSession::displayType() const
327 {
328     return m_client.displayType();
329 }
330
331 bool PlatformMediaSession::activeAudioSessionRequired()
332 {
333     if (mediaType() == PlatformMediaSession::None)
334         return false;
335     if (state() != PlatformMediaSession::State::Playing)
336         return false;
337     return canProduceAudio();
338 }
339
340 bool PlatformMediaSession::canProduceAudio() const
341 {
342     return m_client.canProduceAudio();
343 }
344
345 void PlatformMediaSession::canProduceAudioChanged()
346 {
347     PlatformMediaSessionManager::sharedManager().sessionCanProduceAudioChanged(*this);
348 }
349
350 #if ENABLE(VIDEO)
351 String PlatformMediaSessionClient::mediaSessionTitle() const
352 {
353     return String();
354 }
355
356 double PlatformMediaSessionClient::mediaSessionDuration() const
357 {
358     return MediaPlayer::invalidTime();
359 }
360
361 double PlatformMediaSessionClient::mediaSessionCurrentTime() const
362 {
363     return MediaPlayer::invalidTime();
364 }
365 #endif
366
367 void PlatformMediaSession::clientCharacteristicsChanged()
368 {
369     PlatformMediaSessionManager::sharedManager().clientCharacteristicsChanged(*this);
370 }
371
372 }
373 #endif