【问题标题】:How to convert a kCVPixelFormatType_420YpCbCr8BiPlanarFullRange Buffer to YUV420 using libyuv library in ios?如何使用 ios 中的 libyuv 库将 kCVPixelFormatType_420YpCbCr8BiPlanarFullRange 缓冲区转换为 YUV420?
【发布时间】:2013-09-25 00:10:24
【问题描述】:

我已经使用 AVFoundation 捕获了视频。我已经设置(视频设置)并进入 outputsamplebuffer kCVPixelFormatType_420YpCbCr8BiPlanarFullRange 格式。但我需要 YUV420 格式进行进一步处理。

为此,我使用 libyuv 框架。

LIBYUV_API
int NV12ToI420(const uint8* src_y, int src_stride_y,
           const uint8* src_uv, int src_stride_uv,
           uint8* dst_y, int dst_stride_y,
           uint8* dst_u, int dst_stride_u,
           uint8* dst_v, int dst_stride_v,
           int width, int height);

 libyuv::NV12ToI420(src_yplane, inWidth ,
                   src_uvplane, inWidth,
                   dst_yplane, inWidth,
                   dst_vplane, inWidth / 2,
                   dst_uplane, inWidth / 2,
                   inWidth,  inHeight);

但是我得到的输出缓冲区是全绿色的?我在这个过程中犯了任何错误,请帮助我?

【问题讨论】:

  • 你的意思是kCVPixelFormatType_420YpCbCr8BiPlanarFullRange是一个格式名?亲爱的上帝...

标签: ios6 avfoundation libyuv


【解决方案1】:

看起来不错。确保您的 src_uvplane 指向 src_yplane + inWidth * inHeight

【讨论】:

    【解决方案2】:

    您需要将数据转换为 I420,我也在处理相机,但在 Android 上。我认为它应该在 iOS 上类似。 Android raw camera是NV21或者NV16格式,我从NV21或者NV16转换成YV12,I420和YV12差不多:

    BYTE m_y[BIG_VIDEO_CX * BIG_VIDEO_CY], 
         m_u[(BIG_VIDEO_CX/2) * (BIG_VIDEO_CY/2)],
         m_v[(BIG_VIDEO_CX/2) * (BIG_VIDEO_CY/2)];
    
        void NV21_TO_YV12(BYTE *data)
    {
        int width = BIG_VIDEO_CX;
        int height = BIG_VIDEO_CY;
            m_y2=data;
        data=&data[width*height];
        for (uint32_t i=0; i<(width/2)*(height/2); ++i)
        {
            m_v[i]=*data;
            m_u[i]=*(data+1);
            data+=2;
        }
    }
    void NV16_TO_YV12(BYTE *data)
    {
        int width = BIG_VIDEO_CX;
        int height = BIG_VIDEO_CY;
        m_y2=data;
        const BYTE* src_uv = (const BYTE*)&data[width*height];
        BYTE* dst_u = m_u;
        BYTE* dst_v = m_v;
        for (uint32_t y=0; y<height/2; ++y)
        {
            const BYTE* src_uv2 = src_uv + width;
            for (uint32_t x=0; x<width/2; ++x)
            {
                dst_u[x]=(src_uv[0]+src_uv2[0]+1)>>1;
                dst_v[x]=(src_uv[1]+src_uv2[1]+1)>>1;
                src_uv+=2;
                src_uv2+=2;
            }
            src_uv=src_uv2;
            dst_u+=width/2;
            dst_v+=width/2;
        }
    }
    

    【讨论】:

      【解决方案3】:

      Android 是 NV21,libyuv 支持 Arm 和 Intel。如果需要定向,它还可以作为转换的一部分旋转 90、180 或 270。 Arm优化版比C快2倍左右

      C
      NV12ToI420_Opt (782 毫秒)
      NV21ToI420_Opt (764 毫秒)

      Arm(霓虹灯优化)
      NV12ToI420_Opt (398 毫秒)
      NV21ToI420_Opt (381 毫秒)

      好奇你在 Android 上使用 NV16。我希望 NV61 与 NV21 保持一致。您的代码看起来是正确的,但可以使用 vrhadd.u8 很好地优化为 Neon。如果您想查看,请提交 libyuv 问题。 https://code.google.com/p/libyuv/issues/list

      【讨论】:

        【解决方案4】:

        这是我从 AVCaptureSession(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) 获得原始视频帧后,在 iOS 上的 captureOutput 中执行此操作的方法:

        - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection 
        {
        
            CVImageBufferRef videoFrame = CMSampleBufferGetImageBuffer(sampleBuffer);
        
            CFRetain(sampleBuffer);
        
            CVPixelBufferLockBaseAddress(videoFrame, 0);
            size_t _width = CVPixelBufferGetWidth(videoFrame);
            size_t _height = CVPixelBufferGetHeight(videoFrame);
        
            const uint8* plane1 = (uint8*)CVPixelBufferGetBaseAddressOfPlane(videoFrame,0);
            const uint8* plane2 = (uint8*)CVPixelBufferGetBaseAddressOfPlane(videoFrame,1);
            size_t plane1_stride = CVPixelBufferGetBytesPerRowOfPlane (videoFrame, 0);
            size_t plane2_stride = CVPixelBufferGetBytesPerRowOfPlane (videoFrame, 1);
        
            size_t plane1_size = plane1_stride * CVPixelBufferGetHeightOfPlane(videoFrame, 0);
            size_t plane2_size = CVPixelBufferGetBytesPerRowOfPlane (videoFrame, 1) * CVPixelBufferGetHeightOfPlane(videoFrame, 1);
        
            size_t frame_size = plane1_size + plane2_size;
        
            uint8* buffer = new uint8[ frame_size ];
            uint8* dst_u = buffer + plane1_size;
            uint8* dst_v = dst_u + plane1_size/4;
        
            // Let libyuv convert
            libyuv::NV12ToI420(/*const uint8* src_y=*/plane1, /*int src_stride_y=*/plane1_stride,
                        /*const uint8* src_uv=*/plane2, /*int src_stride_uv=*/plane2_stride,
                           /*uint8* dst_y=*/buffer, /*int dst_stride_y=*/plane1_stride,
                           /*uint8* dst_u=*/dst_u, /*int dst_stride_u=*/plane2_stride/2,
                           /*uint8* dst_v=*/dst_v, /*int dst_stride_v=*/plane2_stride/2,
                           _width, _height);
        
            CVPixelBufferUnlockBaseAddress(videoFrame, 0);
            CFRelease( sampleBuffer)
        
            // TODO: call your method here with 'buffer' variable. note that you need to deallocated the buffer after using it
          }
        

        为了清楚起见,我使代码更具描述性。

        【讨论】:

        • 不需要保留和释放sampleBuffer,使用后锁定和解锁
        • 我调整了代码,所以很明显缓冲区在使用后需要释放
        猜你喜欢
        • 2012-10-23
        • 1970-01-01
        • 1970-01-01
        • 2021-01-14
        • 1970-01-01
        • 2022-11-04
        • 2022-08-05
        • 2023-03-02
        • 2020-04-26
        相关资源
        最近更新 更多