[WIP] Stickers editor

This commit is contained in:
Ilya Laktyushin 2024-04-06 18:08:20 +04:00
parent 698a74b6b6
commit 7c6651db34
7 changed files with 63 additions and 19 deletions

View File

@ -103,6 +103,10 @@ public final class UniversalVideoNode: ASDisplayNode {
public private(set) var ownsContentNode: Bool = false
public var ownsContentNodeUpdated: ((Bool) -> Void)?
public var duration: Double {
return self.content.duration
}
private let _status = Promise<MediaPlayerStatus?>()
public var status: Signal<MediaPlayerStatus?, NoError> {
return self._status.get()

View File

@ -201,6 +201,7 @@ public final class DefaultAnimatedStickerNodeImpl: ASDisplayNode, AnimatedSticke
public var frameUpdated: (Int, Int) -> Void = { _, _ in }
public private(set) var currentFrameIndex: Int = 0
public private(set) var currentFrameCount: Int = 0
public private(set) var currentFrameRate: Int = 0
private var playFromIndex: Int?
public var frameColorUpdated: ((UIColor) -> Void)?
@ -537,6 +538,7 @@ public final class DefaultAnimatedStickerNodeImpl: ASDisplayNode, AnimatedSticke
strongSelf.frameUpdated(frame.index, frame.totalFrames)
strongSelf.currentFrameIndex = frame.index
strongSelf.currentFrameCount = frame.totalFrames
strongSelf.currentFrameRate = frameRate
if frame.isLastFrame {
var stopped = false
@ -652,6 +654,7 @@ public final class DefaultAnimatedStickerNodeImpl: ASDisplayNode, AnimatedSticke
strongSelf.frameUpdated(frame.index, frame.totalFrames)
strongSelf.currentFrameIndex = frame.index
strongSelf.currentFrameCount = frame.totalFrames;
strongSelf.currentFrameRate = frameRate
if frame.isLastFrame {
var stopped = false

View File

@ -86,6 +86,16 @@ public class DrawingStickerEntityView: DrawingEntityView {
var currentSize: CGSize?
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) {
self.imageNode = TransformImageNode()

View File

@ -1637,6 +1637,7 @@ public func recommendedVideoExportConfiguration(values: MediaEditorValues, durat
videoSettings: videoSettings,
audioSettings: audioSettings,
values: values,
frameRate: frameRate
frameRate: frameRate,
preferredDuration: isSticker ? duration: nil
)
}

View File

@ -56,17 +56,20 @@ public final class MediaEditorVideoExport {
public var audioSettings: [String: Any]
public var values: MediaEditorValues
public var frameRate: Float
public var preferredDuration: Double?
public init(
videoSettings: [String: Any],
audioSettings: [String: Any],
values: MediaEditorValues,
frameRate: Float
frameRate: Float,
preferredDuration: Double? = nil
) {
self.videoSettings = videoSettings
self.audioSettings = audioSettings
self.values = values
self.frameRate = frameRate
self.preferredDuration = preferredDuration
}
var isSticker: Bool {
@ -284,7 +287,7 @@ public final class MediaEditorVideoExport {
let duration: CMTime
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 {
if let trimmedDuration = self.configuration.timeRange?.duration {
duration = trimmedDuration

View File

@ -3,6 +3,7 @@ import UIKit
import CoreMedia
import FFMpegBinding
import ImageDCT
import Accelerate
final class MediaEditorVideoFFMpegWriter: MediaEditorVideoExportWriter {
public static let registerFFMpegGlobals: Void = {
@ -12,6 +13,15 @@ final class MediaEditorVideoFFMpegWriter: MediaEditorVideoExportWriter {
let ffmpegWriter = FFMpegVideoWriter()
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) {
let _ = MediaEditorVideoFFMpegWriter.registerFFMpegGlobals
@ -91,28 +101,27 @@ final class MediaEditorVideoFFMpegWriter: MediaEditorVideoExportWriter {
}
func appendPixelBuffer(_ buffer: CVPixelBuffer, at time: CMTime) -> Bool {
let width = Int32(CVPixelBufferGetWidth(buffer))
let height = Int32(CVPixelBufferGetHeight(buffer))
let bytesPerRow = Int32(CVPixelBufferGetBytesPerRow(buffer))
let width = CVPixelBufferGetWidth(buffer)
let height = CVPixelBufferGetHeight(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)
let src = CVPixelBufferGetBaseAddress(buffer)
splitRGBAIntoYUVAPlanes(
src,
frame.data[0],
frame.data[1],
frame.data[2],
frame.data[3],
width,
height,
bytesPerRow,
true,
true
)
var srcBuffer = vImage_Buffer(data: src, height: vImagePixelCount(height), width: vImagePixelCount(width), rowBytes: bytesPerRow)
var yBuffer = vImage_Buffer(data: frame.data[0], height: vImagePixelCount(height), width: vImagePixelCount(width), rowBytes: width)
var uBuffer = vImage_Buffer(data: frame.data[1], height: vImagePixelCount(height / 2), width: vImagePixelCount(width / 2), rowBytes: width / 2)
var vBuffer = vImage_Buffer(data: frame.data[2], height: vImagePixelCount(height / 2), width: vImagePixelCount(width / 2), rowBytes: width / 2)
var aBuffer = vImage_Buffer(data: frame.data[3], height: vImagePixelCount(height), width: vImagePixelCount(width), rowBytes: width)
var outInfo = self.conversionInfo
let _ = vImageConvert_ARGB8888To420Yp8_Cb8_Cr8(&srcBuffer, &yBuffer, &uBuffer, &vBuffer, &outInfo, [ 3, 2, 1, 0 ], vImage_Flags(kvImageDoNotTile))
vImageExtractChannel_ARGB8888(&srcBuffer, &aBuffer, 3, vImage_Flags(kvImageDoNotTile))
CVPixelBufferUnlockBaseAddress(buffer, CVPixelBufferLockFlags.readOnly)
return self.ffmpegWriter.encode(frame)

View File

@ -6847,6 +6847,20 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
if case let .video(video, _) = exportSubject {
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 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)