Rotation of video to be sent to Web Service - iphone

What I'm trying to do sounds simple enough but I can't figure out for the life of me how to do it. When sending a video to my webservice it is always rotated 90 degrees. I want to be able to rotate it so it isn't sideways. I have all the code working perfectly up until this point.
I had the same issue with pictures also, but I was able to make a category that checked the orientation of UIImage object and adjust it accordingly.
The following is the code I'm using to save the movie. At some point before I send the video off, I want to have it rotated.
else if ([mediaType isEqualToString:(NSString *)kUTTypeMovie])
{
NSLog(#"Movie");
NSData *videoData = [NSData dataWithContentsOfURL:videoURL];
NSString *moviePath = [[info objectForKey:UIImagePickerControllerMediaURL]path];
if(UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(moviePath)){
UISaveVideoAtPathToSavedPhotosAlbum(moviePath, self, #selector(video:didFinishSavingWithError:contextInfo:), nil);
[[WebCall sharedClient] postRequestWithVideo:^(AFHTTPRequestOperation *operation, id responseObject) {
connectionLabel.text = operation.responseString;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
connectionLabel.text = operation.responseString;
} atPath:videoData];
}
}
Please Help.

Related

Copy downloaded video to camera roll

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!

having issue in playing video using AVFoundation framework

I am getting frame buffer one by one from video file using AVAssetReader and doing some operation on the frame and then saving new frame to temp file using AVAssetWritter.Now I have temp file path where all new frame is saving one by one. Is there any way to play video at the time frames is continuously adding to temp file?
here is code to play video from temp path(where frames is continuously adding):
(void)loadAssetFromFile {
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:[(mMediaReader.mCameraRecorder) tempVideoFilePath ]] options:nil];
NSString *tracksKey = #"tracks";
[asset loadValuesAsynchronouslyForKeys:[NSArray arrayWithObject:tracksKey] completionHandler:
^{
// Completion handler block.
dispatch_async(dispatch_get_main_queue(),
^{
NSError *error = nil;
AVKeyValueStatus status = [asset statusOfValueForKey:tracksKey error:&error];
if (status == AVKeyValueStatusLoaded) {
self.mPlayerItem = [AVPlayerItem playerItemWithAsset:asset];
[mPlayerItem addObserver:self forKeyPath:#"status"
options:0 context:&ItemStatusContext];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:mPlayerItem];
self.mPlayer = [AVPlayer playerWithPlayerItem:mPlayerItem];
[mPlayerView setPlayer:mPlayer];
[self play:nil];
}
else {
// You should deal with the error appropriately.
NSLog(#"The asset's tracks were not loaded:\n%#", [error localizedDescription]);
}
});
}];
}
(IBAction)play:sender {
[mPlayer play];
}
And code inside the block never runs.
You can convert the CMSampleBuffer that the AVAssetReader returns into a CGImage and then a UIImage and display that in a UIImageView, to render the frames as they are pulled out of the original video file.
There is example code inside the AVFoundation Programming Guide that shows how to do this conversion.

exif data in NSData image representation

