ffmpeg sync

This commit is contained in:
overtake 2022-01-14 13:55:58 +04:00
parent 7f78d57780
commit d3a399ed6a
13 changed files with 174 additions and 70 deletions

View File

@ -27,6 +27,7 @@ typedef struct FFMpegStreamMetrics {
extern int FFMpegCodecIdH264; extern int FFMpegCodecIdH264;
extern int FFMpegCodecIdHEVC; extern int FFMpegCodecIdHEVC;
extern int FFMpegCodecIdMPEG4; extern int FFMpegCodecIdMPEG4;
extern int FFMpegCodecIdVP9;
@class FFMpegAVCodecContext; @class FFMpegAVCodecContext;
@ -47,6 +48,8 @@ extern int FFMpegCodecIdMPEG4;
- (FFMpegFpsAndTimebase)fpsAndTimebaseForStreamIndex:(int32_t)streamIndex defaultTimeBase:(CMTime)defaultTimeBase; - (FFMpegFpsAndTimebase)fpsAndTimebaseForStreamIndex:(int32_t)streamIndex defaultTimeBase:(CMTime)defaultTimeBase;
- (FFMpegStreamMetrics)metricsForStreamAtIndex:(int32_t)streamIndex; - (FFMpegStreamMetrics)metricsForStreamAtIndex:(int32_t)streamIndex;
- (void)forceVideoCodecId:(int)videoCodecId;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@ -7,6 +7,11 @@ typedef NS_ENUM(NSUInteger, FFMpegAVFrameColorRange) {
FFMpegAVFrameColorRangeFull FFMpegAVFrameColorRangeFull
}; };
typedef NS_ENUM(NSUInteger, FFMpegAVFramePixelFormat) {
FFMpegAVFramePixelFormatYUV,
FFMpegAVFramePixelFormatYUVA
};
@interface FFMpegAVFrame : NSObject @interface FFMpegAVFrame : NSObject
@property (nonatomic, readonly) int32_t width; @property (nonatomic, readonly) int32_t width;
@ -16,6 +21,7 @@ typedef NS_ENUM(NSUInteger, FFMpegAVFrameColorRange) {
@property (nonatomic, readonly) int64_t pts; @property (nonatomic, readonly) int64_t pts;
@property (nonatomic, readonly) int64_t duration; @property (nonatomic, readonly) int64_t duration;
@property (nonatomic, readonly) FFMpegAVFrameColorRange colorRange; @property (nonatomic, readonly) FFMpegAVFrameColorRange colorRange;
@property (nonatomic, readonly) FFMpegAVFramePixelFormat pixelFormat;
- (instancetype)init; - (instancetype)init;

View File

@ -9,6 +9,7 @@
int FFMpegCodecIdH264 = AV_CODEC_ID_H264; int FFMpegCodecIdH264 = AV_CODEC_ID_H264;
int FFMpegCodecIdHEVC = AV_CODEC_ID_HEVC; int FFMpegCodecIdHEVC = AV_CODEC_ID_HEVC;
int FFMpegCodecIdMPEG4 = AV_CODEC_ID_MPEG4; int FFMpegCodecIdMPEG4 = AV_CODEC_ID_MPEG4;
int FFMpegCodecIdVP9 = AV_CODEC_ID_VP9;
@interface FFMpegAVFormatContext () { @interface FFMpegAVFormatContext () {
AVFormatContext *_impl; AVFormatContext *_impl;
@ -144,4 +145,9 @@ int FFMpegCodecIdMPEG4 = AV_CODEC_ID_MPEG4;
return (FFMpegStreamMetrics){ .width = _impl->streams[streamIndex]->codecpar->width, .height = _impl->streams[streamIndex]->codecpar->height, .rotationAngle = rotationAngle, .extradata = _impl->streams[streamIndex]->codecpar->extradata, .extradataSize = _impl->streams[streamIndex]->codecpar->extradata_size }; return (FFMpegStreamMetrics){ .width = _impl->streams[streamIndex]->codecpar->width, .height = _impl->streams[streamIndex]->codecpar->height, .rotationAngle = rotationAngle, .extradata = _impl->streams[streamIndex]->codecpar->extradata, .extradataSize = _impl->streams[streamIndex]->codecpar->extradata_size };
} }
- (void)forceVideoCodecId:(int)videoCodecId {
_impl->video_codec_id = videoCodecId;
_impl->video_codec = avcodec_find_decoder(videoCodecId);
}
@end @end

View File

@ -62,4 +62,13 @@
return _impl; return _impl;
} }
- (FFMpegAVFramePixelFormat)pixelFormat {
switch (_impl->format) {
case AV_PIX_FMT_YUVA420P:
return FFMpegAVFramePixelFormatYUVA;
default:
return FFMpegAVFramePixelFormatYUV;
}
}
@end @end

