Commit 1a47caa9 authored by Henrik Boström's avatar Henrik Boström Committed by Commit Bot

[macOS] Optimize SampleBufferTransformer's MJPEG -> NV12 path.

With eshr@'s libyuv::MJPGToNV12() being rolled into chromium, it is now
possible to do MJPEG -> NV12 with a single libyuv function. This CL
gets rid of the MJPEG -> I420 -> NV12 paths and makes use of the new
libyuv function. This also simplifies the MJPEG -> I420 path.

Bug: chromium:1132299
Change-Id: Ia7271703e70b731391873baea009684c00764828
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2516703Reviewed-by: default avatarIlya Nikolaevskiy <ilnik@chromium.org>
Commit-Queue: Henrik Boström <hbos@chromium.org>
Cr-Commit-Position: refs/heads/master@{#823934}
parent 3c3e5374
......@@ -22,6 +22,8 @@ constexpr OSType kPixelFormatNv12 =
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
// I420 a.k.a. y420
constexpr OSType kPixelFormatI420 = kCVPixelFormatType_420YpCbCr8Planar;
// MJPEG a.k.a. dmb1
constexpr OSType kPixelFormatMjpeg = kCMVideoCodecType_JPEG_OpenDML;
// Pixel formats mappings to allow these pixel formats to be converted to I420
// with libyuv.
......@@ -249,6 +251,21 @@ void ConvertFromI420ToNV12(const I420Planes& source,
DCHECK_EQ(result, 0);
}
void ConvertFromMjpegToNV12(uint8_t* source_buffer_data_base_address,
size_t source_buffer_data_size,
const NV12Planes& destination) {
// Despite libyuv::MJPGToNV12() taking both source and destination sizes as
// arguments, this function is only successful if the sizes match. So here we
// require the destination buffer's size to match the source's, which can be
// obtained using libyuv::MJPGSize().
int result = libyuv::MJPGToNV12(
source_buffer_data_base_address, source_buffer_data_size,
destination.y_plane_data, destination.y_plane_stride,
destination.uv_plane_data, destination.uv_plane_stride, destination.width,
destination.height, destination.width, destination.height);
DCHECK_EQ(result, 0);
}
void ScaleI420(const I420Planes& source, const I420Planes& destination) {
int result = libyuv::I420Scale(
source.y_plane_data, source.y_plane_stride, source.u_plane_data,
......@@ -511,17 +528,12 @@ void SampleBufferTransformer::TransformSampleBuffer(
CMSampleBufferRef source_sample_buffer,
CVPixelBufferRef destination_pixel_buffer) {
DCHECK(transformer_ == Transformer::kLibyuv);
// Lock destination pixel buffer.
CVReturn lock_status =
CVPixelBufferLockBaseAddress(destination_pixel_buffer, 0);
DCHECK_EQ(lock_status, kCVReturnSuccess);
// Ensure source pixel format is MJPEG.
CMFormatDescriptionRef source_format_description =
CMSampleBufferGetFormatDescription(source_sample_buffer);
FourCharCode source_pixel_format =
CMFormatDescriptionGetMediaSubType(source_format_description);
DCHECK(source_pixel_format == kCMVideoCodecType_JPEG_OpenDML);
DCHECK(source_pixel_format == kPixelFormatMjpeg);
// Access source pixel buffer bytes.
uint8_t* source_buffer_data_base_address;
......@@ -534,65 +546,91 @@ void SampleBufferTransformer::TransformSampleBuffer(
libyuv::MJPGSize(source_buffer_data_base_address, source_buffer_data_size,
&mjpg_width, &mjpg_height);
DCHECK(result == 0);
size_t source_width = mjpg_width;
size_t source_height = mjpg_height;
// Lock destination pixel buffer.
CVReturn lock_status =
CVPixelBufferLockBaseAddress(destination_pixel_buffer, 0);
DCHECK_EQ(lock_status, kCVReturnSuccess);
// Convert to I420 or NV12.
switch (destination_pixel_format_) {
case kPixelFormatI420:
TransformSampleBufferFromMjpegToI420(
source_buffer_data_base_address, source_buffer_data_size, mjpg_width,
mjpg_height, destination_pixel_buffer);
break;
case kPixelFormatNv12:
TransformSampleBufferFromMjpegToNV12(
source_buffer_data_base_address, source_buffer_data_size, mjpg_width,
mjpg_height, destination_pixel_buffer);
break;
default:
NOTREACHED();
}
// Unlock destination pixel buffer.
lock_status = CVPixelBufferUnlockBaseAddress(destination_pixel_buffer, 0);
DCHECK_EQ(lock_status, kCVReturnSuccess);
}
void SampleBufferTransformer::TransformSampleBufferFromMjpegToI420(
uint8_t* source_buffer_data_base_address,
size_t source_buffer_data_size,
size_t source_width,
size_t source_height,
CVPixelBufferRef destination_pixel_buffer) {
DCHECK(destination_pixel_format_ == kPixelFormatI420);
// Rescaling has to be done in a separate step.
const bool rescale_needed = destination_width_ != source_width ||
destination_height_ != source_height;
// We don't know how to convert MJPEG -> NV12 directly, so we always start
// with MJPEG -> I420.
// TODO(hbos): This is no longer true in libyuv. In a follow-up CL, make use
// of eshr@'s MJPEG -> NV12 converter.
// Step 1: Convert MJPEG -> I420.
I420Planes i420_fullscale_buffer;
if (!rescale_needed && destination_pixel_format_ == kPixelFormatI420) {
if (!rescale_needed) {
i420_fullscale_buffer =
GetI420PlanesFromPixelBuffer(destination_pixel_buffer);
} else {
// Convert into an intermediate buffer, to be converted and/or scaled into
// the destination pixel buffer in subsequent steps.
i420_fullscale_buffer = EnsureI420BufferSizeAndGetPlanes(
source_width, source_height, &intermediate_i420_buffer_);
}
ConvertFromAnyToI420(source_pixel_format, source_buffer_data_base_address,
ConvertFromAnyToI420(kPixelFormatMjpeg, source_buffer_data_base_address,
source_buffer_data_size, i420_fullscale_buffer);
// Step 2: Convert to destination format and then rescale.
if (destination_pixel_format_ == kPixelFormatI420) {
// We are already at I420.
if (rescale_needed) {
I420Planes i420_destination_buffer =
GetI420PlanesFromPixelBuffer(destination_pixel_buffer);
ScaleI420(i420_fullscale_buffer, i420_destination_buffer);
}
} else if (destination_pixel_format_ == kPixelFormatNv12) {
// Convert I420 -> NV12.
NV12Planes nv12_fullscale_buffer;
if (!rescale_needed) {
nv12_fullscale_buffer =
GetNV12PlanesFromPixelBuffer(destination_pixel_buffer);
} else {
nv12_fullscale_buffer = EnsureNV12BufferSizeAndGetPlanes(
source_width, source_height, &intermediate_nv12_buffer_);
}
ConvertFromI420ToNV12(i420_fullscale_buffer, nv12_fullscale_buffer);
// Step 2: Rescale I420.
if (rescale_needed) {
I420Planes i420_destination_buffer =
GetI420PlanesFromPixelBuffer(destination_pixel_buffer);
ScaleI420(i420_fullscale_buffer, i420_destination_buffer);
}
}
// Rescale NV12.
if (rescale_needed) {
NV12Planes nv12_destination_buffer =
GetNV12PlanesFromPixelBuffer(destination_pixel_buffer);
ScaleNV12(nv12_fullscale_buffer, nv12_destination_buffer);
}
void SampleBufferTransformer::TransformSampleBufferFromMjpegToNV12(
uint8_t* source_buffer_data_base_address,
size_t source_buffer_data_size,
size_t source_width,
size_t source_height,
CVPixelBufferRef destination_pixel_buffer) {
DCHECK(destination_pixel_format_ == kPixelFormatNv12);
// Rescaling has to be done in a separate step.
const bool rescale_needed = destination_width_ != source_width ||
destination_height_ != source_height;
// Step 1: Convert MJPEG -> NV12.
NV12Planes nv12_fullscale_buffer;
if (!rescale_needed) {
nv12_fullscale_buffer =
GetNV12PlanesFromPixelBuffer(destination_pixel_buffer);
} else {
NOTREACHED();
nv12_fullscale_buffer = EnsureNV12BufferSizeAndGetPlanes(
source_width, source_height, &intermediate_nv12_buffer_);
}
ConvertFromMjpegToNV12(source_buffer_data_base_address,
source_buffer_data_size, nv12_fullscale_buffer);
// Unlock destination pixel buffer.
lock_status = CVPixelBufferUnlockBaseAddress(destination_pixel_buffer, 0);
DCHECK_EQ(lock_status, kCVReturnSuccess);
// Step 2: Rescale NV12.
if (rescale_needed) {
NV12Planes nv12_destination_buffer =
GetNV12PlanesFromPixelBuffer(destination_pixel_buffer);
ScaleNV12(nv12_fullscale_buffer, nv12_destination_buffer);
}
}
} // namespace media
......@@ -74,6 +74,18 @@ class CAPTURE_EXPORT SampleBufferTransformer {
// Sample buffers from the camera contain byte buffers when MJPEG is used.
void TransformSampleBuffer(CMSampleBufferRef source_sample_buffer,
CVPixelBufferRef destination_pixel_buffer);
void TransformSampleBufferFromMjpegToI420(
uint8_t* source_buffer_data_base_address,
size_t source_buffer_data_size,
size_t source_width,
size_t source_height,
CVPixelBufferRef destination_pixel_buffer);
void TransformSampleBufferFromMjpegToNV12(
uint8_t* source_buffer_data_base_address,
size_t source_buffer_data_size,
size_t source_width,
size_t source_height,
CVPixelBufferRef destination_pixel_buffer);
Transformer transformer_;
OSType destination_pixel_format_;
......
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