IOS视频压缩Swift iOS 8损坏的视频文件 [英] IOS Video Compression Swift iOS 8 corrupt video file

查看:1072
本文介绍了IOS视频压缩Swift iOS 8损坏的视频文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图压缩从UIImagePickerController(不是现有视频,但一个在飞行中)用户相机拍摄的视频上传到我的服务器,并花费少量的时间这样做,所以更小的大小是理想的



这里是在iOS 8中执行swift压缩的代码,它压缩非常好,我从35 mb下降到2.1 mb容易。

  func convertVideo(inputUrl:NSURL,outputURL:NSURL)
{
// setup video writer b $ b var videoAsset = AVURLAsset(URL:inputUrl,options:nil)as AVAsset

var videoTrack = videoAsset.tracksWithMediaType(AVMediaTypeVideo)[0] as AVAssetTrack

var videoSize = videoTrack.naturalSize

var videoWriterCompressionSettings =字典(dictionaryLiteral:(AVVideoAverageBitRateKey,NSNumber(integer:960000)))

var videoWriterSettings = Dictionary(dictionaryLiteral:(AVVideoCodecKey,AVVideoCodecH264) ,
(AVVideoCompressionPropertiesKey,videoWriterCompressionSettings),
(AVVideoWidthKey,videoSize.width),
(AVVideoHeightKey,videoSize.height))

var videoWriterInput = AVAssetWriterInput AVMediaTypeVideo,outputSettings:videoWriterSettings)

videoWriterInput.expectsMediaDataInRealTime = true

videoWriterInput.transform = videoTrack.preferredTransform


var videoWriter = AVAssetWriter (URL:outputURL,fileType:AVFileTypeQuickTimeMovie,error:nil)

videoWriter.addInput(videoWriterInput)

var videoReaderSettings:[String:AnyObject] = [kCVPixelBufferPixelFormatTypeKey:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange]

var videoReaderOutput = AVAssetReaderTrackOutput(track:videoTrack,outputSettings:videoReaderSettings)

var videoReader = AVAssetReader(asset:videoAsset,error:nil)

videoReader .addOutput(videoReaderOutput)



//设置音频编写器
var audioWriterInput = AVAssetWriterInput(mediaType:AVMediaTypeAudio,outputSettings:nil)
$ b b audioWriterInput.expectsMediaDataInRealTime = false

videoWriter.addInput(audioWriterInput)


//设置音频阅读器

var audioTrack = videoAsset。 tracksWithMediaType(AVMediaTypeAudio)[0] as AVAssetTrack

var audioReaderOutput = AVAssetReaderTrackOutput(track:audioTrack,outputSettings:nil)as AVAssetReaderOutput

var audioReader = AVAssetReader(asset:videoAsset,error :nil)


audioReader.addOutput(audioReaderOutput)

videoWriter.startWriting()


//开始写入from video reader
videoReader.startReading()

videoWriter.startSessionAtSourceTime(kCMTimeZero)

// dispatch_queue_t processingQueue = dispatch_queue_create(processingQueue,nil)

var queue = dispatch_queue_create(processingQueue,nil)

videoWriterInput.requestMediaDataWhenReadyOnQueue(queue,usingBlock:{() - &无效
println(Export starting)

while videoWriterInput.readyForMoreMediaData
{
var sampleBuffer:CMSampleBufferRef!

sampleBuffer = videoReaderOutput.copyNextSampleBuffer()

if(videoReader.status == AVAssetReaderStatus.Reading&& sampleBuffer!= nil)
{
videoWriterInput.appendSampleBuffer(sampleBuffer)

}

else
{
videoWriterInput.markAsFinished()

if videoReader。 status == AVAssetReaderStatus.Completed
{
如果audioReader.status == AVAssetReaderStatus.Reading || audioReader.status == AVAssetReaderStatus.Completed
{

}
else {


audioReader.startReading()

videoWriter.startSessionAtSourceTime(kCMTimeZero)

var queue2 = dispatch_queue_create(processingQueue2,nil)


audioWriterInput.requestMediaDataWhenReadyOnQueue(queue2,usingBlock:{ ) - > Void in

while audioWriterInput.readyForMoreMediaData
{
var sampleBuffer:CMSampleBufferRef!

sampleBuffer = audioReaderOutput.copyNextSampleBuffer()

println(sampleBuffer == nil)

if(audioReader.status == AVAssetReaderStatus.Reading&& sampleBuffer!= nil)
{
audioWriterInput。 appendSampleBuffer(sampleBuffer)

}

else
{
audioWriterInput.markAsFinished()

if(audioReader.status = = AVAssetReaderStatus.Completed)
{

videoWriter.finishWritingWithCompletionHandler({() - >无效

println(已写入视频资源)

self.videoUrl = outputURL

var data = NSData(contentsOfURL:outputURL) !

println(压缩后的字节大小:\(data.length / 1048576)mb)

println(videoAsset.playable)

//Networking().uploadVideo(data,fileName:Test2)

self.dismissViewControllerAnimated(true,completion:nil)

})
break
}
}
}
})
break
}
}
} //第二如果

} // first while

})//第一个块
// return
}

