blob: a7c96db4dce4648763fd1268ee1c3ad7c75c289c [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_decoder.h"
#include <lib/fit/defer.h>
#include <lib/syslog/cpp/macros.h>
#include <zircon/assert.h>
#include <memory>
#include <va/va.h>
#include "geometry.h"
namespace media {
MJPEGDecoder::MJPEGAccelerator::MJPEGAccelerator() = default;
MJPEGDecoder::MJPEGAccelerator::~MJPEGAccelerator() = default;
MJPEGDecoder::MJPEGDecoder(std::unique_ptr<MJPEGAccelerator> accelerator)
: accelerator_(std::move(accelerator)) {}
MJPEGDecoder::~MJPEGDecoder() = default;
void MJPEGDecoder::SetStream(int32_t id, const DecoderBuffer& decoder_buffer) {
stream_id_ = id;
stream_data_ = decoder_buffer.data();
stream_bytes_left_ = decoder_buffer.data_size();
FX_DCHECK(stream_data_);
FX_DCHECK(stream_bytes_left_);
}
bool MJPEGDecoder::Flush() {
Reset();
return true;
}
void MJPEGDecoder::Reset() { pending_parse_result_.reset(); }
MJPEGDecoder::DecodeResult MJPEGDecoder::Decode() {
if (!pending_parse_result_.has_value()) {
JpegParseResult parse_result;
if (!ParseJpegPicture(stream_data_, stream_bytes_left_, &parse_result)) {
FX_SLOG(WARNING, "ParseJpegPicture failed");
return kDecodeError;
}
pending_parse_result_ = std::move(parse_result);
}
ZX_ASSERT(pending_parse_result_.has_value());
auto clean_pending_result = fit::defer([this] { pending_parse_result_.reset(); });
const gfx::Size new_coded_size(
safemath::strict_cast<int>(pending_parse_result_->frame_header.coded_width),
safemath::strict_cast<int>(pending_parse_result_->frame_header.coded_height));
const gfx::Rect new_visible_size(
safemath::strict_cast<int>(pending_parse_result_->frame_header.visible_width),
safemath::strict_cast<int>(pending_parse_result_->frame_header.visible_height));
// TODO(https://fxbug.dev/42073234): Currently we only support YUV420
if (VaFormatFromFrameHeader(pending_parse_result_->frame_header) != VA_RT_FORMAT_YUV420) {
return kDecodeError;
}
// Alert callee to a configuration change
if ((pic_size_ != new_coded_size) || (visible_rect_ != new_visible_size)) {
pic_size_ = new_coded_size;
visible_rect_ = new_visible_size;
// We won't output the frame this call, so save the pending parse result for the next |Decode()|
// call.
clean_pending_result.cancel();
return kConfigChange;
}
std::shared_ptr<JPEGPicture> pic = accelerator_->CreateJPEGPicture();
if (!pic) {
return kRanOutOfSurfaces;
}
pic->set_frame_header(pending_parse_result_->frame_header);
pic->set_visible_rect(new_visible_size);
pic->set_bitstream_id(stream_id_);
auto status = accelerator_->SubmitDecode(pic, pending_parse_result_.value());
if (status != MJPEGAccelerator::Status::kOk) {
return kDecodeError;
}
if (!accelerator_->OutputPicture(pic)) {
return kDecodeError;
}
// TODO(https://fxbug.dev/42073235): Right now we limit the caller of stream processor to only submit one
// JPEG encoded frame at a time. We could update this implementation to allow the client to submit
// fractional or multiple frames per buffer.
return kRanOutOfStreamData;
}
gfx::Size MJPEGDecoder::GetPicSize() const { return pic_size_; }
gfx::Rect MJPEGDecoder::GetVisibleRect() const { return visible_rect_; }
VideoCodecProfile MJPEGDecoder::GetProfile() const {
// TODO(https://fxbug.dev/42073236): Fix this
return VIDEO_CODEC_PROFILE_UNKNOWN;
}
uint8_t MJPEGDecoder::GetBitDepth() const { return 8; }
size_t MJPEGDecoder::GetRequiredNumOfPictures() const { return 1; }
size_t MJPEGDecoder::GetNumReferenceFrames() const { return 0; }
bool MJPEGDecoder::IsCurrentFrameKeyframe() const { return true; }
uint32_t MJPEGDecoder::VaFormatFromFrameHeader(const JpegFrameHeader& frame_header) {
// There should be three components: 1 for grayscale and two for color.
if (frame_header.num_components != 3u) {
return kInvalidVaFormat;
}
const uint8_t y_plane_hori = frame_header.components[0u].horizontal_sampling_factor;
const uint8_t y_plane_vert = frame_header.components[0u].vertical_sampling_factor;
const uint8_t u_plane_hori = frame_header.components[1u].horizontal_sampling_factor;
const uint8_t u_plane_vert = frame_header.components[1u].vertical_sampling_factor;
const uint8_t v_plane_hori = frame_header.components[2u].horizontal_sampling_factor;
const uint8_t v_plane_vert = frame_header.components[2u].vertical_sampling_factor;
if ((u_plane_hori != 1u) || (u_plane_vert != 1u) || (v_plane_hori != 1u) ||
(v_plane_vert != 1u)) {
return kInvalidVaFormat;
}
if ((y_plane_hori == 2) && (y_plane_vert == 2)) {
return VA_RT_FORMAT_YUV420;
} else if ((y_plane_hori == 2) && (y_plane_vert == 1)) {
return VA_RT_FORMAT_YUV422;
} else if ((y_plane_hori == 1) && (y_plane_vert == 1)) {
return VA_RT_FORMAT_YUV444;
}
return kInvalidVaFormat;
}
} // namespace media