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 = ...@@ -22,6 +22,8 @@ constexpr OSType kPixelFormatNv12 =
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange; kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
// I420 a.k.a. y420 // I420 a.k.a. y420
constexpr OSType kPixelFormatI420 = kCVPixelFormatType_420YpCbCr8Planar; 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 // Pixel formats mappings to allow these pixel formats to be converted to I420
// with libyuv. // with libyuv.
...@@ -249,6 +251,21 @@ void ConvertFromI420ToNV12(const I420Planes& source, ...@@ -249,6 +251,21 @@ void ConvertFromI420ToNV12(const I420Planes& source,
DCHECK_EQ(result, 0); 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) { void ScaleI420(const I420Planes& source, const I420Planes& destination) {
int result = libyuv::I420Scale( int result = libyuv::I420Scale(
source.y_plane_data, source.y_plane_stride, source.u_plane_data, source.y_plane_data, source.y_plane_stride, source.u_plane_data,
...@@ -511,17 +528,12 @@ void SampleBufferTransformer::TransformSampleBuffer( ...@@ -511,17 +528,12 @@ void SampleBufferTransformer::TransformSampleBuffer(
CMSampleBufferRef source_sample_buffer, CMSampleBufferRef source_sample_buffer,
CVPixelBufferRef destination_pixel_buffer) { CVPixelBufferRef destination_pixel_buffer) {
DCHECK(transformer_ == Transformer::kLibyuv); 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. // Ensure source pixel format is MJPEG.
CMFormatDescriptionRef source_format_description = CMFormatDescriptionRef source_format_description =
CMSampleBufferGetFormatDescription(source_sample_buffer); CMSampleBufferGetFormatDescription(source_sample_buffer);
FourCharCode source_pixel_format = FourCharCode source_pixel_format =
CMFormatDescriptionGetMediaSubType(source_format_description); CMFormatDescriptionGetMediaSubType(source_format_description);
DCHECK(source_pixel_format == kCMVideoCodecType_JPEG_OpenDML); DCHECK(source_pixel_format == kPixelFormatMjpeg);
// Access source pixel buffer bytes. // Access source pixel buffer bytes.
uint8_t* source_buffer_data_base_address; uint8_t* source_buffer_data_base_address;
...@@ -534,65 +546,91 @@ void SampleBufferTransformer::TransformSampleBuffer( ...@@ -534,65 +546,91 @@ void SampleBufferTransformer::TransformSampleBuffer(
libyuv::MJPGSize(source_buffer_data_base_address, source_buffer_data_size, libyuv::MJPGSize(source_buffer_data_base_address, source_buffer_data_size,
&mjpg_width, &mjpg_height); &mjpg_width, &mjpg_height);
DCHECK(result == 0); 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. // Rescaling has to be done in a separate step.
const bool rescale_needed = destination_width_ != source_width || const bool rescale_needed = destination_width_ != source_width ||
destination_height_ != source_height; 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. // Step 1: Convert MJPEG -> I420.
I420Planes i420_fullscale_buffer; I420Planes i420_fullscale_buffer;
if (!rescale_needed && destination_pixel_format_ == kPixelFormatI420) { if (!rescale_needed) {
i420_fullscale_buffer = i420_fullscale_buffer =
GetI420PlanesFromPixelBuffer(destination_pixel_buffer); GetI420PlanesFromPixelBuffer(destination_pixel_buffer);
} else { } else {
// Convert into an intermediate buffer, to be converted and/or scaled into
// the destination pixel buffer in subsequent steps.
i420_fullscale_buffer = EnsureI420BufferSizeAndGetPlanes( i420_fullscale_buffer = EnsureI420BufferSizeAndGetPlanes(
source_width, source_height, &intermediate_i420_buffer_); 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); source_buffer_data_size, i420_fullscale_buffer);
// Step 2: Convert to destination format and then rescale. // Step 2: Rescale I420.
if (destination_pixel_format_ == kPixelFormatI420) { if (rescale_needed) {
// We are already at I420. I420Planes i420_destination_buffer =
if (rescale_needed) { GetI420PlanesFromPixelBuffer(destination_pixel_buffer);
I420Planes i420_destination_buffer = ScaleI420(i420_fullscale_buffer, 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);
// Rescale NV12. void SampleBufferTransformer::TransformSampleBufferFromMjpegToNV12(
if (rescale_needed) { uint8_t* source_buffer_data_base_address,
NV12Planes nv12_destination_buffer = size_t source_buffer_data_size,
GetNV12PlanesFromPixelBuffer(destination_pixel_buffer); size_t source_width,
ScaleNV12(nv12_fullscale_buffer, nv12_destination_buffer); 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 { } 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. // Step 2: Rescale NV12.
lock_status = CVPixelBufferUnlockBaseAddress(destination_pixel_buffer, 0); if (rescale_needed) {
DCHECK_EQ(lock_status, kCVReturnSuccess); NV12Planes nv12_destination_buffer =
GetNV12PlanesFromPixelBuffer(destination_pixel_buffer);
ScaleNV12(nv12_fullscale_buffer, nv12_destination_buffer);
}
} }
} // namespace media } // namespace media
...@@ -74,6 +74,18 @@ class CAPTURE_EXPORT SampleBufferTransformer { ...@@ -74,6 +74,18 @@ class CAPTURE_EXPORT SampleBufferTransformer {
// Sample buffers from the camera contain byte buffers when MJPEG is used. // Sample buffers from the camera contain byte buffers when MJPEG is used.
void TransformSampleBuffer(CMSampleBufferRef source_sample_buffer, void TransformSampleBuffer(CMSampleBufferRef source_sample_buffer,
CVPixelBufferRef destination_pixel_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_; Transformer transformer_;
OSType destination_pixel_format_; 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