前言

最近在开发一款视频剪辑的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(@"视频导出失败");}}];
}

iOS 视频方向修正相关推荐

  1. iOS视频硬编码技术

    iOS视频硬编码技术 一.iOS视频采集硬编码 基本原理 硬编码 & 软编码 硬编码:通过系统自带的Camera录制视频,实际上调用的是底层的高清编码硬件模块,即显卡,不使用CPU,速度快 软 ...

  2. iOS视频采集实战(AVCaptureSession)

    需求:使用AVFoundation中的AVCaptureSession实现设置相机的分辨率,帧率(包括高帧率), 切换前后置摄像头,对焦,屏幕旋转,调节曝光度... 阅读前提: 原理请参考另一篇文章: ...

  3. IOS视频编辑,视频裁剪,视频拼接,音频处理,视频处理

    前言 用代码在简单视频编辑中,主要就是加美颜.水印(贴图).视频截取.视频拼接.音视频的处理,在美颜中,使用GPUImage即可实现多种滤镜.磨皮美颜的功能,并且可以脸部识别实时美颜等功能,这个有很多 ...

  4. WeBRTC IOS视频采集流程

    因CSDN MardDown语法问题,流程图部分不兼容有道云笔记,所以流程图部分请拷贝到有道云笔记生成查看. iOS视频录制: 同拍照一样视频录制功能有两种实现方式 UIImagePickerView ...

  5. ios 视频处理详解一(视频导出)

    - (void)test:(NSURL *)videoUrl{//videoUrl为什么视频的输入地址NSDictionary *options = [NSDictionary dictionaryW ...

  6. 一对一直播软件源码开发,iOS视频采集的实现过程

    在一对一直播软件源码日益火热的发展形势下,音视频开发(采集.编解码.传输.播放.美颜)等技术也随之成为开发者们关注的重点,本系列文章就音视频开发过程中所运用到的技术和原理进行梳理和总结. 认识 AVC ...

  7. Android音视频方向进阶路线及资源合集

    音视频从采集到播放都经历了哪些流程呢:: 通过上面的图,我们简单的把音视频方向分为主要的两块: 媒体部分(蓝色+绿色) 传输部分(红色) 1.媒体部分 我们这篇文章不再从音视频专业知识开始,而从And ...

  8. 直播软件搭建Android音视频方向进阶路线及资源合集

    直播软件搭建Android音视频方向进阶路线及资源合集 直播软件搭建的音视频从采集到播放都经历了哪些流程呢:: 通过上面的图,我们简单的把音视频方向分为主要的两块: 媒体部分(蓝色+绿色) 传输部分( ...

  9. ios视频和音频采集

    ios视频和音频采集以及预览 本文将说明如何用ios做视频和音频的采集,以及预览,预览采用的是系统自带的AVCaptureVideoPreviewLayer和UIView,视频采集用AVCapture ...

  10. iOS视频录制、压缩、上传(整理)

    iOS视频录制.压缩.上传(整理) 我们在项目中有时会碰到视频相关的需求,一般的可以分为几种情况: 1. 简单的视频开发,对界面无要求,可直接使用系统UIImagePickerController. ...

最新文章

  1. c#测试字符串是否为GUID的几种方法
  2. 菜鸟从零开始的第一个应用上线记(三)
  3. 基于tcp协议的socket
  4. TMG 模拟公司网络架构要点
  5. (Oracle)rownum用法详解 转载的
  6. 包管理conda操作,常用conda命令
  7. org.apache.jasper.JasperException: /index.jsp(14,2) The s:form tag declares that it accepts dyna
  8. Centos Python安装graphviz和pydotplus
  9. 怎样查看sql服务器日志文件,怎么通过sql日志,查看以前执行过的sql语句
  10. 课后习题讲解(免费)高数下册
  11. 泛泰A850 (高通8064+720p)刷4.4专用中文recovery TWRP2.7.1.3版
  12. html 必填设置,html如何设置必填项
  13. 【摘录】B2C大点名:国内B2C网站收集
  14. 【vue+springboot】excel模板下载、导入功能实现
  15. Armbian (jammy) 上安装 Docker
  16. matlab画红色爱心(心形图)
  17. 一个笼子里有鸡和兔,现在只知道里面一共有 40 个头,100 只脚,鸡兔各有多少只?
  18. 计算雅思成绩C语言,雅思成绩计算器分析
  19. java分页查询querymap_ES Java API_基于search template实现按品牌分页查询模板
  20. 周志华机器学习(西瓜书)学习笔记(持续更新)

热门文章

  1. 大数据难吗?如何快速掌握大数据开发技能
  2. 桌面客户端框架技术选型
  3. unity物体自身轴旋转_Unity 中物体的旋转
  4. Android8.0以上,打开uiautomatorviewer.bat,报错Unexpected error while obtaining Ul hierarchy
  5. 小米路由器 内核 linux,小米路由器配置ssh登入方法教程
  6. Form_Load():不要甩锅给我
  7. win10雷电3接口驱动_[九猫win10系统]Intel处理器福利普及雷电3接口:微软/苹果强烈支...
  8. 内是独体字还是半包围_独体字与半包围的字究竟怎么区分?
  9. Android 拨号盘应用源码分析
  10. OSG 单体化如何生成