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
35namespace WebCore {
36
37static const Seconds clientDataBufferingTimerThrottleDelay { 100_ms };
38
39#if !RELEASE_LOG_DISABLED
40String convertEnumerationToString(PlatformMediaSession::State state)
41{
42 static const NeverDestroyed<String> values[] = {
43 MAKE_STATIC_STRING_IMPL("Idle"),
44 MAKE_STATIC_STRING_IMPL("Autoplaying"),
45 MAKE_STATIC_STRING_IMPL("Playing"),
46 MAKE_STATIC_STRING_IMPL("Paused"),
47 MAKE_STATIC_STRING_IMPL("Interrupted"),
48 };
49 static_assert(!static_cast<size_t>(PlatformMediaSession::Idle), "PlatformMediaSession::Idle is not 0 as expected");
50 static_assert(static_cast<size_t>(PlatformMediaSession::Autoplaying) == 1, "PlatformMediaSession::Autoplaying is not 1 as expected");
51 static_assert(static_cast<size_t>(PlatformMediaSession::Playing) == 2, "PlatformMediaSession::Playing is not 2 as expected");
52 static_assert(static_cast<size_t>(PlatformMediaSession::Paused) == 3, "PlatformMediaSession::Paused is not 3 as expected");
53 static_assert(static_cast<size_t>(PlatformMediaSession::Interrupted) == 4, "PlatformMediaSession::Interrupted is not 4 as expected");
54 ASSERT(static_cast<size_t>(state) < WTF_ARRAY_LENGTH(values));
55 return values[static_cast<size_t>(state)];
56}
57
58String convertEnumerationToString(PlatformMediaSession::InterruptionType type)
59{
60 static const NeverDestroyed<String> values[] = {
61 MAKE_STATIC_STRING_IMPL("NoInterruption"),
62 MAKE_STATIC_STRING_IMPL("SystemSleep"),
63 MAKE_STATIC_STRING_IMPL("EnteringBackground"),
64 MAKE_STATIC_STRING_IMPL("SystemInterruption"),
65 MAKE_STATIC_STRING_IMPL("SuspendedUnderLock"),
66 MAKE_STATIC_STRING_IMPL("InvisibleAutoplay"),
67 MAKE_STATIC_STRING_IMPL("ProcessInactive"),
68 };
69 static_assert(!static_cast<size_t>(PlatformMediaSession::NoInterruption), "PlatformMediaSession::NoInterruption is not 0 as expected");
70 static_assert(static_cast<size_t>(PlatformMediaSession::SystemSleep) == 1, "PlatformMediaSession::SystemSleep is not 1 as expected");
71 static_assert(static_cast<size_t>(PlatformMediaSession::EnteringBackground) == 2, "PlatformMediaSession::EnteringBackground is not 2 as expected");
72 static_assert(static_cast<size_t>(PlatformMediaSession::SystemInterruption) == 3, "PlatformMediaSession::SystemInterruption is not 3 as expected");
73 static_assert(static_cast<size_t>(PlatformMediaSession::SuspendedUnderLock) == 4, "PlatformMediaSession::SuspendedUnderLock is not 4 as expected");
74 static_assert(static_cast<size_t>(PlatformMediaSession::InvisibleAutoplay) == 5, "PlatformMediaSession::InvisibleAutoplay is not 5 as expected");
75 static_assert(static_cast<size_t>(PlatformMediaSession::ProcessInactive) == 6, "PlatformMediaSession::ProcessInactive is not 6 as expected");
76 ASSERT(static_cast<size_t>(type) < WTF_ARRAY_LENGTH(values));
77 return values[static_cast<size_t>(type)];
78}
79
80String convertEnumerationToString(PlatformMediaSession::RemoteControlCommandType command)
81{
82 static const NeverDestroyed<String> values[] = {
83 MAKE_STATIC_STRING_IMPL("NoCommand"),
84 MAKE_STATIC_STRING_IMPL("PlayCommand"),
85 MAKE_STATIC_STRING_IMPL("PauseCommand"),
86 MAKE_STATIC_STRING_IMPL("StopCommand"),
87 MAKE_STATIC_STRING_IMPL("TogglePlayPauseCommand"),
88 MAKE_STATIC_STRING_IMPL("BeginSeekingBackwardCommand"),
89 MAKE_STATIC_STRING_IMPL("EndSeekingBackwardCommand"),
90 MAKE_STATIC_STRING_IMPL("BeginSeekingForwardCommand"),
91 MAKE_STATIC_STRING_IMPL("EndSeekingForwardCommand"),
92 MAKE_STATIC_STRING_IMPL("SeekToPlaybackPositionCommand"),
93 };
94 static_assert(!static_cast<size_t>(PlatformMediaSession::NoCommand), "PlatformMediaSession::NoCommand is not 0 as expected");
95 static_assert(static_cast<size_t>(PlatformMediaSession::PlayCommand) == 1, "PlatformMediaSession::PlayCommand is not 1 as expected");
96 static_assert(static_cast<size_t>(PlatformMediaSession::PauseCommand) == 2, "PlatformMediaSession::PauseCommand is not 2 as expected");
97 static_assert(static_cast<size_t>(PlatformMediaSession::StopCommand) == 3, "PlatformMediaSession::StopCommand is not 3 as expected");
98 static_assert(static_cast<size_t>(PlatformMediaSession::TogglePlayPauseCommand) == 4, "PlatformMediaSession::TogglePlayPauseCommand is not 4 as expected");
99 static_assert(static_cast<size_t>(PlatformMediaSession::BeginSeekingBackwardCommand) == 5, "PlatformMediaSession::BeginSeekingBackwardCommand is not 5 as expected");
100 static_assert(static_cast<size_t>(PlatformMediaSession::EndSeekingBackwardCommand) == 6, "PlatformMediaSession::EndSeekingBackwardCommand is not 6 as expected");
101 static_assert(static_cast<size_t>(PlatformMediaSession::BeginSeekingForwardCommand) == 7, "PlatformMediaSession::BeginSeekingForwardCommand is not 7 as expected");
102 static_assert(static_cast<size_t>(PlatformMediaSession::EndSeekingForwardCommand) == 8, "PlatformMediaSession::EndSeekingForwardCommand is not 8 as expected");
103 static_assert(static_cast<size_t>(PlatformMediaSession::SeekToPlaybackPositionCommand) == 9, "PlatformMediaSession::SeekToPlaybackPositionCommand is not 9 as expected");
104 ASSERT(static_cast<size_t>(command) < WTF_ARRAY_LENGTH(values));
105 return values[static_cast<size_t>(command)];
106}
107
108#endif
109
110std::unique_ptr<PlatformMediaSession> PlatformMediaSession::create(PlatformMediaSessionClient& client)
111{
112 return std::make_unique<PlatformMediaSession>(client);
113}
114
115PlatformMediaSession::PlatformMediaSession(PlatformMediaSessionClient& client)
116 : m_client(client)
117 , m_state(Idle)
118 , m_stateToRestore(Idle)
119 , m_notifyingClient(false)
120#if !RELEASE_LOG_DISABLED
121 , m_logger(client.hostingDocument()->logger())
122 , m_logIdentifier(uniqueLogIdentifier())
123#endif
124{
125 ASSERT(m_client.mediaType() >= None && m_client.mediaType() <= MediaStreamCapturingAudio);
126 PlatformMediaSessionManager::sharedManager().addSession(*this);
127}
128
129PlatformMediaSession::~PlatformMediaSession()
130{
131 PlatformMediaSessionManager::sharedManager().removeSession(*this);
132}
133
134void PlatformMediaSession::setState(State state)
135{
136 if (state == m_state)
137 return;
138
139 INFO_LOG(LOGIDENTIFIER, state);
140 m_state = state;
141 if (m_state == State::Playing)
142 m_hasPlayedSinceLastInterruption = true;
143 PlatformMediaSessionManager::sharedManager().sessionStateChanged(*this);
144}
145
146void PlatformMediaSession::beginInterruption(InterruptionType type)
147{
148 INFO_LOG(LOGIDENTIFIER, "state = ", m_state, ", interruption type = ", type, ", interruption count = ", m_interruptionCount);
149
150 // When interruptions are overridden, m_interruptionType doesn't get set.
151 // Give nested interruptions a chance when the previous interruptions were overridden.
152 if (++m_interruptionCount > 1 && m_interruptionType != NoInterruption)
153 return;
154
155 if (client().shouldOverrideBackgroundPlaybackRestriction(type)) {
156 INFO_LOG(LOGIDENTIFIER, "returning early because client says to override interruption");
157 return;
158 }
159
160 m_stateToRestore = state();
161 m_notifyingClient = true;
162 setState(Interrupted);
163 m_interruptionType = type;
164 client().suspendPlayback();
165 m_notifyingClient = false;
166}
167
168void PlatformMediaSession::endInterruption(EndInterruptionFlags flags)
169{
170 INFO_LOG(LOGIDENTIFIER, "flags = ", (int)flags, ", stateToRestore = ", m_stateToRestore, ", interruption count = ", m_interruptionCount);
171
172 if (!m_interruptionCount) {
173 INFO_LOG(LOGIDENTIFIER, "!! ignoring spurious interruption end !!");
174 return;
175 }
176
177 if (--m_interruptionCount)
178 return;
179
180 if (m_interruptionType == NoInterruption)
181 return;
182
183 State stateToRestore = m_stateToRestore;
184 m_stateToRestore = Idle;
185 m_interruptionType = NoInterruption;
186 setState(stateToRestore);
187
188 if (stateToRestore == Autoplaying)
189 client().resumeAutoplaying();
190
191 bool shouldResume = flags & MayResumePlaying && stateToRestore == Playing;
192 client().mayResumePlayback(shouldResume);
193}
194
195void PlatformMediaSession::clientWillBeginAutoplaying()
196{
197 if (m_notifyingClient)
198 return;
199
200 INFO_LOG(LOGIDENTIFIER, "state = ", m_state);
201 if (state() == Interrupted) {
202 m_stateToRestore = Autoplaying;
203 INFO_LOG(LOGIDENTIFIER, " setting stateToRestore to \"Autoplaying\"");
204 return;
205 }
206
207 setState(Autoplaying);
208}
209
210bool PlatformMediaSession::clientWillBeginPlayback()
211{
212 if (m_notifyingClient)
213 return true;
214
215 INFO_LOG(LOGIDENTIFIER, "state = ", m_state);
216
217 if (!PlatformMediaSessionManager::sharedManager().sessionWillBeginPlayback(*this)) {
218 if (state() == Interrupted)
219 m_stateToRestore = Playing;
220 return false;
221 }
222
223 setState(Playing);
224 return true;
225}
226
227bool PlatformMediaSession::clientWillPausePlayback()
228{
229 if (m_notifyingClient)
230 return true;
231
232 INFO_LOG(LOGIDENTIFIER, "state = ", m_state);
233 if (state() == Interrupted) {
234 m_stateToRestore = Paused;
235 INFO_LOG(LOGIDENTIFIER, " setting stateToRestore to \"Paused\"");
236 return false;
237 }
238
239 setState(Paused);
240 PlatformMediaSessionManager::sharedManager().sessionWillEndPlayback(*this);
241 return true;
242}
243
244void PlatformMediaSession::pauseSession()
245{
246 INFO_LOG(LOGIDENTIFIER);
247 m_client.suspendPlayback();
248}
249
250void PlatformMediaSession::stopSession()
251{
252 INFO_LOG(LOGIDENTIFIER);
253 m_client.suspendPlayback();
254 PlatformMediaSessionManager::sharedManager().removeSession(*this);
255}
256
257PlatformMediaSession::MediaType PlatformMediaSession::mediaType() const
258{
259 return m_client.mediaType();
260}
261
262PlatformMediaSession::MediaType PlatformMediaSession::presentationType() const
263{
264 return m_client.presentationType();
265}
266
267PlatformMediaSession::CharacteristicsFlags PlatformMediaSession::characteristics() const
268{
269 return m_client.characteristics();
270}
271
272#if ENABLE(VIDEO)
273uint64_t PlatformMediaSession::uniqueIdentifier() const
274{
275 return m_client.mediaSessionUniqueIdentifier();
276}
277
278String PlatformMediaSession::title() const
279{
280 return m_client.mediaSessionTitle();
281}
282
283double PlatformMediaSession::duration() const
284{
285 return m_client.mediaSessionDuration();
286}
287
288double PlatformMediaSession::currentTime() const
289{
290 return m_client.mediaSessionCurrentTime();
291}
292#endif
293
294bool PlatformMediaSession::canReceiveRemoteControlCommands() const
295{
296 return m_client.canReceiveRemoteControlCommands();
297}
298
299void PlatformMediaSession::didReceiveRemoteControlCommand(RemoteControlCommandType command, const PlatformMediaSession::RemoteCommandArgument* argument)
300{
301 INFO_LOG(LOGIDENTIFIER, command);
302
303 m_client.didReceiveRemoteControlCommand(command, argument);
304}
305
306bool PlatformMediaSession::supportsSeeking() const
307{
308 return m_client.supportsSeeking();
309}
310
311String PlatformMediaSession::sourceApplicationIdentifier() const
312{
313 return m_client.sourceApplicationIdentifier();
314}
315
316bool PlatformMediaSession::isSuspended() const
317{
318 return m_client.isSuspended();
319}
320
321bool PlatformMediaSession::shouldOverrideBackgroundLoadingRestriction() const
322{
323 return m_client.shouldOverrideBackgroundLoadingRestriction();
324}
325
326void PlatformMediaSession::isPlayingToWirelessPlaybackTargetChanged(bool isWireless)
327{
328 if (isWireless == m_isPlayingToWirelessPlaybackTarget)
329 return;
330
331 m_isPlayingToWirelessPlaybackTarget = isWireless;
332
333 // Save and restore the interruption count so it doesn't get out of sync if beginInterruption is called because
334 // if we in the background.
335 int interruptionCount = m_interruptionCount;
336 PlatformMediaSessionManager::sharedManager().sessionIsPlayingToWirelessPlaybackTargetChanged(*this);
337 m_interruptionCount = interruptionCount;
338}
339
340PlatformMediaSession::DisplayType PlatformMediaSession::displayType() const
341{
342 return m_client.displayType();
343}
344
345bool PlatformMediaSession::activeAudioSessionRequired()
346{
347 if (mediaType() == PlatformMediaSession::None)
348 return false;
349 if (state() != PlatformMediaSession::State::Playing)
350 return false;
351 return canProduceAudio();
352}
353
354bool PlatformMediaSession::canProduceAudio() const
355{
356 return m_client.canProduceAudio();
357}
358
359void PlatformMediaSession::canProduceAudioChanged()
360{
361 PlatformMediaSessionManager::sharedManager().sessionCanProduceAudioChanged(*this);
362}
363
364#if ENABLE(VIDEO)
365uint64_t PlatformMediaSessionClient::mediaSessionUniqueIdentifier() const
366{
367 return 0;
368}
369
370String PlatformMediaSessionClient::mediaSessionTitle() const
371{
372 return String();
373}
374
375double PlatformMediaSessionClient::mediaSessionDuration() const
376{
377 return MediaPlayer::invalidTime();
378}
379
380double PlatformMediaSessionClient::mediaSessionCurrentTime() const
381{
382 return MediaPlayer::invalidTime();
383}
384#endif
385
386void PlatformMediaSession::clientCharacteristicsChanged()
387{
388 PlatformMediaSessionManager::sharedManager().clientCharacteristicsChanged(*this);
389}
390
391#if !RELEASE_LOG_DISABLED
392WTFLogChannel& PlatformMediaSession::logChannel() const
393{
394 return LogMedia;
395}
396#endif
397
398}
399
400#endif
401