From 8683eb10e1f5e8dd89a4bd07b9310cde6ba870d8 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Tue, 15 Feb 2022 15:39:56 +0100 Subject: [PATCH] mac-virtualcam: Support multiple AV planes This change updates the plugin to support video formats that contain multiple planes (such as NV12). Such functionality is necessary to prevent transcoding the raw video data, which is often delivered in a planar format. --- .../src/obs-plugin/plugin-main.mm | 81 ++++++++++++------- 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/plugins/mac-virtualcam/src/obs-plugin/plugin-main.mm b/plugins/mac-virtualcam/src/obs-plugin/plugin-main.mm index 0221ac660..340216316 100644 --- a/plugins/mac-virtualcam/src/obs-plugin/plugin-main.mm +++ b/plugins/mac-virtualcam/src/obs-plugin/plugin-main.mm @@ -195,18 +195,9 @@ static void virtualcam_output_raw_video(void *data, struct video_data *frame) { UNUSED_PARAMETER(data); - uint8_t *outData = frame->data[0]; - if (frame->linesize[0] != (videoInfo.output_width * 2)) { - blog(LOG_ERROR, - "unexpected frame->linesize (expected:%d actual:%d)", - (videoInfo.output_width * 2), frame->linesize[0]); - } - - size_t height = videoInfo.output_height; - CVPixelBufferRef frameRef = NULL; - CVReturn status = - CVPixelBufferPoolCreatePixelBuffer(NULL, pool, &frameRef); + CVReturn status = CVPixelBufferPoolCreatePixelBuffer( + kCFAllocatorDefault, pool, &frameRef); if (status != kCVReturnSuccess) { blog(LOG_ERROR, "unable to allocate pixel buffer (error %d)", @@ -214,29 +205,63 @@ static void virtualcam_output_raw_video(void *data, struct video_data *frame) return; } - // Copy memory into the pixel buffer + // Copy all planes into pixel buffer + size_t planeCount = CVPixelBufferGetPlaneCount(frameRef); CVPixelBufferLockBaseAddress(frameRef, 0); - uint8_t *dest = - (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(frameRef, 0); - uint8_t *src = outData; - size_t destBytesPerRow = - CVPixelBufferGetBytesPerRowOfPlane(frameRef, 0); - size_t srcBytesPerRow = frame->linesize[0]; + if (planeCount == 0) { + uint8_t *src = frame->data[0]; + uint8_t *dst = (uint8_t *)CVPixelBufferGetBaseAddress(frameRef); - // Sometimes CVPixelBufferCreate will create a pixelbuffer that's a different - // size than necessary to hold the frame (probably for some optimization reason). - // If that is the case this will do a row-by-row copy into the buffer. - if (destBytesPerRow == srcBytesPerRow) { - memcpy(dest, src, destBytesPerRow * height); + size_t destBytesPerRow = CVPixelBufferGetBytesPerRow(frameRef); + size_t srcBytesPerRow = frame->linesize[0]; + size_t height = CVPixelBufferGetHeight(frameRef); + + // Sometimes CVPixelBufferCreate will create a pixel buffer that's a different + // size than necessary to hold the frame (probably for some optimization reason). + // If that is the case this will do a row-by-row copy into the buffer. + if (destBytesPerRow == srcBytesPerRow) { + memcpy(dst, src, destBytesPerRow * height); + } else { + for (int line = 0; (size_t)line < height; line++) { + memcpy(dst, src, srcBytesPerRow); + src += srcBytesPerRow; + dst += destBytesPerRow; + } + } } else { - for (int line = 0; (size_t)line < height; line++) { - memcpy(dest, src, srcBytesPerRow); - src += srcBytesPerRow; - dest += destBytesPerRow; + for (size_t plane = 0; plane < planeCount; plane++) { + uint8_t *src = frame->data[plane]; + + if (!src) { + blog(LOG_WARNING, + "Video data from OBS contains less planes than CVPixelBuffer"); + break; + } + + uint8_t *dst = + (uint8_t *)CVPixelBufferGetBaseAddressOfPlane( + frameRef, plane); + + size_t destBytesPerRow = + CVPixelBufferGetBytesPerRowOfPlane(frameRef, + plane); + size_t srcBytesPerRow = frame->linesize[plane]; + size_t height = + CVPixelBufferGetHeightOfPlane(frameRef, plane); + + if (destBytesPerRow == srcBytesPerRow) { + memcpy(dst, src, destBytesPerRow * height); + } else { + for (int line = 0; (size_t)line < height; + line++) { + memcpy(dst, src, srcBytesPerRow); + src += srcBytesPerRow; + dst += destBytesPerRow; + } + } } } - memcpy(dest, outData, srcBytesPerRow * height); CVPixelBufferUnlockBaseAddress(frameRef, 0);