0
0
mirror of https://github.com/obsproject/obs-studio.git synced 2024-09-20 13:08:50 +02:00

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.
This commit is contained in:
Fabian Mastenbroek 2022-02-15 15:39:56 +01:00
parent 29ca91852b
commit 8683eb10e1
No known key found for this signature in database
GPG Key ID: 405FC6F81F0A7B85

View File

@ -195,18 +195,9 @@ static void virtualcam_output_raw_video(void *data, struct video_data *frame)
{ {
UNUSED_PARAMETER(data); 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; CVPixelBufferRef frameRef = NULL;
CVReturn status = CVReturn status = CVPixelBufferPoolCreatePixelBuffer(
CVPixelBufferPoolCreatePixelBuffer(NULL, pool, &frameRef); kCFAllocatorDefault, pool, &frameRef);
if (status != kCVReturnSuccess) { if (status != kCVReturnSuccess) {
blog(LOG_ERROR, "unable to allocate pixel buffer (error %d)", 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; return;
} }
// Copy memory into the pixel buffer // Copy all planes into pixel buffer
size_t planeCount = CVPixelBufferGetPlaneCount(frameRef);
CVPixelBufferLockBaseAddress(frameRef, 0); CVPixelBufferLockBaseAddress(frameRef, 0);
uint8_t *dest =
(uint8_t *)CVPixelBufferGetBaseAddressOfPlane(frameRef, 0);
uint8_t *src = outData;
size_t destBytesPerRow = if (planeCount == 0) {
CVPixelBufferGetBytesPerRowOfPlane(frameRef, 0); uint8_t *src = frame->data[0];
size_t srcBytesPerRow = frame->linesize[0]; uint8_t *dst = (uint8_t *)CVPixelBufferGetBaseAddress(frameRef);
// Sometimes CVPixelBufferCreate will create a pixelbuffer that's a different size_t destBytesPerRow = CVPixelBufferGetBytesPerRow(frameRef);
// size than necessary to hold the frame (probably for some optimization reason). size_t srcBytesPerRow = frame->linesize[0];
// If that is the case this will do a row-by-row copy into the buffer. size_t height = CVPixelBufferGetHeight(frameRef);
if (destBytesPerRow == srcBytesPerRow) {
memcpy(dest, src, destBytesPerRow * height); // 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 { } else {
for (int line = 0; (size_t)line < height; line++) { for (size_t plane = 0; plane < planeCount; plane++) {
memcpy(dest, src, srcBytesPerRow); uint8_t *src = frame->data[plane];
src += srcBytesPerRow;
dest += destBytesPerRow; 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); CVPixelBufferUnlockBaseAddress(frameRef, 0);