mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
153 lines
5.3 KiB
Swift
153 lines
5.3 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import CoreMedia
|
|
import FFMpegBinding
|
|
import ImageDCT
|
|
import Accelerate
|
|
|
|
final class MediaEditorVideoFFMpegWriter: MediaEditorVideoExportWriter {
|
|
public static let registerFFMpegGlobals: Void = {
|
|
FFMpegGlobals.initializeGlobals()
|
|
return
|
|
}()
|
|
|
|
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
|
|
|
|
let width = Int32(configuration.dimensions.width)
|
|
let height = Int32(configuration.dimensions.height)
|
|
|
|
let bufferOptions: [String: Any] = [
|
|
kCVPixelBufferPoolMinimumBufferCountKey as String: 3 as NSNumber
|
|
]
|
|
let pixelBufferOptions: [String: Any] = [
|
|
kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA as NSNumber,
|
|
kCVPixelBufferWidthKey as String: UInt32(width),
|
|
kCVPixelBufferHeightKey as String: UInt32(height)
|
|
]
|
|
|
|
var pool: CVPixelBufferPool?
|
|
CVPixelBufferPoolCreate(nil, bufferOptions as CFDictionary, pixelBufferOptions as CFDictionary, &pool)
|
|
guard let pool else {
|
|
self.status = .failed
|
|
return
|
|
}
|
|
self.pool = pool
|
|
|
|
if !self.ffmpegWriter.setup(withOutputPath: outputPath, width: width, height: height, bitrate: 240 * 1000, framerate: 30) {
|
|
self.status = .failed
|
|
}
|
|
}
|
|
|
|
func setupVideoInput(configuration: MediaEditorVideoExport.Configuration, preferredTransform: CGAffineTransform?, sourceFrameRate: Float) {
|
|
|
|
}
|
|
|
|
func setupAudioInput(configuration: MediaEditorVideoExport.Configuration) {
|
|
|
|
}
|
|
|
|
func startWriting() -> Bool {
|
|
if self.status != .failed {
|
|
self.status = .writing
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
|
|
func startSession(atSourceTime time: CMTime) {
|
|
|
|
}
|
|
|
|
func finishWriting(completion: @escaping () -> Void) {
|
|
self.ffmpegWriter.finalizeVideo()
|
|
self.status = .completed
|
|
completion()
|
|
}
|
|
|
|
func cancelWriting() {
|
|
|
|
}
|
|
|
|
func requestVideoDataWhenReady(on queue: DispatchQueue, using block: @escaping () -> Void) {
|
|
queue.async {
|
|
block()
|
|
}
|
|
}
|
|
|
|
func requestAudioDataWhenReady(on queue: DispatchQueue, using block: @escaping () -> Void) {
|
|
|
|
}
|
|
|
|
var isReadyForMoreVideoData: Bool {
|
|
return true
|
|
}
|
|
|
|
func appendVideoBuffer(_ buffer: CMSampleBuffer) -> Bool {
|
|
return false
|
|
}
|
|
|
|
func appendPixelBuffer(_ buffer: CVPixelBuffer, at time: CMTime) -> Bool {
|
|
let width = CVPixelBufferGetWidth(buffer)
|
|
let height = CVPixelBufferGetHeight(buffer)
|
|
let bytesPerRow = CVPixelBufferGetBytesPerRow(buffer)
|
|
|
|
let frame = FFMpegAVFrame(pixelFormat: .YUVA, width: Int32(width), height: Int32(height))
|
|
|
|
CVPixelBufferLockBaseAddress(buffer, CVPixelBufferLockFlags.readOnly)
|
|
let src = CVPixelBufferGetBaseAddress(buffer)
|
|
|
|
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)
|
|
}
|
|
|
|
func markVideoAsFinished() {
|
|
|
|
}
|
|
|
|
var pixelBufferPool: CVPixelBufferPool? {
|
|
return self.pool
|
|
}
|
|
|
|
var isReadyForMoreAudioData: Bool {
|
|
return false
|
|
}
|
|
|
|
func appendAudioBuffer(_ buffer: CMSampleBuffer) -> Bool {
|
|
return false
|
|
}
|
|
|
|
func markAudioAsFinished() {
|
|
|
|
}
|
|
|
|
var status: ExportWriterStatus = .unknown
|
|
|
|
var error: Error?
|
|
}
|