从ffmpeg的AVFrame得到iOS的CVPixelBuffer

个人在之前的一篇文章《在iOS端使用AVSampleBufferDisplayLayer进行视频渲染》中提到,可以使用iOS8.0新出的AVSampleBufferDisplayLayer进行视频的渲染,那么如果这个时候解码使用的是ffmpeg,解码后得到的是AVFrame,就需要把AVFrame转成CVPixelbuffer在送给AVSampleBufferDisplayLayer渲染。

如何进行转化呢?如下:

- (void)dispatchAVFrame:(AVFrame*) frame{
    if(!frame || !frame->data[0]){
        return;
    }

    CVReturn theError;
    if (!self.pixelBufferPool){
        NSMutableDictionary* attributes = [NSMutableDictionary dictionary];
        [attributes setObject:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange] forKey:(NSString*)kCVPixelBufferPixelFormatTypeKey];
        [attributes setObject:[NSNumber numberWithInt:frame->width] forKey: (NSString*)kCVPixelBufferWidthKey];
        [attributes setObject:[NSNumber numberWithInt:frame->height] forKey: (NSString*)kCVPixelBufferHeightKey];
        [attributes setObject:@(frame->linesize[0]) forKey:(NSString*)kCVPixelBufferBytesPerRowAlignmentKey];
        [attributes setObject:[NSDictionary dictionary] forKey:(NSString*)kCVPixelBufferIOSurfacePropertiesKey];
        theError = CVPixelBufferPoolCreate(kCFAllocatorDefault, NULL, (__bridge CFDictionaryRef) attributes, &_pixelBufferPool);
        if (theError != kCVReturnSuccess){
            NSLog(@"CVPixelBufferPoolCreate Failed");
        }
    }
    
    CVPixelBufferRef pixelBuffer = nil;
    theError = CVPixelBufferPoolCreatePixelBuffer(NULL, self.pixelBufferPool, &pixelBuffer);
    if(theError != kCVReturnSuccess){
        NSLog(@"CVPixelBufferPoolCreatePixelBuffer Failed");
    }
    
    CVPixelBufferLockBaseAddress(pixelBuffer, 0);
    size_t bytePerRowY = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
    size_t bytesPerRowUV = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1);
    void* base = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
    memcpy(base, frame->data[0], bytePerRowY * frame->height);
    base = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);
    memcpy(base, frame->data[1], bytesPerRowUV * frame->height/2);
    CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
    
    [self dispatchPixelBuffer:pixelBuffer];
}
这里的前提是AVFrame中yuv的格式是nv12;但如果AVFrame是yuv420p,就需要把frame->data[1]和frame->data[2]的每一个字节交叉存储到pixelBUffer的plane1上,即把原来的uuuu和vvvv,保存成uvuvuvuv,如下:

        uint32_t size = frame->linesize[1] * frame->height;
        uint8_t* dstData = new uint8_t[2 * size];
        for (int i = 0; i < 2 * size; i++){
            if (i % 2 == 0){
                dstData[i] = frame->data[1][i/2];
            }else {
                dstData[i] = frame->data[2][i/2];
            }
        }
这样,只要把dstData中的内容拷贝到 pixelBUffer的plane1上即可。

这里使用CVPixelBufferPool的原因是,如果每次都从一个AVFrame去构造一个新的CVPixelbuffer将会非常占用cpu,也比较耗时。


  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
AVFrameFFmpeg中的一个结构体,用于表示视频帧的数据。它包含了视频帧的像素数据、宽度、高度、格式等信息。在使用AVFrame之前,需要先分配内存,并设置其各个字段的值。可以使用av_frame_alloc()函数来分配AVFrame实例,并使用av_frame_get_buffer()或av_image_alloc()函数来分配像素数据的内存。其中av_frame_get_buffer()函数会自动选择最优的对齐方式来分配内存,而av_image_alloc()函数可以指定像素数据的对齐方式。通过设置AVFrame的width、height和format字段,可以指定视频帧的宽度、高度和像素格式。总之,AVFrame是用于存储和处理视频帧数据的关键结构体。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [FFmpeg数据结构AVFrame](https://blog.csdn.net/irainsa/article/details/128977176)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [FFMPEG关键结构体——AVFrame](https://blog.csdn.net/PPPPPPPKD/article/details/125586279)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值