| /* |
| * Copyright (C) 2014, 2015 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| |
| #import "config.h" |
| |
| #if PLATFORM(IOS) |
| |
| #import "WebVideoFullscreenInterfaceAVKit.h" |
| |
| #if HAVE(AVKIT) |
| |
| #import "AVKitSPI.h" |
| #import "GeometryUtilities.h" |
| #import "Logging.h" |
| #import "RuntimeApplicationChecks.h" |
| #import "TimeRanges.h" |
| #import "WebAVPlayerController.h" |
| #import "WebCoreSystemInterface.h" |
| #import "WebPlaybackSessionInterfaceAVKit.h" |
| #import "WebVideoFullscreenChangeObserver.h" |
| #import "WebVideoFullscreenModel.h" |
| #import <AVFoundation/AVTime.h> |
| #import <UIKit/UIKit.h> |
| #import <objc/message.h> |
| #import <objc/runtime.h> |
| #import <wtf/RetainPtr.h> |
| #import <wtf/text/CString.h> |
| #import <wtf/text/WTFString.h> |
| |
| using namespace WebCore; |
| |
| // Soft-linking headers must be included last since they #define functions, constants, etc. |
| #import "CoreMediaSoftLink.h" |
| |
| SOFT_LINK_FRAMEWORK(AVFoundation) |
| SOFT_LINK_CLASS(AVFoundation, AVPlayerLayer) |
| SOFT_LINK_CONSTANT(AVFoundation, AVLayerVideoGravityResize, NSString *) |
| SOFT_LINK_CONSTANT(AVFoundation, AVLayerVideoGravityResizeAspect, NSString *) |
| SOFT_LINK_CONSTANT(AVFoundation, AVLayerVideoGravityResizeAspectFill, NSString *) |
| |
| SOFT_LINK_FRAMEWORK_OPTIONAL(AVKit) |
| SOFT_LINK_CLASS_OPTIONAL(AVKit, AVPictureInPictureController) |
| SOFT_LINK_CLASS_OPTIONAL(AVKit, AVPlayerViewController) |
| SOFT_LINK_CLASS_OPTIONAL(AVKit, __AVPlayerLayerView) |
| |
| SOFT_LINK_FRAMEWORK(UIKit) |
| SOFT_LINK_CLASS(UIKit, UIApplication) |
| SOFT_LINK_CLASS(UIKit, UIScreen) |
| SOFT_LINK_CLASS(UIKit, UIWindow) |
| SOFT_LINK_CLASS(UIKit, UIView) |
| SOFT_LINK_CLASS(UIKit, UIViewController) |
| SOFT_LINK_CLASS(UIKit, UIColor) |
| |
| #if !LOG_DISABLED |
| static const char* boolString(bool val) |
| { |
| return val ? "true" : "false"; |
| } |
| #endif |
| |
| static const double DefaultWatchdogTimerInterval = 1; |
| |
| @class WebAVMediaSelectionOption; |
| |
| @interface WebAVPlayerViewControllerDelegate : NSObject <AVPlayerViewControllerDelegate_WebKitOnly> { |
| RefPtr<WebVideoFullscreenInterfaceAVKit> _fullscreenInterface; |
| } |
| @property (assign) WebVideoFullscreenInterfaceAVKit* fullscreenInterface; |
| - (BOOL)playerViewController:(AVPlayerViewController *)playerViewController shouldExitFullScreenWithReason:(AVPlayerViewControllerExitFullScreenReason)reason; |
| @end |
| |
| @implementation WebAVPlayerViewControllerDelegate |
| - (WebVideoFullscreenInterfaceAVKit*)fullscreenInterface |
| { |
| return _fullscreenInterface.get(); |
| } |
| |
| - (void)setFullscreenInterface:(WebVideoFullscreenInterfaceAVKit*)fullscreenInterface |
| { |
| _fullscreenInterface = fullscreenInterface; |
| } |
| |
| - (void)playerViewControllerWillStartPictureInPicture:(AVPlayerViewController *)playerViewController |
| { |
| UNUSED_PARAM(playerViewController); |
| self.fullscreenInterface->willStartPictureInPicture(); |
| } |
| |
| - (void)playerViewControllerDidStartPictureInPicture:(AVPlayerViewController *)playerViewController |
| { |
| UNUSED_PARAM(playerViewController); |
| self.fullscreenInterface->didStartPictureInPicture(); |
| } |
| |
| - (void)playerViewControllerFailedToStartPictureInPicture:(AVPlayerViewController *)playerViewController withError:(NSError *)error |
| { |
| UNUSED_PARAM(playerViewController); |
| UNUSED_PARAM(error); |
| self.fullscreenInterface->failedToStartPictureInPicture(); |
| } |
| |
| - (void)playerViewControllerWillStopPictureInPicture:(AVPlayerViewController *)playerViewController |
| { |
| UNUSED_PARAM(playerViewController); |
| self.fullscreenInterface->willStopPictureInPicture(); |
| } |
| |
| - (void)playerViewControllerDidStopPictureInPicture:(AVPlayerViewController *)playerViewController |
| { |
| UNUSED_PARAM(playerViewController); |
| self.fullscreenInterface->didStopPictureInPicture(); |
| } |
| |
| static WebVideoFullscreenInterfaceAVKit::ExitFullScreenReason convertToExitFullScreenReason(AVPlayerViewControllerExitFullScreenReason reason) |
| { |
| switch (reason) { |
| case AVPlayerViewControllerExitFullScreenReasonDoneButtonTapped: |
| return WebVideoFullscreenInterfaceAVKit::ExitFullScreenReason::DoneButtonTapped; |
| case AVPlayerViewControllerExitFullScreenReasonFullScreenButtonTapped: |
| return WebVideoFullscreenInterfaceAVKit::ExitFullScreenReason::FullScreenButtonTapped; |
| case AVPlayerViewControllerExitFullScreenReasonPictureInPictureStarted: |
| return WebVideoFullscreenInterfaceAVKit::ExitFullScreenReason::PictureInPictureStarted; |
| case AVPlayerViewControllerExitFullScreenReasonPinchGestureHandled: |
| return WebVideoFullscreenInterfaceAVKit::ExitFullScreenReason::PinchGestureHandled; |
| case AVPlayerViewControllerExitFullScreenReasonRemoteControlStopEventReceived: |
| return WebVideoFullscreenInterfaceAVKit::ExitFullScreenReason::RemoteControlStopEventReceived; |
| } |
| } |
| |
| - (BOOL)playerViewController:(AVPlayerViewController *)playerViewController shouldExitFullScreenWithReason:(AVPlayerViewControllerExitFullScreenReason)reason |
| { |
| UNUSED_PARAM(playerViewController); |
| return self.fullscreenInterface->shouldExitFullscreenWithReason(convertToExitFullScreenReason(reason)); |
| } |
| |
| - (void)playerViewController:(AVPlayerViewController *)playerViewController restoreUserInterfaceForPictureInPictureStopWithCompletionHandler:(void (^)(BOOL restored))completionHandler |
| { |
| UNUSED_PARAM(playerViewController); |
| self.fullscreenInterface->prepareForPictureInPictureStopWithCompletionHandler(completionHandler); |
| } |
| @end |
| |
| @interface WebAVPlayerLayer : CALayer |
| @property (nonatomic, retain) NSString *videoGravity; |
| @property (nonatomic, getter=isReadyForDisplay) BOOL readyForDisplay; |
| @property (nonatomic, assign) WebVideoFullscreenInterfaceAVKit* fullscreenInterface; |
| @property (nonatomic, retain) AVPlayerController *playerController; |
| @property (nonatomic, retain) CALayer *videoSublayer; |
| @property (nonatomic, copy, nullable) NSDictionary *pixelBufferAttributes; |
| @property CGSize videoDimensions; |
| @property CGRect modelVideoLayerFrame; |
| @end |
| |
| @implementation WebAVPlayerLayer { |
| RefPtr<WebVideoFullscreenInterfaceAVKit> _fullscreenInterface; |
| RetainPtr<WebAVPlayerController> _avPlayerController; |
| RetainPtr<CALayer> _videoSublayer; |
| RetainPtr<NSString> _videoGravity; |
| } |
| |
| - (instancetype)init |
| { |
| self = [super init]; |
| if (self) { |
| [self setMasksToBounds:YES]; |
| _videoGravity = getAVLayerVideoGravityResizeAspect(); |
| } |
| return self; |
| } |
| |
| - (void)dealloc |
| { |
| [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resolveBounds) object:nil]; |
| [_pixelBufferAttributes release]; |
| [super dealloc]; |
| } |
| |
| - (WebVideoFullscreenInterfaceAVKit*)fullscreenInterface |
| { |
| return _fullscreenInterface.get(); |
| } |
| |
| - (void)setFullscreenInterface:(WebVideoFullscreenInterfaceAVKit*)fullscreenInterface |
| { |
| _fullscreenInterface = fullscreenInterface; |
| } |
| |
| - (AVPlayerController *)playerController |
| { |
| return (AVPlayerController *)_avPlayerController.get(); |
| } |
| |
| - (void)setPlayerController:(AVPlayerController *)playerController |
| { |
| ASSERT(!playerController || [playerController isKindOfClass:[WebAVPlayerController class]]); |
| _avPlayerController = (WebAVPlayerController *)playerController; |
| } |
| |
| - (void)setVideoSublayer:(CALayer *)videoSublayer |
| { |
| _videoSublayer = videoSublayer; |
| } |
| |
| - (CALayer*)videoSublayer |
| { |
| return _videoSublayer.get(); |
| } |
| |
| - (void)layoutSublayers |
| { |
| if ([_videoSublayer superlayer] != self) |
| return; |
| |
| if (![_avPlayerController delegate]) |
| return; |
| |
| [_videoSublayer setPosition:CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds))]; |
| |
| if (self.videoDimensions.height <= 0 || self.videoDimensions.width <= 0) |
| return; |
| |
| FloatRect sourceVideoFrame; |
| FloatRect targetVideoFrame; |
| float videoAspectRatio = self.videoDimensions.width / self.videoDimensions.height; |
| |
| if ([getAVLayerVideoGravityResize() isEqualToString:self.videoGravity]) { |
| sourceVideoFrame = self.modelVideoLayerFrame; |
| targetVideoFrame = self.bounds; |
| } else if ([getAVLayerVideoGravityResizeAspect() isEqualToString:self.videoGravity]) { |
| sourceVideoFrame = largestRectWithAspectRatioInsideRect(videoAspectRatio, self.modelVideoLayerFrame); |
| targetVideoFrame = largestRectWithAspectRatioInsideRect(videoAspectRatio, self.bounds); |
| } else if ([getAVLayerVideoGravityResizeAspectFill() isEqualToString:self.videoGravity]) { |
| sourceVideoFrame = smallestRectWithAspectRatioAroundRect(videoAspectRatio, self.modelVideoLayerFrame); |
| self.modelVideoLayerFrame = CGRectMake(0, 0, sourceVideoFrame.width(), sourceVideoFrame.height()); |
| ASSERT(_fullscreenInterface->model()); |
| _fullscreenInterface->model()->setVideoLayerFrame(self.modelVideoLayerFrame); |
| targetVideoFrame = smallestRectWithAspectRatioAroundRect(videoAspectRatio, self.bounds); |
| } else |
| ASSERT_NOT_REACHED(); |
| |
| UIView *view = [_videoSublayer delegate]; |
| CGAffineTransform transform = CGAffineTransformMakeScale(targetVideoFrame.width() / sourceVideoFrame.width(), targetVideoFrame.height() / sourceVideoFrame.height()); |
| [view setTransform:transform]; |
| |
| NSTimeInterval animationDuration = [CATransaction animationDuration]; |
| dispatch_async(dispatch_get_main_queue(), ^{ |
| [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resolveBounds) object:nil]; |
| |
| if (!CGAffineTransformIsIdentity(transform)) |
| [self performSelector:@selector(resolveBounds) withObject:nil afterDelay:animationDuration + 0.1]; |
| }); |
| } |
| |
| - (void)resolveBounds |
| { |
| [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resolveBounds) object:nil]; |
| if (![_avPlayerController delegate]) |
| return; |
| |
| if ([_videoSublayer superlayer] != self) |
| return; |
| |
| [CATransaction begin]; |
| [CATransaction setAnimationDuration:0]; |
| [CATransaction setDisableActions:YES]; |
| |
| self.modelVideoLayerFrame = [self bounds]; |
| ASSERT(_fullscreenInterface->model()); |
| _fullscreenInterface->model()->setVideoLayerFrame(self.modelVideoLayerFrame); |
| [(UIView *)[_videoSublayer delegate] setTransform:CGAffineTransformIdentity]; |
| |
| [CATransaction commit]; |
| } |
| |
| - (void)setVideoGravity:(NSString *)videoGravity |
| { |
| _videoGravity = videoGravity; |
| |
| if (![_avPlayerController delegate]) |
| return; |
| |
| WebCore::WebVideoFullscreenModel::VideoGravity gravity = WebCore::WebVideoFullscreenModel::VideoGravityResizeAspect; |
| if (videoGravity == getAVLayerVideoGravityResize()) |
| gravity = WebCore::WebVideoFullscreenModel::VideoGravityResize; |
| if (videoGravity == getAVLayerVideoGravityResizeAspect()) |
| gravity = WebCore::WebVideoFullscreenModel::VideoGravityResizeAspect; |
| else if (videoGravity == getAVLayerVideoGravityResizeAspectFill()) |
| gravity = WebCore::WebVideoFullscreenModel::VideoGravityResizeAspectFill; |
| else |
| ASSERT_NOT_REACHED(); |
| |
| ASSERT(_fullscreenInterface->model()); |
| _fullscreenInterface->model()->setVideoLayerGravity(gravity); |
| } |
| |
| - (NSString *)videoGravity |
| { |
| return _videoGravity.get(); |
| } |
| |
| - (CGRect)videoRect |
| { |
| if (self.videoDimensions.width <= 0 || self.videoDimensions.height <= 0) |
| return self.bounds; |
| |
| float videoAspectRatio = self.videoDimensions.width / self.videoDimensions.height; |
| |
| if ([getAVLayerVideoGravityResizeAspect() isEqualToString:self.videoGravity]) |
| return largestRectWithAspectRatioInsideRect(videoAspectRatio, self.bounds); |
| if ([getAVLayerVideoGravityResizeAspectFill() isEqualToString:self.videoGravity]) |
| return smallestRectWithAspectRatioAroundRect(videoAspectRatio, self.bounds); |
| |
| return self.bounds; |
| } |
| |
| + (NSSet *)keyPathsForValuesAffectingVideoRect |
| { |
| return [NSSet setWithObjects:@"videoDimensions", @"videoGravity", nil]; |
| } |
| |
| @end |
| |
| @interface WebAVPictureInPicturePlayerLayerView : UIView |
| @end |
| |
| static CALayer* WebAVPictureInPicturePlayerLayerView_layerClass(id, SEL) |
| { |
| return [WebAVPlayerLayer class]; |
| } |
| |
| static Class getWebAVPictureInPicturePlayerLayerViewClass() |
| { |
| static Class theClass = nil; |
| static dispatch_once_t onceToken; |
| dispatch_once(&onceToken, ^{ |
| theClass = objc_allocateClassPair(getUIViewClass(), "WebAVPictureInPicturePlayerLayerView", 0); |
| objc_registerClassPair(theClass); |
| Class metaClass = objc_getMetaClass("WebAVPictureInPicturePlayerLayerView"); |
| class_addMethod(metaClass, @selector(layerClass), (IMP)WebAVPictureInPicturePlayerLayerView_layerClass, "@@:"); |
| }); |
| |
| return theClass; |
| } |
| |
| @interface WebAVPlayerLayerView : __AVPlayerLayerView |
| @property (retain) UIView* videoView; |
| @end |
| |
| static CALayer *WebAVPlayerLayerView_layerClass(id, SEL) |
| { |
| return [WebAVPlayerLayer class]; |
| } |
| |
| static AVPlayerController *WebAVPlayerLayerView_playerController(id aSelf, SEL) |
| { |
| __AVPlayerLayerView *playerLayer = aSelf; |
| WebAVPlayerLayer *webAVPlayerLayer = (WebAVPlayerLayer *)[playerLayer playerLayer]; |
| return [webAVPlayerLayer playerController]; |
| } |
| |
| static void WebAVPlayerLayerView_setPlayerController(id aSelf, SEL, AVPlayerController *playerController) |
| { |
| __AVPlayerLayerView *playerLayerView = aSelf; |
| WebAVPlayerLayer *webAVPlayerLayer = (WebAVPlayerLayer *)[playerLayerView playerLayer]; |
| [webAVPlayerLayer setPlayerController: playerController]; |
| } |
| |
| static UIView *WebAVPlayerLayerView_videoView(id aSelf, SEL) |
| { |
| __AVPlayerLayerView *playerLayer = aSelf; |
| WebAVPlayerLayer *webAVPlayerLayer = (WebAVPlayerLayer *)[playerLayer playerLayer]; |
| CALayer* videoLayer = [webAVPlayerLayer videoSublayer]; |
| if (!videoLayer) |
| return nil; |
| ASSERT([[videoLayer delegate] isKindOfClass:getUIViewClass()]); |
| return (UIView *)[videoLayer delegate]; |
| } |
| |
| static void WebAVPlayerLayerView_setVideoView(id aSelf, SEL, UIView *videoView) |
| { |
| __AVPlayerLayerView *playerLayerView = aSelf; |
| WebAVPlayerLayer *webAVPlayerLayer = (WebAVPlayerLayer *)[playerLayerView playerLayer]; |
| [webAVPlayerLayer setVideoSublayer:[videoView layer]]; |
| } |
| |
| static void WebAVPlayerLayerView_startRoutingVideoToPictureInPicturePlayerLayerView(id aSelf, SEL) |
| { |
| WebAVPlayerLayerView *playerLayerView = aSelf; |
| WebAVPictureInPicturePlayerLayerView *pipView = (WebAVPictureInPicturePlayerLayerView *)[playerLayerView pictureInPicturePlayerLayerView]; |
| |
| WebAVPlayerLayer *playerLayer = (WebAVPlayerLayer *)[playerLayerView playerLayer]; |
| WebAVPlayerLayer *pipPlayerLayer = (WebAVPlayerLayer *)[pipView layer]; |
| [playerLayer setVideoGravity:getAVLayerVideoGravityResizeAspect()]; |
| [pipPlayerLayer setVideoSublayer:playerLayer.videoSublayer]; |
| [pipPlayerLayer setVideoDimensions:playerLayer.videoDimensions]; |
| [pipPlayerLayer setVideoGravity:playerLayer.videoGravity]; |
| [pipPlayerLayer setModelVideoLayerFrame:playerLayer.modelVideoLayerFrame]; |
| [pipPlayerLayer setPlayerController:playerLayer.playerController]; |
| [pipPlayerLayer setFullscreenInterface:playerLayer.fullscreenInterface]; |
| [pipView addSubview:playerLayerView.videoView]; |
| } |
| |
| static void WebAVPlayerLayerView_stopRoutingVideoToPictureInPicturePlayerLayerView(id aSelf, SEL) |
| { |
| WebAVPlayerLayerView *playerLayerView = aSelf; |
| [playerLayerView addSubview:playerLayerView.videoView]; |
| WebAVPictureInPicturePlayerLayerView *pipView = (WebAVPictureInPicturePlayerLayerView *)[playerLayerView pictureInPicturePlayerLayerView]; |
| WebAVPlayerLayer *playerLayer = (WebAVPlayerLayer *)[playerLayerView playerLayer]; |
| WebAVPlayerLayer *pipPlayerLayer = (WebAVPlayerLayer *)[pipView layer]; |
| [playerLayer setModelVideoLayerFrame:pipPlayerLayer.modelVideoLayerFrame]; |
| } |
| |
| static WebAVPictureInPicturePlayerLayerView *WebAVPlayerLayerView_pictureInPicturePlayerLayerView(id aSelf, SEL) |
| { |
| WebAVPlayerLayerView *playerLayerView = aSelf; |
| WebAVPictureInPicturePlayerLayerView *pipView = [playerLayerView valueForKey:@"_pictureInPicturePlayerLayerView"]; |
| if (!pipView) { |
| pipView = [[getWebAVPictureInPicturePlayerLayerViewClass() alloc] initWithFrame:CGRectZero]; |
| [playerLayerView setValue:pipView forKey:@"_pictureInPicturePlayerLayerView"]; |
| } |
| return pipView; |
| } |
| |
| static void WebAVPlayerLayerView_dealloc(id aSelf, SEL) |
| { |
| WebAVPlayerLayerView *playerLayerView = aSelf; |
| RetainPtr<WebAVPictureInPicturePlayerLayerView> pipView = adoptNS([playerLayerView valueForKey:@"_pictureInPicturePlayerLayerView"]); |
| [playerLayerView setValue:nil forKey:@"_pictureInPicturePlayerLayerView"]; |
| objc_super superClass { playerLayerView, get__AVPlayerLayerViewClass() }; |
| auto super_dealloc = reinterpret_cast<void(*)(objc_super*, SEL)>(objc_msgSendSuper); |
| super_dealloc(&superClass, @selector(dealloc)); |
| } |
| |
| #pragma mark - Methods |
| |
| static Class getWebAVPlayerLayerViewClass() |
| { |
| static Class theClass = nil; |
| static dispatch_once_t onceToken; |
| dispatch_once(&onceToken, ^{ |
| theClass = objc_allocateClassPair(get__AVPlayerLayerViewClass(), "WebAVPlayerLayerView", 0); |
| class_addMethod(theClass, @selector(dealloc), (IMP)WebAVPlayerLayerView_dealloc, "v@:"); |
| class_addMethod(theClass, @selector(setPlayerController:), (IMP)WebAVPlayerLayerView_setPlayerController, "v@:@"); |
| class_addMethod(theClass, @selector(playerController), (IMP)WebAVPlayerLayerView_playerController, "@@:"); |
| class_addMethod(theClass, @selector(setVideoView:), (IMP)WebAVPlayerLayerView_setVideoView, "v@:@"); |
| class_addMethod(theClass, @selector(videoView), (IMP)WebAVPlayerLayerView_videoView, "@@:"); |
| class_addMethod(theClass, @selector(startRoutingVideoToPictureInPicturePlayerLayerView), (IMP)WebAVPlayerLayerView_startRoutingVideoToPictureInPicturePlayerLayerView, "v@:"); |
| class_addMethod(theClass, @selector(stopRoutingVideoToPictureInPicturePlayerLayerView), (IMP)WebAVPlayerLayerView_stopRoutingVideoToPictureInPicturePlayerLayerView, "v@:"); |
| class_addMethod(theClass, @selector(pictureInPicturePlayerLayerView), (IMP)WebAVPlayerLayerView_pictureInPicturePlayerLayerView, "@@:"); |
| |
| class_addIvar(theClass, "_pictureInPicturePlayerLayerView", sizeof(WebAVPictureInPicturePlayerLayerView *), log2(sizeof(WebAVPictureInPicturePlayerLayerView *)), "@"); |
| |
| objc_registerClassPair(theClass); |
| Class metaClass = objc_getMetaClass("WebAVPlayerLayerView"); |
| class_addMethod(metaClass, @selector(layerClass), (IMP)WebAVPlayerLayerView_layerClass, "@@:"); |
| }); |
| return theClass; |
| } |
| |
| Ref<WebVideoFullscreenInterfaceAVKit> WebVideoFullscreenInterfaceAVKit::create(WebPlaybackSessionInterfaceAVKit& playbackSessionInterface) |
| { |
| Ref<WebVideoFullscreenInterfaceAVKit> interface = adoptRef(*new WebVideoFullscreenInterfaceAVKit(playbackSessionInterface)); |
| [interface->m_playerViewControllerDelegate setFullscreenInterface:interface.ptr()]; |
| return interface; |
| } |
| |
| WebVideoFullscreenInterfaceAVKit::WebVideoFullscreenInterfaceAVKit(WebPlaybackSessionInterfaceAVKit& playbackSessionInterface) |
| : m_playbackSessionInterface(playbackSessionInterface) |
| , m_playerViewControllerDelegate(adoptNS([[WebAVPlayerViewControllerDelegate alloc] init])) |
| , m_watchdogTimer(*this, &WebVideoFullscreenInterfaceAVKit::watchdogTimerFired) |
| { |
| } |
| |
| WebVideoFullscreenInterfaceAVKit::~WebVideoFullscreenInterfaceAVKit() |
| { |
| WebAVPlayerController* playerController = this->playerController(); |
| if (playerController && playerController.externalPlaybackActive) |
| setExternalPlayback(false, TargetTypeNone, ""); |
| } |
| |
| WebAVPlayerController *WebVideoFullscreenInterfaceAVKit::playerController() const |
| { |
| return m_playbackSessionInterface->playerController(); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::resetMediaState() |
| { |
| m_playbackSessionInterface->resetMediaState(); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::setWebVideoFullscreenModel(WebVideoFullscreenModel* model) |
| { |
| m_videoFullscreenModel = model; |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::setWebVideoFullscreenChangeObserver(WebVideoFullscreenChangeObserver* observer) |
| { |
| m_fullscreenChangeObserver = observer; |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::setDuration(double duration) |
| { |
| m_playbackSessionInterface->setDuration(duration); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::setCurrentTime(double currentTime, double anchorTime) |
| { |
| m_playbackSessionInterface->setCurrentTime(currentTime, anchorTime); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::setBufferedTime(double bufferedTime) |
| { |
| m_playbackSessionInterface->setBufferedTime(bufferedTime); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::setRate(bool isPlaying, float playbackRate) |
| { |
| m_playbackSessionInterface->setRate(isPlaying, playbackRate); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::setVideoDimensions(bool hasVideo, float width, float height) |
| { |
| WebAVPlayerLayer *playerLayer = (WebAVPlayerLayer *)[m_playerLayerView playerLayer]; |
| |
| [playerLayer setVideoDimensions:CGSizeMake(width, height)]; |
| [playerController() setHasEnabledVideo:hasVideo]; |
| [playerController() setContentDimensions:CGSizeMake(width, height)]; |
| [m_playerLayerView setNeedsLayout]; |
| |
| WebAVPictureInPicturePlayerLayerView *pipView = (WebAVPictureInPicturePlayerLayerView *)[m_playerLayerView pictureInPicturePlayerLayerView]; |
| WebAVPlayerLayer *pipPlayerLayer = (WebAVPlayerLayer *)[pipView layer]; |
| [pipPlayerLayer setVideoDimensions:playerLayer.videoDimensions]; |
| [pipView setNeedsLayout]; |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::setSeekableRanges(const TimeRanges& timeRanges) |
| { |
| m_playbackSessionInterface->setSeekableRanges(timeRanges); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::setCanPlayFastReverse(bool canPlayFastReverse) |
| { |
| m_playbackSessionInterface->setCanPlayFastReverse(canPlayFastReverse); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::setAudioMediaSelectionOptions(const Vector<String>& options, uint64_t selectedIndex) |
| { |
| m_playbackSessionInterface->setAudioMediaSelectionOptions(options, selectedIndex); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::setLegibleMediaSelectionOptions(const Vector<String>& options, uint64_t selectedIndex) |
| { |
| m_playbackSessionInterface->setLegibleMediaSelectionOptions(options, selectedIndex); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::setExternalPlayback(bool enabled, ExternalPlaybackTargetType targetType, String localizedDeviceName) |
| { |
| m_playbackSessionInterface->setExternalPlayback(enabled, targetType, localizedDeviceName); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::externalPlaybackEnabledChanged(bool enabled) |
| { |
| [m_playerLayerView setHidden:enabled]; |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::setWirelessVideoPlaybackDisabled(bool disabled) |
| { |
| m_playbackSessionInterface->setWirelessVideoPlaybackDisabled(disabled); |
| } |
| |
| bool WebVideoFullscreenInterfaceAVKit::wirelessVideoPlaybackDisabled() const |
| { |
| return m_playbackSessionInterface->wirelessVideoPlaybackDisabled(); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::applicationDidBecomeActive() |
| { |
| LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::applicationDidBecomeActive(%p)", this); |
| if (m_shouldReturnToFullscreenAfterEnteringForeground && m_videoFullscreenModel && m_videoFullscreenModel->isVisible()) { |
| [m_playerViewController stopPictureInPicture]; |
| return; |
| } |
| |
| // If we are both in PiP and in Fullscreen (i.e., via auto-PiP), and we did not stop fullscreen upon returning, it must be |
| // because the originating view is not visible, so hide the fullscreen window. |
| if (isMode(HTMLMediaElementEnums::VideoFullscreenModeStandard | HTMLMediaElementEnums::VideoFullscreenModePictureInPicture)) { |
| RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this); |
| [m_playerViewController exitFullScreenAnimated:NO completionHandler:[strongThis, this] (BOOL, NSError*) { |
| [m_window setHidden:YES]; |
| [[m_playerViewController view] setHidden:YES]; |
| }]; |
| } |
| } |
| |
| @interface UIWindow () |
| - (BOOL)_isHostedInAnotherProcess; |
| @end |
| |
| @interface UIViewController () |
| @property (nonatomic, assign, setter=_setIgnoreAppSupportedOrientations:) BOOL _ignoreAppSupportedOrientations; |
| @end |
| |
| void WebVideoFullscreenInterfaceAVKit::setupFullscreen(UIView& videoView, const WebCore::IntRect& initialRect, UIView* parentView, HTMLMediaElementEnums::VideoFullscreenMode mode, bool allowsPictureInPicturePlayback) |
| { |
| ASSERT(mode != HTMLMediaElementEnums::VideoFullscreenModeNone); |
| LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::setupFullscreen(%p)", this); |
| |
| m_allowsPictureInPicturePlayback = allowsPictureInPicturePlayback; |
| |
| [CATransaction begin]; |
| [CATransaction setDisableActions:YES]; |
| bool isInPictureInPictureMode = hasMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture); |
| m_mode = mode; |
| m_parentView = parentView; |
| m_parentWindow = parentView.window; |
| |
| if (![[parentView window] _isHostedInAnotherProcess]) { |
| if (!m_window) |
| m_window = adoptNS([allocUIWindowInstance() initWithFrame:[[getUIScreenClass() mainScreen] bounds]]); |
| [m_window setBackgroundColor:[getUIColorClass() clearColor]]; |
| if (!m_viewController) |
| m_viewController = adoptNS([allocUIViewControllerInstance() init]); |
| [[m_viewController view] setFrame:[m_window bounds]]; |
| [m_viewController _setIgnoreAppSupportedOrientations:YES]; |
| [m_window setRootViewController:m_viewController.get()]; |
| [m_window makeKeyAndVisible]; |
| } |
| |
| if (!m_playerLayerView) |
| m_playerLayerView = adoptNS([[getWebAVPlayerLayerViewClass() alloc] init]); |
| [m_playerLayerView setHidden:[playerController() isExternalPlaybackActive]]; |
| [m_playerLayerView setBackgroundColor:[getUIColorClass() clearColor]]; |
| |
| if (!isInPictureInPictureMode) { |
| [m_playerLayerView setVideoView:&videoView]; |
| [m_playerLayerView addSubview:&videoView]; |
| } |
| |
| WebAVPlayerLayer *playerLayer = (WebAVPlayerLayer *)[m_playerLayerView playerLayer]; |
| |
| [playerLayer setModelVideoLayerFrame:CGRectMake(0, 0, initialRect.width(), initialRect.height())]; |
| [playerLayer setVideoDimensions:[playerController() contentDimensions]]; |
| playerLayer.fullscreenInterface = this; |
| |
| if (!m_playerViewController) |
| m_playerViewController = adoptNS([allocAVPlayerViewControllerInstance() initWithPlayerLayerView:m_playerLayerView.get()]); |
| |
| [m_playerViewController setShowsPlaybackControls:NO]; |
| [m_playerViewController setPlayerController:(AVPlayerController *)playerController()]; |
| [m_playerViewController setDelegate:m_playerViewControllerDelegate.get()]; |
| [m_playerViewController setAllowsPictureInPicturePlayback:m_allowsPictureInPicturePlayback]; |
| |
| [playerController() setPictureInPicturePossible:m_allowsPictureInPicturePlayback]; |
| |
| if (m_viewController) { |
| [m_viewController addChildViewController:m_playerViewController.get()]; |
| [[m_viewController view] addSubview:[m_playerViewController view]]; |
| } else |
| [parentView addSubview:[m_playerViewController view]]; |
| |
| [m_playerViewController view].frame = [parentView convertRect:initialRect toView:[m_playerViewController view].superview]; |
| |
| [[m_playerViewController view] setBackgroundColor:[getUIColorClass() clearColor]]; |
| [[m_playerViewController view] setAutoresizingMask:(UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin)]; |
| |
| [[m_playerViewController view] setNeedsLayout]; |
| [[m_playerViewController view] layoutIfNeeded]; |
| |
| [CATransaction commit]; |
| |
| RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this); |
| dispatch_async(dispatch_get_main_queue(), [strongThis, this] { |
| if (m_fullscreenChangeObserver) |
| m_fullscreenChangeObserver->didSetupFullscreen(); |
| }); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::enterFullscreen() |
| { |
| LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::enterFullscreen(%p)", this); |
| |
| m_exitCompleted = false; |
| m_exitRequested = false; |
| m_enterRequested = true; |
| |
| [m_playerLayerView setBackgroundColor:[getUIColorClass() blackColor]]; |
| if (mode() == HTMLMediaElementEnums::VideoFullscreenModePictureInPicture) |
| enterPictureInPicture(); |
| else if (mode() == HTMLMediaElementEnums::VideoFullscreenModeStandard) |
| enterFullscreenStandard(); |
| else |
| ASSERT_NOT_REACHED(); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::enterPictureInPicture() |
| { |
| LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::enterPictureInPicture(%p)", this); |
| |
| if ([m_playerViewController isPictureInPicturePossible]) |
| [m_playerViewController startPictureInPicture]; |
| else |
| failedToStartPictureInPicture(); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::enterFullscreenStandard() |
| { |
| LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::enterFullscreenStandard(%p)", this); |
| RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this); |
| |
| if ([m_playerViewController isPictureInPictureActive]) { |
| // NOTE: The fullscreen mode will be restored in prepareForPictureInPictureStopWithCompletionHandler(). |
| m_shouldReturnToFullscreenWhenStoppingPiP = true; |
| [m_playerViewController stopPictureInPicture]; |
| return; |
| } |
| |
| [m_playerLayerView setBackgroundColor:[getUIColorClass() blackColor]]; |
| [m_playerViewController enterFullScreenAnimated:YES completionHandler:[this, strongThis] (BOOL succeeded, NSError*) { |
| UNUSED_PARAM(succeeded); |
| LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::enterFullscreenStandard - lambda(%p) - succeeded(%s)", this, boolString(succeeded)); |
| [m_playerViewController setShowsPlaybackControls:YES]; |
| |
| if (m_fullscreenChangeObserver) |
| m_fullscreenChangeObserver->didEnterFullscreen(); |
| }]; |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::exitFullscreen(const WebCore::IntRect& finalRect) |
| { |
| m_watchdogTimer.stop(); |
| |
| m_exitRequested = true; |
| if (m_exitCompleted) { |
| if (m_fullscreenChangeObserver) |
| m_fullscreenChangeObserver->didExitFullscreen(); |
| return; |
| } |
| |
| LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::exitFullscreen(%p)", this); |
| [m_playerViewController setShowsPlaybackControls:NO]; |
| |
| [m_playerViewController view].frame = [m_parentView convertRect:finalRect toView:[m_playerViewController view].superview]; |
| |
| WebAVPlayerLayer *playerLayer = (WebAVPlayerLayer *)[m_playerLayerView playerLayer]; |
| if ([playerLayer videoGravity] != getAVLayerVideoGravityResizeAspect()) |
| [playerLayer setVideoGravity:getAVLayerVideoGravityResizeAspect()]; |
| [[m_playerViewController view] layoutIfNeeded]; |
| |
| if (isMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture)) { |
| [m_window setHidden:NO]; |
| [m_playerViewController stopPictureInPicture]; |
| } else if (isMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture | HTMLMediaElementEnums::VideoFullscreenModeStandard)) { |
| RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this); |
| [m_playerViewController exitFullScreenAnimated:NO completionHandler:[strongThis, this] (BOOL, NSError*) { |
| [m_window setHidden:NO]; |
| [m_playerViewController stopPictureInPicture]; |
| }]; |
| } else if (isMode(HTMLMediaElementEnums::VideoFullscreenModeStandard)) { |
| RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this); |
| [m_playerViewController exitFullScreenAnimated:YES completionHandler:[strongThis, this] (BOOL, NSError*) { |
| m_exitCompleted = true; |
| |
| [CATransaction begin]; |
| [CATransaction setDisableActions:YES]; |
| [m_playerLayerView setBackgroundColor:[getUIColorClass() clearColor]]; |
| [[m_playerViewController view] setBackgroundColor:[getUIColorClass() clearColor]]; |
| [CATransaction commit]; |
| |
| dispatch_async(dispatch_get_main_queue(), ^{ |
| if (m_fullscreenChangeObserver) |
| m_fullscreenChangeObserver->didExitFullscreen(); |
| }); |
| }]; |
| }; |
| } |
| |
| @interface UIApplication () |
| - (void)_setStatusBarOrientation:(UIInterfaceOrientation)o; |
| @end |
| |
| @interface UIWindow () |
| - (UIInterfaceOrientation)interfaceOrientation; |
| @end |
| |
| void WebVideoFullscreenInterfaceAVKit::cleanupFullscreen() |
| { |
| LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::cleanupFullscreen(%p)", this); |
| if (m_window) { |
| [m_window setHidden:YES]; |
| [m_window setRootViewController:nil]; |
| if (m_parentWindow) |
| [[getUIApplicationClass() sharedApplication] _setStatusBarOrientation:[m_parentWindow interfaceOrientation]]; |
| } |
| |
| [m_playerViewController setDelegate:nil]; |
| [m_playerViewController setPlayerController:nil]; |
| |
| if (hasMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture)) |
| [m_playerViewController stopPictureInPicture]; |
| if (hasMode(HTMLMediaElementEnums::VideoFullscreenModeStandard)) |
| [m_playerViewController exitFullScreenAnimated:NO completionHandler:[] (BOOL, NSError *) { }]; |
| |
| [[m_playerViewController view] removeFromSuperview]; |
| if (m_viewController) |
| [m_playerViewController removeFromParentViewController]; |
| |
| [m_playerLayerView removeFromSuperview]; |
| [[m_viewController view] removeFromSuperview]; |
| |
| m_playerLayerView = nil; |
| m_playerViewController = nil; |
| m_window = nil; |
| m_parentView = nil; |
| m_parentWindow = nil; |
| |
| if (m_fullscreenChangeObserver) |
| m_fullscreenChangeObserver->didCleanupFullscreen(); |
| |
| m_enterRequested = false; |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::invalidate() |
| { |
| m_videoFullscreenModel = nil; |
| m_fullscreenChangeObserver = nil; |
| |
| cleanupFullscreen(); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::requestHideAndExitFullscreen() |
| { |
| if (!m_enterRequested) |
| return; |
| |
| if (hasMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture)) |
| return; |
| |
| LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::requestHideAndExitFullscreen(%p)", this); |
| |
| [m_window setHidden:YES]; |
| [[m_playerViewController view] setHidden:YES]; |
| |
| if (webPlaybackSessionModel() && m_videoFullscreenModel && !m_exitRequested) { |
| webPlaybackSessionModel()->pause(); |
| m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone); |
| } |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::preparedToReturnToInline(bool visible, const IntRect& inlineRect) |
| { |
| LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::preparedToReturnToInline(%p) - visible(%s)", this, boolString(visible)); |
| if (m_prepareToInlineCallback) { |
| |
| [m_playerViewController view].frame = [m_parentView convertRect:inlineRect toView:[m_playerViewController view].superview]; |
| |
| std::function<void(bool)> callback = WTFMove(m_prepareToInlineCallback); |
| callback(visible); |
| } |
| } |
| |
| bool WebVideoFullscreenInterfaceAVKit::mayAutomaticallyShowVideoPictureInPicture() const |
| { |
| return [playerController() isPlaying] && m_mode == HTMLMediaElementEnums::VideoFullscreenModeStandard && supportsPictureInPicture(); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::fullscreenMayReturnToInline(std::function<void(bool)> callback) |
| { |
| m_prepareToInlineCallback = callback; |
| if (m_fullscreenChangeObserver) |
| m_fullscreenChangeObserver->fullscreenMayReturnToInline(); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::willStartPictureInPicture() |
| { |
| LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::willStartPictureInPicture(%p)", this); |
| setMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::didStartPictureInPicture() |
| { |
| LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::didStartPictureInPicture(%p)", this); |
| m_shouldReturnToFullscreenAfterEnteringForeground = [m_playerViewController pictureInPictureWasStartedWhenEnteringBackground]; |
| [m_playerViewController setShowsPlaybackControls:YES]; |
| |
| if (m_mode & HTMLMediaElementEnums::VideoFullscreenModeStandard) { |
| if (![m_playerViewController pictureInPictureWasStartedWhenEnteringBackground]) { |
| RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this); |
| [m_playerViewController exitFullScreenAnimated:YES completionHandler:[strongThis, this] (BOOL, NSError*) { |
| [m_window setHidden:YES]; |
| [[m_playerViewController view] setHidden:YES]; |
| }]; |
| } |
| } else { |
| [m_window setHidden:YES]; |
| [[m_playerViewController view] setHidden:YES]; |
| } |
| |
| if (m_fullscreenChangeObserver) |
| m_fullscreenChangeObserver->didEnterFullscreen(); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::failedToStartPictureInPicture() |
| { |
| LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::failedToStartPictureInPicture(%p)", this); |
| [m_playerViewController setShowsPlaybackControls:YES]; |
| |
| if (hasMode(HTMLMediaElementEnums::VideoFullscreenModeStandard)) |
| return; |
| |
| m_exitCompleted = true; |
| |
| if (m_fullscreenChangeObserver) |
| m_fullscreenChangeObserver->didEnterFullscreen(); |
| |
| if (m_videoFullscreenModel) |
| m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::willStopPictureInPicture() |
| { |
| LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::willStopPictureInPicture(%p)", this); |
| if (hasMode(HTMLMediaElementEnums::VideoFullscreenModeStandard)) |
| return; |
| |
| [m_window setHidden:NO]; |
| [[m_playerViewController view] setHidden:NO]; |
| |
| if (m_videoFullscreenModel) |
| m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::didStopPictureInPicture() |
| { |
| LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::didStopPictureInPicture(%p)", this); |
| if (hasMode(HTMLMediaElementEnums::VideoFullscreenModeStandard)) { |
| // ASSUMPTION: we are exiting pip because we are entering fullscreen |
| clearMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture); |
| [m_playerViewController setShowsPlaybackControls:YES]; |
| |
| if (m_fullscreenChangeObserver) |
| m_fullscreenChangeObserver->didEnterFullscreen(); |
| return; |
| } |
| |
| m_exitCompleted = true; |
| |
| [m_playerLayerView setBackgroundColor:[getUIColorClass() clearColor]]; |
| [[m_playerViewController view] setBackgroundColor:[getUIColorClass() clearColor]]; |
| |
| clearMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture); |
| [m_window setHidden:YES]; |
| [[m_playerViewController view] setHidden:YES]; |
| |
| if (m_fullscreenChangeObserver) |
| m_fullscreenChangeObserver->didExitFullscreen(); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler(void (^completionHandler)(BOOL restored)) |
| { |
| LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler(%p)", this); |
| if (m_shouldReturnToFullscreenWhenStoppingPiP || m_shouldReturnToFullscreenAfterEnteringForeground) { |
| m_shouldReturnToFullscreenWhenStoppingPiP = false; |
| m_shouldReturnToFullscreenAfterEnteringForeground = false; |
| |
| // ASSUMPTION: we are exiting pip because we are entering fullscreen |
| [m_window setHidden:NO]; |
| [[m_playerViewController view] setHidden:NO]; |
| |
| [m_playerViewController enterFullScreenAnimated:YES completionHandler:^(BOOL success, NSError*) { |
| setMode(HTMLMediaElementEnums::VideoFullscreenModeStandard); |
| completionHandler(success); |
| }]; |
| return; |
| } |
| |
| RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this); |
| RetainPtr<id> strongCompletionHandler = adoptNS([completionHandler copy]); |
| fullscreenMayReturnToInline([strongThis, strongCompletionHandler](bool restored) { |
| LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler lambda(%p) - restored(%s)", strongThis.get(), boolString(restored)); |
| void (^completionHandler)(BOOL restored) = strongCompletionHandler.get(); |
| completionHandler(restored); |
| }); |
| } |
| |
| bool WebVideoFullscreenInterfaceAVKit::shouldExitFullscreenWithReason(WebVideoFullscreenInterfaceAVKit::ExitFullScreenReason reason) |
| { |
| if (!m_videoFullscreenModel) |
| return true; |
| |
| if (reason == ExitFullScreenReason::PictureInPictureStarted) { |
| if ([m_playerViewController pictureInPictureWasStartedWhenEnteringBackground]) |
| return false; |
| |
| m_shouldReturnToFullscreenWhenStoppingPiP = hasMode(HTMLMediaElementEnums::VideoFullscreenModeStandard); |
| clearMode(HTMLMediaElementEnums::VideoFullscreenModeStandard); |
| return true; |
| } |
| |
| if (webPlaybackSessionModel() && (reason == ExitFullScreenReason::DoneButtonTapped || reason == ExitFullScreenReason::RemoteControlStopEventReceived)) |
| webPlaybackSessionModel()->pause(); |
| |
| |
| m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone); |
| |
| if (!m_watchdogTimer.isActive()) |
| m_watchdogTimer.startOneShot(DefaultWatchdogTimerInterval); |
| |
| return false; |
| } |
| |
| NO_RETURN_DUE_TO_ASSERT void WebVideoFullscreenInterfaceAVKit::watchdogTimerFired() |
| { |
| LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::watchdogTimerFired(%p) - no exit fullscreen response in %gs; forcing exit", this); |
| ASSERT_NOT_REACHED(); |
| exitFullscreen(IntRect()); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::setMode(HTMLMediaElementEnums::VideoFullscreenMode mode) |
| { |
| HTMLMediaElementEnums::VideoFullscreenMode newMode = m_mode | mode; |
| if (m_mode == newMode) |
| return; |
| |
| m_mode = newMode; |
| if (m_videoFullscreenModel) |
| m_videoFullscreenModel->fullscreenModeChanged(m_mode); |
| } |
| |
| void WebVideoFullscreenInterfaceAVKit::clearMode(HTMLMediaElementEnums::VideoFullscreenMode mode) |
| { |
| HTMLMediaElementEnums::VideoFullscreenMode newMode = m_mode & ~mode; |
| if (m_mode == newMode) |
| return; |
| |
| m_mode = newMode; |
| if (m_videoFullscreenModel) |
| m_videoFullscreenModel->fullscreenModeChanged(m_mode); |
| } |
| |
| #endif // HAVE(AVKIT) |
| |
| bool WebCore::supportsPictureInPicture() |
| { |
| #if PLATFORM(IOS) && HAVE(AVKIT) |
| return [getAVPictureInPictureControllerClass() isPictureInPictureSupported]; |
| #else |
| return false; |
| #endif |
| } |
| |
| #endif // PLATFORM(IOS) |