ac08d1fbdf112c65885b9cd895f2efe11173bb63
[WebKit.git] / Source / WebCore / platform / mac / WebPlaybackControlsManager.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 "WebPlaybackControlsManager.h"
28
29 #if PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE) && ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
30
31 #import "MediaSelectionOption.h"
32 #import "PlaybackSessionInterfaceMac.h"
33 #import "PlaybackSessionModel.h"
34 #import <wtf/SoftLinking.h>
35 #import <wtf/text/WTFString.h>
36
37 IGNORE_CLANG_WARNINGS_BEGIN("nullability-completeness")
38 SOFT_LINK_FRAMEWORK(AVKit)
39 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
40 SOFT_LINK_CLASS_OPTIONAL(AVKit, AVTouchBarMediaSelectionOption)
41 #else
42 SOFT_LINK_CLASS_OPTIONAL(AVKit, AVFunctionBarMediaSelectionOption)
43 #endif // __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
44
45 using WebCore::MediaSelectionOption;
46 using WebCore::PlaybackSessionInterfaceMac;
47
48 @implementation WebPlaybackControlsManager
49
50 @synthesize contentDuration=_contentDuration;
51 @synthesize seekToTime=_seekToTime;
52 @synthesize hasEnabledAudio=_hasEnabledAudio;
53 @synthesize hasEnabledVideo=_hasEnabledVideo;
54 @synthesize rate=_rate;
55 @synthesize canTogglePlayback=_canTogglePlayback;
56 @synthesize allowsPictureInPicturePlayback;
57 @synthesize pictureInPictureActive;
58 @synthesize canTogglePictureInPicture;
59
60 - (void)dealloc
61 {
62     if (_playbackSessionInterfaceMac)
63         _playbackSessionInterfaceMac->setPlayBackControlsManager(nullptr);
64     [super dealloc];
65 }
66
67 - (AVValueTiming *)timing
68 {
69     return _timing.get();
70 }
71
72 - (void)setTiming:(AVValueTiming *)timing
73 {
74     _timing = timing;
75 }
76
77 - (NSArray *)seekableTimeRanges
78 {
79     return _seekableTimeRanges.get();
80 }
81
82 - (void)setSeekableTimeRanges:(NSArray *)timeRanges
83 {
84     _seekableTimeRanges = timeRanges;
85 }
86
87 - (BOOL)isSeeking
88 {
89     return NO;
90 }
91
92 - (void)seekToTime:(NSTimeInterval)time toleranceBefore:(NSTimeInterval)toleranceBefore toleranceAfter:(NSTimeInterval)toleranceAfter
93 {
94     UNUSED_PARAM(toleranceBefore);
95     UNUSED_PARAM(toleranceAfter);
96     _playbackSessionInterfaceMac->playbackSessionModel()->seekToTime(time);
97 }
98
99 - (void)cancelThumbnailAndAudioAmplitudeSampleGeneration
100 {
101 }
102
103 - (void)generateTouchBarThumbnailsForTimes:(NSArray<NSNumber *> *)thumbnailTimes tolerance:(NSTimeInterval)tolerance size:(NSSize)size thumbnailHandler:(void (^)(NSArray<AVThumbnail *> *thumbnails, BOOL thumbnailGenerationFailed))thumbnailHandler
104 {
105     UNUSED_PARAM(thumbnailTimes);
106     UNUSED_PARAM(tolerance);
107     UNUSED_PARAM(size);
108     thumbnailHandler(@[ ], YES);
109 }
110
111 - (void)generateTouchBarAudioAmplitudeSamples:(NSInteger)numberOfSamples completionHandler:(void (^)(NSArray<NSNumber *> *audioAmplitudeSamples))completionHandler
112 {
113     UNUSED_PARAM(numberOfSamples);
114     completionHandler(@[ ]);
115 }
116
117 - (BOOL)canBeginTouchBarScrubbing
118 {
119     // At this time, we return YES for all media that is not a live stream and media that is not Netflix. (A Netflix
120     // quirk means we pretend Netflix is a live stream for Touch Bar.) It's not ideal to return YES all the time for
121     // other media. The intent of the API is that we return NO when the media is being scrubbed via the on-screen scrubber.
122     // But we can only possibly get the right answer for media that uses the default controls.
123     return std::isfinite(_contentDuration);;
124 }
125
126 - (void)beginTouchBarScrubbing
127 {
128     _playbackSessionInterfaceMac->beginScrubbing();
129 }
130
131 - (void)endTouchBarScrubbing
132 {
133     _playbackSessionInterfaceMac->endScrubbing();
134 }
135
136 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 101300
137
138 - (void)generateFunctionBarThumbnailsForTimes:(NSArray<NSNumber *> *)thumbnailTimes size:(NSSize)size completionHandler:(void (^)(NSArray<AVThumbnail *> *thumbnails, NSError *error))completionHandler
139 {
140     UNUSED_PARAM(thumbnailTimes);
141     UNUSED_PARAM(size);
142     completionHandler(@[ ], nil);
143 }
144
145 - (void)generateFunctionBarAudioAmplitudeSamples:(NSInteger)numberOfSamples completionHandler:(void (^)(NSArray<NSNumber *> *audioAmplitudeSamples,  NSError *error))completionHandler
146 {
147     UNUSED_PARAM(numberOfSamples);
148     completionHandler(@[ ], nil);
149 }
150
151 - (BOOL)canBeginFunctionBarScrubbing
152 {
153     return [self canBeginTouchBarScrubbing];
154 }
155
156 - (void)beginFunctionBarScrubbing
157 {
158     [self beginTouchBarScrubbing];
159 }
160
161 - (void)endFunctionBarScrubbing
162 {
163     [self endTouchBarScrubbing];
164 }
165
166 #endif
167
168 - (NSArray<AVTouchBarMediaSelectionOption *> *)audioTouchBarMediaSelectionOptions
169 {
170     return _audioTouchBarMediaSelectionOptions.get();
171 }
172
173 - (void)setAudioTouchBarMediaSelectionOptions:(NSArray<AVTouchBarMediaSelectionOption *> *)audioOptions
174 {
175     _audioTouchBarMediaSelectionOptions = audioOptions;
176 }
177
178 - (AVTouchBarMediaSelectionOption *)currentAudioTouchBarMediaSelectionOption
179 {
180     return _currentAudioTouchBarMediaSelectionOption.get();
181 }
182
183 - (void)setCurrentAudioTouchBarMediaSelectionOption:(AVTouchBarMediaSelectionOption *)audioMediaSelectionOption
184 {
185     if (audioMediaSelectionOption == _currentAudioTouchBarMediaSelectionOption)
186         return;
187
188     _currentAudioTouchBarMediaSelectionOption = audioMediaSelectionOption;
189
190     NSInteger index = NSNotFound;
191
192     if (audioMediaSelectionOption && _audioTouchBarMediaSelectionOptions)
193         index = [_audioTouchBarMediaSelectionOptions indexOfObject:audioMediaSelectionOption];
194
195     _playbackSessionInterfaceMac->playbackSessionModel()->selectAudioMediaOption(index != NSNotFound ? index : UINT64_MAX);
196 }
197
198 - (NSArray<AVTouchBarMediaSelectionOption *> *)legibleTouchBarMediaSelectionOptions
199 {
200     return _legibleTouchBarMediaSelectionOptions.get();
201 }
202
203 - (void)setLegibleTouchBarMediaSelectionOptions:(NSArray<AVTouchBarMediaSelectionOption *> *)legibleOptions
204 {
205     _legibleTouchBarMediaSelectionOptions = legibleOptions;
206 }
207
208 - (AVTouchBarMediaSelectionOption *)currentLegibleTouchBarMediaSelectionOption
209 {
210     return _currentLegibleTouchBarMediaSelectionOption.get();
211 }
212
213 - (void)setCurrentLegibleTouchBarMediaSelectionOption:(AVTouchBarMediaSelectionOption *)legibleMediaSelectionOption
214 {
215     if (legibleMediaSelectionOption == _currentLegibleTouchBarMediaSelectionOption)
216         return;
217
218     _currentLegibleTouchBarMediaSelectionOption = legibleMediaSelectionOption;
219
220     NSInteger index = NSNotFound;
221
222     if (legibleMediaSelectionOption && _legibleTouchBarMediaSelectionOptions)
223         index = [_legibleTouchBarMediaSelectionOptions indexOfObject:legibleMediaSelectionOption];
224
225     _playbackSessionInterfaceMac->playbackSessionModel()->selectLegibleMediaOption(index != NSNotFound ? index : UINT64_MAX);
226 }
227
228 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
229 static AVTouchBarMediaSelectionOptionType toAVTouchBarMediaSelectionOptionType(MediaSelectionOption::Type type)
230 {
231     switch (type) {
232     case MediaSelectionOption::Type::Regular:
233         return AVTouchBarMediaSelectionOptionTypeRegular;
234     case MediaSelectionOption::Type::LegibleOff:
235         return AVTouchBarMediaSelectionOptionTypeLegibleOff;
236     case MediaSelectionOption::Type::LegibleAuto:
237         return AVTouchBarMediaSelectionOptionTypeLegibleAuto;
238     }
239
240     ASSERT_NOT_REACHED();
241     return AVTouchBarMediaSelectionOptionTypeRegular;
242 }
243 #endif
244
245 static RetainPtr<NSMutableArray> mediaSelectionOptions(const Vector<MediaSelectionOption>& options)
246 {
247     RetainPtr<NSMutableArray> webOptions = adoptNS([[NSMutableArray alloc] initWithCapacity:options.size()]);
248     for (auto& option : options) {
249 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
250         if (auto webOption = adoptNS([allocAVTouchBarMediaSelectionOptionInstance() initWithTitle:option.displayName type:toAVTouchBarMediaSelectionOptionType(option.type)]))
251 #else
252         if (auto webOption = adoptNS([allocAVFunctionBarMediaSelectionOptionInstance() initWithTitle:option.displayName]))
253 #endif // __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
254             [webOptions addObject:webOption.get()];
255     }
256     return webOptions;
257 }
258
259 - (void)setAudioMediaSelectionOptions:(const Vector<MediaSelectionOption>&)options withSelectedIndex:(NSUInteger)selectedIndex
260 {
261     RetainPtr<NSMutableArray> webOptions = mediaSelectionOptions(options);
262     [self setAudioTouchBarMediaSelectionOptions:webOptions.get()];
263     if (selectedIndex < [webOptions count])
264         [self setCurrentAudioTouchBarMediaSelectionOption:[webOptions objectAtIndex:selectedIndex]];
265 }
266
267 - (void)setLegibleMediaSelectionOptions:(const Vector<MediaSelectionOption>&)options withSelectedIndex:(NSUInteger)selectedIndex
268 {
269     RetainPtr<NSMutableArray> webOptions = mediaSelectionOptions(options);
270     [self setLegibleTouchBarMediaSelectionOptions:webOptions.get()];
271     if (selectedIndex < [webOptions count])
272         [self setCurrentLegibleTouchBarMediaSelectionOption:[webOptions objectAtIndex:selectedIndex]];
273 }
274
275 - (void)setAudioMediaSelectionIndex:(NSUInteger)selectedIndex
276 {
277     if (selectedIndex >= [_audioTouchBarMediaSelectionOptions count])
278         return;
279
280     [self willChangeValueForKey:@"currentAudioTouchBarMediaSelectionOption"];
281     _currentAudioTouchBarMediaSelectionOption = [_audioTouchBarMediaSelectionOptions objectAtIndex:selectedIndex];
282     [self didChangeValueForKey:@"currentAudioTouchBarMediaSelectionOption"];
283 }
284
285 - (void)setLegibleMediaSelectionIndex:(NSUInteger)selectedIndex
286 {
287     if (selectedIndex >= [_legibleTouchBarMediaSelectionOptions count])
288         return;
289
290     [self willChangeValueForKey:@"currentLegibleTouchBarMediaSelectionOption"];
291     _currentLegibleTouchBarMediaSelectionOption = [_legibleTouchBarMediaSelectionOptions objectAtIndex:selectedIndex];
292     [self didChangeValueForKey:@"currentLegibleTouchBarMediaSelectionOption"];
293 }
294
295 - (PlaybackSessionInterfaceMac*)playbackSessionInterfaceMac
296 {
297     return _playbackSessionInterfaceMac.get();
298 }
299
300 - (void)setPlaybackSessionInterfaceMac:(PlaybackSessionInterfaceMac*)playbackSessionInterfaceMac
301 {
302     if (_playbackSessionInterfaceMac == playbackSessionInterfaceMac)
303         return;
304
305     if (_playbackSessionInterfaceMac)
306         _playbackSessionInterfaceMac->setPlayBackControlsManager(nullptr);
307
308     _playbackSessionInterfaceMac = playbackSessionInterfaceMac;
309
310     if (_playbackSessionInterfaceMac)
311         _playbackSessionInterfaceMac->setPlayBackControlsManager(self);
312 }
313
314 - (void)togglePlayback
315 {
316     if (_playbackSessionInterfaceMac && _playbackSessionInterfaceMac->playbackSessionModel())
317         _playbackSessionInterfaceMac->playbackSessionModel()->togglePlayState();
318 }
319
320 - (void)setPlaying:(BOOL)playing
321 {
322     if (!_playbackSessionInterfaceMac || !_playbackSessionInterfaceMac->playbackSessionModel())
323         return;
324
325     BOOL isCurrentlyPlaying = self.playing;
326     if (!isCurrentlyPlaying && playing)
327         _playbackSessionInterfaceMac->playbackSessionModel()->play();
328     else if (isCurrentlyPlaying && !playing)
329         _playbackSessionInterfaceMac->playbackSessionModel()->pause();
330 }
331
332 - (BOOL)isPlaying
333 {
334     if (_playbackSessionInterfaceMac && _playbackSessionInterfaceMac->playbackSessionModel())
335         return _playbackSessionInterfaceMac->playbackSessionModel()->isPlaying();
336
337     return NO;
338 }
339
340 - (void)togglePictureInPicture
341 {
342     if (_playbackSessionInterfaceMac && _playbackSessionInterfaceMac->playbackSessionModel())
343         _playbackSessionInterfaceMac->playbackSessionModel()->togglePictureInPicture();
344 }
345
346 IGNORE_CLANG_WARNINGS_END
347
348 @end
349
350 #endif // PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE) && ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
351