mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-24 07:05:35 +00:00
Video player improvements
This commit is contained in:
@@ -513,6 +513,11 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
||||
public var updateMessageReaction: ((Message, ChatControllerInteractionReaction, Bool, ContextExtractedContentContainingView?) -> Void)?
|
||||
public var playMessageEffect: ((Message) -> Void)?
|
||||
public var activateAgeRestrictedMedia: (() -> Void)?
|
||||
public var requestInlineUpdate: (() -> Void)?
|
||||
|
||||
private var hlsInlinePlaybackRange: Range<Int64>?
|
||||
private var appliedHlsInlinePlaybackRange: Range<Int64>?
|
||||
private var hlsInlinePlaybackRangeDisposable: Disposable?
|
||||
|
||||
override public init() {
|
||||
self.pinchContainerNode = PinchSourceContainerNode()
|
||||
@@ -618,6 +623,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
||||
self.playerStatusDisposable.dispose()
|
||||
self.fetchDisposable.dispose()
|
||||
self.secretTimer?.invalidate()
|
||||
self.hlsInlinePlaybackRangeDisposable?.dispose()
|
||||
}
|
||||
|
||||
public func isAvailableForGalleryTransition() -> Bool {
|
||||
@@ -779,6 +785,9 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
||||
let currentAutomaticDownload = self.automaticDownload
|
||||
let currentAutomaticPlayback = self.automaticPlayback
|
||||
|
||||
let hlsInlinePlaybackRange = self.hlsInlinePlaybackRange
|
||||
let appliedHlsInlinePlaybackRange = self.appliedHlsInlinePlaybackRange
|
||||
|
||||
return { [weak self] context, presentationData, dateTimeFormat, message, associatedData, attributes, media, mediaIndex, dateAndStatus, automaticDownload, peerType, peerId, sizeCalculation, layoutConstants, contentMode, presentationContext in
|
||||
let _ = peerType
|
||||
|
||||
@@ -1085,6 +1094,9 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
||||
} else {
|
||||
mediaUpdated = true
|
||||
}
|
||||
if hlsInlinePlaybackRange != appliedHlsInlinePlaybackRange {
|
||||
mediaUpdated = true
|
||||
}
|
||||
|
||||
var isSendingUpdated = false
|
||||
if let currentMessage = currentMessage {
|
||||
@@ -1107,6 +1119,8 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
||||
var updateAnimatedStickerFile: TelegramMediaFile?
|
||||
var onlyFullSizeVideoThumbnail: Bool?
|
||||
|
||||
var loadHLSRangeVideoFile: TelegramMediaFile?
|
||||
|
||||
var emptyColor: UIColor
|
||||
var patternArguments: PatternWallpaperArguments?
|
||||
if isSticker {
|
||||
@@ -1216,7 +1230,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
||||
messageMediaImageCancelInteractiveFetch(context: context, messageId: message.id, image: image, resource: resource)
|
||||
}
|
||||
})
|
||||
} else if let file = media as? TelegramMediaFile {
|
||||
} else if var file = media as? TelegramMediaFile {
|
||||
if isSecretMedia {
|
||||
updateImageSignal = { synchronousLoad, _ in
|
||||
return chatSecretMessageVideo(account: context.account, userLocation: .peer(message.id.peerId), videoReference: .message(message: MessageReference(message), media: file))
|
||||
@@ -1248,20 +1262,34 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
||||
}
|
||||
|
||||
if file.isVideo && !file.isVideoSticker && !isSecretMedia && automaticPlayback && !uploading {
|
||||
updateVideoFile = file
|
||||
if hasCurrentVideoNode {
|
||||
if let currentFile = currentMedia as? TelegramMediaFile {
|
||||
if currentFile.resource is EmptyMediaResource {
|
||||
replaceVideoNode = true
|
||||
} else if currentFile.fileId.namespace == Namespaces.Media.CloudFile && file.fileId.namespace == Namespaces.Media.CloudFile && currentFile.fileId != file.fileId {
|
||||
replaceVideoNode = true
|
||||
} else if currentFile.fileId != file.fileId && file.fileId.namespace == Namespaces.Media.CloudSecretFile {
|
||||
replaceVideoNode = true
|
||||
} else if file.isAnimated && currentFile.fileId.namespace == Namespaces.Media.LocalFile && file.fileId.namespace == Namespaces.Media.CloudFile {
|
||||
replaceVideoNode = true
|
||||
}
|
||||
loadHLSRangeVideoFile = file
|
||||
|
||||
var passFile = true
|
||||
if NativeVideoContent.isHLSVideo(file: file), let minimizedQualityFile = HLSVideoContent.minimizedHLSQuality(file: .message(message: MessageReference(message), media: file)) {
|
||||
file = minimizedQualityFile.file.media
|
||||
if hlsInlinePlaybackRange == nil {
|
||||
passFile = false
|
||||
}
|
||||
} else if !(file.resource is LocalFileVideoMediaResource) {
|
||||
}
|
||||
|
||||
if passFile {
|
||||
updateVideoFile = file
|
||||
if hasCurrentVideoNode {
|
||||
if let currentFile = currentMedia as? TelegramMediaFile {
|
||||
if currentFile.resource is EmptyMediaResource {
|
||||
replaceVideoNode = true
|
||||
} else if currentFile.fileId.namespace == Namespaces.Media.CloudFile && file.fileId.namespace == Namespaces.Media.CloudFile && currentFile.fileId != file.fileId {
|
||||
replaceVideoNode = true
|
||||
} else if currentFile.fileId != file.fileId && file.fileId.namespace == Namespaces.Media.CloudSecretFile {
|
||||
replaceVideoNode = true
|
||||
} else if file.isAnimated && currentFile.fileId.namespace == Namespaces.Media.LocalFile && file.fileId.namespace == Namespaces.Media.CloudFile {
|
||||
replaceVideoNode = true
|
||||
}
|
||||
}
|
||||
} else if !(file.resource is LocalFileVideoMediaResource) {
|
||||
replaceVideoNode = true
|
||||
}
|
||||
} else if hasCurrentVideoNode {
|
||||
replaceVideoNode = true
|
||||
}
|
||||
} else {
|
||||
@@ -1352,7 +1380,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
||||
}, cancel: {
|
||||
chatMessageWebFileCancelInteractiveFetch(account: context.account, image: image)
|
||||
})
|
||||
} else if let file = media as? TelegramMediaFile {
|
||||
} else if var file = media as? TelegramMediaFile {
|
||||
if isSecretMedia {
|
||||
updateImageSignal = { synchronousLoad, _ in
|
||||
return chatSecretMessageVideo(account: context.account, userLocation: .peer(message.id.peerId), videoReference: .message(message: MessageReference(message), media: file))
|
||||
@@ -1384,20 +1412,34 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
||||
}
|
||||
|
||||
if file.isVideo && !file.isVideoSticker && !isSecretMedia && automaticPlayback && !uploading {
|
||||
updateVideoFile = file
|
||||
if hasCurrentVideoNode {
|
||||
if let currentFile = currentMedia as? TelegramMediaFile {
|
||||
if currentFile.resource is EmptyMediaResource {
|
||||
replaceVideoNode = true
|
||||
} else if currentFile.fileId.namespace == Namespaces.Media.CloudFile && file.fileId.namespace == Namespaces.Media.CloudFile && currentFile.fileId != file.fileId {
|
||||
replaceVideoNode = true
|
||||
} else if currentFile.fileId != file.fileId && file.fileId.namespace == Namespaces.Media.CloudSecretFile {
|
||||
replaceVideoNode = true
|
||||
} else if file.isAnimated && currentFile.fileId.namespace == Namespaces.Media.LocalFile && file.fileId.namespace == Namespaces.Media.CloudFile {
|
||||
replaceVideoNode = true
|
||||
}
|
||||
loadHLSRangeVideoFile = file
|
||||
|
||||
var passFile = true
|
||||
if NativeVideoContent.isHLSVideo(file: file), let minimizedQualityFile = HLSVideoContent.minimizedHLSQuality(file: .message(message: MessageReference(message), media: file)) {
|
||||
file = minimizedQualityFile.file.media
|
||||
if hlsInlinePlaybackRange == nil {
|
||||
passFile = false
|
||||
}
|
||||
} else if !(file.resource is LocalFileVideoMediaResource) {
|
||||
}
|
||||
|
||||
if passFile {
|
||||
updateVideoFile = file
|
||||
if hasCurrentVideoNode {
|
||||
if let currentFile = currentMedia as? TelegramMediaFile {
|
||||
if currentFile.resource is EmptyMediaResource {
|
||||
replaceVideoNode = true
|
||||
} else if currentFile.fileId.namespace == Namespaces.Media.CloudFile && file.fileId.namespace == Namespaces.Media.CloudFile && currentFile.fileId != file.fileId {
|
||||
replaceVideoNode = true
|
||||
} else if currentFile.fileId != file.fileId && file.fileId.namespace == Namespaces.Media.CloudSecretFile {
|
||||
replaceVideoNode = true
|
||||
} else if file.isAnimated && currentFile.fileId.namespace == Namespaces.Media.LocalFile && file.fileId.namespace == Namespaces.Media.CloudFile {
|
||||
replaceVideoNode = true
|
||||
}
|
||||
}
|
||||
} else if !(file.resource is LocalFileVideoMediaResource) {
|
||||
replaceVideoNode = true
|
||||
}
|
||||
} else if hasCurrentVideoNode {
|
||||
replaceVideoNode = true
|
||||
}
|
||||
} else {
|
||||
@@ -1661,12 +1703,26 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
||||
let loopVideo = updatedVideoFile.isAnimated
|
||||
|
||||
let videoContent: UniversalVideoContent
|
||||
videoContent = NativeVideoContent(id: .message(message.stableId, updatedVideoFile.fileId), userLocation: .peer(message.id.peerId), fileReference: .message(message: MessageReference(message), media: updatedVideoFile), streamVideo: streamVideo ? .conservative : .none, loopVideo: loopVideo, enableSound: false, fetchAutomatically: false, onlyFullSizeThumbnail: (onlyFullSizeVideoThumbnail ?? false), continuePlayingWithoutSoundOnLostAudioSession: isInlinePlayableVideo, placeholderColor: emptyColor, captureProtected: message.isCopyProtected() || isExtendedMedia, storeAfterDownload: { [weak context] in
|
||||
guard let context, let peerId else {
|
||||
return
|
||||
videoContent = NativeVideoContent(
|
||||
id: .message(message.stableId, updatedVideoFile.fileId),
|
||||
userLocation: .peer(message.id.peerId),
|
||||
fileReference: .message(message: MessageReference(message), media: updatedVideoFile),
|
||||
limitedFileRange: hlsInlinePlaybackRange,
|
||||
streamVideo: streamVideo ? .conservative : .none,
|
||||
loopVideo: loopVideo,
|
||||
enableSound: false,
|
||||
fetchAutomatically: false,
|
||||
onlyFullSizeThumbnail: (onlyFullSizeVideoThumbnail ?? false),
|
||||
continuePlayingWithoutSoundOnLostAudioSession: isInlinePlayableVideo,
|
||||
placeholderColor: emptyColor,
|
||||
captureProtected: message.isCopyProtected() || isExtendedMedia,
|
||||
storeAfterDownload: { [weak context] in
|
||||
guard let context, let peerId else {
|
||||
return
|
||||
}
|
||||
let _ = storeDownloadedMedia(storeManager: context.downloadedMediaStoreManager, media: .message(message: MessageReference(message), media: updatedVideoFile), peerId: peerId).startStandalone()
|
||||
}
|
||||
let _ = storeDownloadedMedia(storeManager: context.downloadedMediaStoreManager, media: .message(message: MessageReference(message), media: updatedVideoFile), peerId: peerId).startStandalone()
|
||||
})
|
||||
)
|
||||
let videoNode = UniversalVideoNode(accountId: context.account.id, postbox: context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: decoration, content: videoContent, priority: .embedded)
|
||||
videoNode.isUserInteractionEnabled = false
|
||||
videoNode.ownsContentNodeUpdated = { [weak self] owns in
|
||||
@@ -1916,6 +1972,36 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
||||
strongSelf.updateStatus(animated: synchronousLoads)
|
||||
|
||||
strongSelf.pinchContainerNode.isPinchGestureEnabled = !isSecretMedia && !isExtendedMediaPreview && !hasSpoiler
|
||||
|
||||
strongSelf.appliedHlsInlinePlaybackRange = hlsInlinePlaybackRange
|
||||
|
||||
if let loadHLSRangeVideoFile, NativeVideoContent.isHLSVideo(file: loadHLSRangeVideoFile) {
|
||||
if strongSelf.hlsInlinePlaybackRangeDisposable == nil {
|
||||
strongSelf.hlsInlinePlaybackRangeDisposable = (HLSVideoContent.minimizedHLSQualityPreloadData(
|
||||
postbox: context.account.postbox,
|
||||
file: .message(message: MessageReference(message), media: loadHLSRangeVideoFile),
|
||||
userLocation: .peer(message.id.peerId),
|
||||
prefixSeconds: 10,
|
||||
autofetchPlaylist: false
|
||||
)
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak strongSelf] preloadData in
|
||||
guard let strongSelf else {
|
||||
return
|
||||
}
|
||||
if let preloadData {
|
||||
strongSelf.hlsInlinePlaybackRange = preloadData.1
|
||||
} else {
|
||||
strongSelf.hlsInlinePlaybackRange = nil
|
||||
}
|
||||
strongSelf.requestInlineUpdate?()
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if let hlsInlinePlaybackRangeDisposable = strongSelf.hlsInlinePlaybackRangeDisposable {
|
||||
strongSelf.hlsInlinePlaybackRangeDisposable = nil
|
||||
hlsInlinePlaybackRangeDisposable.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -2140,6 +2226,10 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let file = self.media as? TelegramMediaFile, NativeVideoContent.isHLSVideo(file: file) {
|
||||
fetchStatus = .Local
|
||||
}
|
||||
|
||||
let formatting = DataSizeStringFormatting(strings: strings, decimalSeparator: decimalSeparator)
|
||||
|
||||
@@ -2185,15 +2275,10 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
||||
if let duration = file.duration, !message.flags.contains(.Unsent) {
|
||||
let durationString = file.isAnimated ? gifTitle : stringForDuration(playerDuration > 0 ? playerDuration : Int32(duration), position: playerPosition)
|
||||
if isMediaStreamable(message: message, media: file) {
|
||||
if NativeVideoContent.isHLSVideo(file: file) {
|
||||
mediaDownloadState = .fetching(progress: nil)
|
||||
badgeContent = .text(inset: 12.0, backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, text: NSAttributedString(string: durationString), iconName: nil)
|
||||
} else {
|
||||
badgeContent = .mediaDownload(backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, duration: durationString, size: active ? sizeString : nil, muted: muted, active: active)
|
||||
mediaDownloadState = .fetching(progress: automaticPlayback ? nil : adjustedProgress)
|
||||
if self.playerStatus?.status == .playing {
|
||||
mediaDownloadState = nil
|
||||
}
|
||||
badgeContent = .mediaDownload(backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, duration: durationString, size: active ? sizeString : nil, muted: muted, active: active)
|
||||
mediaDownloadState = .fetching(progress: automaticPlayback ? nil : adjustedProgress)
|
||||
if self.playerStatus?.status == .playing {
|
||||
mediaDownloadState = nil
|
||||
}
|
||||
state = automaticPlayback ? .none : .play(messageTheme.mediaOverlayControlColors.foregroundColor)
|
||||
} else {
|
||||
@@ -2292,11 +2377,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
||||
do {
|
||||
let durationString = file.isAnimated ? gifTitle : stringForDuration(playerDuration > 0 ? playerDuration : (file.duration.flatMap { Int32(floor($0)) } ?? 0), position: playerPosition)
|
||||
if wideLayout {
|
||||
if NativeVideoContent.isHLSVideo(file: file) {
|
||||
state = automaticPlayback ? .none : .play(messageTheme.mediaOverlayControlColors.foregroundColor)
|
||||
mediaDownloadState = nil
|
||||
badgeContent = .text(inset: 12.0, backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, text: NSAttributedString(string: durationString), iconName: nil)
|
||||
} else if isMediaStreamable(message: message, media: file), let fileSize = file.size, fileSize > 0 && fileSize != .max {
|
||||
if isMediaStreamable(message: message, media: file), let fileSize = file.size, fileSize > 0 && fileSize != .max {
|
||||
state = automaticPlayback ? .none : .play(messageTheme.mediaOverlayControlColors.foregroundColor)
|
||||
badgeContent = .mediaDownload(backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, duration: durationString, size: dataSizeString(fileSize, formatting: formatting), muted: muted, active: true)
|
||||
mediaDownloadState = .remote
|
||||
@@ -2680,8 +2761,13 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
||||
|
||||
public func playMediaWithSound() -> (action: (Double?) -> Void, soundEnabled: Bool, isVideoMessage: Bool, isUnread: Bool, badgeNode: ASDisplayNode?)? {
|
||||
var isAnimated = false
|
||||
if let file = self.media as? TelegramMediaFile, file.isAnimated {
|
||||
isAnimated = true
|
||||
if let file = self.media as? TelegramMediaFile {
|
||||
if NativeVideoContent.isHLSVideo(file: file) {
|
||||
return nil
|
||||
}
|
||||
if file.isAnimated {
|
||||
isAnimated = true
|
||||
}
|
||||
}
|
||||
|
||||
var actionAtEnd: MediaPlayerPlayOnceWithSoundActionAtEnd = .loopDisablingSound
|
||||
|
||||
Reference in New Issue
Block a user