AVMutableComposition Rotates Videos
Asked Answered
B

3

15

I've recently discovered an issue using AVMutableComposition and I'm looking for some insight into this.

I want to be able to record video in two orientations - landscape left and right. When I record videos in landscape right (home button is on the right), they are added to the composition and played in the correct orientation. However, if I record it in orientation left (home button on the left), these clips are played upside down.

BUT, they are only played upside-down if they are inserted into the composition. Otherwise they play in the correct orientation. Why is the composition reversing the rotation of clips shot in landscape left? How can I fix this? Any help is appreciated!

Boyles answered 29/5, 2012 at 19:25 Comment(1)
I've also noticed as well that regardless of what I do this is occurring. Setting the recording orientation before-hand, attempting rotations, etc. The track plays right-side-up if I play the first clip on its own, but as soon as it is combined into a composition it flips upside-down.Boyles
U
35

Here's a slightly easier way if you simply want to maintain original rotation.

// Grab the source track from AVURLAsset for example.
AVAssetTrack *assetVideoTrack = [asset tracksWithMediaType:AVMediaTypeVideo].lastObject;

// Grab the composition video track from AVMutableComposition you already made.
AVMutableCompositionTrack *compositionVideoTrack = [composition tracksWithMediaType:AVMediaTypeVideo].lastObject;

// Apply the original transform.    
if (assetVideoTrack && compositionVideoTrack) {
   [compositionVideoTrack setPreferredTransform:assetVideoTrack.preferredTransform];
}

// Export...
Uncivilized answered 24/2, 2014 at 5:59 Comment(8)
This solution worked perfectly for me to preserve the original rotation.Holding
If having multiple tracks in the composition that have different transforms you cannot simple set to the transform of each, you need to compensate differently.Imperceptive
PER-FECT - a much more elegant solution and should definitely be the preferred answer. Kudos to dizy for this oneShiva
I've changed this to be the preferred answer, as it seems to work for others. This thread is almost 4 years old now, so I'm unsure if the APIs have changed, but it seems much more elegant :)Boyles
@bgoers Perhaps, the answer became invalid, for me it doesnt work for me now, because I looked up that PreferredTransform property is readOnly. We can't affect this propertyTon
hey @JulianF.Weinert how are you, i need your help, have you played multiple videos with composition, because we can set preferredtransform for all composition ,not for individual videosHowlyn
@JagveerSingh that's right. I don't have a ready made solution in the back of my head, but you should be able to apply the transformations on a time bases. One tranformation for the given clip time. If you have them simultaneously, you might be able to create a composition of compositions?? (Not sure right now)Imperceptive
@JulianF.Weinert i have asked my question in detail here please join my question and tell me solution, and have you tried composition of compositions . #45146016Howlyn
B
9

Solved my problem. Was finally able to rotate the track and translate it into the frame. Works like a charm.

    //setting up the first video based on previous recording
    CMTimeRange videoDuration = CMTimeRangeMake(kCMTimeZero, [self.previousRecording duration]);
    AVAssetTrack *clipVideoTrack = [[self.previousRecording tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
    AVAssetTrack *clipAudioTrack = [[self.previousRecording tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
    [compositionVideoTrack insertTimeRange:videoDuration ofTrack:clipVideoTrack atTime:nextClipStartTime error:nil];
    [compositionAudioTrack insertTimeRange:videoDuration ofTrack:clipAudioTrack atTime:nextClipStartTime error:nil];

    //our first track instruction - set up the instruction layer, then check the orientation of the track
    //if the track is in landscape-left mode, it needs to be rotated 180 degrees (PI)
    AVMutableVideoCompositionLayerInstruction *firstTrackInstruction =
         [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:clipVideoTrack];

    if([self orientationForTrack:clipVideoTrack] == UIDeviceOrientationLandscapeLeft) {
        CGAffineTransform rotation = CGAffineTransformMakeRotation(M_PI);
        CGAffineTransform translateToCenter = CGAffineTransformMakeTranslation(640, 480);
        CGAffineTransform mixedTransform = CGAffineTransformConcat(rotation, translateToCenter);
        [firstTrackInstruction setTransform:mixedTransform atTime:kCMTimeZero];
    }
Boyles answered 30/5, 2012 at 17:37 Comment(4)
Hi bgoers....I also got the same problem...i used your code but i am getting error near [self orientationForTrack:clipVideoTrack] (method not found) can u please help me in this...Biggerstaff
That was a method I defined myself to determine the orientation of a track. It took an AVAssetTrack as a parameter then used the preferred orientation. I changed mine a bit but this should help https://mcmap.net/q/439920/-how-to-detect-if-a-video-file-was-recorded-in-portrait-orientation-or-landscape-in-iosBoyles
What is [self orientationForTrack:]? What's it's implementation?Imperceptive
@Julian I answered that in the comment above yours. Please refer to that.Boyles
D
0

I think the answer is for sure the best option, but it is only partially correct. In fact, in order to make it work, we also have to adjust the render size of the export, flipping height and width of the portrait track natural size.

I just tested it and I also cite AVFoundation Programming Guide - Editing section, which suggests to implement what is actually suggested in the answer of @dizy but with the mentioned addition:

All AVAssetTrack objects have a preferredTransform property that contains the orientation information for that asset track. This transform is applied whenever the asset track is displayed onscreen. In the previous code, the layer instruction’s transform is set to the asset track’s transform so that the video in the new composition displays properly once you adjust its render size.

The code should be like this one then (just two lines to add):

// Grab the source track from AVURLAsset for example.
AVAssetTrack *assetVideoTrack = [asset tracksWithMediaType:AVMediaTypeVideo].lastObject;

// Grab the composition video track from AVMutableComposition you already made.
AVMutableCompositionTrack *compositionVideoTrack = [composition tracksWithMediaType:AVMediaTypeVideo].lastObject;

// Apply the original transform.    
if (assetVideoTrack && compositionVideoTrack) {
   [compositionVideoTrack setPreferredTransform:assetVideoTrack.preferredTransform];
}

flippedSize = CGSize(compositionVideoTrack.naturalSize.height, compositionVideoTrack.naturalSize.width);
composition.renderSize = flippedSize;

// Export..
Duenna answered 1/10, 2017 at 10:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.