video rotation using AVMutableVideoCompositionLayerInstruction - iphone

I am merging multiple videos and I want to detect which ones are in portrait mode and rotate them in landscape so that all movies are in landscape mode... I have done everything and works perfectly except the actual rotate, I guess it's something with the center of the rotation or a composed rotation.
AVMutableVideoCompositionLayerInstruction *videoTrackLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:compositionVideoTrack];
if([[AppDelegate sharedInstance] orientationForTrack:avAsset] == UIDeviceOrientationPortrait)
{
CGAffineTransform rotation = CGAffineTransformMakeRotation(M_PI/2);
//CGAffineTransform translateToCenter = CGAffineTransformMakeTranslation(640, 480);
//CGAffineTransform mixedTransform = CGAffineTransformConcat(rotation, translateToCenter);
[videoTrackLayerInstruction setTransform:rotation atTime:nextClipStartTime];
}
else if([[AppDelegate sharedInstance] orientationForTrack:avAsset] == UIDeviceOrientationPortraitUpsideDown)
{
CGAffineTransform rotation = CGAffineTransformMakeRotation(-M_PI/2);
[videoTrackLayerInstruction setTransform:rotation atTime:nextClipStartTime];
}
else if([[AppDelegate sharedInstance] orientationForTrack:avAsset] == UIDeviceOrientationLandscapeLeft)
{
CGAffineTransform rotation = CGAffineTransformMakeRotation(M_PI);
[videoTrackLayerInstruction setTransform:rotation atTime:nextClipStartTime];
}
else if([[AppDelegate sharedInstance] orientationForTrack:avAsset] == UIDeviceOrientationLandscapeRight)
{
[videoTrackLayerInstruction setTransform:CGAffineTransformIdentity atTime:nextClipStartTime];
}
How can I rotate them properly? I have tried multiple sources but nothing rotates them as they should. I am not interested in the 320 scale fit solution I want the video to keep as much resolution as possible before exporting using AVAssetExportSession.
A solution like this:
CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(degreesToRadians(90.0));
CGAffineTransform rotateTranslate = CGAffineTransformTranslate(rotateTransform,320,0);
won't suit my needs as I have tried it. Do you have any ideas? Some help will be pretty much appreciated.

Related

Improper iPhone orientation?

I can't figure out why this isn't working. I'm writing a game that involves scrolling rows/columns of blocks around the screen. I have my orientation limited strictly to Portrait, but it doesn't behave as so.
When I call the method below, it is handed the velocity from the pan. Using logs I was able to see that holding my device upside-down and panning around acts as if my orientation supports Portrait. How can I -completely- turn off anything but Portrait?
Here's some code to help explain what I want to do, and to hopefully justify my sanity.
bool horizontalPan = (fabs(velocity.x) >= (fabs(velocity.y)));
if (horizontalPan)
{
if (fabs(velocity.x) > MAX_VELOCITY)
{
if (velocity.x > 0)
velocity = CGPointMake(MAX_VELOCITY, 0);
else
velocity = CGPointMake(-MAX_VELOCITY, 0);
}
else
{
velocity = CGPointMake(velocity.x, 0);
}
velocity = ccpMult(velocity, PAN_SENSITIVITY);
[self panRow:velocity object:object];
}
else
{
if (fabs(velocity.y) > MAX_VELOCITY)
{
if (velocity.y > 0)
velocity = CGPointMake(0, MAX_VELOCITY);
else
velocity = CGPointMake(0, -MAX_VELOCITY);
}
else
{
velocity = CGPointMake(0, velocity.y);
}
velocity = ccpMult(velocity, PAN_SENSITIVITY);
[self panColumn:velocity object:object];
}
}
How did you 'strictly limit to portrait' did you use
-(BOOL)shouldAutorotate{
return NO;
}

Preventing a rotated CGAffineTransform from being wrecked by view autorotation

I have a subclass of a UIImageView containing some gesture recognisers that I'm using to apply transforms to itself. I'm having no issues with panning or scaling, but the rotated transform is causing problems when the device itself is rotated. Basically each time the device is rotated it will have the effect of scaling the image...
I guess it makes sense that the rotation of everything might cause problems with a rotated transform but does anyone know any ways around this kind of behaviour? Preferably something that can be implemented within the UIImageView subclass? I need other sibling views to autoresize so I can't disable "autoresize subviews" in the parent view.
Here's the code responsible for creating the rotated transform if it helps:
- (void)setUpRotation
{
UIRotationGestureRecognizer *newRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(handleRotationGesture)];
newRecognizer.delegate = self;
self.rotationRecognizer = newRecognizer;
[self addGestureRecognizer:self.rotationRecognizer];
[newRecognizer release];
self.userInteractionEnabled = YES;
}
- (void)handleRotationGesture
{
// Initial state
if(self.rotationRecognizer.state == UIGestureRecognizerStateEnded)
{
lastRotation = 0.0;
return;
}
CGFloat rotation = 0.0 - (lastRotation - self.rotationRecognizer.rotation);
CGAffineTransform currentTransform = self.transform;
self.transform = CGAffineTransformRotate(currentTransform,rotation);
lastRotation = self.rotationRecognizer.rotation;
}
This is on iOS 5.0 btw.

