mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Video improvements
This commit is contained in:
parent
d45e58e257
commit
43f028559d
@ -37,6 +37,7 @@ public protocol UniversalVideoContentNode: AnyObject {
|
|||||||
func setBaseRate(_ baseRate: Double)
|
func setBaseRate(_ baseRate: Double)
|
||||||
func setVideoQuality(_ videoQuality: UniversalVideoContentVideoQuality)
|
func setVideoQuality(_ videoQuality: UniversalVideoContentVideoQuality)
|
||||||
func videoQualityState() -> (current: Int, preferred: UniversalVideoContentVideoQuality, available: [Int])?
|
func videoQualityState() -> (current: Int, preferred: UniversalVideoContentVideoQuality, available: [Int])?
|
||||||
|
func videoQualityStateSignal() -> Signal<(current: Int, preferred: UniversalVideoContentVideoQuality, available: [Int])?, NoError>
|
||||||
func addPlaybackCompleted(_ f: @escaping () -> Void) -> Int
|
func addPlaybackCompleted(_ f: @escaping () -> Void) -> Int
|
||||||
func removePlaybackCompleted(_ index: Int)
|
func removePlaybackCompleted(_ index: Int)
|
||||||
func fetchControl(_ control: UniversalVideoNodeFetchControl)
|
func fetchControl(_ control: UniversalVideoNodeFetchControl)
|
||||||
@ -367,6 +368,16 @@ public final class UniversalVideoNode: ASDisplayNode {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func videoQualityStateSignal() -> Signal<(current: Int, preferred: UniversalVideoContentVideoQuality, available: [Int])?, NoError> {
|
||||||
|
var result: Signal<(current: Int, preferred: UniversalVideoContentVideoQuality, available: [Int])?, NoError>?
|
||||||
|
self.manager.withUniversalVideoContent(id: self.content.id, { contentNode in
|
||||||
|
if let contentNode {
|
||||||
|
result = contentNode.videoQualityStateSignal()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return result ?? .single(nil)
|
||||||
|
}
|
||||||
|
|
||||||
public func continuePlayingWithoutSound(actionAtEnd: MediaPlayerPlayOnceWithSoundActionAtEnd = .loopDisablingSound) {
|
public func continuePlayingWithoutSound(actionAtEnd: MediaPlayerPlayOnceWithSoundActionAtEnd = .loopDisablingSound) {
|
||||||
self.manager.withUniversalVideoContent(id: self.content.id, { contentNode in
|
self.manager.withUniversalVideoContent(id: self.content.id, { contentNode in
|
||||||
if let contentNode = contentNode {
|
if let contentNode = contentNode {
|
||||||
|
@ -678,10 +678,11 @@ final class SettingsHeaderButton: HighlightableButtonNode {
|
|||||||
} else if let speedBadge = self.speedBadge {
|
} else if let speedBadge = self.speedBadge {
|
||||||
self.speedBadge = nil
|
self.speedBadge = nil
|
||||||
if let speedBadgeView = speedBadge.view {
|
if let speedBadgeView = speedBadge.view {
|
||||||
transition.setAlpha(layer: speedBadgeView.layer, alpha: 0.0, completion: { [weak speedBadgeView] _ in
|
let speedBadgeLayer = speedBadgeView.layer
|
||||||
speedBadgeView?.layer.removeFromSuperlayer()
|
transition.setAlpha(layer: speedBadgeLayer, alpha: 0.0, completion: { [weak speedBadgeLayer] _ in
|
||||||
|
speedBadgeLayer?.removeFromSuperlayer()
|
||||||
})
|
})
|
||||||
transition.setScale(layer: speedBadgeView.layer, scale: 0.001)
|
transition.setScale(layer: speedBadgeLayer, scale: 0.001)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -719,10 +720,11 @@ final class SettingsHeaderButton: HighlightableButtonNode {
|
|||||||
} else if let qualityBadge = self.qualityBadge {
|
} else if let qualityBadge = self.qualityBadge {
|
||||||
self.qualityBadge = nil
|
self.qualityBadge = nil
|
||||||
if let qualityBadgeView = qualityBadge.view {
|
if let qualityBadgeView = qualityBadge.view {
|
||||||
transition.setAlpha(layer: qualityBadgeView.layer, alpha: 0.0, completion: { [weak qualityBadgeView] _ in
|
let qualityBadgeLayer = qualityBadgeView.layer
|
||||||
qualityBadgeView?.layer.removeFromSuperlayer()
|
transition.setAlpha(layer: qualityBadgeLayer, alpha: 0.0, completion: { [weak qualityBadgeLayer] _ in
|
||||||
|
qualityBadgeLayer?.removeFromSuperlayer()
|
||||||
})
|
})
|
||||||
transition.setScale(layer: qualityBadgeView.layer, scale: 0.001)
|
transition.setScale(layer: qualityBadgeLayer, scale: 0.001)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1888,7 +1890,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var rateString: String?
|
var rateString: String?
|
||||||
if abs(playbackRate - 1.0) > 0.1 {
|
if abs(playbackRate - 1.0) > 0.05 {
|
||||||
var stringValue = String(format: "%.1fx", playbackRate)
|
var stringValue = String(format: "%.1fx", playbackRate)
|
||||||
if stringValue.hasSuffix(".0x") {
|
if stringValue.hasSuffix(".0x") {
|
||||||
stringValue = stringValue.replacingOccurrences(of: ".0x", with: "x")
|
stringValue = stringValue.replacingOccurrences(of: ".0x", with: "x")
|
||||||
@ -3284,9 +3286,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
|
|
||||||
let contextController = ContextController(presentationData: self.presentationData.withUpdated(theme: defaultDarkColorPresentationTheme), source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: items |> map { items in
|
let contextController = ContextController(presentationData: self.presentationData.withUpdated(theme: defaultDarkColorPresentationTheme), source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: items |> map { items in
|
||||||
if !items.topItems.isEmpty {
|
if !items.topItems.isEmpty {
|
||||||
return ContextController.Items(content: .twoLists(items.items, items.topItems))
|
return ContextController.Items(id: AnyHashable(0), content: .twoLists(items.items, items.topItems))
|
||||||
} else {
|
} else {
|
||||||
return ContextController.Items(content: .list(items.items))
|
return ContextController.Items(id: AnyHashable(0), content: .list(items.items))
|
||||||
}
|
}
|
||||||
}, gesture: gesture)
|
}, gesture: gesture)
|
||||||
if isSettings {
|
if isSettings {
|
||||||
@ -3459,9 +3461,12 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
peer = .single(nil)
|
peer = .single(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return combineLatest(queue: Queue.mainQueue(), videoNode.status, peer)
|
return combineLatest(queue: Queue.mainQueue(),
|
||||||
|> take(1)
|
videoNode.status |> take(1),
|
||||||
|> map { [weak self] status, peer -> (items: [ContextMenuItem], topItems: [ContextMenuItem]) in
|
peer,
|
||||||
|
videoNode.videoQualityStateSignal()
|
||||||
|
)
|
||||||
|
|> map { [weak self] status, peer, videoQualityState -> (items: [ContextMenuItem], topItems: [ContextMenuItem]) in
|
||||||
guard let status = status, let strongSelf = self else {
|
guard let status = status, let strongSelf = self else {
|
||||||
return ([], [])
|
return ([], [])
|
||||||
}
|
}
|
||||||
@ -3483,7 +3488,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
sliderValuePromise.set(newValue)
|
sliderValuePromise.set(newValue)
|
||||||
}), true))
|
}), true))
|
||||||
|
|
||||||
if let videoQualityState = strongSelf.videoNode?.videoQualityState(), !videoQualityState.available.isEmpty {
|
if let videoQualityState, !videoQualityState.available.isEmpty {
|
||||||
} else {
|
} else {
|
||||||
items.append(.custom(SectionTitleContextItem(text: strongSelf.presentationData.strings.Gallery_VideoSettings_SpeedSectionTitle), false))
|
items.append(.custom(SectionTitleContextItem(text: strongSelf.presentationData.strings.Gallery_VideoSettings_SpeedSectionTitle), false))
|
||||||
for (text, _, rate) in strongSelf.speedList(strings: strongSelf.presentationData.strings) {
|
for (text, _, rate) in strongSelf.speedList(strings: strongSelf.presentationData.strings) {
|
||||||
@ -3510,7 +3515,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let videoQualityState = strongSelf.videoNode?.videoQualityState(), !videoQualityState.available.isEmpty {
|
if let videoQualityState, !videoQualityState.available.isEmpty {
|
||||||
items.append(.custom(SectionTitleContextItem(text: strongSelf.presentationData.strings.Gallery_VideoSettings_QualitySectionTitle), false))
|
items.append(.custom(SectionTitleContextItem(text: strongSelf.presentationData.strings.Gallery_VideoSettings_QualitySectionTitle), false))
|
||||||
|
|
||||||
do {
|
do {
|
||||||
@ -3522,7 +3527,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
} else {
|
} else {
|
||||||
textLayout = .singleLine
|
textLayout = .singleLine
|
||||||
}
|
}
|
||||||
items.append(.action(ContextMenuActionItem(text: qualityText, textLayout: textLayout, icon: { _ in
|
items.append(.action(ContextMenuActionItem(id: AnyHashable("q"), text: qualityText, textLayout: textLayout, icon: { _ in
|
||||||
if isSelected {
|
if isSelected {
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: .white)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: .white)
|
||||||
} else {
|
} else {
|
||||||
@ -3536,10 +3541,6 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
}
|
}
|
||||||
videoNode.setVideoQuality(.auto)
|
videoNode.setVideoQuality(.auto)
|
||||||
self.videoQualityPromise.set(.auto)
|
self.videoQualityPromise.set(.auto)
|
||||||
|
|
||||||
/*if let controller = strongSelf.galleryController() as? GalleryController {
|
|
||||||
controller.updateSharedPlaybackRate(rate)
|
|
||||||
}*/
|
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,7 +261,7 @@ public final class RasterizedCompositionMonochromeLayer: SimpleLayer {
|
|||||||
override public init() {
|
override public init() {
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.maskedLayer.isHidden = true
|
self.maskedLayer.opacity = 0.0
|
||||||
self.addSublayer(self.maskedLayer)
|
self.addSublayer(self.maskedLayer)
|
||||||
|
|
||||||
self.maskedLayer.mask = self.contentsLayer
|
self.maskedLayer.mask = self.contentsLayer
|
||||||
@ -377,9 +377,9 @@ public final class RasterizedCompositionMonochromeLayer: SimpleLayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func updateRasterizationMode() {
|
private func updateRasterizationMode() {
|
||||||
self.maskedLayer.isHidden = !self.contentsLayer.hasAnimationsInTree
|
self.maskedLayer.opacity = self.contentsLayer.hasAnimationsInTree ? 1.0 : 0.0
|
||||||
if self.rasterizedLayer.isHidden != (!self.maskedLayer.isHidden) {
|
if self.rasterizedLayer.isHidden != (self.maskedLayer.opacity != 0.0) {
|
||||||
self.rasterizedLayer.isHidden = (!self.maskedLayer.isHidden)
|
self.rasterizedLayer.isHidden = self.maskedLayer.opacity != 0.0
|
||||||
if !self.rasterizedLayer.isHidden {
|
if !self.rasterizedLayer.isHidden {
|
||||||
self.updateContents()
|
self.updateContents()
|
||||||
}
|
}
|
||||||
|
@ -978,6 +978,18 @@ final class HLSVideoJSNativeContentNode: ASDisplayNode, UniversalVideoContentNod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct VideoQualityState: Equatable {
|
||||||
|
var current: Int
|
||||||
|
var preferred: UniversalVideoContentVideoQuality
|
||||||
|
var available: [Int]
|
||||||
|
|
||||||
|
init(current: Int, preferred: UniversalVideoContentVideoQuality, available: [Int]) {
|
||||||
|
self.current = current
|
||||||
|
self.preferred = preferred
|
||||||
|
self.available = available
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fileprivate static var sharedBandwidthEstimate: Double?
|
fileprivate static var sharedBandwidthEstimate: Double?
|
||||||
|
|
||||||
private let postbox: Postbox
|
private let postbox: Postbox
|
||||||
@ -1047,9 +1059,13 @@ final class HLSVideoJSNativeContentNode: ASDisplayNode, UniversalVideoContentNod
|
|||||||
fileprivate var playerRate: Double = 0.0
|
fileprivate var playerRate: Double = 0.0
|
||||||
fileprivate var playerDefaultRate: Double = 1.0
|
fileprivate var playerDefaultRate: Double = 1.0
|
||||||
fileprivate var playerTime: Double = 0.0
|
fileprivate var playerTime: Double = 0.0
|
||||||
|
|
||||||
fileprivate var playerAvailableLevels: [Int: Level] = [:]
|
fileprivate var playerAvailableLevels: [Int: Level] = [:]
|
||||||
fileprivate var playerCurrentLevelIndex: Int?
|
fileprivate var playerCurrentLevelIndex: Int?
|
||||||
|
|
||||||
|
private var videoQualityStateValue: VideoQualityState?
|
||||||
|
private let videoQualityStatePromise = Promise<VideoQualityState?>(nil)
|
||||||
|
|
||||||
private var hasRequestedPlayerLoad: Bool = false
|
private var hasRequestedPlayerLoad: Bool = false
|
||||||
|
|
||||||
private var requestedBaseRate: Double = 1.0
|
private var requestedBaseRate: Double = 1.0
|
||||||
@ -1265,6 +1281,8 @@ final class HLSVideoJSNativeContentNode: ASDisplayNode, UniversalVideoContentNod
|
|||||||
self.playerCurrentLevelIndex = nil
|
self.playerCurrentLevelIndex = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.updateVideoQualityState()
|
||||||
|
|
||||||
if self.playerIsReady {
|
if self.playerIsReady {
|
||||||
if !self.hasRequestedPlayerLoad {
|
if !self.hasRequestedPlayerLoad {
|
||||||
if !self.playerAvailableLevels.isEmpty {
|
if !self.playerAvailableLevels.isEmpty {
|
||||||
@ -1566,11 +1584,24 @@ final class HLSVideoJSNativeContentNode: ASDisplayNode, UniversalVideoContentNod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.updateVideoQualityState()
|
||||||
|
|
||||||
if self.playerIsReady {
|
if self.playerIsReady {
|
||||||
SharedHLSVideoWebView.shared.webView?.evaluateJavaScript("window.hlsPlayer_instances[\(self.instanceId)].playerSetLevel(\(self.requestedLevelIndex ?? -1));", completionHandler: nil)
|
SharedHLSVideoWebView.shared.webView?.evaluateJavaScript("window.hlsPlayer_instances[\(self.instanceId)].playerSetLevel(\(self.requestedLevelIndex ?? -1));", completionHandler: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func updateVideoQualityState() {
|
||||||
|
var videoQualityState: VideoQualityState?
|
||||||
|
if let value = self.videoQualityState() {
|
||||||
|
videoQualityState = VideoQualityState(current: value.current, preferred: value.preferred, available: value.available)
|
||||||
|
}
|
||||||
|
if self.videoQualityStateValue != videoQualityState {
|
||||||
|
self.videoQualityStateValue = videoQualityState
|
||||||
|
self.videoQualityStatePromise.set(.single(videoQualityState))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func videoQualityState() -> (current: Int, preferred: UniversalVideoContentVideoQuality, available: [Int])? {
|
func videoQualityState() -> (current: Int, preferred: UniversalVideoContentVideoQuality, available: [Int])? {
|
||||||
if self.playerAvailableLevels.isEmpty {
|
if self.playerAvailableLevels.isEmpty {
|
||||||
if let qualitySet = HLSQualitySet(baseFile: self.fileReference), let minQualityFile = HLSVideoContent.minimizedHLSQuality(file: self.fileReference)?.file {
|
if let qualitySet = HLSQualitySet(baseFile: self.fileReference), let minQualityFile = HLSVideoContent.minimizedHLSQuality(file: self.fileReference)?.file {
|
||||||
@ -1594,6 +1625,16 @@ final class HLSVideoJSNativeContentNode: ASDisplayNode, UniversalVideoContentNod
|
|||||||
return (min(currentLevel.width, currentLevel.height), self.preferredVideoQuality, available)
|
return (min(currentLevel.width, currentLevel.height), self.preferredVideoQuality, available)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func videoQualityStateSignal() -> Signal<(current: Int, preferred: UniversalVideoContentVideoQuality, available: [Int])?, NoError> {
|
||||||
|
return self.videoQualityStatePromise.get()
|
||||||
|
|> map { value -> (current: Int, preferred: UniversalVideoContentVideoQuality, available: [Int])? in
|
||||||
|
guard let value else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return (value.current, value.preferred, value.available)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func addPlaybackCompleted(_ f: @escaping () -> Void) -> Int {
|
func addPlaybackCompleted(_ f: @escaping () -> Void) -> Int {
|
||||||
return self.playbackCompletedListeners.add(f)
|
return self.playbackCompletedListeners.add(f)
|
||||||
}
|
}
|
||||||
|
@ -667,6 +667,10 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func videoQualityStateSignal() -> Signal<(current: Int, preferred: UniversalVideoContentVideoQuality, available: [Int])?, NoError> {
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
|
||||||
func continuePlayingWithoutSound(actionAtEnd: MediaPlayerPlayOnceWithSoundActionAtEnd) {
|
func continuePlayingWithoutSound(actionAtEnd: MediaPlayerPlayOnceWithSoundActionAtEnd) {
|
||||||
assert(Queue.mainQueue().isCurrent())
|
assert(Queue.mainQueue().isCurrent())
|
||||||
let action = { [weak self] in
|
let action = { [weak self] in
|
||||||
|
@ -459,6 +459,10 @@ private final class PlatformVideoContentNode: ASDisplayNode, UniversalVideoConte
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func videoQualityStateSignal() -> Signal<(current: Int, preferred: UniversalVideoContentVideoQuality, available: [Int])?, NoError> {
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
|
||||||
func addPlaybackCompleted(_ f: @escaping () -> Void) -> Int {
|
func addPlaybackCompleted(_ f: @escaping () -> Void) -> Int {
|
||||||
return self.playbackCompletedListeners.add(f)
|
return self.playbackCompletedListeners.add(f)
|
||||||
}
|
}
|
||||||
|
@ -296,6 +296,10 @@ private final class SystemVideoContentNode: ASDisplayNode, UniversalVideoContent
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func videoQualityStateSignal() -> Signal<(current: Int, preferred: UniversalVideoContentVideoQuality, available: [Int])?, NoError> {
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
|
||||||
func addPlaybackCompleted(_ f: @escaping () -> Void) -> Int {
|
func addPlaybackCompleted(_ f: @escaping () -> Void) -> Int {
|
||||||
return self.playbackCompletedListeners.add(f)
|
return self.playbackCompletedListeners.add(f)
|
||||||
}
|
}
|
||||||
|
@ -194,6 +194,10 @@ final class WebEmbedVideoContentNode: ASDisplayNode, UniversalVideoContentNode {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func videoQualityStateSignal() -> Signal<(current: Int, preferred: UniversalVideoContentVideoQuality, available: [Int])?, NoError> {
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
|
||||||
func addPlaybackCompleted(_ f: @escaping () -> Void) -> Int {
|
func addPlaybackCompleted(_ f: @escaping () -> Void) -> Int {
|
||||||
return self.playbackCompletedListeners.add(f)
|
return self.playbackCompletedListeners.add(f)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user