I am trying to rotate avplayer but when i rotate it ,it is rotated from the beginning,i want to rotate while i am playing it at any time and it should rotate from there.I am
Swift 5 Version
func rotateVideoPlayer(player: AVPlayer, degrees: CGFloat) -> AVPlayer? {
let urlAsset = player.currentItem?.asset as! AVURLAsset
let url = urlAsset.url
var composition: AVMutableComposition?
var videoComposition: AVMutableVideoComposition?
var instruction: AVMutableVideoCompositionInstruction?
let asset = AVURLAsset(url: url)
var layerInstruction: AVMutableVideoCompositionLayerInstruction?
var t1: CGAffineTransform?
var t2: CGAffineTransform?
var assetVideoTrack: AVAssetTrack?
var assetAudioTrack: AVAssetTrack?
if asset.tracks(withMediaType: AVMediaType.video).count != 0 {
assetVideoTrack = asset.tracks(withMediaType: AVMediaType.video)[0]
}
if asset.tracks(withMediaType: AVMediaType.audio).count != 0 {
assetAudioTrack = asset.tracks(withMediaType: AVMediaType.audio)[0]
}
let insertionPoint = CMTime.invalid
composition = AVMutableComposition()
if assetVideoTrack != nil {
let compositionVideoTrack: AVMutableCompositionTrack = (composition?.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: kCMPersistentTrackID_Invalid))!
let timeRange = CMTimeRangeMake(start: CMTime.zero, duration: asset.duration)
try! compositionVideoTrack.insertTimeRange(timeRange, of: assetVideoTrack!, at: insertionPoint)
}
if assetAudioTrack != nil {
let compositionAudioTrack: AVMutableCompositionTrack = (composition?.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid))!
let timeRange = CMTimeRangeMake(start: CMTime.zero, duration: asset.duration)
try! compositionAudioTrack.insertTimeRange(timeRange, of: assetAudioTrack!, at: insertionPoint)
}
let width = Float((assetVideoTrack?.naturalSize.width)!)
let height = Float((assetVideoTrack?.naturalSize.height)!)
let toDiagonal = Float(sqrt(width * width + height * height))
let toDiagonalAngle = Float(radiansToDegrees(acosf(width/toDiagonal)))
let toDiagonalAngle2 = Float(90 - radiansToDegrees(acosf(width/toDiagonal)))
var toDiagonalAngleComple: Float
var toDiagonalAngleComple2: Float
var finalHeight: Float = 0
var finalWidth: Float = 0
if degrees >= 0 && degrees <= 90 {
toDiagonalAngleComple = toDiagonalAngle + Float(degrees)
toDiagonalAngleComple2 = toDiagonalAngle2 + Float(degrees)
let sinfValue = sinf(degreesToRadians(toDiagonalAngleComple))
let sinfValue2 = sinf(degreesToRadians(toDiagonalAngleComple2))
finalHeight = abs(toDiagonal * sinfValue)
finalWidth = abs(toDiagonal * sinfValue2)
let side1 = height * sinf(degreesToRadians(Float(degrees)))
let side2 = 0.0
t1 = CGAffineTransform(translationX: CGFloat(side1), y: CGFloat(side2))
} else if degrees > 90 && degrees <= 180 {
let degrees2 = Float(degrees - 90)
toDiagonalAngleComple = toDiagonalAngle + degrees2
toDiagonalAngleComple2 = toDiagonalAngle2 + degrees2
let sinfValue = sinf(degreesToRadians(toDiagonalAngleComple2))
let sinfValue2 = sinf(degreesToRadians(toDiagonalAngleComple))
finalHeight = abs(toDiagonal * sinfValue)
finalWidth = abs(toDiagonal * sinfValue2)
let side1 = width * sinf(degreesToRadians(degrees2)) + height * cosf(degreesToRadians(degrees2))
let side2 = height * sinf(degreesToRadians(degrees2))
t1 = CGAffineTransform(translationX: CGFloat(side1), y: CGFloat(side2))
} else if degrees >= -90 && degrees < 0 {
let degrees2 = Float(degrees - 90)
let degrees2abs = Float(abs(degrees))
toDiagonalAngleComple = toDiagonalAngle + degrees2
toDiagonalAngleComple2 = toDiagonalAngle2 + degrees2
let sinfValue = sinf(degreesToRadians(toDiagonalAngleComple2))
let sinfValue2 = sinf(degreesToRadians(toDiagonalAngleComple))
finalHeight = abs(toDiagonal * sinfValue)
finalWidth = abs(toDiagonal * sinfValue2)
let side1 = 0
let side2 = width * sinf(degreesToRadians(degrees2abs))
t1 = CGAffineTransform(translationX: CGFloat(side1), y: CGFloat(side2))
} else if degrees >= -180 && degrees < -90 {
let degreesabs = Float(abs(degrees))
let degreesPlus = degreesabs - 90
toDiagonalAngleComple = toDiagonalAngle + Float(degrees)
toDiagonalAngleComple2 = toDiagonalAngle2 + Float(degrees)
let sinfValue = sinf(degreesToRadians(toDiagonalAngleComple))
let sinfValue2 = sinf(degreesToRadians(toDiagonalAngleComple2))
finalHeight = abs(toDiagonal * sinfValue)
finalWidth = abs(toDiagonal * sinfValue2)
let side1 = width * sinf(degreesToRadians(degreesPlus))
let side2 = height * sinf(degreesToRadians(degreesPlus)) + width * cosf(degreesToRadians(degreesPlus))
t1 = CGAffineTransform(translationX: CGFloat(side1), y: CGFloat(side2))
}
t2 = t1!.rotated(by: CGFloat(degreesToRadians(Float(degrees))))
videoComposition = AVMutableVideoComposition()
videoComposition?.renderSize = CGSize(width: CGFloat(finalWidth), height: CGFloat(finalHeight))
videoComposition?.frameDuration = CMTimeMake(value: 1, timescale: 30)
instruction = AVMutableVideoCompositionInstruction()
instruction?.timeRange = CMTimeRangeMake(start: CMTime.zero, duration: composition!.duration)
layerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: (composition?.tracks[0])!)
layerInstruction?.setTransform(t2!, at: CMTime.zero)
instruction?.layerInstructions = NSArray(object: layerInstruction!) as! [AVVideoCompositionLayerInstruction]
videoComposition?.instructions = NSArray(object: instruction!) as! [AVVideoCompositionInstructionProtocol]
let playItem_ = AVPlayerItem(asset: composition!)
playItem_.videoComposition = videoComposition
var time: CMTime!
time = CMTime.zero
player.replaceCurrentItem(with: playItem_)
player.seek(to: time, toleranceBefore: CMTime.zero, toleranceAfter: CMTime.zero)
let exportSession = AVAssetExportSession(asset: composition!, presetName: AVAssetExportPresetMediumQuality)
exportSession?.outputURL = URL(string: String(format: "%@_rotated", url as CVarArg))
exportSession?.outputFileType = AVFileType.mov
exportSession?.videoComposition = videoComposition
exportSession?.shouldOptimizeForNetworkUse = true
exportSession?.timeRange = CMTimeRangeMake(start: CMTime.zero, duration: asset.duration)
exportSession?.exportAsynchronously(completionHandler: { () -> Void in
print("Video exported")
})
return player
}
func degreesToRadians(_ input: Float) -> Float {
let float: Float = 180
return Float(input) * .pi / float
}
func radiansToDegrees(_ input: Float) -> Float {
return Float(input) * 180 / .pi
}
Thank you very much, Oleh Kudinov, You are awesome!
Following is the swift version if anyone need it.
func rotateVideoPlayer(player: AVPlayer, degrees: CGFloat) -> AVPlayer? {
let urlAsset = player.currentItem?.asset as! AVURLAsset
let url = urlAsset.URL
var composition: AVMutableComposition?
var videoComposition: AVMutableVideoComposition?
var instruction: AVMutableVideoCompositionInstruction?
let asset = AVURLAsset(URL: url)
var layerInstruction: AVMutableVideoCompositionLayerInstruction?
var t1: CGAffineTransform?
var t2: CGAffineTransform?
var assetVideoTrack: AVAssetTrack?
var assetAudioTrack: AVAssetTrack?
// Check if the asset contains video and audio tracks
if asset.tracksWithMediaType(AVMediaTypeVideo).count != 0
{
assetVideoTrack = asset.tracksWithMediaType(AVMediaTypeVideo)[0]
}
if asset.tracksWithMediaType(AVMediaTypeAudio).count != 0
{
assetAudioTrack = asset.tracksWithMediaType(AVMediaTypeAudio)[0]
}
let insertionPoint = kCMTimeInvalid
// Step 1
// Create a new composition
composition = AVMutableComposition()
// Insert a new composition
if assetVideoTrack != nil {
let compositionVideoTrack: AVMutableCompositionTrack = (composition?.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid))!
let timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration)
try! compositionVideoTrack.insertTimeRange(timeRange, ofTrack: assetVideoTrack!, atTime: insertionPoint)
}
if assetAudioTrack != nil {
let compositionAudioTrack: AVMutableCompositionTrack = (composition?.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid))!
let timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration)
try! compositionAudioTrack.insertTimeRange(timeRange, ofTrack: assetAudioTrack!, atTime: insertionPoint)
}
// Step 2
// Calculate position and size of render video after rotating
let width = Float((assetVideoTrack?.naturalSize.width)!)
let height = Float((assetVideoTrack?.naturalSize.height)!)
let toDiagonal = Float(sqrt(width * width + height * height))
let toDiagonalAngle = Float(radiansToDegrees(acosf(width/toDiagonal)))
let toDiagonalAngle2 = Float(90 - radiansToDegrees(acosf(width/toDiagonal)))
var toDiagonalAngleComple: Float
var toDiagonalAngleComple2: Float
var finalHeight: Float = 0
var finalWidth: Float = 0
if degrees >= 0 && degrees <= 90 {
toDiagonalAngleComple = toDiagonalAngle + Float(degrees)
toDiagonalAngleComple2 = toDiagonalAngle2 + Float(degrees)
let sinfValue = sinf(degreesToRadians(toDiagonalAngleComple))
let sinfValue2 = sinf(degreesToRadians(toDiagonalAngleComple2))
finalHeight = abs(toDiagonal * sinfValue)
finalWidth = abs(toDiagonal * sinfValue2)
let side1 = height * sinf(degreesToRadians(Float(degrees)))
let side2 = 0.0
t1 = CGAffineTransformMakeTranslation(CGFloat(side1), CGFloat(side2))
}
else if degrees > 90 && degrees <= 180 {
let degrees2 = Float(degrees - 90)
toDiagonalAngleComple = toDiagonalAngle + degrees2
toDiagonalAngleComple2 = toDiagonalAngle2 + degrees2
let sinfValue = sinf(degreesToRadians(toDiagonalAngleComple2))
let sinfValue2 = sinf(degreesToRadians(toDiagonalAngleComple))
finalHeight = abs(toDiagonal * sinfValue)
finalWidth = abs(toDiagonal * sinfValue2)
let side1 = width * sinf(degreesToRadians(degrees2)) + height * cosf(degreesToRadians(degrees2))
let side2 = height * sinf(degreesToRadians(degrees2))
t1 = CGAffineTransformMakeTranslation(CGFloat(side1), CGFloat(side2))
}
else if degrees >= -90 && degrees < 0 {
let degrees2 = Float(degrees - 90)
let degrees2abs = Float(abs(degrees))
toDiagonalAngleComple = toDiagonalAngle + degrees2
toDiagonalAngleComple2 = toDiagonalAngle2 + degrees2
let sinfValue = sinf(degreesToRadians(toDiagonalAngleComple2))
let sinfValue2 = sinf(degreesToRadians(toDiagonalAngleComple))
finalHeight = abs(toDiagonal * sinfValue)
finalWidth = abs(toDiagonal * sinfValue2)
let side1 = 0
let side2 = width * sinf(degreesToRadians(degreesabs))
t1 = CGAffineTransformMakeTranslation(CGFloat(side1), CGFloat(side2))
}
else if degrees >= -180 && degrees < -90 {
let degreesabs = Float(abs(degrees))
let degreesPlus = degreesabs - 90
toDiagonalAngleComple = toDiagonalAngle + Float(degrees)
toDiagonalAngleComple2 = toDiagonalAngle2 + Float(degrees)
let sinfValue = sinf(degreesToRadians(toDiagonalAngleComple))
let sinfValue2 = sinf(degreesToRadians(toDiagonalAngleComple2))
finalHeight = abs(toDiagonal * sinfValue)
finalWidth = abs(toDiagonal * sinfValue2)
let side1 = width * sinf(degreesToRadians(degreesPlus))
let side2 = height * sinf(degreesToRadians(degreesPlus)) + width * cosf(degreesToRadians(degreesPlus))
t1 = CGAffineTransformMakeTranslation(CGFloat(side1), CGFloat(side2))
}
// Rotate transformation
t2 = CGAffineTransformRotate(t1!, CGFloat(degreesToRadians(Float(degrees))))
//
// Step 3
//
// Set the appropriate render sizes and rotational transforms
//
// Create a new video composition
// videoComposition = AVMutableComposition
videoComposition = AVMutableVideoComposition()
videoComposition?.renderSize = CGSizeMake(CGFloat(finalWidth), CGFloat(finalHeight))
videoComposition?.frameDuration = CMTimeMake(1, 30)
// The rotate transform is set on a layer instruction
instruction = AVMutableVideoCompositionInstruction()
instruction?.timeRange = CMTimeRangeMake(kCMTimeZero, composition!.duration)
//
// + videoCompositionLayerInstructionWithAssetTrack:
// Returns a new mutable video composition layer instruction for the given track.
//
// Swift
// convenience init(assetTrack track: AVAssetTrack)
//
// Objective-C
// + (instancetype)videoCompositionLayerInstructionWithAssetTrack:(AVAssetTrack *)track
// objectiv-c:
// layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:[composition.tracks objectAtIndex:0]];
// Swift
layerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: (composition?.tracks[0])!)
layerInstruction?.setTransform(t2!, atTime: kCMTimeZero)
//
// Step 4
//
// Add the transfor instructions to the video composition
instruction?.layerInstructions = NSArray(object: layerInstruction!) as! [AVVideoCompositionLayerInstruction]
videoComposition?.instructions = NSArray(object: instruction!) as! [AVVideoCompositionInstructionProtocol]
let playItem_ = AVPlayerItem(asset: composition!)
playItem_.videoComposition = videoComposition
var time: CMTime!
time = kCMTimeZero
player.replaceCurrentItemWithPlayerItem(playItem_)
player.seekToTime(time, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)
// Export rotated video to the file
let exportSession = AVAssetExportSession(asset: composition!, presetName: AVAssetExportPresetMediumQuality)
exportSession?.outputURL = NSURL(string: String(format: "%@_rotated", url))
exportSession?.outputFileType = AVFileTypeQuickTimeMovie
exportSession?.videoComposition = videoComposition
exportSession?.shouldOptimizeForNetworkUse = true
exportSession?.timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration)
exportSession?.exportAsynchronouslyWithCompletionHandler({ () -> Void in
print("Video exported")
})
return player
}
This method will rotate your video frame by frame:
#define degreesToRadians(x) (M_PI * x / 180.0)
#define radiansToDegrees(x) (180.0 * x / M_PI)
//NSURL *url = [[NSBundle mainBundle] URLForResource:nameOfVideo withExtension:@"MOV"];
-(AVPlayer*)rotateVideoPlayer:(AVPlayer*)player withDegrees:(float)degrees{
NSURL* url = [(AVURLAsset *)player.currentItem.asset URL];
AVMutableComposition *composition;
AVMutableVideoComposition *videoComposition;
AVMutableVideoCompositionInstruction * instruction;
AVURLAsset* asset = [[AVURLAsset alloc] initWithURL:url options:nil];
AVMutableVideoCompositionLayerInstruction *layerInstruction = nil;
CGAffineTransform t1;
CGAffineTransform t2;
AVAssetTrack *assetVideoTrack = nil;
AVAssetTrack *assetAudioTrack = nil;
// Check if the asset contains video and audio tracks
if ([[asset tracksWithMediaType:AVMediaTypeVideo] count] != 0) {
assetVideoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
}
if ([[asset tracksWithMediaType:AVMediaTypeAudio] count] != 0) {
assetAudioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
}
CMTime insertionPoint = kCMTimeInvalid;
NSError *error = nil;
// Step 1
// Create a new composition
composition = [AVMutableComposition composition];
// Insert the video and audio tracks from AVAsset
if (assetVideoTrack != nil) {
AVMutableCompositionTrack *compositionVideoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
[compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, [asset duration]) ofTrack:assetVideoTrack atTime:insertionPoint error:&error];
}
if (assetAudioTrack != nil) {
AVMutableCompositionTrack *compositionAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
[compositionAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, [asset duration]) ofTrack:assetAudioTrack atTime:insertionPoint error:&error];
}
// Step 2
// Calculate position and size of render video after rotating
float width=assetVideoTrack.naturalSize.width;
float height=assetVideoTrack.naturalSize.height;
float toDiagonal=sqrt(width*width+height*height);
float toDiagonalAngle=radiansToDegrees(acosf(width/toDiagonal));
float toDiagonalAngle2=90-radiansToDegrees(acosf(width/toDiagonal));
float toDiagonalAngleComple;
float toDiagonalAngleComple2;
float finalHeight;
float finalWidth;
if(degrees>=0&°rees<=90){
toDiagonalAngleComple=toDiagonalAngle+degrees;
toDiagonalAngleComple2=toDiagonalAngle2+degrees;
finalHeight=ABS(toDiagonal*sinf(degreesToRadians(toDiagonalAngleComple)));
finalWidth=ABS(toDiagonal*sinf(degreesToRadians(toDiagonalAngleComple2)));
t1 = CGAffineTransformMakeTranslation(height*sinf(degreesToRadians(degrees)), 0.0);
}
else if(degrees>90&°rees<=180){
float degrees2 = degrees-90;
toDiagonalAngleComple=toDiagonalAngle+degrees2;
toDiagonalAngleComple2=toDiagonalAngle2+degrees2;
finalHeight=ABS(toDiagonal*sinf(degreesToRadians(toDiagonalAngleComple2)));
finalWidth=ABS(toDiagonal*sinf(degreesToRadians(toDiagonalAngleComple)));
t1 = CGAffineTransformMakeTranslation(width*sinf(degreesToRadians(degrees2))+height*cosf(degreesToRadians(degrees2)), height*sinf(degreesToRadians(degrees2)));
}
else if(degrees>=-90&°rees<0){
float degrees2 = degrees-90;
float degreesabs = ABS(degrees);
toDiagonalAngleComple=toDiagonalAngle+degrees2;
toDiagonalAngleComple2=toDiagonalAngle2+degrees2;
finalHeight=ABS(toDiagonal*sinf(degreesToRadians(toDiagonalAngleComple2)));
finalWidth=ABS(toDiagonal*sinf(degreesToRadians(toDiagonalAngleComple)));
t1 = CGAffineTransformMakeTranslation(0, width*sinf(degreesToRadians(degreesabs)));
}
else if(degrees>=-180&°rees<-90){
float degreesabs = ABS(degrees);
float degreesplus = degreesabs-90;
toDiagonalAngleComple=toDiagonalAngle+degrees;
toDiagonalAngleComple2=toDiagonalAngle2+degrees;
finalHeight=ABS(toDiagonal*sinf(degreesToRadians(toDiagonalAngleComple)));
finalWidth=ABS(toDiagonal*sinf(degreesToRadians(toDiagonalAngleComple2)));
t1 = CGAffineTransformMakeTranslation(width*sinf(degreesToRadians(degreesplus)), height*sinf(degreesToRadians(degreesplus))+width*cosf(degreesToRadians(degreesplus)));
}
// Rotate transformation
t2 = CGAffineTransformRotate(t1, degreesToRadians(degrees));
// Step 3
// Set the appropriate render sizes and rotational transforms
// Create a new video composition
videoComposition = [AVMutableVideoComposition videoComposition];
videoComposition.renderSize = CGSizeMake(finalWidth,finalHeight);
videoComposition.frameDuration = CMTimeMake(1, 30);
// The rotate transform is set on a layer instruction
instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
instruction.timeRange = CMTimeRangeMake(kCMTimeZero, [composition duration]);
layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:[composition.tracks objectAtIndex:0]];
[layerInstruction setTransform:t2 atTime:kCMTimeZero];
// Step 4
// Add the transform instructions to the video composition
instruction.layerInstructions = [NSArray arrayWithObject:layerInstruction];
videoComposition.instructions = [NSArray arrayWithObject:instruction];
AVPlayerItem *playerItem_ = [[AVPlayerItem alloc] initWithAsset:composition];
playerItem_.videoComposition = videoComposition;
CMTime time;
time=kCMTimeZero;
[player replaceCurrentItemWithPlayerItem:playerItem_];
[player seekToTime:time toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
//Export rotated video to the file
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetMediumQuality] ;
exportSession.outputURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@_rotated",url]];
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
exportSession.videoComposition = videoComposition;
exportSession.shouldOptimizeForNetworkUse = YES;
exportSession.timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration);
[exportSession exportAsynchronouslyWithCompletionHandler:^{
NSLog(@"Video exported");
}];
return player;
}
How to use it:
For Example
//Creating AVPlayer and adding it to the view
NSURL *path = [[NSBundle mainBundle] URLForResource:@"1" withExtension:@"MOV"];
AVURLAsset* asset = [[AVURLAsset alloc] initWithURL:path options:nil];
AVPlayerItem * item = [[AVPlayerItem alloc] initWithAsset:asset];
AVPlayer *player = [[AVPlayer alloc] initWithPlayerItem:item];
AVPlayerLayer * avPlayerLayer=[AVPlayerLayer playerLayerWithPlayer:player];
avPlayerLayer.frame = CGRectMake(20, 20, 250, 250);
[self.view.layer addSublayer:avPlayerLayer];
[player play];
//Rotate video of AVPlayer and export it
player = [self rotateVideoPlayer:player withDegrees:-45.0];
Note: if you only want to rotate a video on the view use the transform method of the view that contains avplayer.
view.transform=CGAffineTransformMakeRotation(M_PI/2);