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的格式

    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM