f05f027bbfde6ca8c43c0efd8851b9cddba64ae9
[WebKit-https.git] / Source / WebCore / platform / audio / mac / MediaSessionManagerMac.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 "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     updateNowPlayingInfo();
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         MRMediaRemoteSetNowPlayingApplicationPlaybackStateForOrigin(MRMediaRemoteGetLocalOrigin(), kMRPlaybackStateStopped, dispatch_get_main_queue(), ^(MRMediaRemoteError error) {
145 #if LOG_DISABLED
146             UNUSED_PARAM(error);
147 #else
148             LOG(Media, "MediaSessionManagerMac::updateNowPlayingInfo - MRMediaRemoteSetNowPlayingApplicationPlaybackStateForOrigin(stopped) failed with error %ud", error);
149 #endif
150         });
151
152         return;
153     }
154
155     static dispatch_once_t enableNowPlayingToken;
156     dispatch_once(&enableNowPlayingToken, ^() {
157         MRMediaRemoteSetCanBeNowPlayingApplication(true);
158     });
159
160     String title = currentSession->title();
161     double duration = currentSession->supportsSeeking() ? currentSession->duration() : MediaPlayer::invalidTime();
162     double rate = currentSession->state() == PlatformMediaSession::Playing ? 1 : 0;
163     auto info = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
164
165     if (!title.isEmpty()) {
166         CFDictionarySetValue(info.get(), kMRMediaRemoteNowPlayingInfoTitle, title.createCFString().get());
167         m_lastUpdatedNowPlayingTitle = title;
168     }
169
170     if (std::isfinite(duration) && duration != MediaPlayer::invalidTime()) {
171         auto cfDuration = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &duration));
172         CFDictionarySetValue(info.get(), kMRMediaRemoteNowPlayingInfoDuration, cfDuration.get());
173         m_lastUpdatedNowPlayingDuration = duration;
174     }
175
176     auto cfRate = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &rate);
177     CFDictionarySetValue(info.get(), kMRMediaRemoteNowPlayingInfoPlaybackRate, cfRate);
178
179     double currentTime = currentSession->currentTime();
180     if (std::isfinite(currentTime) && currentTime != MediaPlayer::invalidTime() && currentSession->supportsSeeking()) {
181         auto cfCurrentTime = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &currentTime));
182         CFDictionarySetValue(info.get(), kMRMediaRemoteNowPlayingInfoElapsedTime, cfCurrentTime.get());
183         m_lastUpdatedNowPlayingElapsedTime = currentTime;
184     }
185
186     LOG(Media, "MediaSessionManagerMac::updateNowPlayingInfo - title = \"%s\", rate = %f, duration = %f, now = %f",
187         title.utf8().data(), rate, duration, currentTime);
188
189     String parentApplication = currentSession->sourceApplicationIdentifier();
190     if (canLoad_MediaRemote_MRMediaRemoteSetParentApplication() && !parentApplication.isEmpty())
191         MRMediaRemoteSetParentApplication(MRMediaRemoteGetLocalOrigin(), parentApplication.createCFString().get());
192
193     m_nowPlayingActive = currentSession->allowsNowPlayingControlsVisibility();
194     MRPlaybackState playbackState = (currentSession->state() == PlatformMediaSession::Playing) ? kMRPlaybackStatePlaying : kMRPlaybackStatePaused;
195     MRMediaRemoteSetNowPlayingApplicationPlaybackStateForOrigin(MRMediaRemoteGetLocalOrigin(), playbackState, dispatch_get_main_queue(), ^(MRMediaRemoteError error) {
196 #if LOG_DISABLED
197         UNUSED_PARAM(error);
198 #else
199         LOG(Media, "MediaSessionManagerMac::updateNowPlayingInfo - MRMediaRemoteSetNowPlayingApplicationPlaybackStateForOrigin(playing) failed with error %ud", error);
200 #endif
201     });
202     MRMediaRemoteSetNowPlayingInfo(info.get());
203
204     if (canLoad_MediaRemote_MRMediaRemoteSetNowPlayingVisibility()) {
205         MRNowPlayingClientVisibility visibility = currentSession->allowsNowPlayingControlsVisibility() ? MRNowPlayingClientVisibilityAlwaysVisible : MRNowPlayingClientVisibilityNeverVisible;
206         MRMediaRemoteSetNowPlayingVisibility(MRMediaRemoteGetLocalOrigin(), visibility);
207     }
208     END_BLOCK_OBJC_EXCEPTIONS
209 #endif // USE(MEDIAREMOTE)
210 }
211
212 } // namespace WebCore
213
214 #endif // PLATFORM(MAC)