王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: 小  |  中  |  大  |  超大  

版本:

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等——都可以正确播放的目的。

1//…...2NSString * url =@"http://www.yourdomain.com/Videos/1.m3u8";3MPMoviePlayerViewController * vc =[[MPMoviePlayerViewController alloc] init];4vc.moviePlayer.contentURL =[NSURL URLWithString:url];5//这里播放一个Rotation为90的视频,即Home键在下录制的视频6[self rotateVideoView:vc degrees:90];7[selfPResentMoviePlayerViewControllerAnimated:vc];8[vc.moviePlayer play];910//…...11- (void)rotateVideoView:(MPMoviePlayerViewController *)movePlayerViewController degrees:(NSInteger)degrees12{13if(degrees==0||degrees==360)return;14if(degrees<0) degrees = (degrees %360) +360;15if(degrees>360) degrees = degrees %360;16//MPVideoView在iOS8中Tag为1002,不排除苹果以后更改的可能性。参考递归查看View层次结构的lldb命令: (lldb) po [movePlayerViewController.view recursiveDescription]17UIView *videoView = [movePlayerViewController.view viewWithTag:1002];18if([videoView isKindOfClass:NSClassFromString(@"MPVideoView")]) {19videoView.transform = CGAffineTransformMakeRotation(M_PI * degrees /180.0);20videoView.frame =movePlayerViewController.view.bounds;21}22}

View Code

改为Category:

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

四、HTML5控制视频方向

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

五、使用ffmpeg写入Rotation元数据 对于没有Rotation元数据的mp4文件,可通过ffmpeg等工具写入。比如视频需要顺时针旋转90度显示:

ffmpeg -i input.mp4 -c copy -metadata:s:v:0rotate=90output.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){//Portraitdegress =90;

}elseif(t.a ==0&& t.b == -1.0&& t.c ==1.0&& t.d ==0){//PortraitUpsideDowndegress =270;

}elseif(t.a ==1.0&& t.b ==0&& t.c ==0&& t.d ==1.0){//LandscapeRightdegress =0;

}elseif(t.a == -1.0&& t.b ==0&& t.c ==0&& t.d == -1.0){//LandscapeLeftdegress =180;

}

}returndegress;

}

七、按正确方向对视频进行截图关键点是将AVAssetImageGrnerator对象的appliesPreferredTrackTransform属性设置为YES。

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

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

注:

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

1、RGB24旋转90度

1//RGB24旋转90度2voidRGB24Rotate90(int8_t *des,constint8_t *src,intwidth,intheight)3{4if(!des || !src)return;56intn =0;7intlinesize = width *3;8inti, j;9//逆时针旋转10for(j = width; j >0; j--) {11for(i =0; i < height; i++) {12memccpy(&des[n], &src[linesize * i + j *3-3],0,3);13n +=3;14}15}16/*17// 顺时针旋转18for (j = 0 ; j < width; j++) {19for (i = height; i > 0; i--) {20memccpy(&des[n], &src[linesize * (i - 1) + j * 3 - 3], 0, 3);21n += 3;22}23}24*/25}

View Code

2、RGB24旋转90度

1//YUV420旋转90度2voidYUV420Rotate90(int8_t *des,constint8_t *src,intwidth,intheight)3{4inti =0, j =0, n =0;5inthw = width /2, hh = height /2;67constint8_t *ptmp =src;8for(j = width; j >0; j--) {9for(i =0; i < height; i++) {10des[n++] = ptmp[width * i +j];11}12}1314ptmp = src + width *height;15for(j = hw; j >0; j--) {16for(i =0; i < hh; i++) {17des[n++] = ptmp[hw * i +j];18}19}2021ptmp = src + width * height *5/4;22for(j = hw; j >0; j--) {23for(i =0; i < hh; i++) {24des[n++] = ptmp[hw * i +j];25}26}27}

View Code

或:

1int8_t[] rotateYUV420Degree90(int8_t[] data,intimageWidth,intimageHeight)2{3int8_t [] yuv =newint8_t[imageWidth*imageHeight*3/2];4//Rotate the Y luma5inti =0;6for(intx =0;x < imageWidth;x++)7{8for(inty = imageHeight-1;y >=0;y--)9{10yuv[i] = data[y*imageWidth+x];11i++;12}13}14//Rotate the U and V color components15i = imageWidth*imageHeight*3/2-1;16for(intx = imageWidth-1;x >0;x=x-2)17{18for(inty =0;y < imageHeight/2;y++)19{20yuv[i] = data[(imageWidth*imageHeight)+(y*imageWidth)+x];21i--;22yuv[i] = data[(imageWidth*imageHeight)+(y*imageWidth)+(x-1)];23i--;24}25}26returnyuv;27}

View Code

九、参考资料: http://www.rosoo.net/a/201006/9689.html

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

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航