Experimental hls implementation

This commit is contained in:
Isaac 2024-09-27 20:27:11 +08:00
parent d70a0cf0e0
commit 69bb337f6f
20 changed files with 30578 additions and 319 deletions

View File

@ -20,7 +20,7 @@ public protocol UniversalVideoContentNode: AnyObject {
var status: Signal<MediaPlayerStatus, NoError> { get }
var bufferingStatus: Signal<(RangeSet<Int64>, Int64)?, NoError> { get }
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition)
func updateLayout(size: CGSize, actualSize: CGSize, transition: ContainedViewLayoutTransition)
func play()
func pause()
@ -68,7 +68,7 @@ public protocol UniversalVideoDecoration: AnyObject {
func updateContentNode(_ contentNode: (UniversalVideoContentNode & ASDisplayNode)?)
func updateContentNodeSnapshot(_ snapshot: UIView?)
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition)
func updateLayout(size: CGSize, actualSize: CGSize, transition: ContainedViewLayoutTransition)
func tap()
}
@ -247,8 +247,8 @@ public final class UniversalVideoNode: ASDisplayNode {
}
}
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
self.decoration.updateLayout(size: size, transition: transition)
public func updateLayout(size: CGSize, actualSize: CGSize? = nil, transition: ContainedViewLayoutTransition) {
self.decoration.updateLayout(size: size, actualSize: actualSize ?? size, transition: transition)
}
public func play() {

View File

@ -325,7 +325,7 @@ private final class VideoDecoration: UniversalVideoDecoration {
private var contentNode: (ASDisplayNode & UniversalVideoContentNode)?
private var validLayoutSize: CGSize?
private var validLayout: (size: CGSize, actualSize: CGSize)?
public init() {
self.contentContainerNode = ASDisplayNode()
@ -345,9 +345,9 @@ private final class VideoDecoration: UniversalVideoDecoration {
if let contentNode = contentNode {
if contentNode.supernode !== self.contentContainerNode {
self.contentContainerNode.addSubnode(contentNode)
if let validLayoutSize = self.validLayoutSize {
contentNode.frame = CGRect(origin: CGPoint(), size: validLayoutSize)
contentNode.updateLayout(size: validLayoutSize, transition: .immediate)
if let validLayout = self.validLayout {
contentNode.frame = CGRect(origin: CGPoint(), size: validLayout.size)
contentNode.updateLayout(size: validLayout.size, actualSize: validLayout.actualSize, transition: .immediate)
}
}
}
@ -405,8 +405,8 @@ private final class VideoDecoration: UniversalVideoDecoration {
public func updateContentNodeSnapshot(_ snapshot: UIView?) {
}
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayoutSize = size
public func updateLayout(size: CGSize, actualSize: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayout = (size, actualSize)
let bounds = CGRect(origin: CGPoint(), size: size)
if let backgroundNode = self.backgroundNode {
@ -421,7 +421,7 @@ private final class VideoDecoration: UniversalVideoDecoration {
}
if let contentNode = self.contentNode {
transition.updateFrame(node: contentNode, frame: CGRect(origin: CGPoint(), size: size))
contentNode.updateLayout(size: size, transition: transition)
contentNode.updateLayout(size: size, actualSize: actualSize, transition: transition)
}
}

View File

@ -1141,7 +1141,7 @@ private final class StickerVideoDecoration: UniversalVideoDecoration {
private var contentNode: (ASDisplayNode & UniversalVideoContentNode)?
private var validLayoutSize: CGSize?
private var validLayout: (size: CGSize, actualSize: CGSize)?
public init() {
self.contentContainerNode = ASDisplayNode()
@ -1161,9 +1161,9 @@ private final class StickerVideoDecoration: UniversalVideoDecoration {
if let contentNode = contentNode {
if contentNode.supernode !== self.contentContainerNode {
self.contentContainerNode.addSubnode(contentNode)
if let validLayoutSize = self.validLayoutSize {
contentNode.frame = CGRect(origin: CGPoint(), size: validLayoutSize)
contentNode.updateLayout(size: validLayoutSize, transition: .immediate)
if let validLayout = self.validLayout {
contentNode.frame = CGRect(origin: CGPoint(), size: validLayout.size)
contentNode.updateLayout(size: validLayout.size, actualSize: validLayout.actualSize, transition: .immediate)
}
}
}
@ -1221,8 +1221,8 @@ private final class StickerVideoDecoration: UniversalVideoDecoration {
public func updateContentNodeSnapshot(_ snapshot: UIView?) {
}
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayoutSize = size
public func updateLayout(size: CGSize, actualSize: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayout = (size, actualSize)
let bounds = CGRect(origin: CGPoint(), size: size)
if let backgroundNode = self.backgroundNode {
@ -1237,7 +1237,7 @@ private final class StickerVideoDecoration: UniversalVideoDecoration {
}
if let contentNode = self.contentNode {
transition.updateFrame(node: contentNode, frame: CGRect(origin: CGPoint(), size: size))
contentNode.updateLayout(size: size, transition: transition)
contentNode.updateLayout(size: size, actualSize: actualSize, transition: transition)
}
}

View File

@ -14,7 +14,7 @@ public final class GalleryVideoDecoration: UniversalVideoDecoration {
private var contentNode: (ASDisplayNode & UniversalVideoContentNode)?
private var validLayoutSize: CGSize?
private var validLayout: (size: CGSize, actualSize: CGSize)?
public init() {
self.contentContainerNode = ASDisplayNode()
@ -34,9 +34,9 @@ public final class GalleryVideoDecoration: UniversalVideoDecoration {
if let contentNode = contentNode {
if contentNode.supernode !== self.contentContainerNode {
self.contentContainerNode.addSubnode(contentNode)
if let validLayoutSize = self.validLayoutSize {
contentNode.frame = CGRect(origin: CGPoint(), size: validLayoutSize)
contentNode.updateLayout(size: validLayoutSize, transition: .immediate)
if let validLayout = self.validLayout {
contentNode.frame = CGRect(origin: CGPoint(), size: validLayout.size)
contentNode.updateLayout(size: validLayout.size, actualSize: validLayout.actualSize, transition: .immediate)
}
}
}
@ -94,8 +94,8 @@ public final class GalleryVideoDecoration: UniversalVideoDecoration {
public func updateContentNodeSnapshot(_ snapshot: UIView?) {
}
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayoutSize = size
public func updateLayout(size: CGSize, actualSize: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayout = (size, actualSize)
let bounds = CGRect(origin: CGPoint(), size: size)
if let backgroundNode = self.backgroundNode {
@ -110,7 +110,7 @@ public final class GalleryVideoDecoration: UniversalVideoDecoration {
}
if let contentNode = self.contentNode {
transition.updateFrame(node: contentNode, frame: CGRect(origin: CGPoint(), size: size))
contentNode.updateLayout(size: size, transition: transition)
contentNode.updateLayout(size: size, actualSize: actualSize, transition: transition)
}
}

View File

@ -1186,7 +1186,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
videoScale = 2.0
}
let videoSize = CGSize(width: item.content.dimensions.width * videoScale, height: item.content.dimensions.height * videoScale)
videoNode.updateLayout(size: videoSize, transition: .immediate)
let actualVideoSize = CGSize(width: item.content.dimensions.width, height: item.content.dimensions.height)
videoNode.updateLayout(size: videoSize, actualSize: actualVideoSize, transition: .immediate)
videoNode.ownsContentNodeUpdated = { [weak self] value in
if let strongSelf = self {
strongSelf.updateDisplayPlaceholder(!value)

View File

@ -626,7 +626,7 @@ private final class VideoDecoration: UniversalVideoDecoration {
private var contentNode: (ASDisplayNode & UniversalVideoContentNode)?
private var validLayoutSize: CGSize?
private var validLayout: (size: CGSize, actualSize: CGSize)?
public init() {
self.contentContainerNode = ASDisplayNode()
@ -646,9 +646,9 @@ private final class VideoDecoration: UniversalVideoDecoration {
if let contentNode = contentNode {
if contentNode.supernode !== self.contentContainerNode {
self.contentContainerNode.addSubnode(contentNode)
if let validLayoutSize = self.validLayoutSize {
contentNode.frame = CGRect(origin: CGPoint(), size: validLayoutSize)
contentNode.updateLayout(size: validLayoutSize, transition: .immediate)
if let validLayout = self.validLayout {
contentNode.frame = CGRect(origin: CGPoint(), size: validLayout.size)
contentNode.updateLayout(size: validLayout.size, actualSize: validLayout.actualSize, transition: .immediate)
}
}
}
@ -706,8 +706,8 @@ private final class VideoDecoration: UniversalVideoDecoration {
public func updateContentNodeSnapshot(_ snapshot: UIView?) {
}
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayoutSize = size
public func updateLayout(size: CGSize, actualSize: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayout = (size, actualSize)
let bounds = CGRect(origin: CGPoint(), size: size)
if let backgroundNode = self.backgroundNode {
@ -722,7 +722,7 @@ private final class VideoDecoration: UniversalVideoDecoration {
}
if let contentNode = self.contentNode {
transition.updateFrame(node: contentNode, frame: CGRect(origin: CGPoint(), size: size))
contentNode.updateLayout(size: size, transition: transition)
contentNode.updateLayout(size: size, actualSize: actualSize, transition: transition)
}
}

View File

@ -511,7 +511,7 @@ private final class VideoDecoration: UniversalVideoDecoration {
private var contentNode: (ASDisplayNode & UniversalVideoContentNode)?
private var validLayoutSize: CGSize?
private var validLayout: (size: CGSize, actualSize: CGSize)?
public init() {
self.contentContainerNode = ASDisplayNode()
@ -531,9 +531,9 @@ private final class VideoDecoration: UniversalVideoDecoration {
if let contentNode = contentNode {
if contentNode.supernode !== self.contentContainerNode {
self.contentContainerNode.addSubnode(contentNode)
if let validLayoutSize = self.validLayoutSize {
contentNode.frame = CGRect(origin: CGPoint(), size: validLayoutSize)
contentNode.updateLayout(size: validLayoutSize, transition: .immediate)
if let validLayout = self.validLayout {
contentNode.frame = CGRect(origin: CGPoint(), size: validLayout.size)
contentNode.updateLayout(size: validLayout.size, actualSize: validLayout.actualSize, transition: .immediate)
}
}
}
@ -591,8 +591,8 @@ private final class VideoDecoration: UniversalVideoDecoration {
public func updateContentNodeSnapshot(_ snapshot: UIView?) {
}
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayoutSize = size
public func updateLayout(size: CGSize, actualSize: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayout = (size, actualSize)
let bounds = CGRect(origin: CGPoint(), size: size)
if let backgroundNode = self.backgroundNode {
@ -607,7 +607,7 @@ private final class VideoDecoration: UniversalVideoDecoration {
}
if let contentNode = self.contentNode {
transition.updateFrame(node: contentNode, frame: CGRect(origin: CGPoint(), size: size))
contentNode.updateLayout(size: size, transition: transition)
contentNode.updateLayout(size: size, actualSize: actualSize, transition: transition)
}
}

View File

@ -14,7 +14,7 @@ public final class StoryVideoDecoration: UniversalVideoDecoration {
private var contentNode: (ASDisplayNode & UniversalVideoContentNode)?
private var validLayoutSize: CGSize?
private var validLayout: (size: CGSize, actualSize: CGSize)?
public init() {
self.contentContainerNode = ASDisplayNode()
@ -34,9 +34,9 @@ public final class StoryVideoDecoration: UniversalVideoDecoration {
if let contentNode = contentNode {
if contentNode.supernode !== self.contentContainerNode {
self.contentContainerNode.addSubnode(contentNode)
if let validLayoutSize = self.validLayoutSize {
contentNode.frame = CGRect(origin: CGPoint(), size: validLayoutSize)
contentNode.updateLayout(size: validLayoutSize, transition: .immediate)
if let validLayout = self.validLayout {
contentNode.frame = CGRect(origin: CGPoint(), size: validLayout.size)
contentNode.updateLayout(size: validLayout.size, actualSize: validLayout.actualSize, transition: .immediate)
}
}
}
@ -94,8 +94,8 @@ public final class StoryVideoDecoration: UniversalVideoDecoration {
public func updateContentNodeSnapshot(_ snapshot: UIView?) {
}
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayoutSize = size
public func updateLayout(size: CGSize, actualSize: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayout = (size, actualSize)
let bounds = CGRect(origin: CGPoint(), size: size)
if let backgroundNode = self.backgroundNode {
@ -110,7 +110,7 @@ public final class StoryVideoDecoration: UniversalVideoDecoration {
}
if let contentNode = self.contentNode {
transition.updateFrame(node: contentNode, frame: CGRect(origin: CGPoint(), size: size))
contentNode.updateLayout(size: size, transition: transition)
contentNode.updateLayout(size: size, actualSize: actualSize, transition: transition)
}
}

View File

@ -0,0 +1,171 @@
<!DOCTYPE html>
<html>
<head>
<style>
html, body {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
overflow: hidden;
}
#videoPlayer {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
object-fit: fill;
}
</style>
<script src="hls.js"></script>
<script type="text/javascript">
function postPlayerEvent(eventName, eventData) {
window.webkit.messageHandlers.performAction.postMessage({'event': eventName, 'data': eventData});
};
var video;
var hls;
var isManifestParsed = false;
var isFirstFrameReady = false;
var currentTimeUpdateTimeout = null;
function playerInitialize(params) {
video = document.getElementById('videoPlayer');
video.addEventListener('loadeddata', (event) => {
if (!isFirstFrameReady) {
isFirstFrameReady = true;
refreshPlayerStatus();
}
});
video.addEventListener("playing", function() {
refreshPlayerStatus();
});
video.addEventListener("pause", function() {
refreshPlayerStatus();
});
video.addEventListener("seeking", function() {
refreshPlayerStatus();
});
video.addEventListener("waiting", function() {
refreshPlayerStatus();
});
hls = new Hls({
startLevel: 0,
testBandwidth: false,
debug: params['debug'],
autoStartLoad: false
});
hls.on(Hls.Events.MANIFEST_PARSED, function() {
isManifestParsed = true;
refreshPlayerStatus();
});
hls.on(Hls.Events.LEVEL_SWITCHED, function() {
refreshPlayerStatus();
});
hls.on(Hls.Events.LEVELS_UPDATED, function() {
refreshPlayerStatus();
});
hls.loadSource('master.m3u8');
hls.attachMedia(video);
}
function playerLoad(initialLevelIndex) {
hls.startLevel = initialLevelIndex;
hls.startLoad(startPosition=-1);
}
function playerPlay() {
video.play();
}
function playerPause() {
video.pause();
}
function playerSetBaseRate(value) {
video.playbackRate = value;
}
function playerSetLevel(level) {
if (level >= 0) {
hls.autoLevelEnabled = false;
hls.currentLevel = level;
} else {
hls.autoLevelEnabled = true;
}
}
function playerSeek(value) {
video.currentTime = value;
}
function playerSetIsMuted(value) {
video.muted = value;
}
function getLevels() {
var levels = [];
for (var i = 0; i < hls.levels.length; i++) {
level = hls.levels[i];
levels.push({
'index': i,
'bitrate': level.bitrate || 0,
'width': level.width || 0,
'height': level.height || 0
});
}
return levels;
}
function refreshPlayerStatus() {
var isPlaying = false;
if (!video.paused && !video.ended && video.readyState > 2) {
isPlaying = true;
}
postPlayerEvent('playerStatus', {
'isReady': isManifestParsed,
'isFirstFrameReady': isFirstFrameReady,
'isPlaying': !video.paused,
'rate': isPlaying ? video.playbackRate : 0.0,
'defaultRate': video.playbackRate,
'levels': getLevels(),
'currentLevel': hls.currentLevel
});
refreshPlayerCurrentTime();
if (isPlaying) {
if (currentTimeUpdateTimeout == null) {
currentTimeUpdateTimeout = setTimeout(() => {
refreshPlayerCurrentTime();
}, 200);
}
} else {
if(currentTimeUpdateTimeout != null){
clearTimeout(currentTimeUpdateTimeout);
currentTimeUpdateTimeout = null;
}
}
}
function refreshPlayerCurrentTime() {
postPlayerEvent('playerCurrentTime', {
'value': video.currentTime
});
currentTimeUpdateTimeout = setTimeout(() => {
refreshPlayerCurrentTime()
}, 200);
}
</script>
</head>
<body>
<video id="videoPlayer" playsinline></video>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -24,7 +24,7 @@ final class OverlayInstantVideoDecoration: UniversalVideoDecoration {
private var contentNode: (ASDisplayNode & UniversalVideoContentNode)?
private var contentNodeSnapshot: UIView?
private var validLayoutSize: CGSize?
private var validLayout: (size: CGSize, actualSize: CGSize)?
init(tapped: @escaping () -> Void) {
self.tapped = tapped
@ -58,9 +58,9 @@ final class OverlayInstantVideoDecoration: UniversalVideoDecoration {
self.progressNode.status = contentNode.status
if contentNode.supernode !== self.contentContainerNode {
self.contentContainerNode.addSubnode(contentNode)
if let validLayoutSize = self.validLayoutSize {
contentNode.frame = CGRect(origin: CGPoint(), size: validLayoutSize)
contentNode.updateLayout(size: validLayoutSize, transition: .immediate)
if let validLayout = self.validLayout {
contentNode.frame = CGRect(origin: CGPoint(), size: validLayout.size)
contentNode.updateLayout(size: validLayout.size, actualSize: validLayout.actualSize, transition: .immediate)
}
}
}
@ -74,15 +74,15 @@ final class OverlayInstantVideoDecoration: UniversalVideoDecoration {
if let snapshot = snapshot {
self.contentContainerNode.view.addSubview(snapshot)
if let _ = self.validLayoutSize {
if let _ = self.validLayout {
snapshot.frame = CGRect(origin: CGPoint(), size: snapshot.frame.size)
}
}
}
}
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayoutSize = size
func updateLayout(size: CGSize, actualSize: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayout = (size, actualSize)
self.contentContainerNode.cornerRadius = size.width / 2.0
@ -96,7 +96,7 @@ final class OverlayInstantVideoDecoration: UniversalVideoDecoration {
transition.updateFrame(node: self.contentContainerNode, frame: CGRect(origin: CGPoint(), size: size))
if let contentNode = self.contentNode {
transition.updateFrame(node: contentNode, frame: CGRect(origin: CGPoint(), size: size).insetBy(dx: -0.5, dy: -0.5))
contentNode.updateLayout(size: size, transition: transition)
contentNode.updateLayout(size: size, actualSize: actualSize, transition: transition)
}
if let contentNodeSnapshot = self.contentNodeSnapshot {

View File

@ -16,7 +16,7 @@ public final class ChatBubbleInstantVideoDecoration: UniversalVideoDecoration {
private var contentNode: (ASDisplayNode & UniversalVideoContentNode)?
private let inset: CGFloat
private var validLayoutSize: CGSize?
private var validLayout: (size: CGSize, actualSize: CGSize)?
public init(inset: CGFloat, backgroundImage: UIImage?, tapped: @escaping () -> Void) {
self.inset = inset
@ -51,9 +51,9 @@ public final class ChatBubbleInstantVideoDecoration: UniversalVideoDecoration {
if let contentNode = contentNode {
if contentNode.supernode !== self.contentContainerNode {
self.contentContainerNode.addSubnode(contentNode)
if let validLayoutSize = self.validLayoutSize {
contentNode.frame = CGRect(origin: CGPoint(), size: validLayoutSize)
contentNode.updateLayout(size: validLayoutSize, transition: .immediate)
if let validLayout = self.validLayout {
contentNode.frame = CGRect(origin: CGPoint(), size: validLayout.size)
contentNode.updateLayout(size: validLayout.size, actualSize: validLayout.actualSize, transition: .immediate)
}
}
}
@ -63,8 +63,8 @@ public final class ChatBubbleInstantVideoDecoration: UniversalVideoDecoration {
public func updateContentNodeSnapshot(_ snapshot: UIView?) {
}
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayoutSize = size
public func updateLayout(size: CGSize, actualSize: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayout = (size, actualSize)
let diameter = size.width + inset
self.contentContainerNode.cornerRadius = (diameter - 3.0) / 2.0
@ -80,7 +80,7 @@ public final class ChatBubbleInstantVideoDecoration: UniversalVideoDecoration {
self.contentContainerNode.subnodeTransform = CATransform3DMakeScale((contentFrame.width + 2.0) / contentFrame.width, (contentFrame.width + 2.0) / contentFrame.width, 1.0)
if let contentNode = self.contentNode {
transition.updateFrame(node: contentNode, frame: CGRect(origin: CGPoint(), size: size))
contentNode.updateLayout(size: size, transition: transition)
contentNode.updateLayout(size: size, actualSize: actualSize, transition: transition)
}
}

View File

@ -23,7 +23,7 @@ public final class ChatBubbleVideoDecoration: UniversalVideoDecoration {
private var contentNode: (ASDisplayNode & UniversalVideoContentNode)?
private var validLayoutSize: CGSize?
private var validLayout: (size: CGSize, actualSize: CGSize)?
public init(corners: ImageCorners, nativeSize: CGSize, contentMode: ChatBubbleVideoDecorationContentMode, backgroundColor: UIColor) {
self.corners = corners
@ -82,23 +82,23 @@ public final class ChatBubbleVideoDecoration: UniversalVideoDecoration {
if let contentNode = contentNode {
if contentNode.supernode !== self.contentContainerNode {
self.contentContainerNode.addSubnode(contentNode)
if let size = self.validLayoutSize {
if let validLayout = self.validLayout {
var scaledSize: CGSize
switch self.contentMode {
case .aspectFit:
scaledSize = self.nativeSize.aspectFitted(size)
scaledSize = self.nativeSize.aspectFitted(validLayout.size)
case .aspectFill:
scaledSize = self.nativeSize.aspectFilled(size)
scaledSize = self.nativeSize.aspectFilled(validLayout.size)
}
if abs(scaledSize.width - size.width) < 2.0 {
scaledSize.width = size.width
if abs(scaledSize.width - validLayout.size.width) < 2.0 {
scaledSize.width = validLayout.size.width
}
if abs(scaledSize.height - size.height) < 2.0 {
scaledSize.height = size.height
if abs(scaledSize.height - validLayout.size.height) < 2.0 {
scaledSize.height = validLayout.size.height
}
contentNode.frame = CGRect(origin: CGPoint(x: floor((size.width - scaledSize.width) / 2.0), y: floor((size.height - scaledSize.height) / 2.0)), size: scaledSize)
contentNode.updateLayout(size: scaledSize, transition: .immediate)
contentNode.frame = CGRect(origin: CGPoint(x: floor((validLayout.size.width - scaledSize.width) / 2.0), y: floor((validLayout.size.height - scaledSize.height) / 2.0)), size: scaledSize)
contentNode.updateLayout(size: scaledSize, actualSize: scaledSize, transition: .immediate)
}
}
}
@ -108,8 +108,8 @@ public final class ChatBubbleVideoDecoration: UniversalVideoDecoration {
public func updateContentNodeSnapshot(_ snapshot: UIView?) {
}
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayoutSize = size
public func updateLayout(size: CGSize, actualSize: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayout = (size, actualSize)
let bounds = CGRect(origin: CGPoint(), size: size)
if let backgroundNode = self.backgroundNode {
@ -137,7 +137,7 @@ public final class ChatBubbleVideoDecoration: UniversalVideoDecoration {
scaledSize.height = size.height
}
transition.updateFrame(node: contentNode, frame: CGRect(origin: CGPoint(x: floor((size.width - scaledSize.width) / 2.0), y: floor((size.height - scaledSize.height) / 2.0)), size: scaledSize))
contentNode.updateLayout(size: scaledSize, transition: transition)
contentNode.updateLayout(size: scaledSize, actualSize: scaledSize, transition: transition)
}
}

View File

@ -217,7 +217,7 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
private var dimensions: CGSize?
private let dimensionsPromise = ValuePromise<CGSize>(CGSize())
private var validLayout: CGSize?
private var validLayout: (size: CGSize, actualSize: CGSize)?
private var shouldPlay: Bool = false
@ -306,8 +306,8 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
if let dimensions = getSize() {
strongSelf.dimensions = dimensions
strongSelf.dimensionsPromise.set(dimensions)
if let size = strongSelf.validLayout {
strongSelf.updateLayout(size: size, transition: .immediate)
if let validLayout = strongSelf.validLayout {
strongSelf.updateLayout(size: validLayout.size, actualSize: validLayout.actualSize, transition: .immediate)
}
}
}
@ -432,8 +432,8 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
}
}
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayout = size
func updateLayout(size: CGSize, actualSize: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayout = (size, actualSize)
if let dimensions = self.dimensions {
let imageSize = CGSize(width: floor(dimensions.width / 2.0), height: floor(dimensions.height / 2.0))

View File

@ -47,7 +47,7 @@ final class OverlayVideoDecoration: UniversalVideoDecoration {
private let statusDisposable = MetaDisposable()
private var validLayoutSize: CGSize?
private var validLayout: (size: CGSize, actualSize: CGSize)?
init(contentDimensions: CGSize, unminimize: @escaping () -> Void, togglePlayPause: @escaping () -> Void, expand: @escaping () -> Void, close: @escaping () -> Void, controlsAreShowingUpdated: @escaping (Bool) -> Void) {
self.contentDimensions = contentDimensions
@ -106,9 +106,9 @@ final class OverlayVideoDecoration: UniversalVideoDecoration {
if let contentNode = contentNode {
if contentNode.supernode !== self.contentContainerNode {
self.contentContainerNode.addSubnode(contentNode)
if let validLayoutSize = self.validLayoutSize {
contentNode.frame = self.frameForContent(size: validLayoutSize)
contentNode.updateLayout(size: validLayoutSize, transition: .immediate)
if let validLayout = self.validLayout {
contentNode.frame = self.frameForContent(size: validLayout.size)
contentNode.updateLayout(size: validLayout.size, actualSize: validLayout.actualSize, transition: .immediate)
}
}
}
@ -118,8 +118,8 @@ final class OverlayVideoDecoration: UniversalVideoDecoration {
func updateContentNodeSnapshot(_ snapshot: UIView?) {
}
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayoutSize = size
func updateLayout(size: CGSize, actualSize: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayout = (size, actualSize)
let contentFrame = self.frameForContent(size: size)
@ -146,7 +146,7 @@ final class OverlayVideoDecoration: UniversalVideoDecoration {
if let contentNode = self.contentNode {
transition.updateFrame(node: contentNode, frame: contentFrame)
contentNode.updateLayout(size: contentFrame.size, transition: transition)
contentNode.updateLayout(size: contentFrame.size, actualSize: contentFrame.size, transition: transition)
}
}
@ -209,8 +209,8 @@ final class OverlayVideoDecoration: UniversalVideoDecoration {
if self.minimizedBlurView == nil {
let minimizedBlurView = UIVisualEffectView(effect: nil)
self.minimizedBlurView = minimizedBlurView
if let validLayoutSize = self.validLayoutSize {
minimizedBlurView.frame = CGRect(origin: CGPoint(), size: validLayoutSize)
if let validLayout = self.validLayout {
minimizedBlurView.frame = CGRect(origin: CGPoint(), size: validLayout.size)
}
minimizedBlurView.isHidden = true
self.foregroundContainerNode.view.addSubview(minimizedBlurView)
@ -222,8 +222,8 @@ final class OverlayVideoDecoration: UniversalVideoDecoration {
self.minimizedBlurView?.contentView.addSubview(minimizedArrowView)
}
if let minimizedArrowView = self.minimizedArrowView {
if let validLayoutSize = self.validLayoutSize {
setupArrowFrame(size: validLayoutSize, edge: edge, view: minimizedArrowView)
if let validLayout = self.validLayout {
setupArrowFrame(size: validLayout.size, edge: edge, view: minimizedArrowView)
}
minimizedArrowView.setAngled(!adjusting, animated: true)
}

View File

@ -170,7 +170,7 @@ private final class PlatformVideoContentNode: ASDisplayNode, UniversalVideoConte
private var dimensions: CGSize?
private let dimensionsPromise = ValuePromise<CGSize>(CGSize())
private var validLayout: CGSize?
private var validLayout: (size: CGSize, actualSize: CGSize)?
init(postbox: Postbox, audioSessionManager: ManagedAudioSession, userLocation: MediaResourceUserLocation, content: PlatformVideoContent.Content, streamVideo: Bool, loopVideo: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool) {
self.postbox = postbox
@ -203,8 +203,8 @@ private final class PlatformVideoContentNode: ASDisplayNode, UniversalVideoConte
if let dimensions = getSize() {
strongSelf.dimensions = dimensions
strongSelf.dimensionsPromise.set(dimensions)
if let size = strongSelf.validLayout {
strongSelf.updateLayout(size: size, transition: .immediate)
if let validLayout = strongSelf.validLayout {
strongSelf.updateLayout(size: validLayout.size, actualSize: validLayout.actualSize, transition: .immediate)
}
}
}
@ -371,7 +371,7 @@ private final class PlatformVideoContentNode: ASDisplayNode, UniversalVideoConte
}
}
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
func updateLayout(size: CGSize, actualSize: CGSize, transition: ContainedViewLayoutTransition) {
transition.updatePosition(node: self.playerNode, position: CGPoint(x: size.width / 2.0, y: size.height / 2.0))
transition.updateTransformScale(node: self.playerNode, scale: size.width / self.intrinsicDimensions.width)

View File

@ -207,7 +207,7 @@ private final class SystemVideoContentNode: ASDisplayNode, UniversalVideoContent
}
}
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
func updateLayout(size: CGSize, actualSize: CGSize, transition: ContainedViewLayoutTransition) {
transition.updatePosition(node: self.playerNode, position: CGPoint(x: size.width / 2.0, y: size.height / 2.0))
transition.updateTransformScale(node: self.playerNode, scale: size.width / self.intrinsicDimensions.width)

View File

@ -116,7 +116,7 @@ final class WebEmbedVideoContentNode: ASDisplayNode, UniversalVideoContentNode {
self.readyDisposable.dispose()
}
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
func updateLayout(size: CGSize, actualSize: CGSize, transition: ContainedViewLayoutTransition) {
transition.updatePosition(node: self.playerNode, position: CGPoint(x: size.width / 2.0, y: size.height / 2.0))
transition.updateTransformScale(node: self.playerNode, scale: size.width / self.intrinsicDimensions.width)

View File

@ -336,6 +336,10 @@ public final class ExternalMediaStreamingContext: SharedHLSServerSource {
impl.fileData(id: id, range: range).start(next: subscriber.putNext)
}
}
public func arbitraryFileData(path: String) -> Signal<(data: Data, contentType: String)?, NoError> {
return .single(nil)
}
}
public protocol SharedHLSServerSource: AnyObject {
@ -345,6 +349,7 @@ public protocol SharedHLSServerSource: AnyObject {
func playlistData(quality: Int) -> Signal<String, NoError>
func partData(index: Int, quality: Int) -> Signal<Data?, NoError>
func fileData(id: Int64, range: Range<Int>) -> Signal<(TempBoxFile, Range<Int>, Int)?, NoError>
func arbitraryFileData(path: String) -> Signal<(data: Data, contentType: String)?, NoError>
}
@available(iOS 12.0, macOS 14.0, *)
@ -651,7 +656,19 @@ public final class SharedHLSServer {
}
})
} else {
self.sendErrorAndClose(connection: connection, error: .notFound)
let _ = (source.arbitraryFileData(path: filePath)
|> deliverOn(self.queue)
|> take(1)).start(next: { [weak self] result in
guard let self else {
return
}
if let result {
self.sendResponseAndClose(connection: connection, data: result.data, contentType: result.contentType)
} else {
self.sendErrorAndClose(connection: connection, error: .notFound)
}
})
}
}
@ -665,13 +682,14 @@ public final class SharedHLSServer {
})
}
private func sendResponseAndClose(connection: NWConnection, data: Data, range: Range<Int>? = nil, totalSize: Int? = nil) {
private func sendResponseAndClose(connection: NWConnection, data: Data, contentType: String = "application/octet-stream", range: Range<Int>? = nil, totalSize: Int? = nil) {
var responseHeaders = "HTTP/1.1 200 OK\r\n"
responseHeaders.append("Content-Length: \(data.count)\r\n")
if let range, let totalSize {
responseHeaders.append("Content-Range: bytes \(range.lowerBound)-\(range.upperBound)/\(totalSize)\r\n")
}
responseHeaders.append("Content-Type: application/octet-stream\r\n")
responseHeaders.append("Content-Type: \(contentType)\r\n")
responseHeaders.append("Connection: close\r\n")
responseHeaders.append("Access-Control-Allow-Origin: *\r\n")
responseHeaders.append("\r\n")