[iOS]关于视频方向的若干问题

版本:

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:90];
    [self presentMoviePlayerViewControllerAnimated:vc];
    [vc.moviePlayer play];

//…...
- (void)rotateVideoView:(MPMoviePlayerViewController *)movePlayerViewController degrees:(NSInteger)degrees
{
    if(degrees==0||degrees==360) return;
    if(degrees<0) degrees = (degrees % 360) + 360;
    if(degrees>360) degrees = degrees % 360;
    // MPVideoView在iOS8中Tag为1002,不排除苹果以后更改的可能性。参考递归查看View层次结构的lldb命令: (lldb) po [movePlayerViewController.view recursiveDescription]
    UIView *videoView = [movePlayerViewController.view viewWithTag:1002];
    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==0||degrees==360) return;
    if(degrees<0) degrees = (degrees % 360) + 360;
    if(degrees>360) degrees = degrees % 360;

    // MPVideoView在iOS8中Tag为1002,不排除苹果以后更改的可能性。参考递归查看View层次结构的lldb命令: (lldb) po [movePlayerViewController.view recursiveDescription]
    UIView *videoView = [self.view viewWithTag:1002];
    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:0 rotate=90 output.mp4

注:

如果愿意,写入非0、90、180或270的值,比如45之类的也是可以的。

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

+ (NSUInteger)degressFromVideoFileWithURL:(NSURL *)url
{
    NSUInteger degress = 0;

    AVAsset *asset = [AVAsset assetWithURL:url];
    NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
    if([tracks count] > 0) {
        AVAssetTrack *videoTrack = [tracks objectAtIndex:0];
        CGAffineTransform t = videoTrack.preferredTransform;

        if(t.a == 0 && t.b == 1.0 && t.c == -1.0 && t.d == 0){
            // Portrait
            degress = 90;
        }else if(t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0){
            // PortraitUpsideDown
            degress = 270;
        }else if(t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0){
            // LandscapeRight
            degress = 0;
        }else if(t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0){
            // LandscapeLeft
            degress = 180;
        }
    }

    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(1, 60);
    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 = 0;
    int linesize = width * 3;
    int i, j;
    // 逆时针旋转
    for (j = width; j > 0; j--) {
        for (i = 0; i < height; i++) {
            memccpy(&des[n], &src[linesize * i + j * 3 - 3], 0, 3);
            n += 3;
        }
    }
    /*
    // 顺时针旋转
    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 = 0, j = 0, n = 0;
    int hw = width / 2, hh = height / 2;

    const int8_t *ptmp = src;
    for (j = width; j > 0; j--) {
        for (i = 0; i < height; i++) {
            des[n++] = ptmp[width * i + j];
        }
    }

    ptmp = src + width * height;
    for (j = hw; j > 0; j--) {
        for (i = 0; i < hh; i++) {
            des[n++] = ptmp[hw * i + j];
        }
    }

    ptmp = src + width * height * 5 / 4;
    for (j = hw; j > 0; j--) {
        for (i = 0; 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*3/2];
    // Rotate the Y luma
    int i = 0;
    for(int x = 0;x < imageWidth;x++)
    {
        for(int y = imageHeight-1;y >= 0;y--)
        {
            yuv[i] = data[y*imageWidth+x];
            i++;
        }
    }
    // Rotate the U and V color components
    i = imageWidth*imageHeight*3/2-1;
    for(int x = imageWidth-1;x > 0;x=x-2)
    {
        for(int y = 0;y < imageHeight/2;y++)
        {
            yuv[i] = data[(imageWidth*imageHeight)+(y*imageWidth)+x];
            i--;
            yuv[i] = data[(imageWidth*imageHeight)+(y*imageWidth)+(x-1)];
            i--;
        }
    }
    return yuv;
}

九、参考资料:

http://www.rosoo.net/a/201006/9689.html

http://stackoverflow.com/questions/14167976/rotate-an-yuv-byte-array-on-android

时间: 2024-11-03 02:24:26

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

iOS 音频视频制作

--iOS多媒体 概览 随着移动互联网的发展,如今的手机早已不是打电话.发短信那么简单了,播放音乐.视频.录音.拍照等都是很常用的功能.在iOS中对于多媒体的支持是非常强大的,无论是音视频播放.录制,还是对麦克风.摄像头的操作都提供了多套API.在今天的文章中将会对这些内容进行一一介绍: 音频 音效 音乐 音频会话 录音 音频队列服务 视频 MPMoviePlayerController MPMoviePlayerViewController AVPlayer 摄像头 UIImagePicker

iOS屏幕旋转方向的相关方法

在iOS应用开发过程中,经常会遇到设置屏幕方向,或者根据屏幕方向改变界面的时候,所以现在就来说一下屏幕方向的那些事情. 关于方向,经常会遇到以下的两个对象: 1.UIDeviceOrientation(机器设备的方向) ================================== UIDeviceOrientationUnknown //未知方向 UIDeviceOrientationPortrait, //设备直立,home按钮在下 UIDeviceOrientationPortrai

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

本片为转载内容,主要是以后自己看起来方便一些 原文地址:iOS音视频实现边下载边播放 其实音视频本地缓存的思想都差不多,都需要一个中间对象来连接播放器和服务器. 近段时间制作视频播放社区的功能,期间查找了不少资料,做过很多尝试,现在来整理一下其中遇到的一些坑.由于考虑到AVPlayer对视频有更高自由度的控制,而且能够使用它自定义视频播放界面,iOS中所使用的视频播放控件为AVPlayer,而抛弃了高层次的MediaPlayer框架,现在想想挺庆幸当初使用了AVPlayer. AVPlayer的

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

前几天朋友在网上花钱买了个,腾讯ios内部视频,我也跟着下载了, 看着这列表,我感觉没什么东西,一看就是基础的东西,完全没有实战的内容,就像培训机构骗学生的东西啊,讲些毛理论,结果一到实战了,问个ScrollView布局都不会,还装两三年,什么傻逼,还想混日子,面试的时候,全是一些所谓的两三年. 结果所谓的腾讯内部视频,操,什么垃圾玩意啊 八点钟学院,以为技术好屌,讲些垃圾基础的东西,还没讲几分钟就打自己的广告,收费,操

IOS 播放视频 MPMoviePlayerController

在unity游戏的开头播放视频 , 根据需求 , 最后决定用 MPMoviePlayerController 来实现播放, 实现如下: by Tin 需要在AppController.mm的 OpenEAGL_UnityCallback  修改下view的大小 UIView *mainView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]]; // mainView.backgroundColor

移动视频技术——新增API可手工修正视频方向

由于Android的开放性,不同的设备存在一些差异,某些设备所采集的视频数据显示出来在方向上存在一定的误差,这通常与硬件设备的驱动相关,新增加的API接口便是在远程显示时,可以支持手工来修正远程视频的方向. 增加的API接口参数定义(以AnyChat for Web SDK为例): // 远程视频方向修正标志定义 var BRAC_ROTATION_FLAGS_MIRRORED       =        0x1000;        // 图像需要镜像翻转 var BRAC_ROTATION

iOS技术视频 这个是浩哥给大家录得视频,主要针对是拔高的

#iOS开发日报# iOS技术视频 这个是浩哥给大家录得视频,主要针对是拔高的,想要提升的学员,大家有需要的可以看一下. 1 自适应布局Masonry Git:https://github.com/lanoudl/LO_Masonry 链接: http://pan.baidu.com/s/1jGpNYIi 密码: 34r3 断点续传的实现过程 Git:https://github.com/lanoudl/LO_DownLoadTask 链接: http://pan.baidu.com/s/1gd

iOS截取视频预览图,截图方向错误的解决

今天在修改项目bug的时候遇到了,截图视频预览图的时候总是方向不对,竖屏录制的视频(好像是竖屏录制截图出的错,记不太清楚了??)截图出来总是向左旋转了90度. 开始我查看图片的imageOrientation属性,发现都是Up,然后我试着查看图片的width和height,发现无论是对的还是错的,width和height都是一样,oh,shit! 后来我发现了appliesPreferredTrackTransform这个属性,默认是false(Objective-C中为NO),只要将其设置为t

Android视频录制从不入门到入门系列教程(三)————视频方向

运行Android视频录制从不入门到入门系列教程(二)————显示视频图像中的Demo后,我们应该能发现视频的方向是错误的. 由于Android中,Camera给我们的视频图片的原始方向是下图这个样子的: 就是说,即使你是竖着拿手机的,Camera提供给你的视频图像的方向还是上图那样横着的图片. 我们可以通过下述方向改变Camera提供的视频图像的方法: camera.setDisplayOrientation(90); 让图像顺时针旋转90度,视频图像的方向就正常的. 本篇文章DEMO下载.