Thursday, October 8, 2026
HomeiOS Developmentios - Convert YUV information to CVPixelBufferRef and play in AVSampleBufferDisplayLayer

ios – Convert YUV information to CVPixelBufferRef and play in AVSampleBufferDisplayLayer

[ad_1]

I am having a stream of video in IYUV (4:2:0) format and making an attempt to transform it into CVPixelBufferRef after which into CMSampleBufferRef and play it in AVSampleBufferDisplayLayer (AVPictureInPictureController required). I’ve tried a number of model of answer, however none really works effectively, hope somebody with video processing expertise can inform what I’ve finished unsuitable right here.

Full perform:

- (CMSampleBufferRef)makeSampleBufferFromTexturesWithY:(void *)yPtr U:(void *)uPtr V:(void *)vPtr yStride:(int)yStride uStride:(int)uStride vStride:(int)vStride width:(int)width top:(int)top doMirror:(BOOL)doMirror doMirrorVertical:(BOOL)doMirrorVertical
{
    NSDictionary *pixelAttributes = @{(NSString *)kCVPixelBufferIOSurfacePropertiesKey:@{}}; // For 1,2,3
    
    CVPixelBufferRef pixelBuffer = NULL;
    CVReturn outcome;
    outcome = CVPixelBufferCreate(kCFAllocatorDefault,
                                 width,
                                 top,
                                 kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange // For 1,2,3
//                                 kCVPixelFormatType_32BGRA, // For 4.
                                 (__bridge CFDictionaryRef)(pixelAttributes),
                                 &pixelBuffer);
    if (outcome != kCVReturnSuccess) {
        NSLog(@"PIP: Unable to create cvpixelbuffer %d", outcome);
        return nil;
    }

/// Converter code under...

    CMFormatDescriptionRef formatDesc;
    outcome = CMVideoFormatDescriptionCreateForImageBuffer(kCFAllocatorDefault, pixelBuffer, &formatDesc);
    if (outcome != kCVReturnSuccess) {
        NSAssert(NO, @"PIP: Did not create CMFormatDescription: %d", outcome);
        CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
        return nil;
    }
    
    CMTime now = CMTimeMakeWithSeconds(CACurrentMediaTime(), 1000);
    CMSampleTimingInfo timingInfo;
    timingInfo.period = CMTimeMakeWithSeconds(1, 1000);
    timingInfo.presentationTimeStamp = now;
    timingInfo.decodeTimeStamp = now;
    
    @strive {
        if (@accessible(iOS 13.0, *)) {
            CMSampleBufferRef sampleBuffer;
            CMSampleBufferCreateReadyWithImageBuffer(kCFAllocatorDefault, pixelBuffer, formatDesc, &timingInfo, &sampleBuffer);
//            CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
            CVPixelBufferRelease(pixelBuffer);
            pixelBuffer = nil;
//            free(dest.information);
//            free(uvPlane);
            
            return sampleBuffer;
        } else {
            CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
            return nil;
        }
    } @catch (NSException *exception) {
        NSAssert(NO, @"PIP: Did not create CVSampleBuffer: %@", exception);
        CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
        return nil;
    }
}

