简体   繁体   中英

how to convert pixelBuffer from BGRA to YUV

i want to convert pixelBuffer from BGRA to YUV(420V).

Using the convert function, most of the videos in my mobile phone photo albums are running normally, Execpt the one video from my colleagues, after converted the pixels are insanity,

the video from my colleagues is quite normal,

Video
ID                                       : 1
Format                                   : AVC
Format/Info                              : Advanced Video Codec
Format profile                           : Main@L3.1
Format settings                          : CABAC / 1 Ref Frames
Format settings, CABAC                   : Yes
Format settings, Reference frames        : 1 frame
Format settings, GOP                     : M=1, N=15
Codec ID                                 : avc1
Codec ID/Info                            : Advanced Video Coding
Duration                                 : 6 s 623 ms
Source duration                          : 6 s 997 ms
Bit rate                                 : 4 662 kb/s
Width                                    : 884 pixels
Clean aperture width                     : 884 pixels
Height                                   : 492 pixels
Clean aperture height                    : 492 pixels
Display aspect ratio                     : 16:9
Original display aspect ratio            : 16:9
Frame rate mode                          : Variable
Frame rate                               : 57.742 FPS
Minimum frame rate                       : 20.000 FPS
Maximum frame rate                       : 100.000 FPS
Color space                              : YUV
Chroma subsampling                       : 4:2:0
Bit depth                                : 8 bits
Scan type                                : Progressive
Bits/(Pixel*Frame)                       : 0.186
Stream size                              : 3.67 MiB (94%)
Source stream size                       : 3.79 MiB (97%)
Title                                    : Core Media Video
Encoded date                             : UTC 2021-10-29 09:54:03
Tagged date                              : UTC 2021-10-29 09:54:03
Color range                              : Limited
Color primaries                          : Display P3
Transfer characteristics                 : BT.709
Matrix coefficients                      : BT.709
Codec configuration box                  : avcC

this is my function, i do not know what is wrong.


CFDictionaryRef CreateCFDictionary(CFTypeRef* keys, CFTypeRef* values, size_t size) {
      return CFDictionaryCreate(kCFAllocatorDefault,
                                keys,
                                values,
                                size,
                                &kCFTypeDictionaryKeyCallBacks,
                                &kCFTypeDictionaryValueCallBacks);
    }

static void bt709_rgb2yuv8bit_TV(uint8_t R, uint8_t G, uint8_t B, uint8_t &Y, uint8_t &U, uint8_t &V)
    {
        Y =  0.183 * R + 0.614 * G + 0.062 * B + 16;
        U = -0.101 * R - 0.339 * G + 0.439 * B + 128;
        V =  0.439 * R - 0.399 * G - 0.040 * B + 128;
    }

