Various improvements

This commit is contained in:
Isaac
2025-03-07 17:52:50 +01:00
parent a63a3074bd
commit 383ab9e479
33 changed files with 373 additions and 203 deletions

View File

@@ -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
}