diff options
| author | 2023-05-14 01:35:01 +0400 | |
|---|---|---|
| committer | 2023-05-22 01:43:44 +0400 | |
| commit | 7701a00a0277ad59c1b743b9aa14c6861624b847 (patch) | |
| tree | 9214202ee896671be1301d0993931a74686955e5 | |
| parent | Merge pull request #10392 from danilaml/update-cubeb-again (diff) | |
| download | yuzu-7701a00a0277ad59c1b743b9aa14c6861624b847.tar.gz yuzu-7701a00a0277ad59c1b743b9aa14c6861624b847.tar.xz yuzu-7701a00a0277ad59c1b743b9aa14c6861624b847.zip | |
Add support for deinterlaced videos playback
This is a follow up to #10254 to improve the playback of cut scenes in Layton's Mystery Journey.
It uses ffmpeg's yadif filter for deinterlacing.
Diffstat (limited to '')
| -rw-r--r-- | CMakeLists.txt | 1 | ||||
| -rw-r--r-- | externals/ffmpeg/CMakeLists.txt | 5 | ||||
| -rw-r--r-- | src/video_core/host1x/codecs/codec.cpp | 93 | ||||
| -rw-r--r-- | src/video_core/host1x/codecs/codec.h | 8 |
4 files changed, 103 insertions, 4 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 561eaafb2..7276ac9dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -453,6 +453,7 @@ endif() | |||
| 453 | # List of all FFmpeg components required | 453 | # List of all FFmpeg components required |
| 454 | set(FFmpeg_COMPONENTS | 454 | set(FFmpeg_COMPONENTS |
| 455 | avcodec | 455 | avcodec |
| 456 | avfilter | ||
| 456 | avutil | 457 | avutil |
| 457 | swscale) | 458 | swscale) |
| 458 | 459 | ||
diff --git a/externals/ffmpeg/CMakeLists.txt b/externals/ffmpeg/CMakeLists.txt index 0baac8e17..03fad0778 100644 --- a/externals/ffmpeg/CMakeLists.txt +++ b/externals/ffmpeg/CMakeLists.txt | |||
| @@ -131,7 +131,6 @@ if (NOT WIN32) | |||
| 131 | COMMAND | 131 | COMMAND |
| 132 | /bin/bash ${FFmpeg_PREFIX}/configure | 132 | /bin/bash ${FFmpeg_PREFIX}/configure |
| 133 | --disable-avdevice | 133 | --disable-avdevice |
| 134 | --disable-avfilter | ||
| 135 | --disable-avformat | 134 | --disable-avformat |
| 136 | --disable-doc | 135 | --disable-doc |
| 137 | --disable-everything | 136 | --disable-everything |
| @@ -143,6 +142,7 @@ if (NOT WIN32) | |||
| 143 | --enable-decoder=h264 | 142 | --enable-decoder=h264 |
| 144 | --enable-decoder=vp8 | 143 | --enable-decoder=vp8 |
| 145 | --enable-decoder=vp9 | 144 | --enable-decoder=vp9 |
| 145 | --enable-filter=yadif | ||
| 146 | --cc="${FFmpeg_CC}" | 146 | --cc="${FFmpeg_CC}" |
| 147 | --cxx="${FFmpeg_CXX}" | 147 | --cxx="${FFmpeg_CXX}" |
| 148 | ${FFmpeg_HWACCEL_FLAGS} | 148 | ${FFmpeg_HWACCEL_FLAGS} |
| @@ -199,7 +199,7 @@ if (NOT WIN32) | |||
| 199 | endif() | 199 | endif() |
| 200 | else(WIN32) | 200 | else(WIN32) |
| 201 | # Use yuzu FFmpeg binaries | 201 | # Use yuzu FFmpeg binaries |
| 202 | set(FFmpeg_EXT_NAME "ffmpeg-4.4") | 202 | set(FFmpeg_EXT_NAME "ffmpeg-5.1.3") |
| 203 | set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}") | 203 | set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}") |
| 204 | download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "") | 204 | download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "") |
| 205 | set(FFmpeg_FOUND YES) | 205 | set(FFmpeg_FOUND YES) |
| @@ -210,6 +210,7 @@ else(WIN32) | |||
| 210 | set(FFmpeg_LIBRARIES | 210 | set(FFmpeg_LIBRARIES |
| 211 | ${FFmpeg_LIBRARY_DIR}/swscale.lib | 211 | ${FFmpeg_LIBRARY_DIR}/swscale.lib |
| 212 | ${FFmpeg_LIBRARY_DIR}/avcodec.lib | 212 | ${FFmpeg_LIBRARY_DIR}/avcodec.lib |
| 213 | ${FFmpeg_LIBRARY_DIR}/avfilter.lib | ||
| 213 | ${FFmpeg_LIBRARY_DIR}/avutil.lib | 214 | ${FFmpeg_LIBRARY_DIR}/avutil.lib |
| 214 | CACHE PATH "Paths to FFmpeg libraries" FORCE) | 215 | CACHE PATH "Paths to FFmpeg libraries" FORCE) |
| 215 | # exported variables | 216 | # exported variables |
diff --git a/src/video_core/host1x/codecs/codec.cpp b/src/video_core/host1x/codecs/codec.cpp index 3e9022dce..cd6a3a9b8 100644 --- a/src/video_core/host1x/codecs/codec.cpp +++ b/src/video_core/host1x/codecs/codec.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <fstream> | 5 | #include <fstream> |
| 6 | #include <vector> | 6 | #include <vector> |
| 7 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 8 | #include "common/scope_exit.h" | ||
| 8 | #include "common/settings.h" | 9 | #include "common/settings.h" |
| 9 | #include "video_core/host1x/codecs/codec.h" | 10 | #include "video_core/host1x/codecs/codec.h" |
| 10 | #include "video_core/host1x/codecs/h264.h" | 11 | #include "video_core/host1x/codecs/h264.h" |
| @@ -14,6 +15,8 @@ | |||
| 14 | #include "video_core/memory_manager.h" | 15 | #include "video_core/memory_manager.h" |
| 15 | 16 | ||
| 16 | extern "C" { | 17 | extern "C" { |
| 18 | #include <libavfilter/buffersink.h> | ||
| 19 | #include <libavfilter/buffersrc.h> | ||
| 17 | #include <libavutil/opt.h> | 20 | #include <libavutil/opt.h> |
| 18 | #ifdef LIBVA_FOUND | 21 | #ifdef LIBVA_FOUND |
| 19 | // for querying VAAPI driver information | 22 | // for querying VAAPI driver information |
| @@ -85,6 +88,10 @@ Codec::~Codec() { | |||
| 85 | // Free libav memory | 88 | // Free libav memory |
| 86 | avcodec_free_context(&av_codec_ctx); | 89 | avcodec_free_context(&av_codec_ctx); |
| 87 | av_buffer_unref(&av_gpu_decoder); | 90 | av_buffer_unref(&av_gpu_decoder); |
| 91 | |||
| 92 | if (filters_initialized) { | ||
| 93 | avfilter_graph_free(&av_filter_graph); | ||
| 94 | } | ||
| 88 | } | 95 | } |
| 89 | 96 | ||
| 90 | bool Codec::CreateGpuAvDevice() { | 97 | bool Codec::CreateGpuAvDevice() { |
| @@ -167,6 +174,62 @@ void Codec::InitializeGpuDecoder() { | |||
| 167 | av_codec_ctx->get_format = GetGpuFormat; | 174 | av_codec_ctx->get_format = GetGpuFormat; |
| 168 | } | 175 | } |
| 169 | 176 | ||
| 177 | void Codec::InitializeAvFilters(AVFrame* frame) { | ||
| 178 | const AVFilter* buffer_src = avfilter_get_by_name("buffer"); | ||
| 179 | const AVFilter* buffer_sink = avfilter_get_by_name("buffersink"); | ||
| 180 | AVFilterInOut* inputs = avfilter_inout_alloc(); | ||
| 181 | AVFilterInOut* outputs = avfilter_inout_alloc(); | ||
| 182 | SCOPE_EXIT({ | ||
| 183 | avfilter_inout_free(&inputs); | ||
| 184 | avfilter_inout_free(&outputs); | ||
| 185 | }); | ||
| 186 | |||
| 187 | // Don't know how to get the accurate time_base but it doesn't matter for yadif filter | ||
| 188 | // so just use 1/1 to make buffer filter happy | ||
| 189 | std::string args = fmt::format("video_size={}x{}:pix_fmt={}:time_base=1/1", frame->width, | ||
| 190 | frame->height, frame->format); | ||
| 191 | |||
| 192 | av_filter_graph = avfilter_graph_alloc(); | ||
| 193 | int ret = avfilter_graph_create_filter(&av_filter_src_ctx, buffer_src, "in", args.c_str(), | ||
| 194 | nullptr, av_filter_graph); | ||
| 195 | if (ret < 0) { | ||
| 196 | LOG_ERROR(Service_NVDRV, "avfilter_graph_create_filter source error: {}", ret); | ||
| 197 | return; | ||
| 198 | } | ||
| 199 | |||
| 200 | ret = avfilter_graph_create_filter(&av_filter_sink_ctx, buffer_sink, "out", nullptr, nullptr, | ||
| 201 | av_filter_graph); | ||
| 202 | if (ret < 0) { | ||
| 203 | LOG_ERROR(Service_NVDRV, "avfilter_graph_create_filter sink error: {}", ret); | ||
| 204 | return; | ||
| 205 | } | ||
| 206 | |||
| 207 | inputs->name = av_strdup("out"); | ||
| 208 | inputs->filter_ctx = av_filter_sink_ctx; | ||
| 209 | inputs->pad_idx = 0; | ||
| 210 | inputs->next = nullptr; | ||
| 211 | |||
| 212 | outputs->name = av_strdup("in"); | ||
| 213 | outputs->filter_ctx = av_filter_src_ctx; | ||
| 214 | outputs->pad_idx = 0; | ||
| 215 | outputs->next = nullptr; | ||
| 216 | |||
| 217 | const char* description = "yadif=1:-1:0"; | ||
| 218 | ret = avfilter_graph_parse_ptr(av_filter_graph, description, &inputs, &outputs, nullptr); | ||
| 219 | if (ret < 0) { | ||
| 220 | LOG_ERROR(Service_NVDRV, "avfilter_graph_parse_ptr error: {}", ret); | ||
| 221 | return; | ||
| 222 | } | ||
| 223 | |||
| 224 | ret = avfilter_graph_config(av_filter_graph, nullptr); | ||
| 225 | if (ret < 0) { | ||
| 226 | LOG_ERROR(Service_NVDRV, "avfilter_graph_config error: {}", ret); | ||
| 227 | return; | ||
| 228 | } | ||
| 229 | |||
| 230 | filters_initialized = true; | ||
| 231 | } | ||
| 232 | |||
| 170 | void Codec::Initialize() { | 233 | void Codec::Initialize() { |
| 171 | const AVCodecID codec = [&] { | 234 | const AVCodecID codec = [&] { |
| 172 | switch (current_codec) { | 235 | switch (current_codec) { |
| @@ -271,8 +334,34 @@ void Codec::Decode() { | |||
| 271 | UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format); | 334 | UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format); |
| 272 | return; | 335 | return; |
| 273 | } | 336 | } |
| 274 | av_frames.push(std::move(final_frame)); | 337 | if (!final_frame->interlaced_frame) { |
| 275 | if (av_frames.size() > 10) { | 338 | av_frames.push(std::move(final_frame)); |
| 339 | } else { | ||
| 340 | if (!filters_initialized) { | ||
| 341 | InitializeAvFilters(final_frame.get()); | ||
| 342 | } | ||
| 343 | if (const int ret = av_buffersrc_add_frame_flags(av_filter_src_ctx, final_frame.get(), | ||
| 344 | AV_BUFFERSRC_FLAG_KEEP_REF); | ||
| 345 | ret) { | ||
| 346 | LOG_DEBUG(Service_NVDRV, "av_buffersrc_add_frame_flags error {}", ret); | ||
| 347 | return; | ||
| 348 | } | ||
| 349 | while (true) { | ||
| 350 | auto filter_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter}; | ||
| 351 | |||
| 352 | int ret = av_buffersink_get_frame(av_filter_sink_ctx, filter_frame.get()); | ||
| 353 | |||
| 354 | if (ret == AVERROR(EAGAIN) || ret == AVERROR(AVERROR_EOF)) | ||
| 355 | break; | ||
| 356 | if (ret < 0) { | ||
| 357 | LOG_DEBUG(Service_NVDRV, "av_buffersink_get_frame error {}", ret); | ||
| 358 | return; | ||
| 359 | } | ||
| 360 | |||
| 361 | av_frames.push(std::move(filter_frame)); | ||
| 362 | } | ||
| 363 | } | ||
| 364 | while (av_frames.size() > 10) { | ||
| 276 | LOG_TRACE(Service_NVDRV, "av_frames.push overflow dropped frame"); | 365 | LOG_TRACE(Service_NVDRV, "av_frames.push overflow dropped frame"); |
| 277 | av_frames.pop(); | 366 | av_frames.pop(); |
| 278 | } | 367 | } |
diff --git a/src/video_core/host1x/codecs/codec.h b/src/video_core/host1x/codecs/codec.h index 0d45fb7fe..06fe00a4b 100644 --- a/src/video_core/host1x/codecs/codec.h +++ b/src/video_core/host1x/codecs/codec.h | |||
| @@ -15,6 +15,7 @@ extern "C" { | |||
| 15 | #pragma GCC diagnostic ignored "-Wconversion" | 15 | #pragma GCC diagnostic ignored "-Wconversion" |
| 16 | #endif | 16 | #endif |
| 17 | #include <libavcodec/avcodec.h> | 17 | #include <libavcodec/avcodec.h> |
| 18 | #include <libavfilter/avfilter.h> | ||
| 18 | #if defined(__GNUC__) || defined(__clang__) | 19 | #if defined(__GNUC__) || defined(__clang__) |
| 19 | #pragma GCC diagnostic pop | 20 | #pragma GCC diagnostic pop |
| 20 | #endif | 21 | #endif |
| @@ -61,17 +62,24 @@ public: | |||
| 61 | private: | 62 | private: |
| 62 | void InitializeAvCodecContext(); | 63 | void InitializeAvCodecContext(); |
| 63 | 64 | ||
| 65 | void InitializeAvFilters(AVFrame* frame); | ||
| 66 | |||
| 64 | void InitializeGpuDecoder(); | 67 | void InitializeGpuDecoder(); |
| 65 | 68 | ||
| 66 | bool CreateGpuAvDevice(); | 69 | bool CreateGpuAvDevice(); |
| 67 | 70 | ||
| 68 | bool initialized{}; | 71 | bool initialized{}; |
| 72 | bool filters_initialized{}; | ||
| 69 | Host1x::NvdecCommon::VideoCodec current_codec{Host1x::NvdecCommon::VideoCodec::None}; | 73 | Host1x::NvdecCommon::VideoCodec current_codec{Host1x::NvdecCommon::VideoCodec::None}; |
| 70 | 74 | ||
| 71 | const AVCodec* av_codec{nullptr}; | 75 | const AVCodec* av_codec{nullptr}; |
| 72 | AVCodecContext* av_codec_ctx{nullptr}; | 76 | AVCodecContext* av_codec_ctx{nullptr}; |
| 73 | AVBufferRef* av_gpu_decoder{nullptr}; | 77 | AVBufferRef* av_gpu_decoder{nullptr}; |
| 74 | 78 | ||
| 79 | AVFilterContext* av_filter_src_ctx{nullptr}; | ||
| 80 | AVFilterContext* av_filter_sink_ctx{nullptr}; | ||
| 81 | AVFilterGraph* av_filter_graph{nullptr}; | ||
| 82 | |||
| 75 | Host1x::Host1x& host1x; | 83 | Host1x::Host1x& host1x; |
| 76 | const Host1x::NvdecCommon::NvdecRegisters& state; | 84 | const Host1x::NvdecCommon::NvdecRegisters& state; |
| 77 | std::unique_ptr<Decoder::H264> h264_decoder; | 85 | std::unique_ptr<Decoder::H264> h264_decoder; |