aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/video_core/host1x/ffmpeg/ffmpeg.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core/host1x/ffmpeg/ffmpeg.cpp')
-rw-r--r--src/video_core/host1x/ffmpeg/ffmpeg.cpp233
1 files changed, 80 insertions, 153 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