mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
[WIP] Stickers editor
This commit is contained in:
parent
698a74b6b6
commit
7c6651db34
@ -103,6 +103,10 @@ public final class UniversalVideoNode: ASDisplayNode {
|
|||||||
public private(set) var ownsContentNode: Bool = false
|
public private(set) var ownsContentNode: Bool = false
|
||||||
public var ownsContentNodeUpdated: ((Bool) -> Void)?
|
public var ownsContentNodeUpdated: ((Bool) -> Void)?
|
||||||
|
|
||||||
|
public var duration: Double {
|
||||||
|
return self.content.duration
|
||||||
|
}
|
||||||
|
|
||||||
private let _status = Promise<MediaPlayerStatus?>()
|
private let _status = Promise<MediaPlayerStatus?>()
|
||||||
public var status: Signal<MediaPlayerStatus?, NoError> {
|
public var status: Signal<MediaPlayerStatus?, NoError> {
|
||||||
return self._status.get()
|
return self._status.get()
|
||||||
|
@ -201,6 +201,7 @@ public final class DefaultAnimatedStickerNodeImpl: ASDisplayNode, AnimatedSticke
|
|||||||
public var frameUpdated: (Int, Int) -> Void = { _, _ in }
|
public var frameUpdated: (Int, Int) -> Void = { _, _ in }
|
||||||
public private(set) var currentFrameIndex: Int = 0
|
public private(set) var currentFrameIndex: Int = 0
|
||||||
public private(set) var currentFrameCount: Int = 0
|
public private(set) var currentFrameCount: Int = 0
|
||||||
|
public private(set) var currentFrameRate: Int = 0
|
||||||
private var playFromIndex: Int?
|
private var playFromIndex: Int?
|
||||||
|
|
||||||
public var frameColorUpdated: ((UIColor) -> Void)?
|
public var frameColorUpdated: ((UIColor) -> Void)?
|
||||||
@ -537,6 +538,7 @@ public final class DefaultAnimatedStickerNodeImpl: ASDisplayNode, AnimatedSticke
|
|||||||
strongSelf.frameUpdated(frame.index, frame.totalFrames)
|
strongSelf.frameUpdated(frame.index, frame.totalFrames)
|
||||||
strongSelf.currentFrameIndex = frame.index
|
strongSelf.currentFrameIndex = frame.index
|
||||||
strongSelf.currentFrameCount = frame.totalFrames
|
strongSelf.currentFrameCount = frame.totalFrames
|
||||||
|
strongSelf.currentFrameRate = frameRate
|
||||||
|
|
||||||
if frame.isLastFrame {
|
if frame.isLastFrame {
|
||||||
var stopped = false
|
var stopped = false
|
||||||
@ -652,6 +654,7 @@ public final class DefaultAnimatedStickerNodeImpl: ASDisplayNode, AnimatedSticke
|
|||||||
strongSelf.frameUpdated(frame.index, frame.totalFrames)
|
strongSelf.frameUpdated(frame.index, frame.totalFrames)
|
||||||
strongSelf.currentFrameIndex = frame.index
|
strongSelf.currentFrameIndex = frame.index
|
||||||
strongSelf.currentFrameCount = frame.totalFrames;
|
strongSelf.currentFrameCount = frame.totalFrames;
|
||||||
|
strongSelf.currentFrameRate = frameRate
|
||||||
|
|
||||||
if frame.isLastFrame {
|
if frame.isLastFrame {
|
||||||
var stopped = false
|
var stopped = false
|
||||||
|
@ -86,6 +86,16 @@ public class DrawingStickerEntityView: DrawingEntityView {
|
|||||||
var currentSize: CGSize?
|
var currentSize: CGSize?
|
||||||
public var updated: () -> Void = {}
|
public var updated: () -> Void = {}
|
||||||
|
|
||||||
|
public var duration: Double? {
|
||||||
|
if let animationNode = self.animationNode, animationNode.currentFrameCount > 1 {
|
||||||
|
return Double(animationNode.currentFrameCount) / Double(animationNode.currentFrameRate)
|
||||||
|
} else if let videoNode = self.videoNode {
|
||||||
|
return videoNode.duration
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init(context: AccountContext, entity: DrawingStickerEntity) {
|
init(context: AccountContext, entity: DrawingStickerEntity) {
|
||||||
self.imageNode = TransformImageNode()
|
self.imageNode = TransformImageNode()
|
||||||
|
|
||||||
|
@ -1637,6 +1637,7 @@ public func recommendedVideoExportConfiguration(values: MediaEditorValues, durat
|
|||||||
videoSettings: videoSettings,
|
videoSettings: videoSettings,
|
||||||
audioSettings: audioSettings,
|
audioSettings: audioSettings,
|
||||||
values: values,
|
values: values,
|
||||||
frameRate: frameRate
|
frameRate: frameRate,
|
||||||
|
preferredDuration: isSticker ? duration: nil
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -56,17 +56,20 @@ public final class MediaEditorVideoExport {
|
|||||||
public var audioSettings: [String: Any]
|
public var audioSettings: [String: Any]
|
||||||
public var values: MediaEditorValues
|
public var values: MediaEditorValues
|
||||||
public var frameRate: Float
|
public var frameRate: Float
|
||||||
|
public var preferredDuration: Double?
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
videoSettings: [String: Any],
|
videoSettings: [String: Any],
|
||||||
audioSettings: [String: Any],
|
audioSettings: [String: Any],
|
||||||
values: MediaEditorValues,
|
values: MediaEditorValues,
|
||||||
frameRate: Float
|
frameRate: Float,
|
||||||
|
preferredDuration: Double? = nil
|
||||||
) {
|
) {
|
||||||
self.videoSettings = videoSettings
|
self.videoSettings = videoSettings
|
||||||
self.audioSettings = audioSettings
|
self.audioSettings = audioSettings
|
||||||
self.values = values
|
self.values = values
|
||||||
self.frameRate = frameRate
|
self.frameRate = frameRate
|
||||||
|
self.preferredDuration = preferredDuration
|
||||||
}
|
}
|
||||||
|
|
||||||
var isSticker: Bool {
|
var isSticker: Bool {
|
||||||
@ -284,7 +287,7 @@ public final class MediaEditorVideoExport {
|
|||||||
|
|
||||||
let duration: CMTime
|
let duration: CMTime
|
||||||
if self.configuration.isSticker {
|
if self.configuration.isSticker {
|
||||||
duration = CMTime(seconds: 3.0, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
|
duration = CMTime(seconds: self.configuration.preferredDuration ?? 3.0, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
|
||||||
} else if let mainAsset {
|
} else if let mainAsset {
|
||||||
if let trimmedDuration = self.configuration.timeRange?.duration {
|
if let trimmedDuration = self.configuration.timeRange?.duration {
|
||||||
duration = trimmedDuration
|
duration = trimmedDuration
|
||||||
|
@ -3,6 +3,7 @@ import UIKit
|
|||||||
import CoreMedia
|
import CoreMedia
|
||||||
import FFMpegBinding
|
import FFMpegBinding
|
||||||
import ImageDCT
|
import ImageDCT
|
||||||
|
import Accelerate
|
||||||
|
|
||||||
final class MediaEditorVideoFFMpegWriter: MediaEditorVideoExportWriter {
|
final class MediaEditorVideoFFMpegWriter: MediaEditorVideoExportWriter {
|
||||||
public static let registerFFMpegGlobals: Void = {
|
public static let registerFFMpegGlobals: Void = {
|
||||||
@ -13,6 +14,15 @@ final class MediaEditorVideoFFMpegWriter: MediaEditorVideoExportWriter {
|
|||||||
let ffmpegWriter = FFMpegVideoWriter()
|
let ffmpegWriter = FFMpegVideoWriter()
|
||||||
var pool: CVPixelBufferPool?
|
var pool: CVPixelBufferPool?
|
||||||
|
|
||||||
|
let conversionInfo: vImage_ARGBToYpCbCr
|
||||||
|
|
||||||
|
init() {
|
||||||
|
var pixelRange = vImage_YpCbCrPixelRange( Yp_bias: 16, CbCr_bias: 128, YpRangeMax: 235, CbCrRangeMax: 240, YpMax: 235, YpMin: 16, CbCrMax: 240, CbCrMin: 16)
|
||||||
|
var conversionInfo = vImage_ARGBToYpCbCr()
|
||||||
|
let _ = vImageConvert_ARGBToYpCbCr_GenerateConversion(kvImage_ARGBToYpCbCrMatrix_ITU_R_709_2, &pixelRange, &conversionInfo, kvImageARGB8888, kvImage420Yp8_Cb8_Cr8, vImage_Flags(kvImageNoFlags))
|
||||||
|
self.conversionInfo = conversionInfo
|
||||||
|
}
|
||||||
|
|
||||||
func setup(configuration: MediaEditorVideoExport.Configuration, outputPath: String) {
|
func setup(configuration: MediaEditorVideoExport.Configuration, outputPath: String) {
|
||||||
let _ = MediaEditorVideoFFMpegWriter.registerFFMpegGlobals
|
let _ = MediaEditorVideoFFMpegWriter.registerFFMpegGlobals
|
||||||
|
|
||||||
@ -91,27 +101,26 @@ final class MediaEditorVideoFFMpegWriter: MediaEditorVideoExportWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func appendPixelBuffer(_ buffer: CVPixelBuffer, at time: CMTime) -> Bool {
|
func appendPixelBuffer(_ buffer: CVPixelBuffer, at time: CMTime) -> Bool {
|
||||||
let width = Int32(CVPixelBufferGetWidth(buffer))
|
let width = CVPixelBufferGetWidth(buffer)
|
||||||
let height = Int32(CVPixelBufferGetHeight(buffer))
|
let height = CVPixelBufferGetHeight(buffer)
|
||||||
let bytesPerRow = Int32(CVPixelBufferGetBytesPerRow(buffer))
|
let bytesPerRow = CVPixelBufferGetBytesPerRow(buffer)
|
||||||
|
|
||||||
let frame = FFMpegAVFrame(pixelFormat: .YUVA, width: width, height: height)
|
let frame = FFMpegAVFrame(pixelFormat: .YUVA, width: Int32(width), height: Int32(height))
|
||||||
|
|
||||||
CVPixelBufferLockBaseAddress(buffer, CVPixelBufferLockFlags.readOnly)
|
CVPixelBufferLockBaseAddress(buffer, CVPixelBufferLockFlags.readOnly)
|
||||||
let src = CVPixelBufferGetBaseAddress(buffer)
|
let src = CVPixelBufferGetBaseAddress(buffer)
|
||||||
|
|
||||||
splitRGBAIntoYUVAPlanes(
|
|
||||||
src,
|
var srcBuffer = vImage_Buffer(data: src, height: vImagePixelCount(height), width: vImagePixelCount(width), rowBytes: bytesPerRow)
|
||||||
frame.data[0],
|
|
||||||
frame.data[1],
|
var yBuffer = vImage_Buffer(data: frame.data[0], height: vImagePixelCount(height), width: vImagePixelCount(width), rowBytes: width)
|
||||||
frame.data[2],
|
var uBuffer = vImage_Buffer(data: frame.data[1], height: vImagePixelCount(height / 2), width: vImagePixelCount(width / 2), rowBytes: width / 2)
|
||||||
frame.data[3],
|
var vBuffer = vImage_Buffer(data: frame.data[2], height: vImagePixelCount(height / 2), width: vImagePixelCount(width / 2), rowBytes: width / 2)
|
||||||
width,
|
var aBuffer = vImage_Buffer(data: frame.data[3], height: vImagePixelCount(height), width: vImagePixelCount(width), rowBytes: width)
|
||||||
height,
|
|
||||||
bytesPerRow,
|
var outInfo = self.conversionInfo
|
||||||
true,
|
let _ = vImageConvert_ARGB8888To420Yp8_Cb8_Cr8(&srcBuffer, &yBuffer, &uBuffer, &vBuffer, &outInfo, [ 3, 2, 1, 0 ], vImage_Flags(kvImageDoNotTile))
|
||||||
true
|
vImageExtractChannel_ARGB8888(&srcBuffer, &aBuffer, 3, vImage_Flags(kvImageDoNotTile))
|
||||||
)
|
|
||||||
|
|
||||||
CVPixelBufferUnlockBaseAddress(buffer, CVPixelBufferLockFlags.readOnly)
|
CVPixelBufferUnlockBaseAddress(buffer, CVPixelBufferLockFlags.readOnly)
|
||||||
|
|
||||||
|
@ -6847,6 +6847,20 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
if case let .video(video, _) = exportSubject {
|
if case let .video(video, _) = exportSubject {
|
||||||
duration = video.duration.seconds
|
duration = video.duration.seconds
|
||||||
}
|
}
|
||||||
|
if isSticker {
|
||||||
|
duration = 3.0
|
||||||
|
var stickerDurations: [Double] = []
|
||||||
|
self.node.entitiesView.eachView { entityView in
|
||||||
|
if let stickerEntityView = entityView as? DrawingStickerEntityView {
|
||||||
|
if let duration = stickerEntityView.duration, duration > 0.0 {
|
||||||
|
stickerDurations.append(duration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !stickerDurations.isEmpty {
|
||||||
|
duration = stickerDurations.max() ?? 3.0
|
||||||
|
}
|
||||||
|
}
|
||||||
let configuration = recommendedVideoExportConfiguration(values: mediaEditor.values, duration: duration, forceFullHd: true, frameRate: 60.0, isSticker: isSticker)
|
let configuration = recommendedVideoExportConfiguration(values: mediaEditor.values, duration: duration, forceFullHd: true, frameRate: 60.0, isSticker: isSticker)
|
||||||
let outputPath = NSTemporaryDirectory() + "\(Int64.random(in: 0 ..< .max)).\(fileExtension)"
|
let outputPath = NSTemporaryDirectory() + "\(Int64.random(in: 0 ..< .max)).\(fileExtension)"
|
||||||
let videoExport = MediaEditorVideoExport(postbox: self.context.account.postbox, subject: exportSubject, configuration: configuration, outputPath: outputPath, textScale: 2.0)
|
let videoExport = MediaEditorVideoExport(postbox: self.context.account.postbox, subject: exportSubject, configuration: configuration, outputPath: outputPath, textScale: 2.0)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user