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来播放. ...
随机推荐
- hibernate中java类的成员变量类型如何映射到SQL中的数据类型变化
hibernate映射文件??.hbm.xml配置映射元素详解--Hibernate映射类型 在从Hibernate的java的成员类型映射到SQL中的数据类型,其内映射方式它满足,SQL可以自己调制 ...
- FORM
一 .新增的input输入属性 1.email类型 在表单提交E-mail地址时,无效的输入会生成很多无效数据,对后期的数据检索造成一定的影响.所以在表单提交之前,需要对输入的E-mail地址进行有效 ...
- mysql二级索引
以InnoDB来说,每个InnoDB表具有一个特殊的索引称为聚集索引.如果您的表上定义有主键,该主键索引是聚集索引.如果你不定义为您的表的主键 时,MySQL取第一个唯一索引(unique)而且只含非 ...
- leetcode笔记
82. Remove Duplicates from Sorted List II https://leetcode.com/problems/remove-duplicates-from-sorte ...
- 遍历Map的方法
1.使用 entrySet() entrySet() --> 官方推荐将Map转换成Map.Entry对象的Set集合 Set entrys = map.entrySet(); Iterat ...
- XP安装IIS来加载aspx页面(Web调用SAP数据)
1,安装IIS 在XP中安装IIS方法很简单,安装时需要提供安装光盘来加载I386文件,可以使用虚拟光驱或光盘.在此做个简单说明(控制面板-添加/删除 Windows组件-勾选Internet信息服务 ...
- PJAX的实现与应用
一.前言 web发展经历了一个漫长的周期,最开始很多人认为Javascript这们语言是前端开发的累赘,是个鸡肋,那个时候人们还享受着从一个a链接蹦 到另一个页面的web神奇魔术.后来随着JavaSc ...
- window.event.srcElement与window.event.target 触发事件的元素
IE浏览器支持window.event.srcElement , 而firefox支持window.event.target:<input type="text" onblu ...
- javac -encoding utf8 in linux
由于另外负责编码的同事用的是utf-8,我用的默认的编码格式gbk,在提交代码时,为了迁就他,我打算把格式用工具转成utf-8. 转化成果后,然后在make一下,发现javac -encoding u ...
- FFT NNT
算算劳资已经多久没学新算法了,又要重新开始学辣.直接扔板子,跑...话说FFT算法导论里讲的真不错,去看下就懂了. //FFT#include <cstdio> #include < ...