mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 13:35:19 +00:00
Various improvements
This commit is contained in:
parent
a63a3074bd
commit
383ab9e479
Binary file not shown.
@ -112,7 +112,7 @@ public final class DirectAnimatedStickerNode: ASDisplayNode, AnimatedStickerNode
|
||||
}
|
||||
|
||||
if source.isVideo {
|
||||
if let videoSource = makeVideoStickerDirectFrameSource(queue: DirectAnimatedStickerNode.sharedQueue, path: path, width: width, height: height, cachePathPrefix: nil, unpremultiplyAlpha: false) {
|
||||
if let videoSource = makeVideoStickerDirectFrameSource(queue: DirectAnimatedStickerNode.sharedQueue, path: path, hintVP9: true, width: width, height: height, cachePathPrefix: nil, unpremultiplyAlpha: false) {
|
||||
strongSelf.setupPlayback(videoSource: videoSource)
|
||||
}
|
||||
} else {
|
||||
|
@ -273,8 +273,8 @@ private final class VideoStickerFrameSourceCache {
|
||||
|
||||
private let useCache = true
|
||||
|
||||
public func makeVideoStickerDirectFrameSource(queue: Queue, path: String, width: Int, height: Int, cachePathPrefix: String?, unpremultiplyAlpha: Bool) -> AnimatedStickerFrameSource? {
|
||||
return VideoStickerDirectFrameSource(queue: queue, path: path, width: width, height: height, cachePathPrefix: cachePathPrefix, unpremultiplyAlpha: unpremultiplyAlpha)
|
||||
public func makeVideoStickerDirectFrameSource(queue: Queue, path: String, hintVP9: Bool, width: Int, height: Int, cachePathPrefix: String?, unpremultiplyAlpha: Bool) -> AnimatedStickerFrameSource? {
|
||||
return VideoStickerDirectFrameSource(queue: queue, path: path, isVP9: hintVP9, width: width, height: height, cachePathPrefix: cachePathPrefix, unpremultiplyAlpha: unpremultiplyAlpha)
|
||||
}
|
||||
|
||||
public final class VideoStickerDirectFrameSource: AnimatedStickerFrameSource {
|
||||
@ -290,7 +290,7 @@ public final class VideoStickerDirectFrameSource: AnimatedStickerFrameSource {
|
||||
public var duration: Double
|
||||
fileprivate var currentFrame: Int
|
||||
|
||||
private let source: SoftwareVideoSource?
|
||||
private var source: FFMpegFileReader?
|
||||
|
||||
public var frameIndex: Int {
|
||||
if self.frameCount == 0 {
|
||||
@ -300,7 +300,7 @@ public final class VideoStickerDirectFrameSource: AnimatedStickerFrameSource {
|
||||
}
|
||||
}
|
||||
|
||||
public init?(queue: Queue, path: String, width: Int, height: Int, cachePathPrefix: String?, unpremultiplyAlpha: Bool = true) {
|
||||
public init?(queue: Queue, path: String, isVP9: Bool = true, width: Int, height: Int, cachePathPrefix: String?, unpremultiplyAlpha: Bool = true) {
|
||||
self.queue = queue
|
||||
self.path = path
|
||||
self.width = width
|
||||
@ -329,12 +329,25 @@ public final class VideoStickerDirectFrameSource: AnimatedStickerFrameSource {
|
||||
self.frameCount = 1
|
||||
self.duration = 0.0
|
||||
} else {
|
||||
let source = SoftwareVideoSource(path: path, hintVP9: true, unpremultiplyAlpha: unpremultiplyAlpha)
|
||||
self.source = source
|
||||
let source = FFMpegFileReader(
|
||||
source: .file(path),
|
||||
passthroughDecoder: false,
|
||||
useHardwareAcceleration: false,
|
||||
selectedStream: .mediaType(.video),
|
||||
seek: nil,
|
||||
maxReadablePts: nil
|
||||
)
|
||||
if let source {
|
||||
self.source = source
|
||||
self.frameRate = min(30, source.frameRate())
|
||||
self.duration = source.duration().seconds
|
||||
} else {
|
||||
self.source = nil
|
||||
self.frameRate = 30
|
||||
self.duration = 0.0
|
||||
}
|
||||
self.image = nil
|
||||
self.frameRate = min(30, source.getFramerate())
|
||||
self.frameCount = 0
|
||||
self.duration = source.reportedDuration.seconds
|
||||
}
|
||||
}
|
||||
|
||||
@ -365,56 +378,66 @@ public final class VideoStickerDirectFrameSource: AnimatedStickerFrameSource {
|
||||
} else if useCache, let cache = self.cache, let yuvData = cache.readUncompressedYuvaFrame(index: frameIndex) {
|
||||
return AnimatedStickerFrame(data: yuvData, type: .yuva, width: self.width, height: self.height, bytesPerRow: self.width * 2, index: frameIndex, isLastFrame: frameIndex == self.frameCount - 1, totalFrames: self.frameCount)
|
||||
} else if let source = self.source {
|
||||
let frameAndLoop = source.readFrame(maxPts: nil)
|
||||
if frameAndLoop.0 == nil {
|
||||
if frameAndLoop.3 {
|
||||
if self.frameCount == 0 {
|
||||
if let cache = self.cache {
|
||||
if cache.storedFrames == frameIndex {
|
||||
self.frameCount = frameIndex
|
||||
cache.storeFrameRateAndCount(frameRate: self.frameRate, frameCount: self.frameCount)
|
||||
} else {
|
||||
Logger.shared.log("VideoSticker", "Missed a frame? \(frameIndex) \(cache.storedFrames)")
|
||||
}
|
||||
} else {
|
||||
self.frameCount = frameIndex
|
||||
}
|
||||
let frameAndLoop = source.readFrame(argb: true)
|
||||
switch frameAndLoop {
|
||||
case let .frame(frame):
|
||||
var frameData = Data(count: self.bytesPerRow * self.height)
|
||||
frameData.withUnsafeMutableBytes { buffer -> Void in
|
||||
guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
|
||||
return
|
||||
}
|
||||
self.currentFrame = 0
|
||||
} else {
|
||||
Logger.shared.log("VideoSticker", "Skipped a frame?")
|
||||
|
||||
let imageBuffer = CMSampleBufferGetImageBuffer(frame.sampleBuffer)
|
||||
CVPixelBufferLockBaseAddress(imageBuffer!, CVPixelBufferLockFlags(rawValue: 0))
|
||||
let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer!)
|
||||
let width = CVPixelBufferGetWidth(imageBuffer!)
|
||||
let height = CVPixelBufferGetHeight(imageBuffer!)
|
||||
let srcData = CVPixelBufferGetBaseAddress(imageBuffer!)
|
||||
|
||||
var sourceBuffer = vImage_Buffer(data: srcData, height: vImagePixelCount(height), width: vImagePixelCount(width), rowBytes: bytesPerRow)
|
||||
var destBuffer = vImage_Buffer(data: bytes, height: vImagePixelCount(self.height), width: vImagePixelCount(self.width), rowBytes: self.bytesPerRow)
|
||||
|
||||
let _ = vImageScale_ARGB8888(&sourceBuffer, &destBuffer, nil, vImage_Flags(kvImageDoNotTile))
|
||||
|
||||
CVPixelBufferUnlockBaseAddress(imageBuffer!, CVPixelBufferLockFlags(rawValue: 0))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let frame = frameAndLoop.0 else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var frameData = Data(count: self.bytesPerRow * self.height)
|
||||
frameData.withUnsafeMutableBytes { buffer -> Void in
|
||||
guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
|
||||
return
|
||||
}
|
||||
|
||||
let imageBuffer = CMSampleBufferGetImageBuffer(frame.sampleBuffer)
|
||||
CVPixelBufferLockBaseAddress(imageBuffer!, CVPixelBufferLockFlags(rawValue: 0))
|
||||
let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer!)
|
||||
let width = CVPixelBufferGetWidth(imageBuffer!)
|
||||
let height = CVPixelBufferGetHeight(imageBuffer!)
|
||||
let srcData = CVPixelBufferGetBaseAddress(imageBuffer!)
|
||||
|
||||
var sourceBuffer = vImage_Buffer(data: srcData, height: vImagePixelCount(height), width: vImagePixelCount(width), rowBytes: bytesPerRow)
|
||||
var destBuffer = vImage_Buffer(data: bytes, height: vImagePixelCount(self.height), width: vImagePixelCount(self.width), rowBytes: self.bytesPerRow)
|
||||
|
||||
let _ = vImageScale_ARGB8888(&sourceBuffer, &destBuffer, nil, vImage_Flags(kvImageDoNotTile))
|
||||
|
||||
CVPixelBufferUnlockBaseAddress(imageBuffer!, CVPixelBufferLockFlags(rawValue: 0))
|
||||
}
|
||||
|
||||
self.cache?.storeUncompressedRgbFrame(index: frameIndex, rgbData: frameData)
|
||||
|
||||
return AnimatedStickerFrame(data: frameData, type: .argb, width: self.width, height: self.height, bytesPerRow: self.bytesPerRow, index: frameIndex, isLastFrame: frameIndex == self.frameCount - 1, totalFrames: self.frameCount, multiplyAlpha: true)
|
||||
self.cache?.storeUncompressedRgbFrame(index: frameIndex, rgbData: frameData)
|
||||
|
||||
return AnimatedStickerFrame(data: frameData, type: .argb, width: self.width, height: self.height, bytesPerRow: self.bytesPerRow, index: frameIndex, isLastFrame: frameIndex == self.frameCount - 1, totalFrames: self.frameCount, multiplyAlpha: true)
|
||||
case .endOfStream:
|
||||
if self.frameCount == 0 {
|
||||
if let cache = self.cache {
|
||||
if cache.storedFrames == frameIndex {
|
||||
self.frameCount = frameIndex
|
||||
cache.storeFrameRateAndCount(frameRate: self.frameRate, frameCount: self.frameCount)
|
||||
} else {
|
||||
Logger.shared.log("VideoSticker", "Missed a frame? \(frameIndex) \(cache.storedFrames)")
|
||||
}
|
||||
} else {
|
||||
self.frameCount = frameIndex
|
||||
}
|
||||
}
|
||||
self.currentFrame = 0
|
||||
self.source = FFMpegFileReader(
|
||||
source: .file(self.path),
|
||||
passthroughDecoder: false,
|
||||
useHardwareAcceleration: false,
|
||||
selectedStream: .mediaType(.video),
|
||||
seek: nil,
|
||||
maxReadablePts: nil
|
||||
)
|
||||
|
||||
if let cache = self.cache {
|
||||
if let yuvData = cache.readUncompressedYuvaFrame(index: self.currentFrame) {
|
||||
return AnimatedStickerFrame(data: yuvData, type: .yuva, width: self.width, height: self.height, bytesPerRow: self.width * 2, index: frameIndex, isLastFrame: frameIndex == self.frameCount - 1, totalFrames: self.frameCount)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
case .waitingForMoreData, .error:
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ public final class AvatarVideoNode: ASDisplayNode {
|
||||
|
||||
private var emojiMarkup: TelegramMediaImage.EmojiMarkup?
|
||||
|
||||
private var videoFileDisposable: Disposable?
|
||||
private var fileDisposable = MetaDisposable()
|
||||
private var animationFile: TelegramMediaFile?
|
||||
private var itemLayer: EmojiKeyboardItemLayer?
|
||||
@ -32,6 +33,7 @@ public final class AvatarVideoNode: ASDisplayNode {
|
||||
private var animationNode: AnimatedStickerNode?
|
||||
private let stickerFetchedDisposable = MetaDisposable()
|
||||
|
||||
private var videoItemLayer: EmojiKeyboardItemLayer?
|
||||
private var videoNode: UniversalVideoNode?
|
||||
private var videoContent: NativeVideoContent?
|
||||
private let playbackStartDisposable = MetaDisposable()
|
||||
@ -55,6 +57,7 @@ public final class AvatarVideoNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.videoFileDisposable?.dispose()
|
||||
self.fileDisposable.dispose()
|
||||
self.stickerFetchedDisposable.dispose()
|
||||
self.playbackStartDisposable.dispose()
|
||||
@ -137,6 +140,7 @@ public final class AvatarVideoNode: ASDisplayNode {
|
||||
self.videoLoopCount += 1
|
||||
if self.videoLoopCount >= maxVideoLoopCount {
|
||||
self.itemLayer?.isVisibleForAnimations = false
|
||||
self.videoItemLayer?.isVisibleForAnimations = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -211,6 +215,9 @@ public final class AvatarVideoNode: ASDisplayNode {
|
||||
if videoContent.id != self.videoContent?.id {
|
||||
self.videoNode?.removeFromSupernode()
|
||||
self.videoContent = videoContent
|
||||
|
||||
self.videoFileDisposable?.dispose()
|
||||
self.videoFileDisposable = fetchedMediaResource(mediaBox: self.context.account.postbox.mediaBox, userLocation: .peer(peer.id), userContentType: .avatar, reference: videoFileReference.resourceReference(videoFileReference.media.resource)).startStrict()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -231,56 +238,111 @@ public final class AvatarVideoNode: ASDisplayNode {
|
||||
}
|
||||
self.animationNode?.visibility = isVisible
|
||||
if isVisible, let videoContent = self.videoContent, self.videoLoopCount < maxVideoLoopCount {
|
||||
if self.videoNode == nil {
|
||||
let context = self.context
|
||||
let mediaManager = context.sharedContext.mediaManager
|
||||
let videoNode = UniversalVideoNode(context: context, postbox: context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: VideoDecoration(), content: videoContent, priority: .embedded)
|
||||
videoNode.clipsToBounds = true
|
||||
videoNode.isUserInteractionEnabled = false
|
||||
videoNode.isHidden = true
|
||||
videoNode.playbackCompleted = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.videoLoopCount += 1
|
||||
if strongSelf.videoLoopCount >= maxVideoLoopCount {
|
||||
if let videoNode = strongSelf.videoNode {
|
||||
strongSelf.videoNode = nil
|
||||
videoNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak videoNode] _ in
|
||||
videoNode?.removeFromSupernode()
|
||||
})
|
||||
var useDirectCache = false
|
||||
if self.internalSize.width <= 200.0 {
|
||||
useDirectCache = true
|
||||
}
|
||||
|
||||
if useDirectCache {
|
||||
if self.videoItemLayer == nil {
|
||||
let animationData = EntityKeyboardAnimationData(file: TelegramMediaFile.Accessor(videoContent.fileReference.media))
|
||||
let videoItemLayer = EmojiKeyboardItemLayer(
|
||||
item: EmojiPagerContentComponent.Item(
|
||||
animationData: animationData,
|
||||
content: .animation(animationData),
|
||||
itemFile: TelegramMediaFile.Accessor(videoContent.fileReference.media),
|
||||
subgroupId: nil,
|
||||
icon: .none,
|
||||
tintMode: .none
|
||||
),
|
||||
context: self.context,
|
||||
attemptSynchronousLoad: false,
|
||||
content: .animation(animationData),
|
||||
cache: self.context.animationCache,
|
||||
renderer: self.context.animationRenderer,
|
||||
placeholderColor: .clear,
|
||||
blurredBadgeColor: .clear,
|
||||
accentIconColor: .white,
|
||||
pointSize: self.internalSize,
|
||||
onUpdateDisplayPlaceholder: { _, _ in
|
||||
}
|
||||
)
|
||||
videoItemLayer.onLoop = { [weak self] in
|
||||
if let self {
|
||||
self.videoLoopCount += 1
|
||||
if self.videoLoopCount >= maxVideoLoopCount {
|
||||
self.itemLayer?.isVisibleForAnimations = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.videoItemLayer = videoItemLayer
|
||||
self.layer.addSublayer(videoItemLayer)
|
||||
}
|
||||
|
||||
if let _ = videoContent.startTimestamp {
|
||||
self.playbackStartDisposable.set((videoNode.status
|
||||
|> map { status -> Bool in
|
||||
if let status = status, case .playing = status.status {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|> filter { playing in
|
||||
return playing
|
||||
}
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).startStrict(completed: { [weak self] in
|
||||
} else {
|
||||
if let videoItemLayer = self.videoItemLayer {
|
||||
self.videoItemLayer = nil
|
||||
videoItemLayer.removeFromSuperlayer()
|
||||
}
|
||||
}
|
||||
|
||||
if useDirectCache {
|
||||
if let videoNode = self.videoNode {
|
||||
self.videoNode = nil
|
||||
videoNode.removeFromSupernode()
|
||||
}
|
||||
} else {
|
||||
if self.videoNode == nil {
|
||||
let context = self.context
|
||||
let mediaManager = context.sharedContext.mediaManager
|
||||
let videoNode = UniversalVideoNode(context: context, postbox: context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: VideoDecoration(), content: videoContent, priority: .embedded)
|
||||
videoNode.clipsToBounds = true
|
||||
videoNode.isUserInteractionEnabled = false
|
||||
videoNode.isHidden = true
|
||||
videoNode.playbackCompleted = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
Queue.mainQueue().after(0.15) {
|
||||
strongSelf.videoNode?.isHidden = false
|
||||
strongSelf.videoLoopCount += 1
|
||||
if strongSelf.videoLoopCount >= maxVideoLoopCount {
|
||||
if let videoNode = strongSelf.videoNode {
|
||||
strongSelf.videoNode = nil
|
||||
videoNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak videoNode] _ in
|
||||
videoNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
self.playbackStartDisposable.set(nil)
|
||||
videoNode.isHidden = false
|
||||
}
|
||||
|
||||
if let _ = videoContent.startTimestamp {
|
||||
self.playbackStartDisposable.set((videoNode.status
|
||||
|> map { status -> Bool in
|
||||
if let status = status, case .playing = status.status {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|> filter { playing in
|
||||
return playing
|
||||
}
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).startStrict(completed: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
Queue.mainQueue().after(0.15) {
|
||||
strongSelf.videoNode?.isHidden = false
|
||||
}
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
self.playbackStartDisposable.set(nil)
|
||||
videoNode.isHidden = false
|
||||
}
|
||||
videoNode.canAttachContent = true
|
||||
videoNode.play()
|
||||
|
||||
self.addSubnode(videoNode)
|
||||
self.videoNode = videoNode
|
||||
}
|
||||
videoNode.canAttachContent = true
|
||||
videoNode.play()
|
||||
|
||||
self.addSubnode(videoNode)
|
||||
self.videoNode = videoNode
|
||||
}
|
||||
} else if let videoNode = self.videoNode {
|
||||
self.videoNode = nil
|
||||
@ -289,6 +351,7 @@ public final class AvatarVideoNode: ASDisplayNode {
|
||||
if self.videoLoopCount < maxVideoLoopCount {
|
||||
self.itemLayer?.isVisibleForAnimations = isVisible
|
||||
}
|
||||
self.videoItemLayer?.isVisibleForAnimations = isVisible
|
||||
}
|
||||
|
||||
public func updateLayout(size: CGSize, cornerRadius: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
@ -301,6 +364,9 @@ public final class AvatarVideoNode: ASDisplayNode {
|
||||
videoNode.frame = CGRect(origin: .zero, size: size)
|
||||
videoNode.updateLayout(size: size, transition: transition)
|
||||
}
|
||||
if let videoItemLayer = self.videoItemLayer {
|
||||
videoItemLayer.frame = CGRect(origin: .zero, size: size)
|
||||
}
|
||||
|
||||
let itemSize = CGSize(width: size.width * 0.67, height: size.height * 0.67)
|
||||
let itemFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - itemSize.width) / 2.0), y: floorToScreenPixels((size.height - itemSize.height) / 2.0)), size: itemSize)
|
||||
|
@ -1747,7 +1747,11 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
self.avatarNode.font = avatarPlaceholderFont(size: avatarFontSize)
|
||||
}
|
||||
}
|
||||
self.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, clipStyle: isForumAvatar ? .roundedRect : .round, synchronousLoad: synchronousLoads, displayDimensions: CGSize(width: 60.0, height: 60.0))
|
||||
if peer.smallProfileImage != nil && overrideImage == nil {
|
||||
self.avatarNode.setPeerV2(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, clipStyle: isForumAvatar ? .roundedRect : .round, synchronousLoad: synchronousLoads, displayDimensions: CGSize(width: 60.0, height: 60.0))
|
||||
} else {
|
||||
self.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, clipStyle: isForumAvatar ? .roundedRect : .round, synchronousLoad: synchronousLoads, displayDimensions: CGSize(width: 60.0, height: 60.0))
|
||||
}
|
||||
|
||||
if peer.isPremium && peer.id != item.context.account.peerId {
|
||||
let context = item.context
|
||||
|
@ -954,10 +954,10 @@ final class ChatSendMessageContextScreenComponent: Component {
|
||||
}
|
||||
|
||||
var customEffectResource: (FileMediaReference, MediaResource)?
|
||||
if let effectAnimation = messageEffect.effectAnimation {
|
||||
if let effectAnimation = messageEffect.effectAnimation?._parse() {
|
||||
customEffectResource = (FileMediaReference.standalone(media: effectAnimation), effectAnimation.resource)
|
||||
} else {
|
||||
let effectSticker = messageEffect.effectSticker
|
||||
let effectSticker = messageEffect.effectSticker._parse()
|
||||
if let effectFile = effectSticker.videoThumbnails.first {
|
||||
customEffectResource = (FileMediaReference.standalone(media: effectSticker), effectFile.resource)
|
||||
}
|
||||
|
@ -309,7 +309,7 @@ final class MessageItemView: UIView {
|
||||
}
|
||||
let effectIconContent: ChatSendMessageScreenEffectIcon.Content
|
||||
if let staticIcon = effect.staticIcon {
|
||||
effectIconContent = .file(staticIcon)
|
||||
effectIconContent = .file(staticIcon._parse())
|
||||
} else {
|
||||
effectIconContent = .text(effect.emoticon)
|
||||
}
|
||||
|
@ -52,6 +52,8 @@ final class NavigationTransitionCoordinator {
|
||||
private var currentCompletion: (() -> Void)?
|
||||
private var didUpdateProgress: ((CGFloat, ContainedViewLayoutTransition, CGRect, CGRect) -> Void)?
|
||||
|
||||
private var frameRateLink: SharedDisplayLinkDriver.Link?
|
||||
|
||||
init(transition: NavigationTransition, isInteractive: Bool, isFlat: Bool, container: NavigationContainer, topNode: ASDisplayNode, topNavigationBar: NavigationBar?, bottomNode: ASDisplayNode, bottomNavigationBar: NavigationBar?, didUpdateProgress: ((CGFloat, ContainedViewLayoutTransition, CGRect, CGRect) -> Void)? = nil) {
|
||||
self.transition = transition
|
||||
self.isInteractive = isInteractive
|
||||
@ -114,6 +116,8 @@ final class NavigationTransitionCoordinator {
|
||||
|
||||
self.maybeCreateNavigationBarTransition()
|
||||
self.updateProgress(0.0, transition: .immediate, completion: {})
|
||||
|
||||
self.frameRateLink = SharedDisplayLinkDriver.shared.add(framesPerSecond: .max, { _ in })
|
||||
}
|
||||
|
||||
required init(coder aDecoder: NSCoder) {
|
||||
|
@ -403,6 +403,22 @@ public final class FFMpegFileReader {
|
||||
deinit {
|
||||
}
|
||||
|
||||
public func frameRate() -> Int {
|
||||
if let stream = self.stream {
|
||||
return Int(stream.info.fps.seconds)
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
public func duration() -> CMTime {
|
||||
if let stream = self.stream {
|
||||
return stream.info.duration
|
||||
} else {
|
||||
return .zero
|
||||
}
|
||||
}
|
||||
|
||||
private func readPacketInternal() -> FFMpegPacket? {
|
||||
guard let avFormatContext = self.avFormatContext else {
|
||||
return nil
|
||||
@ -452,7 +468,7 @@ public final class FFMpegFileReader {
|
||||
return nil
|
||||
}
|
||||
|
||||
public func readFrame() -> ReadFrameResult {
|
||||
public func readFrame(argb: Bool = false) -> ReadFrameResult {
|
||||
guard let stream = self.stream else {
|
||||
return .error
|
||||
}
|
||||
@ -461,7 +477,7 @@ public final class FFMpegFileReader {
|
||||
var result: MediaTrackFrame?
|
||||
switch stream.decoder {
|
||||
case let .video(decoder):
|
||||
result = decoder.decode(ptsOffset: nil, forceARGB: false, unpremultiplyAlpha: false, displayImmediately: false)
|
||||
result = decoder.decode(ptsOffset: nil, forceARGB: argb, unpremultiplyAlpha: false, displayImmediately: false)
|
||||
case let .videoPassthrough(decoder):
|
||||
result = decoder.decode()
|
||||
case let .audio(decoder):
|
||||
|
@ -448,7 +448,7 @@ private func chatMessageImageFileThumbnailDatas(account: Account, userLocation:
|
||||
return signal
|
||||
}
|
||||
|
||||
private func chatMessageVideoDatas(postbox: Postbox, userLocation: MediaResourceUserLocation, customUserContentType: MediaResourceUserContentType? = nil, fileReference: FileMediaReference, previewSourceFileReference: FileMediaReference?, thumbnailSize: Bool = false, onlyFullSize: Bool = false, useLargeThumbnail: Bool = false, synchronousLoad: Bool = false, autoFetchFullSizeThumbnail: Bool = false, forceThumbnail: Bool = false) -> Signal<Tuple3<Data?, Tuple2<Data, String>?, Bool>, NoError> {
|
||||
private func chatMessageVideoDatas(postbox: Postbox, userLocation: MediaResourceUserLocation, customUserContentType: MediaResourceUserContentType? = nil, fileReference: FileMediaReference, previewSourceFileReference: FileMediaReference?, alternativeFileAndRange: Signal<(TelegramMediaFile, Range<Int64>), NoError>? = nil, thumbnailSize: Bool = false, onlyFullSize: Bool = false, useLargeThumbnail: Bool = false, synchronousLoad: Bool = false, autoFetchFullSizeThumbnail: Bool = false, forceThumbnail: Bool = false) -> Signal<Tuple3<Data?, Tuple2<Data, String>?, Bool>, NoError> {
|
||||
let fullSizeResource = fileReference.media.resource
|
||||
var reducedSizeResource: MediaResource?
|
||||
if let videoThumbnail = fileReference.media.videoThumbnails.first {
|
||||
@ -1627,7 +1627,7 @@ public func mediaGridMessageVideo(postbox: Postbox, userLocation: MediaResourceU
|
||||
}
|
||||
}
|
||||
|
||||
public func internalMediaGridMessageVideo(postbox: Postbox, userLocation: MediaResourceUserLocation, customUserContentType: MediaResourceUserContentType? = nil, videoReference: FileMediaReference, previewSourceFileReference: FileMediaReference? = nil, imageReference: ImageMediaReference? = nil, onlyFullSize: Bool = false, useLargeThumbnail: Bool = false, synchronousLoad: Bool = false, autoFetchFullSizeThumbnail: Bool = false, overlayColor: UIColor? = nil, nilForEmptyResult: Bool = false, useMiniThumbnailIfAvailable: Bool = false, blurred: Bool = false) -> Signal<(() -> CGSize?, (TransformImageArguments) -> DrawingContext?), NoError> {
|
||||
public func internalMediaGridMessageVideo(postbox: Postbox, userLocation: MediaResourceUserLocation, customUserContentType: MediaResourceUserContentType? = nil, videoReference: FileMediaReference, previewSourceFileReference: FileMediaReference? = nil, imageReference: ImageMediaReference? = nil, alternativeFileAndRange: Signal<(TelegramMediaFile, Range<Int64>), NoError>? = nil, onlyFullSize: Bool = false, useLargeThumbnail: Bool = false, synchronousLoad: Bool = false, autoFetchFullSizeThumbnail: Bool = false, overlayColor: UIColor? = nil, nilForEmptyResult: Bool = false, useMiniThumbnailIfAvailable: Bool = false, blurred: Bool = false) -> Signal<(() -> CGSize?, (TransformImageArguments) -> DrawingContext?), NoError> {
|
||||
let signal: Signal<Tuple3<Data?, Tuple2<Data, String>?, Bool>, NoError>
|
||||
if let imageReference = imageReference {
|
||||
signal = chatMessagePhotoDatas(postbox: postbox, userLocation: userLocation, customUserContentType: customUserContentType, photoReference: imageReference, tryAdditionalRepresentations: true, synchronousLoad: synchronousLoad, forceThumbnail: blurred)
|
||||
@ -1638,7 +1638,7 @@ public func internalMediaGridMessageVideo(postbox: Postbox, userLocation: MediaR
|
||||
return Tuple(thumbnailData, fullSizeData.flatMap({ Tuple($0, "") }), fullSizeComplete)
|
||||
}
|
||||
} else {
|
||||
signal = chatMessageVideoDatas(postbox: postbox, userLocation: userLocation, customUserContentType: customUserContentType, fileReference: videoReference, previewSourceFileReference: previewSourceFileReference, onlyFullSize: onlyFullSize, useLargeThumbnail: useLargeThumbnail, synchronousLoad: synchronousLoad, autoFetchFullSizeThumbnail: autoFetchFullSizeThumbnail, forceThumbnail: blurred)
|
||||
signal = chatMessageVideoDatas(postbox: postbox, userLocation: userLocation, customUserContentType: customUserContentType, fileReference: videoReference, previewSourceFileReference: previewSourceFileReference, alternativeFileAndRange: alternativeFileAndRange, onlyFullSize: onlyFullSize, useLargeThumbnail: useLargeThumbnail, synchronousLoad: synchronousLoad, autoFetchFullSizeThumbnail: autoFetchFullSizeThumbnail, forceThumbnail: blurred)
|
||||
}
|
||||
|
||||
return signal
|
||||
|
@ -1893,7 +1893,7 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
for i in 0 ..< 2 {
|
||||
let groupId = i == 0 ? "reactions" : "stickers"
|
||||
for item in i == 0 ? reactionEffects : stickerEffects {
|
||||
let itemFile: TelegramMediaFile = item.effectSticker
|
||||
let itemFile = item.effectSticker
|
||||
|
||||
var tintMode: EmojiPagerContentComponent.Item.TintMode = .none
|
||||
if itemFile.isCustomTemplateEmoji {
|
||||
@ -1917,11 +1917,11 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
let animationData = EntityKeyboardAnimationData(file: TelegramMediaFile.Accessor(itemFile), partialReference: .none)
|
||||
let animationData = EntityKeyboardAnimationData(file: itemFile, partialReference: .none)
|
||||
let resultItem = EmojiPagerContentComponent.Item(
|
||||
animationData: animationData,
|
||||
content: .animation(animationData),
|
||||
itemFile: TelegramMediaFile.Accessor(itemFile),
|
||||
itemFile: itemFile,
|
||||
subgroupId: nil,
|
||||
icon: icon,
|
||||
tintMode: tintMode
|
||||
@ -2257,7 +2257,7 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
for i in 0 ..< 2 {
|
||||
let groupId = i == 0 ? "reactions" : "stickers"
|
||||
for item in i == 0 ? reactionEffects : stickerEffects {
|
||||
let itemFile: TelegramMediaFile = item.effectSticker
|
||||
let itemFile = item.effectSticker
|
||||
|
||||
var tintMode: EmojiPagerContentComponent.Item.TintMode = .none
|
||||
if itemFile.isCustomTemplateEmoji {
|
||||
@ -2281,11 +2281,11 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
let animationData = EntityKeyboardAnimationData(file: TelegramMediaFile.Accessor(itemFile), partialReference: .none)
|
||||
let animationData = EntityKeyboardAnimationData(file: itemFile, partialReference: .none)
|
||||
let resultItem = EmojiPagerContentComponent.Item(
|
||||
animationData: animationData,
|
||||
content: .animation(animationData),
|
||||
itemFile: TelegramMediaFile.Accessor(itemFile),
|
||||
itemFile: itemFile,
|
||||
subgroupId: nil,
|
||||
icon: icon,
|
||||
tintMode: tintMode
|
||||
|
@ -976,7 +976,7 @@ func applyLoadMessageHistoryThreadsResults(accountPeerId: PeerId, transaction: T
|
||||
transaction.replaceMessageTagSummary(peerId: result.peerId, threadId: item.threadId, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, customTag: nil, count: item.unreadReactionsCount, maxId: item.topMessage)
|
||||
|
||||
if item.topMessage != 0 {
|
||||
transaction.removeHole(peerId: result.peerId, threadId: item.threadId, namespace: Namespaces.Message.Cloud, space: .everywhere, range: item.topMessage ... (Int32.max - 1))
|
||||
//transaction.removeHole(peerId: result.peerId, threadId: item.threadId, namespace: Namespaces.Message.Cloud, space: .everywhere, range: item.topMessage ... (Int32.max - 1))
|
||||
}
|
||||
|
||||
for message in result.messages {
|
||||
|
@ -2,6 +2,8 @@ import Foundation
|
||||
import TelegramApi
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
import FlatBuffers
|
||||
import FlatSerialization
|
||||
|
||||
public final class AvailableMessageEffects: Equatable, Codable {
|
||||
public final class MessageEffect: Equatable, Codable {
|
||||
@ -10,24 +12,27 @@ public final class AvailableMessageEffects: Equatable, Codable {
|
||||
case isPremium
|
||||
case emoticon
|
||||
case staticIcon
|
||||
case staticIconData = "sid"
|
||||
case effectSticker
|
||||
case effectStickerData = "esd"
|
||||
case effectAnimation
|
||||
case effectAnimationData = "ead"
|
||||
}
|
||||
|
||||
public let id: Int64
|
||||
public let isPremium: Bool
|
||||
public let emoticon: String
|
||||
public let staticIcon: TelegramMediaFile?
|
||||
public let effectSticker: TelegramMediaFile
|
||||
public let effectAnimation: TelegramMediaFile?
|
||||
public let staticIcon: TelegramMediaFile.Accessor?
|
||||
public let effectSticker: TelegramMediaFile.Accessor
|
||||
public let effectAnimation: TelegramMediaFile.Accessor?
|
||||
|
||||
public init(
|
||||
id: Int64,
|
||||
isPremium: Bool,
|
||||
emoticon: String,
|
||||
staticIcon: TelegramMediaFile?,
|
||||
effectSticker: TelegramMediaFile,
|
||||
effectAnimation: TelegramMediaFile?
|
||||
staticIcon: TelegramMediaFile.Accessor?,
|
||||
effectSticker: TelegramMediaFile.Accessor,
|
||||
effectAnimation: TelegramMediaFile.Accessor?
|
||||
) {
|
||||
self.id = id
|
||||
self.isPremium = isPremium
|
||||
@ -66,19 +71,28 @@ public final class AvailableMessageEffects: Equatable, Codable {
|
||||
self.isPremium = try container.decodeIfPresent(Bool.self, forKey: .isPremium) ?? false
|
||||
self.emoticon = try container.decode(String.self, forKey: .emoticon)
|
||||
|
||||
if let staticIconData = try container.decodeIfPresent(AdaptedPostboxDecoder.RawObjectData.self, forKey: .staticIcon) {
|
||||
self.staticIcon = TelegramMediaFile(decoder: PostboxDecoder(buffer: MemoryBuffer(data: staticIconData.data)))
|
||||
if let staticIconData = try container.decodeIfPresent(Data.self, forKey: .staticIconData) {
|
||||
var byteBuffer = ByteBuffer(data: staticIconData)
|
||||
self.staticIcon = TelegramMediaFile.Accessor(FlatBuffers_getRoot(byteBuffer: &byteBuffer) as TelegramCore_TelegramMediaFile, staticIconData)
|
||||
} else if let staticIconData = try container.decodeIfPresent(AdaptedPostboxDecoder.RawObjectData.self, forKey: .staticIcon) {
|
||||
self.staticIcon = TelegramMediaFile.Accessor(TelegramMediaFile(decoder: PostboxDecoder(buffer: MemoryBuffer(data: staticIconData.data))))
|
||||
} else {
|
||||
self.staticIcon = nil
|
||||
}
|
||||
|
||||
do {
|
||||
if let effectStickerData = try container.decodeIfPresent(Data.self, forKey: .effectStickerData) {
|
||||
var byteBuffer = ByteBuffer(data: effectStickerData)
|
||||
self.effectSticker = TelegramMediaFile.Accessor(FlatBuffers_getRoot(byteBuffer: &byteBuffer) as TelegramCore_TelegramMediaFile, effectStickerData)
|
||||
} else {
|
||||
let effectStickerData = try container.decode(AdaptedPostboxDecoder.RawObjectData.self, forKey: .effectSticker)
|
||||
self.effectSticker = TelegramMediaFile(decoder: PostboxDecoder(buffer: MemoryBuffer(data: effectStickerData.data)))
|
||||
self.effectSticker = TelegramMediaFile.Accessor(TelegramMediaFile(decoder: PostboxDecoder(buffer: MemoryBuffer(data: effectStickerData.data))))
|
||||
}
|
||||
|
||||
if let effectAnimationData = try container.decodeIfPresent(AdaptedPostboxDecoder.RawObjectData.self, forKey: .effectAnimation) {
|
||||
self.effectAnimation = TelegramMediaFile(decoder: PostboxDecoder(buffer: MemoryBuffer(data: effectAnimationData.data)))
|
||||
if let effectAnimationData = try container.decodeIfPresent(Data.self, forKey: .effectAnimationData) {
|
||||
var byteBuffer = ByteBuffer(data: effectAnimationData)
|
||||
self.effectAnimation = TelegramMediaFile.Accessor(FlatBuffers_getRoot(byteBuffer: &byteBuffer) as TelegramCore_TelegramMediaFile, effectAnimationData)
|
||||
} else if let effectAnimationData = try container.decodeIfPresent(AdaptedPostboxDecoder.RawObjectData.self, forKey: .effectAnimation) {
|
||||
self.effectAnimation = TelegramMediaFile.Accessor(TelegramMediaFile(decoder: PostboxDecoder(buffer: MemoryBuffer(data: effectAnimationData.data))))
|
||||
} else {
|
||||
self.effectAnimation = nil
|
||||
}
|
||||
@ -91,12 +105,26 @@ public final class AvailableMessageEffects: Equatable, Codable {
|
||||
try container.encode(self.emoticon, forKey: .emoticon)
|
||||
try container.encode(self.isPremium, forKey: .isPremium)
|
||||
|
||||
if let staticIcon = self.staticIcon {
|
||||
try container.encode(PostboxEncoder().encodeObjectToRawData(staticIcon), forKey: .staticIcon)
|
||||
let encodeFileItem: (TelegramMediaFile.Accessor, CodingKeys) throws -> Void = { file, key in
|
||||
if let serializedFile = file._wrappedData {
|
||||
try container.encode(serializedFile, forKey: key)
|
||||
} else if let file = file._wrappedFile {
|
||||
var builder = FlatBufferBuilder(initialSize: 1024)
|
||||
let value = file.encodeToFlatBuffers(builder: &builder)
|
||||
builder.finish(offset: value)
|
||||
let serializedFile = builder.data
|
||||
try container.encode(serializedFile, forKey: key)
|
||||
} else {
|
||||
preconditionFailure()
|
||||
}
|
||||
}
|
||||
try container.encode(PostboxEncoder().encodeObjectToRawData(self.effectSticker), forKey: .effectSticker)
|
||||
|
||||
if let staticIcon = self.staticIcon {
|
||||
try encodeFileItem(staticIcon, .staticIconData)
|
||||
}
|
||||
try encodeFileItem(self.effectSticker, .effectStickerData)
|
||||
if let effectAnimation = self.effectAnimation {
|
||||
try container.encode(PostboxEncoder().encodeObjectToRawData(effectAnimation), forKey: .effectAnimation)
|
||||
try encodeFileItem(effectAnimation, .effectAnimationData)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -142,8 +170,6 @@ public final class AvailableMessageEffects: Equatable, Codable {
|
||||
}
|
||||
}
|
||||
|
||||
//availableEffect flags:# premium_required:flags.2?true id:long emoticon:string static_icon_id:flags.0?long effect_sticker_id:long effect_animation_id:flags.1?long = AvailableEffect;
|
||||
|
||||
private extension AvailableMessageEffects.MessageEffect {
|
||||
convenience init?(apiMessageEffect: Api.AvailableEffect, files: [Int64: TelegramMediaFile]) {
|
||||
switch apiMessageEffect {
|
||||
@ -157,9 +183,9 @@ private extension AvailableMessageEffects.MessageEffect {
|
||||
id: id,
|
||||
isPremium: isPremium,
|
||||
emoticon: emoticon,
|
||||
staticIcon: staticIconId.flatMap({ files[$0] }),
|
||||
effectSticker: effectSticker,
|
||||
effectAnimation: effectAnimationId.flatMap({ files[$0] })
|
||||
staticIcon: staticIconId.flatMap({ files[$0].flatMap(TelegramMediaFile.Accessor.init) }),
|
||||
effectSticker: TelegramMediaFile.Accessor(effectSticker),
|
||||
effectAnimation: effectAnimationId.flatMap({ files[$0].flatMap(TelegramMediaFile.Accessor.init) })
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -289,22 +315,3 @@ func managedSynchronizeAvailableMessageEffects(postbox: Postbox, network: Networ
|
||||
)
|
||||
|> restart
|
||||
}
|
||||
|
||||
public extension Message {
|
||||
func messageEffect(availableMessageEffects: AvailableMessageEffects?) -> AvailableMessageEffects.MessageEffect? {
|
||||
guard let availableMessageEffects else {
|
||||
return nil
|
||||
}
|
||||
for attribute in self.attributes {
|
||||
if let attribute = attribute as? EffectMessageAttribute {
|
||||
for effect in availableMessageEffects.messageEffects {
|
||||
if effect.id == attribute.id {
|
||||
return effect
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -1411,7 +1411,7 @@ public final class InstantPage: PostboxCoding, Equatable {
|
||||
self.url = decoder.decodeStringForKey("url", orElse: "")
|
||||
self.views = decoder.decodeOptionalInt32ForKey("v")
|
||||
|
||||
#if DEBUG
|
||||
#if DEBUG && false
|
||||
var builder = FlatBufferBuilder(initialSize: 1024)
|
||||
let offset = self.encodeToFlatBuffers(builder: &builder)
|
||||
builder.finish(offset: offset)
|
||||
|
@ -104,7 +104,7 @@ public final class StickerPackCollectionInfo: ItemCollectionInfo, Equatable {
|
||||
encoder.encodeInt32(self.flags.rawValue, forKey: "f")
|
||||
encoder.encodeInt32(self.count, forKey: "n")
|
||||
|
||||
#if DEBUG
|
||||
#if DEBUG && false
|
||||
var builder = FlatBufferBuilder(initialSize: 1024)
|
||||
let offset = self.encodeToFlatBuffers(builder: &builder)
|
||||
builder.finish(offset: offset)
|
||||
|
@ -347,7 +347,7 @@ public final class TelegramChannel: Peer, Equatable {
|
||||
self.verificationIconFileId = decoder.decodeOptionalInt64ForKey("vfid")
|
||||
self.sendPaidMessageStars = decoder.decodeCodable(StarsAmount.self, forKey: "sendPaidMessageStars")
|
||||
|
||||
#if DEBUG
|
||||
#if DEBUG && false
|
||||
var builder = FlatBufferBuilder(initialSize: 1024)
|
||||
let offset = self.encodeToFlatBuffers(builder: &builder)
|
||||
builder.finish(offset: offset)
|
||||
|
@ -207,7 +207,7 @@ public final class TelegramGroup: Peer, Equatable {
|
||||
self.creationDate = decoder.decodeInt32ForKey("d", orElse: 0)
|
||||
self.version = Int(decoder.decodeInt32ForKey("v", orElse: 0))
|
||||
|
||||
#if DEBUG
|
||||
#if DEBUG && false
|
||||
var builder = FlatBufferBuilder(initialSize: 1024)
|
||||
let offset = self.encodeToFlatBuffers(builder: &builder)
|
||||
builder.finish(offset: offset)
|
||||
|
@ -698,8 +698,7 @@ public final class TelegramMediaFile: Media, Equatable, Codable {
|
||||
} else if let lhsWrappedData = lhs._wrappedData, let rhsWrappedData = rhs._wrappedData {
|
||||
return lhsWrappedData == rhsWrappedData
|
||||
} else {
|
||||
assertionFailure()
|
||||
return false
|
||||
return lhs._parse() == rhs._parse()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -903,13 +902,6 @@ public final class TelegramMediaFile: Media, Equatable, Codable {
|
||||
try container.encode(postboxEncoder.makeData(), forKey: .data)
|
||||
}
|
||||
|
||||
public func encodeToFlatBuffersData() -> Data {
|
||||
var builder = FlatBufferBuilder(initialSize: 1024)
|
||||
let value = self.encodeToFlatBuffers(builder: &builder)
|
||||
builder.finish(offset: value)
|
||||
return builder.data
|
||||
}
|
||||
|
||||
public init(flatBuffersObject: TelegramCore_TelegramMediaFile) throws {
|
||||
self.fileId = MediaId(namespace: flatBuffersObject.fileId.namespace, id: flatBuffersObject.fileId.id)
|
||||
self.partialReference = try flatBuffersObject.partialReference.flatMap { try PartialMediaReference(flatBuffersObject: $0 ) }
|
||||
|
@ -21,10 +21,21 @@ private let cloudSoundMapping: [Int32: Int64] = [
|
||||
108: 5078299559046677216,
|
||||
109: 5078299559046677217,
|
||||
110: 5078299559046677218,
|
||||
111: 5078299559046677219
|
||||
111: 5078299559046677219,
|
||||
200: 5032932652722685163,
|
||||
201: 5032932652722685160,
|
||||
202: 5032932652722685159,
|
||||
203: 5032932652722685158,
|
||||
204: 5032932652722685168,
|
||||
205: 5032932652722685167,
|
||||
206: 5032932652722685166,
|
||||
207: 5032932652722685165,
|
||||
208: 5032932652722685164,
|
||||
209: 5032932652722685162,
|
||||
210: 5032932652722685161
|
||||
]
|
||||
|
||||
public let defaultCloudPeerNotificationSound: PeerMessageSound = .cloud(fileId: cloudSoundMapping[100]!)
|
||||
public let defaultCloudPeerNotificationSound: PeerMessageSound = .cloud(fileId: cloudSoundMapping[200]!)
|
||||
|
||||
public enum CloudSoundBuiltinCategory {
|
||||
case modern
|
||||
|
@ -331,7 +331,7 @@ public final class TelegramUser: Peer, Equatable {
|
||||
self.subscriberCount = decoder.decodeOptionalInt32ForKey("ssc")
|
||||
self.verificationIconFileId = decoder.decodeOptionalInt64ForKey("vfid")
|
||||
|
||||
#if DEBUG
|
||||
#if DEBUG && false
|
||||
var builder = FlatBufferBuilder(initialSize: 1024)
|
||||
let offset = self.encodeToFlatBuffers(builder: &builder)
|
||||
builder.finish(offset: offset)
|
||||
|
@ -642,3 +642,22 @@ public func _internal_parseMediaAttachment(data: Data) -> Media? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public extension Message {
|
||||
func messageEffect(availableMessageEffects: AvailableMessageEffects?) -> AvailableMessageEffects.MessageEffect? {
|
||||
guard let availableMessageEffects else {
|
||||
return nil
|
||||
}
|
||||
for attribute in self.attributes {
|
||||
if let attribute = attribute as? EffectMessageAttribute {
|
||||
for effect in availableMessageEffects.messageEffects {
|
||||
if effect.id == attribute.id {
|
||||
return effect
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,18 @@ private let modernSoundsNamePaths: [KeyPath<PresentationStrings, String>] = [
|
||||
\.NotificationsSound_Keys,
|
||||
\.NotificationsSound_Popcorn,
|
||||
\.NotificationsSound_Pulse,
|
||||
\.NotificationsSound_Synth
|
||||
\.NotificationsSound_Synth,
|
||||
\.NotificationsSound_Rebound,
|
||||
\.NotificationsSound_Antic,
|
||||
\.NotificationsSound_Cheers,
|
||||
\.NotificationsSound_Droplet,
|
||||
\.NotificationsSound_Handoff,
|
||||
\.NotificationsSound_Milestone,
|
||||
\.NotificationsSound_Passage,
|
||||
\.NotificationsSound_Portal,
|
||||
\.NotificationsSound_Rattle,
|
||||
\.NotificationsSound_Slide,
|
||||
\.NotificationsSound_Welcome
|
||||
]
|
||||
|
||||
private let classicSoundNamePaths: [KeyPath<PresentationStrings, String>] = [
|
||||
|
@ -1116,7 +1116,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
|
||||
var centerAnimation: TelegramMediaFile?
|
||||
|
||||
centerAnimation = messageEffect.staticIcon
|
||||
centerAnimation = messageEffect.staticIcon?._parse()
|
||||
|
||||
node.update(
|
||||
context: arguments.context,
|
||||
|
@ -2286,7 +2286,8 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
||||
|
||||
if automaticDownload != .none, let file = media as? TelegramMediaFile, NativeVideoContent.isHLSVideo(file: file) {
|
||||
let postbox = context.account.postbox
|
||||
let fetchSignal = HLSVideoContent.minimizedHLSQualityPreloadData(postbox: context.account.postbox, file: .message(message: MessageReference(message), media: file), userLocation: .peer(message.id.peerId), prefixSeconds: 10, autofetchPlaylist: true, codecConfiguration: HLSCodecConfiguration(context: context))
|
||||
let fetchSignal: Signal<Never, NoError>
|
||||
fetchSignal = HLSVideoContent.minimizedHLSQualityPreloadData(postbox: context.account.postbox, file: .message(message: MessageReference(message), media: file), userLocation: .peer(message.id.peerId), prefixSeconds: 10, autofetchPlaylist: true, codecConfiguration: HLSCodecConfiguration(context: context))
|
||||
|> mapToSignal { fileAndRange -> Signal<Never, NoError> in
|
||||
guard let fileAndRange else {
|
||||
return .complete()
|
||||
|
@ -938,13 +938,13 @@ open class ChatMessageItemView: ListViewItemNode, ChatMessageItemNodeProtocol {
|
||||
}
|
||||
self.playedEffectAnimation = true
|
||||
|
||||
if let effectAnimation = effect.effectAnimation {
|
||||
if let effectAnimation = effect.effectAnimation?._parse() {
|
||||
self.playEffectAnimation(resource: effectAnimation.resource)
|
||||
if self.fetchEffectDisposable == nil {
|
||||
self.fetchEffectDisposable = freeMediaFileResourceInteractiveFetched(account: item.context.account, userLocation: .other, fileReference: .standalone(media: effectAnimation), resource: effectAnimation.resource).startStrict()
|
||||
}
|
||||
} else {
|
||||
let effectSticker = effect.effectSticker
|
||||
let effectSticker = effect.effectSticker._parse()
|
||||
if let effectFile = effectSticker.videoThumbnails.first {
|
||||
self.playEffectAnimation(resource: effectFile.resource)
|
||||
if self.fetchEffectDisposable == nil {
|
||||
|
@ -501,7 +501,7 @@ public func effectMessageReactions(context: AccountContext) -> Signal<[ReactionI
|
||||
}
|
||||
existingIds.insert(messageEffect.id)
|
||||
|
||||
let mainFile = TelegramMediaFile.Accessor(messageEffect.effectSticker)
|
||||
let mainFile = messageEffect.effectSticker
|
||||
|
||||
result.append(ReactionItem(
|
||||
reaction: ReactionItem.Reaction(rawValue: .custom(messageEffect.id)),
|
||||
|
@ -82,18 +82,20 @@ public func generateTopicIcon(title: String, backgroundColors: [UIColor], stroke
|
||||
})
|
||||
}
|
||||
|
||||
public enum AnimationCacheAnimationType {
|
||||
public enum AnimationCacheAnimationType: Equatable {
|
||||
case still
|
||||
case lottie
|
||||
case video
|
||||
case video(isVP9: Bool)
|
||||
}
|
||||
|
||||
public extension AnimationCacheAnimationType {
|
||||
init(file: TelegramMediaFile) {
|
||||
if file.isVideoSticker || file.isVideoEmoji {
|
||||
self = .video
|
||||
self = .video(isVP9: true)
|
||||
} else if file.isAnimatedSticker {
|
||||
self = .lottie
|
||||
} else if file.isVideo {
|
||||
self = .video(isVP9: false)
|
||||
} else {
|
||||
self = .still
|
||||
}
|
||||
@ -122,8 +124,8 @@ public func animationCacheFetchFile(postbox: Postbox, userLocation: MediaResourc
|
||||
}
|
||||
|
||||
switch type {
|
||||
case .video:
|
||||
cacheVideoAnimation(path: result, width: Int(options.size.width), height: Int(options.size.height), writer: options.writer, firstFrameOnly: options.firstFrameOnly, customColor: customColor)
|
||||
case let .video(isVP9):
|
||||
cacheVideoAnimation(path: result, hintVP9: isVP9, width: Int(options.size.width), height: Int(options.size.height), writer: options.writer, firstFrameOnly: options.firstFrameOnly, customColor: customColor)
|
||||
case .lottie:
|
||||
guard let data = try? Data(contentsOf: URL(fileURLWithPath: result)) else {
|
||||
options.writer.finish()
|
||||
@ -153,8 +155,8 @@ public func animationCacheLoadLocalFile(name: String, type: AnimationCacheAnimat
|
||||
}
|
||||
|
||||
switch type {
|
||||
case .video:
|
||||
cacheVideoAnimation(path: result, width: Int(options.size.width), height: Int(options.size.height), writer: options.writer, firstFrameOnly: options.firstFrameOnly, customColor: customColor)
|
||||
case let .video(isVP9):
|
||||
cacheVideoAnimation(path: result, hintVP9: isVP9, width: Int(options.size.width), height: Int(options.size.height), writer: options.writer, firstFrameOnly: options.firstFrameOnly, customColor: customColor)
|
||||
case .lottie:
|
||||
guard let data = try? Data(contentsOf: URL(fileURLWithPath: result)) else {
|
||||
options.writer.finish()
|
||||
|
@ -33,7 +33,7 @@ public final class EmojiKeyboardItemLayer: MultiAnimationRenderTarget {
|
||||
case locked
|
||||
case featured
|
||||
case text(String)
|
||||
case customFile(TelegramMediaFile)
|
||||
case customFile(TelegramMediaFile.Accessor)
|
||||
}
|
||||
|
||||
public let item: EmojiPagerContentComponent.Item
|
||||
|
@ -43,10 +43,10 @@ public final class EntityKeyboardAnimationData: Equatable {
|
||||
case gift(String)
|
||||
}
|
||||
|
||||
public enum ItemType {
|
||||
public enum ItemType: Equatable {
|
||||
case still
|
||||
case lottie
|
||||
case video
|
||||
case video(isVP9: Bool)
|
||||
|
||||
var animationCacheAnimationType: AnimationCacheAnimationType {
|
||||
switch self {
|
||||
@ -54,8 +54,8 @@ public final class EntityKeyboardAnimationData: Equatable {
|
||||
return .still
|
||||
case .lottie:
|
||||
return .lottie
|
||||
case .video:
|
||||
return .video
|
||||
case let .video(isVP9):
|
||||
return .video(isVP9: isVP9)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -105,9 +105,11 @@ public final class EntityKeyboardAnimationData: Equatable {
|
||||
public convenience init(file: TelegramMediaFile.Accessor, isReaction: Bool = false, partialReference: PartialMediaReference? = nil) {
|
||||
let type: ItemType
|
||||
if file.isVideoSticker || file.isVideoEmoji {
|
||||
type = .video
|
||||
type = .video(isVP9: true)
|
||||
} else if file.isAnimatedSticker {
|
||||
type = .lottie
|
||||
} else if file.isVideo {
|
||||
type = .video(isVP9: false)
|
||||
} else {
|
||||
type = .still
|
||||
}
|
||||
@ -406,7 +408,7 @@ public final class EmojiPagerContentComponent: Component {
|
||||
case locked
|
||||
case premium
|
||||
case text(String)
|
||||
case customFile(TelegramMediaFile)
|
||||
case customFile(TelegramMediaFile.Accessor)
|
||||
}
|
||||
|
||||
public enum TintMode: Equatable {
|
||||
|
@ -287,7 +287,9 @@ public extension EmojiPagerContentComponent {
|
||||
if item.file.isAnimatedSticker {
|
||||
type = .lottie
|
||||
} else if item.file.isVideoEmoji || item.file.isVideoSticker {
|
||||
type = .video
|
||||
type = .video(isVP9: true)
|
||||
} else if item.file.isVideo {
|
||||
type = .video(isVP9: false)
|
||||
} else {
|
||||
type = .still
|
||||
}
|
||||
@ -1390,7 +1392,9 @@ public extension EmojiPagerContentComponent {
|
||||
if item.file.isAnimatedSticker {
|
||||
type = .lottie
|
||||
} else if item.file.isVideoEmoji || item.file.isVideoSticker {
|
||||
type = .video
|
||||
type = .video(isVP9: true)
|
||||
} else if item.file.isVideo {
|
||||
type = .video(isVP9: false)
|
||||
} else {
|
||||
type = .still
|
||||
}
|
||||
@ -1477,7 +1481,9 @@ public extension EmojiPagerContentComponent {
|
||||
if item.file.isAnimatedSticker {
|
||||
type = .lottie
|
||||
} else if item.file.isVideoEmoji || item.file.isVideoSticker {
|
||||
type = .video
|
||||
type = .video(isVP9: true)
|
||||
} else if item.file.isVideo {
|
||||
type = .video(isVP9: false)
|
||||
} else {
|
||||
type = .still
|
||||
}
|
||||
@ -1774,7 +1780,9 @@ public extension EmojiPagerContentComponent {
|
||||
if item.file.isAnimatedSticker {
|
||||
type = .lottie
|
||||
} else if item.file.isVideoEmoji || item.file.isVideoSticker {
|
||||
type = .video
|
||||
type = .video(isVP9: true)
|
||||
} else if item.file.isVideo {
|
||||
type = .video(isVP9: false)
|
||||
} else {
|
||||
type = .still
|
||||
}
|
||||
@ -2011,7 +2019,9 @@ public extension EmojiPagerContentComponent {
|
||||
if item.file.isAnimatedSticker {
|
||||
type = .lottie
|
||||
} else if item.file.isVideoEmoji || item.file.isVideoSticker {
|
||||
type = .video
|
||||
type = .video(isVP9: true)
|
||||
} else if item.file.isVideo {
|
||||
type = .video(isVP9: false)
|
||||
} else {
|
||||
type = .still
|
||||
}
|
||||
@ -2090,7 +2100,9 @@ public extension EmojiPagerContentComponent {
|
||||
if item.file.isAnimatedSticker {
|
||||
type = .lottie
|
||||
} else if item.file.isVideoEmoji || item.file.isVideoSticker {
|
||||
type = .video
|
||||
type = .video(isVP9: true)
|
||||
} else if item.file.isVideo {
|
||||
type = .video(isVP9: false)
|
||||
} else {
|
||||
type = .still
|
||||
}
|
||||
@ -2234,7 +2246,7 @@ public extension EmojiPagerContentComponent {
|
||||
continue
|
||||
}
|
||||
|
||||
let itemFile: TelegramMediaFile = item.effectSticker
|
||||
let itemFile = item.effectSticker
|
||||
|
||||
var tintMode: Item.TintMode = .none
|
||||
if itemFile.isCustomTemplateEmoji {
|
||||
@ -2258,11 +2270,11 @@ public extension EmojiPagerContentComponent {
|
||||
}
|
||||
}
|
||||
|
||||
let animationData = EntityKeyboardAnimationData(file: TelegramMediaFile.Accessor(itemFile), partialReference: .none)
|
||||
let animationData = EntityKeyboardAnimationData(file: itemFile, partialReference: .none)
|
||||
let resultItem = EmojiPagerContentComponent.Item(
|
||||
animationData: animationData,
|
||||
content: .animation(animationData),
|
||||
itemFile: TelegramMediaFile.Accessor(itemFile),
|
||||
itemFile: itemFile,
|
||||
subgroupId: nil,
|
||||
icon: icon,
|
||||
tintMode: tintMode
|
||||
|
@ -81,7 +81,7 @@ final class PremiumBadgeView: UIView {
|
||||
context: self.context,
|
||||
userLocation: .other,
|
||||
attemptSynchronousLoad: false,
|
||||
file: customFile,
|
||||
file: customFile._parse(),
|
||||
cache: self.context.animationCache,
|
||||
renderer: self.context.animationRenderer,
|
||||
unique: false,
|
||||
|
@ -18,9 +18,9 @@ private func roundUp(_ numToRound: Int, multiple: Int) -> Int {
|
||||
return numToRound + multiple - remainder
|
||||
}
|
||||
|
||||
public func cacheVideoAnimation(path: String, width: Int, height: Int, writer: AnimationCacheItemWriter, firstFrameOnly: Bool, customColor: UIColor?) {
|
||||
public func cacheVideoAnimation(path: String, hintVP9: Bool, width: Int, height: Int, writer: AnimationCacheItemWriter, firstFrameOnly: Bool, customColor: UIColor?) {
|
||||
let work: () -> Void = {
|
||||
guard let frameSource = makeVideoStickerDirectFrameSource(queue: writer.queue, path: path, width: roundUp(width, multiple: 16), height: roundUp(height, multiple: 16), cachePathPrefix: nil, unpremultiplyAlpha: false) else {
|
||||
guard let frameSource = makeVideoStickerDirectFrameSource(queue: writer.queue, path: path, hintVP9: hintVP9, width: roundUp(width, multiple: 16), height: roundUp(height, multiple: 16), cachePathPrefix: nil, unpremultiplyAlpha: false) else {
|
||||
return
|
||||
}
|
||||
let frameDuration = 1.0 / Double(frameSource.frameRate)
|
||||
|
@ -102,7 +102,7 @@ private final class EffectBadgeView: UIView {
|
||||
}
|
||||
let effectIconContent: ChatSendMessageScreenEffectIcon.Content
|
||||
if let staticIcon = effect.staticIcon {
|
||||
effectIconContent = .file(staticIcon)
|
||||
effectIconContent = .file(staticIcon._parse())
|
||||
} else {
|
||||
effectIconContent = .text(effect.emoticon)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user