blob: 15571cdc3922abd195fef3e815b4ca3bd232c862 [file] [log] [blame]
/*
* Copyright (C) 2016-2019 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"
#import "VideoFullscreenInterfaceMac.h"
#if PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE)
#import "IntRect.h"
#import "Logging.h"
#import "PictureInPictureSupport.h"
#import "PlaybackSessionInterfaceMac.h"
#import "TimeRanges.h"
#import "VideoFullscreenChangeObserver.h"
#import "VideoFullscreenModel.h"
#import "WebPlaybackControlsManager.h"
#import <AVFoundation/AVTime.h>
#import <pal/avfoundation/MediaTimeAVFoundation.h>
#import <pal/spi/cocoa/AVKitSPI.h>
#import <pal/spi/mac/PIPSPI.h>
#import <pal/cf/CoreMediaSoftLink.h>
SOFTLINK_AVKIT_FRAMEWORK()
SOFT_LINK_CLASS_OPTIONAL(AVKit, AVValueTiming)
SOFT_LINK_PRIVATE_FRAMEWORK_OPTIONAL(PIP)
SOFT_LINK_CLASS_OPTIONAL(PIP, PIPViewController)
@class WebVideoViewContainer;
@protocol WebVideoViewContainerDelegate <NSObject>
- (void)boundsDidChangeForVideoViewContainer:(WebVideoViewContainer *)videoViewContainer;
- (void)superviewDidChangeForVideoViewContainer:(WebVideoViewContainer *)videoViewContainer;
@end
using WebCore::VideoFullscreenModel;
using WebCore::HTMLMediaElementEnums;
using WebCore::MediaPlayerEnums;
using WebCore::VideoFullscreenInterfaceMac;
using WebCore::VideoFullscreenChangeObserver;
using WebCore::PlaybackSessionModel;
@interface WebVideoViewContainer : NSView {
__unsafe_unretained id <WebVideoViewContainerDelegate> _videoViewContainerDelegate;
}
@property (nonatomic, assign) id <WebVideoViewContainerDelegate> videoViewContainerDelegate;
@end
@implementation WebVideoViewContainer
@synthesize videoViewContainerDelegate=_videoViewContainerDelegate;
- (void)resizeWithOldSuperviewSize:(NSSize)oldBoundsSize
{
[super resizeWithOldSuperviewSize:oldBoundsSize];
[_videoViewContainerDelegate boundsDidChangeForVideoViewContainer:self];
}
- (void)viewDidMoveToSuperview
{
[super viewDidMoveToSuperview];
[_videoViewContainerDelegate superviewDidChangeForVideoViewContainer:self];
}
@end
enum class PIPState {
NotInPIP,
InPIP,
ExitingPIP
};
@interface WebVideoFullscreenInterfaceMacObjC : NSObject <PIPViewControllerDelegate, WebVideoViewContainerDelegate> {
WebCore::VideoFullscreenInterfaceMac* _videoFullscreenInterfaceMac;
NSSize _videoDimensions;
RetainPtr<PIPViewController> _pipViewController;
RetainPtr<NSViewController> _videoViewContainerController;
RetainPtr<WebVideoViewContainer> _videoViewContainer;
PIPState _pipState;
RetainPtr<NSWindow> _returningWindow;
NSRect _returningRect;
BOOL _playing;
BOOL _exitingToStandardFullscreen;
}
- (instancetype)initWithVideoFullscreenInterfaceMac:(WebCore::VideoFullscreenInterfaceMac*)videoFullscreenInterfaceMac;
- (void)invalidateFullscreenState;
- (void)invalidate;
// Tracking video playback state
@property (nonatomic) NSSize videoDimensions;
@property (nonatomic, getter=isPlaying) BOOL playing;
- (void)updateIsPlaying:(BOOL)isPlaying newPlaybackRate:(float)playbackRate;
// Handling PIP transitions
@property (nonatomic, getter=isExitingToStandardFullscreen) BOOL exitingToStandardFullscreen;
- (void)setUpPIPForVideoView:(NSView *)videoView withFrame:(NSRect)frame inWindow:(NSWindow *)window;
- (void)enterPIP;
- (void)exitPIP;
- (void)exitPIPAnimatingToRect:(NSRect)rect inWindow:(NSWindow *)window;
@end
@implementation WebVideoFullscreenInterfaceMacObjC
@synthesize playing=_playing;
@synthesize videoDimensions=_videoDimensions;
@synthesize exitingToStandardFullscreen=_exitingToStandardFullscreen;
- (instancetype)initWithVideoFullscreenInterfaceMac:(WebCore::VideoFullscreenInterfaceMac*)videoFullscreenInterfaceMac
{
if (!(self = [super init]))
return nil;
_videoFullscreenInterfaceMac = videoFullscreenInterfaceMac;
_pipState = PIPState::NotInPIP;
return self;
}
- (void)invalidateFullscreenState
{
[_pipViewController setDelegate:nil];
_pipViewController = nil;
[_videoViewContainer removeFromSuperview];
[_videoViewContainer setVideoViewContainerDelegate:nil];
_videoViewContainer = nil;
_videoViewContainerController = nil;
_pipState = PIPState::NotInPIP;
_exitingToStandardFullscreen = NO;
_returningWindow = nil;
_returningRect = NSZeroRect;
}
- (void)invalidate
{
[self invalidateFullscreenState];
_videoFullscreenInterfaceMac = nullptr;
_videoDimensions = NSZeroSize;
}
- (void)updateIsPlaying:(BOOL)isPlaying newPlaybackRate:(float)playbackRate
{
_playing = isPlaying && playbackRate;
[_pipViewController setPlaying:_playing];
}
- (void)setVideoDimensions:(NSSize)videoDimensions
{
_videoDimensions = videoDimensions;
[_pipViewController setAspectRatio:_videoDimensions];
}
- (void)setUpPIPForVideoView:(NSView *)videoView withFrame:(NSRect)frame inWindow:(NSWindow *)window
{
ASSERT(!_pipViewController);
ASSERT(!_videoViewContainerController);
ASSERT(!_videoViewContainer);
_pipViewController = adoptNS([allocPIPViewControllerInstance() init]);
[_pipViewController setDelegate:self];
[_pipViewController setUserCanResize:YES];
[_pipViewController setPlaying:_playing];
[self setVideoDimensions:NSEqualSizes(_videoDimensions, NSZeroSize) ? frame.size : _videoDimensions];
if (_videoFullscreenInterfaceMac && _videoFullscreenInterfaceMac->videoFullscreenModel())
_videoFullscreenInterfaceMac->videoFullscreenModel()->setVideoLayerGravity(MediaPlayerEnums::VideoGravityResizeAspectFill);
_videoViewContainer = adoptNS([[WebVideoViewContainer alloc] initWithFrame:frame]);
[_videoViewContainer setVideoViewContainerDelegate:self];
[_videoViewContainer addSubview:videoView];
videoView.frame = [_videoViewContainer bounds];
videoView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
_videoViewContainerController = adoptNS([[NSViewController alloc] init]);
[_videoViewContainerController setView:_videoViewContainer.get()];
[window.contentView addSubview:_videoViewContainer.get() positioned:NSWindowAbove relativeTo:nil];
}
- (void)enterPIP
{
if (_pipState == PIPState::InPIP)
return;
[_videoViewContainerController view].layer.backgroundColor = CGColorGetConstantColor(kCGColorBlack);
[_pipViewController presentViewControllerAsPictureInPicture:_videoViewContainerController.get()];
_pipState = PIPState::InPIP;
}
- (void)exitPIP
{
if (_pipState != PIPState::InPIP || !_pipViewController || !_videoViewContainerController)
return;
[_videoViewContainerController view].layer.backgroundColor = CGColorGetConstantColor(kCGColorClear);
[_pipViewController dismissViewController:_videoViewContainerController.get()];
_pipState = PIPState::ExitingPIP;
}
- (void)exitPIPAnimatingToRect:(NSRect)rect inWindow:(NSWindow *)window
{
_returningWindow = window;
_returningRect = rect;
[_pipViewController setReplacementRect:rect];
[_pipViewController setReplacementWindow:window];
[self exitPIP];
}
// WebVideoViewContainerDelegate
- (void)boundsDidChangeForVideoViewContainer:(WebVideoViewContainer *)videoViewContainer
{
if (!_videoViewContainer || !_pipViewController)
return;
ASSERT_UNUSED(videoViewContainer, videoViewContainer == _videoViewContainer);
if (_videoFullscreenInterfaceMac && _videoFullscreenInterfaceMac->videoFullscreenModel())
_videoFullscreenInterfaceMac->videoFullscreenModel()->setVideoLayerFrame([_videoViewContainer bounds]);
}
- (void)superviewDidChangeForVideoViewContainer:(WebVideoViewContainer *)videoViewContainer
{
if (!_videoViewContainer || !_pipViewController)
return;
ASSERT(videoViewContainer == _videoViewContainer);
if (![videoViewContainer isDescendantOf:[_pipViewController view]])
return;
// Once the view is moved into the pip view, make sure it resizes with the pip view.
videoViewContainer.frame = [videoViewContainer superview].bounds;
videoViewContainer.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
}
// PIPViewControllerDelegate
- (BOOL)pipShouldClose:(PIPViewController *)pip
{
ASSERT_UNUSED(pip, pip == _pipViewController);
if (!_videoFullscreenInterfaceMac)
return YES;
if (_videoFullscreenInterfaceMac->videoFullscreenChangeObserver())
_videoFullscreenInterfaceMac->videoFullscreenChangeObserver()->fullscreenMayReturnToInline();
_videoFullscreenInterfaceMac->requestHideAndExitPiP();
return NO;
}
- (void)pipDidClose:(PIPViewController *)pip
{
ASSERT_UNUSED(pip, pip == _pipViewController);
if (!_videoFullscreenInterfaceMac)
return;
if (_pipState != PIPState::ExitingPIP) {
// We got told to close without going through -pipActionStop, nor by exlicitly being asked to in -exitPiP:.
// Call -pipActionStop: here in order to set the fullscreen state to an expected value.
[self pipActionStop:pip];
}
if (_videoFullscreenInterfaceMac->videoFullscreenModel() && _videoViewContainer && _returningWindow && !NSEqualRects(_returningRect, NSZeroRect)) {
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
context.allowsImplicitAnimation = NO;
[_videoViewContainer setFrame:_returningRect];
_videoFullscreenInterfaceMac->videoFullscreenModel()->setVideoLayerFrame([_videoViewContainer bounds]);
_videoFullscreenInterfaceMac->videoFullscreenModel()->setVideoLayerGravity(MediaPlayerEnums::VideoGravityResizeAspect);
[[_returningWindow contentView] addSubview:_videoViewContainer.get() positioned:NSWindowAbove relativeTo:nil];
} completionHandler:nil];
}
if (!self.isExitingToStandardFullscreen) {
if (VideoFullscreenModel* videoFullscreenModel = _videoFullscreenInterfaceMac->videoFullscreenModel()) {
videoFullscreenModel->didExitPictureInPicture();
videoFullscreenModel->setVideoLayerGravity(MediaPlayerEnums::VideoGravityResizeAspect);
}
}
if (VideoFullscreenChangeObserver* fullscreenChangeObserver = _videoFullscreenInterfaceMac->videoFullscreenChangeObserver())
fullscreenChangeObserver->didExitFullscreen();
_videoFullscreenInterfaceMac->clearMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
}
- (void)pipActionPlay:(PIPViewController *)pip
{
ASSERT_UNUSED(pip, pip == _pipViewController);
if (_videoFullscreenInterfaceMac && _videoFullscreenInterfaceMac->playbackSessionModel())
_videoFullscreenInterfaceMac->playbackSessionModel()->play();
}
- (void)pipActionPause:(PIPViewController *)pip
{
ASSERT_UNUSED(pip, pip == _pipViewController);
if (_videoFullscreenInterfaceMac && _videoFullscreenInterfaceMac->playbackSessionModel())
_videoFullscreenInterfaceMac->playbackSessionModel()->pause();
}
- (void)pipActionStop:(PIPViewController *)pip
{
ASSERT_UNUSED(pip, pip == _pipViewController);
if (!_videoFullscreenInterfaceMac)
return;
if (PlaybackSessionModel* playbackSessionModel = _videoFullscreenInterfaceMac->playbackSessionModel())
playbackSessionModel->pause();
_videoFullscreenInterfaceMac->requestHideAndExitPiP();
_pipState = PIPState::ExitingPIP;
}
@end
namespace WebCore {
using namespace PAL;
VideoFullscreenInterfaceMac::VideoFullscreenInterfaceMac(PlaybackSessionInterfaceMac& playbackSessionInterface)
: m_playbackSessionInterface(playbackSessionInterface)
{
ASSERT(m_playbackSessionInterface->playbackSessionModel());
auto model = m_playbackSessionInterface->playbackSessionModel();
model->addClient(*this);
[videoFullscreenInterfaceObjC() updateIsPlaying:model->isPlaying() newPlaybackRate:model->playbackRate()];
}
VideoFullscreenInterfaceMac::~VideoFullscreenInterfaceMac()
{
if (auto* model = m_playbackSessionInterface->playbackSessionModel())
model->removeClient(*this);
if (m_videoFullscreenModel)
m_videoFullscreenModel->removeClient(*this);
}
void VideoFullscreenInterfaceMac::setVideoFullscreenModel(VideoFullscreenModel* model)
{
if (m_videoFullscreenModel)
m_videoFullscreenModel->removeClient(*this);
m_videoFullscreenModel = makeWeakPtr(model);
if (m_videoFullscreenModel)
m_videoFullscreenModel->addClient(*this);
}
void VideoFullscreenInterfaceMac::setVideoFullscreenChangeObserver(VideoFullscreenChangeObserver* observer)
{
m_fullscreenChangeObserver = makeWeakPtr(observer);
}
void VideoFullscreenInterfaceMac::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 VideoFullscreenInterfaceMac::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);
}
void VideoFullscreenInterfaceMac::rateChanged(bool isPlaying, float playbackRate)
{
[videoFullscreenInterfaceObjC() updateIsPlaying:isPlaying newPlaybackRate:playbackRate];
}
void VideoFullscreenInterfaceMac::ensureControlsManager()
{
m_playbackSessionInterface->ensureControlsManager();
}
WebVideoFullscreenInterfaceMacObjC *VideoFullscreenInterfaceMac::videoFullscreenInterfaceObjC()
{
if (!m_webVideoFullscreenInterfaceObjC)
m_webVideoFullscreenInterfaceObjC = adoptNS([[WebVideoFullscreenInterfaceMacObjC alloc] initWithVideoFullscreenInterfaceMac:this]);
return m_webVideoFullscreenInterfaceObjC.get();
}
void VideoFullscreenInterfaceMac::setupFullscreen(NSView& layerHostedView, const IntRect& initialRect, NSWindow *parentWindow, HTMLMediaElementEnums::VideoFullscreenMode mode, bool allowsPictureInPicturePlayback)
{
LOG(Fullscreen, "VideoFullscreenInterfaceMac::setupFullscreen(%p), initialRect:{%d, %d, %d, %d}, parentWindow:%p, mode:%d", this, initialRect.x(), initialRect.y(), initialRect.width(), initialRect.height(), parentWindow, mode);
UNUSED_PARAM(allowsPictureInPicturePlayback);
ASSERT(mode == HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
m_mode = mode;
[videoFullscreenInterfaceObjC() setUpPIPForVideoView:&layerHostedView withFrame:(NSRect)initialRect inWindow:parentWindow];
dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), this] {
if (m_fullscreenChangeObserver)
m_fullscreenChangeObserver->didSetupFullscreen();
});
}
void VideoFullscreenInterfaceMac::enterFullscreen()
{
LOG(Fullscreen, "VideoFullscreenInterfaceMac::enterFullscreen(%p)", this);
RELEASE_ASSERT(m_videoFullscreenModel);
if (mode() == HTMLMediaElementEnums::VideoFullscreenModePictureInPicture) {
m_videoFullscreenModel->willEnterPictureInPicture();
[m_webVideoFullscreenInterfaceObjC enterPIP];
#if ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
[m_playbackSessionInterface->playBackControlsManager() setPictureInPictureActive:YES];
#endif
// FIXME(rdar://problem/42250952): Move this call into a completion handler or delegate callback.
m_videoFullscreenModel->didEnterPictureInPicture();
if (m_fullscreenChangeObserver)
m_fullscreenChangeObserver->didEnterFullscreen();
}
}
void VideoFullscreenInterfaceMac::exitFullscreen(const IntRect& finalRect, NSWindow *parentWindow)
{
LOG(Fullscreen, "VideoFullscreenInterfaceMac::exitFullscreen(%p), finalRect:{%d, %d, %d, %d}, parentWindow:%p", this, finalRect.x(), finalRect.y(), finalRect.width(), finalRect.height(), parentWindow);
#if ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
[m_playbackSessionInterface->playBackControlsManager() setPictureInPictureActive:NO];
#endif
if (finalRect.isEmpty())
[m_webVideoFullscreenInterfaceObjC exitPIP];
else
[m_webVideoFullscreenInterfaceObjC exitPIPAnimatingToRect:finalRect inWindow:parentWindow];
}
void VideoFullscreenInterfaceMac::exitFullscreenWithoutAnimationToMode(HTMLMediaElementEnums::VideoFullscreenMode mode)
{
LOG(Fullscreen, "VideoFullscreenInterfaceMac::exitFullscreenWithoutAnimationToMode(%p), mode:%d", this, mode);
#if ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
[m_playbackSessionInterface->playBackControlsManager() setPictureInPictureActive:NO];
#endif
bool isExitingToStandardFullscreen = mode == HTMLMediaElementEnums::VideoFullscreenModeStandard;
// On Mac, standard fullscreen is handled by the Fullscreen API and not by VideoFullscreenManager.
// Just update m_mode directly to HTMLMediaElementEnums::VideoFullscreenModeStandard in that case to keep
// m_mode in sync with the fullscreen mode in HTMLMediaElement.
if (isExitingToStandardFullscreen)
m_mode = HTMLMediaElementEnums::VideoFullscreenModeStandard;
[m_webVideoFullscreenInterfaceObjC setExitingToStandardFullscreen:isExitingToStandardFullscreen];
[m_webVideoFullscreenInterfaceObjC exitPIP];
}
void VideoFullscreenInterfaceMac::cleanupFullscreen()
{
LOG(Fullscreen, "VideoFullscreenInterfaceMac::cleanupFullscreen(%p)", this);
[m_webVideoFullscreenInterfaceObjC exitPIP];
[m_webVideoFullscreenInterfaceObjC invalidateFullscreenState];
if (m_fullscreenChangeObserver)
m_fullscreenChangeObserver->didCleanupFullscreen();
}
void VideoFullscreenInterfaceMac::invalidate()
{
LOG(Fullscreen, "VideoFullscreenInterfaceMac::invalidate(%p)", this);
m_videoFullscreenModel = nullptr;
m_fullscreenChangeObserver = nullptr;
cleanupFullscreen();
[m_webVideoFullscreenInterfaceObjC invalidate];
m_webVideoFullscreenInterfaceObjC = nil;
}
void VideoFullscreenInterfaceMac::requestHideAndExitPiP()
{
if (!m_videoFullscreenModel)
return;
m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone);
m_videoFullscreenModel->willExitPictureInPicture();
}
#if !LOG_DISABLED
static const char* boolString(bool val)
{
return val ? "true" : "false";
}
#endif
void VideoFullscreenInterfaceMac::preparedToReturnToInline(bool visible, const IntRect& inlineRect, NSWindow *parentWindow)
{
LOG(Fullscreen, "VideoFullscreenInterfaceMac::preparedToReturnToInline(%p), visible:%s, inlineRect:{%d, %d, %d, %d}, parentWindow:%p", this, boolString(visible), inlineRect.x(), inlineRect.y(), inlineRect.width(), inlineRect.height(), parentWindow);
if (!visible) {
[m_webVideoFullscreenInterfaceObjC exitPIP];
return;
}
ASSERT(parentWindow);
[m_webVideoFullscreenInterfaceObjC exitPIPAnimatingToRect:(NSRect)inlineRect inWindow:parentWindow];
}
void VideoFullscreenInterfaceMac::externalPlaybackChanged(bool enabled, PlaybackSessionModel::ExternalPlaybackTargetType, const String&)
{
LOG(Fullscreen, "VideoFullscreenInterfaceMac::externalPlaybackChanged(%p), enabled:%s", this, boolString(enabled));
if (enabled && m_mode == HTMLMediaElementEnums::VideoFullscreenModePictureInPicture)
exitFullscreen(IntRect(), nil);
}
void VideoFullscreenInterfaceMac::hasVideoChanged(bool hasVideo)
{
LOG(Fullscreen, "VideoFullscreenInterfaceMac::hasVideoChanged(%p):%s", this, boolString(hasVideo));
if (!hasVideo)
exitFullscreenWithoutAnimationToMode(HTMLMediaElementEnums::VideoFullscreenModeNone);
}
void VideoFullscreenInterfaceMac::videoDimensionsChanged(const FloatSize& videoDimensions)
{
LOG(Fullscreen, "VideoFullscreenInterfaceMac::videoDimensionsChanged(%p), width:%.0f, height:%.0f", this, videoDimensions.width(), videoDimensions.height());
// Width and height can be zero when we are transitioning from one video to another. Ignore zero values.
if (!videoDimensions.isZero())
[m_webVideoFullscreenInterfaceObjC setVideoDimensions:videoDimensions];
}
bool VideoFullscreenInterfaceMac::isPlayingVideoInEnhancedFullscreen() const
{
return hasMode(WebCore::HTMLMediaElementEnums::VideoFullscreenModePictureInPicture) && [m_webVideoFullscreenInterfaceObjC isPlaying];
}
bool supportsPictureInPicture()
{
return PIPLibrary() && getPIPViewControllerClass();
}
}
#endif