blob: fe191b814d23649ba953b3cbd09f262bbf0ad43d [file] [log] [blame]
// Copyright 2022 The Fuchsia 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 "mjpeg_accelerator.h"
#include <safemath/safe_conversions.h>
#include <va/va.h>
#include "codec_adapter_vaapi_decoder.h"
#include "vaapi_utils.h"
VaapiJpegPicture::VaapiJpegPicture(std::shared_ptr<VASurface> va_surface)
: va_surface_(va_surface) {}
VaapiJpegPicture::~VaapiJpegPicture() = default;
MJPEGAccelerator::MJPEGAccelerator(CodecAdapterVaApiDecoder* adapter) : adapter_(adapter) {
FX_DCHECK(adapter_);
}
MJPEGAccelerator::~MJPEGAccelerator() = default;
// Set picture parameters.
std::shared_ptr<media::JPEGPicture> MJPEGAccelerator::CreateJPEGPicture() {
auto surface = adapter_->GetVASurface();
auto surface_ptr = std::make_shared<VaapiJpegPicture>(surface);
return surface_ptr;
}
MJPEGAccelerator::Status MJPEGAccelerator::SubmitDecode(
std::shared_ptr<media::JPEGPicture> picture, const media::JpegParseResult& parse_result) {
// Populate the picture parameters
VAPictureParameterBufferJPEGBaseline pic_param{};
PopulatePictureParameterBuffer(picture->frame_header(), pic_param);
// Populate the IQ matrix
VAIQMatrixBufferJPEGBaseline matrix_buffer{};
PopulateIQMatrix(parse_result.q_table, matrix_buffer);
// Populate huffman table
VAHuffmanTableBufferJPEGBaseline huffman_table{};
PopulateHuffmanTable(parse_result.dc_table, parse_result.ac_table, huffman_table);
// Populate slice parameters
VASliceParameterBufferJPEGBaseline slice_param{};
PopulateSliceParameters(parse_result, slice_param);
VAStatus status;
VADisplay display = VADisplayWrapper::GetSingleton()->display();
VABufferID pic_params_buffer_id;
status = vaCreateBuffer(display, adapter_->context_id(), VAPictureParameterBufferType,
sizeof(pic_param), 1, &pic_param, &pic_params_buffer_id);
if (status != VA_STATUS_SUCCESS) {
FX_SLOG(ERROR, "vaCreateBuffer for pic_param failed", FX_KV("error_str", vaErrorStr(status)));
return Status::kFail;
}
ScopedBufferID pic_params_buffer(pic_params_buffer_id);
VABufferID iq_matrix_buffer_id;
status = vaCreateBuffer(display, adapter_->context_id(), VAIQMatrixBufferType,
sizeof(matrix_buffer), 1, &matrix_buffer, &iq_matrix_buffer_id);
if (status != VA_STATUS_SUCCESS) {
FX_SLOG(ERROR, "vaCreateBuffer for matrix_buffer failed",
FX_KV("error_str", vaErrorStr(status)));
return Status::kFail;
}
ScopedBufferID iq_matrix_buffer(iq_matrix_buffer_id);
VABufferID huffman_table_buffer_id;
status = vaCreateBuffer(display, adapter_->context_id(), VAHuffmanTableBufferType,
sizeof(huffman_table), 1, &huffman_table, &huffman_table_buffer_id);
if (status != VA_STATUS_SUCCESS) {
FX_SLOG(ERROR, "vaCreateBuffer for huffman_table failed",
FX_KV("error_str", vaErrorStr(status)));
return Status::kFail;
}
ScopedBufferID huffman_table_buffer(huffman_table_buffer_id);
VABufferID slice_param_buffer_id;
status = vaCreateBuffer(display, adapter_->context_id(), VASliceParameterBufferType,
sizeof(slice_param), 1, &slice_param, &slice_param_buffer_id);
if (status != VA_STATUS_SUCCESS) {
FX_SLOG(ERROR, "vaCreateBuffer for slice_param failed", FX_KV("error_str", vaErrorStr(status)));
return Status::kFail;
}
ScopedBufferID slice_param_buffer(slice_param_buffer_id);
VABufferID jpeg_data_buffer_id;
status = vaCreateBuffer(display, adapter_->context_id(), VASliceDataBufferType,
static_cast<unsigned int>(parse_result.data_size), 1,
reinterpret_cast<uint8_t*>(const_cast<char*>(parse_result.data)),
&jpeg_data_buffer_id);
if (status != VA_STATUS_SUCCESS) {
FX_SLOG(ERROR, "vaCreateBuffer for jpeg_data_buffer_id failed",
FX_KV("error_str", vaErrorStr(status)));
return Status::kFail;
}
ScopedBufferID jpeg_data_buffer(jpeg_data_buffer_id);
auto va_surface_id = static_cast<VaapiJpegPicture*>(picture.get())->GetVASurfaceID();
status = vaBeginPicture(display, adapter_->context_id(), va_surface_id);
if (status != VA_STATUS_SUCCESS) {
FX_SLOG(ERROR, "BeginPicture failed", FX_KV("error_str", vaErrorStr(status)));
return Status::kFail;
}
std::vector<VABufferID> buffers{pic_params_buffer.id(), iq_matrix_buffer.id(),
huffman_table_buffer.id(), slice_param_buffer.id(),
jpeg_data_buffer.id()};
status = vaRenderPicture(display, adapter_->context_id(), buffers.data(),
static_cast<int>(buffers.size()));
if (status != VA_STATUS_SUCCESS) {
FX_SLOG(ERROR, "RenderPicture failed", FX_KV("error_str", vaErrorStr(status)));
return Status::kFail;
}
status = vaEndPicture(display, adapter_->context_id());
if (status != VA_STATUS_SUCCESS) {
FX_SLOG(ERROR, "EndPicture failed", FX_KV("error_str", vaErrorStr(status)));
return Status::kFail;
}
return Status::kOk;
}
bool MJPEGAccelerator::OutputPicture(std::shared_ptr<media::JPEGPicture> picture) {
auto va_surface = static_cast<VaapiJpegPicture*>(picture.get())->va_surface();
return adapter_->ProcessOutput(va_surface, picture->bitstream_id());
}
void MJPEGAccelerator::PopulatePictureParameterBuffer(
const media::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 component_idx = 0; component_idx < pic_param.num_components; component_idx += 1) {
const auto& header_comp = frame_header.components[component_idx];
auto& pic_comp = pic_param.components[component_idx];
pic_comp.component_id = header_comp.id;
pic_comp.h_sampling_factor = header_comp.horizontal_sampling_factor;
pic_comp.v_sampling_factor = header_comp.vertical_sampling_factor;
pic_comp.quantiser_table_selector = header_comp.quantization_table_selector;
}
}
void MJPEGAccelerator::PopulateIQMatrix(
const media::JpegQuantizationTable q_table[media::kJpegMaxQuantizationTableNum],
VAIQMatrixBufferJPEGBaseline& matrix_buffer) {
static_assert(media::kJpegMaxQuantizationTableNum ==
std::extent<decltype(matrix_buffer.load_quantiser_table)>(),
"max number of quantization table mismatched");
static_assert(sizeof(matrix_buffer.quantiser_table[0]) == sizeof(q_table[0].value),
"number of quantization entries mismatched");
for (size_t i = 0; i < media::kJpegMaxQuantizationTableNum; i++) {
if (!q_table[i].valid) {
continue;
}
matrix_buffer.load_quantiser_table[i] = 1;
for (size_t j = 0; j < std::size(q_table[i].value); j++) {
matrix_buffer.quantiser_table[i][j] = q_table[i].value[j];
}
}
}
void MJPEGAccelerator::PopulateHuffmanTable(
const media::JpegHuffmanTable dc_table[media::kJpegMaxHuffmanTableNumBaseline],
const media::JpegHuffmanTable ac_table[media::kJpegMaxHuffmanTableNumBaseline],
VAHuffmanTableBufferJPEGBaseline& huffman_table) {
// Use default huffman tables if not specified in header.
bool has_huffman_table = false;
for (size_t i = 0; i < media::kJpegMaxHuffmanTableNumBaseline; i++) {
if (dc_table[i].valid || ac_table[i].valid) {
has_huffman_table = true;
break;
}
}
if (!has_huffman_table) {
dc_table = media::kDefaultDcTable;
ac_table = media::kDefaultAcTable;
}
static_assert(media::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 < media::kJpegMaxHuffmanTableNumBaseline; i++) {
if (!dc_table[i].valid || !ac_table[i].valid) {
continue;
}
huffman_table.load_huffman_table[i] = 1;
std::memcpy(huffman_table.huffman_table[i].num_dc_codes, dc_table[i].code_length,
sizeof(huffman_table.huffman_table[i].num_dc_codes));
std::memcpy(huffman_table.huffman_table[i].dc_values, dc_table[i].code_value,
sizeof(huffman_table.huffman_table[i].dc_values));
std::memcpy(huffman_table.huffman_table[i].num_ac_codes, ac_table[i].code_length,
sizeof(huffman_table.huffman_table[i].num_ac_codes));
std::memcpy(huffman_table.huffman_table[i].ac_values, ac_table[i].code_value,
sizeof(huffman_table.huffman_table[i].ac_values));
}
}
void MJPEGAccelerator::PopulateSliceParameters(const media::JpegParseResult& parse_result,
VASliceParameterBufferJPEGBaseline& slice_param) {
slice_param.slice_data_size = safemath::checked_cast<uint32_t>(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 = safemath::strict_cast<int>(
parse_result.frame_header.components[0].horizontal_sampling_factor);
int max_v_factor =
safemath::strict_cast<int>(parse_result.frame_header.components[0].vertical_sampling_factor);
int mcu_cols =
safemath::strict_cast<int>(parse_result.frame_header.coded_width) / (max_h_factor * 8);
int mcu_rows =
safemath::strict_cast<int>(parse_result.frame_header.coded_height) / (max_v_factor * 8);
FX_DCHECK(mcu_cols > 0);
FX_DCHECK(mcu_rows > 0);
slice_param.num_mcus = mcu_rows * mcu_cols;
}