zak*_*ces 56 video file-upload objective-c avfoundation ios
我有一个应用程序,允许用户录制视频,UIImagePickerController然后将其上传到YouTube.问题是UIImagePickerController创建的视频文件是巨大的,即使视频只有5秒长.例如,5秒长的视频是16-20兆字节.我想保持540或720质量的视频,但我想减少文件大小.
我一直在尝试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!");
}];
Run Code Online (Sandbox Code Playgroud)
但这并没有减少文件大小.我知道我正在做的事情是可能的,因为在Apple的照片应用程序中,当您选择"在YouTube上分享"时,会自动处理视频文件,因此它的小到可以上传.我想在我的应用程序中做同样的事情.
我怎么能做到这一点?
jgh*_*jgh 68
使用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];
Run Code Online (Sandbox Code Playgroud)
编辑:我想如果你坚持使用UIImagePicker首先创建电影,你将不得不使用AVAssetReader's copyNextSampleBuffer和AVAssetWriter's appendSampleBuffer方法来进行转码.
Eri*_*rik 22
yourfriendzak是对的:设置cameraUI.videoQuality = UIImagePickerControllerQualityTypeLow;不是解决方案.解决方案是降低数据速率或比特率,这正是jgh所暗示的.
我有三种方法.第一个方法处理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];
}
Run Code Online (Sandbox Code Playgroud)
第二种方法将视频转换为较低的比特率,而不是较低的维度.
- (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];
}];
}
}
}
}
];
}
}
}
}
];
}
Run Code Online (Sandbox Code Playgroud)
如果成功,sendMovieFileAtURL:则调用第三种方法,将压缩视频上传outputURL到服务器.
请注意,我已在项目中启用了ARC,因此release如果您的ARC已关闭,则必须添加一些调用.
Sas*_*ats 19
在UImagePickerController您拥有类型videoQuality属性时UIImagePickerControllerQualityType,将应用于录制的电影以及您从库中选取的电影(在转码阶段发生).
或者,如果您不必处理来自库的现有资产(文件),您可能需要查看这些预设:
AVAssetExportPresetLowQuality
AVAssetExportPresetMediumQuality
AVAssetExportPresetHighestQuality
Run Code Online (Sandbox Code Playgroud)
和
AVAssetExportPreset640x480
AVAssetExportPreset960x540
AVAssetExportPreset1280x720
AVAssetExportPreset1920x1080
Run Code Online (Sandbox Code Playgroud)
并通过其中的一个初始化的AVAssetExportSession类.我担心你必须为你的特定内容玩那些,因为没有关于什么low和medium质量或将用于640x480或1280x720预设的质量的精确描述.文档中唯一有用的信息如下:
导出适用于设备的QuickTime文件的预设名称您可以使用这些导出选项生成具有适合当前设备的视频大小的QuickTime .mov文件.
导出不会从较小的尺寸缩放视频.使用H.264压缩视频; 使用AAC压缩音频
有些设备无法支持某些尺寸.
除此之外,我不记得对帧速率或自由形状等的质量进行精确控制 AVFoundation
我错了,有一种方法可以调整你提到的所有参数,它确实是AVAssetWriter:如何将UIImage数组导出为电影?
顺便说一句,这里是一个代码示例类似问题的链接:iPhone:以编程方式压缩录制的视频以进行共享?
eta*_*luz 13
Erik的回答在他写这篇文章的时候可能是正确的 - 但现在iOS8只是左右撞击,我自己花了几个小时.
您需要博士才能使用AVAssetWriter - 这非常重要:https://developer.apple.com/library/mac/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/05_Export.html#//apple_ref/doc/uid/ TP40010188-CH9-SW1
有一个令人惊叹的库,可以完全按照您的需要进行操作,这只是一个AVAssetExportSession直接替换,具有更重要的功能,如更改比特率:https://github.com/rs/SDAVAssetExportSession
以下是如何使用它:
-(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);
}
}];
}
Run Code Online (Sandbox Code Playgroud)
小智 9
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();
})
}
}
}
})
}
}
}
})
}
Run Code Online (Sandbox Code Playgroud)
您可以在要打开UIImagePickerController以下任何一项时设置视频质量:
UIImagePickerControllerQualityType640x480
UIImagePickerControllerQualityTypeLow
UIImagePickerControllerQualityTypeMedium
UIImagePickerControllerQualityTypeHigh
UIImagePickerControllerQualityTypeIFrame960x540
UIImagePickerControllerQualityTypeIFrame1280x720
打开时尝试使用此代码更改质量类型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];
Run Code Online (Sandbox Code Playgroud)
Swift 5和高质量的代码
以下是按照此链接中的代码执行此操作的方法。链接的问题是它只适用于.mov文件输出,如果你想输出一个.mp4文件它会崩溃。下面的代码让你得到一个.mp4输出。它已经过尝试、测试并且有效。例如,最初为 27mb 的 15 秒视频减少到 2mb。如果您想要更好的质量,请提高bitrate. 我将它设置为1250000。
c+p 这段代码:
import AVFoundation
// add these properties
var assetWriter: AVAssetWriter!
var assetWriterVideoInput: AVAssetWriterInput!
var audioMicInput: AVAssetWriterInput!
var videoURL: URL!
var audioAppInput: AVAssetWriterInput!
var channelLayout = AudioChannelLayout()
var assetReader: AVAssetReader?
let bitrate: NSNumber = NSNumber(value: 1250000) // *** you can change this number to increase/decrease the quality. The more you increase, the better the video quality but the the compressed file size will also increase
// compression function, it returns a .mp4 but you can change it to .mov inside the do try block towards the middle. Change assetWriter = try AVAssetWriter ... AVFileType.mp4 to AVFileType.mov
func compressFile(_ urlToCompress: URL, completion:@escaping (URL)->Void) {
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 {
print("Could not iniitalize asset reader probably failed its try catch")
// show user error message/alert
return
}
guard let videoTrack = asset.tracks(withMediaType: AVMediaType.video).first else { return }
let videoReaderSettings: [String:Any] = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32ARGB]
let assetReaderVideoOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: videoReaderSettings)
var assetReaderAudioOutput: AVAssetReaderTrackOutput?
if let audioTrack = asset.tracks(withMediaType: AVMediaType.audio).first {
let audioReaderSettings: [String : Any] = [
AVFormatIDKey: kAudioFormatLinearPCM,
AVSampleRateKey: 44100,
AVNumberOfChannelsKey: 2
]
assetReaderAudioOutput = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: audioReaderSettings)
if reader.canAdd(assetReaderAudioOutput!) {
reader.add(assetReaderAudioOutput!)
} else {
print("Couldn't add audio output reader")
// show user error message/alert
return
}
}
if reader.canAdd(assetReaderVideoOutput) {
reader.add(assetReaderVideoOutput)
} else {
print("Couldn't add video output reader")
// show user error message/alert
return
}
let videoSettings:[String:Any] = [
AVVideoCompressionPropertiesKey: [AVVideoAverageBitRateKey: self.bitrate],
AVVideoCodecKey: AVVideoCodecType.h264,
AVVideoHeightKey: videoTrack.naturalSize.height,
AVVideoWidthKey: videoTrack.naturalSize.width,
AVVideoScalingModeKey: AVVideoScalingModeResizeAspectFill
]
let audioSettings: [String:Any] = [AVFormatIDKey : kAudioFormatMPEG4AAC,
AVNumberOfChannelsKey : 2,
AVSampleRateKey : 44100.0,
AVEncoderBitRateKey: 128000
]
let audioInput = AVAssetWriterInput(mediaType: AVMediaType.audio, outputSettings: audioSettings)
let videoInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: videoSettings)
videoInput.transform = videoTrack.preferredTransform
let videoInputQueue = DispatchQueue(label: "videoQueue")
let audioInputQueue = DispatchQueue(label: "audioQueue")
do {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"
let date = Date()
let tempDir = NSTemporaryDirectory()
let outputPath = "\(tempDir)/\(formatter.string(from: date)).mp4"
let outputURL = URL(fileURLWithPath: outputPath)
assetWriter = try AVAssetWriter(outputURL: outputURL, fileType: AVFileType.mp4)
} catch {
assetWriter = nil
}
guard let writer = assetWriter else {
print("assetWriter was nil")
// show user error message/alert
return
}
writer.shouldOptimizeForNetworkUse = true
writer.add(videoInput)
writer.add(audioInput)
writer.startWriting()
reader.startReading()
writer.startSession(atSourceTime: CMTime.zero)
let closeWriter:()->Void = {
if (audioFinished && videoFinished) {
self.assetWriter?.finishWriting(completionHandler: { [weak self] in
if let assetWriter = self?.assetWriter {
do {
let data = try Data(contentsOf: assetWriter.outputURL)
print("compressFile -file size after compression: \(Double(data.count / 1048576)) mb")
} catch let err as NSError {
print("compressFile Error: \(err.localizedDescription)")
}
}
if let safeSelf = self, let assetWriter = safeSelf.assetWriter {
completion(assetWriter.outputURL)
}
})
self.assetReader?.cancelReading()
}
}
audioInput.requestMediaDataWhenReady(on: audioInputQueue) {
while(audioInput.isReadyForMoreMediaData) {
if let cmSampleBuffer = assetReaderAudioOutput?.copyNextSampleBuffer() {
audioInput.append(cmSampleBuffer)
} else {
audioInput.markAsFinished()
DispatchQueue.main.async {
audioFinished = true
closeWriter()
}
break;
}
}
}
videoInput.requestMediaDataWhenReady(on: videoInputQueue) {
// request data here
while(videoInput.isReadyForMoreMediaData) {
if let cmSampleBuffer = assetReaderVideoOutput.copyNextSampleBuffer() {
videoInput.append(cmSampleBuffer)
} else {
videoInput.markAsFinished()
DispatchQueue.main.async {
videoFinished = true
closeWriter()
}
break;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果您要压缩URL. 在回调中返回compressedURL:
@IBAction func buttonTapped(sender: UIButton) {
// show activity indicator
let videoURL = URL(string: "...")
compressFile(videoURL) { (compressedURL) in
// remove activity indicator
// do something with the compressedURL such as sending to Firebase or playing it in a player on the *main queue*
}
}
Run Code Online (Sandbox Code Playgroud)
仅供参考,我注意到音频使事情变慢了很多,您也可以在后台任务中尝试此操作,看看它是否运行得更快。如果您在compressFile函数本身中添加了类似于警报的任何内容,则必须在 mainQueue 上显示它,否则应用程序将崩溃。
DispatchQueue.global(qos: .background).async { [weak self] in
self?.compressFile(videoURL) { (compressedURL) in
DispatchQueue.main.async { [weak self] in
// also remove activity indicator on mainQueue in addition to whatever is inside the function itself that needs to be updated on the mainQueue
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果您要压缩混合组合,请按以下步骤操作。您将需要使用 an AVMutableComposition、 anAVAssetExportSession和compressFile(:completion:)上面的函数:
@IBAction func buttonTapped(sender: UIButton) {
// show activity indicator
let mixComposition = AVMutableComposition()
// code to create mix ...
// create a local file
let tempDir = NSTemporaryDirectory()
let dirPath = "\(tempDir)/videos_\(UUID().uuidString).mp4"
let outputFileURL = URL(fileURLWithPath: dirPath)
removeUrlFromFileManager(outputFileURL) // check to see if the file already exists, if it does remove it, code is at the bottom of the answer
createAssetExportSession(mixComposition, outputFileURL)
}
// here is the AssetExportSession function with the compressFile(:completion:) inside the callback
func createAssetExportSession(_ mixComposition: AVMutableComposition, _ outputFileURL: URL) {
// *** If your video/url doesn't have sound (not mute but literally no sound, my iPhone's mic was broken when I recorded the video), change this to use AVAssetExportPresetPassthrough instead of HighestQulity. When my video didn't have sound the exporter.status kept returning .failed *** You can check for sound using /sf/answers/4531353641/
guard let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) else {
// alert user there is a problem
return
}
exporter.outputURL = outputFileURL
exporter.outputFileType = AVFileType.mp4
exporter.shouldOptimizeForNetworkUse = true
exporter.exportAsynchronously {
switch exporter.status {
case .completed:
print("completed")
// view the AssetExportSession file size using HighestQuality which will be very high
do {
let data = try Data(contentsOf: outputFileURL)
print("createAssetExportSession -file size: \(Double(data.count / 1048576)) mb")
} catch let err as NSError {
print("createAssetExportSession Error: \(err.localizedDescription)")
}
case .failed:
print("failed:", exporter.error as Any)
DispatchQueue.main.async { [weak self] in
// remove activity indicator
// alert user there is a problem
}
return
case .cancelled:
print("cancelled", exporter.error as Any)
DispatchQueue.main.async { [weak self] in
// remove activity indicator
// alert user there is a problem
}
return
default:
print("complete")
}
guard let exporterOutputURL = exporter.outputURL else {
// alert user there is a problem
return
}
DispatchQueue.main.async { [weak self] in
self?.compressFile(exporterOutputURL) { (compressedURL) in
// remove activity indicator
// do something with the compressedURL such as sending to Firebase or playing it in a player on the *main queue*
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
确保在完成后从文件系统中删除compressedURL,例如在关闭vc之前
func dismissVC() {
removeUrlFromFileManager(compressedURL)
// dismiss vc ...
}
removeUrlFromFileManager(_ outputFileURL: URL?) {
if let outputFileURL = outputFileURL {
let path = outputFileURL.path
if FileManager.default.fileExists(atPath: path) {
do {
try FileManager.default.removeItem(atPath: path)
print("url SUCCESSFULLY removed: \(outputFileURL)")
} catch {
print("Could not remove file at url: \(outputFileURL)")
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
55388 次 |
| 最近记录: |