aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/video_core/host1x/ffmpeg
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core/host1x/ffmpeg')
-rw-r--r--src/video_core/host1x/ffmpeg/ffmpeg.cpp233
-rw-r--r--src/video_core/host1x/ffmpeg/ffmpeg.h61
2 files changed, 114 insertions, 180 deletions
diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.cpp b/src/video_core/host1x/ffmpeg/ffmpeg.cpp
index 1003cd38d..d603bad8b 100644
--- a/src/video_core/host1x/ffmpeg/ffmpeg.cpp
+++ b/src/video_core/host1x/ffmpeg/ffmpeg.cpp
@@ -5,7 +5,9 @@
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "common/settings.h"
+#include "core/memory.h"
#include "video_core/host1x/ffmpeg/ffmpeg.h"
+#include "video_core/memory_manager.h"
extern "C" {
#ifdef LIBVA_FOUND
@@ -149,6 +151,7 @@ bool HardwareContext::InitializeForDecoder(DecoderContext& decoder_context,
}
}
+ LOG_INFO(HW_GPU, "Hardware decoding is disabled due to implementation issues, using CPU.");
return false;
}
@@ -183,8 +186,8 @@ bool HardwareContext::InitializeWithType(AVHWDeviceType type) {
return true;
}
-DecoderContext::DecoderContext(const Decoder& decoder) {
- m_codec_context = avcodec_alloc_context3(decoder.GetCodec());
+DecoderContext::DecoderContext(const Decoder& decoder) : m_decoder{decoder} {
+ m_codec_context = avcodec_alloc_context3(m_decoder.GetCodec());
av_opt_set(m_codec_context->priv_data, "tune", "zerolatency", 0);
m_codec_context->thread_count = 0;
m_codec_context->thread_type &= ~FF_THREAD_FRAME;
@@ -216,6 +219,25 @@ bool DecoderContext::OpenContext(const Decoder& decoder) {
}
bool DecoderContext::SendPacket(const Packet& packet) {
+ m_temp_frame = std::make_shared<Frame>();
+ m_got_frame = 0;
+
+// Android can randomly crash when calling decode directly, so skip.
+// TODO update ffmpeg and hope that fixes it.
+#ifndef ANDROID
+ if (!m_codec_context->hw_device_ctx && m_codec_context->codec_id == AV_CODEC_ID_H264) {
+ m_decode_order = true;
+ auto* codec{ffcodec(m_decoder.GetCodec())};
+ if (const int ret = codec->cb.decode(m_codec_context, m_temp_frame->GetFrame(),
+ &m_got_frame, packet.GetPacket());
+ ret < 0) {
+ LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", AVError(ret));
+ return false;
+ }
+ return true;
+ }
+#endif
+
if (const int ret = avcodec_send_packet(m_codec_context, packet.GetPacket()); ret < 0) {
LOG_ERROR(HW_GPU, "avcodec_send_packet error: {}", AVError(ret));
return false;
@@ -224,139 +246,73 @@ bool DecoderContext::SendPacket(const Packet& packet) {
return true;
}
-std::unique_ptr<Frame> DecoderContext::ReceiveFrame(bool* out_is_interlaced) {
- auto dst_frame = std::make_unique<Frame>();
-
- const auto ReceiveImpl = [&](AVFrame* frame) {
- if (const int ret = avcodec_receive_frame(m_codec_context, frame); ret < 0) {
- LOG_ERROR(HW_GPU, "avcodec_receive_frame error: {}", AVError(ret));
- return false;
+std::shared_ptr<Frame> DecoderContext::ReceiveFrame() {
+ // Android can randomly crash when calling decode directly, so skip.
+ // TODO update ffmpeg and hope that fixes it.
+#ifndef ANDROID
+ if (!m_codec_context->hw_device_ctx && m_codec_context->codec_id == AV_CODEC_ID_H264) {
+ m_decode_order = true;
+ auto* codec{ffcodec(m_decoder.GetCodec())};
+ int ret{0};
+
+ if (m_got_frame == 0) {
+ Packet packet{{}};
+ auto* pkt = packet.GetPacket();
+ pkt->data = nullptr;
+ pkt->size = 0;
+ ret = codec->cb.decode(m_codec_context, m_temp_frame->GetFrame(), &m_got_frame, pkt);
+ m_codec_context->has_b_frames = 0;
}
- *out_is_interlaced =
-#if defined(FF_API_INTERLACED_FRAME) || LIBAVUTIL_VERSION_MAJOR >= 59
- (frame->flags & AV_FRAME_FLAG_INTERLACED) != 0;
-#else
- frame->interlaced_frame != 0;
-#endif
- return true;
- };
-
- if (m_codec_context->hw_device_ctx) {
- // If we have a hardware context, make a separate frame here to receive the
- // hardware result before sending it to the output.
- Frame intermediate_frame;
-
- if (!ReceiveImpl(intermediate_frame.GetFrame())) {
+ if (m_got_frame == 0 || ret < 0) {
+ LOG_ERROR(Service_NVDRV, "Failed to receive a frame! error {}", ret);
return {};
}
+ } else
+#endif
+ {
- dst_frame->SetFormat(PreferredGpuFormat);
- if (const int ret =
- av_hwframe_transfer_data(dst_frame->GetFrame(), intermediate_frame.GetFrame(), 0);
- ret < 0) {
- LOG_ERROR(HW_GPU, "av_hwframe_transfer_data error: {}", AVError(ret));
- return {};
- }
- } else {
- // Otherwise, decode the frame as normal.
- if (!ReceiveImpl(dst_frame->GetFrame())) {
- return {};
- }
- }
-
- return dst_frame;
-}
-
-DeinterlaceFilter::DeinterlaceFilter(const Frame& frame) {
- const AVFilter* buffer_src = avfilter_get_by_name("buffer");
- const AVFilter* buffer_sink = avfilter_get_by_name("buffersink");
- AVFilterInOut* inputs = avfilter_inout_alloc();
- AVFilterInOut* outputs = avfilter_inout_alloc();
- SCOPE_EXIT {
- avfilter_inout_free(&inputs);
- avfilter_inout_free(&outputs);
- };
-
- // Don't know how to get the accurate time_base but it doesn't matter for yadif filter
- // so just use 1/1 to make buffer filter happy
- std::string args = fmt::format("video_size={}x{}:pix_fmt={}:time_base=1/1", frame.GetWidth(),
- frame.GetHeight(), static_cast<int>(frame.GetPixelFormat()));
-
- m_filter_graph = avfilter_graph_alloc();
- int ret = avfilter_graph_create_filter(&m_source_context, buffer_src, "in", args.c_str(),
- nullptr, m_filter_graph);
- if (ret < 0) {
- LOG_ERROR(HW_GPU, "avfilter_graph_create_filter source error: {}", AVError(ret));
- return;
- }
-
- ret = avfilter_graph_create_filter(&m_sink_context, buffer_sink, "out", nullptr, nullptr,
- m_filter_graph);
- if (ret < 0) {
- LOG_ERROR(HW_GPU, "avfilter_graph_create_filter sink error: {}", AVError(ret));
- return;
- }
-
- inputs->name = av_strdup("out");
- inputs->filter_ctx = m_sink_context;
- inputs->pad_idx = 0;
- inputs->next = nullptr;
-
- outputs->name = av_strdup("in");
- outputs->filter_ctx = m_source_context;
- outputs->pad_idx = 0;
- outputs->next = nullptr;
-
- const char* description = "yadif=1:-1:0";
- ret = avfilter_graph_parse_ptr(m_filter_graph, description, &inputs, &outputs, nullptr);
- if (ret < 0) {
- LOG_ERROR(HW_GPU, "avfilter_graph_parse_ptr error: {}", AVError(ret));
- return;
- }
-
- ret = avfilter_graph_config(m_filter_graph, nullptr);
- if (ret < 0) {
- LOG_ERROR(HW_GPU, "avfilter_graph_config error: {}", AVError(ret));
- return;
- }
-
- m_initialized = true;
-}
-
-bool DeinterlaceFilter::AddSourceFrame(const Frame& frame) {
- if (const int ret = av_buffersrc_add_frame_flags(m_source_context, frame.GetFrame(),
- AV_BUFFERSRC_FLAG_KEEP_REF);
- ret < 0) {
- LOG_ERROR(HW_GPU, "av_buffersrc_add_frame_flags error: {}", AVError(ret));
- return false;
- }
+ const auto ReceiveImpl = [&](AVFrame* frame) {
+ if (const int ret = avcodec_receive_frame(m_codec_context, frame); ret < 0) {
+ LOG_ERROR(HW_GPU, "avcodec_receive_frame error: {}", AVError(ret));
+ return false;
+ }
- return true;
-}
+ return true;
+ };
-std::unique_ptr<Frame> DeinterlaceFilter::DrainSinkFrame() {
- auto dst_frame = std::make_unique<Frame>();
- const int ret = av_buffersink_get_frame(m_sink_context, dst_frame->GetFrame());
+ if (m_codec_context->hw_device_ctx) {
+ // If we have a hardware context, make a separate frame here to receive the
+ // hardware result before sending it to the output.
+ Frame intermediate_frame;
- if (ret == AVERROR(EAGAIN) || ret == AVERROR(AVERROR_EOF)) {
- return {};
- }
+ if (!ReceiveImpl(intermediate_frame.GetFrame())) {
+ return {};
+ }
- if (ret < 0) {
- LOG_ERROR(HW_GPU, "av_buffersink_get_frame error: {}", AVError(ret));
- return {};
+ m_temp_frame->SetFormat(PreferredGpuFormat);
+ if (const int ret = av_hwframe_transfer_data(m_temp_frame->GetFrame(),
+ intermediate_frame.GetFrame(), 0);
+ ret < 0) {
+ LOG_ERROR(HW_GPU, "av_hwframe_transfer_data error: {}", AVError(ret));
+ return {};
+ }
+ } else {
+ // Otherwise, decode the frame as normal.
+ if (!ReceiveImpl(m_temp_frame->GetFrame())) {
+ return {};
+ }
+ }
}
- return dst_frame;
-}
-
-DeinterlaceFilter::~DeinterlaceFilter() {
- avfilter_graph_free(&m_filter_graph);
+#if defined(FF_API_INTERLACED_FRAME) || LIBAVUTIL_VERSION_MAJOR >= 59
+ m_temp_frame->GetFrame()->interlaced_frame =
+ (m_temp_frame->GetFrame()->flags & AV_FRAME_FLAG_INTERLACED) != 0;
+#endif
+ return std::move(m_temp_frame);
}
void DecodeApi::Reset() {
- m_deinterlace_filter.reset();
m_hardware_context.reset();
m_decoder_context.reset();
m_decoder.reset();
@@ -382,43 +338,14 @@ bool DecodeApi::Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
return true;
}
-bool DecodeApi::SendPacket(std::span<const u8> packet_data, size_t configuration_size) {
+bool DecodeApi::SendPacket(std::span<const u8> packet_data) {
FFmpeg::Packet packet(packet_data);
return m_decoder_context->SendPacket(packet);
}
-void DecodeApi::ReceiveFrames(std::queue<std::unique_ptr<Frame>>& frame_queue) {
+std::shared_ptr<Frame> DecodeApi::ReceiveFrame() {
// Receive raw frame from decoder.
- bool is_interlaced;
- auto frame = m_decoder_context->ReceiveFrame(&is_interlaced);
- if (!frame) {
- return;
- }
-
- if (!is_interlaced) {
- // If the frame is not interlaced, we can pend it now.
- frame_queue.push(std::move(frame));
- } else {
- // Create the deinterlacer if needed.
- if (!m_deinterlace_filter) {
- m_deinterlace_filter.emplace(*frame);
- }
-
- // Add the frame we just received.
- if (!m_deinterlace_filter->AddSourceFrame(*frame)) {
- return;
- }
-
- // Pend output fields.
- while (true) {
- auto filter_frame = m_deinterlace_filter->DrainSinkFrame();
- if (!filter_frame) {
- break;
- }
-
- frame_queue.push(std::move(filter_frame));
- }
- }
+ return m_decoder_context->ReceiveFrame();
}
} // namespace FFmpeg
diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.h b/src/video_core/host1x/ffmpeg/ffmpeg.h
index 1de0bbd83..a74fcba80 100644
--- a/src/video_core/host1x/ffmpeg/ffmpeg.h
+++ b/src/video_core/host1x/ffmpeg/ffmpeg.h
@@ -20,17 +20,20 @@ extern "C" {
#endif
#include <libavcodec/avcodec.h>
-#include <libavfilter/avfilter.h>
-#include <libavfilter/buffersink.h>
-#include <libavfilter/buffersrc.h>
-#include <libavutil/avutil.h>
#include <libavutil/opt.h>
+#ifndef ANDROID
+#include <libavcodec/codec_internal.h>
+#endif
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
}
+namespace Tegra {
+class MemoryManager;
+}
+
namespace FFmpeg {
class Packet;
@@ -90,6 +93,10 @@ public:
return m_frame->data[plane];
}
+ const u8* GetPlane(int plane) const {
+ return m_frame->data[plane];
+ }
+
u8** GetPlanes() const {
return m_frame->data;
}
@@ -98,6 +105,14 @@ public:
m_frame->format = format;
}
+ bool IsInterlaced() const {
+ return m_frame->interlaced_frame != 0;
+ }
+
+ bool IsHardwareDecoded() const {
+ return m_frame->hw_frames_ctx != nullptr;
+ }
+
AVFrame* GetFrame() const {
return m_frame;
}
@@ -160,33 +175,22 @@ public:
void InitializeHardwareDecoder(const HardwareContext& context, AVPixelFormat hw_pix_fmt);
bool OpenContext(const Decoder& decoder);
bool SendPacket(const Packet& packet);
- std::unique_ptr<Frame> ReceiveFrame(bool* out_is_interlaced);
+ std::shared_ptr<Frame> ReceiveFrame();
AVCodecContext* GetCodecContext() const {
return m_codec_context;
}
-private:
- AVCodecContext* m_codec_context{};
-};
-
-// Wraps an AVFilterGraph.
-class DeinterlaceFilter {
-public:
- YUZU_NON_COPYABLE(DeinterlaceFilter);
- YUZU_NON_MOVEABLE(DeinterlaceFilter);
-
- explicit DeinterlaceFilter(const Frame& frame);
- ~DeinterlaceFilter();
-
- bool AddSourceFrame(const Frame& frame);
- std::unique_ptr<Frame> DrainSinkFrame();
+ bool UsingDecodeOrder() const {
+ return m_decode_order;
+ }
private:
- AVFilterGraph* m_filter_graph{};
- AVFilterContext* m_source_context{};
- AVFilterContext* m_sink_context{};
- bool m_initialized{};
+ const Decoder& m_decoder;
+ AVCodecContext* m_codec_context{};
+ s32 m_got_frame{};
+ std::shared_ptr<Frame> m_temp_frame{};
+ bool m_decode_order{};
};
class DecodeApi {
@@ -200,14 +204,17 @@ public:
bool Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec);
void Reset();
- bool SendPacket(std::span<const u8> packet_data, size_t configuration_size);
- void ReceiveFrames(std::queue<std::unique_ptr<Frame>>& frame_queue);
+ bool UsingDecodeOrder() const {
+ return m_decoder_context->UsingDecodeOrder();
+ }
+
+ bool SendPacket(std::span<const u8> packet_data);
+ std::shared_ptr<Frame> ReceiveFrame();
private:
std::optional<FFmpeg::Decoder> m_decoder;
std::optional<FFmpeg::DecoderContext> m_decoder_context;
std::optional<FFmpeg::HardwareContext> m_hardware_context;
- std::optional<FFmpeg::DeinterlaceFilter> m_deinterlace_filter;
};
} // namespace FFmpeg