Camera overlayview not change landscape orientation(Left and Right) in iPhone4?

I have customized the camera with custom buttons(UIButton) by overlay view. My iPhone application will run only in Landscape mode(Left and Right). Now, my camera overlay view change it's orientation left and right perfectly in iPhone 3GS but not run clearly in iPhone4. In ios5 if i launch the camera with overlay view in landscapemode(Right/Left) it launching perfectly but, if i change the orientation to (Right -> Left / Left -> Right) the overlay view not changing perfectly the frame size of the UIView(CameraOverlayView),UIButton frame sizes are changing. How can i fix it? Please help me to fix this problem. I hope you my friends fix my problem. Thanks in advance. Here i have attached my code that i used.
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if(orientation == UIDeviceOrientationLandscapeLeft)
{
CGAffineTransform transform = self.view.transform;
transform = CGAffineTransformRotate(transform, (M_PI / 2.0));
imgpicker.cameraOverlayView.transform = transform;
NSLog(#"Orientation Landscape Left");
}
else if(orientation == UIDeviceOrientationLandscapeRight)
{
imgpicker.cameraOverlayView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, 117.81);
NSLog(#"Orientation Landscape Right");
}
This code works good in iPhone#GS(ios4) but not working good in iPhone4(ios5). Can anyone please help me to fix this problem?
Thank you my friends. I have solved this issue by myself. I just set the bounds to UIView so it covers full view on Camera OverlayView and also i have tried above code that i mentioned in my question.
cameraview.bounds = CGRectMake(0.0, 0.0, 480.0f, 320.0f);
And so i have checked this code at beginning of Camera launch,
UIInterfaceOrientation orientation= [[UIApplication sharedApplication] statusBarOrientation];
if (orientation == UIInterfaceOrientationLandscapeLeft)
{
imgpicker.cameraOverlayView.transform = CGAffineTransformRotate(CGAffineTransformIdentity,117.81);
}
else if (orientation == UIInterfaceOrientationLandscapeRight)
{
CGAffineTransform transform = self.view.transform;
transform = CGAffineTransformRotate(transform, (M_PI/2.0));
imgpicker.cameraOverlayView.transform = transform;
}
After this i was check this condition in UIDeviceOrientation Notification also. Now, my app working charm. Thanks.

Why does AVCaptureVideoOrientation landscape modes result in upside down still images?

I am using AVFoundation classes to implement a custom camera in my app. I am only capturing still images, not video. I have everything working but am stumped by something. I take into account the device orientation when a still image is captured and set the videoOrientation of the video connection appropriately. A code snippet:
// set the videoOrientation based on the device orientation to
// ensure the pic is right side up for all orientations
AVCaptureVideoOrientation videoOrientation;
switch ([UIDevice currentDevice].orientation) {
case UIDeviceOrientationLandscapeLeft:
// Not clear why but the landscape orientations are reversed
// if I use AVCaptureVideoOrientationLandscapeLeft here the pic ends up upside down
videoOrientation = AVCaptureVideoOrientationLandscapeRight;
break;
case UIDeviceOrientationLandscapeRight:
// Not clear why but the landscape orientations are reversed
// if I use AVCaptureVideoOrientationLandscapeRight here the pic ends up upside down
videoOrientation = AVCaptureVideoOrientationLandscapeLeft;
break;
case UIDeviceOrientationPortraitUpsideDown:
videoOrientation = AVCaptureVideoOrientationPortraitUpsideDown;
break;
default:
videoOrientation = AVCaptureVideoOrientationPortrait;
break;
}
videoConnection.videoOrientation = videoOrientation;
Note my comments in the landscape cases. I have to reverse the orientation mapping or the resulting image is upside down. I capture and save the image with the following code:
[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection
completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error)
{
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
self.stillImage = [UIImage imageWithData:imageData];
// notify observers (image gets saved to the camera roll)
[[NSNotificationCenter defaultCenter] postNotificationName:CaptureSessionManagerDidCaptureStillImageNotification object:self];
self.stillImage = nil;
}];
There is no other image processing or manipulation.
My app works with the code above. I'm just trying to understand why the orientation constants must be reversed for landscape orientations. Thanks!
Heh, it seems nobody felt like chiming in on this one. Turns out the answer is straightforward. Images captured via the stillImageOutput captureStillImageAsynchronouslyFromConnection:... method always end up with the following properties:
UIImage orientation = always UIImageOrientationRight regardless of device orientation
UIImage size = W x H (e.g. portrait width x portrait height, depends on your camera resolution)
CGImage size = depends on device orientation (e.g. portrait or landscape)
So the solution to rotate the image up is to use the device orientation in conjunction with the CGImage size to apply an appropriate affine transform. As I'm answering my own question, I'm not the solution in code but I ended up writing a routine called:
- (UIImage *)imageRotatedUpForDeviceOrientation:(UIDeviceOrientation)deviceOrientation
in a UIImage category containing various image processing enhancements.
EDIT - Implementation Example
I've received a number of requests for functional code on this. I've extracted the relevant implementation from a working app.
// this method is implemented in your capture session manager (wherever AVCaptureSession is used)
// capture a still image and save the device orientation
- (void)captureStillImage
{
UIDeviceOrientation currentDeviceOrientation = UIDevice.currentDevice.orientation;
[self.stillImageOutput
captureStillImageAsynchronouslyFromConnection:self.videoConnection
completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
if (imageData) {
UIImage *image = [UIImage imageWithData:imageData];
NSDictionary *captureInfo = {
#"image" : image,
#"deviceOrientation" : #(currentDeviceOrientation)
};
// TODO: send image & orientation to delegate or post notification to observers
}
else {
// TODO: handle image capture error
}
}];
}
// this method rotates the UIImage captured by the capture session manager based on the
// device orientation when the image was captured
- (UIImage *)imageRotatedUpFromCaptureInfo:(NSDictionary *)captureInfo
{
UIImage *image = [captureInfo objectForKey:#"image"];
UIDeviceOrientation deviceOrientation = [[captureInfo objectForKey:#"deviceOrientation"] integerValue];
UIImageOrientation rotationOrientation = [self rotationNeededForImageCapturedWithDeviceOrientation:deviceOrientation];
// TODO: scale the image if desired
CGSize newSize = image.size;
return [imageScaledToSize:newSize andRotatedByOrientation:rotationOrientation];
}
// return a scaled and rotated an image
- (UIImage *)imageScaledToSize:(CGSize)newSize andRotatedByOrientation:(UIImageOrientation)orientation
{
CGImageRef imageRef = self.CGImage;
CGRect imageRect = CGRectMake(0.0, 0.0, newSize.width, newSize.height);
CGRect contextRect = imageRect;
CGAffineTransform transform = CGAffineTransformIdentity;
switch (orientation)
{
case UIImageOrientationDown: { // rotate 180 deg
transform = CGAffineTransformTranslate(transform, imageRect.size.width, imageRect.size.height);
transform = CGAffineTransformRotate(transform, M_PI);
} break;
case UIImageOrientationLeft: { // rotate 90 deg left
contextRect = CGRectTranspose(contextRect);
transform = CGAffineTransformTranslate(transform, imageRect.size.height, 0.0);
transform = CGAffineTransformRotate(transform, M_PI / 2.0);
} break;
case UIImageOrientationRight: { // rotate 90 deg right
contextRect = CGRectTranspose(contextRect);
transform = CGAffineTransformTranslate(transform, 0.0, imageRect.size.width);
transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
} break;
case UIImageOrientationUp: // no rotation
default:
break;
}
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
CGColorSpaceRef colorSpaceRef = CGImageGetColorSpace(imageRef);
// madify bitmapInfo to work with PNG if necessary
if (bitmapInfo == kCGImageAlphaNone) {
bitmapInfo = kCGImageAlphaNoneSkipLast;
}
else if (bitmapInfo == kCGImageAlphaLast) {
bitmapInfo = kCGImageAlphaPremultipliedLast;
}
// Build a context that's the same dimensions as the new size
CGContextRef context = CGBitmapContextCreate(NULL,
contextRect.size.width,
contextRect.size.height,
CGImageGetBitsPerComponent(imageRef),
0,
colorSpaceRef,
bitmapInfo);
CGContextConcatCTM(context, transform);
CGContextDrawImage(context, imageRect, imageRef);
// Get the rotated image from the context and a UIImage
CGImageRef rotatedImageRef = CGBitmapContextCreateImage(context);
UIImage *rotatedImage = [UIImage imageWithCGImage:rotatedImageRef];
// Clean up
CGImageRelease(rotatedImageRef);
CGContextRelease(context);
return rotatedImage;
}
// return the UIImageOrientation needed for an image captured with a specific deviceOrientation
- (UIImageOrientation)rotationNeededForImageCapturedWithDeviceOrientation:(UIDeviceOrientation)deviceOrientation
{
UIImageOrientation rotationOrientation;
switch (deviceOrientation) {
case UIDeviceOrientationPortraitUpsideDown: {
rotationOrientation = UIImageOrientationLeft;
} break;
case UIDeviceOrientationLandscapeRight: {
rotationOrientation = UIImageOrientationDown;
} break;
case UIDeviceOrientationLandscapeLeft: {
rotationOrientation = UIImageOrientationUp;
} break;
case UIDeviceOrientationPortrait:
default: {
rotationOrientation = UIImageOrientationRight;
} break;
}
return rotationOrientation;
}
As for why you need AVCaptureVideoOrientationLandscapeRight when the device's orientation is UIDeviceOrientationLandscapeLeft, that's because, for some reason, UIDeviceOrientationLandscapeLeft == UIInterfaceOrientationLandscapeRight, and the AVCaptureVideoOrientations are following the UIInterfaceOrientation convention.
Also, UIDeviceOrientation incudes other options like UIDeviceOrientationFaceUp, UIDeviceOrientationFaceDown, and UIDeviceOrientationUnknown. If you're having your interface rotate to match the device's orientation, you could try getting the UIDeviceOrientation from [UIApplication sharedApplication].statusBarOrientation instead.
Actually, there is no mistake in your implementation and you can eliminate the entire switch case.
Why switch case can be eliminated:
The corressponding values of device and video orientations are numerically equal, i.e. as long as device orientation is not unknown, face up or face down, you can equate UIDeviceOrientation and AVCaptureVideoOrientation
About the confusing nomenclature:
Device orientation LandscapeLeft => the top of your phone is on the left.
So, just imagine where the starting point of video is..... that's right, TOP RIGHT corner => Landscape right.
Similarly in the other landscape orientation, the video orientation is away from the top of the phone.
This isn't confusing in case of portrait and upside down
Proof
When you capture a still Image, check the orientation EXIF value.
For landscape left, the EXIF orientation is 3 (180 degrees inversion)
For landscape right, the EXIF orientation is 1 (no rotation)
When you are displaying, your exif orientation is converted to corresponding UIImageOrientation, so there should be no inversion.
In the case of front camera, I have noticed that EXIF values, simply follow the video orientation values (mirror effect is not taken care of). Only 1, 3, 6, 8 values of EXIF are used.
Also, the mirror is always along the line from top of device to bottom
So, landscape will seem upside down while portrait and upside down portrait is just mirrored

MPMoviePlayerViewController in UIModalPresentationFormSheet Problem

My problem is that i have a MPMoviePlayerViewController embeded inside a modalviewcontroller that has the formsheet atribute and when the video goes to fullscreen using the pinch or the arrows the controls dont work.
I have figured out that they dont work because only touches inside the rectangle that makes the modalviewcontroller are registered. for example, double tapping to zoom inside the rectangle works while everywhere else it doesnt.
This is a problem since the movie controls can't be used due to this problem. Can anyone help?
Here is how I solved it. I changed the size of the modal view controller when the video was going into fullscreen.
-(void)movieDidEnterFullscreen:(NSNotification *)notification{
NSLog(#"did enter");
self.navigationController.view.superview.frame = CGRectMake(0, 0, 1500,1500);
self.navigationController.view.superview.center = self.view.center;
[mpviewController moviePlayer].controlStyle = MPMovieControlStyleDefault;
}
-(void)movieDidExitFullscreen:(NSNotification *)notification{
NSLog(#"did exit");
UIDevice *device = [UIDevice currentDevice];
[device beginGeneratingDeviceOrientationNotifications];
if (([device orientation] == UIDeviceOrientationLandscapeLeft) || ([device orientation] == UIDeviceOrientationLandscapeRight)){
self.navigationController.view.superview.frame = CGRectMake(0, 0, 620,540);
self.navigationController.view.superview.center = CGPointMake(384, 512);
}
else {
self.navigationController.view.superview.frame = CGRectMake(0, 0, 540,620);
self.navigationController.view.superview.center = CGPointMake(384, 512);
}
[mpviewController moviePlayer].controlStyle = MPMovieControlStyleEmbedded;
}