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来播放. ...
随机推荐
- phpmyadmin #1045 - Access denied for user 'root'@'localhost' (using password: NO)
phpmyadmin访问遇到1045问题 #1045 - Access denied for user 'root'@'localhost' (using password: NO) 解决办法 找到p ...
- ECSHOP 商品字段增加新字段的方法
结合ecshop后台“商品编辑”.“商品录入”来谈谈如何给ecshop商品增加一个新字段,假设我们将这个新字段命名为 new_add 1.首先要修改数据表结构,给表 ecs_goods 增加新字段:n ...
- 关于TableView上有一段留白的解决方法
当cell的类型是plaint类型时 直接设置self.automaticallyAdjustsScrollViewInsets=NO; 还有要注意检查你自己设置的frame是否正确 当cel ...
- jQuery知识点二 实现隔行变色
<!DOCTYPE html> <html> <head> <meta name="viewport" content="wid ...
- MSSQL部分补丁的列表及下载地址(持续更新)
整理了MSSQL部分补丁的列表及下载地址(截至2016-11-18),供参考下. Edition Version Date Published Download Link SQL Server 201 ...
- 集中日志服务器Rsyslog
http://www.gaizaoren.org/archives/408 基于主机的管理一般需要收集服务器的日志信息用于及时发现错误,处理故障. 搭建linux下的集中日志服务器的程序一般可以用sy ...
- GetWord 3.3 屏幕取词
1. 缘起 要搞一个作弊软件,需要把屏幕上的试题取下来. 据说针对IE的取词很难,所以也就打消了自己开发的念头,找一找好用的控件. 发现了两个可以用的,一个是金山词霸的XdictGrb.dll文件,一 ...
- MVC中渲染页面
mvc中当返回的字符带有html代码的时候,可以直接使用@Html.Raw(Model.description)这句代码的意思就是返回不是html编码,因此用了这句代码就不需要单独再转换一次
- check用户协议
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- LUA中将未分类数据分为测试集和训练集
require 'torch' require 'image' local setting = {parent_root = '/home/pxu/image'} function list_chil ...