Video Stickers Improvements

This commit is contained in:
Ilya Laktyushin 2022-01-25 06:05:02 +03:00
parent cc5de9372f
commit 7e9fea5c48
8 changed files with 221 additions and 106 deletions

View File

@ -22,6 +22,7 @@ swift_library(
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
"//submodules/PresentationDataUtils:PresentationDataUtils",
"//submodules/ShimmerEffect:ShimmerEffect",
"//submodules/SoftwareVideo:SoftwareVideo",
],
visibility = [
"//visibility:public",

View File

@ -12,6 +12,7 @@ import StickerResources
import AnimatedStickerNode
import TelegramAnimatedStickerNode
import ShimmerEffect
import SoftwareVideo
public struct ItemListStickerPackItemEditing: Equatable {
public var editable: Bool
@ -121,7 +122,7 @@ public final class ItemListStickerPackItem: ListViewItem, ItemListItem {
public enum StickerPackThumbnailItem: Equatable {
case still(TelegramMediaImageRepresentation)
case animated(MediaResource, PixelDimensions)
case animated(MediaResource, PixelDimensions, Bool)
public static func ==(lhs: StickerPackThumbnailItem, rhs: StickerPackThumbnailItem) -> Bool {
switch lhs {
@ -131,8 +132,8 @@ public enum StickerPackThumbnailItem: Equatable {
} else {
return false
}
case let .animated(lhsResource, lhsDimensions):
if case let .animated(rhsResource, rhsDimensions) = rhs, lhsResource.isEqual(to: rhsResource), lhsDimensions == rhsDimensions {
case let .animated(lhsResource, lhsDimensions, lhsIsVideo):
if case let .animated(rhsResource, rhsDimensions, rhsIsVideo) = rhs, lhsResource.isEqual(to: rhsResource), lhsDimensions == rhsDimensions, lhsIsVideo == rhsIsVideo {
return true
} else {
return false
@ -158,6 +159,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
fileprivate let imageNode: TransformImageNode
private var animationNode: AnimatedStickerNode?
private var videoNode: VideoStickerNode?
private var placeholderNode: StickerShimmerEffectNode?
private let unreadNode: ASImageNode
private let titleNode: TextNode
@ -471,16 +473,15 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
var thumbnailItem: StickerPackThumbnailItem?
var resourceReference: MediaResourceReference?
if let thumbnail = item.packInfo.thumbnail {
if item.packInfo.flags.contains(.isAnimated) {
thumbnailItem = .animated(thumbnail.resource, thumbnail.dimensions)
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: item.packInfo.id.id, accessHash: item.packInfo.accessHash), resource: thumbnail.resource)
if item.packInfo.flags.contains(.isAnimated) || item.packInfo.flags.contains(.isVideo) {
thumbnailItem = .animated(thumbnail.resource, thumbnail.dimensions, item.packInfo.flags.contains(.isVideo))
} else {
thumbnailItem = .still(thumbnail)
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: item.packInfo.id.id, accessHash: item.packInfo.accessHash), resource: thumbnail.resource)
}
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: item.packInfo.id.id, accessHash: item.packInfo.accessHash), resource: thumbnail.resource)
} else if let item = item.topItem {
if item.file.isAnimatedSticker {
thumbnailItem = .animated(item.file.resource, item.file.dimensions ?? PixelDimensions(width: 100, height: 100))
if item.file.isAnimatedSticker || item.file.isVideoSticker {
thumbnailItem = .animated(item.file.resource, item.file.dimensions ?? PixelDimensions(width: 100, height: 100), item.file.isVideoSticker)
resourceReference = MediaResourceReference.media(media: .standalone(media: item.file), resource: item.file.resource)
} else if let dimensions = item.file.dimensions, let resource = chatMessageStickerResource(file: item.file, small: true) as? TelegramMediaResource {
thumbnailItem = .still(TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource, progressiveSizes: [], immediateThumbnailData: nil))
@ -507,7 +508,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: stillImageSize, boundingSize: stillImageSize, intrinsicInsets: UIEdgeInsets()))
updatedImageSignal = chatMessageStickerPackThumbnail(postbox: item.account.postbox, resource: representation.resource, nilIfEmpty: true)
}
case let .animated(resource, _):
case let .animated(resource, _, _):
imageSize = imageBoundingSize
if fileUpdated {
@ -745,29 +746,51 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
let boundingSize = CGSize(width: 34.0, height: 34.0)
if let thumbnailItem = thumbnailItem, let imageSize = imageSize {
let imageFrame = CGRect(origin: CGPoint(x: params.leftInset + revealOffset + editingOffset + 15.0 + floor((boundingSize.width - imageSize.width) / 2.0), y: floor((layout.contentSize.height - imageSize.height) / 2.0)), size: imageSize)
transition.updateFrame(node: strongSelf.imageNode, frame: imageFrame)
var thumbnailDimensions = PixelDimensions(width: 512, height: 512)
switch thumbnailItem {
case let .still(representation):
transition.updateFrame(node: strongSelf.imageNode, frame: imageFrame)
thumbnailDimensions = representation.dimensions
case let .animated(resource, _):
transition.updateFrame(node: strongSelf.imageNode, frame: imageFrame)
let animationNode: AnimatedStickerNode
if let current = strongSelf.animationNode {
animationNode = current
case let .animated(resource, _, isVideo):
if isVideo {
let videoNode: VideoStickerNode
if let current = strongSelf.videoNode {
videoNode = current
} else {
videoNode = VideoStickerNode()
strongSelf.videoNode = videoNode
strongSelf.addSubnode(videoNode)
if let resource = resource as? TelegramMediaResource {
let dummyFile = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 1), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/webm", size: resource.size ?? 1, attributes: [.Video(duration: 1, size: PixelDimensions(width: 100, height: 100), flags: [])])
videoNode.update(account: item.account, fileReference: .standalone(media: dummyFile))
}
}
videoNode.updateLayout(size: imageFrame.size)
videoNode.update(isPlaying: strongSelf.visibility != .none && item.playAnimatedStickers)
videoNode.isHidden = !item.playAnimatedStickers
strongSelf.imageNode.isHidden = item.playAnimatedStickers
if let videoNode = strongSelf.videoNode {
transition.updateFrame(node: videoNode, frame: imageFrame)
}
} else {
animationNode = AnimatedStickerNode()
strongSelf.animationNode = animationNode
strongSelf.addSubnode(animationNode)
animationNode.setup(source: AnimatedStickerResourceSource(account: item.account, resource: resource), width: 80, height: 80, mode: .cached)
}
animationNode.visibility = strongSelf.visibility != .none && item.playAnimatedStickers
animationNode.isHidden = !item.playAnimatedStickers
strongSelf.imageNode.isHidden = item.playAnimatedStickers
if let animationNode = strongSelf.animationNode {
transition.updateFrame(node: animationNode, frame: imageFrame)
let animationNode: AnimatedStickerNode
if let current = strongSelf.animationNode {
animationNode = current
} else {
animationNode = AnimatedStickerNode()
strongSelf.animationNode = animationNode
strongSelf.addSubnode(animationNode)
animationNode.setup(source: AnimatedStickerResourceSource(account: item.account, resource: resource), width: 80, height: 80, mode: .cached)
}
animationNode.visibility = strongSelf.visibility != .none && item.playAnimatedStickers
animationNode.isHidden = !item.playAnimatedStickers
strongSelf.imageNode.isHidden = item.playAnimatedStickers
if let animationNode = strongSelf.animationNode {
transition.updateFrame(node: animationNode, frame: imageFrame)
}
}
}

View File

@ -307,19 +307,46 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
let bytesPerRowUV = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1)
let bytesPerRowA = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 2)
var base = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0)!
var requiresAlphaMultiplication = false
var base: UnsafeMutableRawPointer
if case .YUVA = frame.pixelFormat {
requiresAlphaMultiplication = true
base = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 2)!
if bytesPerRowA == frame.lineSize[3] {
memcpy(base, frame.data[3]!, bytesPerRowA * Int(frame.height))
} else {
var dest = base
var src = frame.data[3]!
let lineSize = Int(frame.lineSize[3])
for _ in 0 ..< Int(frame.height) {
memcpy(dest, src, lineSize)
dest = dest.advanced(by: bytesPerRowA)
src = src.advanced(by: lineSize)
}
}
}
base = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0)!
if bytesPerRowY == frame.lineSize[0] {
memcpy(base, frame.data[0]!, bytesPerRowY * Int(frame.height))
} else {
var dest = base
var src = frame.data[0]!
let linesize = Int(frame.lineSize[0])
let lineSize = Int(frame.lineSize[0])
for _ in 0 ..< Int(frame.height) {
memcpy(dest, src, linesize)
memcpy(dest, src, lineSize)
dest = dest.advanced(by: bytesPerRowY)
src = src.advanced(by: linesize)
src = src.advanced(by: lineSize)
}
}
if requiresAlphaMultiplication {
var y = vImage_Buffer(data: CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0)!, height: vImagePixelCount(frame.height), width: vImagePixelCount(bytesPerRowY), rowBytes: bytesPerRowY)
var a = vImage_Buffer(data: CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 2)!, height: vImagePixelCount(frame.height), width: vImagePixelCount(bytesPerRowY), rowBytes: bytesPerRowA)
let _ = vImagePremultiplyData_Planar8(&y, &a, &y, vImage_Flags(kvImageDoNotTile))
}
base = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1)!
if bytesPerRowUV == frame.lineSize[1] * 2 {
@ -327,27 +354,11 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
} else {
var dest = base
var src = dstPlane
let linesize = Int(frame.lineSize[1]) * 2
let lineSize = Int(frame.lineSize[1]) * 2
for _ in 0 ..< Int(frame.height / 2) {
memcpy(dest, src, linesize)
memcpy(dest, src, lineSize)
dest = dest.advanced(by: bytesPerRowUV)
src = src.advanced(by: linesize)
}
}
if case .YUVA = frame.pixelFormat {
base = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 2)!
if bytesPerRowA == frame.lineSize[3] {
memcpy(base, frame.data[3]!, bytesPerRowA * Int(frame.height))
} else {
var dest = base
var src = frame.data[3]!
let linesize = Int(frame.lineSize[3])
for _ in 0 ..< Int(frame.height) {
memcpy(dest, src, linesize)
dest = dest.advanced(by: bytesPerRowA)
src = src.advanced(by: linesize)
}
src = src.advanced(by: lineSize)
}
}

View File

@ -24,6 +24,9 @@ public struct StickerPackCollectionInfoFlags: OptionSet {
if flags.contains(StickerPackCollectionInfoFlags.isAnimated) {
rawValue |= StickerPackCollectionInfoFlags.isAnimated.rawValue
}
if flags.contains(StickerPackCollectionInfoFlags.isVideo) {
rawValue |= StickerPackCollectionInfoFlags.isVideo.rawValue
}
self.rawValue = rawValue
}
@ -31,6 +34,7 @@ public struct StickerPackCollectionInfoFlags: OptionSet {
public static let isMasks = StickerPackCollectionInfoFlags(rawValue: 1 << 0)
public static let isOfficial = StickerPackCollectionInfoFlags(rawValue: 1 << 1)
public static let isAnimated = StickerPackCollectionInfoFlags(rawValue: 1 << 2)
public static let isVideo = StickerPackCollectionInfoFlags(rawValue: 1 << 3)
}

View File

@ -43,6 +43,9 @@ extension StickerPackCollectionInfo {
if (flags & (1 << 5)) != 0 {
setFlags.insert(.isAnimated)
}
if (flags & (1 << 6)) != 0 {
setFlags.insert(.isVideo)
}
var thumbnailRepresentation: TelegramMediaImageRepresentation?
var immediateThumbnailData: Data?

View File

@ -84,7 +84,7 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
private let scalingNode: ASDisplayNode
private let imageNode: TransformImageNode
private var animatedStickerNode: AnimatedStickerNode?
private var videoStickerNode: VideoStickerNode?
private var videoNode: VideoStickerNode?
private var placeholderNode: StickerShimmerEffectNode?
private let highlightNode: ASImageNode
private let titleNode: ImmediateTextNode
@ -109,7 +109,9 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
didSet {
if self.visibilityStatus != oldValue {
let loopAnimatedStickers = self.inputNodeInteraction?.stickerSettings?.loopAnimatedStickers ?? false
self.animatedStickerNode?.visibility = self.visibilityStatus && loopAnimatedStickers
let visibility = self.visibilityStatus && loopAnimatedStickers
self.videoNode?.update(isPlaying: visibility)
self.animatedStickerNode?.visibility = visibility
}
}
}
@ -191,16 +193,15 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
var thumbnailItem: StickerPackThumbnailItem?
var resourceReference: MediaResourceReference?
if let thumbnail = info.thumbnail {
if info.flags.contains(.isAnimated) {
thumbnailItem = .animated(thumbnail.resource, thumbnail.dimensions)
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)
if info.flags.contains(.isAnimated) || info.flags.contains(.isVideo) {
thumbnailItem = .animated(thumbnail.resource, thumbnail.dimensions, info.flags.contains(.isVideo))
} else {
thumbnailItem = .still(thumbnail)
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)
}
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)
} else if let item = item {
if item.file.isAnimatedSticker {
thumbnailItem = .animated(item.file.resource, item.file.dimensions ?? PixelDimensions(width: 100, height: 100))
if item.file.isAnimatedSticker || item.file.isVideoSticker {
thumbnailItem = .animated(item.file.resource, item.file.dimensions ?? PixelDimensions(width: 100, height: 100), item.file.isVideoSticker)
resourceReference = MediaResourceReference.media(media: .standalone(media: item.file), resource: item.file.resource)
} else if let dimensions = item.file.dimensions, let resource = chatMessageStickerResource(file: item.file, small: true) as? TelegramMediaResource {
thumbnailItem = .still(TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource, progressiveSizes: [], immediateThumbnailData: nil))
@ -225,30 +226,54 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
let imageApply = self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(radius: 6.0), imageSize: imageSize, boundingSize: boundingImageSize, intrinsicInsets: UIEdgeInsets()))
imageApply()
self.imageNode.setSignal(chatMessageStickerPackThumbnail(postbox: account.postbox, resource: representation.resource, nilIfEmpty: true))
case let .animated(resource, _):
case let .animated(resource, _, isVideo):
let imageApply = self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: boundingImageSize, intrinsicInsets: UIEdgeInsets()))
imageApply()
self.imageNode.setSignal(chatMessageStickerPackThumbnail(postbox: account.postbox, resource: resource, animated: true, nilIfEmpty: true))
let loopAnimatedStickers = self.inputNodeInteraction?.stickerSettings?.loopAnimatedStickers ?? false
let animatedStickerNode: AnimatedStickerNode
if let current = self.animatedStickerNode {
animatedStickerNode = current
} else {
animatedStickerNode = AnimatedStickerNode()
animatedStickerNode.started = { [weak self] in
self?.imageNode.isHidden = true
}
self.animatedStickerNode = animatedStickerNode
if let placeholderNode = self.placeholderNode {
self.scalingNode.insertSubnode(animatedStickerNode, belowSubnode: placeholderNode)
if isVideo {
let videoNode: VideoStickerNode
if let current = self.videoNode {
videoNode = current
} else {
self.scalingNode.addSubnode(animatedStickerNode)
videoNode = VideoStickerNode()
videoNode.started = { [weak self] in
self?.imageNode.isHidden = true
}
self.videoNode = videoNode
if let placeholderNode = self.placeholderNode {
self.scalingNode.insertSubnode(videoNode, belowSubnode: placeholderNode)
} else {
self.scalingNode.addSubnode(videoNode)
}
if let resource = resource as? TelegramMediaResource {
let dummyFile = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 1), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/webm", size: resource.size ?? 1, attributes: [.Video(duration: 1, size: PixelDimensions(width: 100, height: 100), flags: [])])
videoNode.update(account: account, fileReference: .standalone(media: dummyFile))
}
}
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource), width: 128, height: 128, mode: .cached)
videoNode.update(isPlaying: self.visibilityStatus && loopAnimatedStickers)
} else {
let animatedStickerNode: AnimatedStickerNode
if let current = self.animatedStickerNode {
animatedStickerNode = current
} else {
animatedStickerNode = AnimatedStickerNode()
animatedStickerNode.started = { [weak self] in
self?.imageNode.isHidden = true
}
self.animatedStickerNode = animatedStickerNode
if let placeholderNode = self.placeholderNode {
self.scalingNode.insertSubnode(animatedStickerNode, belowSubnode: placeholderNode)
} else {
self.scalingNode.addSubnode(animatedStickerNode)
}
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource), width: 128, height: 128, mode: .cached)
}
animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers
}
animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers
}
if let resourceReference = resourceReference {
self.stickerFetchedDisposable.set(fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: resourceReference).start())
@ -289,8 +314,9 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
animatedStickerNode.frame = self.imageNode.frame
animatedStickerNode.updateLayout(size: self.imageNode.frame.size)
}
if let videoNode = self.videoStickerNode {
if let videoNode = self.videoNode {
videoNode.frame = self.imageNode.frame
videoNode.updateLayout(size: self.imageNode.frame.size)
}
if let placeholderNode = self.placeholderNode {
placeholderNode.bounds = CGRect(origin: CGPoint(), size: boundingImageSize)
@ -338,7 +364,7 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
}
override func snapshotForReordering() -> UIView? {
if let account = account, let thumbnailItem = self.currentThumbnailItem {
if let account = self.account, let thumbnailItem = self.currentThumbnailItem {
var imageSize = boundingImageSize
let loopAnimatedStickers = self.inputNodeInteraction?.stickerSettings?.loopAnimatedStickers ?? false
let containerNode = ASDisplayNode()
@ -348,6 +374,7 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
var snapshotImageNode: TransformImageNode?
var snapshotAnimationNode: AnimatedStickerNode?
var snapshotVideoNode: VideoStickerNode?
switch thumbnailItem {
case let .still(representation):
imageSize = representation.dimensions.cgSize.aspectFitted(boundingImageSize)
@ -359,16 +386,28 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
scalingNode.addSubnode(imageNode)
snapshotImageNode = imageNode
case let .animated(resource, _):
let animatedStickerNode = AnimatedStickerNode()
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource), width: 128, height: 128, mode: .cached)
animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers
scalingNode.addSubnode(animatedStickerNode)
animatedStickerNode.cloneCurrentFrame(from: self.animatedStickerNode)
animatedStickerNode.play(fromIndex: self.animatedStickerNode?.currentFrameIndex)
snapshotAnimationNode = animatedStickerNode
case let .animated(resource, _, isVideo):
if isVideo {
let videoNode = VideoStickerNode()
if let resource = resource as? TelegramMediaResource {
let dummyFile = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 1), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/webm", size: resource.size ?? 1, attributes: [.Video(duration: 1, size: PixelDimensions(width: 100, height: 100), flags: [])])
videoNode.update(account: account, fileReference: .standalone(media: dummyFile))
}
videoNode.update(isPlaying: self.visibilityStatus && loopAnimatedStickers)
scalingNode.addSubnode(videoNode)
snapshotVideoNode = videoNode
} else {
let animatedStickerNode = AnimatedStickerNode()
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource), width: 128, height: 128, mode: .cached)
animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers
scalingNode.addSubnode(animatedStickerNode)
animatedStickerNode.cloneCurrentFrame(from: self.animatedStickerNode)
animatedStickerNode.play(fromIndex: self.animatedStickerNode?.currentFrameIndex)
snapshotAnimationNode = animatedStickerNode
}
}
containerNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: expandedBoundingSize)
@ -388,6 +427,10 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
animatedStickerNode.frame = imageFrame
animatedStickerNode.updateLayout(size: imageFrame.size)
}
if let videoStickerNode = snapshotVideoNode {
videoStickerNode.frame = imageFrame
videoStickerNode.updateLayout(size: imageFrame.size)
}
let expanded = self.currentExpanded
let scale = expanded ? 1.0 : boundingImageScale

View File

@ -1733,7 +1733,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, self, avatarNode.frame)
}
}
item.controllerInteraction.openPeer(openPeerId, navigate, item.message)
item.controllerInteraction.openPeer(openPeerId, navigate, MessageReference(item.message), item.message.peers[openPeerId])
}
})
}

View File

@ -12,6 +12,7 @@ import AnimatedStickerNode
import TelegramAnimatedStickerNode
import ShimmerEffect
import MergeLists
import SoftwareVideo
private let boundingSize = CGSize(width: 41.0, height: 41.0)
private let boundingImageSize = CGSize(width: 28.0, height: 28.0)
@ -154,6 +155,7 @@ private final class FeaturedPackItemNode: ListViewItemNode {
private let containerNode: ASDisplayNode
private let imageNode: TransformImageNode
private var animatedStickerNode: AnimatedStickerNode?
private var videoNode: VideoStickerNode?
private var placeholderNode: StickerShimmerEffectNode?
private let unreadNode: ASImageNode
@ -255,16 +257,16 @@ private final class FeaturedPackItemNode: ListViewItemNode {
var thumbnailItem: StickerPackThumbnailItem?
var resourceReference: MediaResourceReference?
if let thumbnail = info.thumbnail {
if info.flags.contains(.isAnimated) {
thumbnailItem = .animated(thumbnail.resource, thumbnail.dimensions)
if info.flags.contains(.isAnimated) || info.flags.contains(.isVideo) {
thumbnailItem = .animated(thumbnail.resource, thumbnail.dimensions, info.flags.contains(.isVideo))
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)
} else {
thumbnailItem = .still(thumbnail)
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)
}
} else if let item = item {
if item.file.isAnimatedSticker {
thumbnailItem = .animated(item.file.resource, item.file.dimensions ?? PixelDimensions(width: 100, height: 100))
if item.file.isAnimatedSticker || item.file.isVideoSticker {
thumbnailItem = .animated(item.file.resource, item.file.dimensions ?? PixelDimensions(width: 100, height: 100), item.file.isVideoSticker)
resourceReference = MediaResourceReference.media(media: .standalone(media: item.file), resource: item.file.resource)
} else if let dimensions = item.file.dimensions, let resource = chatMessageStickerResource(file: item.file, small: true) as? TelegramMediaResource {
thumbnailItem = .still(TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource, progressiveSizes: [], immediateThumbnailData: nil))
@ -284,30 +286,54 @@ private final class FeaturedPackItemNode: ListViewItemNode {
let imageApply = self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))
imageApply()
self.imageNode.setSignal(chatMessageStickerPackThumbnail(postbox: account.postbox, resource: representation.resource, nilIfEmpty: true))
case let .animated(resource, _):
case let .animated(resource, _, isVideo):
let imageApply = self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))
imageApply()
self.imageNode.setSignal(chatMessageStickerPackThumbnail(postbox: account.postbox, resource: resource, animated: true, nilIfEmpty: true))
let loopAnimatedStickers = self.inputNodeInteraction?.stickerSettings?.loopAnimatedStickers ?? false
let animatedStickerNode: AnimatedStickerNode
if let current = self.animatedStickerNode {
animatedStickerNode = current
} else {
animatedStickerNode = AnimatedStickerNode()
animatedStickerNode.started = { [weak self] in
self?.imageNode.isHidden = true
}
self.animatedStickerNode = animatedStickerNode
if let placeholderNode = self.placeholderNode {
self.containerNode.insertSubnode(animatedStickerNode, belowSubnode: placeholderNode)
if isVideo {
let videoNode: VideoStickerNode
if let current = self.videoNode {
videoNode = current
} else {
self.containerNode.addSubnode(animatedStickerNode)
videoNode = VideoStickerNode()
videoNode.started = { [weak self] in
self?.imageNode.isHidden = true
}
self.videoNode = videoNode
if let placeholderNode = self.placeholderNode {
self.containerNode.insertSubnode(videoNode, belowSubnode: placeholderNode)
} else {
self.containerNode.addSubnode(videoNode)
}
if let resource = resource as? TelegramMediaResource {
let dummyFile = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 1), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/webm", size: resource.size ?? 1, attributes: [.Video(duration: 1, size: PixelDimensions(width: 100, height: 100), flags: [])])
videoNode.update(account: account, fileReference: .standalone(media: dummyFile))
}
}
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource), width: 128, height: 128, mode: .cached)
videoNode.update(isPlaying: self.visibilityStatus && loopAnimatedStickers)
} else {
let animatedStickerNode: AnimatedStickerNode
if let current = self.animatedStickerNode {
animatedStickerNode = current
} else {
animatedStickerNode = AnimatedStickerNode()
animatedStickerNode.started = { [weak self] in
self?.imageNode.isHidden = true
}
self.animatedStickerNode = animatedStickerNode
if let placeholderNode = self.placeholderNode {
self.containerNode.insertSubnode(animatedStickerNode, belowSubnode: placeholderNode)
} else {
self.containerNode.addSubnode(animatedStickerNode)
}
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource), width: 128, height: 128, mode: .cached)
}
animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers
}
animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers
}
if let resourceReference = resourceReference {
self.stickerFetchedDisposable.set(fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: resourceReference).start())
@ -328,6 +354,10 @@ private final class FeaturedPackItemNode: ListViewItemNode {
animatedStickerNode.frame = self.imageNode.frame
animatedStickerNode.updateLayout(size: self.imageNode.frame.size)
}
if let videoStickerNode = self.videoNode {
videoStickerNode.frame = self.imageNode.frame
videoStickerNode.updateLayout(size: self.imageNode.frame.size)
}
if let placeholderNode = self.placeholderNode {
placeholderNode.bounds = CGRect(origin: CGPoint(), size: boundingImageSize)
placeholderNode.position = self.imageNode.position