Browse Source

[video_core] clean up Android MediaCodec NVDEC support (#4154)

THIS MAY BE BLOBBED WITH TWO PREVIOUS ONES!
(git reset --mixed head~3, commit, profit)

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4154
lizzie/mediatek-stuff-for-fun
xbzk 4 weeks ago
committed by crueter
parent
commit
b96701ef71
  1. 12
      src/video_core/host1x/codecs/decoder.cpp
  2. 9
      src/video_core/host1x/codecs/decoder.h
  3. 14
      src/video_core/host1x/codecs/h264.cpp
  4. 1
      src/video_core/host1x/codecs/h264.h
  5. 1
      src/video_core/host1x/codecs/vp8.cpp
  6. 1
      src/video_core/host1x/codecs/vp9.cpp
  7. 50
      src/video_core/host1x/ffmpeg.cpp
  8. 3
      src/video_core/host1x/ffmpeg.h
  9. 11
      src/video_core/host1x/vic.cpp
  10. 1
      src/video_core/host1x/vic.h

12
src/video_core/host1x/codecs/decoder.cpp

@ -20,6 +20,14 @@ Decoder::Decoder(Host1x::Host1x& host1x_, s32 id_, const Host1x::NvdecCommon::Nv
Decoder::~Decoder() = default; Decoder::~Decoder() = default;
void Decoder::SetFrameDimensions(s32 width, s32 height) {
if (width <= 0 || height <= 0) {
frame_dimensions.reset();
return;
}
frame_dimensions = FFmpeg::FrameDimensions{width, height};
}
void Decoder::Decode() { void Decoder::Decode() {
if (!initialized) { if (!initialized) {
return; return;
@ -30,10 +38,10 @@ void Decoder::Decode() {
offsets.hidden = vp9_hidden_frame; offsets.hidden = vp9_hidden_frame;
offsets.interlaced = IsInterlaced(); offsets.interlaced = IsInterlaced();
if (offsets.interlaced) { if (offsets.interlaced) {
std::tie(offsets.luma, offsets.luma_bottom, offsets.chroma, offsets.chroma_bottom) =
std::tie(offsets.luma, offsets.luma_bottom, std::ignore, std::ignore) =
GetInterlacedOffsets(); GetInterlacedOffsets();
} else { } else {
std::tie(offsets.luma, offsets.chroma) = GetProgressiveOffsets();
std::tie(offsets.luma, std::ignore) = GetProgressiveOffsets();
} }
if (!decode_api.SendPacket(packet_data, offsets, GetFrameDimensions())) { if (!decode_api.SendPacket(packet_data, offsets, GetFrameDimensions())) {

9
src/video_core/host1x/codecs/decoder.h

@ -10,6 +10,7 @@
#include <mutex> #include <mutex>
#include <optional> #include <optional>
#include <string_view> #include <string_view>
#include <tuple>
#include <ankerl/unordered_dense.h> #include <ankerl/unordered_dense.h>
#include <queue> #include <queue>
@ -45,8 +46,11 @@ protected:
virtual std::tuple<u64, u64> GetProgressiveOffsets() = 0; virtual std::tuple<u64, u64> GetProgressiveOffsets() = 0;
virtual std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() = 0; virtual std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() = 0;
virtual bool IsInterlaced() = 0; virtual bool IsInterlaced() = 0;
virtual std::optional<FFmpeg::FrameDimensions> GetFrameDimensions() {
return std::nullopt;
void SetFrameDimensions(s32 width, s32 height);
std::optional<FFmpeg::FrameDimensions> GetFrameDimensions() const {
return frame_dimensions;
} }
FFmpeg::DecodeApi decode_api; FFmpeg::DecodeApi decode_api;
@ -55,6 +59,7 @@ protected:
s32 id; s32 id;
bool initialized : 1 = false; bool initialized : 1 = false;
bool vp9_hidden_frame : 1 = false; bool vp9_hidden_frame : 1 = false;
std::optional<FFmpeg::FrameDimensions> frame_dimensions;
}; };
} // namespace Tegra } // namespace Tegra

14
src/video_core/host1x/codecs/h264.cpp

@ -50,18 +50,12 @@ bool H264::IsInterlaced() {
current_context.h264_parameter_set.luma_bot_offset.Address() != 0; current_context.h264_parameter_set.luma_bot_offset.Address() != 0;
} }
std::optional<FFmpeg::FrameDimensions> H264::GetFrameDimensions() {
const auto& params = current_context.h264_parameter_set;
const s32 width = static_cast<s32>(params.pic_width_in_mbs) * 16;
const s32 height = static_cast<s32>(params.frame_height_in_mbs) * 16;
if (width <= 0 || height <= 0) {
return std::nullopt;
}
return FFmpeg::FrameDimensions{width, height};
}
std::span<const u8> H264::ComposeFrame() { std::span<const u8> H264::ComposeFrame() {
host1x.gmmu_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context, sizeof(H264DecoderContext)); host1x.gmmu_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context, sizeof(H264DecoderContext));
const auto& params = current_context.h264_parameter_set;
SetFrameDimensions(static_cast<s32>(params.pic_width_in_mbs) * 16,
static_cast<s32>(params.frame_height_in_mbs) * 16);
const s64 frame_number = current_context.h264_parameter_set.frame_number.Value(); const s64 frame_number = current_context.h264_parameter_set.frame_number.Value();
if (!is_first_frame && frame_number != 0) { if (!is_first_frame && frame_number != 0) {
frame_scratch.resize_destructive(current_context.stream_len); frame_scratch.resize_destructive(current_context.stream_len);

1
src/video_core/host1x/codecs/h264.h

@ -79,7 +79,6 @@ public:
std::tuple<u64, u64> GetProgressiveOffsets() override; std::tuple<u64, u64> GetProgressiveOffsets() override;
std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() override; std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() override;
bool IsInterlaced() override; bool IsInterlaced() override;
std::optional<FFmpeg::FrameDimensions> GetFrameDimensions() override;
std::string_view GetCurrentCodecName() const override { std::string_view GetCurrentCodecName() const override {
return "H264"; return "H264";

1
src/video_core/host1x/codecs/vp8.cpp

@ -35,6 +35,7 @@ std::tuple<u64, u64, u64, u64> VP8::GetInterlacedOffsets() {
std::span<const u8> VP8::ComposeFrame() { std::span<const u8> VP8::ComposeFrame() {
host1x.gmmu_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context, sizeof(VP8PictureInfo)); host1x.gmmu_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context, sizeof(VP8PictureInfo));
SetFrameDimensions(current_context.frame_width, current_context.frame_height);
const bool is_key_frame = current_context.key_frame == 1u; const bool is_key_frame = current_context.key_frame == 1u;
const auto bitstream_size = size_t(current_context.vld_buffer_size); const auto bitstream_size = size_t(current_context.vld_buffer_size);

1
src/video_core/host1x/codecs/vp9.cpp

@ -841,6 +841,7 @@ std::span<const u8> VP9::ComposeFrame() {
{ {
Vp9FrameContainer curr_frame = GetCurrentFrame(); Vp9FrameContainer curr_frame = GetCurrentFrame();
current_frame_info = curr_frame.info; current_frame_info = curr_frame.info;
SetFrameDimensions(current_frame_info.frame_size.width, current_frame_info.frame_size.height);
bitstream = std::move(curr_frame.bit_stream); bitstream = std::move(curr_frame.bit_stream);
} }
// The uncompressed header routine sets PrevProb parameters needed for the compressed header // The uncompressed header routine sets PrevProb parameters needed for the compressed header

50
src/video_core/host1x/ffmpeg.cpp

@ -5,6 +5,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring> #include <cstring>
#include <string>
#include <string_view> #include <string_view>
#include <vector> #include <vector>
@ -23,6 +24,7 @@ extern "C" {
#endif #endif
#include <libavutil/hwcontext.h> #include <libavutil/hwcontext.h>
#include <libavutil/log.h>
} }
namespace FFmpeg { namespace FFmpeg {
@ -88,8 +90,8 @@ std::string AVError(int errnum) {
} }
#ifdef __ANDROID__ #ifdef __ANDROID__
// Match a 3- or 4-byte annex-B start code at `i`. Returns its length, or 0.
size_t MatchStartCode(std::span<const u8> data, size_t i) {
// Match a 3- or 4-byte annex-B NAL start code at `i`. Returns its length, or 0.
size_t FindNalStartCode(std::span<const u8> data, size_t i) {
const size_t n = data.size(); const size_t n = data.size();
if (i + 3 < n && data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 0 && data[i + 3] == 1) { if (i + 3 < n && data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 0 && data[i + 3] == 1) {
return 4; return 4;
@ -103,12 +105,12 @@ size_t MatchStartCode(std::span<const u8> data, size_t i) {
// Pull SPS (NAL type 7) + PPS (NAL type 8) out of an annex-B frame into an // Pull SPS (NAL type 7) + PPS (NAL type 8) out of an annex-B frame into an
// extradata buffer, each prefixed with a 4-byte start code. Eden synthesizes // extradata buffer, each prefixed with a 4-byte start code. Eden synthesizes
// these inline into the very first frame; h264_mediacodec wants them at open. // these inline into the very first frame; h264_mediacodec wants them at open.
std::vector<u8> ExtractH264AnnexBExtradata(std::span<const u8> packet) {
std::vector<u8> ExtractH264ParameterSetExtradata(std::span<const u8> packet) {
std::vector<u8> extradata; std::vector<u8> extradata;
const size_t size = packet.size(); const size_t size = packet.size();
size_t i = 0; size_t i = 0;
while (i < size) { while (i < size) {
const size_t sc = MatchStartCode(packet, i);
const size_t sc = FindNalStartCode(packet, i);
if (sc == 0) { if (sc == 0) {
++i; ++i;
continue; continue;
@ -120,7 +122,7 @@ std::vector<u8> ExtractH264AnnexBExtradata(std::span<const u8> packet) {
const u8 nal_type = packet[nal_start] & 0x1F; const u8 nal_type = packet[nal_start] & 0x1F;
size_t j = nal_start + 1; size_t j = nal_start + 1;
while (j < size && MatchStartCode(packet, j) == 0) {
while (j < size && FindNalStartCode(packet, j) == 0) {
++j; ++j;
} }
@ -368,6 +370,7 @@ void DecodeApi::Reset() {
m_decoder_context.reset(); m_decoder_context.reset();
m_decoder.reset(); m_decoder.reset();
m_opened = false; m_opened = false;
m_defer_android_mediacodec_open = false;
m_needs_h264_extradata = false; m_needs_h264_extradata = false;
m_next_pts = 0; m_next_pts = 0;
while (!m_pending_offsets.empty()) { while (!m_pending_offsets.empty()) {
@ -389,11 +392,17 @@ bool DecodeApi::Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
} }
#ifdef __ANDROID__ #ifdef __ANDROID__
// h264_mediacodec needs SPS/PPS in extradata at open. We pull them from
// the first frame's bitstream in SendPacket.
m_needs_h264_extradata = m_decoder->GetCodec() &&
std::string_view(m_decoder->GetCodec()->name) == "h264_mediacodec";
if (m_needs_h264_extradata) {
const std::string_view decoder_name = m_decoder->GetCodec() ? m_decoder->GetCodec()->name : "";
// MediaCodec decoders need the frame dimensions before avcodec_open2().
m_defer_android_mediacodec_open = decoder_name == "h264_mediacodec" ||
decoder_name == "vp8_mediacodec" ||
decoder_name == "vp9_mediacodec";
// h264_mediacodec also needs SPS/PPS in extradata at open. We pull them
// from the first frame's bitstream in SendPacket.
m_needs_h264_extradata = decoder_name == "h264_mediacodec";
if (m_defer_android_mediacodec_open) {
return true; return true;
} }
#endif #endif
@ -412,18 +421,23 @@ bool DecodeApi::SendPacket(std::span<const u8> packet_data, const FrameOffsets&
if (!m_opened) { if (!m_opened) {
std::vector<u8> extradata; std::vector<u8> extradata;
#ifdef __ANDROID__ #ifdef __ANDROID__
if (m_defer_android_mediacodec_open) {
if (!dimensions) {
return true;
}
auto* ctx = m_decoder_context->GetCodecContext();
ctx->width = dimensions->width;
ctx->height = dimensions->height;
ctx->coded_width = dimensions->width;
ctx->coded_height = dimensions->height;
}
if (m_needs_h264_extradata) { if (m_needs_h264_extradata) {
extradata = ExtractH264AnnexBExtradata(packet_data);
extradata = ExtractH264ParameterSetExtradata(packet_data);
if (extradata.empty()) { if (extradata.empty()) {
return true; return true;
} }
if (dimensions) {
auto* ctx = m_decoder_context->GetCodecContext();
ctx->width = dimensions->width;
ctx->height = dimensions->height;
ctx->coded_width = dimensions->width;
ctx->coded_height = dimensions->height;
}
} }
#endif #endif
if (!m_decoder_context->OpenContext(*m_decoder, extradata)) { if (!m_decoder_context->OpenContext(*m_decoder, extradata)) {

3
src/video_core/host1x/ffmpeg.h

@ -202,9 +202,7 @@ struct FrameOffsets {
bool interlaced{}; bool interlaced{};
bool hidden{}; bool hidden{};
u64 luma{}; u64 luma{};
u64 chroma{};
u64 luma_bottom{}; u64 luma_bottom{};
u64 chroma_bottom{};
}; };
struct FrameDimensions { struct FrameDimensions {
@ -241,6 +239,7 @@ private:
std::optional<FFmpeg::DecoderContext> m_decoder_context; std::optional<FFmpeg::DecoderContext> m_decoder_context;
std::optional<FFmpeg::HardwareContext> m_hardware_context; std::optional<FFmpeg::HardwareContext> m_hardware_context;
bool m_opened{}; bool m_opened{};
bool m_defer_android_mediacodec_open{};
bool m_needs_h264_extradata{}; bool m_needs_h264_extradata{};
s64 m_next_pts{}; s64 m_next_pts{};
std::queue<FrameOffsets> m_pending_offsets; std::queue<FrameOffsets> m_pending_offsets;

11
src/video_core/host1x/vic.cpp

@ -118,6 +118,7 @@ void Vic::Execute() noexcept {
output_surface.resize(output_width * output_height); output_surface.resize(output_width * output_height);
if (Settings::values.nvdec_emulation.GetValue() != Settings::NvdecEmulation::Off) { if (Settings::values.nvdec_emulation.GetValue() != Settings::NvdecEmulation::Off) {
bool decoded_frame = false;
for (size_t i = 0; i < config.slot_structs.size(); i++) { for (size_t i = 0; i < config.slot_structs.size(); i++) {
if (auto& slot_config = config.slot_structs[i]; slot_config.config.slot_enable) { if (auto& slot_config = config.slot_structs[i]; slot_config.config.slot_enable) {
auto const luma_offset = regs.surfaces[i][SurfaceIndex::Current].luma.Address(); auto const luma_offset = regs.surfaces[i][SurfaceIndex::Current].luma.Address();
@ -136,11 +137,17 @@ void Vic::Execute() noexcept {
break; break;
} }
Blend(config, slot_config, config.output_surface_config.out_pixel_format); Blend(config, slot_config, config.output_surface_config.out_pixel_format);
} else if (nvdec_id != -1) {
LOG_ERROR(HW_GPU, "Vic {} failed to get frame with offset {:#X}", id, luma_offset);
decoded_frame = true;
} else {
LOG_TRACE(HW_GPU, "Vic {} failed to get frame with offset {:#X}", id, luma_offset);
} }
} }
} }
if (decoded_frame) {
has_decoded_frame = true;
} else if (!has_decoded_frame) {
return;
}
} else { } else {
// Fill the frame with black, as otherwise they can have random data and be very glitchy. // Fill the frame with black, as otherwise they can have random data and be very glitchy.
std::fill(output_surface.begin(), output_surface.end(), Pixel{}); std::fill(output_surface.begin(), output_surface.end(), Pixel{});

1
src/video_core/host1x/vic.h

@ -628,6 +628,7 @@ private:
s32 id; s32 id;
s32 nvdec_id{-1}; s32 nvdec_id{-1};
bool has_decoded_frame{};
u32 syncpoint; u32 syncpoint;
}; };

Loading…
Cancel
Save