I am using UIImagePickerController to record video, the problem is its recording the video in mov format for android compatibility.
I need to convert the video into mp4 format using below code the issue is its taking hell of a time for a 6 sec video it takes around 30 to 35 seconds.
Any solution that I can directly record the video in mp4 format or faster method would be of great help. Thanks in Advance
-(void)movToMp4:(NSURL *)videoURL{ // method for mov to mp4 conversion
AVURLAsset *avAsset = [AVURLAsset URLAssetWithURL:videoURL options:nil];
NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset];
if ([compatiblePresets containsObject:AVAssetExportPresetLowQuality])
{
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]initWithAsset:avAsset presetName:AVAssetExportPresetPassthrough];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* videoPath = [NSString stringWithFormat:#"%#/xyz.mp4", [paths objectAtIndex:0]];
exportSession.outputURL = [NSURL fileURLWithPath:videoPath];
exportSession.outputFileType = AVFileTypeMPEG4;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
switch ([exportSession status]) { // switch case to get completion case where i put my delegate
return;
break;
case AVAssetExportSessionStatusCompleted: {
[self.delegate mp4Response:videoPath];
break;
}
}
}
}];
}
}
Yes because it takes time when you load video from library / album using AVURLAsset.
So you need to use block here to load video from library.
Also line
[self.delegate mp4Response:videoPath];
Which in a completion block - It should be on main thread.
Follow this approach:
UIImagePickerController delegate method to get videos from library.
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSURL *localUrl = (NSURL *)[info valueForKey:UIImagePickerControllerMediaURL];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* videoPath = [NSString stringWithFormat:#"%#/xyz.mp4", [paths objectAtIndex:0]];
NSURL *outputURL = [NSURL fileURLWithPath:videoPath];
[self convertVideoToLowQuailtyWithInputURL:localUrl outputURL:outputURL handler:^(AVAssetExportSession *exportSession)
{
if (exportSession.status == AVAssetExportSessionStatusCompleted) {
NSLog(#"Capture video complete");
[self performSelectorOnMainThread:#selector(doneCompressing) withObject:nil waitUntilDone:YES];
}
}];
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)convertVideoToLowQuailtyWithInputURL:(NSURL*)inputURL outputURL:(NSURL*)outputURL handler:(void (^)(AVAssetExportSession*))handler {
[[NSFileManager defaultManager] removeItemAtURL:outputURL error:nil];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:inputURL options:nil];
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPresetPassthrough];
exportSession.outputURL = outputURL;
exportSession.outputFileType = AVFileTypeMPEG4;
[exportSession exportAsynchronouslyWithCompletionHandler:^(void) {
handler(exportSession);
}];
}
In didFinishPickingMediaWithInfo method observed this line:
[self performSelectorOnMainThread:#selector(doneCompressing) withObject:nil waitUntilDone:YES];
It will call a one more method doneCompressing in main thread (in foreground). So that you can call delegate method in doneCompressing. And this will reduce time.
- (void) doneCompressing {
[self.delegate mp4Response:videoPath];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSLog(#"file info = %#", info);
NSString *pickedType = [info objectForKey:#"UIImagePickerControllerMediaType"];
NSData *videoData;
if ([pickedType isEqualToString:#"public.movie"]){
NSURL *videoUrl=(NSURL*)[info objectForKey:UIImagePickerControllerMediaURL];
videoData = [NSData dataWithContentsOfURL:videoUrl];
}
[self dismissViewControllerAnimated:YES completion:^{
//
[self writeFileData:videoData];
}];
}
// to get path of document directory
- (NSString *)applicationDocumentsDirectory
{
// return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return documentsDirectory;
}
- (void) writeFileData:(NSData *)fileData{
float size = fileData.length / (1024 * 1024);
NSString *fileName = nil;
NSString *strPath = nil;
NSString *documentsDirectory = [self applicationDocumentsDirectory];
double CurrentTime = CACurrentMediaTime();
fileName = [NSString stringWithFormat:#"%d.mp4",(int)CurrentTime];
strPath = [documentsDirectory stringByAppendingPathComponent:fileName];
NSFileManager *filemanager=[[NSFileManager alloc] init];
NSError *er;
if (![filemanager fileExistsAtPath:documentsDirectory]) {
[filemanager createDirectoryAtPath:documentsDirectory withIntermediateDirectories:YES attributes:nil error:&er];
NSLog(#"error in folder creation = %#", er);
}
NSLog(#"size of data = %lu", (unsigned long)[fileData length]);
BOOL saved = [fileData writeToFile:strPath atomically:YES];
if (saved) {
NSURL *videoURL = [NSURL URLWithString:strPath];
// now u can handle mp4 video from videoURL
}
else
return;
}
Related
When I tried to copy a video from album to app using UIImagePickerController, the video is compressed.
When the video imported via Photos app on Mac, the ten-second long video is:
1920x1280 ~22MB
After the video is imported into app, then copied via iTunes Sharing, the compressed video is:
1280x720 ~6.3MB
How can I import video into app in original resolution and quality?
Code:
- (void)importVideo:(id)sender {
UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
imagePickerController.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
imagePickerController.mediaTypes = [[NSArray alloc] initWithObjects: (NSString *) kUTTypeMovie, nil];
imagePickerController.allowsEditing = YES;
imagePickerController.delegate = self;
[self presentViewController:imagePickerController animated:YES completion:nil];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSString *mediaType = [info objectForKey: UIImagePickerControllerMediaType];
[self dismissViewControllerAnimated:YES completion:nil];
if (CFStringCompare((__bridge_retained CFStringRef)mediaType, kUTTypeMovie, 0) == kCFCompareEqualTo) {
NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL];
NSData *videoData = [NSData dataWithContentsOfURL:videoURL];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *folder = [paths firstObject];
NSString *filename = [folder stringByAppendingPathComponent:self.uniqueFilename];
BOOL success = [videoData writeToFile:filename atomically:NO];
NSLog(#"Write to file %#", success ? #"OK" : #"Error");
}
}
Once a PHAsset is obtained via GMImagePicker, it can be saved by:
for (PHAsset *asset in assetArray) {
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
options.version = PHVideoRequestOptionsVersionOriginal;
[[PHImageManager defaultManager] requestAVAssetForVideo:asset
options:options
resultHandler:^(AVAsset *avAsset, AVAudioMix *audioMix, NSDictionary *info) {
if ([avAsset isKindOfClass:[AVURLAsset class]]) {
NSURL *videoURL = [(AVURLAsset *) avAsset URL];
NSData *videoData = [NSData dataWithContentsOfURL:videoURL];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *folder = [paths firstObject];
NSString *filename = [folder stringByAppendingPathComponent:#"foo.mp4"];
[videoData writeToFile:filename atomically:NO];
}
}];
}
Probably most of you using WhatsApp and you probably know that when you want to send a video to one of your contacts WhatsApp first of all compressing the video to maximum size of 16mb and only afterword it uploads the chosen video to your contact.
what i am trying to do it is simply the same thing using AV Foundation or to be more specific AVAssetExportSession.
here is my code:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSURL *videoURL = info[UIImagePickerControllerMediaURL];
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *sourcePath =[documentsDirectory stringByAppendingString:#"/output.mov"];
NSURL *outputURL = [NSURL fileURLWithPath:sourcePath];
[self convertVideoToLowQuailtyWithInputURL:videoURL outputURL:outputURL handler:^(AVAssetExportSession *exportSession)
{
if (exportSession.status == AVAssetExportSessionStatusCompleted)
{
NSLog(#"completed");
}
else
{
NSLog(#"error: %#",exportSession.error);
}
}];
[picker dismissViewControllerAnimated:YES completion:NULL];
}
- (void)convertVideoToLowQuailtyWithInputURL:(NSURL*)inputURL outputURL:(NSURL*)outputURL handler:(void (^)(AVAssetExportSession*))handler
{
[[NSFileManager defaultManager] removeItemAtURL:outputURL error:nil];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:inputURL options:nil];
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPresetMediumQuality];
exportSession.outputURL = outputURL;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
[exportSession exportAsynchronouslyWithCompletionHandler:^(void)
{
handler(exportSession);
}];
}
this code works wonderful, it takes the video and compressing it really small size.
the problem is when the user try to upload a quite long video with powerful camera the size is not small enough to me.
What i want to do is actually to compress any video to a limited size
lets say for example to 16Mb like WhatsApp do.
How can i do that?
It doesn't seems to exist an easy way but AVAssetExportSession has an estimatedOutputFileLenght that could help.
In my code I iterate over different qualities and check if the file size is in the size I want:
NSURL * inputURL = [NSURL fileURLWithPath:path];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:inputURL options:nil];
AVAssetExportSession *exportSession = nil;
for (NSString * string in #[AVAssetExportPresetHighestQuality,AVAssetExportPresetMediumQuality,AVAssetExportPresetLowQuality]) {
exportSession = [[AVAssetExportSession alloc] initWithAsset:asset presetName:string];
exportSession.shouldOptimizeForNetworkUse = YES;
exportSession.outputFileType = AVFileTypeMPEG4;
exportSession.timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration);
unsigned long long espectedFileSize = exportSession.estimatedOutputFileLength;
if (espectedFileSize < VIDE_LIMIT) {
break;
}
}
//Temp file
NSString *fileName = [NSString stringWithFormat:#"%#_%#", [[NSProcessInfo processInfo] globallyUniqueString], #"video.mov"];
NSString * filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
NSURL *fileURL = [NSURL fileURLWithPath:filePath];
exportSession.outputURL = fileURL;
[exportSession exportAsynchronouslyWithCompletionHandler:^(void)
I am recording video using UIImagePicker, then trying to save it to the documents directory in NSData format.
This part works, but then I cannot play it back? How do you get MediaPlayer to play an NSData object?
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
[self dismissViewControllerAnimated:YES completion:NULL];
self.videoURL = info[UIImagePickerControllerMediaURL];
NSLog(#"video url-%#",self.videoURL);
NSData *videoData = [NSData dataWithContentsOfURL:self.videoURL];
NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [documentDirectories firstObject];
NSString *videoPath = [documentDirectory stringByAppendingPathComponent:self.video.fileKey];
NSLog(#"video path-%#",videoPath);
[videoData writeToFile:videoPath atomically:YES];
self.videoURL = [NSURL fileURLWithPath:videoPath];
[self.videoController setContentURL:self.videoURL];
[self.videoController play];
}
Its OK I solved it. Was not added the .mov file extension when naming the new path.
How can I extract the Audio (preferably .mp3) from a .MOV file I got from the user's camera roll?
I extract the MOV file here:
- (void) imagePickerController: (UIImagePickerController *) picker
didFinishPickingMediaWithInfo: (NSDictionary *) info {
NSString *mediaType = [info objectForKey: UIImagePickerControllerMediaType];
[self dismissViewControllerAnimated:NO completion:nil];
if (CFStringCompare ((__bridge_retained CFStringRef)mediaType, kUTTypeMovie, 0)
== kCFCompareEqualTo) {
NSString *fileName = [[info objectForKey:
UIImagePickerControllerMediaURL] path];
NSData *data = [[NSData alloc] initWithContentsOfFile:moviePath];
[self dismissViewControllerAnimated:YES completion:nil];
}
}
Please let me know if more code need be provided but I think this is a sufficient amount of information.
I have tried a few things but gotten absolutely nowhere. Any help is appreciated. Thank you
I extract m4a file using the following piece of code and write the file application documents directory
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
AVMutableComposition *newAudioAsset = [AVMutableComposition composition];
AVMutableCompositionTrack *dstCompositionTrack = [newAudioAsset addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
// 1 - Get media type
NSString *mediaType = [info objectForKey: UIImagePickerControllerMediaType];
// 2 - Dismiss image picker
[self dismissViewControllerAnimated:YES completion:nil];
// 3 - Handle video selection
if (CFStringCompare ((__bridge_retained CFStringRef) mediaType, kUTTypeMovie, 0) == kCFCompareEqualTo) {
videoAsset = [AVAsset assetWithURL:[info objectForKey:UIImagePickerControllerMediaURL]];
// NSLog(#"track = %#",[videoAsset tracksWithMediaType:AVMediaTypeAudio]);
NSArray *trackArray = [videoAsset tracksWithMediaType:AVMediaTypeAudio];
if(!trackArray.count){
NSLog(#"Track returns empty array for mediatype AVMediaTypeAudio");
return;
}
AVAssetTrack *srcAssetTrack = [trackArray objectAtIndex:0];
//Extract time range
CMTimeRange timeRange = srcAssetTrack.timeRange;
NSError *err = nil;
if(NO == [dstCompositionTrack insertTimeRange:timeRange ofTrack:srcAssetTrack atTime:kCMTimeZero error:&err]){
NSLog(#"Failed to insert audio from the video to mutable avcomposition track");
return;
}
//Export the avcompostion track to destination path
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex: 0];
NSString *dstPath = [documentsDirectory stringByAppendingString:#"/sample_audio.m4a"];
NSURL *dstURL = [NSURL fileURLWithPath:dstPath];
//Remove if any file already exists
[[NSFileManager defaultManager] removeItemAtURL:dstURL error:nil];
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]initWithAsset:newAudioAsset presetName:AVAssetExportPresetPassthrough];
NSLog(#"support file types= %#", [exportSession supportedFileTypes]);
exportSession.outputFileType = AVFileTypeAppleM4A;
exportSession.outputURL = dstURL;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
AVAssetExportSessionStatus status = exportSession.status;
if(AVAssetExportSessionStatusCompleted != status){
NSLog(#"Export status not yet completed. Error: %#", exportSession.error.description);
}
}];
// UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Asset Loaded" message:#"Video Asset Loaded"
// delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
// [alert show];
}
}
I have a video file from the device camera -- stored as /private/var/mobile/Media/DCIM/100APPLE/IMG_0203.MOV, for example, and I need to cut the first 10 seconds of this video. What API or libraries I can use?
I found solution with standard API: AVAssetExportSession
- (void)getTrimmedVideoForFile:(NSString *)filePath withInfo:(NSArray *)info
{
//[[NSFileManager defaultManager] removeItemAtURL:outputURL error:nil];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:filePath] options:nil];
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPresetLowQuality];
// NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSAllDomainsMask, YES);
// NSString *outputURL = paths[0];
NSFileManager *manager = [NSFileManager defaultManager];
// [manager createDirectoryAtPath:outputURL withIntermediateDirectories:YES attributes:nil error:nil];
// outputURL = [outputURL stringByAppendingPathComponent:#"output.mp4"];
NSString *outputURL = [NSString stringWithFormat:#"/tmp/%#.mp4", [info objectAtIndex:2]];
NSLog(#"OUTPUT: %#", outputURL);
// Remove Existing File
// [manager removeItemAtPath:outputURL error:nil];
if (![manager fileExistsAtPath:outputURL]) {
exportSession.outputURL = [NSURL fileURLWithPath:outputURL];
exportSession.shouldOptimizeForNetworkUse = YES;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
CMTime start = kCMTimeZero;
CMTime duration = kCMTimeIndefinite;
if ([[NSString stringWithFormat:#"%#", [info objectAtIndex:3]] floatValue] > 20.0) {
start = CMTimeMakeWithSeconds(1.0, 600);
duration = CMTimeMakeWithSeconds(10.0, 600);
}
CMTimeRange range = CMTimeRangeMake(start, duration);
exportSession.timeRange = range;
[exportSession exportAsynchronouslyWithCompletionHandler:^(void) {
switch (exportSession.status) {
case AVAssetExportSessionStatusCompleted:
NSLog(#"Export Complete %d %#", exportSession.status, exportSession.error);
[self sendVideoPreview:info];
break;
case AVAssetExportSessionStatusFailed:
NSLog(#"Failed:%#",exportSession.error);
// [self addToDelayed:info withAction:#"add"];
break;
case AVAssetExportSessionStatusCancelled:
NSLog(#"Canceled:%#",exportSession.error);
// [self addToDelayed:info withAction:#"add"];
break;
default:
break;
}
}];
} else {
[self sendVideoPreview:info];
}