14

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!

anon_dev1234
  • 2,143
  • 1
  • 17
  • 33
  • 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. – anon_dev1234 May 30 '12 at 16:48

3 Answers3

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...
dizy
  • 7,951
  • 10
  • 53
  • 54
  • This solution worked perfectly for me to preserve the original rotation. – Brad Mar 12 '15 at 00:43
  • 2
    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. – Julian F. Weinert Oct 22 '15 at 10:15
  • PER-FECT - a much more elegant solution and should definitely be the preferred answer. Kudos to dizy for this one – PinkFloydRocks Dec 03 '15 at 19:07
  • 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 :) – anon_dev1234 Dec 17 '15 at 19:48
  • 2
    @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 property – kokos8998 Jan 28 '16 at 10:56
  • 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 videos – Jagveer Singh Jul 17 '17 at 05:18
  • @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) – Julian F. Weinert Jul 17 '17 at 14:30
  • @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 . https://stackoverflow.com/questions/45145516/play-avplayer-using-avmutablecomposition-if-videos-having-different-preferredtra – Jagveer Singh Jul 18 '17 at 04:36
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];
    }
anon_dev1234
  • 2,143
  • 1
  • 17
  • 33
  • 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... – ask123 Jun 21 '12 at 07:18
  • 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 http://stackoverflow.com/a/6046421/1415949 – anon_dev1234 Jun 21 '12 at 17:43
  • What is `[self orientationForTrack:]`? What's it's implementation? – Julian F. Weinert Oct 22 '15 at 10:16
  • @Julian I answered that in the comment above yours. Please refer to that. – anon_dev1234 Oct 22 '15 at 17:34
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..
Freddie Mash
  • 159
  • 1
  • 1
  • 5