Copy downloaded video to camera roll - iphone

Even though it looks like a simple procedure, it's by now 3 hours I'm trying without success. I am probably missing something really stupid.
So, I have this app downloading videos from the Internet. The videos are correctly stored locally because I can play them providing the local url. But, I can't succeed in copying the videos to the camera roll. Here is what I do:
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
ALAssetsLibraryWriteVideoCompletionBlock videoWriteCompletionBlock =
^(NSURL *newURL, NSError *error) {
if (error) {
NSLog( #"Error writing image with metadata to Photo Library: %#", error );
} else {
NSLog( #"Wrote image with metadata to Photo Library %#", newURL.absoluteString);
}
};
NSLog(#"file %#", localPath);
NSURL *url = [NSURL fileURLWithPath:localPath isDirectory:NO];
[library writeVideoAtPathToSavedPhotosAlbum:url
completionBlock:videoWriteCompletionBlock];
But the output I get is:
2013-07-24 00:13:32.094 App[1716:907] file /var/mobile/Applications/70C18C4E-9F97-4A6A-B63E-1BD19961F010/Documents/downloaded_video.mp4
2013-07-24 00:13:32.374 App[1716:907] Wrote image with metadata to Photo Library (null)
And of course the file is not saved in the camera roll. It's a simple mp4, compatible with the device I'm using (i.e. it should be possible to save it).
I honestly have no idea what to do. Any hint will be highly appreciated. Thanks

I may have found a workaround for you. Have you tried an AVAssetExportSession?
In the sample below, I built a simple app that has two buttons on the screen. One calls onSaveBtn:, which simply grabs the URL of a video I have in my app's resource bundle and saves it to the user's saved photos album. (Though, in my case my videos do return YES from videoAtPathIsCompatibleWithSavedPhotosAlbum:. I didn't have any videos that don't return otherwise.)
The second button is wired to onExportBtn:, which takes the video we want to save, creates an AVAssetExportSession, exports the video to a temp directory, and then copies the exported video to the saved photos album. Due to the export time, this method does take longer than a simple copy, but maybe this could be an alternate path - check the results of videoAtPathIsCompatibleWithSavedPhotosAlbum:, and if YES, copy directly to the album. Otherwise, export the video, then copy.
Without having a video file that doesn't return NO to the compatibility call, I'm not 100% sure this would work for you, but it's worth a shot.
You may also want to check out this question, which explores what video formats are compatible on the device you may be using.
#import <AVFoundation/AVFoundation.h>
#import <AssetsLibrary/AssetsLibrary.h>
- (IBAction)onSaveBtn:(id)sender
{
NSURL *srcURL = [[NSBundle mainBundle] URLForResource:#"WP_20121214_001" withExtension:#"mp4"];
[self saveToCameraRoll:srcURL];
}
- (IBAction)onExportBtn:(id)sender
{
NSURL *srcURL = [[NSBundle mainBundle] URLForResource:#"WP_20121214_001" withExtension:#"mp4"];
AVAsset *srcAsset = [AVAsset assetWithURL:srcURL];
// create an export session
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:srcAsset presetName:AVAssetExportPresetHighestQuality];
// Export the file to a tmp dir
NSString *fileName = [srcURL lastPathComponent];
NSString *tmpDir = NSTemporaryDirectory();
NSURL *tmpURL = [NSURL fileURLWithPath:[tmpDir stringByAppendingPathComponent:fileName]];
exportSession.outputURL = tmpURL;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
// now copy the tmp file to the camera roll
switch ([exportSession status]) {
case AVAssetExportSessionStatusFailed:
NSLog(#"Export failed: %#", [[exportSession error] localizedDescription]);
break;
case AVAssetExportSessionStatusCancelled:
NSLog(#"Export canceled");
break;
case AVAssetExportSessionStatusCompleted:
NSLog(#"Export successful");
[self saveToCameraRoll:exportSession.outputURL];
break;
default:
break;
}
}];
}
- (void) saveToCameraRoll:(NSURL *)srcURL
{
NSLog(#"srcURL: %#", srcURL);
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
ALAssetsLibraryWriteVideoCompletionBlock videoWriteCompletionBlock =
^(NSURL *newURL, NSError *error) {
if (error) {
NSLog( #"Error writing image with metadata to Photo Library: %#", error );
} else {
NSLog( #"Wrote image with metadata to Photo Library %#", newURL.absoluteString);
}
};
if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:srcURL])
{
[library writeVideoAtPathToSavedPhotosAlbum:srcURL
completionBlock:videoWriteCompletionBlock];
}
}

Where are you providing the URL to the block.
I think you need to do this way..
NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL];
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeVideoAtPathToSavedPhotosAlbum:videoURL completionBlock:^(NSURL *assetURL, NSError *error){
/*notify of completion*/
NSLog(#"AssetURL: %#",assetURL);
NSLog(#"Error: %#",error);
if (!error) {
//video saved
}else{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:error.domain delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
[alert release];
}
}];
You can change the url here, I have used for the imagePickerController..
See if its helps you..

Here's a shorter answer.
In my case, I've used AFNetworking to download a video from a URL and in the downloadCompletedBlock of the download operation, responseObject returns the download file. Logging responseObject returns me the full file path of the downloaded video.
If you are using another method to download videos, simply replace responseObject with the full file path of your video, probably with the usual NSSearchPathForDirectoriesInDomains method.
Here's the snippet I use to export videos in the application's local file directory to the Camera Roll:
NSURL *responseObjectPath = [NSURL URLWithString:responseObject];
// If video is compatible with Camera Roll
if ([[ALAssetsLibrary new] videoAtPathIsCompatibleWithSavedPhotosAlbum:responseObjectPath])
{
// Export to Camera Roll
[[ALAssetsLibrary new] writeVideoAtPathToSavedPhotosAlbum:responseObjectPath completionBlock:nil];
}
else
{
NSLog(#"Incompatible File Type");
}
Cheers!

Related

iOS: How do i prevent temporary files Visibility

i am working on a app that generates temporary video from the still image. But i don't want the user to view the temporary file what i have generated. But the moment when the app is generating the video file,the video is listing out by the camera roll.
my Question is How can i generate the temporary media file that could't have access to 'camera roll,iTunes and iCloud'?
please help me. thanks in advance.
Code:
[assetWriter finishWritingWithCompletionHandler:^
{
NSURL *url = _outMovieURL; ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeVideoAtPathToSavedPhotosAlbum:url
completionBlock:^(NSURL *assetURL, NSError *error)
{
NSLog(#"Done!");
}];
}];
This block:
[assetWriter finishWritingWithCompletionHandler:^
{
NSURL *url = _outMovieURL; ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeVideoAtPathToSavedPhotosAlbum:url
completionBlock:^(NSURL *assetURL, NSError *error)
{
NSLog(#"Done!");
}];
}];
Is to write file in camera roll. So don't use this while you have only created temporary video. Write that temporary video file in documentDirectory instead, (or probably it's already in documentDirectory, while you are creating it, all you need to do is to not write that file in camera roll with this code).
You should use NSTemporaryDirectory() and append your own affix at the end for any temporary files. Files in that directory will be cleaned by the OS or you can delete right after you are done with it.

How can I save videos to the Photo Album with specific name

When I try to save video by ALAssetLibrary method writeVideoAtPathToSavedPhotosAlbum the video's will be saved by name which will generated by AssetLibrary e.g. IMG_0111, IMG_0123 etc. How can I save these video's by specific name like "myAppName1.mov"
Following is the code snippet.
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeVideoAtPathToSavedPhotosAlbum:tempURL
completionBlock:^(NSURL *assetURL, NSError *error)
{
if(error)
{
}
else
{
NSLog(#"Saved video at %#\n", [assetURL path]);
}
}];
[library release], library = nil;
There is no way to achieve this at the moment. You can't specify a file name, the Photolibrary assigns as you described filenames conforming to the pattern "IMG_XXX.ext" automatically.
The only case, when the file name is preserved is when photos get imported through the camera connection kit.
Cheers,
Hendrik

how to check if a file ( video) is already in ALAssetsLibrary (photo album ) before saving it?

I'm saving a file from a server to ALAssetLibrary so that the user can view it in offline mode
however i don't want the user to download a single file multiple times to avoid creating duplicates in the photo album application.
that's my code :
ALAssetsLibrary* library = [[ALAssetsLibrary alloc] init];
void (^completionBlock)(NSURL *, NSError *) = ^(NSURL *assetURL, NSError *error)
{
if ( error != nil )
{
//writeFailed = YES;
NSLog(#"error = %#", error);
}
//[self notifyCompletionTarget];
NSLog(#" saving is done");
};
NSURL *url = [[NSURL alloc] initFileURLWithPath:path];
[library writeVideoAtPathToSavedPhotosAlbum:url completionBlock:completionBlock ];
}
I think You Cant. You can use many algorithms to compare the files like SHA-1, MD5,CRC etc. However in the case of ALAsset, the mentioned all will fail.
Because , Whenever you are saving a file to Photo-Library using ALAsset, it will store the location, time,etc.. information's with the file. So the same file contend will be different corresponding to the save process .
The all above algorithms are regarding to the file contend.So they all will fail in the case of ALAsset

How can I use AVAssetExportSession to overwrite songs in my iPhone/iPod library?

So supposedly in the iOS 4 SDK you can edit and write to the user's iTunes library. I can successfully load an AVAsset from my iPhone/iPod library, but as a quick test I'm trying to just overwrite the same file right away using AVAssetExportSession but it's always returning the status "4" which I THINK is AVAssetExportSessionStatusFailed... In the documentation it says:
enum {
AVAssetExportSessionStatusUnknown,
AVAssetExportSessionStatusExporting,
AVAssetExportSessionStatusCompleted,
AVAssetExportSessionStatusFailed,
AVAssetExportSessionStatusCancelled,
AVAssetExportSessionStatusWaiting
};
but in AVAssetExportSession.h it says:
enum {
AVAssetExportSessionStatusUnknown,
AVAssetExportSessionStatusWaiting,
AVAssetExportSessionStatusExporting,
AVAssetExportSessionStatusCompleted,
AVAssetExportSessionStatusFailed,
AVAssetExportSessionStatusCancelled
};
typedef NSInteger AVAssetExportSessionStatus;
Here's the code I'm using:
// before this, i'm using mpmediapicker to pick an m4a file i synched with my itunes library
NSURL *assetUrl = [[self.userMediaItemCollection.items objectAtIndex: 0] valueForProperty: MPMediaItemPropertyAssetURL];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL: assetUrl options: nil];
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset: asset presetName: AVAssetExportPresetAppleM4A];
exportSession.outputURL = asset.URL;
exportSession.outputFileType = AVFileTypeAppleM4A;
NSLog(#"output filetype: %#", exportSession.outputFileType);
// prints "com.apple.m4a-audio"
[exportSession exportAsynchronouslyWithCompletionHandler: ^(void) {
NSLog(#"status: %i for %#", exportSession.status, exportSession.outputURL);
// prints "status: 4 for ipod-library://item/item.m4a?id=3631988601206299774"
}];
[exportSession release];
So either way... I guess it's "failed" or "cancelled." Has anyone else successfully written to the media library before?
Thanks!
you cannot write to itunes library, only read from it now.
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
NSParameterAssert(library);
if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:[NSURL fileURLWithPath:movieFileName]]) {
   [library writeVideoAtPathToSavedPhotosAlbum:[NSURL fileURLWithPath:movieFileName] completionBlock:^(NSURL *assetURL, NSError *error){}];
}
[library release];

AVAudioPlayer only initializes with some files

I'm having trouble playing some files with AVAudioPlayer. When I try to play a certain m4a, it works fine. It also works with an mp3 that I try. However it fails on one particular mp3 every time (15 Step, by Radiohead), regardless of the order in which I try to play them. The audio just does not play, though the view loading and everything that happens concurrently happens correctly. The code is below. I get the "Player loaded." log output on the other two songs, but not on 15 Step. I know the file path is correct (I have it log outputted earlier in the app, and it is correct). Any ideas?
NSData *musicData = [NSData dataWithContentsOfURL:[[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:[song filename] ofType:nil]]];
NSLog([[NSBundle mainBundle] pathForResource:[song filename] ofType:nil]);
if(musicData)
{
NSLog(#"File found.");
}
self.songView.player = [[AVAudioPlayer alloc] initWithData:musicData error:nil];
if(self.songView.player)
{
NSLog(#"Player loaded.");
}
[self.songView.player play];
NSLog(#"You should be hearing something now.");
Sorry for entering the party so late. Just want to post a tip so that It will be useful for any person referring this in the future.
A small correction to shaggy frog's answer. The correct usage would be:
NSError *anError = nil;
anAVAudioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:someURL] error:&anError];
if (!anAVAudioPlayer) {
NSLog(#"localizedDescription : %#", [anError localizedDescription]);
}
NSData *musicData = [NSData dataWithContentsOfURL:[[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:[song filename] ofType:nil]]];
Here, is musicData nil for the problem case?
Also:
self.songView.player = [[AVAudioPlayer alloc] initWithData:musicData error:nil];
You shouldn't pass nil for methods that take an NSError object. Likely it will shed more light on your problem if you use it correctly:
NSError* error = nil;
self.songView.player = [[AVAudioPlayer alloc] initWithData:musicData error:&error];
if (error)
{
NSLog(#"Error with initWithData: %#", [error localizedDescription]);
}