View File

@ -245,6 +245,17 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
} }
var pixelBufferRef: CVPixelBuffer? var pixelBufferRef: CVPixelBuffer?
let pixelFormat: OSType
switch frame.pixelFormat {
case .YUV:
pixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
case .YUVA:
pixelFormat = kCVPixelFormatType_420YpCbCr8VideoRange_8A_TriPlanar
default:
pixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
}
if let pixelBufferPool = self.pixelBufferPool { if let pixelBufferPool = self.pixelBufferPool {
let auxAttributes: [String: Any] = [kCVPixelBufferPoolAllocationThresholdKey as String: bufferCount as NSNumber]; let auxAttributes: [String: Any] = [kCVPixelBufferPoolAllocationThresholdKey as String: bufferCount as NSNumber];
let err = CVPixelBufferPoolCreatePixelBufferWithAuxAttributes(kCFAllocatorDefault, pixelBufferPool, auxAttributes as CFDictionary, &pixelBufferRef) let err = CVPixelBufferPoolCreatePixelBufferWithAuxAttributes(kCFAllocatorDefault, pixelBufferPool, auxAttributes as CFDictionary, &pixelBufferRef)
@ -262,7 +273,7 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
CVPixelBufferCreate(kCFAllocatorDefault, CVPixelBufferCreate(kCFAllocatorDefault,
Int(frame.width), Int(frame.width),
Int(frame.height), Int(frame.height),
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, pixelFormat,
options as CFDictionary, options as CFDictionary,
&pixelBufferRef) &pixelBufferRef)
} }
@ -273,6 +284,7 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
let srcPlaneSize = Int(frame.lineSize[1]) * Int(frame.height / 2) let srcPlaneSize = Int(frame.lineSize[1]) * Int(frame.height / 2)
let dstPlaneSize = srcPlaneSize * 2 let dstPlaneSize = srcPlaneSize * 2
let dstPlane: UnsafeMutablePointer<UInt8> let dstPlane: UnsafeMutablePointer<UInt8>
if let (existingDstPlane, existingDstPlaneSize) = self.dstPlane, existingDstPlaneSize == dstPlaneSize { if let (existingDstPlane, existingDstPlaneSize) = self.dstPlane, existingDstPlaneSize == dstPlaneSize {
dstPlane = existingDstPlane dstPlane = existingDstPlane
@ -285,25 +297,26 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
} }
fillDstPlane(dstPlane, frame.data[1]!, frame.data[2]!, srcPlaneSize) fillDstPlane(dstPlane, frame.data[1]!, frame.data[2]!, srcPlaneSize)
let status = CVPixelBufferLockBaseAddress(pixelBuffer, []) let status = CVPixelBufferLockBaseAddress(pixelBuffer, [])
if status != kCVReturnSuccess { if status != kCVReturnSuccess {
return nil return nil
} }
let bytePerRowY = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0) let bytesPerRowY = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0)
let bytesPerRowUV = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1) let bytesPerRowUV = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1)
let bytesPerRowA = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 2)
var base = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0)! var base = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0)!
if bytePerRowY == frame.lineSize[0] { if bytesPerRowY == frame.lineSize[0] {
memcpy(base, frame.data[0]!, bytePerRowY * Int(frame.height)) memcpy(base, frame.data[0]!, bytesPerRowY * Int(frame.height))
} else { } else {
var dest = base var dest = base
var src = frame.data[0]! var src = frame.data[0]!
let linesize = Int(frame.lineSize[0]) let linesize = Int(frame.lineSize[0])
for _ in 0 ..< Int(frame.height) { for _ in 0 ..< Int(frame.height) {
memcpy(dest, src, linesize) memcpy(dest, src, linesize)
dest = dest.advanced(by: bytePerRowY) dest = dest.advanced(by: bytesPerRowY)
src = src.advanced(by: linesize) src = src.advanced(by: linesize)
} }
} }
@ -322,6 +335,22 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
} }
} }
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)
}
}
}
CVPixelBufferUnlockBaseAddress(pixelBuffer, []) CVPixelBufferUnlockBaseAddress(pixelBuffer, [])
var formatRef: CMVideoFormatDescription? var formatRef: CMVideoFormatDescription?

View File

