版本:
OS X 10.10.5
Xcode 6.4(6E35b)
iOS >= 7 

一、MOV/MP4视频文件中的Rotation元数据

iOS上内置相机应用录制的mov/mp4视频可能产生一个Rotation元数据,表示录制视频时摄像头旋转到了多少角度。其值一般为这四个:0、90、180或270。类似于图片文件的Exif信息中的Orientation元数据。
Rotation元数据用于播放器确定渲染视频的方向,但有的播放器会对其视而不见。稍后会测试几种常见的播放器/播放控件对Rotation元数据的支持。
注:
实际上视频文件的Rotation元数据并不是保存的角度值,不过如果只关心角度问题而不是图像拉伸之类的,可以这样简单理解。关于如何获取Rotation元数据角度值,有兴趣的可以参看vlc的源码
 
下面用MediaInfo看看用iPhone相机应用使用后置摄像头录制的两个视频,观察其Rotation元数据。请留意文件名分别为IMG_1427.MOV和IMG_1428.MOV,后文也会用这两个文件做对比。
1、使用后置摄像头在Portrait(竖屏,Home键在下边)模式时录制的视频,其Rotation值为90。
(图一:Rotation值为90)
 
2、使用后置摄像头在LandscapeRigth(横屏,Home键在右边)模式时录制的视频,则无Rotation元数据,或者说Rotation值为0。
(图二:无Rotation值或者说Rotation值为0)
  
关于Rotation的0、90、180和270这四个角度值可以这样理解:LandscapeRigth为0度;以Home键或摄像头为圆心,顺时针旋转到Portrait为90度;旋转到LandscapeLeft为180度;旋转到PortraitUpsideDown为270度。
 
这里先在OS X 10.10.4和Windows 8上看看这两个视频文件的属性:
1、将手机里的视频文件导出到OS X,并在Finder中看预览,两个文件的显示方向都是正确的。再查看Rotation值为90的IMG_1427.MOV视频文件的属性。显示其尺寸为1080*1920,而不是1920*1080。但不要被这个假象欺骗了,视频的实际尺寸还是1920*1080。最后看没有Rotation值或者说Rotation值为0的IMG_1428.MOV视频文件,显示其尺寸为1920*1080,一切正常。使用QuickTime播放,能正确识别出两个视频的方向。
 
(图三) 
2、在Windows资源管理器中看预览,IMG_1427.MOV和IMG_1428.MOV的显示方向都是正确的;再查看两个文件的属性,尺寸都显示为1920*1080;使用Windows Media Player播放,能正确识别出两个视频的方向。

二、常见视频播放器对方向的识别

iOS相册调出的播放器和Win8上的Windows Media Player能够正确识别出MOV/MP4的方向,即实际尺寸为1920*1080的、Rotation值为90的IMG_1427.MOV视频能够按1080*1920的尺寸并调整方向进行渲染;没有Rotation值或者说Rotation值为0的IMG_1428.MOV视频按1920*1080的尺寸并按实际方向进行渲染。Andriod也存在类似情况。
VLC for OS X(why?)和iOS的MPMoviePlayerViewControlle对Rotation没有识别,它们总是按实际尺寸和默认方向进行渲染。对于MPMoviePlayerViewControlle下面有解决方案。
Safari浏览器调出的播放器应该也是MPMoviePlayerViewController,所以也无法正确识别方向。 

三、MPMoviePlayerViewController控制视频方向

需要额外的参数来确定视频的方向,然后旋转播放器,达到各种视频——mov/mp4/m3u8等——都可以正确播放的目的。
 
 //…...
