19625692fb4fccd46f0b0cc4cf114f2fc63acacd
[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 MediaSessionManagerMac::MediaSessionManagerMac()
58     : MediaSessionManagerCocoa()
59 {
60     resetRestrictions();
61 }
62
63 MediaSessionManagerMac::~MediaSessionManagerMac()
64 {
65 }
66
67 void MediaSessionManagerMac::scheduleUpdateNowPlayingInfo()
68 {
69     if (!m_nowPlayingUpdateTaskQueue.hasPendingTasks())
70         m_nowPlayingUpdateTaskQueue.enqueueTask(std::bind(&MediaSessionManagerMac::updateNowPlayingInfo, this));
71 }
72
73 bool MediaSessionManagerMac::sessionWillBeginPlayback(PlatformMediaSession& session)
74 {
75     if (!PlatformMediaSessionManager::sessionWillBeginPlayback(session))
76         return false;
77
78     LOG(Media, "MediaSessionManagerMac::sessionWillBeginPlayback");
79     scheduleUpdateNowPlayingInfo();
80     return true;
81 }
82
83 void MediaSessionManagerMac::sessionDidEndRemoteScrubbing(const PlatformMediaSession&)
84 {
85     scheduleUpdateNowPlayingInfo();
86 }
87
88 void MediaSessionManagerMac::removeSession(PlatformMediaSession& session)
89 {
90     PlatformMediaSessionManager::removeSession(session);
91     LOG(Media, "MediaSessionManagerMac::removeSession");
92     scheduleUpdateNowPlayingInfo();
93 }
94
95 void MediaSessionManagerMac::sessionWillEndPlayback(PlatformMediaSession& session)
96 {
97     PlatformMediaSessionManager::sessionWillEndPlayback(session);
98     LOG(Media, "MediaSessionManagerMac::sessionWillEndPlayback");
99     updateNowPlayingInfo();
100 }
101
102 void MediaSessionManagerMac::clientCharacteristicsChanged(PlatformMediaSession&)
103 {
104     LOG(Media, "MediaSessionManagerMac::clientCharacteristicsChanged");
105     scheduleUpdateNowPlayingInfo();
106 }
107     
108 void MediaSessionManagerMac::sessionCanProduceAudioChanged(PlatformMediaSession& session)
109 {
110     PlatformMediaSessionManager::sessionCanProduceAudioChanged(session);
111     scheduleUpdateNowPlayingInfo();
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
140         MRMediaRemoteSetCanBeNowPlayingApplication(false);
141         m_registeredAsNowPlayingApplication = false;
142
143         MRMediaRemoteSetNowPlayingInfo(nullptr);
144         m_nowPlayingActive = false;
145         m_lastUpdatedNowPlayingTitle = emptyString();
146         m_lastUpdatedNowPlayingDuration = NAN;
147         m_lastUpdatedNowPlayingElapsedTime = NAN;
148         m_lastUpdatedNowPlayingInfoUniqueIdentifier = 0;
149         MRMediaRemoteSetNowPlayingApplicationPlaybackStateForOrigin(MRMediaRemoteGetLocalOrigin(), kMRPlaybackStateStopped, dispatch_get_main_queue(), ^(MRMediaRemoteError error) {
150 #if LOG_DISABLED
151             UNUSED_PARAM(error);
152 #else
153             if (error)
154                 LOG(Media, "MediaSessionManagerMac::updateNowPlayingInfo - MRMediaRemoteSetNowPlayingApplicationPlaybackStateForOrigin(stopped) failed with error %ud", error);
155 #endif
156         });
157
158         return;
159     }
160
161     if (!m_registeredAsNowPlayingApplication) {
162         m_registeredAsNowPlayingApplication = true;
163         MRMediaRemoteSetCanBeNowPlayingApplication(true);
164     }
165
166     String title = currentSession->title();
167     double duration = currentSession->supportsSeeking() ? currentSession->duration() : MediaPlayer::invalidTime();
168     double rate = currentSession->state() == PlatformMediaSession::Playing ? 1 : 0;
169     auto info = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
170
171     if (!title.isEmpty()) {
172         CFDictionarySetValue(info.get(), kMRMediaRemoteNowPlayingInfoTitle, title.createCFString().get());
173         m_lastUpdatedNowPlayingTitle = title;
174     }
175
176     if (std::isfinite(duration) && duration != MediaPlayer::invalidTime()) {
177         auto cfDuration = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &duration));
178         CFDictionarySetValue(info.get(), kMRMediaRemoteNowPlayingInfoDuration, cfDuration.get());
179         m_lastUpdatedNowPlayingDuration = duration;
180     }
181
182     auto cfRate = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &rate));
183     CFDictionarySetValue(info.get(), kMRMediaRemoteNowPlayingInfoPlaybackRate, cfRate.get());
184
185     m_lastUpdatedNowPlayingInfoUniqueIdentifier = currentSession->uniqueIdentifier();
186     auto cfIdentifier = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &m_lastUpdatedNowPlayingInfoUniqueIdentifier));
187     CFDictionarySetValue(info.get(), kMRMediaRemoteNowPlayingInfoUniqueIdentifier, cfIdentifier.get());
188
189     double currentTime = currentSession->currentTime();
190     if (std::isfinite(currentTime) && currentTime != MediaPlayer::invalidTime() && currentSession->supportsSeeking()) {
191         auto cfCurrentTime = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &currentTime));
192         CFDictionarySetValue(info.get(), kMRMediaRemoteNowPlayingInfoElapsedTime, cfCurrentTime.get());
193         m_lastUpdatedNowPlayingElapsedTime = currentTime;
194     }
195
196     LOG(Media, "MediaSessionManagerMac::updateNowPlayingInfo - title = \"%s\", rate = %f, duration = %f, now = %f",
197         title.utf8().data(), rate, duration, currentTime);
198
199     String parentApplication = currentSession->sourceApplicationIdentifier();
200     if (canLoad_MediaRemote_MRMediaRemoteSetParentApplication() && !parentApplication.isEmpty())
201         MRMediaRemoteSetParentApplication(MRMediaRemoteGetLocalOrigin(), parentApplication.createCFString().get());
202
203     m_nowPlayingActive = currentSession->allowsNowPlayingControlsVisibility();
204     MRPlaybackState playbackState = (currentSession->state() == PlatformMediaSession::Playing) ? kMRPlaybackStatePlaying : kMRPlaybackStatePaused;
205     MRMediaRemoteSetNowPlayingApplicationPlaybackStateForOrigin(MRMediaRemoteGetLocalOrigin(), playbackState, dispatch_get_main_queue(), ^(MRMediaRemoteError error) {
206 #if LOG_DISABLED
207         UNUSED_PARAM(error);
208 #else
209         LOG(Media, "MediaSessionManagerMac::updateNowPlayingInfo - MRMediaRemoteSetNowPlayingApplicationPlaybackStateForOrigin(playing) failed with error %ud", error);
210 #endif
211     });
212     MRMediaRemoteSetNowPlayingInfo(info.get());
213
214     if (canLoad_MediaRemote_MRMediaRemoteSetNowPlayingVisibility()) {
215         MRNowPlayingClientVisibility visibility = currentSession->allowsNowPlayingControlsVisibility() ? MRNowPlayingClientVisibilityAlwaysVisible : MRNowPlayingClientVisibilityNeverVisible;
216         MRMediaRemoteSetNowPlayingVisibility(MRMediaRemoteGetLocalOrigin(), visibility);
217     }
218     END_BLOCK_OBJC_EXCEPTIONS
219 #endif // USE(MEDIAREMOTE)
220 }
221
222 } // namespace WebCore
223
224 #endif // PLATFORM(MAC)