Hardening: Use WeakPtrs in PlaybackSessionInterface{Mac,AVKit}
[WebKit-https.git] / Source / WebCore / platform / ios / PlaybackSessionInterfaceAVKit.mm
1 /*
2  * Copyright (C) 2014-2019 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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26
27 #import "config.h"
28 #import "PlaybackSessionInterfaceAVKit.h"
29
30 #if PLATFORM(IOS_FAMILY)
31 #if HAVE(AVKIT)
32
33 #import "Logging.h"
34 #import "MediaSelectionOption.h"
35 #import "PlaybackSessionModel.h"
36 #import "TimeRanges.h"
37 #import "WebAVPlayerController.h"
38 #import <AVFoundation/AVTime.h>
39 #import <pal/spi/cocoa/AVKitSPI.h>
40 #import <wtf/RetainPtr.h>
41 #import <wtf/text/CString.h>
42 #import <wtf/text/WTFString.h>
43
44 #import <pal/cf/CoreMediaSoftLink.h>
45
46 SOFTLINK_AVKIT_FRAMEWORK()
47 SOFT_LINK_CLASS_OPTIONAL(AVKit, AVValueTiming)
48
49 namespace WebCore {
50 using namespace PAL;
51
52 PlaybackSessionInterfaceAVKit::PlaybackSessionInterfaceAVKit(PlaybackSessionModel& model)
53     : m_playerController(adoptNS([[WebAVPlayerController alloc] init]))
54     , m_playbackSessionModel(makeWeakPtr(model))
55 {
56     model.addClient(*this);
57     [m_playerController setPlaybackSessionInterface:this];
58     [m_playerController setDelegate:&model];
59
60     durationChanged(model.duration());
61     currentTimeChanged(model.currentTime(), [[NSProcessInfo processInfo] systemUptime]);
62     bufferedTimeChanged(model.bufferedTime());
63     rateChanged(model.isPlaying(), model.playbackRate());
64     seekableRangesChanged(model.seekableRanges(), model.seekableTimeRangesLastModifiedTime(), model.liveUpdateInterval());
65     canPlayFastReverseChanged(model.canPlayFastReverse());
66     audioMediaSelectionOptionsChanged(model.audioMediaSelectionOptions(), model.audioMediaSelectedIndex());
67     legibleMediaSelectionOptionsChanged(model.legibleMediaSelectionOptions(), model.legibleMediaSelectedIndex());
68     externalPlaybackChanged(model.externalPlaybackEnabled(), model.externalPlaybackTargetType(), model.externalPlaybackLocalizedDeviceName());
69     wirelessVideoPlaybackDisabledChanged(model.wirelessVideoPlaybackDisabled());
70 }
71
72 PlaybackSessionInterfaceAVKit::~PlaybackSessionInterfaceAVKit()
73 {
74     [m_playerController setPlaybackSessionInterface:nullptr];
75     [m_playerController setExternalPlaybackActive:false];
76
77     invalidate();
78 }
79
80 PlaybackSessionModel* playbackSessionModel() const
81 {
82     return m_playbackSessionModel.get();
83 }
84
85 void PlaybackSessionInterfaceAVKit::durationChanged(double duration)
86 {
87     WebAVPlayerController* playerController = m_playerController.get();
88
89     playerController.contentDuration = duration;
90     playerController.contentDurationWithinEndTimes = duration;
91
92     // FIXME: we take this as an indication that playback is ready.
93     playerController.canPlay = YES;
94     playerController.canPause = YES;
95     playerController.canTogglePlayback = YES;
96     playerController.hasEnabledAudio = YES;
97     playerController.canSeek = YES;
98     playerController.status = AVPlayerControllerStatusReadyToPlay;
99 }
100
101 void PlaybackSessionInterfaceAVKit::currentTimeChanged(double currentTime, double anchorTime)
102 {
103     if ([m_playerController isScrubbing])
104         return;
105
106     NSTimeInterval anchorTimeStamp = ![m_playerController rate] ? NAN : anchorTime;
107     AVValueTiming *timing = [getAVValueTimingClass() valueTimingWithAnchorValue:currentTime
108         anchorTimeStamp:anchorTimeStamp rate:0];
109
110     [m_playerController setTiming:timing];
111 }
112
113 void PlaybackSessionInterfaceAVKit::bufferedTimeChanged(double bufferedTime)
114 {
115     WebAVPlayerController* playerController = m_playerController.get();
116     double duration = playerController.contentDuration;
117     double normalizedBufferedTime;
118     if (!duration)
119         normalizedBufferedTime = 0;
120     else
121         normalizedBufferedTime = bufferedTime / duration;
122     playerController.loadedTimeRanges = @[@0, @(normalizedBufferedTime)];
123 }
124
125 void PlaybackSessionInterfaceAVKit::rateChanged(bool isPlaying, float playbackRate)
126 {
127     [m_playerController setRate:isPlaying ? playbackRate : 0.];
128 }
129
130 void PlaybackSessionInterfaceAVKit::seekableRangesChanged(const TimeRanges& timeRanges, double lastModifiedTime, double liveUpdateInterval)
131 {
132     RetainPtr<NSMutableArray> seekableRanges = adoptNS([[NSMutableArray alloc] init]);
133
134 #if !PLATFORM(WATCHOS)
135     for (unsigned i = 0; i < timeRanges.length(); i++) {
136         double start = timeRanges.start(i).releaseReturnValue();
137         double end = timeRanges.end(i).releaseReturnValue();
138
139         CMTimeRange range = CMTimeRangeMake(CMTimeMakeWithSeconds(start, 1000), CMTimeMakeWithSeconds(end-start, 1000));
140         [seekableRanges addObject:[NSValue valueWithCMTimeRange:range]];
141     }
142 #else
143     UNUSED_PARAM(timeRanges);
144 #endif
145
146     [m_playerController setSeekableTimeRanges:seekableRanges.get()];
147     [m_playerController setSeekableTimeRangesLastModifiedTime: lastModifiedTime];
148     [m_playerController setLiveUpdateInterval:liveUpdateInterval];
149 }
150
151 void PlaybackSessionInterfaceAVKit::canPlayFastReverseChanged(bool canPlayFastReverse)
152 {
153     [m_playerController setCanScanBackward:canPlayFastReverse];
154 }
155
156 static RetainPtr<NSMutableArray> mediaSelectionOptions(const Vector<MediaSelectionOption>& options)
157 {
158     RetainPtr<NSMutableArray> webOptions = adoptNS([[NSMutableArray alloc] initWithCapacity:options.size()]);
159     for (auto& option : options) {
160         RetainPtr<WebAVMediaSelectionOption> webOption = adoptNS([[WebAVMediaSelectionOption alloc] init]);
161         [webOption setLocalizedDisplayName:option.displayName];
162         [webOptions addObject:webOption.get()];
163     }
164     return webOptions;
165 }
166
167 void PlaybackSessionInterfaceAVKit::audioMediaSelectionOptionsChanged(const Vector<MediaSelectionOption>& options, uint64_t selectedIndex)
168 {
169     RetainPtr<NSMutableArray> webOptions = mediaSelectionOptions(options);
170     [m_playerController setAudioMediaSelectionOptions:webOptions.get()];
171     if (selectedIndex < [webOptions count])
172         [m_playerController setCurrentAudioMediaSelectionOption:[webOptions objectAtIndex:static_cast<NSUInteger>(selectedIndex)]];
173 }
174
175 void PlaybackSessionInterfaceAVKit::legibleMediaSelectionOptionsChanged(const Vector<MediaSelectionOption>& options, uint64_t selectedIndex)
176 {
177     RetainPtr<NSMutableArray> webOptions = mediaSelectionOptions(options);
178     [m_playerController setLegibleMediaSelectionOptions:webOptions.get()];
179     if (selectedIndex < [webOptions count])
180         [m_playerController setCurrentLegibleMediaSelectionOption:[webOptions objectAtIndex:static_cast<NSUInteger>(selectedIndex)]];
181 }
182
183 void PlaybackSessionInterfaceAVKit::externalPlaybackChanged(bool enabled, PlaybackSessionModel::ExternalPlaybackTargetType targetType, const String& localizedDeviceName)
184 {
185     AVPlayerControllerExternalPlaybackType externalPlaybackType = AVPlayerControllerExternalPlaybackTypeNone;
186     if (targetType == PlaybackSessionModel::TargetTypeAirPlay)
187         externalPlaybackType = AVPlayerControllerExternalPlaybackTypeAirPlay;
188     else if (targetType == PlaybackSessionModel::TargetTypeTVOut)
189         externalPlaybackType = AVPlayerControllerExternalPlaybackTypeTVOut;
190
191     WebAVPlayerController* playerController = m_playerController.get();
192     playerController.externalPlaybackAirPlayDeviceLocalizedName = localizedDeviceName;
193     playerController.externalPlaybackType = externalPlaybackType;
194     playerController.externalPlaybackActive = enabled;
195 }
196
197 void PlaybackSessionInterfaceAVKit::wirelessVideoPlaybackDisabledChanged(bool disabled)
198 {
199     [m_playerController setAllowsExternalPlayback:!disabled];
200 }
201
202 void PlaybackSessionInterfaceAVKit::mutedChanged(bool muted)
203 {
204     [m_playerController setMuted:muted];
205 }
206
207 void PlaybackSessionInterfaceAVKit::volumeChanged(double volume)
208 {
209     [m_playerController volumeChanged:volume];
210 }
211
212 void PlaybackSessionInterfaceAVKit::invalidate()
213 {
214     if (!m_playbackSessionModel)
215         return;
216
217     [m_playerController setDelegate:nullptr];
218     m_playbackSessionModel->removeClient(*this);
219     m_playbackSessionModel = nullptr;
220 }
221
222 }
223
224 #endif // HAVE(AVKIT)
225 #endif // PLATFORM(IOS_FAMILY)