I'm trying to create a PhoneGap plugin which uses AFFoundation to capture photos and return the image as base64 encoded string. This all works fine but the exif data seems to be missing from the returned image. Tried a few variations and got some pointers from other questions asked here but it still doesn't seem to work correctly. The current code is
NSMutableString *stringToReturn = [NSMutableString stringWithString: #""];
NSLog(#"about to request a capture from: %#", stillImageOutput);
[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageBuffer, NSError *error)
{
if (imageBuffer != NULL)
{
CFDictionaryRef exifAttachments = CMGetAttachment(imageBuffer, kCGImagePropertyExifDictionary, NULL);
CFMutableDictionaryRef mutable;
if (exifAttachments)
{
//
// Set orientation
//
mutable = CFDictionaryCreateMutableCopy(NULL, 0, exifAttachments);
int orientation = (int)[UIDevice currentDevice].orientation;
CFDictionaryAddValue(mutable, kCGImagePropertyOrientation, CFNumberCreate(NULL, kCFNumberIntType, &orientation));
CMSetAttachments(imageBuffer, mutable, kCMAttachmentMode_ShouldPropagate);
NSLog(#"attachements: %#", mutable);
}
//
// Get the image
//
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageBuffer];
[stringToReturn setString:[imageData base64EncodedString]];
//
// Call the success callback
//
[self performSelectorOnMainThread:#selector(doSuccessCallback:) withObject:[NSString stringWithString:stringToReturn] waitUntilDone:NO];
//
// Save it to the photo album
// It is done after we did the success callback to avoid accidental issues with
// the photo album stopping further processing
//
UIImage *image = [[UIImage alloc] initWithData:imageData];
UIImageWriteToSavedPhotosAlbum(image, self, #selector(image:didFinishSavingWithError:contextInfo:), nil);
}
}];
Can anyone shed some light please? I seem to be completely stuck here.

AVCaptureMovieFileOutput causing wrong orientation when saving photo to Camera Roll

I have an odd problem with captureStillImageAsynchronouslyFromConnection. If I save the image using jpegStillImageNSDataRepresentation while the video is mirrored, the image in the camera roll is rotated 90 degrees clockwise. However, if it's not mirrored, the orientation is fine.
I'll post the code, anyone else have this problem/know of a fix?
Update: Just ran some tests, the heights and widths (640x480) are fine and reflect the device's orientation. When I Take a picture in portrait, it reports UIImageOrientationLeft and when mirrored, UIImageOrientationLeftMirrored.
Update 2: When I view the saved photo in the camera roll, the preview of the image has the right orientation, as does the image when you swipe between photos, but when the photo is fully loaded, it rotates 90 degrees. Could this be a camera roll problem? (I'm on 4.3.3)
- (void) captureImageAndSaveToCameraRoll
{
AVCaptureConnection *stillImageConnection = [AVCamUtilities connectionWithMediaType:AVMediaTypeVideo fromConnections:[[self stillImageOutput] connections]];
if ([stillImageConnection isVideoOrientationSupported])
[stillImageConnection setVideoOrientation:[self orientation]];
[[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:stillImageConnection
completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
ALAssetsLibraryWriteImageCompletionBlock completionBlock = ^(NSURL *assetURL, NSError *error) {
if (error) {
if ([[self delegate] respondsToSelector:#selector(captureManager:didFailWithError:)]) {
[[self delegate] captureManager:self didFailWithError:error];
}
}
};
if (imageDataSampleBuffer != NULL) {
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
UIImage *image = [[UIImage alloc] initWithData:imageData];
[library writeImageToSavedPhotosAlbum:[image CGImage]
orientation:(ALAssetOrientation)[image imageOrientation]
completionBlock:completionBlock];
[image release];
[library release];
}
else
completionBlock(nil, error);
if ([[self delegate] respondsToSelector:#selector(captureManagerStillImageCaptured:)]) {
[[self delegate] captureManagerStillImageCaptured:self];
}
}];
}
I'm wondering if you the newly created image really has it's orientation set as you are assuming here:
[library writeImageToSavedPhotosAlbum:[image CGImage] orientation: (ALAssetOrientation) [image imageOrientation] completionBlock:completionBlock];
[image imageOrientation] in particular seems potentially the source of the problem, especially with the cast required...
you say you are seeing the image orientation somewhere, is it from [image imageOrientation] just after creating it with:
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation: imageDataSampleBuffer];
UIImage *image = [[UIImage alloc] initWithData:imageData];
I'm wondering if you are assuming that this metadata is in the imageData returned from AVCaptureStillImageOutput jpegStillImageNSDataRepresentation: when that in fact only contains the image pixel data itself?
According to some reports, the iPhone's photos application back in 2009 didn't support any of the mirrored orientations at all; it's certainly possible that they partially fixed this but still have bugs in some cases (I know, for example, that UIImageView doesn't handle contentStretch correctly in some cases). This should be easy enough to test: just load the sample images from that blog into the camera roll and see what happens.
Create the category for UIImage described in iOS UIImagePickerController result image orientation after upload
Call this method on the image before saving it to your library.

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]);
}