您的位置:首页 > 其它

关于视频方向的若干问题

2016-04-01 17:52 465 查看
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等——都可以正确播放的目的。


View
Code
改为Category:

1 #import "MPMoviePlayerViewController+Rotation.h"
2
3 @implementation MPMoviePlayerViewController (Rotation)
4
5 - (void)rotateVideoViewWithDegrees:(NSInteger)degrees
6 {
7     if(degrees==0||degrees==360) return;
8     if(degrees<0) degrees = (degrees % 360) + 360;
9     if(degrees>360) degrees = degrees % 360;
10
11     // MPVideoView在iOS8中Tag为1002,不排除苹果以后更改的可能性。参考递归查看View层次结构的lldb命令: (lldb) po [movePlayerViewController.view recursiveDescription]
12     UIView *videoView = [self.view viewWithTag:1002];
13     if ([videoView isKindOfClass:NSClassFromString(@"MPVideoView")]) {
14         videoView.transform = CGAffineTransformMakeRotation(M_PI * degrees / 180.0);
15         videoView.frame = self.view.bounds;
16     }
17 }
18
19 @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。

1 + (UIImage *)extractImageFromVideoFileWithUrl:(NSURL *)url
2 {
3     NSDictionary *opts = @{AVURLAssetPreferPreciseDurationAndTimingKey:@(NO)};
4     AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:url options:opts];
5     AVAssetImageGenerator *gen = [[AVAssetImageGenerator alloc] initWithAsset:asset];
6     // 应用方向
7     gen.appliesPreferredTrackTransform = YES;
8     CMTime time = CMTimeMakeWithSeconds(1, 60);
9     NSError *error = nil;
10     CMTime actualTime;
11     CGImageRef image = [gen copyCGImageAtTime:time actualTime:&actualTime error:&error];
12     if(error)
13     {
14         DLog(@"%@ %@",__FUNCTION_FILE_LINE__,error);
15         return nil;
16     }
17     UIImage *thumb = [[UIImage alloc] initWithCGImage:image];
18     CGImageRelease(image);
19
20     return thumb;
21 }



八、实时视频的方向处理

使用AVFoundation制作自定义相机时,采集出来的视频帧保存在CMSampleBufferRef结构中,颜色空间可以设置为sRGB或YUV。进行一些内存操作就可实现旋转。以下代码是针对YUV的。

注:
这种涉及大量内存拷贝的操作,实际应用中要权衡其利弊。以下代码未经过测试。

1、RGB24旋转90度


View
Code

2、RGB24旋转90度


View
Code
或:


View
Code


九、参考资料:

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

http://stackoverflow.com/questions/14167976/rotate-an-yuv-byte-array-on-android
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: