Commit 6f961aae authored by Moja Hsu's avatar Moja Hsu Committed by Commit Bot

Add V4L2 JPEG Encoder support.

Support V4L2 JPEG devices.
It supports both JPEG and JPEG_RAW devices for different hardware
accelerators.
This CL also increase the kMeanDiffThreshold to 10 in
jpeg_encode_accelerator_unittest.
The encoded results are much different between SW and HW encode.


CQ-DEPEND=CL:1161846
BUG=b:77835279
TEST=Pass compile on scarlet and nautilus.
Pass jpeg_encode_accelerator_unittest
Pass libjea_test
Take picture on dru and check if we use HW encoder for jpeg.

Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel
Change-Id: I9d12625d398e9d66a5e228081a718f55e194a8a4
Reviewed-on: https://chromium-review.googlesource.com/c/1122146
Commit-Queue: Hsu Wei-Cheng <mojahsu@chromium.org>
Reviewed-by: default avatarRicky Liang <jcliang@chromium.org>
Reviewed-by: default avatarDan Sanders <sandersd@chromium.org>
Reviewed-by: default avatarKenneth Russell <kbr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#607555}
parent 78cf9afa
......@@ -122,6 +122,10 @@ void AddV4L2GpuWhitelist(
// Device node for V4L2 JPEG decode accelerator drivers.
static const char kDevJpegDecPath[] = "/dev/jpeg-dec";
permissions->push_back(BrokerFilePermission::ReadWrite(kDevJpegDecPath));
// Device node for V4L2 JPEG encode accelerator drivers.
static const char kDevJpegEncPath[] = "/dev/jpeg-enc";
permissions->push_back(BrokerFilePermission::ReadWrite(kDevJpegEncPath));
}
void AddArmMaliGpuWhitelist(std::vector<BrokerFilePermission>* permissions) {
......
......@@ -91,6 +91,31 @@ const JpegHuffmanTable kDefaultAcTable[kJpegMaxHuffmanTableNumBaseline] = {
},
};
constexpr uint8_t kZigZag8x8[64] = {
0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5,
12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28,
35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63};
constexpr JpegQuantizationTable kDefaultQuantTable[2] = {
// Table K.1 Luminance quantization table values.
{
true,
{16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55,
14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62,
18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92,
49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99},
},
// Table K.2 Chrominance quantization table values.
{
true,
{17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99,
24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99},
},
};
static bool InRange(int value, int a, int b) {
return a <= value && value <= b;
}
......
......@@ -49,6 +49,25 @@ enum JpegMarker {
JPEG_MARKER_PREFIX = 0xFF, // jpeg marker prefix
};
// JPEG format uses 2 bytes to denote the size of a segment, and the size
// includes the 2 bytes used for specifying it. Therefore, maximum data size
// allowed is: 65535 - 2 = 65533.
constexpr size_t kMaxMarkerSizeAllowed = 65533;
// JPEG header only uses 2 bytes to represent width and height.
constexpr int kMaxDimension = 65535;
constexpr size_t kDctSize = 64;
constexpr size_t kNumDcRunSizeBits = 16;
constexpr size_t kNumAcRunSizeBits = 16;
constexpr size_t kNumDcCodeWordsHuffVal = 12;
constexpr size_t kNumAcCodeWordsHuffVal = 162;
constexpr size_t kJpegDefaultHeaderSize =
67 + (kDctSize * 2) + (kNumDcRunSizeBits * 2) +
(kNumDcCodeWordsHuffVal * 2) + (kNumAcRunSizeBits * 2) +
(kNumAcCodeWordsHuffVal * 2);
constexpr size_t kJFIFApp0Size = 16;
const size_t kJpegMaxHuffmanTableNumBaseline = 2;
const size_t kJpegMaxComponents = 4;
const size_t kJpegMaxQuantizationTableNum = 4;
......@@ -71,9 +90,15 @@ MEDIA_EXPORT extern const JpegHuffmanTable
// Parsing result of JPEG DQT marker.
struct JpegQuantizationTable {
bool valid;
uint8_t value[64]; // baseline only supports 8 bits quantization table
uint8_t value[kDctSize]; // baseline only supports 8 bits quantization table
};
MEDIA_EXPORT extern const uint8_t kZigZag8x8[64];
// Table K.1 Luminance quantization table
// Table K.2 Chrominance quantization table
MEDIA_EXPORT extern const JpegQuantizationTable kDefaultQuantTable[2];
// Parsing result of a JPEG component.
struct JpegComponent {
uint8_t id;
......
......@@ -230,6 +230,8 @@ component("gpu") {
"v4l2/v4l2_image_processor.h",
"v4l2/v4l2_jpeg_decode_accelerator.cc",
"v4l2/v4l2_jpeg_decode_accelerator.h",
"v4l2/v4l2_jpeg_encode_accelerator.cc",
"v4l2/v4l2_jpeg_encode_accelerator.h",
"v4l2/v4l2_slice_video_decode_accelerator.cc",
"v4l2/v4l2_slice_video_decode_accelerator.h",
"v4l2/v4l2_video_decode_accelerator.cc",
......@@ -546,7 +548,7 @@ if (use_v4l2_codec || use_vaapi || is_mac || is_win) {
}
}
if (use_vaapi) {
if (use_v4l2_codec || use_vaapi) {
test("jpeg_encode_accelerator_unittest") {
deps = [
"//base",
......
......@@ -19,10 +19,22 @@
#include "media/gpu/vaapi/vaapi_jpeg_encode_accelerator.h"
#endif
#if defined(USE_V4L2_JEA)
#include "media/gpu/v4l2/v4l2_device.h"
#include "media/gpu/v4l2/v4l2_jpeg_encode_accelerator.h"
#endif
namespace media {
namespace {
#if defined(USE_V4L2_JEA)
std::unique_ptr<JpegEncodeAccelerator> CreateV4L2JEA(
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) {
return std::make_unique<V4L2JpegEncodeAccelerator>(std::move(io_task_runner));
}
#endif
#if BUILDFLAG(USE_VAAPI)
std::unique_ptr<JpegEncodeAccelerator> CreateVaapiJEA(
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) {
......@@ -45,7 +57,7 @@ GpuJpegEncodeAcceleratorFactory::GetAcceleratorFactories() {
// This list is ordered by priority of use.
std::vector<CreateAcceleratorCB> result;
#if defined(USE_V4L2_JEA)
// TODO(mojahsu): Add CreateV4l2JEA here.
result.push_back(base::BindRepeating(&CreateV4L2JEA));
#endif
#if BUILDFLAG(USE_VAAPI)
result.push_back(base::BindRepeating(&CreateVaapiJEA));
......
......@@ -466,6 +466,11 @@ bool GenericV4L2Device::IsJpegDecodingSupported() {
return !devices.empty();
}
bool GenericV4L2Device::IsJpegEncodingSupported() {
const auto& devices = GetDevicesForType(Type::kJpegEncoder);
return !devices.empty();
}
bool GenericV4L2Device::OpenDevicePath(const std::string& path, Type type) {
DCHECK(!device_fd_.is_valid());
......@@ -511,6 +516,7 @@ void GenericV4L2Device::EnumerateDevicesForType(Type type) {
static const std::string kEncoderDevicePattern = "/dev/video-enc";
static const std::string kImageProcessorDevicePattern = "/dev/image-proc";
static const std::string kJpegDecoderDevicePattern = "/dev/jpeg-dec";
static const std::string kJpegEncoderDevicePattern = "/dev/jpeg-enc";
std::string device_pattern;
v4l2_buf_type buf_type;
......@@ -531,6 +537,10 @@ void GenericV4L2Device::EnumerateDevicesForType(Type type) {
device_pattern = kJpegDecoderDevicePattern;
buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
break;
case Type::kJpegEncoder:
device_pattern = kJpegEncoderDevicePattern;
buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
break;
}
std::vector<std::string> candidate_paths;
......
......@@ -75,6 +75,7 @@ class GenericV4L2Device : public V4L2Device {
bool IsImageProcessingSupported() override;
bool IsJpegDecodingSupported() override;
bool IsJpegEncodingSupported() override;
private:
// Vector of video device node paths and corresponding pixelformats supported
......
......@@ -302,4 +302,8 @@ bool TegraV4L2Device::IsJpegDecodingSupported() {
return false;
}
bool TegraV4L2Device::IsJpegEncodingSupported() {
return false;
}
} // namespace media
......@@ -71,6 +71,7 @@ class TegraV4L2Device : public V4L2Device {
bool IsImageProcessingSupported() override;
bool IsJpegDecodingSupported() override;
bool IsJpegEncodingSupported() override;
private:
~TegraV4L2Device() override;
......
......@@ -847,7 +847,7 @@ VideoPixelFormat V4L2Device::V4L2PixFmtToVideoPixelFormat(uint32_t pix_fmt) {
case V4L2_PIX_FMT_NV12M:
return PIXEL_FORMAT_NV12;
case V4L2_PIX_FMT_MT21:
case V4L2_PIX_FMT_MT21C:
return PIXEL_FORMAT_MT21;
case V4L2_PIX_FMT_YUV420:
......@@ -876,7 +876,7 @@ uint32_t V4L2Device::VideoPixelFormatToV4L2PixFmt(VideoPixelFormat format) {
return V4L2_PIX_FMT_NV12M;
case PIXEL_FORMAT_MT21:
return V4L2_PIX_FMT_MT21;
return V4L2_PIX_FMT_MT21C;
case PIXEL_FORMAT_I420:
return V4L2_PIX_FMT_YUV420M;
......@@ -1014,7 +1014,7 @@ uint32_t V4L2Device::V4L2PixFmtToDrmFormat(uint32_t format) {
case V4L2_PIX_FMT_RGB32:
return DRM_FORMAT_ARGB8888;
case V4L2_PIX_FMT_MT21:
case V4L2_PIX_FMT_MT21C:
return DRM_FORMAT_MT21;
default:
......
......@@ -30,10 +30,15 @@
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_image.h"
// TODO(posciak): remove this once V4L2 headers are updated.
#define V4L2_PIX_FMT_MT21 v4l2_fourcc('M', 'T', '2', '1')
#ifndef V4L2_BUF_FLAG_LAST
#define V4L2_BUF_FLAG_LAST 0x00100000
// TODO(mojahsu): remove this once V4L2 headers are updated.
#ifndef V4L2_PIX_FMT_JPEG_RAW
#define V4L2_PIX_FMT_JPEG_RAW v4l2_fourcc('J', 'P', 'G', 'R')
#endif
#ifndef V4L2_CID_JPEG_LUMA_QUANTIZATION
#define V4L2_CID_JPEG_LUMA_QUANTIZATION (V4L2_CID_JPEG_CLASS_BASE + 5)
#endif
#ifndef V4L2_CID_JPEG_CHROMA_QUANTIZATION
#define V4L2_CID_JPEG_CHROMA_QUANTIZATION (V4L2_CID_JPEG_CLASS_BASE + 6)
#endif
namespace media {
......@@ -310,6 +315,7 @@ class MEDIA_GPU_EXPORT V4L2Device
kEncoder,
kImageProcessor,
kJpegDecoder,
kJpegEncoder,
};
// Create and initialize an appropriate V4L2Device instance for the current
......@@ -433,8 +439,9 @@ class MEDIA_GPU_EXPORT V4L2Device
// Return true if image processing is supported, false otherwise.
virtual bool IsImageProcessingSupported() = 0;
// Return true if JPEG decoding is supported, false otherwise.
// Return true if JPEG codec is supported, false otherwise.
virtual bool IsJpegDecodingSupported() = 0;
virtual bool IsJpegEncodingSupported() = 0;
protected:
friend class base::RefCountedThreadSafe<V4L2Device>;
......
This diff is collapsed.
This diff is collapsed.
......@@ -18,6 +18,7 @@
#include "base/trace_event/trace_event.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/video_frame.h"
#include "media/filters/jpeg_parser.h"
#include "media/gpu/vaapi/vaapi_jpeg_encoder.h"
#define VLOGF(level) VLOG(level) << __func__ << "(): "
......@@ -27,11 +28,6 @@ namespace media {
namespace {
// JPEG format uses 2 bytes to denote the size of a segment, and the size
// includes the 2 bytes used for specifying it. Therefore, maximum data size
// allowed is: 65535 - 2 = 65533.
constexpr size_t kMaxExifSizeAllowed = 65533;
// UMA results that the VaapiJpegEncodeAccelerator class reports.
// These values are persisted to logs, and should therefore never be renumbered
// nor reused.
......@@ -297,7 +293,7 @@ void VaapiJpegEncodeAccelerator::Encode(scoped_refptr<VideoFrame> video_frame,
weak_this_, buffer_id, PLATFORM_FAILURE));
return;
}
if (exif_shm->size() > kMaxExifSizeAllowed) {
if (exif_shm->size() > kMaxMarkerSizeAllowed) {
VLOGF(1) << "Exif buffer too big: " << exif_shm->size();
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&VaapiJpegEncodeAccelerator::NotifyError,
......
......@@ -25,44 +25,6 @@ namespace media {
namespace {
// JPEG header only uses 2 bytes to represent width and height.
constexpr int kMaxDimension = 65535;
constexpr size_t kDctSize2 = 64;
constexpr size_t kNumDcRunSizeBits = 16;
constexpr size_t kNumAcRunSizeBits = 16;
constexpr size_t kNumDcCodeWordsHuffVal = 12;
constexpr size_t kNumAcCodeWordsHuffVal = 162;
constexpr size_t kJpegDefaultHeaderSize =
67 + (kDctSize2 * 2) + (kNumDcRunSizeBits * 2) +
(kNumDcCodeWordsHuffVal * 2) + (kNumAcRunSizeBits * 2) +
(kNumAcCodeWordsHuffVal * 2);
constexpr size_t kJFIFApp0Size = 16;
constexpr uint8_t kZigZag8x8[64] = {
0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5,
12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28,
35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63};
constexpr JpegQuantizationTable kDefaultQuantTable[2] = {
// Table K.1 Luminance quantization table values.
{
true,
{16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55,
14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62,
18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92,
49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99},
},
// Table K.2 Chrominance quantization table values.
{
true,
{17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99,
24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99},
},
};
void FillPictureParameters(const gfx::Size& input_size,
int quality,
VABufferID output_buffer_id,
......@@ -236,7 +198,7 @@ size_t FillJpegHeader(const gfx::Size& input_size,
for (size_t i = 0; i < 2; ++i) {
const uint8_t kQuantSegment[] = {
0xFF, JPEG_DQT, 0x00,
0x03 + kDctSize2, // Segment length:67 (2-byte).
0x03 + kDctSize, // Segment length:67 (2-byte).
static_cast<uint8_t>(i) // Precision (4-bit high) = 0,
// Index (4-bit low) = i.
};
......@@ -244,7 +206,7 @@ size_t FillJpegHeader(const gfx::Size& input_size,
idx += sizeof(kQuantSegment);
const JpegQuantizationTable& quant_table = kDefaultQuantTable[i];
for (size_t j = 0; j < kDctSize2; ++j) {
for (size_t j = 0; j < kDctSize; ++j) {
uint32_t scaled_quant_value =
(quant_table.value[kZigZag8x8[j]] * quality_normalized) / 100;
scaled_quant_value = std::min(255u, std::max(1u, scaled_quant_value));
......
......@@ -16,6 +16,7 @@ namespace media {
// JPEG encoder interface.
class MEDIA_EXPORT JpegEncodeAccelerator {
public:
static constexpr int32_t kInvalidBitstreamBufferId = -1;
enum Status {
ENCODE_OK,
......@@ -91,7 +92,8 @@ class MEDIA_EXPORT JpegEncodeAccelerator {
// Client::NotifyError() callback.
// Parameters:
// |video_frame| contains the YUV image to be encoded.
// |quality| of JPEG image.
// |quality| of JPEG image. The range is from 1~100. High value means high
// quality.
// |exif_buffer| contains Exif data to be inserted into JPEG image. If it's
// nullptr, the JFIF APP0 segment will be inserted.
// |output_buffer| that contains output buffer for encoded result. Clients
......
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