NSString * url = @"http://www.yourdomain.com/Videos/1.m3u8";
MPMoviePlayerViewController * vc = [[MPMoviePlayerViewController alloc] init];
vc.moviePlayer.contentURL = [NSURL URLWithString:url];
// 这里播放一个Rotation为90的视频,即Home键在下录制的视频
[self rotateVideoView:vc degrees:];
[self presentMoviePlayerViewControllerAnimated:vc];
[vc.moviePlayer play]; //…...
- (void)rotateVideoView:(MPMoviePlayerViewController *)movePlayerViewController degrees:(NSInteger)degrees
{
if(degrees==||degrees==) return;
if(degrees<) degrees = (degrees % ) + ;
if(degrees>) degrees = degrees % ;
// MPVideoView在iOS8中Tag为1002,不排除苹果以后更改的可能性。参考递归查看View层次结构的lldb命令: (lldb) po [movePlayerViewController.view recursiveDescription]
UIView *videoView = [movePlayerViewController.view viewWithTag:];
if ([videoView isKindOfClass:NSClassFromString(@"MPVideoView")]) {
videoView.transform = CGAffineTransformMakeRotation(M_PI * degrees / 180.0);
videoView.frame = movePlayerViewController.view.bounds;
}
}

改为Category:

 #import "MPMoviePlayerViewController+Rotation.h"

 @implementation MPMoviePlayerViewController (Rotation)

 - (void)rotateVideoViewWithDegrees:(NSInteger)degrees
{
if(degrees==||degrees==) return;
if(degrees<) degrees = (degrees % ) + ;
if(degrees>) degrees = degrees % ; // MPVideoView在iOS8中Tag为1002,不排除苹果以后更改的可能性。参考递归查看View层次结构的lldb命令: (lldb) po [movePlayerViewController.view recursiveDescription]
UIView *videoView = [self.view viewWithTag:];
if ([videoView isKindOfClass:NSClassFromString(@"MPVideoView")]) {
videoView.transform = CGAffineTransformMakeRotation(M_PI * degrees / 180.0);
videoView.frame = self.view.bounds;
}
} @end

四、HTML5控制视频方向

在video标签中增加 style="-webkit-transform: rotate(90deg);” ,不过控件也被旋转了。这就需要将默认播放控件隐藏了并且自绘控件,此略。

五、使用ffmpeg写入Rotation元数据

对于没有Rotation元数据的mp4文件,可通过ffmpeg等工具写入。比如视频需要顺时针旋转90度显示:
 ffmpeg -i input.mp4 -c copy -metadata:s:v: rotate= output.mp4 
注:
如果愿意,写入非0、90、180或270的值,比如45之类的也是可以的。 

六、获取视频方向(角度)

+ (NSUInteger)degressFromVideoFileWithURL:(NSURL *)url
{
NSUInteger degress = ; AVAsset *asset = [AVAsset assetWithURL:url];
NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
if([tracks count] > ) {
AVAssetTrack *videoTrack = [tracks objectAtIndex:];
CGAffineTransform t = videoTrack.preferredTransform; if(t.a == && t.b == 1.0 && t.c == -1.0 && t.d == ){
// Portrait
degress = ;
}else if(t.a == && t.b == -1.0 && t.c == 1.0 && t.d == ){
// PortraitUpsideDown
degress = ;
}else if(t.a == 1.0 && t.b == && t.c == && t.d == 1.0){
// LandscapeRight
degress = ;
}else if(t.a == -1.0 && t.b == && t.c == && t.d == -1.0){
// LandscapeLeft
degress = ;
}
} return degress;
}

七、按正确方向对视频进行截图

关键点是将AVAssetImageGrnerator对象的appliesPreferredTrackTransform属性设置为YES。

 + (UIImage *)extractImageFromVideoFileWithUrl:(NSURL *)url
{
NSDictionary *opts = @{AVURLAssetPreferPreciseDurationAndTimingKey:@(NO)};
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:url options:opts];
AVAssetImageGenerator *gen = [[AVAssetImageGenerator alloc] initWithAsset:asset];
// 应用方向
gen.appliesPreferredTrackTransform = YES;
CMTime time = CMTimeMakeWithSeconds(, );
NSError *error = nil;
CMTime actualTime;
CGImageRef image = [gen copyCGImageAtTime:time actualTime:&actualTime error:&error];
if(error)
{
DLog(@"%@ %@",__FUNCTION_FILE_LINE__,error);
return nil;
}
UIImage *thumb = [[UIImage alloc] initWithCGImage:image];
CGImageRelease(image); return thumb;

