mirror of
				https://git.suyu.dev/suyu/suyu.git
				synced 2025-10-31 23:06:43 +08:00 
			
		
		
		
	Merge pull request #7467 from liushuyu/fix-linux-decoding
video_core/codecs: more robust ffmpeg hwdecoder selection logic
This commit is contained in:
		
						commit
						e482dd82b9
					
				| @ -3,6 +3,7 @@ add_subdirectory(host_shaders) | |||||||
| if(LIBVA_FOUND) | if(LIBVA_FOUND) | ||||||
|     set_source_files_properties(command_classes/codecs/codec.cpp |     set_source_files_properties(command_classes/codecs/codec.cpp | ||||||
|         PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1) |         PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1) | ||||||
|  |     list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES}) | ||||||
| endif() | endif() | ||||||
| 
 | 
 | ||||||
| add_library(video_core STATIC | add_library(video_core STATIC | ||||||
|  | |||||||
| @ -17,12 +17,28 @@ | |||||||
| 
 | 
 | ||||||
| extern "C" { | extern "C" { | ||||||
| #include <libavutil/opt.h> | #include <libavutil/opt.h> | ||||||
|  | #ifdef LIBVA_FOUND | ||||||
|  | // for querying VAAPI driver information
 | ||||||
|  | #include <libavutil/hwcontext_vaapi.h> | ||||||
|  | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| namespace Tegra { | namespace Tegra { | ||||||
| namespace { | namespace { | ||||||
| constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12; | constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12; | ||||||
| constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P; | constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P; | ||||||
|  | constexpr std::array PREFERRED_GPU_DECODERS = { | ||||||
|  |     AV_HWDEVICE_TYPE_CUDA, | ||||||
|  | #ifdef _WIN32 | ||||||
|  |     AV_HWDEVICE_TYPE_D3D11VA, | ||||||
|  |     AV_HWDEVICE_TYPE_DXVA2, | ||||||
|  | #elif defined(__linux__) | ||||||
|  |     AV_HWDEVICE_TYPE_VAAPI, | ||||||
|  |     AV_HWDEVICE_TYPE_VDPAU, | ||||||
|  | #endif | ||||||
|  |     // last resort for Linux Flatpak (w/ NVIDIA)
 | ||||||
|  |     AV_HWDEVICE_TYPE_VULKAN, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| void AVPacketDeleter(AVPacket* ptr) { | void AVPacketDeleter(AVPacket* ptr) { | ||||||
|     av_packet_free(&ptr); |     av_packet_free(&ptr); | ||||||
| @ -61,83 +77,50 @@ Codec::~Codec() { | |||||||
|     av_buffer_unref(&av_gpu_decoder); |     av_buffer_unref(&av_gpu_decoder); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #ifdef LIBVA_FOUND | // List all the currently available hwcontext in ffmpeg
 | ||||||
| // List all the currently loaded Linux modules
 | static std::vector<AVHWDeviceType> ListSupportedContexts() { | ||||||
| static std::vector<std::string> ListLinuxKernelModules() { |     std::vector<AVHWDeviceType> contexts{}; | ||||||
|     using FILEPtr = std::unique_ptr<FILE, decltype(&std::fclose)>; |     AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE; | ||||||
|     auto module_listing = FILEPtr{fopen("/proc/modules", "rt"), std::fclose}; |     do { | ||||||
|     std::vector<std::string> modules{}; |         current_device_type = av_hwdevice_iterate_types(current_device_type); | ||||||
|     if (!module_listing) { |         contexts.push_back(current_device_type); | ||||||
|         LOG_WARNING(Service_NVDRV, "Could not open /proc/modules to collect available modules"); |     } while (current_device_type != AV_HWDEVICE_TYPE_NONE); | ||||||
|         return modules; |     return contexts; | ||||||
|     } |  | ||||||
|     char* buffer = nullptr; |  | ||||||
|     size_t buf_len = 0; |  | ||||||
|     while (getline(&buffer, &buf_len, module_listing.get()) != -1) { |  | ||||||
|         // format for the module listing file (sysfs)
 |  | ||||||
|         // <name> <module_size> <depended_by_count> <depended_by_names> <status> <load_address>
 |  | ||||||
|         auto line = std::string(buffer); |  | ||||||
|         // we are only interested in module names
 |  | ||||||
|         auto name_pos = line.find_first_of(" "); |  | ||||||
|         if (name_pos == std::string::npos) { |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|         modules.push_back(line.erase(name_pos)); |  | ||||||
|     } |  | ||||||
|     free(buffer); |  | ||||||
|     return modules; |  | ||||||
| } | } | ||||||
| #endif |  | ||||||
| 
 | 
 | ||||||
| bool Codec::CreateGpuAvDevice() { | bool Codec::CreateGpuAvDevice() { | ||||||
| #if defined(LIBVA_FOUND) |     static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX; | ||||||
|     static constexpr std::array<const char*, 3> VAAPI_DRIVERS = { |     static const auto supported_contexts = ListSupportedContexts(); | ||||||
|         "i915", |     for (const auto& type : PREFERRED_GPU_DECODERS) { | ||||||
|         "iHD", |         if (std::none_of(supported_contexts.begin(), supported_contexts.end(), | ||||||
|         "amdgpu", |                          [&type](const auto& context) { return context == type; })) { | ||||||
|     }; |             LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type)); | ||||||
|     AVDictionary* hwdevice_options = nullptr; |  | ||||||
|     const auto loaded_modules = ListLinuxKernelModules(); |  | ||||||
|     av_dict_set(&hwdevice_options, "connection_type", "drm", 0); |  | ||||||
|     for (const auto& driver : VAAPI_DRIVERS) { |  | ||||||
|         // first check if the target driver is loaded in the kernel
 |  | ||||||
|         bool found = std::any_of(loaded_modules.begin(), loaded_modules.end(), |  | ||||||
|                                  [&driver](const auto& module) { return module == driver; }); |  | ||||||
|         if (!found) { |  | ||||||
|             LOG_DEBUG(Service_NVDRV, "Kernel driver {} is not loaded, trying the next one", driver); |  | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|         av_dict_set(&hwdevice_options, "kernel_driver", driver, 0); |  | ||||||
|         const int hwdevice_error = av_hwdevice_ctx_create(&av_gpu_decoder, AV_HWDEVICE_TYPE_VAAPI, |  | ||||||
|                                                           nullptr, hwdevice_options, 0); |  | ||||||
|         if (hwdevice_error >= 0) { |  | ||||||
|             LOG_INFO(Service_NVDRV, "Using VA-API with {}", driver); |  | ||||||
|             av_dict_free(&hwdevice_options); |  | ||||||
|             av_codec_ctx->pix_fmt = AV_PIX_FMT_VAAPI; |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
|         LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed {}", hwdevice_error); |  | ||||||
|     } |  | ||||||
|     LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed for all drivers"); |  | ||||||
|     av_dict_free(&hwdevice_options); |  | ||||||
| #endif |  | ||||||
|     static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX; |  | ||||||
|     static constexpr std::array GPU_DECODER_TYPES{ |  | ||||||
| #ifdef linux |  | ||||||
|         AV_HWDEVICE_TYPE_VDPAU, |  | ||||||
| #endif |  | ||||||
|         AV_HWDEVICE_TYPE_CUDA, |  | ||||||
| #ifdef _WIN32 |  | ||||||
|         AV_HWDEVICE_TYPE_D3D11VA, |  | ||||||
| #endif |  | ||||||
|     }; |  | ||||||
|     for (const auto& type : GPU_DECODER_TYPES) { |  | ||||||
|         const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0); |         const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0); | ||||||
|         if (hwdevice_res < 0) { |         if (hwdevice_res < 0) { | ||||||
|             LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}", |             LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}", | ||||||
|                       av_hwdevice_get_type_name(type), hwdevice_res); |                       av_hwdevice_get_type_name(type), hwdevice_res); | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  | #ifdef LIBVA_FOUND | ||||||
|  |         if (type == AV_HWDEVICE_TYPE_VAAPI) { | ||||||
|  |             // we need to determine if this is an impersonated VAAPI driver
 | ||||||
|  |             AVHWDeviceContext* hwctx = | ||||||
|  |                 static_cast<AVHWDeviceContext*>(static_cast<void*>(av_gpu_decoder->data)); | ||||||
|  |             AVVAAPIDeviceContext* vactx = static_cast<AVVAAPIDeviceContext*>(hwctx->hwctx); | ||||||
|  |             const char* vendor_name = vaQueryVendorString(vactx->display); | ||||||
|  |             if (strstr(vendor_name, "VDPAU backend")) { | ||||||
|  |                 // VDPAU impersonated VAAPI impl's are super buggy, we need to skip them
 | ||||||
|  |                 LOG_DEBUG(Service_NVDRV, "Skipping vdapu impersonated VAAPI driver"); | ||||||
|  |                 continue; | ||||||
|  |             } else { | ||||||
|  |                 // according to some user testing, certain vaapi driver (Intel?) could be buggy
 | ||||||
|  |                 // so let's log the driver name which may help the developers/supporters
 | ||||||
|  |                 LOG_DEBUG(Service_NVDRV, "Using VAAPI driver: {}", vendor_name); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | #endif | ||||||
|         for (int i = 0;; i++) { |         for (int i = 0;; i++) { | ||||||
|             const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i); |             const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i); | ||||||
|             if (!config) { |             if (!config) { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user