123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- // Copyright 2019 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "media/gpu/vaapi/vaapi_jpeg_decoder.h"
- #include <string.h>
- #include <va/va.h>
- #include <iostream>
- #include <type_traits>
- #include "base/logging.h"
- #include "base/numerics/safe_conversions.h"
- #include "media/base/video_types.h"
- #include "media/gpu/macros.h"
- #include "media/gpu/vaapi/vaapi_utils.h"
- #include "media/gpu/vaapi/vaapi_wrapper.h"
- #include "media/parsers/jpeg_parser.h"
- #include "ui/gfx/geometry/size.h"
- namespace media {
- namespace {
- static void FillPictureParameters(
- const JpegFrameHeader& frame_header,
- VAPictureParameterBufferJPEGBaseline* pic_param) {
- pic_param->picture_width = frame_header.coded_width;
- pic_param->picture_height = frame_header.coded_height;
- pic_param->num_components = frame_header.num_components;
- for (int i = 0; i < pic_param->num_components; i++) {
- pic_param->components[i].component_id = frame_header.components[i].id;
- pic_param->components[i].h_sampling_factor =
- frame_header.components[i].horizontal_sampling_factor;
- pic_param->components[i].v_sampling_factor =
- frame_header.components[i].vertical_sampling_factor;
- pic_param->components[i].quantiser_table_selector =
- frame_header.components[i].quantization_table_selector;
- }
- }
- static void FillIQMatrix(const JpegQuantizationTable* q_table,
- VAIQMatrixBufferJPEGBaseline* iq_matrix) {
- static_assert(kJpegMaxQuantizationTableNum ==
- std::extent<decltype(iq_matrix->load_quantiser_table)>(),
- "max number of quantization table mismatched");
- static_assert(
- sizeof(iq_matrix->quantiser_table[0]) == sizeof(q_table[0].value),
- "number of quantization entries mismatched");
- for (size_t i = 0; i < kJpegMaxQuantizationTableNum; i++) {
- if (!q_table[i].valid)
- continue;
- iq_matrix->load_quantiser_table[i] = 1;
- for (size_t j = 0; j < std::size(q_table[i].value); j++)
- iq_matrix->quantiser_table[i][j] = q_table[i].value[j];
- }
- }
- static void FillHuffmanTable(const JpegHuffmanTable* dc_table,
- const JpegHuffmanTable* ac_table,
- VAHuffmanTableBufferJPEGBaseline* huffman_table) {
- // Use default huffman tables if not specified in header.
- bool has_huffman_table = false;
- for (size_t i = 0; i < kJpegMaxHuffmanTableNumBaseline; i++) {
- if (dc_table[i].valid || ac_table[i].valid) {
- has_huffman_table = true;
- break;
- }
- }
- if (!has_huffman_table) {
- dc_table = kDefaultDcTable;
- ac_table = kDefaultAcTable;
- }
- static_assert(kJpegMaxHuffmanTableNumBaseline ==
- std::extent<decltype(huffman_table->load_huffman_table)>(),
- "max number of huffman table mismatched");
- static_assert(sizeof(huffman_table->huffman_table[0].num_dc_codes) ==
- sizeof(dc_table[0].code_length),
- "size of huffman table code length mismatch");
- static_assert(sizeof(huffman_table->huffman_table[0].dc_values[0]) ==
- sizeof(dc_table[0].code_value[0]),
- "size of huffman table code value mismatch");
- for (size_t i = 0; i < kJpegMaxHuffmanTableNumBaseline; i++) {
- if (!dc_table[i].valid || !ac_table[i].valid)
- continue;
- huffman_table->load_huffman_table[i] = 1;
- memcpy(huffman_table->huffman_table[i].num_dc_codes,
- dc_table[i].code_length,
- sizeof(huffman_table->huffman_table[i].num_dc_codes));
- memcpy(huffman_table->huffman_table[i].dc_values, dc_table[i].code_value,
- sizeof(huffman_table->huffman_table[i].dc_values));
- memcpy(huffman_table->huffman_table[i].num_ac_codes,
- ac_table[i].code_length,
- sizeof(huffman_table->huffman_table[i].num_ac_codes));
- memcpy(huffman_table->huffman_table[i].ac_values, ac_table[i].code_value,
- sizeof(huffman_table->huffman_table[i].ac_values));
- }
- }
- static void FillSliceParameters(
- const JpegParseResult& parse_result,
- VASliceParameterBufferJPEGBaseline* slice_param) {
- slice_param->slice_data_size = parse_result.data_size;
- slice_param->slice_data_offset = 0;
- slice_param->slice_data_flag = VA_SLICE_DATA_FLAG_ALL;
- slice_param->slice_horizontal_position = 0;
- slice_param->slice_vertical_position = 0;
- slice_param->num_components = parse_result.scan.num_components;
- for (int i = 0; i < slice_param->num_components; i++) {
- slice_param->components[i].component_selector =
- parse_result.scan.components[i].component_selector;
- slice_param->components[i].dc_table_selector =
- parse_result.scan.components[i].dc_selector;
- slice_param->components[i].ac_table_selector =
- parse_result.scan.components[i].ac_selector;
- }
- slice_param->restart_interval = parse_result.restart_interval;
- // Cast to int to prevent overflow.
- int max_h_factor =
- parse_result.frame_header.components[0].horizontal_sampling_factor;
- int max_v_factor =
- parse_result.frame_header.components[0].vertical_sampling_factor;
- int mcu_cols = parse_result.frame_header.coded_width / (max_h_factor * 8);
- DCHECK_GT(mcu_cols, 0);
- int mcu_rows = parse_result.frame_header.coded_height / (max_v_factor * 8);
- DCHECK_GT(mcu_rows, 0);
- slice_param->num_mcus = mcu_rows * mcu_cols;
- }
- // VAAPI only supports a subset of JPEG profiles. This function determines
- // whether a given parsed JPEG result is supported or not.
- static bool IsVaapiSupportedJpeg(const JpegParseResult& jpeg) {
- // Make sure the JPEG's chroma subsampling format is supported.
- if (!VaapiWrapper::IsDecodingSupportedForInternalFormat(
- VAProfileJPEGBaseline, VaSurfaceFormatForJpeg(jpeg.frame_header))) {
- DLOG(ERROR) << "The JPEG's subsampling format is unsupported";
- return false;
- }
- // Validate the visible size.
- if (jpeg.frame_header.visible_width == 0u) {
- DLOG(ERROR) << "Visible width can't be zero";
- return false;
- }
- if (jpeg.frame_header.visible_height == 0u) {
- DLOG(ERROR) << "Visible height can't be zero";
- return false;
- }
- // Validate the coded size.
- gfx::Size min_jpeg_resolution;
- gfx::Size max_jpeg_resolution;
- if (!VaapiWrapper::GetSupportedResolutions(
- VAProfileJPEGBaseline, VaapiWrapper::CodecMode::kDecode,
- min_jpeg_resolution, max_jpeg_resolution)) {
- DLOG(ERROR) << "Could not get the minimum and maximum resolutions";
- return false;
- }
- const int actual_jpeg_coded_width =
- base::strict_cast<int>(jpeg.frame_header.coded_width);
- const int actual_jpeg_coded_height =
- base::strict_cast<int>(jpeg.frame_header.coded_height);
- if (actual_jpeg_coded_width < min_jpeg_resolution.width() ||
- actual_jpeg_coded_height < min_jpeg_resolution.height() ||
- actual_jpeg_coded_width > max_jpeg_resolution.width() ||
- actual_jpeg_coded_height > max_jpeg_resolution.height()) {
- DLOG(ERROR) << "VAAPI doesn't support size " << actual_jpeg_coded_width
- << "x" << actual_jpeg_coded_height << ": not in range "
- << min_jpeg_resolution.ToString() << " - "
- << max_jpeg_resolution.ToString();
- return false;
- }
- return true;
- }
- } // namespace
- unsigned int VaSurfaceFormatForJpeg(const JpegFrameHeader& frame_header) {
- if (frame_header.num_components != 3)
- return kInvalidVaRtFormat;
- const uint8_t y_h = frame_header.components[0].horizontal_sampling_factor;
- const uint8_t y_v = frame_header.components[0].vertical_sampling_factor;
- const uint8_t u_h = frame_header.components[1].horizontal_sampling_factor;
- const uint8_t u_v = frame_header.components[1].vertical_sampling_factor;
- const uint8_t v_h = frame_header.components[2].horizontal_sampling_factor;
- const uint8_t v_v = frame_header.components[2].vertical_sampling_factor;
- if (u_h != 1 || u_v != 1 || v_h != 1 || v_v != 1)
- return kInvalidVaRtFormat;
- if (y_h == 2 && y_v == 2)
- return VA_RT_FORMAT_YUV420;
- else if (y_h == 2 && y_v == 1)
- return VA_RT_FORMAT_YUV422;
- else if (y_h == 1 && y_v == 1)
- return VA_RT_FORMAT_YUV444;
- return kInvalidVaRtFormat;
- }
- VaapiJpegDecoder::VaapiJpegDecoder()
- : VaapiImageDecoder(VAProfileJPEGBaseline) {}
- VaapiJpegDecoder::~VaapiJpegDecoder() = default;
- VaapiImageDecodeStatus VaapiJpegDecoder::AllocateVASurfaceAndSubmitVABuffers(
- base::span<const uint8_t> encoded_image) {
- DCHECK(vaapi_wrapper_);
- // Parse the JPEG encoded data.
- JpegParseResult parse_result;
- if (!ParseJpegPicture(encoded_image.data(), encoded_image.size(),
- &parse_result)) {
- VLOGF(1) << "ParseJpegPicture failed";
- return VaapiImageDecodeStatus::kParseFailed;
- }
- // Figure out the right format for the VaSurface.
- const unsigned int picture_va_rt_format =
- VaSurfaceFormatForJpeg(parse_result.frame_header);
- if (picture_va_rt_format == kInvalidVaRtFormat) {
- VLOGF(1) << "Unsupported subsampling";
- return VaapiImageDecodeStatus::kUnsupportedSubsampling;
- }
- // Make sure this JPEG can be decoded.
- if (!IsVaapiSupportedJpeg(parse_result)) {
- VLOGF(1) << "The supplied JPEG is unsupported";
- return VaapiImageDecodeStatus::kUnsupportedImage;
- }
- // Prepare the surface for decoding.
- const gfx::Size new_visible_size(
- base::strict_cast<int>(parse_result.frame_header.visible_width),
- base::strict_cast<int>(parse_result.frame_header.visible_height));
- const gfx::Size new_coded_size(
- base::strict_cast<int>(parse_result.frame_header.coded_width),
- base::strict_cast<int>(parse_result.frame_header.coded_height));
- if (!MaybeCreateSurface(picture_va_rt_format, new_coded_size,
- new_visible_size)) {
- return VaapiImageDecodeStatus::kSurfaceCreationFailed;
- }
- // Submit input buffers.
- if (!SubmitBuffers(parse_result))
- return VaapiImageDecodeStatus::kSubmitVABuffersFailed;
- return VaapiImageDecodeStatus::kSuccess;
- }
- gpu::ImageDecodeAcceleratorType VaapiJpegDecoder::GetType() const {
- return gpu::ImageDecodeAcceleratorType::kJpeg;
- }
- SkYUVColorSpace VaapiJpegDecoder::GetYUVColorSpace() const {
- return SkYUVColorSpace::kJPEG_SkYUVColorSpace;
- }
- std::unique_ptr<ScopedVAImage> VaapiJpegDecoder::GetImage(
- uint32_t preferred_image_fourcc,
- VaapiImageDecodeStatus* status) {
- if (!scoped_va_context_and_surface_) {
- VLOGF(1) << "No decoded JPEG available";
- *status = VaapiImageDecodeStatus::kInvalidState;
- return nullptr;
- }
- DCHECK(scoped_va_context_and_surface_->IsValid());
- DCHECK(vaapi_wrapper_);
- uint32_t image_fourcc;
- if (!VaapiWrapper::GetJpegDecodeSuitableImageFourCC(
- scoped_va_context_and_surface_->format(), preferred_image_fourcc,
- &image_fourcc)) {
- VLOGF(1) << "Cannot determine the output FOURCC";
- *status = VaapiImageDecodeStatus::kCannotGetImage;
- return nullptr;
- }
- VAImageFormat image_format{.fourcc = image_fourcc};
- // In at least one driver, the VPP seems to have problems if we request a
- // VAImage with odd dimensions. Rather than debugging the issue in depth, we
- // disable support for odd dimensions since the VAImage path is only expected
- // to be used in camera captures (and we don't expect JPEGs with odd
- // dimensions in that path).
- if ((scoped_va_context_and_surface_->size().width() & 1) ||
- (scoped_va_context_and_surface_->size().height() & 1)) {
- VLOGF(1) << "Getting images with odd dimensions is not supported";
- *status = VaapiImageDecodeStatus::kCannotGetImage;
- NOTREACHED();
- return nullptr;
- }
- auto scoped_image = vaapi_wrapper_->CreateVaImage(
- scoped_va_context_and_surface_->id(), &image_format,
- scoped_va_context_and_surface_->size());
- if (!scoped_image) {
- VLOGF(1) << "Cannot get VAImage, FOURCC = "
- << FourccToString(image_format.fourcc);
- *status = VaapiImageDecodeStatus::kCannotGetImage;
- return nullptr;
- }
- *status = VaapiImageDecodeStatus::kSuccess;
- return scoped_image;
- }
- bool VaapiJpegDecoder::MaybeCreateSurface(unsigned int picture_va_rt_format,
- const gfx::Size& new_coded_size,
- const gfx::Size& new_visible_size) {
- DCHECK(!scoped_va_context_and_surface_ ||
- scoped_va_context_and_surface_->IsValid());
- if (scoped_va_context_and_surface_ &&
- new_visible_size == scoped_va_context_and_surface_->size() &&
- picture_va_rt_format == scoped_va_context_and_surface_->format()) {
- // No need to allocate a new surface. We can re-use the current one.
- return true;
- }
- scoped_va_context_and_surface_.reset();
- // We'll request a surface of |new_coded_size| from the VAAPI, but we will
- // keep track of the |new_visible_size| inside the ScopedVASurface so that
- // when we create a VAImage or export the surface as a NativePixmapDmaBuf, we
- // can report the size that clients should be using to read the contents.
- auto scoped_va_surfaces = vaapi_wrapper_->CreateContextAndScopedVASurfaces(
- picture_va_rt_format, new_coded_size,
- {VaapiWrapper::SurfaceUsageHint::kGeneric}, 1u, new_visible_size);
- if (scoped_va_surfaces.empty()) {
- VLOGF(1) << "CreateContextAndScopedVASurface() failed";
- return false;
- }
- scoped_va_context_and_surface_.reset(scoped_va_surfaces[0].release());
- DCHECK(scoped_va_context_and_surface_->IsValid());
- return true;
- }
- bool VaapiJpegDecoder::SubmitBuffers(const JpegParseResult& parse_result) {
- // Set picture parameters.
- VAPictureParameterBufferJPEGBaseline pic_param{};
- FillPictureParameters(parse_result.frame_header, &pic_param);
- // Set quantization table.
- VAIQMatrixBufferJPEGBaseline iq_matrix{};
- FillIQMatrix(parse_result.q_table, &iq_matrix);
- // Set huffman table.
- VAHuffmanTableBufferJPEGBaseline huffman_table{};
- FillHuffmanTable(parse_result.dc_table, parse_result.ac_table,
- &huffman_table);
- // Set slice parameters.
- VASliceParameterBufferJPEGBaseline slice_param{};
- FillSliceParameters(parse_result, &slice_param);
- return vaapi_wrapper_->SubmitBuffers(
- {{VAPictureParameterBufferType, sizeof(pic_param), &pic_param},
- {VAIQMatrixBufferType, sizeof(iq_matrix), &iq_matrix},
- {VAHuffmanTableBufferType, sizeof(huffman_table), &huffman_table},
- {VASliceParameterBufferType, sizeof(slice_param), &slice_param},
- {VASliceDataBufferType, parse_result.data_size,
- const_cast<char*>(parse_result.data)}});
- }
- } // namespace media
|