1/* 2 * Copyright (C) 2014 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 29#if PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000 30 31#import "WebVideoFullscreenInterfaceAVKit.h" 32 33#import "Logging.h" 34#import "GeometryUtilities.h" 35#import "WebVideoFullscreenModel.h" 36#import <AVFoundation/AVTime.h> 37#import <AVKit/AVKit.h> 38#import <AVKit/AVPlayerController.h> 39#import <AVKit/AVPlayerViewController_Private.h> 40#import <AVKit/AVPlayerViewController_WebKitOnly.h> 41#import <AVKit/AVValueTiming.h> 42#import <AVKit/AVVideoLayer.h> 43#import <CoreMedia/CMTime.h> 44#import <UIKit/UIKit.h> 45#import <WebCore/RuntimeApplicationChecksIOS.h> 46#import <WebCore/SoftLinking.h> 47#import <WebCore/TimeRanges.h> 48#import <WebCore/WebCoreThreadRun.h> 49#import <wtf/RetainPtr.h> 50#import <wtf/text/CString.h> 51 52using namespace WebCore; 53 54SOFT_LINK_FRAMEWORK(AVFoundation) 55SOFT_LINK_CLASS(AVFoundation, AVPlayerLayer) 56 57SOFT_LINK_FRAMEWORK(AVKit) 58SOFT_LINK_CLASS(AVKit, AVPlayerController) 59SOFT_LINK_CLASS(AVKit, AVPlayerViewController) 60SOFT_LINK_CLASS(AVKit, AVValueTiming) 61 62SOFT_LINK_FRAMEWORK(UIKit) 63SOFT_LINK_CLASS(UIKit, UIApplication) 64SOFT_LINK_CLASS(UIKit, UIScreen) 65SOFT_LINK_CLASS(UIKit, UIWindow) 66SOFT_LINK_CLASS(UIKit, UIView) 67SOFT_LINK_CLASS(UIKit, UIViewController) 68SOFT_LINK_CLASS(UIKit, UIColor) 69 70SOFT_LINK_FRAMEWORK(CoreMedia) 71SOFT_LINK(CoreMedia, CMTimeMakeWithSeconds, CMTime, (Float64 seconds, int32_t preferredTimeScale), (seconds, preferredTimeScale)) 72SOFT_LINK(CoreMedia, CMTimeGetSeconds, Float64, (CMTime time), (time)) 73SOFT_LINK(CoreMedia, CMTimeMake, CMTime, (int64_t value, int32_t timescale), (value, timescale)) 74SOFT_LINK(CoreMedia, CMTimeRangeContainsTime, Boolean, (CMTimeRange range, CMTime time), (range, time)) 75SOFT_LINK(CoreMedia, CMTimeRangeGetEnd, CMTime, (CMTimeRange range), (range)) 76SOFT_LINK(CoreMedia, CMTimeRangeMake, CMTimeRange, (CMTime start, CMTime duration), (start, duration)) 77SOFT_LINK(CoreMedia, CMTimeSubtract, CMTime, (CMTime minuend, CMTime subtrahend), (minuend, subtrahend)) 78SOFT_LINK(CoreMedia, CMTimeMaximum, CMTime, (CMTime time1, CMTime time2), (time1, time2)) 79SOFT_LINK(CoreMedia, CMTimeMinimum, CMTime, (CMTime time1, CMTime time2), (time1, time2)) 80SOFT_LINK_CONSTANT(CoreMedia, kCMTimeIndefinite, CMTime) 81 82#define kCMTimeIndefinite getkCMTimeIndefinite() 83 84@class WebAVMediaSelectionOption; 85 86@interface WebAVPlayerController : NSObject <AVPlayerViewControllerDelegate> 87{ 88 WebAVMediaSelectionOption *_currentAudioMediaSelectionOption; 89 WebAVMediaSelectionOption *_currentLegibleMediaSelectionOption; 90} 91 92@property(retain) AVPlayerController* playerControllerProxy; 93@property(assign) WebVideoFullscreenModel* delegate; 94 95@property (readonly) BOOL canScanForward; 96@property BOOL canScanBackward; 97@property (readonly) BOOL canSeekToBeginning; 98@property (readonly) BOOL canSeekToEnd; 99 100@property BOOL canPlay; 101@property(getter=isPlaying) BOOL playing; 102@property BOOL canPause; 103@property BOOL canTogglePlayback; 104@property double rate; 105@property BOOL canSeek; 106@property NSTimeInterval contentDuration; 107@property NSSize contentDimensions; 108@property BOOL hasEnabledAudio; 109@property BOOL hasEnabledVideo; 110@property NSTimeInterval minTime; 111@property NSTimeInterval maxTime; 112@property NSTimeInterval contentDurationWithinEndTimes; 113@property(retain) NSArray *loadedTimeRanges; 114@property AVPlayerControllerStatus status; 115@property(retain) AVValueTiming *timing; 116@property(retain) NSArray *seekableTimeRanges; 117 118@property (readonly) BOOL hasMediaSelectionOptions; 119@property (readonly) BOOL hasAudioMediaSelectionOptions; 120@property (retain) NSArray *audioMediaSelectionOptions; 121@property (retain) WebAVMediaSelectionOption *currentAudioMediaSelectionOption; 122@property (readonly) BOOL hasLegibleMediaSelectionOptions; 123@property (retain) NSArray *legibleMediaSelectionOptions; 124@property (retain) WebAVMediaSelectionOption *currentLegibleMediaSelectionOption; 125 126@property (readonly, getter=isPlayingOnExternalScreen) BOOL playingOnExternalScreen; 127@property (getter=isExternalPlaybackActive) BOOL externalPlaybackActive; 128@property AVPlayerControllerExternalPlaybackType externalPlaybackType; 129@property (retain) NSString *externalPlaybackAirPlayDeviceLocalizedName; 130 131- (BOOL)playerViewController:(AVPlayerViewController *)playerViewController shouldExitFullScreenWithReason:(AVPlayerViewControllerExitFullScreenReason)reason; 132@end 133 134@implementation WebAVPlayerController 135 136- (instancetype)init 137{ 138 if (!(self = [super init])) 139 return self; 140 141 initAVPlayerController(); 142 self.playerControllerProxy = [[[getAVPlayerControllerClass() alloc] init] autorelease]; 143 return self; 144} 145 146- (void)dealloc 147{ 148 [_playerControllerProxy release]; 149 [_loadedTimeRanges release]; 150 [_seekableTimeRanges release]; 151 [_timing release]; 152 [_audioMediaSelectionOptions release]; 153 [_legibleMediaSelectionOptions release]; 154 [_currentAudioMediaSelectionOption release]; 155 [_currentLegibleMediaSelectionOption release]; 156 [super dealloc]; 157} 158 159- (id)forwardingTargetForSelector:(SEL)selector 160{ 161 UNUSED_PARAM(selector); 162 return self.playerControllerProxy; 163} 164 165- (BOOL)playerViewController:(AVPlayerViewController *)playerViewController shouldExitFullScreenWithReason:(AVPlayerViewControllerExitFullScreenReason)reason 166{ 167 UNUSED_PARAM(playerViewController); 168 UNUSED_PARAM(reason); 169 ASSERT(self.delegate); 170 if (reason == AVPlayerViewControllerExitFullScreenReasonDoneButtonTapped || reason == AVPlayerViewControllerExitFullScreenReasonRemoteControlStopEventReceived) 171 self.delegate->pause(); 172 self.delegate->requestExitFullscreen(); 173 return NO; 174} 175 176- (void)play:(id)sender 177{ 178 UNUSED_PARAM(sender); 179 ASSERT(self.delegate); 180 self.delegate->play(); 181} 182 183- (void)pause:(id)sender 184{ 185 UNUSED_PARAM(sender); 186 ASSERT(self.delegate); 187 self.delegate->pause(); 188} 189 190- (void)togglePlayback:(id)sender 191{ 192 UNUSED_PARAM(sender); 193 ASSERT(self.delegate); 194 self.delegate->togglePlayState(); 195} 196 197- (BOOL)isPlaying 198{ 199 return [self rate] != 0; 200} 201 202- (void)setPlaying:(BOOL)playing 203{ 204 ASSERT(self.delegate); 205 if (playing) 206 self.delegate->play(); 207 else 208 self.delegate->pause(); 209 } 210 211+ (NSSet *)keyPathsForValuesAffectingPlaying 212{ 213 return [NSSet setWithObject:@"rate"]; 214} 215 216- (void)beginScrubbing:(id)sender 217{ 218 UNUSED_PARAM(sender); 219 ASSERT(self.delegate); 220 self.delegate->beginScrubbing(); 221} 222 223- (void)endScrubbing:(id)sender 224{ 225 UNUSED_PARAM(sender); 226 ASSERT(self.delegate); 227 self.delegate->endScrubbing(); 228} 229 230- (void)seekToTime:(NSTimeInterval)time 231{ 232 ASSERT(self.delegate); 233 self.delegate->fastSeek(time); 234} 235 236- (BOOL)hasLiveStreamingContent 237{ 238 if ([self status] == AVPlayerControllerStatusReadyToPlay) 239 return [self contentDuration] == std::numeric_limits<float>::infinity(); 240 return NO; 241} 242 243+ (NSSet *)keyPathsForValuesAffectingHasLiveStreamingContent 244{ 245 return [NSSet setWithObjects:@"contentDuration", @"status", nil]; 246} 247 248- (void)skipBackwardThirtySeconds:(id)sender 249{ 250 UNUSED_PARAM(sender); 251 BOOL isTimeWithinSeekableTimeRanges = NO; 252 CMTime currentTime = CMTimeMakeWithSeconds([[self timing] currentValue], 1000); 253 CMTime thirtySecondsBeforeCurrentTime = CMTimeSubtract(currentTime, CMTimeMake(30, 1)); 254 255 for (NSValue *seekableTimeRangeValue in [self seekableTimeRanges]) { 256 if (CMTimeRangeContainsTime([seekableTimeRangeValue CMTimeRangeValue], thirtySecondsBeforeCurrentTime)) { 257 isTimeWithinSeekableTimeRanges = YES; 258 break; 259 } 260 } 261 262 if (isTimeWithinSeekableTimeRanges) 263 [self seekToTime:CMTimeGetSeconds(thirtySecondsBeforeCurrentTime)]; 264} 265 266- (void)gotoEndOfSeekableRanges:(id)sender 267{ 268 UNUSED_PARAM(sender); 269 NSTimeInterval timeAtEndOfSeekableTimeRanges = NAN; 270 271 for (NSValue *seekableTimeRangeValue in [self seekableTimeRanges]) { 272 CMTimeRange seekableTimeRange = [seekableTimeRangeValue CMTimeRangeValue]; 273 NSTimeInterval endOfSeekableTimeRange = CMTimeGetSeconds(CMTimeRangeGetEnd(seekableTimeRange)); 274 if (isnan(timeAtEndOfSeekableTimeRanges) || endOfSeekableTimeRange > timeAtEndOfSeekableTimeRanges) 275 timeAtEndOfSeekableTimeRanges = endOfSeekableTimeRange; 276 } 277 278 if (!isnan(timeAtEndOfSeekableTimeRanges)) 279 [self seekToTime:timeAtEndOfSeekableTimeRanges]; 280} 281 282- (BOOL)canScanForward 283{ 284 return [self canPlay]; 285} 286 287+ (NSSet *)keyPathsForValuesAffectingCanScanForward 288{ 289 return [NSSet setWithObject:@"canPlay"]; 290} 291 292- (void)beginScanningForward:(id)sender 293{ 294 UNUSED_PARAM(sender); 295 ASSERT(self.delegate); 296 self.delegate->beginScanningForward(); 297} 298 299- (void)endScanningForward:(id)sender 300{ 301 UNUSED_PARAM(sender); 302 ASSERT(self.delegate); 303 self.delegate->endScanning(); 304} 305 306- (void)beginScanningBackward:(id)sender 307{ 308 UNUSED_PARAM(sender); 309 ASSERT(self.delegate); 310 self.delegate->beginScanningBackward(); 311} 312 313- (void)endScanningBackward:(id)sender 314{ 315 UNUSED_PARAM(sender); 316 ASSERT(self.delegate); 317 self.delegate->endScanning(); 318} 319 320- (BOOL)canSeekToBeginning 321{ 322 CMTime minimumTime = kCMTimeIndefinite; 323 324 for (NSValue *value in [self seekableTimeRanges]) 325 minimumTime = CMTimeMinimum([value CMTimeRangeValue].start, minimumTime); 326 327 return CMTIME_IS_NUMERIC(minimumTime); 328} 329 330+ (NSSet *)keyPathsForValuesAffectingCanSeekToBeginning 331{ 332 return [NSSet setWithObject:@"seekableTimeRanges"]; 333} 334 335- (void)seekToBeginning:(id)sender 336{ 337 UNUSED_PARAM(sender); 338 ASSERT(self.delegate); 339 340 self.delegate->seekToTime(-INFINITY); 341} 342 343- (void)seekChapterBackward:(id)sender 344{ 345 [self seekToBeginning:sender]; 346} 347 348- (BOOL)canSeekToEnd 349{ 350 CMTime maximumTime = kCMTimeIndefinite; 351 352 for (NSValue *value in [self seekableTimeRanges]) 353 maximumTime = CMTimeMaximum(CMTimeRangeGetEnd([value CMTimeRangeValue]), maximumTime); 354 355 return CMTIME_IS_NUMERIC(maximumTime); 356} 357 358+ (NSSet *)keyPathsForValuesAffectingCanSeekToEnd 359{ 360 return [NSSet setWithObject:@"seekableTimeRanges"]; 361} 362 363- (void)seekToEnd:(id)sender 364{ 365 UNUSED_PARAM(sender); 366 ASSERT(self.delegate); 367 368 self.delegate->seekToTime(INFINITY); 369} 370 371- (void)seekChapterForward:(id)sender 372{ 373 [self seekToEnd:sender]; 374} 375 376- (BOOL)hasMediaSelectionOptions 377{ 378 return [self hasAudioMediaSelectionOptions] || [self hasLegibleMediaSelectionOptions]; 379} 380 381+ (NSSet *)keyPathsForValuesAffectingHasMediaSelectionOptions 382{ 383 return [NSSet setWithObjects:@"hasAudioMediaSelectionOptions", @"hasLegibleMediaSelectionOptions", nil]; 384} 385 386- (BOOL)hasAudioMediaSelectionOptions 387{ 388 return [[self audioMediaSelectionOptions] count] > 0; 389} 390 391+ (NSSet *)keyPathsForValuesAffectingHasAudioMediaSelectionOptions 392{ 393 return [NSSet setWithObject:@"audioMediaSelectionOptions"]; 394} 395 396- (BOOL)hasLegibleMediaSelectionOptions 397{ 398 return [[self legibleMediaSelectionOptions] count] > 0; 399} 400 401+ (NSSet *)keyPathsForValuesAffectingHasLegibleMediaSelectionOptions 402{ 403 return [NSSet setWithObject:@"legibleMediaSelectionOptions"]; 404} 405 406- (WebAVMediaSelectionOption *)currentAudioMediaSelectionOption 407{ 408 return _currentAudioMediaSelectionOption; 409} 410 411- (void)setCurrentAudioMediaSelectionOption:(WebAVMediaSelectionOption *)option 412{ 413 if (option == _currentAudioMediaSelectionOption) 414 return; 415 416 [_currentAudioMediaSelectionOption release]; 417 _currentAudioMediaSelectionOption = [option retain]; 418 419 ASSERT(self.delegate); 420 421 NSInteger index = NSNotFound; 422 423 if (option && self.audioMediaSelectionOptions) 424 index = [self.audioMediaSelectionOptions indexOfObject:option]; 425 426 self.delegate->selectAudioMediaOption(index != NSNotFound ? index : UINT64_MAX); 427} 428 429- (WebAVMediaSelectionOption *)currentLegibleMediaSelectionOption 430{ 431 return _currentLegibleMediaSelectionOption; 432} 433 434- (void)setCurrentLegibleMediaSelectionOption:(WebAVMediaSelectionOption *)option 435{ 436 if (option == _currentLegibleMediaSelectionOption) 437 return; 438 439 [_currentLegibleMediaSelectionOption release]; 440 _currentLegibleMediaSelectionOption = [option retain]; 441 442 ASSERT(self.delegate); 443 444 NSInteger index = NSNotFound; 445 446 if (option && self.legibleMediaSelectionOptions) 447 index = [self.legibleMediaSelectionOptions indexOfObject:option]; 448 449 self.delegate->selectLegibleMediaOption(index != NSNotFound ? index : UINT64_MAX); 450} 451 452- (BOOL)isPlayingOnExternalScreen 453{ 454 return [self isExternalPlaybackActive]; 455} 456 457+ (NSSet *)keyPathsForValuesAffectingPlayingOnExternalScreen 458{ 459 return [NSSet setWithObjects:@"externalPlaybackActive", nil]; 460} 461 462@end 463 464@interface WebAVMediaSelectionOption : NSObject 465@property (retain) NSString *localizedDisplayName; 466@end 467 468@implementation WebAVMediaSelectionOption 469@end 470 471@interface WebAVVideoLayer : CALayer <AVVideoLayer> 472+(WebAVVideoLayer *)videoLayer; 473@property (nonatomic) AVVideoLayerGravity videoLayerGravity; 474@property (nonatomic, getter = isReadyForDisplay) BOOL readyForDisplay; 475@property (nonatomic) CGRect videoRect; 476- (void)setPlayerViewController:(AVPlayerViewController *)playerViewController; 477- (void)setPlayerController:(AVPlayerController *)playerController; 478@end 479 480@implementation WebAVVideoLayer 481{ 482 RetainPtr<WebAVPlayerController> _avPlayerController; 483 RetainPtr<AVPlayerViewController> _avPlayerViewController; 484 AVVideoLayerGravity _videoLayerGravity; 485} 486 487+(WebAVVideoLayer *)videoLayer 488{ 489 return [[[WebAVVideoLayer alloc] init] autorelease]; 490} 491 492- (instancetype)init 493{ 494 self = [super init]; 495 if (self) { 496 [self setMasksToBounds:YES]; 497 [self setVideoLayerGravity:AVVideoLayerGravityResizeAspect]; 498 } 499 return self; 500} 501 502- (void)setPlayerController:(AVPlayerController *)playerController 503{ 504 ASSERT(!playerController || [playerController isKindOfClass:[WebAVPlayerController class]]); 505 _avPlayerController = (WebAVPlayerController *)playerController; 506} 507 508- (void)setPlayerViewController:(AVPlayerViewController *)playerViewController 509{ 510 _avPlayerViewController = playerViewController; 511} 512 513- (void)setBounds:(CGRect)bounds 514{ 515 [super setBounds:bounds]; 516 517 if (![_avPlayerController delegate] || !_avPlayerViewController) 518 return; 519 520 UIView* rootView = [[_avPlayerViewController view] window]; 521 if (!rootView) 522 return; 523 524 FloatRect rootBounds = [rootView bounds]; 525 [_avPlayerController delegate]->setVideoLayerFrame(rootBounds); 526 527 FloatRect sourceBounds = largestRectWithAspectRatioInsideRect(CGRectGetWidth(bounds) / CGRectGetHeight(bounds), rootBounds); 528 CATransform3D transform = CATransform3DMakeScale(bounds.size.width / sourceBounds.width(), bounds.size.height / sourceBounds.height(), 1); 529 transform = CATransform3DTranslate(transform, bounds.origin.x - sourceBounds.x(), bounds.origin.y - sourceBounds.y(), 0); 530 [self setSublayerTransform:transform]; 531} 532 533- (void)setVideoLayerGravity:(AVVideoLayerGravity)videoLayerGravity 534{ 535 _videoLayerGravity = videoLayerGravity; 536 537 if (![_avPlayerController delegate]) 538 return; 539 540 WebCore::WebVideoFullscreenModel::VideoGravity gravity = WebCore::WebVideoFullscreenModel::VideoGravityResizeAspect; 541 if (videoLayerGravity == AVVideoLayerGravityResize) 542 gravity = WebCore::WebVideoFullscreenModel::VideoGravityResize; 543 if (videoLayerGravity == AVVideoLayerGravityResizeAspect) 544 gravity = WebCore::WebVideoFullscreenModel::VideoGravityResizeAspect; 545 else if (videoLayerGravity == AVVideoLayerGravityResizeAspectFill) 546 gravity = WebCore::WebVideoFullscreenModel::VideoGravityResizeAspectFill; 547 else 548 ASSERT_NOT_REACHED(); 549 550 [_avPlayerController delegate]->setVideoLayerGravity(gravity); 551} 552 553- (AVVideoLayerGravity)videoLayerGravity 554{ 555 return _videoLayerGravity; 556} 557 558@end 559 560WebVideoFullscreenInterfaceAVKit::WebVideoFullscreenInterfaceAVKit() 561 : m_videoFullscreenModel(nullptr) 562{ 563} 564 565WebAVPlayerController *WebVideoFullscreenInterfaceAVKit::playerController() 566{ 567 if (!m_playerController) 568 { 569 m_playerController = adoptNS([[WebAVPlayerController alloc] init]); 570 if (m_videoFullscreenModel) 571 [m_playerController setDelegate:m_videoFullscreenModel]; 572 } 573 return m_playerController.get(); 574} 575 576 577void WebVideoFullscreenInterfaceAVKit::setWebVideoFullscreenModel(WebVideoFullscreenModel* model) 578{ 579 m_videoFullscreenModel = model; 580 [m_playerController setDelegate:m_videoFullscreenModel]; 581} 582 583void WebVideoFullscreenInterfaceAVKit::setWebVideoFullscreenChangeObserver(WebVideoFullscreenChangeObserver* observer) 584{ 585 m_fullscreenChangeObserver = observer; 586} 587 588void WebVideoFullscreenInterfaceAVKit::setDuration(double duration) 589{ 590 WebAVPlayerController* playerController = this->playerController(); 591 592 __block RefPtr<WebVideoFullscreenInterfaceAVKit> protect(this); 593 594 dispatch_async(dispatch_get_main_queue(), ^{ 595 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=127017 use correct values instead of duration for all these 596 playerController.contentDuration = duration; 597 playerController.maxTime = duration; 598 playerController.contentDurationWithinEndTimes = duration; 599 playerController.loadedTimeRanges = @[@0, @(duration)]; 600 601 // FIXME: we take this as an indication that playback is ready. 602 playerController.canPlay = YES; 603 playerController.canPause = YES; 604 playerController.canTogglePlayback = YES; 605 playerController.hasEnabledAudio = YES; 606 playerController.canSeek = YES; 607 playerController.minTime = 0; 608 playerController.status = AVPlayerControllerStatusReadyToPlay; 609 610 protect = nullptr; 611 }); 612} 613 614void WebVideoFullscreenInterfaceAVKit::setCurrentTime(double currentTime, double anchorTime) 615{ 616 __block RefPtr<WebVideoFullscreenInterfaceAVKit> protect(this); 617 618 dispatch_async(dispatch_get_main_queue(), ^{ 619 NSTimeInterval anchorTimeStamp = ![playerController() rate] ? NAN : anchorTime; 620 AVValueTiming *timing = [getAVValueTimingClass() valueTimingWithAnchorValue:currentTime 621 anchorTimeStamp:anchorTimeStamp rate:0]; 622 playerController().timing = timing; 623 624 protect = nullptr; 625 }); 626} 627 628void WebVideoFullscreenInterfaceAVKit::setRate(bool isPlaying, float playbackRate) 629{ 630 __block RefPtr<WebVideoFullscreenInterfaceAVKit> protect(this); 631 632 dispatch_async(dispatch_get_main_queue(), ^{ 633 playerController().rate = isPlaying ? playbackRate : 0.; 634 635 protect = nullptr; 636 }); 637} 638 639void WebVideoFullscreenInterfaceAVKit::setVideoDimensions(bool hasVideo, float width, float height) 640{ 641 __block RefPtr<WebVideoFullscreenInterfaceAVKit> protect(this); 642 643 dispatch_async(dispatch_get_main_queue(), ^{ 644 playerController().hasEnabledVideo = hasVideo; 645 playerController().contentDimensions = CGSizeMake(width, height); 646 647 protect = nullptr; 648 }); 649} 650 651void WebVideoFullscreenInterfaceAVKit::setSeekableRanges(const TimeRanges& timeRanges) 652{ 653 NSMutableArray* seekableRanges = [NSMutableArray array]; 654 ExceptionCode exceptionCode; 655 656 for (unsigned i = 0; i < timeRanges.length(); i++) { 657 double start = timeRanges.start(i, exceptionCode); 658 double end = timeRanges.end(i, exceptionCode); 659 660 CMTimeRange range = CMTimeRangeMake(CMTimeMakeWithSeconds(start, 1000), CMTimeMakeWithSeconds(end-start, 1000)); 661 [seekableRanges addObject:[NSValue valueWithCMTimeRange:range]]; 662 } 663 664 __block RefPtr<WebVideoFullscreenInterfaceAVKit> protect(this); 665 666 dispatch_async(dispatch_get_main_queue(), ^{ 667 playerController().seekableTimeRanges = seekableRanges; 668 669 protect = nullptr; 670 }); 671} 672 673void WebVideoFullscreenInterfaceAVKit::setCanPlayFastReverse(bool canPlayFastReverse) 674{ 675 playerController().canScanBackward = canPlayFastReverse; 676} 677 678static NSMutableArray *mediaSelectionOptions(const Vector<String>& options) 679{ 680 NSMutableArray *webOptions = [NSMutableArray arrayWithCapacity:options.size()]; 681 for (auto& name : options) { 682 RetainPtr<WebAVMediaSelectionOption> webOption = adoptNS([[WebAVMediaSelectionOption alloc] init]); 683 [webOption setLocalizedDisplayName:name]; 684 [webOptions addObject:webOption.get()]; 685 } 686 return webOptions; 687} 688 689void WebVideoFullscreenInterfaceAVKit::setAudioMediaSelectionOptions(const Vector<String>& options, uint64_t selectedIndex) 690{ 691 NSMutableArray *webOptions = mediaSelectionOptions(options); 692 __block RefPtr<WebVideoFullscreenInterfaceAVKit> protect(this); 693 694 dispatch_async(dispatch_get_main_queue(), ^{ 695 playerController().audioMediaSelectionOptions = webOptions; 696 if (selectedIndex < webOptions.count) 697 playerController().currentAudioMediaSelectionOption = webOptions[(size_t)selectedIndex]; 698 699 protect = nullptr; 700 }); 701} 702 703void WebVideoFullscreenInterfaceAVKit::setLegibleMediaSelectionOptions(const Vector<String>& options, uint64_t selectedIndex) 704{ 705 NSMutableArray *webOptions = mediaSelectionOptions(options); 706 __block RefPtr<WebVideoFullscreenInterfaceAVKit> protect(this); 707 708 dispatch_async(dispatch_get_main_queue(), ^{ 709 playerController().legibleMediaSelectionOptions = webOptions; 710 if (selectedIndex < webOptions.count) 711 playerController().currentLegibleMediaSelectionOption = webOptions[(size_t)selectedIndex]; 712 713 protect = nullptr; 714 }); 715} 716 717void WebVideoFullscreenInterfaceAVKit::setExternalPlayback(bool enabled, ExternalPlaybackTargetType targetType, String localizedDeviceName) 718{ 719 AVPlayerControllerExternalPlaybackType externalPlaybackType = AVPlayerControllerExternalPlaybackTypeNone; 720 if (targetType == TargetTypeAirPlay) 721 externalPlaybackType = AVPlayerControllerExternalPlaybackTypeAirPlay; 722 else if (targetType == TargetTypeTVOut) 723 externalPlaybackType = AVPlayerControllerExternalPlaybackTypeTVOut; 724 725 __block RefPtr<WebVideoFullscreenInterfaceAVKit> protect(this); 726 727 dispatch_async(dispatch_get_main_queue(), ^{ 728 playerController().externalPlaybackAirPlayDeviceLocalizedName = localizedDeviceName; 729 playerController().externalPlaybackType = externalPlaybackType; 730 playerController().externalPlaybackActive = enabled; 731 [m_videoLayerContainer.get() setHidden:enabled]; 732 733 protect = nullptr; 734 }); 735} 736 737void WebVideoFullscreenInterfaceAVKit::setupFullscreen(PlatformLayer& videoLayer, WebCore::IntRect initialRect, UIView* parentView) 738{ 739 __block RefPtr<WebVideoFullscreenInterfaceAVKit> protect(this); 740 741 m_videoLayer = &videoLayer; 742 743 dispatch_async(dispatch_get_main_queue(), ^{ 744 745 [CATransaction begin]; 746 [CATransaction setDisableActions:YES]; 747 m_parentView = parentView; 748 749 if (!applicationIsAdSheet()) { 750 m_window = adoptNS([[getUIWindowClass() alloc] initWithFrame:[[getUIScreenClass() mainScreen] bounds]]); 751 [m_window setBackgroundColor:[getUIColorClass() clearColor]]; 752 m_viewController = adoptNS([[getUIViewControllerClass() alloc] init]); 753 [[m_viewController view] setFrame:[m_window bounds]]; 754 [m_window setRootViewController:m_viewController.get()]; 755 [m_window makeKeyAndVisible]; 756 } 757 758 [m_videoLayer removeFromSuperlayer]; 759 760 m_videoLayerContainer = [WebAVVideoLayer videoLayer]; 761 [m_videoLayerContainer setHidden:playerController().externalPlaybackActive]; 762 [m_videoLayerContainer addSublayer:m_videoLayer.get()]; 763 764 CGSize videoSize = playerController().contentDimensions; 765 CGRect videoRect = CGRectMake(0, 0, videoSize.width, videoSize.height); 766 [m_videoLayerContainer setVideoRect:videoRect]; 767 768 m_playerViewController = adoptNS([[getAVPlayerViewControllerClass() alloc] initWithVideoLayer:m_videoLayerContainer.get()]); 769 [m_playerViewController setShowsPlaybackControls:NO]; 770 [m_playerViewController setPlayerController:(AVPlayerController *)playerController()]; 771 [m_playerViewController setDelegate:playerController()]; 772 [m_videoLayerContainer setPlayerViewController:m_playerViewController.get()]; 773 774 if (m_viewController) { 775 [m_viewController addChildViewController:m_playerViewController.get()]; 776 [[m_viewController view] addSubview:[m_playerViewController view]]; 777 [m_playerViewController view].frame = [parentView convertRect:initialRect toView:nil]; 778 } else { 779 [parentView addSubview:[m_playerViewController view]]; 780 [m_playerViewController view].frame = initialRect; 781 } 782 783 [[m_playerViewController view] setBackgroundColor:[getUIColorClass() clearColor]]; 784 [[m_playerViewController view] setNeedsLayout]; 785 [[m_playerViewController view] layoutIfNeeded]; 786 787 [CATransaction commit]; 788 789 dispatch_async(dispatch_get_main_queue(), ^{ 790 if (m_fullscreenChangeObserver) 791 m_fullscreenChangeObserver->didSetupFullscreen(); 792 793 protect = nullptr; 794 }); 795 }); 796} 797 798void WebVideoFullscreenInterfaceAVKit::enterFullscreen() 799{ 800 __block RefPtr<WebVideoFullscreenInterfaceAVKit> protect(this); 801 802 dispatch_async(dispatch_get_main_queue(), ^{ 803 [m_videoLayerContainer setBackgroundColor:[[getUIColorClass() blackColor] CGColor]]; 804 [m_playerViewController enterFullScreenWithCompletionHandler:^(BOOL, NSError*) 805 { 806 [m_playerViewController setShowsPlaybackControls:YES]; 807 if (m_fullscreenChangeObserver) 808 m_fullscreenChangeObserver->didEnterFullscreen(); 809 protect = nullptr; 810 }]; 811 }); 812} 813 814void WebVideoFullscreenInterfaceAVKit::exitFullscreen(WebCore::IntRect finalRect) 815{ 816 __block RefPtr<WebVideoFullscreenInterfaceAVKit> protect(this); 817 818 m_playerController = nil; 819 820 dispatch_async(dispatch_get_main_queue(), ^{ 821 [m_playerViewController setShowsPlaybackControls:NO]; 822 if (m_viewController) 823 [m_playerViewController view].frame = [m_parentView convertRect:finalRect toView:nil]; 824 else 825 [m_playerViewController view].frame = finalRect; 826 827 if ([m_videoLayerContainer videoLayerGravity] != AVVideoLayerGravityResizeAspect) 828 [m_videoLayerContainer setVideoLayerGravity:AVVideoLayerGravityResizeAspect]; 829 [[m_playerViewController view] layoutIfNeeded]; 830 [m_playerViewController exitFullScreenWithCompletionHandler:^(BOOL, NSError*) { 831 [m_videoLayerContainer setBackgroundColor:[[getUIColorClass() clearColor] CGColor]]; 832 [[m_playerViewController view] setBackgroundColor:[getUIColorClass() clearColor]]; 833 if (m_fullscreenChangeObserver) 834 m_fullscreenChangeObserver->didExitFullscreen(); 835 protect = nullptr; 836 }]; 837 }); 838} 839 840@interface UIApplication () 841-(void)_setStatusBarOrientation:(UIInterfaceOrientation)o; 842@end 843 844@interface UIWindow () 845-(UIInterfaceOrientation)interfaceOrientation; 846@end 847 848void WebVideoFullscreenInterfaceAVKit::cleanupFullscreen() 849{ 850 // Retain this to extend object life until async block completes. 851 __block RefPtr<WebVideoFullscreenInterfaceAVKit> protect(this); 852 853 dispatch_async(dispatch_get_main_queue(), ^{ 854 if (m_window) { 855 [m_window setHidden:YES]; 856 [m_window setRootViewController:nil]; 857 [[getUIApplicationClass() sharedApplication] _setStatusBarOrientation:[[m_parentView window] interfaceOrientation]]; 858 } 859 [m_playerViewController setDelegate:nil]; 860 [[m_playerViewController view] removeFromSuperview]; 861 if (m_viewController) 862 [m_playerViewController removeFromParentViewController]; 863 [m_playerViewController setPlayerController:nil]; 864 m_playerViewController = nil; 865 [m_videoLayer removeFromSuperlayer]; 866 m_videoLayer = nil; 867 [m_videoLayerContainer removeFromSuperlayer]; 868 [m_videoLayerContainer setPlayerViewController:nil]; 869 m_videoLayerContainer = nil; 870 [[m_viewController view] removeFromSuperview]; 871 m_viewController = nil; 872 m_window = nil; 873 m_parentView = nil; 874 875 if (m_fullscreenChangeObserver) 876 m_fullscreenChangeObserver->didCleanupFullscreen(); 877 protect = nullptr; 878 }); 879} 880 881void WebVideoFullscreenInterfaceAVKit::invalidate() 882{ 883 [m_window setHidden:YES]; 884 [m_window setRootViewController:nil]; 885 [m_playerViewController exitFullScreenAnimated:NO completionHandler:nil]; 886 m_playerController = nil; 887 [m_playerViewController setDelegate:nil]; 888 [[m_playerViewController view] removeFromSuperview]; 889 if (m_viewController) 890 [m_playerViewController removeFromParentViewController]; 891 [m_playerViewController setPlayerController:nil]; 892 m_playerViewController = nil; 893 [m_videoLayer removeFromSuperlayer]; 894 m_videoLayer = nil; 895 [m_videoLayerContainer removeFromSuperlayer]; 896 [m_videoLayerContainer setPlayerViewController:nil]; 897 m_videoLayerContainer = nil; 898 [[m_viewController view] removeFromSuperview]; 899 m_viewController = nil; 900 m_window = nil; 901 m_parentView = nil; 902} 903 904void WebVideoFullscreenInterfaceAVKit::requestHideAndExitFullscreen() 905{ 906 __block RefPtr<WebVideoFullscreenInterfaceAVKit> protect(this); 907 908 dispatch_async(dispatch_get_main_queue(), ^{ 909 [m_window setHidden:YES]; 910 [m_playerViewController exitFullScreenAnimated:NO completionHandler:^(BOOL, NSError*) { 911 protect = nullptr; 912 }]; 913 }); 914 915 if (m_videoFullscreenModel) 916 m_videoFullscreenModel->requestExitFullscreen(); 917} 918 919 920#endif 921