• iOS11 ReplayKit2 问题总结


    一、苹果自6月30日发布iOS11系统之后,其中的Airplay的协议发生变更,导致市场上的苹果直播助手(录屏)大部分变得不可用,因此在iOS11之后需要寻找新的技术方案来录屏

      1)采用系统提供的ReplayKit2 包含的System Screen Record的框架

      2) 采用libUSB的方案,这个方案利用的苹果的USB协议,github上面已经存在一个库,据说比较难编译,国外的直播平台Mobcrush,体验了一下效果非常好。

    二、采用苹果的提供的方案才是正途,不然以后每次升级去破解Airplay的协议太折腾,也不经济。下面总结遇到的一些难题

      1)ReplayKit2 本身存在bug,在不断更新的beta版本中,一直存在框架回调视频帧时序错位、声音消失,无法正常启动框架必须重启

      2)ReplayKit2 开发Xcode调试很难,每次启动调试,Xcode调试器默认挂起的是主App,如果你需要调试一个一启动就发生的问题,很可能进程直接结束了,调试器什么信息都没有,吐槽Xcode

         苹果这么大的市值,到底拿出多少钱用于研发测试,大概以大众为测试,这种态度一定会没落!!!

      3)ReplayKit2 直到正式版本中存在的问题,内存不能超过50MB,如果一超过,系统马上干掉你

      4)ReplayKit2 与主App之间没有进程通信机制(重大缺陷),直播平台一般主播都有自己的账号,直播权限,弹幕,礼物,苹果只考虑推流么??并且推的流还只能是竖屏,需要hack解决

    三、一些经验

      1)内存不能超过50MB

       在系统回调给你的YUV数据(NV12格式)中,这个回调在多个线程,之前为了避免时序的问题,将回调统一调度到一个串行队列中:

      

        if([_txLivePush isPublishing]){
    
            __weak typeof(self) wSelf = self;
            CFRetain(videoSample);
            dispatch_async(_encodeQueue, ^{
                [wSelf NV12ToI420AndRotate:videoSample];
    //            [_txLivePush sendVideoSampleBuffer:videoSample];
                CFRelease(videoSample);
            });
    

      这里带来一个问题,大屏手机在按home键的过程中,upload进程的线程调度受到影响,导致视频数据在队列中积压,内存峰值一旦超过50MB,系统立马把你杀掉

      所以在视频的数据流中,一定要注意缓冲区的长度,申请内存一般不要超过3MB,采用同步的方案更可控一些

      2)隐私模式

        隐私模式就是将系统给的数据替换成一张YUV图片,通常涉及给到的是PNG、JPG

        这里需要将JPG->UIImage->pix Data -> YUV I420

        下面是一些介绍:

        PG->UIImage->pix Data 

        

    + (unsigned char *)pixelARGBBytesFromImageRef:(CGImageRef)imageRef {
        
        NSUInteger iWidth  = CGImageGetWidth(imageRef);
        NSUInteger iHeight = CGImageGetHeight(imageRef);
        NSUInteger iBytesPerPixel = 4;
        NSUInteger iBytesPerRow = iBytesPerPixel * iWidth;
        NSUInteger iBitsPerComponent = 8;
        unsigned char *imageBytes = malloc(iWidth * iHeight * iBytesPerPixel);
        
        CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
        
        CGContextRef context = CGBitmapContextCreate(imageBytes,
                                                     iWidth,
                                                     iHeight,
                                                     iBitsPerComponent,
                                                     iBytesPerRow,
                                                     colorspace,
                                                     kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
        
        CGRect rect = CGRectMake(0 , 0 , iWidth , iHeight);
        CGContextDrawImage(context , rect ,imageRef);
        CGColorSpaceRelease(colorspace);
        CGContextRelease(context);
        CGImageRelease(imageRef);
        
        return imageBytes;
    }
    

        上面的方法将UIImage转成ARGB的格式,因为libyuv中有一个ARGBToI420的方法。上面的方法中注意选项

        kCGImageAlphaNone,               /* For example, RGB. */
        kCGImageAlphaPremultipliedLast,  /* For example, premultiplied RGBA */
        kCGImageAlphaPremultipliedFirst, /* For example, premultiplied ARGB */
        kCGImageAlphaLast,               /* For example, non-premultiplied RGBA */
        kCGImageAlphaFirst,              /* For example, non-premultiplied ARGB */
        kCGImageAlphaNoneSkipLast,       /* For example, RBGX. */
        kCGImageAlphaNoneSkipFirst,      /* For example, XRGB. */
        kCGImageAlphaOnly    
    

        控制颜色通道的顺序,数据的大小端

      转好的数据,再转成I420

      

      //ToI420
    CVReturn rc = CVPixelBufferCreate(NULL,
                                      imgSize.width,
                                      imgSize.height,
                                      kCVPixelFormatType_420YpCbCr8PlanarFullRange,
                                      NULL,
                                      &_pausePixBuffer);
    
    
    rc = CVPixelBufferLockBaseAddress(_pausePixBuffer, 0);
            
    uint8_t *y_copyBaseAddress = (uint8_t*)CVPixelBufferGetBaseAddressOfPlane(_pausePixBuffer, 0);
    uint8_t *u_copyBaseAddress = (uint8_t*)CVPixelBufferGetBaseAddressOfPlane(_pausePixBuffer, 1);
    uint8_t *v_copyBaseAddress = (uint8_t*)CVPixelBufferGetBaseAddressOfPlane(_pausePixBuffer, 2);
                
    size_t dYLineSize = (size_t)CVPixelBufferGetBytesPerRowOfPlane(_pausePixBuffer, 0);
    size_t dULineSize = (size_t)CVPixelBufferGetBytesPerRowOfPlane(_pausePixBuffer, 1);
    size_t dVLineSize = (size_t)CVPixelBufferGetBytesPerRowOfPlane(_pausePixBuffer, 2);
                
    tx_ARGBToI420(argbData,
                imgSize.width*4,
                y_copyBaseAddress,
                (int)dYLineSize,
                u_copyBaseAddress,
                (int)dULineSize,
                v_copyBaseAddress,
                (int)dVLineSize,
                (int)imgSize.width,
                (int)imgSize.height);
                
    free(argbData);
    

      

    kCVPixelFormatType_420YpCbCr8PlanarFullRange 代表I420的格式

        

  • 相关阅读:
    操作系统、存储介质以及电信行业单位换算差异
    Luogu P1659 [国家集训队]拉拉队排练
    AC自动机
    KMP
    Luogu P1470 最长前缀 Longest Prefix
    Luogu P2292 [HNOI2004]L语言
    Manacher算法
    字典(Trie)树
    逆序对
    vs
  • 原文地址:https://www.cnblogs.com/doudouyoutang/p/7574023.html
Copyright © 2020-2023  润新知