Skip to content

Commit

Permalink
obs-ffmpeg: Make AMF encoder work on Linux
Browse files Browse the repository at this point in the history
Only the fallback encoders are available (no texture support).

Requires AMD proprietary Vulkan driver, using different driver
will be detected on startup and the encoders disabled.
  • Loading branch information
nowrep committed Feb 17, 2023
1 parent 08af16e commit 1b62220
Show file tree
Hide file tree
Showing 6 changed files with 257 additions and 23 deletions.
3 changes: 2 additions & 1 deletion plugins/obs-ffmpeg/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,11 @@ if(OS_WINDOWS)
obs-ffmpeg.rc)

elseif(OS_POSIX AND NOT OS_MACOS)
add_subdirectory(obs-amf-test)
find_package(Libva REQUIRED)
find_package(Libpci REQUIRED)
target_sources(obs-ffmpeg PRIVATE obs-ffmpeg-vaapi.c vaapi-utils.c
vaapi-utils.h)
vaapi-utils.h texture-amf.cpp)
target_link_libraries(obs-ffmpeg PRIVATE Libva::va Libva::drm LIBPCI::LIBPCI)
endif()

Expand Down
11 changes: 9 additions & 2 deletions plugins/obs-ffmpeg/obs-amf-test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,15 @@ project(obs-amf-test)
include_directories(SYSTEM ${CMAKE_SOURCE_DIR}/libobs)

add_executable(obs-amf-test)
target_sources(obs-amf-test PRIVATE obs-amf-test.cpp)
target_link_libraries(obs-amf-test d3d11 dxgi dxguid)

if(OS_WINDOWS)
target_sources(obs-amf-test PRIVATE obs-amf-test.cpp)
target_link_libraries(obs-amf-test d3d11 dxgi dxguid)
elseif(OS_POSIX AND NOT OS_MACOS)
find_package(Vulkan REQUIRED)
target_sources(obs-amf-test PRIVATE obs-amf-test-linux.cpp)
target_link_libraries(obs-amf-test dl Vulkan::Vulkan)
endif()

set_target_properties(obs-amf-test PROPERTIES FOLDER "plugins/obs-ffmpeg")

Expand Down
140 changes: 140 additions & 0 deletions plugins/obs-ffmpeg/obs-amf-test/obs-amf-test-linux.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#include "../external/AMF/include/core/Factory.h"
#include "../external/AMF/include/core/Trace.h"
#include "../external/AMF/include/components/VideoEncoderVCE.h"
#include "../external/AMF/include/components/VideoEncoderHEVC.h"
#include "../external/AMF/include/components/VideoEncoderAV1.h"

#include <dlfcn.h>
#include <vulkan/vulkan.hpp>

#include <string>
#include <map>

using namespace amf;

struct adapter_caps {
bool is_amd = false;
bool supports_avc = false;
bool supports_hevc = false;
bool supports_av1 = false;
};

static AMFFactory *amf_factory = nullptr;
static std::map<uint32_t, adapter_caps> adapter_info;

static bool has_encoder(AMFContextPtr &amf_context, const wchar_t *encoder_name)
{
AMFComponentPtr encoder;
AMF_RESULT res = amf_factory->CreateComponent(amf_context, encoder_name,
&encoder);
return res == AMF_OK;
}

static bool get_adapter_caps(uint32_t adapter_idx)
{
if (adapter_idx)
return false;

adapter_caps &caps = adapter_info[adapter_idx];

AMF_RESULT res;
AMFContextPtr amf_context;
res = amf_factory->CreateContext(&amf_context);
if (res != AMF_OK)
return true;

AMFContext1 *context1 = NULL;
res = amf_context->QueryInterface(AMFContext1::IID(),
(void **)&context1);
if (res != AMF_OK)
return false;
res = context1->InitVulkan(nullptr);
context1->Release();
if (res != AMF_OK)
return false;

caps.is_amd = true;
caps.supports_avc = has_encoder(amf_context, AMFVideoEncoderVCE_AVC);
caps.supports_hevc = has_encoder(amf_context, AMFVideoEncoder_HEVC);
caps.supports_av1 = has_encoder(amf_context, AMFVideoEncoder_AV1);

return true;
}

int main(void)
try {
AMF_RESULT res;
VkResult vkres;

VkApplicationInfo app_info = {};
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
app_info.pApplicationName = "obs-amf-test";
app_info.apiVersion = VK_API_VERSION_1_2;

VkInstanceCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
info.pApplicationInfo = &app_info;

VkInstance instance;
vkres = vkCreateInstance(&info, nullptr, &instance);
if (vkres != VK_SUCCESS)
throw "Failed to initialize Vulkan";

uint32_t device_count;
vkres = vkEnumeratePhysicalDevices(instance, &device_count, nullptr);
if (vkres != VK_SUCCESS || !device_count)
throw "Failed to enumerate Vulkan devices";

VkPhysicalDevice *devices = new VkPhysicalDevice[device_count];
vkres = vkEnumeratePhysicalDevices(instance, &device_count, devices);
if (vkres != VK_SUCCESS)
throw "Failed to enumerate Vulkan devices";

VkPhysicalDeviceDriverProperties driver_props = {};
driver_props.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
VkPhysicalDeviceProperties2 device_props = {};
device_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
device_props.pNext = &driver_props;
vkGetPhysicalDeviceProperties2(devices[0], &device_props);

if (strcmp(driver_props.driverName, "AMD proprietary driver"))
throw "Not running AMD proprietary driver";

vkDestroyInstance(instance, nullptr);

/* --------------------------------------------------------- */
/* try initializing amf, I guess */

void *amf_module = dlopen(AMF_DLL_NAMEA, RTLD_LAZY);
if (!amf_module)
throw "Failed to load AMF lib";

auto init = (AMFInit_Fn)dlsym(amf_module, AMF_INIT_FUNCTION_NAME);
if (!init)
throw "Failed to get init func";

res = init(AMF_FULL_VERSION, &amf_factory);
if (res != AMF_OK)
throw "AMFInit failed";

uint32_t idx = 0;
while (get_adapter_caps(idx++))
;

for (auto &[idx, caps] : adapter_info) {
printf("[%u]\n", idx);
printf("is_amd=%s\n", caps.is_amd ? "true" : "false");
printf("supports_avc=%s\n",
caps.supports_avc ? "true" : "false");
printf("supports_hevc=%s\n",
caps.supports_hevc ? "true" : "false");
printf("supports_av1=%s\n",
caps.supports_av1 ? "true" : "false");
}

return 0;
} catch (const char *text) {
printf("[error]\nstring=%s\n", text);
return 0;
}
10 changes: 8 additions & 2 deletions plugins/obs-ffmpeg/obs-ffmpeg.c
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,9 @@ static bool h264_vaapi_supported(void)
#ifdef _WIN32
extern void jim_nvenc_load(bool h264, bool hevc, bool av1);
extern void jim_nvenc_unload(void);
#endif

#if defined(_WIN32) || defined(__linux__)
extern void amf_load(void);
extern void amf_unload(void);
#endif
Expand Down Expand Up @@ -408,7 +411,7 @@ bool obs_module_load(void)
#endif
}

#ifdef _WIN32
#if defined(_WIN32) || defined(__linux__)
amf_load();
#endif

Expand Down Expand Up @@ -440,8 +443,11 @@ void obs_module_unload(void)
obs_ffmpeg_unload_logging();
#endif

#ifdef _WIN32
#if defined(_WIN32) || defined(__linux__)
amf_unload();
#endif

#ifdef _WIN32
jim_nvenc_unload();
#endif
}
2 changes: 1 addition & 1 deletion plugins/obs-ffmpeg/texture-amf-opts.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ static void amf_apply_opt(amf_base *enc, obs_option *opt)
val = atoi(opt->value);
}

os_utf8_to_wcs(opt->name, 0, wname, _countof(wname));
os_utf8_to_wcs(opt->name, 0, wname, amf_countof(wname));
if (is_bool) {
bool bool_val = (bool)val;
set_amf_property(enc, wname, bool_val);
Expand Down
Loading

0 comments on commit 1b62220

Please sign in to comment.