1
votes

I am struggling changing the orientation of my video. It is recorded in portrait but then is saved in landscape. Changing the transform is only make the video rotate within a landscape video. In this example with M_PI_2 it disappears since it rotates off the screen or is flat. But if I change it to M_PI_2/2 or something it appears but crooked. I know AVFoundation does this by default. How do I change this? I got a lot of this code from this tutorial: https://www.raywenderlich.com/30200/avfoundation-tutorial-adding-overlays-and-animations-to-videos but using the AVMutableVideoCompositionLayerInstruction is not working.

AVMutableCompositionTrack *videoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo
                                                                    preferredTrackID:kCMPersistentTrackID_Invalid];
CMTime insertTime = kCMTimeZero;
for(AVAsset *videoAsset in self.videoArray){
    [videoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration) ofTrack:[[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:insertTime error:nil];
    // Updating the insertTime for the next insert
    insertTime = CMTimeAdd(insertTime, videoAsset.duration);
}
CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(M_PI_2);
videoTrack.preferredTransform = rotationTransform;

// 3.1 - Create AVMutableVideoCompositionInstruction
AVMutableVideoCompositionInstruction *mainInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
mainInstruction.timeRange = videoTrack.timeRange;

// 3.2 - Create an AVMutableVideoCompositionLayerInstruction for the video track and fix the orientation.
AVMutableVideoCompositionLayerInstruction *videolayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];
AVAssetTrack *videoAssetTrack = [[videoTrack.asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];

UIImageOrientation videoAssetOrientation_  = UIImageOrientationUp;
BOOL isVideoAssetPortrait_  = NO;

CGAffineTransform videoTransform = videoAssetTrack.preferredTransform;
if (videoTransform.a == 0 && videoTransform.b == 1.0 && videoTransform.c == -1.0 && videoTransform.d == 0) {
    videoAssetOrientation_ = UIImageOrientationRight;
    isVideoAssetPortrait_ = YES;
}
if (videoTransform.a == 0 && videoTransform.b == -1.0 && videoTransform.c == 1.0 && videoTransform.d == 0) {
    videoAssetOrientation_ =  UIImageOrientationLeft;
    isVideoAssetPortrait_ = YES;
}
if (videoTransform.a == 1.0 && videoTransform.b == 0 && videoTransform.c == 0 && videoTransform.d == 1.0) {
    videoAssetOrientation_ =  UIImageOrientationUp;
}
if (videoTransform.a == -1.0 && videoTransform.b == 0 && videoTransform.c == 0 && videoTransform.d == -1.0) {
    videoAssetOrientation_ = UIImageOrientationDown;
}

//CGAffineTransform rotationTransform = videoAssetTrack.preferredTransform;

[videolayerInstruction setTransform:videoAssetTrack.preferredTransform atTime:kCMTimeZero];
[videolayerInstruction setOpacity:0.0 atTime:videoTrack.timeRange.duration];

Is there a way to set an anchor point or to make my own transform?

1

1 Answers

5
votes

Check https://developer.apple.com/library/content/qa/qa1744/_index.html, that's the official explanation of setting the orientation of video with AVFoundation. Simply speaking, you need to use an AVMutableVideoCompositionLayerInstruction object to modify the transform to apply to a given track in the video composition.
Then let's talk about the transform you should apply. There're so many people who suggest using AVAssetTrack's preferredTransform. In most cases it works and you should use it to get the video orientation of your assetTrack. But sometimes preferredTransform may lack of value of tx(ty) (you should check CGAffineTransform In Apple API Reference if you don't know what is tx(ty)) or tx(ty) has inaccurate value which results in wrong video positioning.
So the main idea is: use preferredTransform to determine the origin video orientation and make the transform of your own.
here is the code of getting the orientation of track:

- (UIInterfaceOrientation)orientationForTrack:(AVAsset *)asset {
    UIInterfaceOrientation orientation = UIInterfaceOrientationPortrait;
    NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo];

    if([tracks count] > 0) {
        AVAssetTrack *videoTrack = [tracks objectAtIndex:0];
        CGAffineTransform t = videoTrack.preferredTransform;

        // Portrait
        if(t.a == 0 && t.b == 1.0 && t.c == -1.0 && t.d == 0) {
            orientation = UIInterfaceOrientationPortrait;
        }
        // PortraitUpsideDown
        if(t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0) {
            orientation = UIInterfaceOrientationPortraitUpsideDown;
        }
        // LandscapeRight
        if(t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0) {
            orientation = UIInterfaceOrientationLandscapeRight;
        }
        // LandscapeLeft
        if(t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0) {
            orientation = UIInterfaceOrientationLandscapeLeft;
        }
    }
    return orientation;
}

And code for getting the needed transform to apply:

- (CGAffineTransform)transformBasedOnAsset:(AVAsset *)asset {
    UIInterfaceOrientation orientation = [AVUtilities orientationForTrack:asset];
    AVAssetTrack *assetTrack = [asset tracksWithMediaType:AVMediaTypeVideo][0];
    CGSize naturalSize = assetTrack.naturalSize;
    CGAffineTransform finalTranform;
    switch (orientation) {
        case UIInterfaceOrientationLandscapeLeft:
            finalTranform = CGAffineTransformMake(-1, 0, 0, -1, naturalSize.width, naturalSize.height);
            break;
        case UIInterfaceOrientationLandscapeRight:
            finalTranform = CGAffineTransformMake(1, 0, 0, 1, 0, 0);
            break;
        case UIInterfaceOrientationPortrait:
            finalTranform = CGAffineTransformMake(0, 1, -1, 0, naturalSize.height, 0);
            break;
        case UIInterfaceOrientationPortraitUpsideDown:
            finalTranform = CGAffineTransformMake(0, -1, 1, 0, 0, naturalSize.width);
            break;
        default:
            break;
    }
    return finalTranform;
}

If you get confused, I suggest you taking videos with different orientation first, then print out the preferredTransform of the video using NSStringFromCGAffineTransform() or something else to check for details.