对于播放视频,大家应该一开始就想到比较方便快捷使用简单的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:];

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 > ) {
// 读取 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, );
// 得到 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, , bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
//根据这个位图 context 中的像素创建一个 Quartz image 对象
CGImageRef quartzImage = CGBitmapContextCreateImage(context);
// 解锁 pixel buffer
CVPixelBufferUnlockBaseAddress(imageBuffer, );
// 释放 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);

第五十八篇、iOS 微信聊天发送小视频的秘密的更多相关文章

  1. 第五十八篇:webpack的Source Map

    好家伙,Source Map没听过 1.什么是Source Map? 字面意义上来看应该是个好东西 Source Map 就是一个信息文件,里面储存着位置信息. 也就是说,Source Map 文件中 ...

  2. Python之路【第十八篇】:Web框架们

    Python之路[第十八篇]:Web框架们   Python的WEB框架 Bottle Bottle是一个快速.简洁.轻量级的基于WSIG的微型Web框架,此框架只由一个 .py 文件,除了Pytho ...

  3. 第八篇 :微信公众平台开发实战Java版之如何网页授权获取用户基本信息

    第一部分:微信授权获取基本信息的介绍 我们首先来看看官方的文档怎么说: 如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑. 关于网页授权回调域 ...

  4. Egret入门学习日记 --- 第十八篇(书中 8.5~8.7 节 内容)

    第十八篇(书中 8.5~8.7 节 内容) 其实语法篇,我感觉没必要写录入到日记里. 我也犹豫了好久,到底要不要录入. 这样,我先读一遍语法篇的所有内容,我觉得值得留下的,我就录入日记里. 不然像昨天 ...

  5. 《手把手教你》系列技巧篇(五十八)-java+ selenium自动化测试-分页测试(详细教程)

    1.简介 前几天,有人私信里留言问宏哥,分页怎么自动化测试了,完了给他说了说思路,不知道最后搞定没有,索性宏哥就写一篇文章来讲解和介绍如何处理分页. 2.测试场景 对分页来说,我们最感兴趣的和测试的无 ...

  6. Android UI开发第二十八篇——Fragment中使用左右滑动菜单

    Fragment实现了Android UI的分片管理,尤其在平板开发中,好处多多.这一篇将借助Android UI开发第二十六篇——Fragment间的通信. Android UI开发第二十七篇——实 ...

  7. (转)OpenFire源码学习之十八:IOS离线推送

    转:http://blog.csdn.net/huwenfeng_2011/article/details/43458213 IOS离线推送 场景: 如果您有iOS端的APP,在会话聊天的时候,用户登 ...

  8. iOS燃烧动画、3D视图框架、天气动画、立体相册、微信朋友圈小视频等源码

    iOS精选源码 iOS天气动画,包括太阳,云,雨,雷暴,雪动画. 较为美观的多级展开列表 3D立体相册,可以旋转的立方体 一个仪表盘Demo YGDashboardView 一个基于UIScrollV ...

  9. 【第十二篇】微信支付(APP)集成时碰到的问题(.net提示“无权限”、iOS跳转到微信支付页面中间只有一个“确定”按钮)(转)

    直入主题之前,请容我吐槽一下微*的官方东西:ASDFQ%#$%$#$%^FG@#$%DSFQ#$%.......:吐槽玩了!大家心照就好. 要完成手机APP跳转到微信的APP进行微信支付,需要进行如下 ...

随机推荐

  1. GitHub托管项目步骤

    1.打开Git Shell ,进入你要托管的项目目录里.然后输入git init ,该项目下就会多一个.git文件夹 2.点击add,然后再path里面输入你项目的,git文件夹目录地址.如下: 3. ...

  2. Sonatype Nexus 搭建Maven 私服

    国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html 内部邀请码:C8E245J (不写邀请码,没有现金送) 国 ...

  3. Java中单例设计模式总结

    两种单例常见的实现方式: 1:懒汉的设计模式,在第一次调用的时候才完成相关的初始化操作 懒汉式是典型的时间换空间,就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断的时间.当然,如果一直没有人 ...

  4. php7.0 redis扩展下载地址

    http://windows.php.net/downloads/pecl/snaps/redis/20160319/

  5. 细看InnoDB数据落盘 图解 MYSQL 专家hatemysql

    http://hatemysql.com/?p=503 1.  概述 前面很多大侠都分享过MySQL的InnoDB存储引擎将数据刷新的各种情况.我们这篇文章从InnoDB往下,看看数据从InnoDB的 ...

  6. PAT 1021

    1021. Deepest Root (25) A graph which is connected and acyclic can be considered a tree. The height ...

  7. Helpers\PHPMailer

    Helpers\PHPMailer PHPMailer is a third party class for sending emails, Full docs are available athtt ...

  8. MII、RMII、GMII接口的详细介绍

    转载:http://blog.csdn.net/reille/article/details/6312156 概述: MII (Media Independent Interface(介质无关接口)或 ...

  9. XGPush集成(信鸽集成)

    #import "AppDelegate.h" #import "XGPush.h" #import "XGSetting.h" #defi ...

  10. Java SE ---数据类型

    Java中数据类型(Data Type)分为基本数据类型(Primitive Data Type)和引用类型(Reference Data Type). Java中的基本数据类型共有以下8种:   1 ...