| // 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 |