Here is some options that I discovered:

  1. Mix UV, however half backside is inexperienced.
    CVPixelBufferLockBaseAddress(pixelBuffer, 0);
    uint8_t *yDestPlane = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
    memcpy(yDestPlane, yPtr, width * top);

    CGFloat uPlaneSize =  width * top / 4;
    CGFloat vPlaneSize = width * top / 4;
    CGFloat numberOfElementsForChroma = uPlaneSize + vPlaneSize;
    // for simplicity and pace create a mixed UV panel to carry the pixels
    uint8_t *uvPlane = calloc(numberOfElementsForChroma, sizeof(uint8_t));
    memcpy(uvPlane, uPtr, uPlaneSize);
    memcpy(uvPlane += (uint8_t)(uPlaneSize), vPtr, vPlaneSize);
    uint8_t *uvDestPlane = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);
    memcpy(uvDestPlane, uvPlane, numberOfElementsForChroma);
  1. Interleave U and V, picture remains to be distorted
    CVPixelBufferLockBaseAddress(pixelBuffer, 0);
    uint8_t *yDestPlane = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
    for (int i = 0, okay = 0; i < top; i ++) {
        for (int j = 0; j < width; j ++) {
            yDestPlane[k++] = ((unsigned char *)yPtr)[j + i * yStride];
        }
    }
    uint8_t *uvDestPlane = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);
    for (int row = 0, index = 0; row < top / 2; row++) {
        for (int col = 0; col < width / 2; col++) {
            uvDestPlane[index++] = ((unsigned char *)uPtr)[col + row * uStride];
            uvDestPlane[index++] = ((unsigned char *)vPtr)[col + row * vStride];
        }
    }
  1. Some what much like 1.
    int yPixels = yStride * top;
    int uPixels = uStride * top/2;
    int vPixels = vStride * top/2;
    CVPixelBufferLockBaseAddress(pixelBuffer, 0);
    uint8_t *yDestPlane = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
    memcpy(yDestPlane, yPtr, yPixels);

    uint8_t *uvDestPlane = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);
    memcpy(uvDestPlane , uPtr, uPixels);
    memcpy(uvDestPlane + uPixels, vPtr, vPixels);
  1. Use Speed up to transform YUV to BGRA after which convert to CVPixelBuffer, no error however no video rendered
    vImage_Buffer srcYp = {
        .width = width,
        .top = top,
        .rowBytes = yStride,
        .information = yPtr,
    };
    vImage_Buffer srcCb = {
        .width = width / 2,
        .top = top / 2,
        .rowBytes = uStride,
        .information = uPtr,
    };
    vImage_Buffer srcCr = {
        .width = width / 2,
        .top = top / 2,
        .rowBytes = vStride,
        .information = vPtr,
    };

    vImage_Buffer dest;
    dest.information = NULL;
    dest.width = width;
    dest.top = top;

    vImage_Error error = kvImageNoError;
    error = vImageBuffer_Init(&dest, top, width, 32, kvImagePrintDiagnosticsToConsole);
    // vImage_YpCbCrPixelRange pixelRange = (vImage_YpCbCrPixelRange){ 0, 128, 255, 255, 255, 1, 255, 0 };
    vImage_YpCbCrPixelRange pixelRange = { 16, 128, 235, 240, 255, 0, 255, 0 };
    vImage_YpCbCrToARGB data;
    error = kvImageNoError;
    error = vImageConvert_YpCbCrToARGB_GenerateConversion(kvImage_YpCbCrToARGBMatrix_ITU_R_601_4,
                                                          &pixelRange,
                                                          &data,
                                                          kvImage420Yp8_Cb8_Cr8,
                                                          kvImageARGB8888,
                                                          kvImagePrintDiagnosticsToConsole);
    error = kvImageNoError;
    uint8_t permuteMap[4] = {3, 2, 1, 0}; // BGRA - iOS solely assist BGRA
    error = vImageConvert_420Yp8_Cb8_Cr8ToARGB8888(&srcYp,
                                                   &srcCb,
                                                   &srcCr,
                                                   &dest,
                                                   &data,
                                                   permuteMap, // for iOS should be no NULL, mac may be NULL iOS solely assist BGRA
                                                   255,
                                                   kvImagePrintDiagnosticsToConsole);

    if (error != kvImageNoError) {
        NSAssert(NO, @"PIP: vImageConvert error %ld", error);
        return nil;
    }

    // vImageBuffer_CopyToCVPixelBuffer will give out error destFormat bitsPerComponent = 0 will not be supported
//    vImage_CGImageFormat format =  kCGImageAlphaNoneSkipFirst,
//        .colorSpace = CGColorSpaceCreateDeviceRGB()
//    ;
//    vImageCVImageFormatRef vformat = vImageCVImageFormat_CreateWithCVPixelBuffer(pixelBuffer);
//
//    error = vImageBuffer_CopyToCVPixelBuffer(&dest, &format, pixelBuffer, vformat, 0, kvImagePrintDiagnosticsToConsole);

    outcome = CVPixelBufferCreateWithBytes(kCFAllocatorDefault,
                                          width,
                                          top,
                                          kCVPixelFormatType_32BGRA,
                                          dest.information,
                                          dest.rowBytes,
                                          NULL,
                                          NULL,
                                          (__bridge CFDictionaryRef)pixelAttributes,
                                          &pixelBuffer);

[ad_2]

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments