126 lines
4.7 KiB
Swift
126 lines
4.7 KiB
Swift
//
|
||
// VideoConvertor4.swift
|
||
// tdvideo
|
||
//
|
||
// Created by mac on 2024/2/22.
|
||
//
|
||
|
||
import UIKit
|
||
|
||
|
||
import Foundation
|
||
import AVKit
|
||
import VideoToolbox
|
||
import CoreImage
|
||
import ImageIO
|
||
|
||
class VideoConvertor4 {
|
||
|
||
|
||
///在立体视频中,正在处理的当前帧的左眼视图。
|
||
var leftEyeImage: CVPixelBuffer?
|
||
|
||
///在立体视频中,正在处理的当前帧的右眼视图。
|
||
var rightEyeImage: CVPixelBuffer?
|
||
|
||
|
||
func convertVideo( asset : AVAsset, outputFile: URL, progress: ((Float)->())? = nil ) async throws {
|
||
do {
|
||
try FileManager.default.removeItem(atPath: outputFile.path)
|
||
print("视频文件删除成功")
|
||
} catch {
|
||
print("删除视频文件出错:\(error)")
|
||
}
|
||
|
||
let assetReader = try AVAssetReader(asset: asset)
|
||
|
||
|
||
//检查是否为空间视频 print("该视频不是空间视频或图片")
|
||
let userDataItems = try await asset.loadMetadata(for:.quickTimeMetadata)
|
||
let spacialCharacteristics = userDataItems.filter { $0.identifier?.rawValue == "mdta/com.apple.quicktime.spatial.format-version" }
|
||
if spacialCharacteristics.count == 0 {
|
||
print("不是空间视频")
|
||
}
|
||
|
||
//获取输入视频的方向和大小(用于设置输出方向)
|
||
let (orientation, videoSize) = try await getOrientationAndResolutionSizeForVideo(asset: asset)
|
||
|
||
//输出宽度为宽度的一半
|
||
//我们有两个并排的视频,我们保持长宽比
|
||
let vw:VideoWriter?
|
||
vw = VideoWriter(url: outputFile, width: Int(videoSize.width), height: Int(videoSize.height), orientation: orientation, sessionStartTime: CMTime(value: 1, timescale: 30 ), isRealTime: false, queue: .main)
|
||
|
||
|
||
|
||
//加载音轨
|
||
let output = try await AVAssetReaderTrackOutput(
|
||
track: asset.loadTracks(withMediaType: .video).first!,
|
||
outputSettings: [
|
||
AVVideoDecompressionPropertiesKey: [
|
||
kVTDecompressionPropertyKey_RequestedMVHEVCVideoLayerIDs: [0, 1] as CFArray,
|
||
],
|
||
]
|
||
)
|
||
assetReader.add(output)
|
||
assetReader.startReading()
|
||
|
||
let duration = try await asset.load(.duration)
|
||
|
||
while let nextSampleBuffer = output.copyNextSampleBuffer() {
|
||
|
||
guard let taggedBuffers = nextSampleBuffer.taggedBuffers else { return }
|
||
|
||
let leftEyeBuffer = taggedBuffers.first(where: {
|
||
$0.tags.first(matchingCategory: .stereoView) == .stereoView(.leftEye)
|
||
})?.buffer
|
||
let rightEyeBuffer = taggedBuffers.first(where: {
|
||
$0.tags.first(matchingCategory: .stereoView) == .stereoView(.rightEye)
|
||
})?.buffer
|
||
|
||
|
||
if let leftEyeBuffer,
|
||
let rightEyeBuffer,
|
||
case let .pixelBuffer(leftEyePixelBuffer) = leftEyeBuffer,
|
||
case let .pixelBuffer(rightEyePixelBuffer) = rightEyeBuffer {
|
||
|
||
leftEyeImage = leftEyePixelBuffer
|
||
rightEyeImage = rightEyePixelBuffer
|
||
|
||
let lciImage = CIImage(cvPixelBuffer: leftEyePixelBuffer)
|
||
let rciImage = CIImage(cvPixelBuffer: rightEyePixelBuffer)
|
||
|
||
let compositeFilter = CIFilter(name: "CIScreenBlendMode")!
|
||
compositeFilter.setValue(lciImage, forKey: kCIInputImageKey)
|
||
compositeFilter.setValue(rciImage, forKey: kCIInputBackgroundImageKey)
|
||
let time = CMSampleBufferGetOutputPresentationTimeStamp(nextSampleBuffer)
|
||
_ = vw!.add(image: compositeFilter.outputImage!, presentationTime: time)
|
||
|
||
print( "Added frame at \(time)")
|
||
progress?( Float(time.value)/Float(duration.value))
|
||
try await Task.sleep(nanoseconds: 3_000_000)
|
||
|
||
}
|
||
}
|
||
|
||
print( "status - \(assetReader.status)")
|
||
print( "status - \(assetReader.error?.localizedDescription ?? "None")")
|
||
print( "Finished")
|
||
_ = try await vw!.finish()
|
||
|
||
|
||
}
|
||
|
||
|
||
func getOrientationAndResolutionSizeForVideo(asset:AVAsset) async throws -> (CGAffineTransform, CGSize) {
|
||
guard let track = try await asset.loadTracks(withMediaType: AVMediaType.video).first
|
||
else{throw VideoReaderError.invalidVideo}
|
||
let naturalSize = try await track.load(.naturalSize)
|
||
let naturalTransform = try await track.load(.preferredTransform)
|
||
let size = naturalSize.applying(naturalTransform)
|
||
return (naturalTransform, CGSize(width: abs(size.width), height: abs(size.height)) )
|
||
}
|
||
|
||
|
||
}
|
||
|