这是我的UIImagePickerController的代码调用compress方法

  func imagePickerController (选择器:UIImagePickerController,didFinishPickingMediaWithInfo info:[NSObject:AnyObject])
{
//从选择中提取媒体类型

let type = info [UIImagePickerControllerMediaType] as String

if(type == kUTTypeMovie)
{

self.videoUrl = info [UIImagePickerControllerMediaURL] as? NSURL

var uploadUrl = NSURL.fileURLWithPath(NSTemporaryDirectory()。stringByAppendingPathComponent(captured)。stringByAppendingString(。mov))

var data = NSData(contentsOfURL: self.videoUrl!)!

println(压缩前大小:\(data.length / 1048576)mb)


self.convertVideo(self.videoUrl !, outputURL: uploadUrl!)

//从信息中获取视频并进行相应设置。

/*self.dismissViewControllerAnimated(true,completion:{() - > Void in


//self.next.enabled = true

})* /
}
}

上面这个工作到文件大小缩减,但当我得到的文件(它仍然是类型.mov)quicktime不能播放它。 Quicktime尝试转换它最初,但中途失败(打开文件后1-2秒)我甚至测试了AVPlayerController中的视频文件,但它没有给出任何有关电影的信息,它只是一个播放按钮没有蚂蚁加载和没有任何长度只是 - 的时间通常在播放器。 IE一个损坏的文件,不会播放。



确定它与写入资产的设置有关,它是视频写作或音频写作我不知道。它甚至可能是导致它被腐败的资产的阅读。我已经尝试改变变量和设置不同的键读取和写入,但我没有找到正确的组合,这很糟糕,我可以压缩,但得到一个损坏的文件。我不确定,任何帮助将不胜感激。 Pleeeeeeeeease。

解决方案

Andrew的回答很好,并且作为我在Swift 2.2中实现它的基础。但是,自从他发布以来,有一些事情发生了变化,所以我把现在使用的那个产生了很好的结果。



你在这里调用方法视频文件的网址和压缩副本的目标网址。



这是我对Swift 3.0的回答。



请参阅下面的2.2,虽然它没有更新以反映3.0版本有改进的风格。

  extension ViewController:AVCaptureFileOutputRecordingDelegate {
func capture(_ captureOutput:AVCaptureFileOutput !, didFinishRecordingToOutputFileAt outputFileURL:URL !, fromConnections connections:[Any] !,, error:Error!){
guard let data = NSData(contentsOf:outputFileURL as URL)else {
return
}

print(压缩之前的文件大小:\(Double(data.length / 1048576)) mb)
let compressedURL = NSURL.fileURL(withPath:NSTemporaryDirectory()+ NSUUID()。uuidString +.m4v)
compressVideo(inputURL:outputFileURL as URL,outputURL:compressedURL){ )in
guard let session = exportSession else {
return
}

switch session.status {
case .unknown:
break
case .waiting:
break
case .exporting:
break
case .completed:
guard let compressedData = NSData(contentsOf:compressedURL)else {
return
}

print(压缩后的文件大小:\(Double(compressedData.length / 1048576))mb)
case .failed:
break
case .cancelled:
break
}
}
}

func compressVideo(inputURL:URL,outputURL:URL, handler:@escaping(_ exportSession:AVAssetExportSession?) - > Void){
let urlAsset = AVURLAsset(url:inputURL,options:nil)
guard let exportSession = AVAssetExportSession(asset:urlAsset,presetName:AVAssetExportPresetMediumQuality)else {
handler(nil)

return
}

exportSession.outputURL = outputURL
exportSession.outputFileType = AVFileTypeQuickTimeMovie
exportSession.shouldOptimizeForNetworkUse = true
exportSession.exportAsynchronously {() - >无效
处理程序(exportSession)
}
}
}


$ b b

回答Swift 2.2

  let compressedURL = NSURL.fileURLWithPath(NSTemporaryDirectory NSUUID()。UUIDString +.m4v)
compressVideo(outputFileURL,outputURL:compressedURL){(session)in
switch session.status {
case .Unknown:
break
case .Waiting:
break
case。导出:
break
case .Completed:
let data = NSData(contentsOfURL:compressedURL)
print(压缩后的文件大小:\(Double(data!.length / 1048576))mb)
case .Failed:
break
case .Cancelled:
break
}
}

这将使用开关处理所有可能的结果声明。我把大部分都留空,因为内容会因应用而有所不同。



对于实际的压缩方法,我实现了一个unwrap并改变了var。

func compressVideo(inputURL:NSURL,outputURL:NSURL,handler:(session:AVAssetExportSession) - > Void){
let urlAsset = AVURLAsset(URL:inputURL,options:nil)
如果let exportSession = AVAssetExportSession(asset:urlAsset,presetName:AVAssetExportPresetMediumQuality){
exportSession.outputURL = outputURL
exportSession.outputFileType = AVFileTypeQuickTimeMovie
exportSession.shouldOptimizeForNetworkUse = true
exportSession.exportAsynchronouslyWithCompletionHandler {() - >无效
处理程序(会话:exportSession)
}
}
}


b $ b

我用一些视频测试了它,并且看到了一些相当大的减少(例如:20 MB成为500k)。


I am trying to compress video taken with the users camera from UIImagePickerController (Not an existing video but one on the fly) to upload to my server and take a small amount of time to do so, so a smaller size is ideal instead of 30-45 mb on newer quality cameras.

Here is the code to do a compression in swift for iOS 8 and it compresses wonderfully, i go from 35 mb down to 2.1 mb easily.

   func convertVideo(inputUrl: NSURL, outputURL: NSURL) 
   {
    //setup video writer
    var videoAsset = AVURLAsset(URL: inputUrl, options: nil) as AVAsset

    var videoTrack = videoAsset.tracksWithMediaType(AVMediaTypeVideo)[0] as AVAssetTrack

    var videoSize = videoTrack.naturalSize

    var videoWriterCompressionSettings = Dictionary(dictionaryLiteral:(AVVideoAverageBitRateKey,NSNumber(integer:960000)))

    var videoWriterSettings = Dictionary(dictionaryLiteral:(AVVideoCodecKey,AVVideoCodecH264),
        (AVVideoCompressionPropertiesKey,videoWriterCompressionSettings),
        (AVVideoWidthKey,videoSize.width),
        (AVVideoHeightKey,videoSize.height))

    var videoWriterInput = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: videoWriterSettings)

    videoWriterInput.expectsMediaDataInRealTime = true

    videoWriterInput.transform = videoTrack.preferredTransform


    var videoWriter = AVAssetWriter(URL: outputURL, fileType: AVFileTypeQuickTimeMovie, error: nil)

    videoWriter.addInput(videoWriterInput)

    var videoReaderSettings: [String:AnyObject] = [kCVPixelBufferPixelFormatTypeKey:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange]

    var videoReaderOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: videoReaderSettings)

    var videoReader = AVAssetReader(asset: videoAsset, error: nil)

    videoReader.addOutput(videoReaderOutput)



    //setup audio writer
    var audioWriterInput = AVAssetWriterInput(mediaType: AVMediaTypeAudio, outputSettings: nil)

    audioWriterInput.expectsMediaDataInRealTime = false

    videoWriter.addInput(audioWriterInput)


    //setup audio reader

    var audioTrack = videoAsset.tracksWithMediaType(AVMediaTypeAudio)[0] as AVAssetTrack

    var audioReaderOutput = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: nil) as AVAssetReaderOutput

    var audioReader = AVAssetReader(asset: videoAsset, error: nil)


    audioReader.addOutput(audioReaderOutput)

    videoWriter.startWriting()


    //start writing from video reader
    videoReader.startReading()

    videoWriter.startSessionAtSourceTime(kCMTimeZero)

    //dispatch_queue_t processingQueue = dispatch_queue_create("processingQueue", nil)

    var queue = dispatch_queue_create("processingQueue", nil)

    videoWriterInput.requestMediaDataWhenReadyOnQueue(queue, usingBlock: { () -> Void in
        println("Export starting")

        while videoWriterInput.readyForMoreMediaData
        {
            var sampleBuffer:CMSampleBufferRef!

            sampleBuffer = videoReaderOutput.copyNextSampleBuffer()

            if (videoReader.status == AVAssetReaderStatus.Reading && sampleBuffer != nil)
            {
                videoWriterInput.appendSampleBuffer(sampleBuffer)

            }

            else
            {
                videoWriterInput.markAsFinished()

                if videoReader.status == AVAssetReaderStatus.Completed
                {
                    if audioReader.status == AVAssetReaderStatus.Reading || audioReader.status == AVAssetReaderStatus.Completed
                    {

                    }
                    else {


                        audioReader.startReading()

                        videoWriter.startSessionAtSourceTime(kCMTimeZero)

                        var queue2 = dispatch_queue_create("processingQueue2", nil)


                        audioWriterInput.requestMediaDataWhenReadyOnQueue(queue2, usingBlock: { () -> Void in

                            while audioWriterInput.readyForMoreMediaData
                            {
                                var sampleBuffer:CMSampleBufferRef!

                                sampleBuffer = audioReaderOutput.copyNextSampleBuffer()

                                println(sampleBuffer == nil)

                                if (audioReader.status == AVAssetReaderStatus.Reading && sampleBuffer != nil)
                                {
                                    audioWriterInput.appendSampleBuffer(sampleBuffer)

                                }

                                else
                                {
                                    audioWriterInput.markAsFinished()

                                    if (audioReader.status == AVAssetReaderStatus.Completed)
                                    {

                                        videoWriter.finishWritingWithCompletionHandler({ () -> Void in

                                            println("Finished writing video asset.")

                                            self.videoUrl = outputURL

                                                var data = NSData(contentsOfURL: outputURL)!

                                                 println("Byte Size After Compression: \(data.length / 1048576) mb")

                                                println(videoAsset.playable)

                                                //Networking().uploadVideo(data, fileName: "Test2")

                                            self.dismissViewControllerAnimated(true, completion: nil)

                                        })
                                        break
                                    }
                                }
                            }
                        })
                        break
                    }
                }
            }// Second if

        }//first while

    })// first block
   // return
}

Here is the code for my UIImagePickerController that calls the compress method

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject])
{
    // Extract the media type from selection

    let type = info[UIImagePickerControllerMediaType] as String

    if (type == kUTTypeMovie)
    {

        self.videoUrl = info[UIImagePickerControllerMediaURL] as? NSURL

        var uploadUrl = NSURL.fileURLWithPath(NSTemporaryDirectory().stringByAppendingPathComponent("captured").stringByAppendingString(".mov"))

        var data = NSData(contentsOfURL: self.videoUrl!)!

        println("Size Before Compression: \(data.length / 1048576) mb")


        self.convertVideo(self.videoUrl!, outputURL: uploadUrl!)

        // Get the video from the info and set it appropriately.

        /*self.dismissViewControllerAnimated(true, completion: { () -> Void in


        //self.next.enabled = true

        })*/
    }
}

