J'ai une application qui permet à un utilisateur d'enregistrer une vidéo avec UIImagePickerController
puis de la télécharger sur YouTube. Le problème est que le fichier vidéo créé par UIImagePickerController
est ÉNORME, même lorsque la vidéo ne dure que 5 secondes. Par exemple, une vidéo de 5 secondes fait 16-20 mégaoctets. Je souhaite conserver la vidéo en qualité 540 ou 720, mais je souhaite réduire la taille du fichier.
J'ai testé AVFoundation et AVAssetExportSession
pour essayer d'obtenir une taille de fichier plus petite. J'ai essayé le code suivant:
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!");
}];
Mais cela n'a pas du tout réduit la taille du fichier. Je sais que ce que je fais est possible parce que dans l'application Photos d'Apple, lorsque vous sélectionnez "Partager sur YouTube", traitera automatiquement le fichier vidéo afin qu'il soit suffisamment petit pour être téléchargé. Je veux faire la même chose dans mon application.
Comment puis-je accomplir cela?
Avec AVCaptureSession
et AVAssetWriter
, vous pouvez définir les paramètres de compression comme tels:
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: Je suppose que si vous insistez pour utiliser le UIImagePicker
pour créer le film en premier lieu, vous devrez utiliser AVAssetReader's
copyNextSampleBuffer
et AVAssetWriter's
appendSampleBuffer
méthodes pour effectuer le transcodage.
yourfriendzak a raison: définir cameraUI.videoQuality = UIImagePickerControllerQualityTypeLow;
n'est pas la solution ici. La solution est de réduire le débit de données, ou débit binaire, ce que suggère jgh.
J'ai, trois méthodes. La première méthode gère la méthode déléguée 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];
}
La deuxième méthode convertit la vidéo à un débit binaire inférieur, et non à des dimensions inférieures.
- (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];
}];
}
}
}
}
];
}
}
}
}
];
}
En cas de succès, la troisième méthode, sendMovieFileAtURL:
est appelé, qui télécharge la vidéo compressée à outputURL
sur le serveur.
Notez que j'ai activé ARC dans mon projet, vous devrez donc ajouter des appels release
si ARC est désactivé dans le vôtre.
Sur UImagePickerController
vous avez une propriété videoQuality
de type UIImagePickerControllerQualityType
, et sera appliquée aux films enregistrés ainsi qu'à ceux que vous avez choisis à partir de la bibliothèque (cela se produit pendant la phase de transcodage).
Ou si vous devez gérer un élément existant (fichier) ne provenant pas de la bibliothèque, vous pouvez consulter ces paramètres prédéfinis:
AVAssetExportPresetLowQuality
AVAssetExportPresetMediumQuality
AVAssetExportPresetHighestQuality
et
AVAssetExportPreset640x480
AVAssetExportPreset960x540
AVAssetExportPreset1280x720
AVAssetExportPreset1920x1080
et passez l'un d'eux à initialiseur de la classe AVAssetExportSession
. Je crains que vous ne deviez jouer avec ceux-ci pour votre contenu particulier car il n'y a pas de description précise de la qualité de low
et medium
ou de la qualité qui sera utilisée pour 640x480
ou pour 1280x720
préréglé. Les seules informations utiles dans les documents sont les suivantes:
Exporter des noms prédéfinis pour les fichiers QuickTime appropriés au périphérique Vous utilisez ces options d'exportation pour produire des fichiers QuickTime .mov avec une taille vidéo appropriée au périphérique actuel.
L'exportation n'augmente pas la vidéo à partir d'une taille plus petite. La vidéo est compressée en utilisant H.264; l'audio est compressé en utilisant AAC
Certains appareils ne peuvent pas prendre en charge certaines tailles.
En dehors de cela, je ne me souviens pas avoir eu un contrôle précis sur la qualité, comme la taille du framerate ou la forme libre, etc. dans AVFoundation
Je me trompais, il existe un moyen de modifier tous les paramètres que vous mentionnez et il s'agit en effet d'AVAssetWriter: Comment puis-je exporter le tableau UIImage en tant que film?
btw, voici un lien vers une question similaire avec un exemple de code: iPhone: compresser par programme la vidéo enregistrée à partager?
La réponse d'Erik était peut-être correcte au moment où il l'a écrite - mais maintenant, avec iOS8, ça plante juste à gauche et à droite, j'ai moi-même passé quelques heures dessus.
Vous avez besoin d'un doctorat pour travailler avec AVAssetWriter - ce n'est pas trivial: https://developer.Apple.com/library/mac/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/05_Export.html#//Apple_ref/ doc/uid/TP40010188-CH9-SW1
Il y a une bibliothèque incroyable pour faire exactement ce que vous voulez, qui est juste un remplacement drop-in AVAssetExportSession avec des fonctionnalités plus cruciales comme la modification du débit binaire: https://github.com/rs/SDAVAssetExportSession
Voici comment l'utiliser:
-(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);
}
}];
}
Code Erik Wegener réécrit en 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();
})
}
}
}
})
}
}
}
})
}
Vous pouvez définir la qualité vidéo lorsque vous souhaitez ouvrir UIImagePickerController
à l’une des options suivantes:
UIImagePickerControllerQualityType640x480
UIImagePickerControllerQualityTypeLow
UIImagePickerControllerQualityTypeMedium
UIImagePickerControllerQualityTypeHigh
UIImagePickerControllerQualityTypeIFrame960x540
UIImagePickerControllerQualityTypeIFrame1280x720
Essayez ce code pour changer le type de qualité lorsque vous ouvrez le 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];
Code Erik Wegener réécrit dans 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();
})
}
}
}
})
}
}
}
})
}
Je soutiens etayluz la réponse SDAVAssetExportSession est une classe personnalisée impressionnante pour faire la compression vidéo. Voici mon code travaillé. Vous pouvez télécharger SDAVAssetExportSession à partir de ce lien .
Après avoir téléchargé, ajoutez les fichiers SDAVAssetExportSession.h et SDAVAssetExportSession.m dans votre projet, puis utilisez le code ci-dessous pour effectuer la compression. Dans le code ci-dessous, vous pouvez compresser la vidéo en spécifiant la résolution et le débit binaire
#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");
}
}];
}
Pour annuler la compression, utilisez sous la ligne de code
[compressionEncoder cancelExport]; //Video compression cancel
Il existe une classe personnalisée impressionnante ( SDAVAssetExportSession ) pour effectuer la compression vidéo. Vous pouvez le télécharger à partir de ce lien .
Après avoir téléchargé, ajoutez les fichiers SDAVAssetExportSession.h et SDAVAssetExportSession.m dans votre projet, puis utilisez le code ci-dessous pour effectuer la compression. Dans le code ci-dessous, vous pouvez compresser la vidéo en spécifiant la résolution et le débit binaire
#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");
}
}];
}
Pour annuler la compression, utilisez sous la ligne de code
[compressionEncoder cancelExport]; //Video compression cancel
Swift 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)
})
}
}
}
})
}
}
}
})
}
Use exportSession.fileLengthLimit = 1048576 * 10 //10 MB
10 Mo est un numéro codé en dur. Utilisez selon votre débit requis.
fileLengthLimit
session ne doit pas dépasser. Selon le contenu de l'actif source, il est possible que la sortie dépasse légèrement la limite de longueur de fichier. La longueur du fichier de sortie doit être testée si vous souhaitez qu'une limite stricte soit observée avant d'utiliser la sortie. Voir aussi maxDuration et timeRange.Indique la longueur du fichier que la sortie du