热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

版本:OSX10.10.5Xcode6.4(6E35b)iOS7?一、MOVMP4视频文件中的Rotation元数据iOS上内置相机应用录制的movmp4视频可能产生一个Rota
版本:
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  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  0; j--) {
        for (i = 0; i  0; j--) {
        for (i = 0; i  

或:

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 = 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  

 九、参考资料:   
http://www.rosoo.net/a/201006/9689.html
http://stackoverflow.com/questions/14167976/rotate-an-yuv-byte-array-on-android

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


推荐阅读
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 本文讲述了作者通过点火测试男友的性格和承受能力,以考验婚姻问题。作者故意不安慰男友并再次点火,观察他的反应。这个行为是善意的玩人,旨在了解男友的性格和避免婚姻问题。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
author-avatar
手机用户2702936061
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有