As i mentioned above this works as far as file size reduction, but when i get the file back (it is still of type .mov) quicktime cannot play it. Quicktime does try to convert it initially but fails halfway through (1-2 seconds after opening the file.) I've even tested the video file in AVPlayerController but it doesn't give any info about the movie, its just a play button without ant loading and without any length just "--" where the time is usually in the player. IE a corrupt file that won't play.

Im sure it has something to do with the settings for writing the asset out wether it is the video writing or the audio writing I'm not sure at all. It could even be the reading of the asset that is causing it to be corrupt. I've tried changing the variables around and setting different keys for reading and writing but i haven't found the right combination and this sucks that i can compress but get a corrupt file out of it. I'm not sure at all and any help would be appreciated. Pleeeeeeeeease.

解决方案

Andrew's answer is good and served as the basis for me implementing it in Swift 2.2. However, a few things have changed since he posted it, so I am putting up the one I use now which produces great results.

You call the method here with the existing URL of the video file, and a destination URL for the compressed copy.

This is my answer for Swift 3.0.

Please see below for 2.2, though it has not been updated to reflect improved style as the 3.0 one has.

extension ViewController: AVCaptureFileOutputRecordingDelegate {
    func capture(_ captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAt outputFileURL: URL!, fromConnections connections: [Any]!, error: Error!) {
        guard let data = NSData(contentsOf: outputFileURL as URL) else {
            return
        }

        print("File size before compression: \(Double(data.length / 1048576)) mb")
        let compressedURL = NSURL.fileURL(withPath: NSTemporaryDirectory() + NSUUID().uuidString + ".m4v")
        compressVideo(inputURL: outputFileURL as URL, outputURL: compressedURL) { (exportSession) in
            guard let session = exportSession else {
                return
            }

            switch session.status {
            case .unknown:
                break
            case .waiting:
                break
            case .exporting:
                break
            case .completed:
                guard let compressedData = NSData(contentsOf: compressedURL) else {
                    return
                }

                print("File size after compression: \(Double(compressedData.length / 1048576)) mb")
            case .failed:
                break
            case .cancelled:
                break
            }
        }
    }

