VideoLayerRemoteCocoa.mm [plain text]
/*
* Copyright (C) 2020 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. AND ITS CONTRIBUTORS ``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 ITS 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 "VideoLayerRemoteCocoa.h"
#if ENABLE(GPU_PROCESS) && PLATFORM(COCOA)
#import "LayerHostingContext.h"
#import "MediaPlayerPrivateRemote.h"
#import "VideoLayerRemote.h"
#import <WebCore/FloatRect.h>
#import <WebCore/Timer.h>
#import <pal/spi/cocoa/QuartzCoreSPI.h>
#import <wtf/MachSendRight.h>
#import <wtf/WeakObjCPtr.h>
#import <wtf/WeakPtr.h>
// We want to wait for a short time after the completion of the animation (we choose 100 ms here) to fire the timer
// to avoid excessive XPC messages from the Web process to the GPU process.
static const Seconds PostAnimationDelay { 100_ms };
@implementation WKVideoLayerRemote {
WeakPtr<WebKit::MediaPlayerPrivateRemote> _mediaPlayerPrivateRemote;
RetainPtr<CAContext> _context;
std::unique_ptr<WebCore::Timer> _resolveBoundsTimer;
bool _shouldRestartWhenTimerFires;
Seconds _delay;
}
- (instancetype)init
{
self = [super init];
if (!self)
return nil;
self.masksToBounds = YES;
_resolveBoundsTimer = makeUnique<WebCore::Timer>([weakSelf = WeakObjCPtr<WKVideoLayerRemote>(self)] {
auto localSelf = weakSelf.get();
if (!localSelf)
return;
[localSelf resolveBounds];
});
_shouldRestartWhenTimerFires = false;
return self;
}
- (WebKit::MediaPlayerPrivateRemote*)mediaPlayerPrivateRemote
{
return _mediaPlayerPrivateRemote.get();
}
- (void)setMediaPlayerPrivateRemote:(WebKit::MediaPlayerPrivateRemote*)mediaPlayerPrivateRemote
{
_mediaPlayerPrivateRemote = makeWeakPtr(*mediaPlayerPrivateRemote);
}
- (void)layoutSublayers
{
auto* sublayers = [self sublayers];
if ([sublayers count] != 1) {
ASSERT_NOT_REACHED();
return;
}
WebCore::FloatRect sourceVideoFrame = self.videoLayerFrame;
WebCore::FloatRect targetVideoFrame = self.bounds;
CGAffineTransform transform = CGAffineTransformMakeScale(targetVideoFrame.width() / sourceVideoFrame.width(), targetVideoFrame.height() / sourceVideoFrame.height());
auto* videoSublayer = [sublayers objectAtIndex:0];
[CATransaction begin];
[CATransaction setDisableActions:YES];
[videoSublayer setPosition:CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds))];
[videoSublayer setAffineTransform:transform];
[CATransaction commit];
_context = [CAContext currentContext];
NSTimeInterval animationDuration = [CATransaction animationDuration];
_delay = Seconds(animationDuration) + PostAnimationDelay;
if (_resolveBoundsTimer->isActive()) {
_shouldRestartWhenTimerFires = true;
_delay -= _resolveBoundsTimer->nextFireInterval();
return;
}
_resolveBoundsTimer->startOneShot(_delay);
}
- (void)resolveBounds
{
if (_shouldRestartWhenTimerFires) {
_shouldRestartWhenTimerFires = false;
_resolveBoundsTimer->startOneShot(_delay);
return;
}
auto* sublayers = [self sublayers];
if ([sublayers count] != 1) {
ASSERT_NOT_REACHED();
return;
}
if (CGRectEqualToRect(self.videoLayerFrame, self.bounds) && CGAffineTransformIsIdentity(self.affineTransform))
return;
[CATransaction begin];
[CATransaction setDisableActions:YES];
if (!CGRectEqualToRect(self.videoLayerFrame, self.bounds)) {
self.videoLayerFrame = self.bounds;
if (auto* mediaPlayerPrivateRemote = self.mediaPlayerPrivateRemote) {
MachSendRight fenceSendRight = MachSendRight::adopt([_context createFencePort]);
mediaPlayerPrivateRemote->setVideoInlineSizeFenced(WebCore::IntSize(self.videoLayerFrame.size), fenceSendRight);
}
}
auto* videoSublayer = [sublayers objectAtIndex:0];
[videoSublayer setAffineTransform:CGAffineTransformIdentity];
[videoSublayer setFrame:self.bounds];
[CATransaction commit];
}
@end
namespace WebKit {
PlatformLayerContainer createVideoLayerRemote(MediaPlayerPrivateRemote* mediaPlayerPrivateRemote, LayerHostingContextID contextId)
{
// Initially, all the layers will be empty (both width and height are 0) and invisible.
// The renderer will change the sizes of WKVideoLayerRemote to trigger layout of sublayers and make them visible.
auto videoLayerRemote = adoptNS([[WKVideoLayerRemote alloc] init]);
[videoLayerRemote setName:@"WKVideoLayerRemote"];
[videoLayerRemote setMediaPlayerPrivateRemote:mediaPlayerPrivateRemote];
[videoLayerRemote addSublayer:LayerHostingContext::createPlatformLayerForHostingContext(contextId).get()];
return videoLayerRemote;
}
} // namespace WebKit
#endif