CVPixelBufferRef RGB2YCbCr8Bit(CVPixelBufferRef pixelBuffer)
    {
        CVPixelBufferLockBaseAddress(pixelBuffer, 0);
        uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddress(pixelBuffer);
        int w = (int) CVPixelBufferGetWidth(pixelBuffer);
        int h = (int) CVPixelBufferGetHeight(pixelBuffer);
//        int stride = (int) CVPixelBufferGetBytesPerRow(pixelBuffer) / 4;

        OSType pixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;

        CVPixelBufferRef pixelBufferCopy = NULL;
        const size_t attributes_size = 1;
        CFTypeRef keys[attributes_size] = {
            kCVPixelBufferIOSurfacePropertiesKey,
        };
        CFDictionaryRef io_surface_value = CreateCFDictionary(nullptr, nullptr, 0);
        CFTypeRef values[attributes_size] = {io_surface_value};
        
        CFDictionaryRef attributes = CreateCFDictionary(keys, values, attributes_size);
        CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault,
                                              w,
                                              h,
                                              pixelFormat,
                                              attributes,
                                              &pixelBufferCopy);
        if (status != kCVReturnSuccess) {
            std::cout << "YUVBufferCopyWithPixelBuffer :: failed" << std::endl;
            return nullptr;
        }
        if (attributes) {
            CFRelease(attributes);
            attributes = nullptr;
        }
        
        CVPixelBufferLockBaseAddress(pixelBufferCopy, 0);

        size_t y_stride = CVPixelBufferGetBytesPerRowOfPlane(pixelBufferCopy, 0);
        size_t uv_stride  = CVPixelBufferGetBytesPerRowOfPlane(pixelBufferCopy, 1);
        
        int plane_h1 = (int) CVPixelBufferGetHeightOfPlane(pixelBufferCopy, 0);
        int plane_h2 = (int) CVPixelBufferGetHeightOfPlane(pixelBufferCopy, 1);
        
        uint8_t *y = (uint8_t *) CVPixelBufferGetBaseAddressOfPlane(pixelBufferCopy, 0);
        memset(y, 0x80, plane_h1 * y_stride);
        
        uint8_t *uv = (uint8_t *) CVPixelBufferGetBaseAddressOfPlane(pixelBufferCopy, 1);
        memset(uv, 0x80, plane_h2 * uv_stride);
        
        int y_bufferSize = w * h;
        int uv_bufferSize = w * h / 4;
        uint8_t *y_planeData = (uint8_t *) malloc(y_bufferSize * sizeof(uint8_t));
        uint8_t *u_planeData = (uint8_t *) malloc(uv_bufferSize * sizeof(uint8_t));
        uint8_t *v_planeData = (uint8_t *) malloc(uv_bufferSize * sizeof(uint8_t));
            
        int u_offset = 0;
        int v_offset = 0;
        uint8_t R, G, B;
        uint8_t Y, U, V;
        
        for (int i = 0; i < h; i ++) {
            for (int j = 0; j < w; j ++) {
                int offset = i * w + j;
                B = baseAddress[offset * 4];
                G = baseAddress[offset * 4 + 1];
                R = baseAddress[offset * 4 + 2];
                bt709_rgb2yuv8bit_TV(R, G, B, Y, U, V);
                y_planeData[offset] = Y;
                //隔行扫描 偶数行的偶数列取U 奇数行的偶数列取V
                if (j % 2 == 0) {
                    (i % 2 == 0) ? u_planeData[u_offset++] = U : v_planeData[v_offset++] = V;
                }
            }
        }
        
        for (int i = 0; i < plane_h1; i ++) {
            memcpy(y + i * y_stride, y_planeData + i * w, w);
            if (i < plane_h2) {
                for (int j = 0 ; j < w ; j+=2) {
                    //NV12 和 NV21 格式都属于 YUV420SP 类型。它也是先存储了 Y 分量,但接下来并不是再存储所有的 U 或者 V 分量,而是把 UV 分量交替连续存储。
                    //NV12 是 IOS 中有的模式,它的存储顺序是先存 Y 分量,再 UV 进行交替存储。
                    memcpy(uv + i * y_stride + j, u_planeData + i * w/2 + j/2, 1);
                    memcpy(uv + i * y_stride + j + 1, v_planeData + i * w/2 + j/2, 1);
                }
            }
        }
        free(y_planeData);
        free(u_planeData);
        free(v_planeData);
        
        CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
        CVPixelBufferUnlockBaseAddress(pixelBufferCopy, 0);
        return pixelBufferCopy;
    }

pixelBuffer BGRA is normal

pixelBuffer YUV insanity

  1. In the video metadata there is a line Color space: YUV It looks like that this video isn't BGRA

  2. When you calculate source pixel you must use stride (length of image row in bytes) instead of width.

    int offset = i * stride + j;

You already has it commented at the beginning of function:

int stride = (int) CVPixelBufferGetBytesPerRow(pixelBuffer) / 4;
  1. It is better to use builtin functions for converting images. Here is an example from one of my projects:
    vImage_CGImageFormat out_cg_format = CreateVImage_CGImageFormat( target_pixel_format );
    CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
    vImageCVImageFormatRef in_cv_format = vImageCVImageFormat_Create(
                 MSPixFmt_to_CVPixelFormatType(source_pixel_format),
                 kvImage_ARGBToYpCbCrMatrix_ITU_R_601_4,
                 kCVImageBufferChromaLocation_Center,
                 color_space,
                 0 );
    CGColorSpaceRelease(color_space);
    CGColorSpaceRelease(out_cg_format.colorSpace);

    vImage_Error err = kvImageNoError;                
    vImageConverterRef converter = vImageConverter_CreateForCVToCGImageFormat(in_cv_format, &out_cg_format, NULL, kvImagePrintDiagnosticsToConsole, &err);

    vImage_Buffer src_planes[4] = {{0}};
    vImage_Buffer dst_planes[4] = {{0}};
                
    unsigned long source_plane_count = vImageConverter_GetNumberOfSourceBuffers(converter);
    for( unsigned int i = 0; i < source_plane_count; i++ )
    {
        src_planes[i] = (vImage_Buffer){planes_in[i], pic_size.height, pic_size.width, strides_in[i]};
    }
                    
    unsigned long target_plane_count = vImageConverter_GetNumberOfDestinationBuffers(converter);
    for( unsigned int i = 0; i < target_plane_count; i++ )
    {
        dst_planes[i] = (vImage_Buffer){planes_out[i], pic_size.height, pic_size.width, strides_out[i]};
    }

    err = vImageConvert_AnyToAny(converter, src_planes, dst_planes, NULL,  kvImagePrintDiagnosticsToConsole);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM