Commit 79e4c835 authored by Christopher Cameron's avatar Christopher Cameron Committed by Chromium LUCI CQ

Capture/macOS: Handle not-tightly-packed CVPixelBuffers

The functions VideoCaptureDeviceMac::ReceiveFrame and then
VideoCaptureDeviceClient::OnIncomingCapturedData accept a data pointer,
but not stride information. These functions assume that all video frame
data provided is tightly packed. This is not a valid assumption.

When processing a CVPixelBuffer, retrieve the layout of the buffer, and
compute the layout of a tightly-packed version of the buffer. If they
do not match up, copy the CVPixelBuffer to a tightly-packed temporary
buffer and use that.

         naturally and are okay with the fix,
         Manually forced testing of NV12 frames

Testing: Logi 4k pro capture at 360x249 and BRIO 4K 240p hits this
Bug: 1151936
Change-Id: I1ce74629717316ae9387881d70dfb43186e3e906
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2566094Reviewed-by: default avatarMarkus Handell <handellm@google.com>
Commit-Queue: ccameron <ccameron@chromium.org>
Cr-Commit-Position: refs/heads/master@{#832085}
parent ee104c44
......@@ -566,31 +566,85 @@ AVCaptureDeviceFormat* FindBestCaptureFormat(
kCVReturnSuccess) {
return NO;
}
char* baseAddress = 0;
size_t frameSize = 0;
// Retrieve the layout of the planes of |pixelBuffer|.
const size_t numPlanes =
media::VideoFrame::NumPlanes(captureFormat.pixel_format);
std::vector<uint8_t*> pixelBufferAddresses;
std::vector<size_t> pixelBufferBytesPerRows;
std::vector<size_t> pixelBufferHeights;
if (!CVPixelBufferIsPlanar(pixelBuffer)) {
// For nonplanar buffers, CVPixelBufferGetBaseAddress returns a pointer
// to (0,0). (For planar buffers, it returns something else.)
// https://developer.apple.com/documentation/corevideo/1457115-cvpixelbuffergetbaseaddress?language=objc
baseAddress = static_cast<char*>(CVPixelBufferGetBaseAddress(pixelBuffer));
CHECK_EQ(numPlanes, 1u);
pixelBufferAddresses.push_back(
static_cast<uint8_t*>(CVPixelBufferGetBaseAddress(pixelBuffer)));
pixelBufferBytesPerRows.push_back(CVPixelBufferGetBytesPerRow(pixelBuffer));
pixelBufferHeights.push_back(CVPixelBufferGetHeight(pixelBuffer));
} else {
// For planar buffers, CVPixelBufferGetBaseAddressOfPlane() is used. If
// the buffer is contiguous (CHECK'd below) then we only need to know
// the address of the first plane, regardless of
// CVPixelBufferGetPlaneCount().
baseAddress =
static_cast<char*>(CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0));
CHECK_EQ(numPlanes, CVPixelBufferGetPlaneCount(pixelBuffer));
for (size_t plane = 0; plane < numPlanes; ++plane) {
pixelBufferAddresses.push_back(static_cast<uint8_t*>(
CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, plane)));
pixelBufferBytesPerRows.push_back(
CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, plane));
pixelBufferHeights.push_back(
CVPixelBufferGetHeightOfPlane(pixelBuffer, plane));
}
}
// CVPixelBufferGetDataSize() works for both nonplanar and planar buffers
// as long as they are contiguous in memory. If it is not contiguous, 0 is
// returned.
frameSize = CVPixelBufferGetDataSize(pixelBuffer);
size_t frameSize = CVPixelBufferGetDataSize(pixelBuffer);
// Only contiguous buffers are supported.
CHECK(frameSize);
// Compute the tightly-packed layout for |captureFormat|.
size_t packedBufferSize = 0;
std::vector<size_t> packedBytesPerRows;
std::vector<size_t> packedHeights;
for (size_t plane = 0; plane < numPlanes; ++plane) {
size_t bytesPerRow = media::VideoFrame::RowBytes(
plane, captureFormat.pixel_format, captureFormat.frame_size.width());
size_t height =
media::VideoFrame::PlaneSize(captureFormat.pixel_format, plane,
captureFormat.frame_size)
.height();
packedBytesPerRows.push_back(bytesPerRow);
packedHeights.push_back(height);
packedBufferSize += bytesPerRow * height;
}
// If |pixelBuffer| is not tightly packed, then copy it to |packedBufferCopy|,
// because ReceiveFrame() below assumes tight packing.
// https://crbug.com/1151936
bool needsCopyToPackedBuffer = pixelBufferBytesPerRows != packedBytesPerRows;
CHECK(pixelBufferHeights == packedHeights);
std::vector<uint8_t> packedBufferCopy;
if (needsCopyToPackedBuffer) {
CHECK(pixelBufferHeights == packedHeights);
packedBufferCopy.resize(packedBufferSize);
uint8_t* dstAddr = packedBufferCopy.data();
for (size_t plane = 0; plane < numPlanes; ++plane) {
uint8_t* srcAddr = pixelBufferAddresses[plane];
for (size_t row = 0; row < packedHeights[plane]; ++row) {
memcpy(dstAddr, srcAddr, packedBytesPerRows[plane]);
dstAddr += packedBytesPerRows[plane];
srcAddr += pixelBufferBytesPerRows[plane];
}
}
}
_lock.AssertAcquired();
_frameReceiver->ReceiveFrame(reinterpret_cast<const uint8_t*>(baseAddress),
frameSize, captureFormat, colorSpace, 0, 0,
timestamp);
_frameReceiver->ReceiveFrame(
packedBufferCopy.empty() ? pixelBufferAddresses[0]
: packedBufferCopy.data(),
frameSize, captureFormat, colorSpace, 0, 0, timestamp);
CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
return YES;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment