美文网首页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