95 lines
3.8 KiB
Swift
95 lines
3.8 KiB
Swift
//
|
|
// AttachmentViewModel+Compress.swift
|
|
//
|
|
//
|
|
// Created by MainasuK on 2022/11/11.
|
|
//
|
|
|
|
import os.log
|
|
import UIKit
|
|
import AVKit
|
|
import SessionExporter
|
|
import MastodonCore
|
|
|
|
extension AttachmentViewModel {
|
|
func comporessVideo(url: URL) async throws -> URL {
|
|
let urlAsset = AVURLAsset(url: url)
|
|
let exporter = NextLevelSessionExporter(withAsset: urlAsset)
|
|
exporter.outputFileType = .mp4
|
|
|
|
let isLandscape: Bool = {
|
|
guard let track = urlAsset.tracks(withMediaType: .video).first else {
|
|
return true
|
|
}
|
|
|
|
let size = track.naturalSize.applying(track.preferredTransform)
|
|
return abs(size.width) >= abs(size.height)
|
|
}()
|
|
|
|
let outputURL = try FileManager.default.createTemporaryFileURL(
|
|
filename: UUID().uuidString,
|
|
pathExtension: url.pathExtension
|
|
)
|
|
exporter.outputURL = outputURL
|
|
|
|
let compressionDict: [String: Any] = [
|
|
AVVideoAverageBitRateKey: NSNumber(integerLiteral: 3000000), // 3000k
|
|
AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel as String,
|
|
AVVideoAverageNonDroppableFrameRateKey: NSNumber(floatLiteral: 30), // 30 FPS
|
|
]
|
|
exporter.videoOutputConfiguration = [
|
|
AVVideoCodecKey: AVVideoCodecType.h264,
|
|
AVVideoWidthKey: NSNumber(integerLiteral: isLandscape ? 1280 : 720),
|
|
AVVideoHeightKey: NSNumber(integerLiteral: isLandscape ? 720 : 1280),
|
|
AVVideoScalingModeKey: AVVideoScalingModeResizeAspectFill,
|
|
AVVideoCompressionPropertiesKey: compressionDict
|
|
]
|
|
exporter.audioOutputConfiguration = [
|
|
AVFormatIDKey: kAudioFormatMPEG4AAC,
|
|
AVEncoderBitRateKey: NSNumber(integerLiteral: 128000), // 128k
|
|
AVNumberOfChannelsKey: NSNumber(integerLiteral: 2),
|
|
AVSampleRateKey: NSNumber(value: Float(44100))
|
|
]
|
|
|
|
// needs set to LOW priority to prevent priority inverse issue
|
|
let task = Task(priority: .utility) {
|
|
_ = try await exportVideo(by: exporter)
|
|
}
|
|
_ = try await task.value
|
|
|
|
return outputURL
|
|
}
|
|
|
|
private func exportVideo(by exporter: NextLevelSessionExporter) async throws -> URL {
|
|
guard let outputURL = exporter.outputURL else {
|
|
throw AppError.badRequest
|
|
}
|
|
return try await withCheckedThrowingContinuation { continuation in
|
|
exporter.export(progressHandler: { progress in
|
|
DispatchQueue.main.async { [weak self] in
|
|
guard let self = self else { return }
|
|
self.videoCompressProgress = Double(progress)
|
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: export progress: %.2f", ((#file as NSString).lastPathComponent), #line, #function, progress)
|
|
}
|
|
}, completionHandler: { result in
|
|
switch result {
|
|
case .success(let status):
|
|
switch status {
|
|
case .completed:
|
|
print("NextLevelSessionExporter, export completed, \(exporter.outputURL?.description ?? "")")
|
|
continuation.resume(with: .success(outputURL))
|
|
default:
|
|
if Task.isCancelled {
|
|
exporter.cancelExport()
|
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: cancel export", ((#file as NSString).lastPathComponent), #line, #function)
|
|
}
|
|
print("NextLevelSessionExporter, did not complete")
|
|
}
|
|
case .failure(let error):
|
|
continuation.resume(with: .failure(error))
|
|
}
|
|
})
|
|
}
|
|
} // end func
|
|
}
|