ios 如何减小使用 UIImagePickerController 创建的视频的文件大小?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11751883/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I reduce the file size of a video created with UIImagePickerController?
提问by zakdances
I have an app that allows a user to record a video with UIImagePickerController
and then upload it to YouTube. The problem is that the video file that UIImagePickerController
creates is HUGE, even when the video is only 5 seconds long. For example, a 5 second long video is 16-20 megabytes. I want to keep the video in 540 or 720 quality, but I want to reduce the file size.
我有一个应用程序,允许用户录制视频,UIImagePickerController
然后将其上传到 YouTube。问题是UIImagePickerController
创建的视频文件很大,即使视频只有 5 秒长。例如,一个 5 秒长的视频是 16-20 兆字节。我想将视频保持在 540 或 720 质量,但我想减小文件大小。
I've been experimenting with AVFoundation and AVAssetExportSession
to try to get a smaller file size. I've tried the following code:
我一直在试验 AVFoundation 并AVAssetExportSession
尝试获得更小的文件大小。我试过以下代码:
AVAsset *video = [AVAsset assetWithURL:videoURL];
AVAssetExportSession *exportSession = [AVAssetExportSession exportSessionWithAsset:video presetName:AVAssetExportPresetPassthrough];
exportSession.shouldOptimizeForNetworkUse = YES;
exportSession.outputFileType = AVFileTypeMPEG4;
exportSession.outputURL = [pathToSavedVideosDirectory URLByAppendingPathComponent:@"vid1.mp4"];
[exportSession exportAsynchronouslyWithCompletionHandler:^{
NSLog(@"done processing video!");
}];
But this hasn't reduced the file size at all. I know what I'm doing is possible because in Apple's Photos app, when you select "share on YouTube", will automatically process the video file so its small enough to upload. I want to do the same thing in my app.
但这根本没有减少文件大小。我知道我在做什么是可能的,因为在 Apple 的照片应用程序中,当您选择“在 YouTube 上共享”时,将自动处理视频文件,使其小到可以上传。我想在我的应用程序中做同样的事情。
How can I accomplish this?
我怎样才能做到这一点?
回答by jgh
With AVCaptureSession
and AVAssetWriter
you can set the compression settings as such:
使用AVCaptureSession
和AVAssetWriter
您可以将压缩设置设置为:
NSDictionary *settings = @{AVVideoCodecKey:AVVideoCodecH264,
AVVideoWidthKey:@(video_width),
AVVideoHeightKey:@(video_height),
AVVideoCompressionPropertiesKey:
@{AVVideoAverageBitRateKey:@(desired_bitrate),
AVVideoProfileLevelKey:AVVideoProfileLevelH264Main31, /* Or whatever profile & level you wish to use */
AVVideoMaxKeyFrameIntervalKey:@(desired_keyframe_interval)}};
AVAssetWriterInput* writer_input = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:settings];
Edit: I guess if you insist on using the UIImagePicker
to create the movie in the first place, you'll have to use AVAssetReader's
copyNextSampleBuffer
and AVAssetWriter's
appendSampleBuffer
methods to do the transcode.
编辑:我想如果您首先坚持使用UIImagePicker
来创建电影,则必须使用AVAssetReader's
copyNextSampleBuffer
和AVAssetWriter's
appendSampleBuffer
方法来进行转码。
回答by Erik
yourfriendzak is right: Setting cameraUI.videoQuality = UIImagePickerControllerQualityTypeLow;
isn't the solution here. The solution is to reduce the data rate, or bit rate, which is what jgh is suggesting.
yourfriendzak 是对的:设置cameraUI.videoQuality = UIImagePickerControllerQualityTypeLow;
不是这里的解决方案。解决方案是降低数据速率或比特率,这正是 jgh 所建议的。
I have, three methods. The first method handles the UIImagePicker
delegate method:
我有,三种方法。第一个方法处理UIImagePicker
委托方法:
// For responding to the user accepting a newly-captured picture or movie
- (void) imagePickerController: (UIImagePickerController *) picker didFinishPickingMediaWithInfo: (NSDictionary *) info {
// Handle movie capture
NSURL *movieURL = [info objectForKey:
UIImagePickerControllerMediaURL];
NSURL *uploadURL = [NSURL fileURLWithPath:[[NSTemporaryDirectory() stringByAppendingPathComponent:[self randomString]] stringByAppendingString:@".mp4"]];
// Compress movie first
[self convertVideoToLowQuailtyWithInputURL:movieURL outputURL:uploadURL];
}
The second method converts the video to a lower bitrate, not to lower dimensions.
第二种方法将视频转换为较低的比特率,而不是较低的尺寸。
- (void)convertVideoToLowQuailtyWithInputURL:(NSURL*)inputURL
outputURL:(NSURL*)outputURL
{
//setup video writer
AVAsset *videoAsset = [[AVURLAsset alloc] initWithURL:inputURL options:nil];
AVAssetTrack *videoTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
CGSize videoSize = videoTrack.naturalSize;
NSDictionary *videoWriterCompressionSettings = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:1250000], AVVideoAverageBitRateKey, nil];
NSDictionary *videoWriterSettings = [NSDictionary dictionaryWithObjectsAndKeys:AVVideoCodecH264, AVVideoCodecKey, videoWriterCompressionSettings, AVVideoCompressionPropertiesKey, [NSNumber numberWithFloat:videoSize.width], AVVideoWidthKey, [NSNumber numberWithFloat:videoSize.height], AVVideoHeightKey, nil];
AVAssetWriterInput* videoWriterInput = [AVAssetWriterInput
assetWriterInputWithMediaType:AVMediaTypeVideo
outputSettings:videoWriterSettings];
videoWriterInput.expectsMediaDataInRealTime = YES;
videoWriterInput.transform = videoTrack.preferredTransform;
AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:outputURL fileType:AVFileTypeQuickTimeMovie error:nil];
[videoWriter addInput:videoWriterInput];
//setup video reader
NSDictionary *videoReaderSettings = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange] forKey:(id)kCVPixelBufferPixelFormatTypeKey];
AVAssetReaderTrackOutput *videoReaderOutput = [[AVAssetReaderTrackOutput alloc] initWithTrack:videoTrack outputSettings:videoReaderSettings];
AVAssetReader *videoReader = [[AVAssetReader alloc] initWithAsset:videoAsset error:nil];
[videoReader addOutput:videoReaderOutput];
//setup audio writer
AVAssetWriterInput* audioWriterInput = [AVAssetWriterInput
assetWriterInputWithMediaType:AVMediaTypeAudio
outputSettings:nil];
audioWriterInput.expectsMediaDataInRealTime = NO;
[videoWriter addInput:audioWriterInput];
//setup audio reader
AVAssetTrack* audioTrack = [[videoAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
AVAssetReaderOutput *audioReaderOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:audioTrack outputSettings:nil];
AVAssetReader *audioReader = [AVAssetReader assetReaderWithAsset: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("processingQueue1", NULL);
[videoWriterInput requestMediaDataWhenReadyOnQueue:processingQueue usingBlock:
^{
while ([videoWriterInput isReadyForMoreMediaData]) {
CMSampleBufferRef sampleBuffer;
if ([videoReader status] == AVAssetReaderStatusReading &&
(sampleBuffer = [videoReaderOutput copyNextSampleBuffer])) {
[videoWriterInput appendSampleBuffer:sampleBuffer];
CFRelease(sampleBuffer);
}
else {
[videoWriterInput markAsFinished];
if ([videoReader status] == AVAssetReaderStatusCompleted) {
//start writing from audio reader
[audioReader startReading];
[videoWriter startSessionAtSourceTime:kCMTimeZero];
dispatch_queue_t processingQueue = dispatch_queue_create("processingQueue2", NULL);
[audioWriterInput requestMediaDataWhenReadyOnQueue:processingQueue usingBlock:^{
while (audioWriterInput.readyForMoreMediaData) {
CMSampleBufferRef sampleBuffer;
if ([audioReader status] == AVAssetReaderStatusReading &&
(sampleBuffer = [audioReaderOutput copyNextSampleBuffer])) {
[audioWriterInput appendSampleBuffer:sampleBuffer];
CFRelease(sampleBuffer);
}
else {
[audioWriterInput markAsFinished];
if ([audioReader status] == AVAssetReaderStatusCompleted) {
[videoWriter finishWritingWithCompletionHandler:^(){
[self sendMovieFileAtURL:outputURL];
}];
}
}
}
}
];
}
}
}
}
];
}
When successful, the third method, sendMovieFileAtURL:
is called, which uploads the compressed video at outputURL
to the server.
成功后,sendMovieFileAtURL:
调用第三个方法,将压缩视频上传outputURL
到服务器。
Note that I've enabled ARC in my project, so you will have to add some release
calls if ARC is turned off in yours.
请注意,我在我的项目中启用了 ARC,因此如果您的项目中release
关闭了 ARC ,您将不得不添加一些调用。
回答by Sash Zats
On UImagePickerController
you have a videoQuality
property of UIImagePickerControllerQualityType
type, and will be applied to recorded movies as well as to the ones that you picked picked from the library (that happens during transcoding phase).
在UImagePickerController
你拥有videoQuality
的物业UIImagePickerControllerQualityType
类型,将被应用到录制的动画,以及对那些您选择从库(在转码阶段出现这种情况)采摘。
Or if you have to deal with existent asset (file) not from the library you might want to look at these presets:
或者,如果您必须处理不是来自库的现有资产(文件),您可能需要查看这些预设:
AVAssetExportPresetLowQuality
AVAssetExportPresetMediumQuality
AVAssetExportPresetHighestQuality
and
和
AVAssetExportPreset640x480
AVAssetExportPreset960x540
AVAssetExportPreset1280x720
AVAssetExportPreset1920x1080
and pass one of them to initializerof AVAssetExportSession
class. I'm afraid you have to play with those for your particular content as there is no precise description for what is low
and medium
quality or which quality will be used for 640x480
or for 1280x720
preset. The only useful information in the docs is following:
并通过其中的一个初始化的AVAssetExportSession
类。恐怕您必须针对您的特定内容使用这些内容,因为没有对什么是low
和medium
质量或将用于640x480
或用于1280x720
预设的质量的准确描述。文档中唯一有用的信息如下:
Export Preset Names for Device-Appropriate QuickTime Files You use these export options to produce QuickTime .mov files with video size appropriate to the current device.
The export will not scale the video up from a smaller size. Video is compressed using H.264; audio is compressed using AAC
Some devices cannot support some sizes.
导出适用于设备的 QuickTime 文件的预设名称 您可以使用这些导出选项生成视频大小适合当前设备的 QuickTime .mov 文件。
导出不会将视频从较小的尺寸放大。视频使用 H.264 压缩;音频使用 AAC 压缩
某些设备无法支持某些尺寸。
Aside from that I do not remember having precise control over quality such as framerate or freeform size etc in AVFoundation
除此之外,我不记得对质量进行精确控制,例如帧率或自由格式大小等 AVFoundation
I was wrong, there is a way to tweak all parameters you mentions and it is AVAssetWriter indeed: How do I export UIImage array as a movie?
我错了,有一种方法可以调整您提到的所有参数,它确实是 AVAssetWriter:如何将 UIImage 数组导出为电影?
btw, here is a link to a similar question with a code sample: iPhone:Programmatically compressing recorded video to share?
顺便说一句,这是一个带有代码示例的类似问题的链接:iPhone:以编程方式压缩录制的视频以共享?
回答by etayluz
Erik's answer may have been correct at the time he wrote it - but now with iOS8 it's just crashing left and right, I've spent a few hours on it myself.
Erik 的回答在他写的时候可能是正确的 - 但是现在使用 iOS8 它只是左右崩溃,我自己花了几个小时。
You need a PhD to work with AVAssetWriter - it's non-trivial: https://developer.apple.com/library/mac/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/05_Export.html#//apple_ref/doc/uid/TP40010188-CH9-SW1
您需要博士学位才能使用 AVAssetWriter - 这很重要:https: //developer.apple.com/library/mac/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/05_Export.html#//apple_ref/doc/uid/ TP40010188-CH9-SW1
There's an amazing library for doing exactly what you want which is just an AVAssetExportSession drop-in replacement with more crucial features like changing the bit rate: https://github.com/rs/SDAVAssetExportSession
有一个惊人的库可以完全满足您的要求,它只是一个 AVAssetExportSession 替代品,具有更重要的功能,例如更改比特率:https: //github.com/rs/SDAVAssetExportSession
Here's how to use it:
以下是如何使用它:
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
SDAVAssetExportSession *encoder = [SDAVAssetExportSession.alloc initWithAsset:[AVAsset assetWithURL:[info objectForKey:UIImagePickerControllerMediaURL]]];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
self.myPathDocs = [documentsDirectory stringByAppendingPathComponent:
[NSString stringWithFormat:@"lowerBitRate-%d.mov",arc4random() % 1000]];
NSURL *url = [NSURL fileURLWithPath:self.myPathDocs];
encoder.outputURL=url;
encoder.outputFileType = AVFileTypeMPEG4;
encoder.shouldOptimizeForNetworkUse = YES;
encoder.videoSettings = @
{
AVVideoCodecKey: AVVideoCodecH264,
AVVideoCompressionPropertiesKey: @
{
AVVideoAverageBitRateKey: @2300000, // Lower bit rate here
AVVideoProfileLevelKey: AVVideoProfileLevelH264High40,
},
};
encoder.audioSettings = @
{
AVFormatIDKey: @(kAudioFormatMPEG4AAC),
AVNumberOfChannelsKey: @2,
AVSampleRateKey: @44100,
AVEncoderBitRateKey: @128000,
};
[encoder exportAsynchronouslyWithCompletionHandler:^
{
int status = encoder.status;
if (status == AVAssetExportSessionStatusCompleted)
{
AVAssetTrack *videoTrack = nil;
AVURLAsset *asset = [AVAsset assetWithURL:encoder.outputURL];
NSArray *videoTracks = [asset tracksWithMediaType:AVMediaTypeVideo];
videoTrack = [videoTracks objectAtIndex:0];
float frameRate = [videoTrack nominalFrameRate];
float bps = [videoTrack estimatedDataRate];
NSLog(@"Frame rate == %f",frameRate);
NSLog(@"bps rate == %f",bps/(1024.0 * 1024.0));
NSLog(@"Video export succeeded");
// encoder.outputURL <- this is what you want!!
}
else if (status == AVAssetExportSessionStatusCancelled)
{
NSLog(@"Video export cancelled");
}
else
{
NSLog(@"Video export failed with error: %@ (%d)", encoder.error.localizedDescription, encoder.error.code);
}
}];
}
回答by parth
Erik Wegener code rewrited to swift 3:
Erik Wegener 代码重写为 swift 3:
class func convertVideoToLowQuailtyWithInputURL(inputURL: NSURL, outputURL: NSURL, onDone: @escaping () -> ()) {
//setup video writer
let videoAsset = AVURLAsset(url: inputURL as URL, options: nil)
let videoTrack = videoAsset.tracks(withMediaType: AVMediaTypeVideo)[0]
let videoSize = videoTrack.naturalSize
let videoWriterCompressionSettings = [
AVVideoAverageBitRateKey : Int(125000)
]
let videoWriterSettings:[String : AnyObject] = [
AVVideoCodecKey : AVVideoCodecH264 as AnyObject,
AVVideoCompressionPropertiesKey : videoWriterCompressionSettings as AnyObject,
AVVideoWidthKey : Int(videoSize.width) as AnyObject,
AVVideoHeightKey : Int(videoSize.height) as AnyObject
]
let videoWriterInput = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: videoWriterSettings)
videoWriterInput.expectsMediaDataInRealTime = true
videoWriterInput.transform = videoTrack.preferredTransform
let videoWriter = try! AVAssetWriter(outputURL: outputURL as URL, fileType: AVFileTypeQuickTimeMovie)
videoWriter.add(videoWriterInput)
//setup video reader
let videoReaderSettings:[String : AnyObject] = [
kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange) as AnyObject
]
let videoReaderOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: videoReaderSettings)
let videoReader = try! AVAssetReader(asset: videoAsset)
videoReader.add(videoReaderOutput)
//setup audio writer
let audioWriterInput = AVAssetWriterInput(mediaType: AVMediaTypeAudio, outputSettings: nil)
audioWriterInput.expectsMediaDataInRealTime = false
videoWriter.add(audioWriterInput)
//setup audio reader
let audioTrack = videoAsset.tracks(withMediaType: AVMediaTypeAudio)[0]
let audioReaderOutput = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: nil)
let audioReader = try! AVAssetReader(asset: videoAsset)
audioReader.add(audioReaderOutput)
videoWriter.startWriting()
//start writing from video reader
videoReader.startReading()
videoWriter.startSession(atSourceTime: kCMTimeZero)
let processingQueue = DispatchQueue(label: "processingQueue1")
videoWriterInput.requestMediaDataWhenReady(on: processingQueue, using: {() -> Void in
while videoWriterInput.isReadyForMoreMediaData {
let sampleBuffer:CMSampleBuffer? = videoReaderOutput.copyNextSampleBuffer();
if videoReader.status == .reading && sampleBuffer != nil {
videoWriterInput.append(sampleBuffer!)
}
else {
videoWriterInput.markAsFinished()
if videoReader.status == .completed {
//start writing from audio reader
audioReader.startReading()
videoWriter.startSession(atSourceTime: kCMTimeZero)
let processingQueue = DispatchQueue(label: "processingQueue2")
audioWriterInput.requestMediaDataWhenReady(on: processingQueue, using: {() -> Void in
while audioWriterInput.isReadyForMoreMediaData {
let sampleBuffer:CMSampleBuffer? = audioReaderOutput.copyNextSampleBuffer()
if audioReader.status == .reading && sampleBuffer != nil {
audioWriterInput.append(sampleBuffer!)
}
else {
audioWriterInput.markAsFinished()
if audioReader.status == .completed {
videoWriter.finishWriting(completionHandler: {() -> Void in
onDone();
})
}
}
}
})
}
}
}
})
}
回答by Mehdi
You can set the video quality when you want to open UIImagePickerController
to any one of the following :
当您要打开时,您可以将视频质量设置UIImagePickerController
为以下任何一种:
UIImagePickerControllerQualityType640x480
UIImagePickerControllerQualityTypeLow
UIImagePickerControllerQualityTypeMedium
UIImagePickerControllerQualityTypeHigh
UIImagePickerControllerQualityTypeIFrame960x540
UIImagePickerControllerQualityTypeIFrame1280x720
UIImagePickerControllerQualityType640x480
UIImagePickerControllerQualityTypeLow
UIImagePickerControllerQualityTypeMedium
UIImagePickerControllerQualityTypeHigh
UIImagePickerControllerQualityTypeIFrame960x540
UIImagePickerControllerQualityTypeIFrame1280x720
Try this code for changing quality type when open the UIImagePickerController
:
打开以下代码时,请尝试更改质量类型的代码UIImagePickerController
:
if (([UIImagePickerController isSourceTypeAvailable:
UIImagePickerControllerSourceTypeCamera] == NO))
return NO;
UIImagePickerController *cameraUI = [[UIImagePickerController alloc] init];
cameraUI.sourceType = UIImagePickerControllerSourceTypeCamera;
cameraUI.mediaTypes = [[NSArray alloc] initWithObjects: (NSString *) kUTTypeMovie, nil];
cameraUI.allowsEditing = NO;
cameraUI.delegate = self;
cameraUI.videoQuality = UIImagePickerControllerQualityTypeLow;//you can change the quality here
[self presentModalViewController:cameraUI animated:YES];
回答by Jakub Trzciński
Erik Wegener code rewrited to swift:
Erik Wegener 代码重写为 swift:
class func convertVideoToLowQuailtyWithInputURL(inputURL: NSURL, outputURL: NSURL, onDone: () -> ()) {
//setup video writer
let videoAsset = AVURLAsset(URL: inputURL, options: nil)
let videoTrack = videoAsset.tracksWithMediaType(AVMediaTypeVideo)[0]
let videoSize = videoTrack.naturalSize
let videoWriterCompressionSettings = [
AVVideoAverageBitRateKey : Int(125000)
]
let videoWriterSettings:[String : AnyObject] = [
AVVideoCodecKey : AVVideoCodecH264,
AVVideoCompressionPropertiesKey : videoWriterCompressionSettings,
AVVideoWidthKey : Int(videoSize.width),
AVVideoHeightKey : Int(videoSize.height)
]
let videoWriterInput = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: videoWriterSettings)
videoWriterInput.expectsMediaDataInRealTime = true
videoWriterInput.transform = videoTrack.preferredTransform
let videoWriter = try! AVAssetWriter(URL: outputURL, fileType: AVFileTypeQuickTimeMovie)
videoWriter.addInput(videoWriterInput)
//setup video reader
let videoReaderSettings:[String : AnyObject] = [
kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange)
]
let videoReaderOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: videoReaderSettings)
let videoReader = try! AVAssetReader(asset: videoAsset)
videoReader.addOutput(videoReaderOutput)
//setup audio writer
let audioWriterInput = AVAssetWriterInput(mediaType: AVMediaTypeAudio, outputSettings: nil)
audioWriterInput.expectsMediaDataInRealTime = false
videoWriter.addInput(audioWriterInput)
//setup audio reader
let audioTrack = videoAsset.tracksWithMediaType(AVMediaTypeAudio)[0]
let audioReaderOutput = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: nil)
let audioReader = try! AVAssetReader(asset: videoAsset)
audioReader.addOutput(audioReaderOutput)
videoWriter.startWriting()
//start writing from video reader
videoReader.startReading()
videoWriter.startSessionAtSourceTime(kCMTimeZero)
let processingQueue = dispatch_queue_create("processingQueue1", nil)
videoWriterInput.requestMediaDataWhenReadyOnQueue(processingQueue, usingBlock: {() -> Void in
while videoWriterInput.readyForMoreMediaData {
let sampleBuffer:CMSampleBuffer? = videoReaderOutput.copyNextSampleBuffer();
if videoReader.status == .Reading && sampleBuffer != nil {
videoWriterInput.appendSampleBuffer(sampleBuffer!)
}
else {
videoWriterInput.markAsFinished()
if videoReader.status == .Completed {
//start writing from audio reader
audioReader.startReading()
videoWriter.startSessionAtSourceTime(kCMTimeZero)
let processingQueue = dispatch_queue_create("processingQueue2", nil)
audioWriterInput.requestMediaDataWhenReadyOnQueue(processingQueue, usingBlock: {() -> Void in
while audioWriterInput.readyForMoreMediaData {
let sampleBuffer:CMSampleBufferRef? = audioReaderOutput.copyNextSampleBuffer()
if audioReader.status == .Reading && sampleBuffer != nil {
audioWriterInput.appendSampleBuffer(sampleBuffer!)
}
else {
audioWriterInput.markAsFinished()
if audioReader.status == .Completed {
videoWriter.finishWritingWithCompletionHandler({() -> Void in
onDone();
})
}
}
}
})
}
}
}
})
}
回答by Sarwar Jahan
Swift 4:
斯威夫特 4:
func convertVideoToLowQuailtyWithInputURL(inputURL: NSURL, outputURL: NSURL, completion: @escaping (Bool) -> Void) {
let videoAsset = AVURLAsset(url: inputURL as URL, options: nil)
let videoTrack = videoAsset.tracks(withMediaType: AVMediaType.video)[0]
let videoSize = videoTrack.naturalSize
let videoWriterCompressionSettings = [
AVVideoAverageBitRateKey : Int(125000)
]
let videoWriterSettings:[String : AnyObject] = [
AVVideoCodecKey : AVVideoCodecH264 as AnyObject,
AVVideoCompressionPropertiesKey : videoWriterCompressionSettings as AnyObject,
AVVideoWidthKey : Int(videoSize.width) as AnyObject,
AVVideoHeightKey : Int(videoSize.height) as AnyObject
]
let videoWriterInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: videoWriterSettings)
videoWriterInput.expectsMediaDataInRealTime = true
videoWriterInput.transform = videoTrack.preferredTransform
let videoWriter = try! AVAssetWriter(outputURL: outputURL as URL, fileType: AVFileType.mov)
videoWriter.add(videoWriterInput)
//setup video reader
let videoReaderSettings:[String : AnyObject] = [
kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange) as AnyObject
]
let videoReaderOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: videoReaderSettings)
var videoReader: AVAssetReader!
do{
videoReader = try AVAssetReader(asset: videoAsset)
}
catch {
print("video reader error: \(error)")
completion(false)
}
videoReader.add(videoReaderOutput)
//setup audio writer
let audioWriterInput = AVAssetWriterInput(mediaType: AVMediaType.audio, outputSettings: nil)
audioWriterInput.expectsMediaDataInRealTime = false
videoWriter.add(audioWriterInput)
//setup audio reader
let audioTrack = videoAsset.tracks(withMediaType: AVMediaType.audio)[0]
let audioReaderOutput = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: nil)
let audioReader = try! AVAssetReader(asset: videoAsset)
audioReader.add(audioReaderOutput)
videoWriter.startWriting()
//start writing from video reader
videoReader.startReading()
videoWriter.startSession(atSourceTime: kCMTimeZero)
let processingQueue = DispatchQueue(label: "processingQueue1")
videoWriterInput.requestMediaDataWhenReady(on: processingQueue, using: {() -> Void in
while videoWriterInput.isReadyForMoreMediaData {
let sampleBuffer:CMSampleBuffer? = videoReaderOutput.copyNextSampleBuffer();
if videoReader.status == .reading && sampleBuffer != nil {
videoWriterInput.append(sampleBuffer!)
}
else {
videoWriterInput.markAsFinished()
if videoReader.status == .completed {
//start writing from audio reader
audioReader.startReading()
videoWriter.startSession(atSourceTime: kCMTimeZero)
let processingQueue = DispatchQueue(label: "processingQueue2")
audioWriterInput.requestMediaDataWhenReady(on: processingQueue, using: {() -> Void in
while audioWriterInput.isReadyForMoreMediaData {
let sampleBuffer:CMSampleBuffer? = audioReaderOutput.copyNextSampleBuffer()
if audioReader.status == .reading && sampleBuffer != nil {
audioWriterInput.append(sampleBuffer!)
}
else {
audioWriterInput.markAsFinished()
if audioReader.status == .completed {
videoWriter.finishWriting(completionHandler: {() -> Void in
completion(true)
})
}
}
}
})
}
}
}
})
}
回答by arunjos007
There is an awesome custom class(SDAVAssetExportSession) to do the video compression. You can download it from this link.
有一个很棒的自定义类(SDAVAssetExportSession)来进行视频压缩。你可以从这个链接下载。
After downloading add SDAVAssetExportSession.h and SDAVAssetExportSession.m files into your project, Then use below code to do the compression. In below code you can compress video by specifying resolution and bitrate
下载后将 SDAVAssetExportSession.h 和 SDAVAssetExportSession.m 文件添加到您的项目中,然后使用下面的代码进行压缩。在下面的代码中,您可以通过指定分辨率和比特率来压缩视频
#import "SDAVAssetExportSession.h"
- (void)compressVideoWithInputVideoUrl:(NSURL *) inputVideoUrl
{
/* Create Output File Url */
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *finalVideoURLString = [documentsDirectory stringByAppendingPathComponent:@"compressedVideo.mp4"];
NSURL *outputVideoUrl = ([[NSURL URLWithString:finalVideoURLString] isFileURL] == 1)?([NSURL URLWithString:finalVideoURLString]):([NSURL fileURLWithPath:finalVideoURLString]); // Url Should be a file Url, so here we check and convert it into a file Url
SDAVAssetExportSession *compressionEncoder = [SDAVAssetExportSession.alloc initWithAsset:[AVAsset assetWithURL:inputVideoUrl]]; // provide inputVideo Url Here
compressionEncoder.outputFileType = AVFileTypeMPEG4;
compressionEncoder.outputURL = outputVideoUrl; //Provide output video Url here
compressionEncoder.videoSettings = @
{
AVVideoCodecKey: AVVideoCodecH264,
AVVideoWidthKey: @800, //Set your resolution width here
AVVideoHeightKey: @600, //set your resolution height here
AVVideoCompressionPropertiesKey: @
{
AVVideoAverageBitRateKey: @45000, // Give your bitrate here for lower size give low values
AVVideoProfileLevelKey: AVVideoProfileLevelH264High40,
},
};
compressionEncoder.audioSettings = @
{
AVFormatIDKey: @(kAudioFormatMPEG4AAC),
AVNumberOfChannelsKey: @2,
AVSampleRateKey: @44100,
AVEncoderBitRateKey: @128000,
};
[compressionEncoder exportAsynchronouslyWithCompletionHandler:^
{
if (compressionEncoder.status == AVAssetExportSessionStatusCompleted)
{
NSLog(@"Compression Export Completed Successfully");
}
else if (compressionEncoder.status == AVAssetExportSessionStatusCancelled)
{
NSLog(@"Compression Export Canceled");
}
else
{
NSLog(@"Compression Failed");
}
}];
}
To Cancel Compression Use Below Line Of code
取消压缩使用下面的代码行
[compressionEncoder cancelExport]; //Video compression cancel
回答by arunjos007
I am supporting etayluz's answer SDAVAssetExportSessionis an awesome custom class to do the video compression. Here is my worked code. You can download SDAVAssetExportSessionfrom this link.
我支持etayluz的回答SDAVAssetExportSession是一个很棒的自定义类来进行视频压缩。这是我的工作代码。您可以从此链接下载SDAVAssetExportSession。
After downloading add SDAVAssetExportSession.h and SDAVAssetExportSession.m files into your project, Then use below code to do the compression. In below code you can compress video by specifying resolution and bitrate
下载后将 SDAVAssetExportSession.h 和 SDAVAssetExportSession.m 文件添加到您的项目中,然后使用下面的代码进行压缩。在下面的代码中,您可以通过指定分辨率和比特率来压缩视频
#import "SDAVAssetExportSession.h"
- (void)compressVideoWithInputVideoUrl:(NSURL *) inputVideoUrl
{
/* Create Output File Url */
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *finalVideoURLString = [documentsDirectory stringByAppendingPathComponent:@"compressedVideo.mp4"];
NSURL *outputVideoUrl = ([[NSURL URLWithString:finalVideoURLString] isFileURL] == 1)?([NSURL URLWithString:finalVideoURLString]):([NSURL fileURLWithPath:finalVideoURLString]); // Url Should be a file Url, so here we check and convert it into a file Url
SDAVAssetExportSession *compressionEncoder = [SDAVAssetExportSession.alloc initWithAsset:[AVAsset assetWithURL:inputVideoUrl]]; // provide inputVideo Url Here
compressionEncoder.outputFileType = AVFileTypeMPEG4;
compressionEncoder.outputURL = outputVideoUrl; //Provide output video Url here
compressionEncoder.videoSettings = @
{
AVVideoCodecKey: AVVideoCodecH264,
AVVideoWidthKey: @800, //Set your resolution width here
AVVideoHeightKey: @600, //set your resolution height here
AVVideoCompressionPropertiesKey: @
{
AVVideoAverageBitRateKey: @45000, // Give your bitrate here for lower size give low values
AVVideoProfileLevelKey: AVVideoProfileLevelH264High40,
},
};
compressionEncoder.audioSettings = @
{
AVFormatIDKey: @(kAudioFormatMPEG4AAC),
AVNumberOfChannelsKey: @2,
AVSampleRateKey: @44100,
AVEncoderBitRateKey: @128000,
};
[compressionEncoder exportAsynchronouslyWithCompletionHandler:^
{
if (compressionEncoder.status == AVAssetExportSessionStatusCompleted)
{
NSLog(@"Compression Export Completed Successfully");
}
else if (compressionEncoder.status == AVAssetExportSessionStatusCancelled)
{
NSLog(@"Compression Export Canceled");
}
else
{
NSLog(@"Compression Failed");
}
}];
}
To Cancel Compression Use Below Line Of code
取消压缩使用下面的代码行
[compressionEncoder cancelExport]; //Video compression cancel