@ -1,5 +1,9 @@
import Foundation import Foundation
#if !os(macOS)
import UIKit import UIKit
#else
import AppKit
#endif
import CoreMedia import CoreMedia
import SwiftSignalKit import SwiftSignalKit
import FFMpegBinding import FFMpegBinding
@ -57,7 +61,7 @@ public final class SoftwareVideoSource {
private var enqueuedFrames: [(MediaTrackFrame, CGFloat, CGFloat, Bool)] = [] private var enqueuedFrames: [(MediaTrackFrame, CGFloat, CGFloat, Bool)] = []
private var hasReadToEnd: Bool = false private var hasReadToEnd: Bool = false
public init(path: String) { public init(path: String, hintVP9: Bool) {
let _ = FFMpegMediaFrameSourceContextHelpers.registerFFMpegGlobals let _ = FFMpegMediaFrameSourceContextHelpers.registerFFMpegGlobals
var s = stat() var s = stat()
@ -74,7 +78,9 @@ public final class SoftwareVideoSource {
self.path = path self.path = path
let avFormatContext = FFMpegAVFormatContext() let avFormatContext = FFMpegAVFormatContext()
if hintVP9 {
avFormatContext.forceVideoCodecId(FFMpegCodecIdVP9)
}
let ioBufferSize = 64 * 1024 let ioBufferSize = 64 * 1024
let avIoContext = FFMpegAVIOContext(bufferSize: Int32(ioBufferSize), opaqueContext: Unmanaged.passUnretained(self).toOpaque(), readPacket: readPacketCallback, writePacket: nil, seek: seekCallback) let avIoContext = FFMpegAVIOContext(bufferSize: Int32(ioBufferSize), opaqueContext: Unmanaged.passUnretained(self).toOpaque(), readPacket: readPacketCallback, writePacket: nil, seek: seekCallback)

View File

@ -1,5 +1,9 @@
import Foundation import Foundation
#if !os(macOS)
import UIKit import UIKit
#else
import AppKit
#endif
import SwiftSignalKit import SwiftSignalKit
import Postbox import Postbox
import TelegramCore import TelegramCore
@ -111,7 +115,7 @@ private final class UniversalSoftwareVideoSourceImpl {
fileprivate var currentNumberOfReads: Int = 0 fileprivate var currentNumberOfReads: Int = 0
fileprivate var currentReadBytes: Int = 0 fileprivate var currentReadBytes: Int = 0
init?(mediaBox: MediaBox, fileReference: FileMediaReference, state: ValuePromise<UniversalSoftwareVideoSourceState>, cancelInitialization: Signal<Bool, NoError>, automaticallyFetchHeader: Bool) { init?(mediaBox: MediaBox, fileReference: FileMediaReference, state: ValuePromise<UniversalSoftwareVideoSourceState>, cancelInitialization: Signal<Bool, NoError>, automaticallyFetchHeader: Bool, hintVP9: Bool = false) {
guard let size = fileReference.media.size else { guard let size = fileReference.media.size else {
return nil return nil
} }
@ -134,6 +138,9 @@ private final class UniversalSoftwareVideoSourceImpl {
self.avIoContext = avIoContext self.avIoContext = avIoContext
let avFormatContext = FFMpegAVFormatContext() let avFormatContext = FFMpegAVFormatContext()
if hintVP9 {
avFormatContext.forceVideoCodecId(FFMpegCodecIdVP9)
}
avFormatContext.setIO(avIoContext) avFormatContext.setIO(avIoContext)
if !avFormatContext.openInput() { if !avFormatContext.openInput() {
@ -286,19 +293,22 @@ private final class UniversalSoftwareVideoSourceThreadParams: NSObject {
let state: ValuePromise<UniversalSoftwareVideoSourceState> let state: ValuePromise<UniversalSoftwareVideoSourceState>
let cancelInitialization: Signal<Bool, NoError> let cancelInitialization: Signal<Bool, NoError>
let automaticallyFetchHeader: Bool let automaticallyFetchHeader: Bool
let hintVP9: Bool
init( init(
mediaBox: MediaBox, mediaBox: MediaBox,
fileReference: FileMediaReference, fileReference: FileMediaReference,
state: ValuePromise<UniversalSoftwareVideoSourceState>, state: ValuePromise<UniversalSoftwareVideoSourceState>,
cancelInitialization: Signal<Bool, NoError>, cancelInitialization: Signal<Bool, NoError>,
automaticallyFetchHeader: Bool automaticallyFetchHeader: Bool,
hintVP9: Bool
) { ) {
self.mediaBox = mediaBox self.mediaBox = mediaBox
self.fileReference = fileReference self.fileReference = fileReference
self.state = state self.state = state
self.cancelInitialization = cancelInitialization self.cancelInitialization = cancelInitialization
self.automaticallyFetchHeader = automaticallyFetchHeader self.automaticallyFetchHeader = automaticallyFetchHeader
self.hintVP9 = hintVP9
} }
} }
@ -381,8 +391,8 @@ public final class UniversalSoftwareVideoSource {
} }
} }
public init(mediaBox: MediaBox, fileReference: FileMediaReference, automaticallyFetchHeader: Bool = false) { public init(mediaBox: MediaBox, fileReference: FileMediaReference, automaticallyFetchHeader: Bool = false, hintVP9: Bool = false) {
self.thread = Thread(target: UniversalSoftwareVideoSourceThread.self, selector: #selector(UniversalSoftwareVideoSourceThread.entryPoint(_:)), object: UniversalSoftwareVideoSourceThreadParams(mediaBox: mediaBox, fileReference: fileReference, state: self.stateValue, cancelInitialization: self.cancelInitialization.get(), automaticallyFetchHeader: automaticallyFetchHeader)) self.thread = Thread(target: UniversalSoftwareVideoSourceThread.self, selector: #selector(UniversalSoftwareVideoSourceThread.entryPoint(_:)), object: UniversalSoftwareVideoSourceThreadParams(mediaBox: mediaBox, fileReference: fileReference, state: self.stateValue, cancelInitialization: self.cancelInitialization.get(), automaticallyFetchHeader: automaticallyFetchHeader, hintVP9: hintVP9))
self.thread.name = "UniversalSoftwareVideoSource" self.thread.name = "UniversalSoftwareVideoSource"
self.thread.start() self.thread.start()
} }

View File

@ -32,7 +32,7 @@ private let inlineBotPrefixFont = Font.regular(14.0)
private let inlineBotNameFont = nameFont private let inlineBotNameFont = nameFont
protocol GenericAnimatedStickerNode: ASDisplayNode { protocol GenericAnimatedStickerNode: ASDisplayNode {
func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool) func setOverlayColor(_ color: UIColor?, animated: Bool)
var currentFrameIndex: Int { get } var currentFrameIndex: Int { get }
func setFrameIndex(_ frameIndex: Int) func setFrameIndex(_ frameIndex: Int)
@ -58,7 +58,7 @@ private class VideoStickerNode: ASDisplayNode, GenericAnimatedStickerNode {
private var layerHolder: SampleBufferLayer? private var layerHolder: SampleBufferLayer?
var manager: SoftwareVideoLayerFrameManager? var manager: SoftwareVideoLayerFrameManager?
func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool) { func setOverlayColor(_ color: UIColor?, animated: Bool) {
} }
@ -367,15 +367,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
if let shareButtonNode = strongSelf.shareButtonNode, shareButtonNode.frame.contains(point) { if let shareButtonNode = strongSelf.shareButtonNode, shareButtonNode.frame.contains(point) {
return .fail return .fail
} }
if let reactionButtonsNode = strongSelf.reactionButtonsNode {
if let _ = reactionButtonsNode.hitTest(strongSelf.view.convert(point, to: reactionButtonsNode.view), with: nil) {
return .fail
}
}
if strongSelf.telegramFile == nil { if false, strongSelf.telegramFile == nil {
if let animationNode = strongSelf.animationNode, animationNode.frame.contains(point) { if let animationNode = strongSelf.animationNode, animationNode.frame.contains(point) {
return .waitForSingleTap return .waitForDoubleTap
} }
} }
} }
@ -898,7 +893,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
} }
} }
if item.associatedData.isCopyProtectionEnabled || item.message.isCopyProtected() { if item.associatedData.isCopyProtectionEnabled {
needsShareButton = false needsShareButton = false
} }
} }
@ -945,8 +940,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
var dateReplies = 0 var dateReplies = 0
let dateReactionsAndPeers = mergedMessageReactionsAndPeers(message: item.message) let dateReactionsAndPeers = mergedMessageReactionsAndPeers(message: item.message)
for attribute in item.message.attributes { for attribute in item.message.attributes {
if let attribute = attribute as? EditedMessageAttribute, isEmoji { if let _ = attribute as? EditedMessageAttribute, isEmoji {
edited = !attribute.isHidden edited = true
} else if let attribute = attribute as? ViewCountMessageAttribute { } else if let attribute = attribute as? ViewCountMessageAttribute {
viewCount = attribute.count viewCount = attribute.count
} else if let attribute = attribute as? ReplyThreadMessageAttribute, case .peer = item.chatLocation { } else if let attribute = attribute as? ReplyThreadMessageAttribute, case .peer = item.chatLocation {
@ -977,8 +972,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
reactionPeers: dateReactionsAndPeers.peers, reactionPeers: dateReactionsAndPeers.peers,
replyCount: dateReplies, replyCount: dateReplies,
isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread,
hasAutoremove: item.message.isSelfExpiring, hasAutoremove: item.message.isSelfExpiring
canViewReactionList: canViewMessageReactionList(message: item.message)
)) ))
let (dateAndStatusSize, dateAndStatusApply) = statusSuggestedWidthAndContinue.1(statusSuggestedWidthAndContinue.0) let (dateAndStatusSize, dateAndStatusApply) = statusSuggestedWidthAndContinue.1(statusSuggestedWidthAndContinue.0)
@ -1111,9 +1105,9 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
let reactions: ReactionsMessageAttribute let reactions: ReactionsMessageAttribute
if shouldDisplayInlineDateReactions(message: item.message) { if shouldDisplayInlineDateReactions(message: item.message) {
reactions = ReactionsMessageAttribute(canViewList: false, reactions: [], recentPeers: []) reactions = ReactionsMessageAttribute(reactions: [], recentPeers: [])
} else { } else {
reactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(canViewList: false, reactions: [], recentPeers: []) reactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(reactions: [], recentPeers: [])
} }
var reactionButtonsFinalize: ((CGFloat) -> (CGSize, (_ animation: ListViewItemUpdateAnimation) -> ChatMessageReactionButtonsNode))? var reactionButtonsFinalize: ((CGFloat) -> (CGSize, (_ animation: ListViewItemUpdateAnimation) -> ChatMessageReactionButtonsNode))?
if !reactions.reactions.isEmpty { if !reactions.reactions.isEmpty {
@ -1144,7 +1138,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
layoutSize.height += actionButtonsSizeAndApply.0.height layoutSize.height += actionButtonsSizeAndApply.0.height
} }
if let reactionButtonsSizeAndApply = reactionButtonsSizeAndApply { if let reactionButtonsSizeAndApply = reactionButtonsSizeAndApply {
layoutSize.height += 4.0 + reactionButtonsSizeAndApply.0.height layoutSize.height += reactionButtonsSizeAndApply.0.height
} }
return (ListViewItemNodeLayout(contentSize: layoutSize, insets: layoutInsets), { [weak self] animation, _, _ in return (ListViewItemNodeLayout(contentSize: layoutSize, insets: layoutInsets), { [weak self] animation, _, _ in
@ -1411,13 +1405,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
if let reactionButtonsSizeAndApply = reactionButtonsSizeAndApply { if let reactionButtonsSizeAndApply = reactionButtonsSizeAndApply {
let reactionButtonsNode = reactionButtonsSizeAndApply.1(animation) let reactionButtonsNode = reactionButtonsSizeAndApply.1(animation)
var reactionButtonsFrame = CGRect(origin: CGPoint(x: imageFrame.minX, y: imageFrame.maxY), size: reactionButtonsSizeAndApply.0) let reactionButtonsFrame = CGRect(origin: CGPoint(x: imageFrame.minX, y: imageFrame.maxY), size: reactionButtonsSizeAndApply.0)
if !incoming {
reactionButtonsFrame.origin.x = imageFrame.maxX - reactionButtonsSizeAndApply.0.width
}
if let actionButtonsSizeAndApply = actionButtonsSizeAndApply {
reactionButtonsFrame.origin.y += 4.0 + actionButtonsSizeAndApply.0.height
}
if reactionButtonsNode !== strongSelf.reactionButtonsNode { if reactionButtonsNode !== strongSelf.reactionButtonsNode {
strongSelf.reactionButtonsNode = reactionButtonsNode strongSelf.reactionButtonsNode = reactionButtonsNode
reactionButtonsNode.reactionSelected = { value in reactionButtonsNode.reactionSelected = { value in
@ -1426,14 +1414,6 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
} }
item.controllerInteraction.updateMessageReaction(item.message, .reaction(value)) item.controllerInteraction.updateMessageReaction(item.message, .reaction(value))
} }
reactionButtonsNode.openReactionPreview = { gesture, sourceNode, value in
guard let strongSelf = self, let item = strongSelf.item else {
gesture?.cancel()
return
}
item.controllerInteraction.openMessageReactionContextMenu(item.message, sourceNode, gesture, value)
}
reactionButtonsNode.frame = reactionButtonsFrame reactionButtonsNode.frame = reactionButtonsFrame
if let (rect, containerSize) = strongSelf.absoluteRect { if let (rect, containerSize) = strongSelf.absoluteRect {
var rect = rect var rect = rect
@ -1516,10 +1496,6 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
} }
} else if case .tap = gesture { } else if case .tap = gesture {
item.controllerInteraction.clickThroughMessage() item.controllerInteraction.clickThroughMessage()
} else if case .doubleTap = gesture {
if canAddMessageReactions(message: item.message) {
item.controllerInteraction.updateMessageReaction(item.message, .default)
}
} }
} }
default: default:
@ -2193,10 +2169,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
if highlighted { if highlighted {
self.imageNode.setOverlayColor(item.presentationData.theme.theme.chat.message.mediaHighlightOverlayColor, animated: false) self.imageNode.setOverlayColor(item.presentationData.theme.theme.chat.message.mediaHighlightOverlayColor, animated: false)
self.animationNode?.setOverlayColor(item.presentationData.theme.theme.chat.message.mediaHighlightOverlayColor, replace: false, animated: false) self.animationNode?.setOverlayColor(item.presentationData.theme.theme.chat.message.mediaHighlightOverlayColor, animated: false)
} else { } else {
self.imageNode.setOverlayColor(nil, animated: animated) self.imageNode.setOverlayColor(nil, animated: animated)
self.animationNode?.setOverlayColor(nil, replace: false, animated: false) self.animationNode?.setOverlayColor(nil, animated: false)
} }
} }
} }
@ -2406,13 +2382,6 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
} }
} }
override func openMessageContextMenu() {
guard let item = self.item else {
return
}
item.controllerInteraction.openMessageContextMenu(item.message, false, self, self.imageNode.frame, nil)
}
override func targetReactionView(value: String) -> UIView? { override func targetReactionView(value: String) -> UIView? {
if let result = self.reactionButtonsNode?.reactionTargetView(value: value) { if let result = self.reactionButtonsNode?.reactionTargetView(value: value) {
return result return result

View File

@ -380,6 +380,10 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
loop: for media in self.message.media { loop: for media in self.message.media {
if let telegramFile = media as? TelegramMediaFile { if let telegramFile = media as? TelegramMediaFile {
if let fileName = telegramFile.fileName, fileName.hasSuffix(".webm") {
viewClassName = ChatMessageAnimatedStickerItemNode.self
break loop
}
if telegramFile.isAnimatedSticker, let size = telegramFile.size, size > 0 && size <= 128 * 1024 { if telegramFile.isAnimatedSticker, let size = telegramFile.size, size > 0 && size <= 128 * 1024 {
if self.message.id.peerId.namespace == Namespaces.Peer.SecretChat { if self.message.id.peerId.namespace == Namespaces.Peer.SecretChat {
if telegramFile.fileId.namespace == Namespaces.Media.CloudFile { if telegramFile.fileId.namespace == Namespaces.Media.CloudFile {

View File

@ -14,6 +14,7 @@ final class SoftwareVideoLayerFrameManager {
private let fetchDisposable: Disposable private let fetchDisposable: Disposable
private var dataDisposable = MetaDisposable() private var dataDisposable = MetaDisposable()
private let source = Atomic<SoftwareVideoSource?>(value: nil) private let source = Atomic<SoftwareVideoSource?>(value: nil)
private let hintVP9: Bool
private var baseTimestamp: Double? private var baseTimestamp: Double?
private var frames: [MediaTrackFrame] = [] private var frames: [MediaTrackFrame] = []
@ -31,7 +32,7 @@ final class SoftwareVideoLayerFrameManager {
private var layerRotationAngleAndAspect: (CGFloat, CGFloat)? private var layerRotationAngleAndAspect: (CGFloat, CGFloat)?
init(account: Account, fileReference: FileMediaReference, layerHolder: SampleBufferLayer) { init(account: Account, fileReference: FileMediaReference, layerHolder: SampleBufferLayer, hintVP9: Bool = false) {
var resource = fileReference.media.resource var resource = fileReference.media.resource
var secondaryResource: MediaResource? var secondaryResource: MediaResource?
for attribute in fileReference.media.attributes { for attribute in fileReference.media.attributes {
@ -46,6 +47,7 @@ final class SoftwareVideoLayerFrameManager {
nextWorker += 1 nextWorker += 1
self.account = account self.account = account
self.resource = resource self.resource = resource
self.hintVP9 = hintVP9
self.secondaryResource = secondaryResource self.secondaryResource = secondaryResource
self.queue = ThreadPoolQueue(threadPool: workers) self.queue = ThreadPoolQueue(threadPool: workers)
self.layerHolder = layerHolder self.layerHolder = layerHolder
@ -108,7 +110,7 @@ final class SoftwareVideoLayerFrameManager {
let size = fileSize(path) let size = fileSize(path)
Logger.shared.log("SoftwareVideo", "loaded video from \(stringForResource(resource)) (file size: \(String(describing: size))") Logger.shared.log("SoftwareVideo", "loaded video from \(stringForResource(resource)) (file size: \(String(describing: size))")
let _ = strongSelf.source.swap(SoftwareVideoSource(path: path)) let _ = strongSelf.source.swap(SoftwareVideoSource(path: path, hintVP9: strongSelf.hintVP9))
} }
})) }))
} }

View File

@ -120,6 +120,24 @@ filegroup(
srcs = source_files, srcs = source_files,
) )
vpx_headers = [
"vp8.h",
"vp8cx.h",
"vp8dx.h",
"vpx_codec.h",
"vpx_decoder.h",
"vpx_encoder.h",
"vpx_frame_buffer.h",
"vpx_image.h",
"vpx_integer.h",
"vpx_version.h",
"vpx_ext_ratectrl.h",
]
vpx_libs = [
"VPX",
]
opus_headers = [ opus_headers = [
"opus.h", "opus.h",
"opus_defines.h", "opus_defines.h",
@ -136,6 +154,10 @@ genrule(
name = "libffmpeg_build", name = "libffmpeg_build",
srcs = [ srcs = [
":FFMpegSources" ":FFMpegSources"
] + [
"//third-party/libvpx:Public/vpx/{}".format(x) for x in vpx_headers
] + [
"//third-party/libvpx:Public/vpx/lib{}.a".format(x) for x in vpx_libs
] + [ ] + [
"//third-party/opus:Public/opus/{}".format(x) for x in opus_headers "//third-party/opus:Public/opus/{}".format(x) for x in opus_headers
] + [ ] + [
@ -153,6 +175,21 @@ genrule(
cp -R "submodules/ffmpeg/Sources/FFMpeg" "$$SOURCE_PATH" cp -R "submodules/ffmpeg/Sources/FFMpeg" "$$SOURCE_PATH"
mkdir "$$SOURCE_PATH/libvpx"
mkdir -p "$$SOURCE_PATH/libvpx/include/vpx"
mkdir -p "$$SOURCE_PATH/libvpx/lib"
""" +
"\n" +
"\n".join([
"cp $(location //third-party/libvpx:Public/vpx/{}) $$SOURCE_PATH/libvpx/include/vpx/".format(x) for x in vpx_headers
]) +
"\n" +
"\n".join([
"cp $(location //third-party/libvpx:Public/vpx/libVPX.a) $$SOURCE_PATH/libvpx/lib/".format(x) for x in vpx_libs
]) +
"\n" +
"""
mkdir "$$SOURCE_PATH/libopus" mkdir "$$SOURCE_PATH/libopus"
mkdir -p "$$SOURCE_PATH/libopus/include/opus" mkdir -p "$$SOURCE_PATH/libopus/include/opus"
mkdir -p "$$SOURCE_PATH/libopus/lib" mkdir -p "$$SOURCE_PATH/libopus/lib"
@ -231,6 +268,7 @@ objc_library(
], ],
deps = [ deps = [
":ffmpeg_lib", ":ffmpeg_lib",
"//third-party/libvpx:vpx",
"//third-party/opus:opus", "//third-party/opus:opus",
], ],
visibility = [ visibility = [

View File

@ -45,9 +45,10 @@ CONFIGURE_FLAGS="--enable-cross-compile --disable-programs \
--enable-avformat \ --enable-avformat \
--disable-xlib \ --disable-xlib \
--enable-libopus \ --enable-libopus \
--enable-libvpx \
--enable-audiotoolbox \ --enable-audiotoolbox \
--enable-bsf=aac_adtstoasc \ --enable-bsf=aac_adtstoasc \
--enable-decoder=h264,hevc,libopus,mp3,aac,flac,alac_at,pcm_s16le,pcm_s24le,gsm_ms_at \ --enable-decoder=h264,libvpx_vp9,hevc,libopus,mp3,aac,flac,alac_at,pcm_s16le,pcm_s24le,gsm_ms_at \
--enable-demuxer=aac,mov,m4v,mp3,ogg,libopus,flac,wav,aiff,matroska \ --enable-demuxer=aac,mov,m4v,mp3,ogg,libopus,flac,wav,aiff,matroska \
--enable-parser=aac,h264,mp3,libopus \ --enable-parser=aac,h264,mp3,libopus \
--enable-protocol=file \ --enable-protocol=file \
@ -122,6 +123,7 @@ then
pushd "$SCRATCH/$RAW_ARCH" pushd "$SCRATCH/$RAW_ARCH"
LIBOPUS_PATH="$SOURCE_DIR/libopus" LIBOPUS_PATH="$SOURCE_DIR/libopus"
LIBVPX_PATH="$SOURCE_DIR/libvpx"
CFLAGS="$EXTRA_CFLAGS -arch $ARCH" CFLAGS="$EXTRA_CFLAGS -arch $ARCH"
if [ "$RAW_ARCH" = "i386" -o "$RAW_ARCH" = "x86_64" ] if [ "$RAW_ARCH" = "i386" -o "$RAW_ARCH" = "x86_64" ]
@ -174,7 +176,7 @@ then
--extra-ldflags="$LDFLAGS" \ --extra-ldflags="$LDFLAGS" \
--prefix="$THIN/$RAW_ARCH" \ --prefix="$THIN/$RAW_ARCH" \
--pkg-config="$PKG_CONFIG" \ --pkg-config="$PKG_CONFIG" \
--pkg-config-flags="--libopus_path $LIBOPUS_PATH" \ --pkg-config-flags="--libopus_path $LIBOPUS_PATH --libvpx_path $LIBVPX_PATH" \
|| exit 1 || exit 1
echo "$CONFIGURE_FLAGS" > "$CONFIGURED_MARKER" echo "$CONFIGURE_FLAGS" > "$CONFIGURED_MARKER"
fi fi

View File

@ -14,6 +14,8 @@ elif [ "$1" == "--exists" ]; then
exit 0 exit 0
elif [ "$NAME" == "opus" ]; then elif [ "$NAME" == "opus" ]; then
exit 0 exit 0
elif [ "$NAME" == "vpx" ]; then
exit 0
else else
if [ "PRINT_ERRORS" == "1" ]; then if [ "PRINT_ERRORS" == "1" ]; then
echo "Package $NAME was not found in the pkg-config search path." echo "Package $NAME was not found in the pkg-config search path."
@ -25,9 +27,15 @@ elif [ "$1" == "--exists" ]; then
elif [ "$1" == "--cflags" ]; then elif [ "$1" == "--cflags" ]; then
NAME="$2" NAME="$2"
LIBOPUS_PATH="" LIBOPUS_PATH=""
LIBVPX_PATH=""
if [ "$2" == "--libopus_path" ]; then if [ "$2" == "--libopus_path" ]; then
LIBOPUS_PATH="$3" LIBOPUS_PATH="$3"
NAME="$4" NAME="$6"
else
exit 1
fi
if [ "$4" == "--libvpx_path" ]; then
LIBVPX_PATH="$5"
else else
exit 1 exit 1
fi fi
@ -37,15 +45,24 @@ elif [ "$1" == "--cflags" ]; then
elif [ "$NAME" == "opus" ]; then elif [ "$NAME" == "opus" ]; then
echo "-I$LIBOPUS_PATH/include/opus" echo "-I$LIBOPUS_PATH/include/opus"
exit 0 exit 0
elif [ "$NAME" == "vpx" ]; then
echo "-I$LIBVPX_PATH/include"
exit 0
else else
exit 1 exit 1
fi fi
elif [ "$1" == "--libs" ]; then elif [ "$1" == "--libs" ]; then
NAME="$2" NAME="$2"
LIBOPUS_PATH="" LIBOPUS_PATH=""
LIBVPX_PATH=""
if [ "$2" == "--libopus_path" ]; then if [ "$2" == "--libopus_path" ]; then
LIBOPUS_PATH="$3" LIBOPUS_PATH="$3"
NAME="$4" NAME="$6"
else
exit 1
fi
if [ "$4" == "--libvpx_path" ]; then
LIBVPX_PATH="$5"
else else
exit 1 exit 1
fi fi
@ -55,6 +72,9 @@ elif [ "$1" == "--libs" ]; then
elif [ "$NAME" == "opus" ]; then elif [ "$NAME" == "opus" ]; then
echo "-L$LIBOPUS_PATH/lib -lopus" echo "-L$LIBOPUS_PATH/lib -lopus"
exit 0 exit 0
elif [ "$NAME" == "vpx" ]; then
echo "-L$LIBVPX_PATH/lib -lVPX"
exit 0
else else
exit 1 exit 1
fi fi