    func compressVideo(inputURL: URL, outputURL: URL, handler:@escaping (_ exportSession: AVAssetExportSession?)-> Void) {
        let urlAsset = AVURLAsset(url: inputURL, options: nil)
        guard let exportSession = AVAssetExportSession(asset: urlAsset, presetName: AVAssetExportPresetMediumQuality) else {
            handler(nil)

            return
        }

        exportSession.outputURL = outputURL
        exportSession.outputFileType = AVFileTypeQuickTimeMovie
        exportSession.shouldOptimizeForNetworkUse = true
        exportSession.exportAsynchronously { () -> Void in
            handler(exportSession)
        }
    }
}

Answer for Swift 2.2

    let compressedURL = NSURL.fileURLWithPath(NSTemporaryDirectory() + NSUUID().UUIDString + ".m4v")
    compressVideo(outputFileURL, outputURL: compressedURL) { (session) in
        switch session.status {
        case .Unknown:
            break
        case .Waiting:
            break
        case .Exporting:
            break
        case .Completed:
            let data = NSData(contentsOfURL: compressedURL)
            print("File size after compression: \(Double(data!.length / 1048576)) mb")
        case .Failed:
            break
        case .Cancelled:
            break
        }
    }

This handles all of your possible outcomes with the switch statement. I left most of them empty, since the content will vary by application.

For the actual compression method, I implemented an unwrap and changed var to let.

func compressVideo(inputURL: NSURL, outputURL: NSURL, handler:(session: AVAssetExportSession)-> Void) {
    let urlAsset = AVURLAsset(URL: inputURL, options: nil)
    if let exportSession = AVAssetExportSession(asset: urlAsset, presetName: AVAssetExportPresetMediumQuality) {
        exportSession.outputURL = outputURL
        exportSession.outputFileType = AVFileTypeQuickTimeMovie
        exportSession.shouldOptimizeForNetworkUse = true
        exportSession.exportAsynchronouslyWithCompletionHandler { () -> Void in
            handler(session: exportSession)
        }
    }
}

I tested it with a few videos, and was seeing some pretty significant reductions (ex: 20 MB becoming 500k).

这篇关于IOS视频压缩Swift iOS 8损坏的视频文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