八、实时视频的方向处理

使用AVFoundation制作自定义相机时,采集出来的视频帧保存在CMSampleBufferRef结构中,颜色空间可以设置为sRGB或YUV。进行一些内存操作就可实现旋转。以下代码是针对YUV的。 
注:
这种涉及大量内存拷贝的操作,实际应用中要权衡其利弊。以下代码未经过测试。

1、RGB24旋转90度  
 // RGB24旋转90度
void RGB24Rotate90(int8_t *des, const int8_t *src, int width, int height)
{
if(!des || !src) return; int n = ;
int linesize = width * ;
int i, j;
// 逆时针旋转
for (j = width; j > ; j--) {
for (i = ; i < height; i++) {
memccpy(&des[n], &src[linesize * i + j * - ], , );
n += ;
}
}
/*
// 顺时针旋转
for (j = 0 ; j < width; j++) {
for (i = height; i > 0; i--) {
memccpy(&des[n], &src[linesize * (i - 1) + j * 3 - 3], 0, 3);
n += 3;
}
}
*/
}

 
2、RGB24旋转90度  
 // YUV420旋转90度
void YUV420Rotate90(int8_t *des, const int8_t *src, int width, int height)
{
int i = , j = , n = ;
int hw = width / , hh = height / ; const int8_t *ptmp = src;
for (j = width; j > ; j--) {
for (i = ; i < height; i++) {
des[n++] = ptmp[width * i + j];
}
} ptmp = src + width * height;
for (j = hw; j > ; j--) {
for (i = ; i < hh; i++) {
des[n++] = ptmp[hw * i + j];
}
} ptmp = src + width * height * / ;
for (j = hw; j > ; j--) {
for (i = ; i < hh; i++) {
des[n++] = ptmp[hw * i + j];
}
}
}

或:

 int8_t[] rotateYUV420Degree90(int8_t[] data, int imageWidth, int imageHeight)
{
int8_t [] yuv = new int8_t[imageWidth*imageHeight*/];
// Rotate the Y luma
int i = ;
for(int x = ;x < imageWidth;x++)
{
for(int y = imageHeight-;y >= ;y--)
{
yuv[i] = data[y*imageWidth+x];
i++;
}
}
// Rotate the U and V color components
i = imageWidth*imageHeight*/-;
for(int x = imageWidth-;x > ;x=x-)
{
for(int y = ;y < imageHeight/;y++)
{
yuv[i] = data[(imageWidth*imageHeight)+(y*imageWidth)+x];
i--;
yuv[i] = data[(imageWidth*imageHeight)+(y*imageWidth)+(x-)];
i--;
}
}
return yuv;
}

九、参考资料:

 
 

