我正在使用AVAssetWriter压缩视频。如果我将视频压缩文件设置为Quicktime影片它工作正常,但我想将其导出到MPEG4,但它运行时给我这个错误:
为了执行passthrough到文件类型public.mpeg-4,请在AVAssetWriterInput初始化程序中提供格式提示
这是我声明文件类型的具体代码:
let videoInputQueue = DispatchQueue(label: "videoQueue")
let audioInputQueue = DispatchQueue(label: "audioQueue")
let formatter = DateFormatter()
formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"
let date = Date()
let documentsPath = NSTemporaryDirectory()
let outputPath = "\(documentsPath)/\(formatter.string(from: date)).mp4"
let newOutputUrl = URL(fileURLWithPath: outputPath)
do{
assetWriter = try AVAssetWriter(outputURL: newOutputUrl, fileType: AVFileTypeMPEG4)
}catch{
assetWriter = nil
}
guard let writer = assetWriter else{
fatalError("assetWriter was nil")
}
writer.shouldOptimizeForNetworkUse = true
writer.add(videoInput)
writer.add(audioInput)
这是我的压缩的完整代码:
func compressFile(urlToCompress: URL, completion:@escaping (URL)->Void){
//video file to make the asset
var audioFinished = false
var videoFinished = false
let asset = AVAsset(url: urlToCompress)
//create asset reader
do{
assetReader = try AVAssetReader(asset: asset)
} catch{
assetReader = nil
}
guard let reader = assetReader else{
fatalError("Could not initalize asset reader probably failed its try catch")
}
let videoTrack = asset.tracks(withMediaType: AVMediaTypeVideo).first!
let audioTrack = asset.tracks(withMediaType: AVMediaTypeAudio).first!
let videoReaderSettings: [String:Any] = [kCVPixelBufferPixelFormatTypeKey as String!:kCVPixelFormatType_32ARGB ]
// ADJUST BIT RATE OF VIDEO HERE
let videoSettings:[String:Any] = [
AVVideoCompressionPropertiesKey: [AVVideoAverageBitRateKey:self.bitrate],
AVVideoCodecKey: AVVideoCodecH264,
AVVideoHeightKey: videoTrack.naturalSize.height,
AVVideoWidthKey: videoTrack.naturalSize.width
]
let assetReaderVideoOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: videoReaderSettings)
let assetReaderAudioOutput = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: nil)
if reader.canAdd(assetReaderVideoOutput){
reader.add(assetReaderVideoOutput)
}else{
fatalError("Couldn't add video output reader")
}
if reader.canAdd(assetReaderAudioOutput){
reader.add(assetReaderAudioOutput)
}else{
fatalError("Couldn't add audio output reader")
}
let audioInput = AVAssetWriterInput(mediaType: AVMediaTypeAudio, outputSettings: nil)
let videoInput = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: videoSettings)
videoInput.transform = videoTrack.preferredTransform
//we need to add samples to the video input
let videoInputQueue = DispatchQueue(label: "videoQueue")
let audioInputQueue = DispatchQueue(label: "audioQueue")
let formatter = DateFormatter()
formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"
let date = Date()
let documentsPath = NSTemporaryDirectory()
let outputPath = "\(documentsPath)/\(formatter.string(from: date)).mp4"
let newOutputUrl = URL(fileURLWithPath: outputPath)
do{
assetWriter = try AVAssetWriter(outputURL: newOutputUrl, fileType: AVFileTypeMPEG4)
}catch{
assetWriter = nil
}
guard let writer = assetWriter else{
fatalError("assetWriter was nil")
}
writer.shouldOptimizeForNetworkUse = true
writer.add(videoInput)
writer.add(audioInput)
writer.startWriting()
reader.startReading()
writer.startSession(atSourceTime: kCMTimeZero)
let closeWriter:()->Void = {
if (audioFinished && videoFinished){
self.assetWriter?.finishWriting(completionHandler: {
self.checkFileSize(sizeUrl: (self.assetWriter?.outputURL)!, message: "The file size of the compressed file is: ")
completion((self.assetWriter?.outputURL)!)
print("Completed 1")
})
self.assetReader?.cancelReading()
}
}
audioInput.requestMediaDataWhenReady(on: audioInputQueue) {
while(audioInput.isReadyForMoreMediaData){
let sample = assetReaderAudioOutput.copyNextSampleBuffer()
if (sample != nil){
audioInput.append(sample!)
}else{
audioInput.markAsFinished()
DispatchQueue.main.async {
audioFinished = true
closeWriter()
print("Completed 2")
}
break;
}
}
}
videoInput.requestMediaDataWhenReady(on: videoInputQueue) {
//request data here
while(videoInput.isReadyForMoreMediaData){
let sample = assetReaderVideoOutput.copyNextSampleBuffer()
if (sample != nil){
videoInput.append(sample!)
}else{
videoInput.markAsFinished()
DispatchQueue.main.async {
videoFinished = true
print("Completed 3")
closeWriter()
}
break;
}
}
}
}
通过使用AVAssetWriterInput
nil
创建音频outputSettings
,您可以指示您想要传递音频数据。 assetWriterInputWithMediaType:outputSettings:
头文件评论说:
AVAssetWriter仅支持传递一组受限制的媒体类型和子类型。为了将媒体数据传递给AVFileTypeQuickTimeMovie以外的文件,必须使用+ assetWriterInputWithMediaType:outputSettings:sourceFormatHint:而不是此方法提供非NULL格式提示。
需要格式描述,幸运的是,您可以从遇到的样本缓冲区中获取一个:
let formatDesc = CMSampleBufferGetFormatDescription(anAudioSampleBuffer)!
let audioInput = AVAssetWriterInput(mediaType: AVMediaTypeAudio, outputSettings: nil, sourceFormatHint: formatDesc)
所以,如果这很容易做到,为什么AVAssetWriter
不为我们做呢?我猜是因为在你添加了几个AVAssetWriter
s之后,或者(可能?)因为不是所有的CMSampleBuffer
s都有格式描述,它会笨拙地推动CMSampleBuffer
通常的初始化直到某一点。