美文网首页IOS个人开发常用技术收集AVPlayer
IOS 微信聊天发送小视频的秘密(AVAssetReader+A

IOS 微信聊天发送小视频的秘密(AVAssetReader+A

作者: Abson在简书 | 来源:发表于2015-11-28 01:21 被阅读13247次

对于播放视频,大家应该一开始就想到比较方便快捷使用简单的MPMoviePlayerController类,确实用这个苹果官方为我们包装好了的 API 确实有很多事情都不用我们烦心,我们可以很快的做出一个视频播放器,但是很遗憾,高度封装的东西,就证明了可自定义性越受限制,而MPMoviePlayerController却正正证明了这一点。所以大家又相对的想起了AVPlayer,是的,AVPlayer是一个很好的自定义播放器,但是,AVPlayer却有着性能限制,微信团队也证实这一点,AVPlayer只能同事播放16个视频,之后创建一个视频,对可滚动的聊天界面来说,是一个非常致命的性能限制了。

AVAssetReader+AVAssetReaderTrackOutput

那么既然AVPlayer有着性能限制,我们就做一个属于我们的播放器吧,AVAssetReader 可以从原始数据里获取解码后的音视频数据。结合AVAssetReaderTrackOutput ,能读取一帧帧的CMSampleBufferRefCMSampleBufferRef 可以转化成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

之前写了的文章大家反应也算不错,这就是让我坚持写作的一方面,我们尽量让大家都能看完我的文章后都有所收获,
如果你遇到文章上有某些位置不太明白的,可以私信我,如果想看源码的朋友,也可以私信我,
我会尽我所能帮大家解决问题的。

心如止水,奋力前行

相关文章

网友评论

  • 半碗大米汤:为什么要 demo 的需求量这么大,这玩意不能播放网络视频,只能本地,那用它来干啥?
  • 4eeb8b9aa518:大神,求demo学习一下,1584916804@qq.com,谢谢
  • 79e21aa33746:求demo 1215401534@qq.com 现在急需做这个功能。万分感谢
  • 2bc7cb8d69d6:播放音频的代码有吗?谢谢了
  • 陆辞书:这样的视频文件播放应该没有声音吧?
  • 追逐葡萄的蜗牛:建议把demo放在GitHub上。
  • 风尘子:大神,求demo学习一下,466817968@qq.com,谢谢大神
  • 晓晓198:给力,求Demo 412965691@qq.com 感激不尽
  • fa91b0267bf8:同求demo,504644862@qq.com,谢啦:balloon:
  • JackJin:AVPlayer 只能播放播放16 视频,后面添加就会出现性能问题?有相关的资料地址吗?
  • 30f49ce9e81a:为何只能无声播放呢?是否可以用AVAssetReaderTrackOutput将音频数据读出来,播放音频+视频呢?
  • L_Glenn:我自己又搜了一下,发现会宽和高为0的时候,会爆error,然后在捕捉视频帧的时候加入了if (width==0 && height==0) {
    return nil;
    }
    但是,发现内存还是会爆,我的15秒视频,模拟器内存直接到3.51G,真机闪退。
    发现是捕捉视频帧率那个方法有某个地方内存不会释放,具体我也太懂。。。忘楼主有时间的话,看一下。
  • ed5adb533762:大神求demo 邮箱huisyi123@163.com 能讲的这么通透挺厉害的
  • 格调main:[images addObject:((__bridge id)(cgimage))]; 这里全放到数组中内存就爆了
    L_Glenn:@Vasili_biubiu 不是这个方法的问题,是那个捕捉视频帧的那个方法。先涨的内存, 然后放进数组里的。
  • 格调main:求一份demo 247023187@qq.com
  • 我的月亮你的心:兄弟啊!这么多人找你要demo,怎么就不分享出来了!😓😓
  • b1b123389a58:一直报错,请问下面两个方法该如何实现?

    [self.delegate mMoveDecoder:self onNewVideoFrameReady:videoBuffer];

    7.通过代理告诉上层解码结束

    [self.delegate mMoveDecoderOnDecoderFinished:self];

    多谢
  • 左手边是幸福:你好,有git地址吗?求一份代码260497176@qq.com
  • 明七夜:大哥,给一份源码吧..谢了 408110617@qq.com
  • 杰杰_94058:内存爆了
  • RunLoop:发一份源码demo学习下,谢谢,1154551505@qq.com
  • 6b8dcd6e43cb:最近有一个需求需要用到这个,自己写的内存一下就爆了!发一份Demo吧谢谢!! 540186555@qq.com
  • 1a416da6092e:请问一下,有源码吗? 可不可以发一份Demo, 260341329@qq.com, 谢谢
  • 2961adb20e72:求demo 谢谢大神 1443473742@qq.com
  • cd1fcb172f50:最近在研究短视频,阅读了您的文章,其中还有一些疑惑,您否查看一下Demo源码 :pray: (2959077377@qq.com)
  • b7803b4cd2c6:求 demo xref_lee@yeah.net 谢谢
  • 312b81756cd2:最近项目中也要向微信那样的小视频,求个demo, 381575916@qq.com
  • 5fe3ee03c48b:博主,给源码看看吧。635873117@qq.com 谢谢
  • 313d06b03f46:大神 我最近项目中的需求跟这个一样 求demo 570547651@qq.com 谢谢
  • 纳兰怮翌:大神你好,最近项目需求,但是视频一出来内存就暴涨,求demo一份 707431492@qq.com谢谢
  • Ylang:超过16个视频进行,就开始失败了
  • 0ae6e2d01b8b:写的很好,能分享下demo吗; 944120988@qq.com, 谢谢。
  • bea626d6f261:有demo吗,有的话,可不可以发一份啊 403288207@163.com
  • d42f566c3f09:求demo 648981069@qq.com
  • f6fdade5d418:@葱神大大 + (CGImageRef) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer rotation:(UIImageOrientation)orientation orientation = UIImageOrientationRight ,请问怎么旋转成UIImageOrientationUp 。有好的解决方案吗?
    f6fdade5d418:+ (UIImage *)decodedImageWithImage:(UIImage *)image是说的这段么,,,
    Abson在简书:相信也用过webimage了吧,我就这么说不清楚,可以去参考一下他旋转图片的那段源码
  • 云之君兮鹏:大神赐一份代码吧630239746@qq.com
  • 5f48ff8b703c:kCVPixelFormatType_32BGRA 给出宏定义代码?
    Abson在简书:@未来的瞎想 avfoundation 定义的宏。
  • 5f48ff8b703c:我现在项目中用的AVPlayer播放小视频,发现滑动界面的时候卡顿,怎么解决?还是使用AVAssetReader+AVAssetReaderTrackOutput才能解决问题,如果可以解决求大神分享下demo,多谢了,jazzforlove@163.com,顶你大神!
  • 313d06b03f46:请问一下,有源码吗? 可不可以发一份Demo, 570547651@qq.com, 谢谢!
  • 摘心: 您好,我的项目需要cell上多个视频播放的功能,可以发我一份demo吗?谢谢!1067221279@qq.com
  • akali:楼主 ,怎么用avassetReader 获取音轨 数据呢
  • LJ的ios开发:为什么我的视频显示,旋转了90°啊
    f6fdade5d418:@LJ的ios开发 同问,如果旋转了90度怎么还原。
  • shuolol:求demo 6603418@qq.com
  • 62999a2ff5c5:769159094@qq.com 请发一份demo行不,谢谢
  • 619e7a8026e3:求Demo 335928444@qq.com
  • 雾霾大哥:跪求demo 843968901@qq.com 好人一生平安
  • 81c682f8bf82:请问有源码地址吗?可不可以发一份Demo详细, 18513959114@163.com,写写拉
  • bdacd2233ffd:求demo, zengweijianjian@foxmail.com 不胜感激!
  • 1ec8e2bf5c1e:楼主再发一个多个cell上同时展示小视频的优化方案文章呗 :grin:
  • 丿丨丨:大神,可以求一份demo吗?935148040@qq.com
  • SAW_:楼主,求demo,正在处理这块的东西 lbh_hqhc@126.com
  • 原味豆浆:请问一下,有源码吗? 可不可以发一份Demo, 495419587@qq.com, 谢谢!
  • _Zero丶:大神求Demo 478027478@qq.com 谢谢了
  • dbdc058588c3:你好,源码能发我一下吗 ,谢谢了。 289704828@qq.com
  • 丝滑:能否发一份源码 Demo 935343062@qq.com ,谢谢了
  • akali:求源码 18210809272@163.com
  • 3600965b6888:楼主,发个Demo呗,1433636131@qq.com
  • 3600965b6888:求Demo,1433636131@qq.com
  • 阿饼了:你好.求一下Demo. 515142007@qq.com 谢谢
  • 7anOS:楼主能否给个源码看看
  • 7anOS:楼主,有空的时候发份源码吧,fanmaoyu0871@163.com,先谢谢!
  • c0cd4988f6cd:求 demo 710155453@qq.com 谢谢啦
  • 0e1492b6868c:求demo 3q 2572018810@qq.com
  • NicWhite:大神,用这个写的播放视频的播放出来没有声音是怎么回事呢?应该怎么弄?希望能用这个写一个详细的,能控制播放进度和声音的!
  • 7f44002b1bfa:随便加载一个小视频就吃了1个G内存。。
  • 37e1832c47d6:大神,看完受益非浅,可否发源码参考下,meichao.xiong@163.com,感激不尽
  • Metoo33:求个demo 1192157487@qq.com 感谢叻。。大神
  • 7d8f3f04748b:大侠,麻烦发下demo研究研究,谢谢!645884848@qq.com
  • 再见远洋:这么多人要Demo 大神就发个github呗
    前行哲:@再见远洋 赞
  • 94471a4b6d76:楼主,麻烦发下demo借鉴下,谢谢,1030625100@qq.com
    披萨配可乐:楼主,麻烦发一下demo参考学习,谢谢,xuyang767660732@163.com
    0e1492b6868c:@Mozzie2016 楼上收到回复了么?


  • 6e7045356543:请问一下,有源码吗? 可不可以发一份Demo, 1065991630@qq.com, 谢谢!
  • 55e00fb571fb:请问一下,有源码吗? 可不可以发一份Demo, 200894542@qq.com, 谢谢!
  • a254b13fa944:大神您好,最近项目用到小视频,看了这个很久没搞懂,可不可以发个demo给我看看,万分感谢!我的邮箱是:jason@laowo.me
  • 大兵布莱恩特:求demo 1060545231@163.com 谢谢了。
    CveniEs:楼上有收到demo嘛, 求发一份..谢啦
    冬的天:你好,有收到这个demo吗?可以给我发一份吗
  • 0fc5cca8c816:效果是实现了,但是放到tableView该怎么处理?
  • bc8f36347bc3:请问一下,有源码吗? 可不可以发一份Demo, 925462931@qq.com, 谢谢!
  • cd95e197cff6:已经打赏了。能发个demo 嘛 leoxyw@163.com 谢谢
  • cd95e197cff6:求demo leoxyw@163.com 谢谢。 另外这里有用私有接口吗?不影响上架吧
  • d6e71759189e:求demo 392411822@qq.com 谢谢!
  • Mitchell:这几个私有方法能上架么?
  • a1e798f2e80f:现在要做像微信的朋友那样的小视频,但是按说的去弄,在视频所在的cell出现的时候会卡顿,而且不知道为什么trans方法只能执行一次不然调试手机会直接和xcode失去连接,希望有demo,刚刚支持你了~邮箱:lightair03@qq.com
    e823645e589d:是因为内存太高被系统杀死了
  • NS西北风:求demo
  • 790f084c451a:很赞的分享,那么请问一下,这里只处理图片,但声音貌似没办法像图片一样处理吧?
  • 78d66f6f469d:请问一下,有源码吗? 可不可以发一份Demo, 641529071@qq.com, 谢谢!
  • 6b9fdd26197a:请问一下,有源码吗? 可不可以发一份Demo, 1290374862@qq.com, 谢谢!
  • ismilesky:请问一下,有源码吗? 可不可以发一份Demo, 923654855@qq.com, 谢谢!
  • 于是有了涛:写的非常好,我公司最近也要做一个类似的功能,请问大神能否发一份demo,学习学习,谢谢了.453745143@qq.com
  • 5ac8909f59a7:求demo 415715165@qq.com 谢谢
  • 一个正直的小龙猫:你好.求一下Demo. 13261769078@163.com 谢谢
  • LB_Zhang:大神,求demo ,libohot5991@163.com
  • fac5245be7ab:试了一下,单个短视频还可以。但是要象微信那样多个视频同时显示的时侯,内存就爆了。不知道,是我写的问题,还是其它原因。
    求看一下demo yss163com@163.com
    一只特立独行的道哥:也遇到了这个问题,很容易爆掉内存
    dc6fbcbb4b19:的确这样,像微信那样多个视频同时显示的时侯,内存爆了,有没有解决方案
    snackbaby:@yss163com 这个问题有解决办法吗,感觉这种方法没法避免呀
  • e6a4db1a9746:求demo 496288307@qq.com
  • 81d5266cd1df:求Demo 540483352@qq.com
  • HHHHysteria:看完博主写的内容后感觉受益匪浅. 可否发下源码看一下? zxj_ios@163.com
  • 寒桥:求Demo hanqiao6666@163.com:blush::blush:
  • 孙玮超:大神 我最近项目中的需求跟这个一样 求demo sunweichao1024@163.com 谢谢
  • 张周择:求demo; pdzhang163@163.com, 谢谢。
    歪冒:求demo;13212746266@163.com
  • 张周择:[videoReaderOutput copyNextSampleBuffer], 返回空是什么原因呀,开发是遇到了么?
    Abson在简书:@pd 可以看看 copyNextSampleBuffer 这个方法的介绍,它不保证每次一返回的 Buffer 都是有效的

本文标题:IOS 微信聊天发送小视频的秘密(AVAssetReader+A

本文链接:https://www.haomeiwen.com/subject/ggidhttx.html