[iOS]关于视频方向的若干问题的更多相关文章

  1. 《转》iOS音频视频初级开发

    代码改变世界 Posts - 73, Articles - 0, Comments - 1539 Cnblogs Dashboard Logout HOME CONTACT GALLERY RSS   ...

  2. iOS 音频视频制作

    --iOS多媒体 概览 随着移动互联网的发展,如今的手机早已不是打电话.发短信那么简单了,播放音乐.视频.录音.拍照等都是很常用的功能.在iOS中对于多媒体的支持是非常强大的,无论是音视频播放.录制, ...

  3. 如何实现 iOS 短视频跨页面的无痕续播?

    在一切皆可视频化的今天,短视频内容作为移动端产品新的促活点,受到了越来越多的重视与投入.盒马在秒播.卡顿率.播放成功率等基础优化之外,在用户使用体验上引入了无痕续播能力,提升用户观看视频内容的延续性. ...

  4. ios 音视频实现边播边缓存的思路和解决方案 (转)

    本片为转载内容,主要是以后自己看起来方便一些 原文地址:iOS音视频实现边下载边播放 其实音视频本地缓存的思想都差不多,都需要一个中间对象来连接播放器和服务器. 近段时间制作视频播放社区的功能,期间查 ...

  5. iOS 音频视频图像合成那点事

    代码地址如下:http://www.demodashi.com/demo/13420.html 人而无信不知其可 前言 很久很久没有写点什么了,只因为最近事情太多了,这几天终于闲下来了,趁此机会,记录 ...

  6. 腾讯ios内部视频,什么垃圾视频

    前几天朋友在网上花钱买了个,腾讯ios内部视频,我也跟着下载了, 看着这列表,我感觉没什么东西,一看就是基础的东西,完全没有实战的内容,就像培训机构骗学生的东西啊,讲些毛理论,结果一到实战了,问个Sc ...

  7. iOS RTMP 视频直播开发笔记(1) – 采集摄像头图像

    1. 采集硬件(摄像头)视频图像 这里简单说下 iOS 的摄像头采集. 首先初始化AVCaptureSession,说到Session,有没有人想到AVAudioSession呢? // 初始化 AV ...

  8. 从350ms到80ms,揭秘阿里工程师 iOS 短视频优化方案

    内容作为 App 产品新的促活点,受到了越来越多的重视与投入,短视频则是增加用户粘性.增加用户停留时长的一把利器.短视频的内容与体验直接关系到用户是否愿意长时停留,盒马也提出全链路内容视频化的规划,以 ...

  9. [[其他教程]] 2015年最新版iOS基础视频_最适合初学者入门

    主讲:孙庆虎类型:iOS 适合对象:初学者入门视频介绍:本视频是iOS学院精心录制的免费精华版iOS语言基础视频,该视频特点在于最大程度保证了知识点的完整性,按知识点进行视频录制,每个视频控制在20分 ...

随机推荐

  1. verify.js使用验证插件使用

    github:https://github.com/52fhy/verify.js 首先引入js,最好拷贝verify整个目录,因为里面有图标. <script src="verify ...

  2. uva111动态规划之最长公共子序列

    http://acm.hust.edu.cn/vjudge/contest/view.action?cid=74662#problem/C     A B C D E C - Largest Rect ...

  3. vs2012 MSDN帮助文档离线包下载安装方法

    vs2012安装文件 自带的 MSDN帮助文档不全, 需要自己手动添加需要的离线文档包, 具体方法如下 1. 打开 vs2012 2. 按 ctrl + alt + F1 打开帮助文档管理器 3. 在 ...

  4. IrregularGridCollectionView处理不定宽度的标签cell

    IrregularGridCollectionView处理不定宽度的标签cell 效果 源码 https://github.com/YouXianMing/UI-Component-Collectio ...

  5. 使用commons-beanutils迭代获取javabean的属性

    NoteEntity entity = new NoteEntity(); entity.setNote001("a1"); entity.setNote002("a2& ...

  6. GitHub 优秀的 Android 开源项目(转)

    今天查找资源时看到的一篇文章,总结了很多实用资源,十分感谢原作者分享. 转自:http://blog.csdn.net/shulianghan/article/details/18046021 主要介 ...

  7. Sublime Text 3103 Crack 破解 注册码(亲测有效)

    随机复制下面的几四个注册码 粘贴到sublime text 3(Build 3103)注册框 就可以了! 第一个--first licence key : ====================== ...

  8. Scala 深入浅出实战经典 第41讲:List继承体系实现内幕和方法操作源码揭秘

    Scala 深入浅出实战经典 第41讲:List继承体系实现内幕和方法操作源码揭秘 package com.parllay.scala.dataset /** * Created by richard ...

  9. Mac上编译libimobiledevice库

    0.准备工作: 使用brew或Mac Ports安装:libgnutls or openssl. libplist .libusb.libusbmuxd 1.下载代码: 下载地址:https://gi ...

  10. apache工作模式:prefork和worker

    apache作为现今web服务器用的最广泛也是最稳定的开源服务器软件,其工作模式有许多中,目前主要有两种模式:prefork模式和worker模式 一.两种模式 prefork模式: prefork是 ...