92f0b4c4603d0acaa3f0c6e72225633d00b5497b
[WebKit-https.git] / Source / WebCore / platform / ios / WebAVPlayerController.mm
1 /*
2  * Copyright (C) 2014, 2015 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 "WebAVPlayerController.h"
29
30 #if PLATFORM(IOS) && HAVE(AVKIT)
31
32 #import "AVKitSPI.h"
33 #import "Logging.h"
34 #import "TimeRanges.h"
35 #import "WebPlaybackSessionInterfaceAVKit.h"
36 #import "WebPlaybackSessionModel.h"
37 #import <AVFoundation/AVTime.h>
38 #import <wtf/text/CString.h>
39 #import <wtf/text/WTFString.h>
40
41 #import "CoreMediaSoftLink.h"
42
43 SOFT_LINK_FRAMEWORK_OPTIONAL(AVKit)
44 SOFT_LINK_CLASS_OPTIONAL(AVKit, AVPlayerController)
45
46 using namespace WebCore;
47
48 @implementation WebAVPlayerController
49
50 - (instancetype)init
51 {
52     if (!getAVPlayerControllerClass())
53         return nil;
54
55     if (!(self = [super init]))
56         return self;
57
58     initAVPlayerController();
59     self.playerControllerProxy = [[allocAVPlayerControllerInstance() init] autorelease];
60     return self;
61 }
62
63 - (void)dealloc
64 {
65     [_playerControllerProxy release];
66     [_loadedTimeRanges release];
67     [_seekableTimeRanges release];
68     [_timing release];
69     [_audioMediaSelectionOptions release];
70     [_legibleMediaSelectionOptions release];
71     [_currentAudioMediaSelectionOption release];
72     [_currentLegibleMediaSelectionOption release];
73     [_externalPlaybackAirPlayDeviceLocalizedName release];
74     [super dealloc];
75 }
76
77 - (AVPlayer *)player
78 {
79     return nil;
80 }
81
82 - (id)forwardingTargetForSelector:(SEL)selector
83 {
84     UNUSED_PARAM(selector);
85     return self.playerControllerProxy;
86 }
87
88 - (void)play:(id)sender
89 {
90     UNUSED_PARAM(sender);
91     if (!self.delegate)
92         return;
93     self.delegate->play();
94 }
95
96 - (void)pause:(id)sender
97 {
98     UNUSED_PARAM(sender);
99     if (!self.delegate)
100         return;
101     self.delegate->pause();
102 }
103
104 - (void)togglePlayback:(id)sender
105 {
106     UNUSED_PARAM(sender);
107     if (!self.delegate)
108         return;
109     self.delegate->togglePlayState();
110 }
111
112 - (void)togglePlaybackEvenWhenInBackground:(id)sender
113 {
114     [self togglePlayback:sender];
115 }
116
117 - (BOOL)isPlaying
118 {
119     return [self rate];
120 }
121
122 - (void)setPlaying:(BOOL)playing
123 {
124     if (!self.delegate)
125         return;
126     if (playing)
127         self.delegate->play();
128     else
129         self.delegate->pause();
130 }
131
132 + (NSSet *)keyPathsForValuesAffectingPlaying
133 {
134     return [NSSet setWithObject:@"rate"];
135 }
136
137 - (void)beginScrubbing:(id)sender
138 {
139     UNUSED_PARAM(sender);
140     if (!self.delegate)
141         return;
142     self.delegate->beginScrubbing();
143 }
144
145 - (void)endScrubbing:(id)sender
146 {
147     UNUSED_PARAM(sender);
148     if (!self.delegate)
149         return;
150     self.delegate->endScrubbing();
151 }
152
153 - (void)seekToTime:(NSTimeInterval)time
154 {
155     if (!self.delegate)
156         return;
157     self.delegate->fastSeek(time);
158 }
159
160 - (NSTimeInterval)currentTimeWithinEndTimes
161 {
162     return self.timing.currentValue;
163 }
164
165 - (void)setCurrentTimeWithinEndTimes:(NSTimeInterval)currentTimeWithinEndTimes
166 {
167     [self seekToTime:currentTimeWithinEndTimes];
168 }
169
170 + (NSSet *)keyPathsForValuesAffectingCurrentTimeWithinEndTimes
171 {
172     return [NSSet setWithObject:@"timing"];
173 }
174
175 - (BOOL)hasLiveStreamingContent
176 {
177     if ([self status] == AVPlayerControllerStatusReadyToPlay)
178         return [self contentDuration] == std::numeric_limits<float>::infinity();
179     return NO;
180 }
181
182 + (NSSet *)keyPathsForValuesAffectingHasLiveStreamingContent
183 {
184     return [NSSet setWithObjects:@"contentDuration", @"status", nil];
185 }
186
187 - (void)skipBackwardThirtySeconds:(id)sender
188 {
189     UNUSED_PARAM(sender);
190     BOOL isTimeWithinSeekableTimeRanges = NO;
191     CMTime currentTime = CMTimeMakeWithSeconds([[self timing] currentValue], 1000);
192     CMTime thirtySecondsBeforeCurrentTime = CMTimeSubtract(currentTime, CMTimeMake(30, 1));
193
194     for (NSValue *seekableTimeRangeValue in [self seekableTimeRanges]) {
195         if (CMTimeRangeContainsTime([seekableTimeRangeValue CMTimeRangeValue], thirtySecondsBeforeCurrentTime)) {
196             isTimeWithinSeekableTimeRanges = YES;
197             break;
198         }
199     }
200
201     if (isTimeWithinSeekableTimeRanges)
202         [self seekToTime:CMTimeGetSeconds(thirtySecondsBeforeCurrentTime)];
203 }
204
205 - (void)gotoEndOfSeekableRanges:(id)sender
206 {
207     UNUSED_PARAM(sender);
208     NSTimeInterval timeAtEndOfSeekableTimeRanges = NAN;
209
210     for (NSValue *seekableTimeRangeValue in [self seekableTimeRanges]) {
211         CMTimeRange seekableTimeRange = [seekableTimeRangeValue CMTimeRangeValue];
212         NSTimeInterval endOfSeekableTimeRange = CMTimeGetSeconds(CMTimeRangeGetEnd(seekableTimeRange));
213         if (isnan(timeAtEndOfSeekableTimeRanges) || endOfSeekableTimeRange > timeAtEndOfSeekableTimeRanges)
214             timeAtEndOfSeekableTimeRanges = endOfSeekableTimeRange;
215     }
216
217     if (!isnan(timeAtEndOfSeekableTimeRanges))
218         [self seekToTime:timeAtEndOfSeekableTimeRanges];
219 }
220
221 - (BOOL)canScanForward
222 {
223     return [self canPlay];
224 }
225
226 + (NSSet *)keyPathsForValuesAffectingCanScanForward
227 {
228     return [NSSet setWithObject:@"canPlay"];
229 }
230
231 - (void)beginScanningForward:(id)sender
232 {
233     UNUSED_PARAM(sender);
234     if (!self.delegate)
235         return;
236     self.delegate->beginScanningForward();
237 }
238
239 - (void)endScanningForward:(id)sender
240 {
241     UNUSED_PARAM(sender);
242     if (!self.delegate)
243         return;
244     self.delegate->endScanning();
245 }
246
247 - (void)beginScanningBackward:(id)sender
248 {
249     UNUSED_PARAM(sender);
250     if (!self.delegate)
251         return;
252     self.delegate->beginScanningBackward();
253 }
254
255 - (void)endScanningBackward:(id)sender
256 {
257     UNUSED_PARAM(sender);
258     if (!self.delegate)
259         return;
260     self.delegate->endScanning();
261 }
262
263 - (BOOL)canSeekToBeginning
264 {
265     CMTime minimumTime = kCMTimeIndefinite;
266
267     for (NSValue *value in [self seekableTimeRanges])
268         minimumTime = CMTimeMinimum([value CMTimeRangeValue].start, minimumTime);
269
270     return CMTIME_IS_NUMERIC(minimumTime);
271 }
272
273 + (NSSet *)keyPathsForValuesAffectingCanSeekToBeginning
274 {
275     return [NSSet setWithObject:@"seekableTimeRanges"];
276 }
277
278 - (void)seekToBeginning:(id)sender
279 {
280     UNUSED_PARAM(sender);
281     if (!self.delegate)
282         return;
283     self.delegate->seekToTime(-INFINITY);
284 }
285
286 - (void)seekChapterBackward:(id)sender
287 {
288     [self seekToBeginning:sender];
289 }
290
291 - (BOOL)canSeekToEnd
292 {
293     CMTime maximumTime = kCMTimeIndefinite;
294
295     for (NSValue *value in [self seekableTimeRanges])
296         maximumTime = CMTimeMaximum(CMTimeRangeGetEnd([value CMTimeRangeValue]), maximumTime);
297
298     return CMTIME_IS_NUMERIC(maximumTime);
299 }
300
301 + (NSSet *)keyPathsForValuesAffectingCanSeekToEnd
302 {
303     return [NSSet setWithObject:@"seekableTimeRanges"];
304 }
305
306 - (void)seekToEnd:(id)sender
307 {
308     UNUSED_PARAM(sender);
309     if (!self.delegate)
310         return;
311     self.delegate->seekToTime(INFINITY);
312 }
313
314 - (void)seekChapterForward:(id)sender
315 {
316     [self seekToEnd:sender];
317 }
318
319 - (BOOL)hasMediaSelectionOptions
320 {
321     return [self hasAudioMediaSelectionOptions] || [self hasLegibleMediaSelectionOptions];
322 }
323
324 + (NSSet *)keyPathsForValuesAffectingHasMediaSelectionOptions
325 {
326     return [NSSet setWithObjects:@"hasAudioMediaSelectionOptions", @"hasLegibleMediaSelectionOptions", nil];
327 }
328
329 - (BOOL)hasAudioMediaSelectionOptions
330 {
331     return [[self audioMediaSelectionOptions] count] > 1;
332 }
333
334 + (NSSet *)keyPathsForValuesAffectingHasAudioMediaSelectionOptions
335 {
336     return [NSSet setWithObject:@"audioMediaSelectionOptions"];
337 }
338
339 - (BOOL)hasLegibleMediaSelectionOptions
340 {
341     const NSUInteger numDefaultLegibleOptions = 2;
342     return [[self legibleMediaSelectionOptions] count] > numDefaultLegibleOptions;
343 }
344
345 + (NSSet *)keyPathsForValuesAffectingHasLegibleMediaSelectionOptions
346 {
347     return [NSSet setWithObject:@"legibleMediaSelectionOptions"];
348 }
349
350 - (WebAVMediaSelectionOption *)currentAudioMediaSelectionOption
351 {
352     return _currentAudioMediaSelectionOption;
353 }
354
355 - (void)setCurrentAudioMediaSelectionOption:(WebAVMediaSelectionOption *)option
356 {
357     if (option == _currentAudioMediaSelectionOption)
358         return;
359
360     [_currentAudioMediaSelectionOption release];
361     _currentAudioMediaSelectionOption = [option retain];
362
363     if (!self.delegate)
364         return;
365
366     NSInteger index = NSNotFound;
367
368     if (option && self.audioMediaSelectionOptions)
369         index = [self.audioMediaSelectionOptions indexOfObject:option];
370
371     if (index == NSNotFound)
372         return;
373
374     self.delegate->selectAudioMediaOption(index);
375 }
376
377 - (WebAVMediaSelectionOption *)currentLegibleMediaSelectionOption
378 {
379     return _currentLegibleMediaSelectionOption;
380 }
381
382 - (void)setCurrentLegibleMediaSelectionOption:(WebAVMediaSelectionOption *)option
383 {
384     if (option == _currentLegibleMediaSelectionOption)
385         return;
386
387     [_currentLegibleMediaSelectionOption release];
388     _currentLegibleMediaSelectionOption = [option retain];
389
390     if (!self.delegate)
391         return;
392
393     NSInteger index = NSNotFound;
394
395     if (option && self.legibleMediaSelectionOptions)
396         index = [self.legibleMediaSelectionOptions indexOfObject:option];
397
398     if (index == NSNotFound)
399         return;
400
401     self.delegate->selectLegibleMediaOption(index);
402 }
403
404 - (BOOL)isPlayingOnExternalScreen
405 {
406     return [self isExternalPlaybackActive] || [self isPlayingOnSecondScreen];
407 }
408
409 + (NSSet *)keyPathsForValuesAffectingPlayingOnExternalScreen
410 {
411     return [NSSet setWithObjects:@"externalPlaybackActive", @"playingOnSecondScreen", nil];
412 }
413
414 - (BOOL)isPictureInPictureInterrupted
415 {
416     return _pictureInPictureInterrupted;
417 }
418
419 - (void)setPictureInPictureInterrupted:(BOOL)pictureInPictureInterrupted
420 {
421     if (_pictureInPictureInterrupted != pictureInPictureInterrupted) {
422         _pictureInPictureInterrupted = pictureInPictureInterrupted;
423         if (pictureInPictureInterrupted)
424             [self setPlaying:NO];
425     }
426 }
427 @end
428
429 @implementation WebAVMediaSelectionOption
430
431 - (void)dealloc
432 {
433     [_localizedDisplayName release];
434     [super dealloc];
435 }
436
437 @end
438
439 #endif // PLATFORM(IOS)
440