Skip to content

Instantly share code, notes, and snippets.

@MRobertEvers
Created November 18, 2021 23:42
Show Gist options
  • Save MRobertEvers/a79ee3a69a58677f49370ea326ced73b to your computer and use it in GitHub Desktop.
Save MRobertEvers/a79ee3a69a58677f49370ea326ced73b to your computer and use it in GitHub Desktop.
Create CVPixelBuffer from RTCVideoFrame

Create CVPixelBuffer from RTCVideoFrame

Imports

// Google's WebRTC SDK
#import <WebRTC/WebRTC.h>

@import CoreImage;
@import CoreVideo;
@import AVFoundation;

Function

// Input (RTCVideoFrame*)frame

NSDictionary *pixelAttributes = @{(NSString*)kCVPixelBufferCGImageCompatibilityKey: @YES, (NSString*)kCVPixelBufferCGBitmapContextCompatibilityKey: @YES};
CVPixelBufferRef pixelBuffer = NULL;

// https://developer.apple.com/documentation/corevideo/cvpixelbuffer?language=objc

CVReturn createResult = CVPixelBufferCreate(kCFAllocatorDefault,
                                            frame.width,
                                            frame.height,
                                            kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,
                                            (__bridge CFDictionaryRef)(pixelAttributes),
                                            &pixelBuffer);

if (createResult != kCVReturnSuccess) {
    NSLog(@"Unable to create cvpixelbuffer %d", createResult);
    return;
}

// https://chromium.googlesource.com/external/webrtc/+/refs/heads/main/sdk/objc/base/RTCVideoFrame.h  
// https://chromium.googlesource.com/external/webrtc/+/refs/heads/main/sdk/objc/base/RTCVideoFrameBuffer.h
// This guarantees the buffer will be RTCI420Buffer
// https://wiki.videolan.org/YUV#I420
RTCI420Buffer *buffer = [frame.buffer toI420];

// The i420 buffer stores the data Planar
// https://developer.apple.com/documentation/accelerate/conversion/understanding_ypcbcr_image_formats?language=objc
// https://chromium.googlesource.com/external/webrtc/+/HEAD/api/video/i420_buffer.cc
uint32_t strideY = [buffer strideY];
uint32_t strideU = [buffer strideU];
uint32_t strideV = [buffer strideV];

uint32_t yPlaneSize = buffer.height * strideY;
uint32_t uPlaneSize = ((buffer.height + 1) / 2) * strideU;
uint32_t vPlaneSize = ((buffer.height + 1) / 2) * strideV;

// BiPlanar is 
// YYYYYYYYxN UVxN
CVPixelBufferLockBaseAddress(pixelBuffer, 0);
uint8_t *yDestPlane = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);

memcpy(yDestPlane, [buffer dataY], yPlaneSize);

uint8_t *uvDestPlane = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);

for (int i = 0; i <= vPlaneSize; i++)
{
    uvDestPlane[i*2] = [buffer dataU][i];
    uvDestPlane[i*2+1] = [buffer dataV][i];
}
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment