IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> iOS 视频方向修正 -> 正文阅读

[移动开发]iOS 视频方向修正

前言

最近在开发一款视频剪辑的App发现一个奇怪的问题,用iPhone手机的相机拍摄的视频,经过剪辑之后导出视频,发现视频被自动旋转了90度。于是,顺着这个问题深入研究了一下,将研究的过程和结果记录一下。

一、视频拍摄的方向与角度

iOS上内置相机应用录制的mov/mp4视频会产生一个Rotation元数据,表示录制视频时摄像头旋转到了多少角度。其值一般为这四个:0、90、180或270。类似于图片文件的 Exif 信息中的Orientation元数据。

Rotation元数据用于播放器确定渲染视频的方向,但是有的播放器会对其视而不见。也就是说当你用iPhone手机旋转90度或者180度录制的视频,然后在iphone手机上播放,仍然是播放没有被旋转的视频,但是其实视频信息中的roration已经记录了手机拍摄旋转的角度,当你将这个视频上传到服务器上可能就会展示它真实的旋转状态。

关于Rotation的0、90、180和270这四个角度值可以这样理解:LandscapeRigth为0度;以Home键或摄像头为圆心,顺时针旋转到Portrait为90度;旋转到LandscapeLeft为180度;旋转到PortraitUpsideDown为270度。

(1) roration为0,LandscapeRigth为0度,此时拍出来的视频才是正常的,没有被旋转的。这个与我们平常生活中的认知是不一样的。

在这里插入图片描述
(2) roration为90,以Home键或摄像头为圆心,顺时针旋转到Portrait为90度。

在这里插入图片描述

(3) roration为180,以Home键或摄像头为圆心,顺时针旋转到旋转到LandscapeLeft为180度。

在这里插入图片描述

(4) roration为270,以Home键或摄像头为圆心,顺时针旋转到PortraitUpsideDown为270度。

在这里插入图片描述

所以,其实iPhone手机拍摄视频角度的旋转和我们所觉得的不一样,我们平时不管90度还是180度旋转手机拍摄的视频,都能正向播放,是因为播放器忽略了旋转角度,但是你如果上传视频到服务器或者重新导出视频,就会发现视频被旋转了。不用觉得奇怪,其实这才是视频真实的状态。

二、修正视频方向

下面就讲一讲,当视频被旋转了,改如何修正视频的方向。

2.1 视频被旋转了90度

在这里插入图片描述

当视频被旋转了90度,要修正视频的方向,只需要经过两个步骤的变换:

  1. 将图1顺时针旋转90,便可得到图二,红点代表旋转的中心;
  2. 将图2沿着X轴移动height个单位,便可修正视频的方向;
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformRotate(transform, M_PI_2*1);
transform = CGAffineTransformTranslate(transform, videoTrack.naturalSize.height, 0);   
renderSize = CGSizeMake(videoTrack.naturalSize.height, videoTrack.naturalSize.width);

2.2 视频被旋转了180度

在这里插入图片描述

当视频被旋转了180度,要修正视频的方向,只需要经过两个步骤的变换:

  1. 将图1顺时针旋转180,便可得到图二,红点代表旋转的中心;
  2. 将图2沿着X轴移动width个单位,沿着Y轴移动height个单位,便可修正视频的方向;
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformRotate(transform, M_PI_2*2);
transform = CGAffineTransformTranslate(transform, videoTrack.naturalSize.width, videoTrack.naturalSize.height);   
renderSize = CGSizeMake(videoTrack.naturalSize.width, videoTrack.naturalSize.height);

2.3 视频被旋转了270度

在这里插入图片描述

当视频被旋转了270度,要修正视频的方向,只需要经过两个步骤的变换:

  1. 将图1顺时针旋转270度,便可得到图二,红点代表旋转的中心;
  2. 将图2沿着Y轴向下移动height个单位,沿着Y轴移动height个单位,便可修正视频的方向;
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformRotate(transform, M_PI_2*3);
transform = CGAffineTransformTranslate(transform, 0, videoTrack.naturalSize.width);
renderSize = CGSizeMake(videoTrack.naturalSize.height, videoTrack.naturalSize.width);

特别注意,除开旋转视频的方向和将视频进行位移,还需要调整视频画布的大小。

三、示例代码

- (void)fixVideoDirection:(AVURLAsset *)asset resultBlcok:(void (^)(NSURL *outputURL))resultBlock
{
    
    NSString *videoName = [NSString stringWithFormat:@"%@.mp4",[[NSDate date] stringWithFormat:@"YYYY-MM-dd HH:mm:ss"]];
    NSString *outputURL = [[FileTools createDirectoryInDocumentDirectory:@"/Videos"] stringByAppendingPathComponent:videoName];

    AVMutableComposition *composition = [AVMutableComposition composition];
    
    // 获取音频轨道
    AVAssetTrack *audioTrack = [asset tracksWithMediaType:AVMediaTypeAudio].firstObject;
    // 插入音频轨道
    AVMutableCompositionTrack *audioCompositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    [audioCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration) ofTrack:audioTrack atTime:kCMTimeZero error:nil];
    
    // 获取视频轨道
    AVAssetTrack *videoTrack = [asset tracksWithMediaType:AVMediaTypeVideo].firstObject;
    // 插入视频轨道
    AVMutableCompositionTrack *videoCompositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
    [videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration) ofTrack:videoTrack atTime:kCMTimeZero error:nil];
    
    // 获取视频修正方向(默认为摄像头方向)
    CGAffineTransform t = videoTrack.preferredTransform;
    CGAffineTransform transform = CGAffineTransformIdentity;
    CGSize renderSize = CGSizeMake(videoTrack.naturalSize.width, videoTrack.naturalSize.height);
    
    if(t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0) { // LandscapeRight, 0度
       //不需要作处理
        NSLog(@"视频没有被旋转");
        
   } else if (t.a == 0 && t.b == 1.0 && t.c == -1.0 && t.d == 0){ //90度
       
        transform = CGAffineTransformTranslate(transform, videoTrack.naturalSize.height, 0);
        transform = CGAffineTransformRotate(transform, M_PI_2*1);
        renderSize = CGSizeMake(videoTrack.naturalSize.height, videoTrack.naturalSize.width);
        NSLog(@"视频被旋转了90度");
       
    } else if (t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0) { //180度
        
        transform = CGAffineTransformTranslate(transform, videoTrack.naturalSize.width, videoTrack.naturalSize.height);
        transform = CGAffineTransformRotate(transform, M_PI_2*2);
        renderSize = CGSizeMake(videoTrack.naturalSize.width, videoTrack.naturalSize.height);
        NSLog(@"视频被旋转了180度");

    } else if(t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0) { //270度
        
        transform = CGAffineTransformTranslate(transform, 0, videoTrack.naturalSize.width);
        transform = CGAffineTransformRotate(transform, M_PI_2*3);
        renderSize = CGSizeMake(videoTrack.naturalSize.height, videoTrack.naturalSize.width);
        NSLog(@"视频被旋转了270度");
    }
    
    AVMutableVideoCompositionLayerInstruction *layerInstruciton = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoCompositionTrack];
    [layerInstruciton setTransform:transform atTime:kCMTimeZero]; //旋转
    
    AVMutableVideoCompositionInstruction *compositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
    compositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration);
    compositionInstruction.layerInstructions = @[layerInstruciton];
    
    AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition];
    videoComposition.instructions = @[compositionInstruction];
    videoComposition.frameDuration = CMTimeMakeWithSeconds( 1 / videoTrack.nominalFrameRate, 600);;
    videoComposition.renderScale = 1;
    videoComposition.renderSize = renderSize;
    
    // 创建视频导出会话
    AVAssetExportSession *session = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetHighestQuality];
    session.outputFileType = AVFileTypeMPEG4;
    session.outputURL = [NSURL fileURLWithPath:outputURL];
    session.shouldOptimizeForNetworkUse = YES;
    session.videoComposition = videoComposition;
       
    // 开始导出
    [session exportAsynchronouslyWithCompletionHandler:^{
        if (session.status == AVAssetExportSessionStatusCompleted) {
            resultBlock([NSURL fileURLWithPath:outputURL]);
        } else {
            NSLog(@"视频导出失败");
        }
    }];
}
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-03-03 16:26:11  更:2022-03-03 16:29:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 16:36:48-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码