Create CVPixelBuffer from RTCVideoFrame
// Google's WebRTC SDK
#import <WebRTC/WebRTC.h>
@import CoreImage;
@import CoreVideo;
@import AVFoundation;
// 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 );