ASSERTION FAILED: NoEventDispatchAssertion::InMainThread::isEventAllowed() || (frameV...
[WebKit-https.git] / Source / WebCore / platform / audio / mac / MediaSessionManagerMac.mm
1 /*
2  * Copyright (C) 2016-2017 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 "MediaSessionManagerMac.h"
28
29 #if PLATFORM(MAC)
30
31 #import "HTMLMediaElement.h"
32 #import "Logging.h"
33 #import "MediaPlayer.h"
34 #import "PlatformMediaSession.h"
35 #import <wtf/BlockObjCExceptions.h>
36
37 #import "MediaRemoteSoftLink.h"
38
39 using namespace WebCore;
40
41 namespace WebCore {
42
43 static MediaSessionManagerMac* platformMediaSessionManager = nullptr;
44
45 PlatformMediaSessionManager& PlatformMediaSessionManager::sharedManager()
46 {
47     if (!platformMediaSessionManager)
48         platformMediaSessionManager = new MediaSessionManagerMac;
49     return *platformMediaSessionManager;
50 }
51
52 PlatformMediaSessionManager* PlatformMediaSessionManager::sharedManagerIfExists()
53 {
54     return platformMediaSessionManager;
55 }
56
57 void PlatformMediaSessionManager::updateNowPlayingInfoIfNecessary()
58 {
59     if (auto existingManager = (MediaSessionManagerMac *)PlatformMediaSessionManager::sharedManagerIfExists())
60         existingManager->scheduleUpdateNowPlayingInfo();
61 }
62
63 MediaSessionManagerMac::MediaSessionManagerMac()
64     : PlatformMediaSessionManager()
65 {
66     resetRestrictions();
67 }
68
69 MediaSessionManagerMac::~MediaSessionManagerMac()
70 {
71 }
72
73 void MediaSessionManagerMac::scheduleUpdateNowPlayingInfo()
74 {
75     if (!m_nowPlayingUpdateTaskQueue.hasPendingTasks())
76         m_nowPlayingUpdateTaskQueue.enqueueTask(std::bind(&MediaSessionManagerMac::updateNowPlayingInfo, this));
77 }
78
79 bool MediaSessionManagerMac::sessionWillBeginPlayback(PlatformMediaSession& session)
80 {
81     if (!PlatformMediaSessionManager::sessionWillBeginPlayback(session))
82         return false;
83
84     LOG(Media, "MediaSessionManagerMac::sessionWillBeginPlayback");
85     scheduleUpdateNowPlayingInfo();
86     return true;
87 }
88
89 void MediaSessionManagerMac::sessionDidEndRemoteScrubbing(const PlatformMediaSession&)
90 {
91     scheduleUpdateNowPlayingInfo();
92 }
93
94 void MediaSessionManagerMac::removeSession(PlatformMediaSession& session)
95 {
96     PlatformMediaSessionManager::removeSession(session);
97     LOG(Media, "MediaSessionManagerMac::removeSession");
98     scheduleUpdateNowPlayingInfo();
99 }
100
101 void MediaSessionManagerMac::sessionWillEndPlayback(PlatformMediaSession& session)
102 {
103     PlatformMediaSessionManager::sessionWillEndPlayback(session);
104     LOG(Media, "MediaSessionManagerMac::sessionWillEndPlayback");
105     updateNowPlayingInfo();
106 }
107
108 void MediaSessionManagerMac::clientCharacteristicsChanged(PlatformMediaSession&)
109 {
110     LOG(Media, "MediaSessionManagerMac::clientCharacteristicsChanged");
111     updateNowPlayingInfo();
112 }
113
114 PlatformMediaSession* MediaSessionManagerMac::nowPlayingEligibleSession()
115 {
116     if (auto element = HTMLMediaElement::bestMediaElementForShowingPlaybackControlsManager(MediaElementSession::PlaybackControlsPurpose::NowPlaying))
117         return &element->mediaSession();
118
119     return nullptr;
120 }
121
122 void MediaSessionManagerMac::updateNowPlayingInfo()
123 {
124 #if USE(MEDIAREMOTE)
125     if (!isMediaRemoteFrameworkAvailable())
126         return;
127
128     BEGIN_BLOCK_OBJC_EXCEPTIONS
129
130     const PlatformMediaSession* currentSession = this->nowPlayingEligibleSession();
131
132     LOG(Media, "MediaSessionManagerMac::updateNowPlayingInfo - currentSession = %p", currentSession);
133
134     if (!currentSession) {
135         if (canLoad_MediaRemote_MRMediaRemoteSetNowPlayingVisibility())
136             MRMediaRemoteSetNowPlayingVisibility(MRMediaRemoteGetLocalOrigin(), MRNowPlayingClientVisibilityNeverVisible);
137
138         LOG(Media, "MediaSessionManagerMac::updateNowPlayingInfo - clearing now playing info");
139         MRMediaRemoteSetNowPlayingInfo(nullptr);
140         m_nowPlayingActive = false;
141         m_lastUpdatedNowPlayingTitle = emptyString();
142         m_lastUpdatedNowPlayingDuration = NAN;
143         m_lastUpdatedNowPlayingElapsedTime = NAN;
144         m_lastUpdatedNowPlayingInfoUniqueIdentifier = 0;
145         MRMediaRemoteSetNowPlayingApplicationPlaybackStateForOrigin(MRMediaRemoteGetLocalOrigin(), kMRPlaybackStateStopped, dispatch_get_main_queue(), ^(MRMediaRemoteError error) {
146 #if LOG_DISABLED
147             UNUSED_PARAM(error);
148 #else
149             if (error)
150                 LOG(Media, "MediaSessionManagerMac::updateNowPlayingInfo - MRMediaRemoteSetNowPlayingApplicationPlaybackStateForOrigin(stopped) failed with error %ud", error);
151 #endif
152         });
153
154         return;
155     }
156
157     static dispatch_once_t enableNowPlayingToken;
158     dispatch_once(&enableNowPlayingToken, ^() {
159         MRMediaRemoteSetCanBeNowPlayingApplication(true);
160     });
161
162     String title = currentSession->title();
163     double duration = currentSession->supportsSeeking() ? currentSession->duration() : MediaPlayer::invalidTime();
164     double rate = currentSession->state() == PlatformMediaSession::Playing ? 1 : 0;
165     auto info = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
166
167     if (!title.isEmpty()) {
168         CFDictionarySetValue(info.get(), kMRMediaRemoteNowPlayingInfoTitle, title.createCFString().get());
169         m_lastUpdatedNowPlayingTitle = title;
170     }
171
172     if (std::isfinite(duration) && duration != MediaPlayer::invalidTime()) {
173         auto cfDuration = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &duration));
174         CFDictionarySetValue(info.get(), kMRMediaRemoteNowPlayingInfoDuration, cfDuration.get());
175         m_lastUpdatedNowPlayingDuration = duration;
176     }
177
178     auto cfRate = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &rate));
179     CFDictionarySetValue(info.get(), kMRMediaRemoteNowPlayingInfoPlaybackRate, cfRate.get());
180
181     m_lastUpdatedNowPlayingInfoUniqueIdentifier = title.impl() ? title.impl()->hash() : 0;
182     auto cfIdentifier = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &m_lastUpdatedNowPlayingInfoUniqueIdentifier));
183     CFDictionarySetValue(info.get(), kMRMediaRemoteNowPlayingInfoUniqueIdentifier, cfIdentifier.get());
184
185     double currentTime = currentSession->currentTime();
186     if (std::isfinite(currentTime) && currentTime != MediaPlayer::invalidTime() && currentSession->supportsSeeking()) {
187         auto cfCurrentTime = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &currentTime));
188         CFDictionarySetValue(info.get(), kMRMediaRemoteNowPlayingInfoElapsedTime, cfCurrentTime.get());
189         m_lastUpdatedNowPlayingElapsedTime = currentTime;
190     }
191
192     LOG(Media, "MediaSessionManagerMac::updateNowPlayingInfo - title = \"%s\", rate = %f, duration = %f, now = %f",
193         title.utf8().data(), rate, duration, currentTime);
194
195     String parentApplication = currentSession->sourceApplicationIdentifier();
196     if (canLoad_MediaRemote_MRMediaRemoteSetParentApplication() && !parentApplication.isEmpty())
197         MRMediaRemoteSetParentApplication(MRMediaRemoteGetLocalOrigin(), parentApplication.createCFString().get());
198
199     m_nowPlayingActive = currentSession->allowsNowPlayingControlsVisibility();
200     MRPlaybackState playbackState = (currentSession->state() == PlatformMediaSession::Playing) ? kMRPlaybackStatePlaying : kMRPlaybackStatePaused;
201     MRMediaRemoteSetNowPlayingApplicationPlaybackStateForOrigin(MRMediaRemoteGetLocalOrigin(), playbackState, dispatch_get_main_queue(), ^(MRMediaRemoteError error) {
202 #if LOG_DISABLED
203         UNUSED_PARAM(error);
204 #else
205         LOG(Media, "MediaSessionManagerMac::updateNowPlayingInfo - MRMediaRemoteSetNowPlayingApplicationPlaybackStateForOrigin(playing) failed with error %ud", error);
206 #endif
207     });
208     MRMediaRemoteSetNowPlayingInfo(info.get());
209
210     if (canLoad_MediaRemote_MRMediaRemoteSetNowPlayingVisibility()) {
211         MRNowPlayingClientVisibility visibility = currentSession->allowsNowPlayingControlsVisibility() ? MRNowPlayingClientVisibilityAlwaysVisible : MRNowPlayingClientVisibilityNeverVisible;
212         MRMediaRemoteSetNowPlayingVisibility(MRMediaRemoteGetLocalOrigin(), visibility);
213     }
214     END_BLOCK_OBJC_EXCEPTIONS
215 #endif // USE(MEDIAREMOTE)
216 }
217
218 } // namespace WebCore
219
220 #endif // PLATFORM(MAC)