AVAssetReader+AVAssetReaderTrackOutput播放视频
该文章引用自:http://www.jianshu.com/p/3d5ccbde0de1
IOS 微信聊天发送小视频的秘密(AVAssetReader+AVAssetReaderTrackOutput播放视频)
MPMoviePlayerController
类,确实用这个苹果官方为我们包装好了的 API 确实有很多事情都不用我们烦心,我们可以很快的做出一个视频播放器,但是很遗憾,高度封装的东西,就证明了可自定义性越受限制,而MPMoviePlayerController
却正正证明了这一点。所以大家又相对的想起了AVPlayer
,是的,AVPlayer
是一个很好的自定义播放器,但是,AVPlayer
却有着性能限制,微信团队也证实这一点,AVPlayer
只能同事播放16个视频,之后创建一个视频,对可滚动的聊天界面来说,是一个非常致命的性能限制了。AVAssetReader+AVAssetReaderTrackOutput
那么既然AVPlayer
有着性能限制,我们就做一个属于我们的播放器吧,AVAssetReader
可以从原始数据里获取解码后的音视频数据。结合AVAssetReaderTrackOutput
,能读取一帧帧的CMSampleBufferRef
。CMSampleBufferRef
可以转化成CGImageRef
。为此,我们可以创建一个ABSMovieDecoder
的一个类来负责视频解码,把读出的每一个CMSampleBufferRef
传递给上层。
那么用ABSMovieDecoder
的- (void)transformViedoPathToSampBufferRef:(NSString *)videoPath
方法利用AVAssetReader+AVAssetReaderTrackOutput
解码的步骤如下:
1.获取媒体文件的资源AVURLAsset
// 获取媒体文件路径的 URL,必须用 fileURLWithPath: 来获取文件 URL
NSURL *fileUrl = [NSURL fileURLWithPath:videoPath];
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:fileUrl options:nil];
NSError *error = nil;
AVAssetReader *reader = [[AVAssetReader alloc] initWithAsset:asset error:&error];
2.创建一个读取媒体数据的阅读器AVAssetReader
AVAssetReader *reader = [[AVAssetReader alloc] initWithAsset:asset error:&error];
3.获取视频的轨迹AVAssetTrack
其实就是我们的视频来源
NSArray *videoTracks = [asset tracksWithMediaType:AVMediaTypeVideo];
AVAssetTrack *videoTrack =[videoTracks objectAtIndex:0];
4.为我们的阅读器AVAssetReader
进行配置,如配置读取的像素,视频压缩等等,得到我们的输出端口videoReaderOutput
轨迹,也就是我们的数据来源
int m_pixelFormatType;
// 视频播放时,
m_pixelFormatType = kCVPixelFormatType_32BGRA;
// 其他用途,如视频压缩
// m_pixelFormatType = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
NSMutableDictionary *options = [NSMutableDictionary dictionary];
[options setObject:@(m_pixelFormatType) forKey:(id)kCVPixelBufferPixelFormatTypeKey];
AVAssetReaderTrackOutput *videoReaderOutput = [[AVAssetReaderTrackOutput alloc] initWithTrack:videoTrack outputSettings:options];
5.为阅读器添加输出端口,并开启阅读器
[reader addOutput:videoReaderOutput];
[reader startReading];
6.获取阅读器输出的数据源 CMSampleBufferRef
// 要确保nominalFrameRate>0,之前出现过android拍的0帧视频
while ([reader status] == AVAssetReaderStatusReading && videoTrack.nominalFrameRate > 0) {
// 读取 video sample
CMSampleBufferRef videoBuffer = [videoReaderOutput copyNextSampleBuffer];
[self.delegate mMoveDecoder:self onNewVideoFrameReady:videoBuffer];
// 根据需要休眠一段时间;比如上层播放视频时每帧之间是有间隔的,这里的 sampleInternal 我设置为0.001秒
[NSThread sleepForTimeInterval:sampleInternal];
}
7.通过代理告诉上层解码结束
// 告诉上层视频解码结束
[self.delegate mMoveDecoderOnDecoderFinished:self];
至此,我们就能获取视频的每一帧的元素CMSampleBufferRef
,但是我们要把它转换成对我们有用的东西,例如图片
// AVFoundation 捕捉视频帧,很多时候都需要把某一帧转换成 image
+ (CGImageRef)imageFromSampleBufferRef:(CMSampleBufferRef)sampleBufferRef
{
// 为媒体数据设置一个CMSampleBufferRef
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBufferRef);
// 锁定 pixel buffer 的基地址
CVPixelBufferLockBaseAddress(imageBuffer, 0);
// 得到 pixel buffer 的基地址
void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
// 得到 pixel buffer 的行字节数
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
// 得到 pixel buffer 的宽和高
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
// 创建一个依赖于设备的 RGB 颜色空间
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
// 用抽样缓存的数据创建一个位图格式的图形上下文(graphic context)对象
CGContextRef context = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
//根据这个位图 context 中的像素创建一个 Quartz image 对象
CGImageRef quartzImage = CGBitmapContextCreateImage(context);
// 解锁 pixel buffer
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
// 释放 context 和颜色空间
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
// 用 Quzetz image 创建一个 UIImage 对象
// UIImage *image = [UIImage imageWithCGImage:quartzImage];
// 释放 Quartz image 对象
// CGImageRelease(quartzImage);
return quartzImage;
}
从上面大家可以可得出,获取图片图片的最直接有效的是 UIImage
了,但是为什么我不需要 UIImage
却要了个撇足的 CGImageRef
呢? 那是因为创建CGImageRef不会做图片数据的内存拷贝,它只会当 Core Animation
执行 Transaction::commit()
触发 layer -display
时,才把图片数据拷贝到 layer buffer
里。简单点的意思就是说不会消耗太多的内存!
接下来我们需要把所有得到的CGImageRef
元素都合成视频了。当然在这之前应该把所有的 CGImageRef
当做对象放在一个数组中。那么知道CGImageRef
为 C 语言的结构体,这时候我们要用到桥接来将CGImageRef
转换成我们能用的对象了
CGImageRef cgimage = [UIImage imageFromSampleBufferRef:videoBuffer];
if (!(__bridge id)(cgimage)) { return; }
[images addObject:((__bridge id)(cgimage))];
CGImageRelease(cgimage);
- (void)mMoveDecoderOnDecoderFinished:(TransformVideo *)transformVideo
{
NSLog(@"视频解档完成");
// 得到媒体的资源
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:filePath] options:nil];
// 通过动画来播放我们的图片
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
// asset.duration.value/asset.duration.timescale 得到视频的真实时间
animation.duration = asset.duration.value/asset.duration.timescale;
animation.values = images;
animation.repeatCount = MAXFLOAT;
[self.preView.layer addAnimation:animation forKey:nil];
// 确保内存能及时释放掉
[images enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (obj) {
obj = nil;
}
}];
}
@end
AVAssetReader+AVAssetReaderTrackOutput播放视频的更多相关文章
- Android实现播放视频
转载:http://www.bdqn.cn/news/201311/12100.shtml 使用VideoView播放视频 VideoView,用于播放一段视频媒体,它继承了SurfaceView,位 ...
- Android使用TextureView播放视频
1.引言 如果你想显示一段在线视频或者任意的数据流比如视频或者OpenGL 场景,你可以用android中的TextureView做到. 1).TextureView的兄弟SurfaceView 应用 ...
- WPF播放视频
在现在的项目中需要使用到播放视频的功能,本来打算使用VLC来做的.后来发现WPF 4.0之后新增了MediaElement类,可以实现视频播放. <Grid> <Grid.RowDe ...
- Cocos2dx3.11.1Android播放视频,后台 黑屏,无法记忆播放bug修改
/* * Copyright (C) 2006 The Android Open Source Project * Copyright (c) 2014 Chukong Technologies In ...
- 不完全解决Android微信HTML5 播放视频的问题(不显示控制条,可交互)
首先你需要知道以下内容: http://ad.weixin.qq.com/learn/2-3-3--%E9%80%9A%E7%94%A8%E5%BA%93 这是微信为广告商开放的API,我一直认为只有 ...
- Bootstrap 3 模态框播放视频
Bootstrap 3 模态框播放视频 I'm trying to use the Modal feature from Bootstrap 3 to show my Youtube video. I ...
- android SurfaceView中播放视频 按视频的原始比例播放
OnPreparedListener mediaPlayerOnPreparedListener = new OnPreparedListener() { @Override public void ...
- Android WebView 总结 —— 使用HTML5播放视频及全屏方案
在APP开发的过程中,会碰到需要在WebView中播放视频的需求,下面讲解一下如何在WebView中使用html5播放视频. 1.让视频在各个Android版本能够正常播放 在AndroidManif ...
- Android三种播放视频的方式
在Android中,我们有三种方式来实现视频的播放: 1.使用其自带的播放器.指定Action为ACTION_VIEW,Data为Uri,Type为其MIME类型. 2.使用VideoView来播放. ...
随机推荐
- [译]Node.js Interview Questions and Answers (2017 Edition)
原文 Node.js Interview Questions for 2017 什么是error-first callback? 如何避免无止境的callback? 什么是Promises? 用什么工 ...
- 【IOS】自定义可点击的多文本跑马灯YFRollingLabel
需求 项目中需要用到跑马灯来仅展示一条消息,长度合适则不滚动,过长则循环滚动. 虽然不是我写的,但看了看代码,是在一个UIView里面放入两个UILabel, 在前一个快结束的时候,另一个显示.然而点 ...
- PHP数组常用函数
[官方]PHP Array 函数 一.数组操作的基本函数 数组的键名和值 array_values($arr); 获得数组的值 array_keys($arr); 获得数组的键名 array_fl ...
- Bookshop(一)数据库连接
连接池配置文件db.properties配置 1.新建一个普通文件->改名为db.properties(后缀)手动添加属性 一般为数据库驱动类.数据库连接地址.用户名.用户密码 driver=c ...
- 修改html页面文字选中样式
::selection { background-color: #31B0D5; color: #fff; text-shadow: 0 1px 0 rgba(0,0,0,.2); }
- ORACLE 11G EXPDP交互模式 interactive mode
expdp导出工具可以实现在导出任务执行过程中修改并行度.终止.监控任务功能. 做实验验证一下: [oracle@test dpdump]$ expdp system/password directo ...
- angularJS实践过程中出现的问题总结
同名服务 在一次项目里,之前是同事写的.我有一次在异步获取服务器上的数据时,习惯把api地址写在一个服务Store里,但是程序总是返回Store.api.get()里的get is undefined ...
- BZOJ 3160: 万径人踪灭
Description 一个ab串,问有多少回文子序列,字母和位置都对称,并且不连续. Sol FFT+Manacher. 不连续只需要减去连续的就可以了,连续的可以直接Manacher算出来. 其他 ...
- ubuntu 15.10 安装swift开发环境 2016/4/17
ubuntu 15.10 64位 下载地址 https://swift.org/download/#using-downloads 1.首先在ubuntu终端上 (ctl+alt+t打开) 下载cla ...
- 解决JSP页面获取的数据库数据乱码问题
将java项目部署到服务器,页面数据乱码: 解决:首先查看了数据库编码和jsp编码都是utf-8,说明jsp和数据库没问题,于是查看了tomcat设置的编码 没有设置编码,于是加了URIEncodin ...