From 2f673b338d11cde077128f66ee8e0f375c42d9ba Mon Sep 17 00:00:00 2001 From: David Rosca Date: Tue, 14 Nov 2023 23:25:18 +0100 Subject: [PATCH] Linux: Remove AMF encoder (#1905) With all the recent Mesa changes there is no point using AMF instead of VAAPI anymore. --- alvr/server/cpp/platform/linux/CEncoder.cpp | 10 +- .../cpp/platform/linux/EncodePipeline.cpp | 11 - .../cpp/platform/linux/EncodePipelineAMF.cpp | 459 ------------------ .../cpp/platform/linux/EncodePipelineAMF.h | 101 ---- alvr/server/cpp/platform/linux/amf_helper.cpp | 128 ----- alvr/server/cpp/platform/linux/amf_helper.h | 33 -- ...uration-Information-and-Recommendations.md | 15 +- 7 files changed, 2 insertions(+), 755 deletions(-) delete mode 100644 alvr/server/cpp/platform/linux/EncodePipelineAMF.cpp delete mode 100644 alvr/server/cpp/platform/linux/EncodePipelineAMF.h delete mode 100644 alvr/server/cpp/platform/linux/amf_helper.cpp delete mode 100644 alvr/server/cpp/platform/linux/amf_helper.h diff --git a/alvr/server/cpp/platform/linux/CEncoder.cpp b/alvr/server/cpp/platform/linux/CEncoder.cpp index 8fd186319b..f8981f35ee 100644 --- a/alvr/server/cpp/platform/linux/CEncoder.cpp +++ b/alvr/server/cpp/platform/linux/CEncoder.cpp @@ -24,7 +24,6 @@ #include "ffmpeg_helper.h" #include "EncodePipeline.h" #include "FrameRender.h" -#include "amf_helper.h" extern "C" { #include @@ -204,14 +203,7 @@ void CEncoder::Run() { av_log_set_callback(av_logfn); - bool useAmf = alvr::AMFContext::get()->isValid(); - std::vector deviceExtensions; - - if (useAmf) { - deviceExtensions = alvr::AMFContext::get()->requiredDeviceExtensions(); - } - - alvr::VkContext vk_ctx(init.device_uuid.data(), deviceExtensions); + alvr::VkContext vk_ctx(init.device_uuid.data(), {}); FrameRender render(vk_ctx, init, m_fds); auto output = render.CreateOutput(); diff --git a/alvr/server/cpp/platform/linux/EncodePipeline.cpp b/alvr/server/cpp/platform/linux/EncodePipeline.cpp index aa01d8acea..f308c903f3 100644 --- a/alvr/server/cpp/platform/linux/EncodePipeline.cpp +++ b/alvr/server/cpp/platform/linux/EncodePipeline.cpp @@ -5,7 +5,6 @@ #include "EncodePipelineSW.h" #include "EncodePipelineVAAPI.h" #include "EncodePipelineNvEnc.h" -#include "EncodePipelineAMF.h" #include "ffmpeg_helper.h" extern "C" { @@ -35,16 +34,6 @@ std::unique_ptr alvr::EncodePipeline::Create(Renderer *ren Info("failed to create NvEnc encoder: %s", e.what()); } } else { - if (vk_ctx.amd) { - try { - auto amf = std::make_unique(render, width, height); - Info("using AMF encoder"); - return amf; - } catch (std::exception &e) - { - Info("failed to create AMF encoder: %s", e.what()); - } - } try { auto vaapi = std::make_unique(render, vk_ctx, input_frame, width, height); Info("using VAAPI encoder"); diff --git a/alvr/server/cpp/platform/linux/EncodePipelineAMF.cpp b/alvr/server/cpp/platform/linux/EncodePipelineAMF.cpp deleted file mode 100644 index b21c091e6e..0000000000 --- a/alvr/server/cpp/platform/linux/EncodePipelineAMF.cpp +++ /dev/null @@ -1,459 +0,0 @@ -#include "EncodePipelineAMF.h" -#include "amf_helper.h" - -#include "alvr_server/Logger.h" -#include "alvr_server/Settings.h" - -#include - -#define AMF_THROW_IF(expr) {AMF_RESULT res = expr;\ -if(res != AMF_OK){throw MakeException("AMF Error %ls: %s", AMFContext::get()->resultString(res), #expr);}} - -static amf::AMF_SURFACE_FORMAT fromVkFormat(VkFormat format) -{ - switch (format) { - case VK_FORMAT_R8G8B8A8_UNORM: - return amf::AMF_SURFACE_RGBA; - case VK_FORMAT_B8G8R8A8_UNORM: - return amf::AMF_SURFACE_BGRA; - default: - return amf::AMF_SURFACE_RGBA; - } -} - -AMFPipe::AMFPipe(amf::AMFComponentPtr src, AMFDataReceiver receiver) - : m_amfComponentSrc(src) - , m_receiver(receiver) -{ -} - -AMFPipe::~AMFPipe() -{ - Debug("AMFPipe::~AMFPipe() m_amfComponentSrc->Drain\n"); - m_amfComponentSrc->Drain(); -} - -void AMFPipe::doPassthrough() -{ - amf::AMFDataPtr data; - auto res = m_amfComponentSrc->QueryOutput(&data); - switch (res) { - case AMF_OK: - if (data) { - m_receiver(data); - } - break; - case AMF_NO_DEVICE: - Debug("m_amfComponentSrc->QueryOutput returns AMF_NO_DEVICE.\n"); - return; - case AMF_REPEAT: - break; - case AMF_EOF: - Debug("m_amfComponentSrc->QueryOutput returns AMF_EOF.\n"); - return; - default: - Debug("m_amfComponentSrc->QueryOutput returns unknown status.\n"); - return; - } -} - -AMFSolidPipe::AMFSolidPipe(amf::AMFComponentPtr src, amf::AMFComponentPtr dst) - : AMFPipe(src, std::bind(&AMFSolidPipe::Passthrough, this, std::placeholders::_1)) - , m_amfComponentDst(dst) -{ -} - -void AMFSolidPipe::Passthrough(amf::AMFDataPtr data) -{ - auto res = m_amfComponentDst->SubmitInput(data); - switch (res) { - case AMF_OK: - break; - case AMF_INPUT_FULL: - Debug("m_amfComponentDst->SubmitInput returns AMF_INPUT_FULL.\n"); - break; - case AMF_NEED_MORE_INPUT: - Debug("m_amfComponentDst->SubmitInput returns AMF_NEED_MORE_INPUT.\n"); - break; - default: - Debug("m_amfComponentDst->SubmitInput returns code %d.\n", res); - break; - } -} - -AMFPipeline::AMFPipeline() -{ -} - -AMFPipeline::~AMFPipeline() -{ - for (auto &pipe : m_pipes) { - delete pipe; - } -} - -void AMFPipeline::Connect(AMFPipe *pipe) -{ - m_pipes.emplace_back(pipe); -} - -void AMFPipeline::Run() -{ - for (auto &pipe : m_pipes) { - pipe->doPassthrough(); - } -} - -namespace alvr -{ - -EncodePipelineAMF::EncodePipelineAMF(Renderer *render, uint32_t width, uint32_t height) - : m_render(render) - , m_surfaceFormat(fromVkFormat(m_render->GetOutput().imageInfo.format)) - , m_codec(Settings::Instance().m_codec) - , m_refreshRate(Settings::Instance().m_refreshRate) - , m_renderWidth(width) - , m_renderHeight(height) -{ - if (!AMFContext::get()->isValid()) { - throw MakeException("AMFContext not valid"); - } - - Debug("Initializing EncodePipelineAMF.\n"); - - amf::AMFVulkanDevice *dev = new amf::AMFVulkanDevice; - dev->cbSizeof = sizeof(amf::AMFVulkanDevice); - dev->pNext = nullptr; - dev->hInstance = m_render->m_inst; - dev->hPhysicalDevice = m_render->m_physDev; - dev->hDevice = m_render->m_dev; - AMFContext::get()->initialize(dev); - - m_amfFactory = AMFContext::get()->factory(); - m_amfContext = AMFContext::get()->context(); - m_amfContext1 = amf::AMFContext1Ptr(m_amfContext); - - amf::AMF_SURFACE_FORMAT inFormat = m_surfaceFormat; - if (m_codec == ALVR_CODEC_H265 && Settings::Instance().m_use10bitEncoder) { - inFormat = amf::AMF_SURFACE_R10G10B10A2; - m_amfComponents.emplace_back(MakeConverter(m_surfaceFormat, m_renderWidth, m_renderHeight, inFormat)); - } else { - if (Settings::Instance().m_usePreproc) { - inFormat = amf::AMF_SURFACE_NV12; - m_amfComponents.emplace_back(MakeConverter(m_surfaceFormat, m_renderWidth, m_renderHeight, inFormat)); - m_amfComponents.emplace_back(MakePreprocessor(inFormat, m_renderWidth, m_renderHeight)); - } - } - m_amfComponents.emplace_back(MakeEncoder(inFormat, m_renderWidth, m_renderHeight, m_codec, m_refreshRate)); - auto params = FfiDynamicEncoderParams {}; - params.updated = true; - params.bitrate_bps = 30'000'000; - params.framerate = 60.0; - SetParams(params); - - m_pipeline = std::make_unique(); - for (size_t i = 0; i < m_amfComponents.size() - 1; i++) { - m_pipeline->Connect(new AMFSolidPipe(m_amfComponents[i], m_amfComponents[i + 1])); - } - - m_pipeline->Connect(new AMFPipe(m_amfComponents.back(), std::bind(&EncodePipelineAMF::Receive, this, std::placeholders::_1))); - - Debug("Successfully initialized EncodePipelineAMF.\n"); -} - -EncodePipelineAMF::~EncodePipelineAMF() = default; - -amf::AMFComponentPtr EncodePipelineAMF::MakeEncoder(amf::AMF_SURFACE_FORMAT inputFormat, int width, int height, int codec, int refreshRate) -{ - const wchar_t *pCodec; - - amf_int32 frameRateIn = refreshRate; - - switch (codec) { - case ALVR_CODEC_H264: - pCodec = AMFVideoEncoderVCE_AVC; - break; - case ALVR_CODEC_H265: - pCodec = AMFVideoEncoder_HEVC; - break; - default: - throw MakeException("Unsupported video encoding %d", codec); - } - - amf::AMFComponentPtr amfEncoder; - - // Create encoder component. - AMF_THROW_IF(m_amfFactory->CreateComponent(m_amfContext, pCodec, &amfEncoder)); - - if (codec == ALVR_CODEC_H264) { - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_USAGE, AMF_VIDEO_ENCODER_USAGE_ULTRA_LOW_LATENCY); - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_PROFILE, AMF_VIDEO_ENCODER_PROFILE_HIGH); - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_PROFILE_LEVEL, 42); - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_FRAMESIZE, ::AMFConstructSize(width, height)); - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_FRAMERATE, ::AMFConstructRate(frameRateIn, 1)); - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_B_PIC_PATTERN, 0); - - switch (Settings::Instance().m_rateControlMode) { - case ALVR_VBR: - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD, AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR); - break; - case ALVR_CBR: - default: - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD, AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR); - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_FILLER_DATA_ENABLE, Settings::Instance().m_fillerData); - break; - } - - switch (Settings::Instance().m_entropyCoding) { - case ALVR_CABAC: - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_CABAC_ENABLE, AMF_VIDEO_ENCODER_CABAC); - break; - case ALVR_CAVLC: - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_CABAC_ENABLE, AMF_VIDEO_ENCODER_CALV); - break; - } - - switch (Settings::Instance().m_amdEncoderQualityPreset) { - case ALVR_QUALITY: - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_QUALITY_PRESET, AMF_VIDEO_ENCODER_QUALITY_PRESET_QUALITY); - break; - case ALVR_BALANCED: - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_QUALITY_PRESET, AMF_VIDEO_ENCODER_QUALITY_PRESET_BALANCED); - break; - case ALVR_SPEED: - default: - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_QUALITY_PRESET, AMF_VIDEO_ENCODER_QUALITY_PRESET_SPEED); - break; - } - - // No noticable performance difference and should improve subjective quality by allocating more bits to smooth areas - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_ENABLE_VBAQ, Settings::Instance().m_enableVbaq); - - // Turns Off IDR/I Frames - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_IDR_PERIOD, 0); - - // Disable AUD to produce the same stream format as VideoEncoderNVENC. - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_INSERT_AUD, false); - - amf::AMFCapsPtr caps; - if (amfEncoder->GetCaps(&caps) == AMF_OK) { - caps->GetProperty(AMF_VIDEO_ENCODER_CAPS_QUERY_TIMEOUT_SUPPORT, &m_hasQueryTimeout); - } - if (m_hasQueryTimeout) { - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_QUERY_TIMEOUT, 1000); // 1s timeout - } - } else { - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_USAGE, AMF_VIDEO_ENCODER_HEVC_USAGE_ULTRA_LOW_LATENCY); - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_FRAMESIZE, ::AMFConstructSize(width, height)); - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_FRAMERATE, ::AMFConstructRate(frameRateIn, 1)); - - switch (Settings::Instance().m_rateControlMode) { - case ALVR_VBR: - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR); - break; - case ALVR_CBR: - default: - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR); - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_FILLER_DATA_ENABLE, Settings::Instance().m_fillerData); - break; - } - - switch (Settings::Instance().m_amdEncoderQualityPreset) { - case ALVR_QUALITY: - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET, AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_QUALITY); - break; - case ALVR_BALANCED: - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET, AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_BALANCED); - break; - case ALVR_SPEED: - default: - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET, AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_SPEED); - break; - } - - if (Settings::Instance().m_use10bitEncoder) { - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_COLOR_BIT_DEPTH, AMF_COLOR_BIT_DEPTH_10); - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_PROFILE, AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN_10); - } else { - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_COLOR_BIT_DEPTH, AMF_COLOR_BIT_DEPTH_8); - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_PROFILE, AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN); - } - - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE, AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE_FULL); - - // No noticable performance difference and should improve subjective quality by allocating more bits to smooth areas - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_ENABLE_VBAQ, Settings::Instance().m_enableVbaq); - - // Turns Off IDR/I Frames - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_NUM_GOPS_PER_IDR, 0); - // Set infinite GOP length - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_GOP_SIZE, 0); - - // Disable AUD to produce the same stream format as VideoEncoderNVENC. - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_INSERT_AUD, false); - - amf::AMFCapsPtr caps; - if (amfEncoder->GetCaps(&caps) == AMF_OK) { - caps->GetProperty(AMF_VIDEO_ENCODER_CAPS_HEVC_QUERY_TIMEOUT_SUPPORT, &m_hasQueryTimeout); - } - if (m_hasQueryTimeout) { - amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_QUERY_TIMEOUT, 1000); // 1s timeout - } - } - - Debug("Configured %s.\n", pCodec); - AMF_THROW_IF(amfEncoder->Init(inputFormat, width, height)); - - Debug("Initialized %s.\n", pCodec); - - return amfEncoder; -} - -amf::AMFComponentPtr EncodePipelineAMF::MakeConverter(amf::AMF_SURFACE_FORMAT inputFormat, int width, int height, amf::AMF_SURFACE_FORMAT outputFormat) -{ - amf::AMFComponentPtr amfConverter; - AMF_THROW_IF(m_amfFactory->CreateComponent(m_amfContext, AMFVideoConverter, &amfConverter)); - - AMF_THROW_IF(amfConverter->SetProperty(AMF_VIDEO_CONVERTER_MEMORY_TYPE, amf::AMF_MEMORY_VULKAN)); - AMF_THROW_IF(amfConverter->SetProperty(AMF_VIDEO_CONVERTER_OUTPUT_FORMAT, outputFormat)); - AMF_THROW_IF(amfConverter->SetProperty(AMF_VIDEO_CONVERTER_OUTPUT_SIZE, ::AMFConstructSize(width, height))); - - AMF_THROW_IF(amfConverter->Init(inputFormat, width, height)); - - Debug("Initialized %s.\n", AMFVideoConverter); - return amfConverter; -} - -amf::AMFComponentPtr EncodePipelineAMF::MakePreprocessor(amf::AMF_SURFACE_FORMAT inputFormat, int width, int height) -{ - amf::AMFComponentPtr amfPreprocessor; - AMF_THROW_IF(m_amfFactory->CreateComponent(m_amfContext, AMFPreProcessing, &amfPreprocessor)); - - AMF_THROW_IF(amfPreprocessor->SetProperty(AMF_PP_ENGINE_TYPE, amf::AMF_MEMORY_VULKAN)); - AMF_THROW_IF(amfPreprocessor->SetProperty(AMF_PP_ADAPTIVE_FILTER_STRENGTH, Settings::Instance().m_preProcSigma)); - AMF_THROW_IF(amfPreprocessor->SetProperty(AMF_PP_ADAPTIVE_FILTER_SENSITIVITY, Settings::Instance().m_preProcTor)); - - AMF_THROW_IF(amfPreprocessor->Init(inputFormat, width, height)); - - Debug("Initialized %s.\n", AMFPreProcessing); - return amfPreprocessor; -} - -void EncodePipelineAMF::PushFrame(uint64_t targetTimestampNs, bool idr) -{ - m_targetTimestampNs = targetTimestampNs; - - amf::AMFVulkanSurface surfaceVk; - surfaceVk.cbSizeof = sizeof(amf::AMFVulkanSurface); - surfaceVk.pNext = nullptr; - surfaceVk.hImage = m_render->GetOutput().image; - surfaceVk.hMemory = m_render->GetOutput().memory; - surfaceVk.iSize = m_render->GetOutput().size; - surfaceVk.eFormat = m_render->GetOutput().imageInfo.format; - surfaceVk.iWidth = m_render->GetOutput().imageInfo.extent.width; - surfaceVk.iHeight = m_render->GetOutput().imageInfo.extent.height; - surfaceVk.eCurrentLayout = m_render->GetOutput().layout; - surfaceVk.eUsage = amf::AMF_SURFACE_USAGE_TRANSFER_SRC | amf::AMF_SURFACE_USAGE_UNORDERED_ACCESS; - surfaceVk.eAccess = amf::AMF_MEMORY_CPU_LOCAL; - surfaceVk.Sync.cbSizeof = sizeof(amf::AMFVulkanSync); - surfaceVk.Sync.pNext = nullptr; - surfaceVk.Sync.hSemaphore = m_render->GetOutput().semaphore; - surfaceVk.Sync.bSubmitted = true; - surfaceVk.Sync.hFence = nullptr; - - amf::AMFSurfacePtr surface; - AMF_THROW_IF(m_amfContext1->CreateSurfaceFromVulkanNative(&surfaceVk, &surface, nullptr)); - - ApplyFrameProperties(surface, idr); - - m_amfComponents.front()->SubmitInput(surface); - - m_render->GetOutput().layout = static_cast(surfaceVk.eCurrentLayout); -} - -bool EncodePipelineAMF::GetEncoded(FramePacket &packet) -{ - m_frameBuffer = NULL; - if (m_hasQueryTimeout) { - m_pipeline->Run(); - } else { - uint32_t timeout = 4 * 1000; // 1 second - while (m_frameBuffer == NULL && --timeout != 0) { - std::this_thread::sleep_for(std::chrono::microseconds(250)); - m_pipeline->Run(); - } - } - - if (m_frameBuffer == NULL) { - Error("Timed out waiting for encoder data"); - return false; - } - - packet.data = reinterpret_cast(m_frameBuffer->GetNative()); - packet.size = static_cast(m_frameBuffer->GetSize()); - packet.pts = m_targetTimestampNs; - std::uint64_t type; - if (m_codec == ALVR_CODEC_H264) { - m_frameBuffer->GetProperty(AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE, &type); - packet.isIDR = type == AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE_IDR; - } else { - m_frameBuffer->GetProperty(AMF_VIDEO_ENCODER_HEVC_OUTPUT_DATA_TYPE, &type); - packet.isIDR = type == AMF_VIDEO_ENCODER_HEVC_OUTPUT_DATA_TYPE_IDR; - } - - return true; -} - -void EncodePipelineAMF::SetParams(FfiDynamicEncoderParams params) -{ - if (!params.updated) { - return; - } - amf_int64 bitRateIn = params.bitrate_bps / params.framerate * m_refreshRate; - if (m_codec == ALVR_CODEC_H264) { - m_amfComponents.back()->SetProperty(AMF_VIDEO_ENCODER_TARGET_BITRATE, bitRateIn); - m_amfComponents.back()->SetProperty(AMF_VIDEO_ENCODER_PEAK_BITRATE, bitRateIn); - m_amfComponents.back()->SetProperty(AMF_VIDEO_ENCODER_VBV_BUFFER_SIZE, bitRateIn / m_refreshRate * 1.1); - } else { - m_amfComponents.back()->SetProperty(AMF_VIDEO_ENCODER_HEVC_TARGET_BITRATE, bitRateIn); - m_amfComponents.back()->SetProperty(AMF_VIDEO_ENCODER_HEVC_PEAK_BITRATE, bitRateIn); - m_amfComponents.back()->SetProperty(AMF_VIDEO_ENCODER_HEVC_VBV_BUFFER_SIZE, bitRateIn / m_refreshRate * 1.1); - } - - if (Settings::Instance().m_amdBitrateCorruptionFix) { - RequestIDR(); - } -} - -void EncodePipelineAMF::Receive(amf::AMFDataPtr data) -{ - m_frameBuffer = amf::AMFBufferPtr(data); // query for buffer interface -} - -void EncodePipelineAMF::ApplyFrameProperties(const amf::AMFSurfacePtr &surface, bool insertIDR) -{ - switch (m_codec) { - case ALVR_CODEC_H264: - surface->SetProperty(AMF_VIDEO_ENCODER_INSERT_AUD, false); - if (insertIDR) { - Debug("Inserting IDR frame for H.264.\n"); - surface->SetProperty(AMF_VIDEO_ENCODER_INSERT_SPS, true); - surface->SetProperty(AMF_VIDEO_ENCODER_INSERT_PPS, true); - surface->SetProperty(AMF_VIDEO_ENCODER_FORCE_PICTURE_TYPE, AMF_VIDEO_ENCODER_PICTURE_TYPE_IDR); - } - break; - case ALVR_CODEC_H265: - surface->SetProperty(AMF_VIDEO_ENCODER_HEVC_INSERT_AUD, false); - if (insertIDR) { - Debug("Inserting IDR frame for H.265.\n"); - // Insert VPS,SPS,PPS - surface->SetProperty(AMF_VIDEO_ENCODER_HEVC_INSERT_HEADER, true); - surface->SetProperty(AMF_VIDEO_ENCODER_HEVC_FORCE_PICTURE_TYPE, AMF_VIDEO_ENCODER_HEVC_PICTURE_TYPE_IDR); - } - break; - default: - throw MakeException("Invalid video codec"); - } -} - -}; diff --git a/alvr/server/cpp/platform/linux/EncodePipelineAMF.h b/alvr/server/cpp/platform/linux/EncodePipelineAMF.h deleted file mode 100644 index 5a8db9779b..0000000000 --- a/alvr/server/cpp/platform/linux/EncodePipelineAMF.h +++ /dev/null @@ -1,101 +0,0 @@ -#pragma once -#include "EncodePipeline.h" -#include "ffmpeg_helper.h" - -#include -#include - -#include "../../shared/amf/public/common/AMFFactory.h" -#include "../../shared/amf/public/include/components/VideoEncoderVCE.h" -#include "../../shared/amf/public/include/components/VideoEncoderHEVC.h" -#include "../../shared/amf/public/include/components/VideoConverter.h" -#include "../../shared/amf/public/include/components/PreProcessing.h" -#include "../../shared/amf/public/include/core/VulkanAMF.h" -#include "../../shared/amf/public/common/AMFSTL.h" -#include "../../shared/amf/public/common/Thread.h" - -typedef std::function AMFDataReceiver; - -class AMFPipeline; - -class AMFPipe -{ -public: - AMFPipe(amf::AMFComponentPtr src, AMFDataReceiver receiver); - virtual ~AMFPipe(); - - void doPassthrough(); -protected: - amf::AMFComponentPtr m_amfComponentSrc; - AMFDataReceiver m_receiver; -}; - -class AMFSolidPipe : public AMFPipe -{ -public: - AMFSolidPipe(amf::AMFComponentPtr src, amf::AMFComponentPtr dst); -protected: - void Passthrough(amf::AMFDataPtr data); - - amf::AMFComponentPtr m_amfComponentDst; -}; - -class AMFPipeline -{ -public: - AMFPipeline(); - ~AMFPipeline(); - - void Connect(AMFPipe *pipe); - void Run(); -protected: - std::vector m_pipes; -}; - -enum EncoderQualityPreset { - QUALITY = 0, - BALANCED = 1, - SPEED = 2 -}; - -namespace alvr -{ - -class EncodePipelineAMF : public EncodePipeline -{ -public: - EncodePipelineAMF(Renderer *render, uint32_t width, uint32_t height); - ~EncodePipelineAMF(); - - void PushFrame(uint64_t targetTimestampNs, bool idr) override; - bool GetEncoded(FramePacket &packet) override; - void SetParams(FfiDynamicEncoderParams params) override; - -private: - amf::AMFComponentPtr MakeConverter(amf::AMF_SURFACE_FORMAT inputFormat, int width, int height, amf::AMF_SURFACE_FORMAT outputFormat); - amf::AMFComponentPtr MakePreprocessor(amf::AMF_SURFACE_FORMAT inputFormat, int width, int height); - amf::AMFComponentPtr MakeEncoder(amf::AMF_SURFACE_FORMAT inputFormat, int width, int height, int codec, int refreshRate); - void Receive(amf::AMFDataPtr data); - void ApplyFrameProperties(const amf::AMFSurfacePtr &surface, bool insertIDR); - - amf::AMFFactory *m_amfFactory = nullptr; - amf::AMFContextPtr m_amfContext; - amf::AMFContext1Ptr m_amfContext1; - std::unique_ptr m_pipeline; - std::vector m_amfComponents; - - Renderer *m_render; - amf::AMF_SURFACE_FORMAT m_surfaceFormat; - - int m_codec; - int m_refreshRate; - int m_renderWidth; - int m_renderHeight; - - bool m_hasQueryTimeout = false; - - amf::AMFBufferPtr m_frameBuffer; - uint64_t m_targetTimestampNs; -}; - -}; diff --git a/alvr/server/cpp/platform/linux/amf_helper.cpp b/alvr/server/cpp/platform/linux/amf_helper.cpp deleted file mode 100644 index 8ab9a15db9..0000000000 --- a/alvr/server/cpp/platform/linux/amf_helper.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include "amf_helper.h" -#include "alvr_server/Logger.h" - -#include -#include -#include - -namespace alvr -{ - -class TraceWriter : public amf::AMFTraceWriter -{ -public: - void AMF_CDECL_CALL Write(const wchar_t *, const wchar_t *message) override - { - Info("AMF: %ls", message); - } - - void AMF_CDECL_CALL Flush() override - { - } -}; - -AMFContext::AMFContext() -{ - init(); -} - -bool AMFContext::isValid() const -{ - return m_valid; -} - -amf::AMFFactory *AMFContext::factory() const -{ - return m_factory; -} - -amf::AMFContextPtr AMFContext::context() const -{ - return m_context; -} - -std::vector AMFContext::requiredDeviceExtensions() const -{ - if (!m_context1) { - return {}; - } - size_t count; - m_context1->GetVulkanDeviceExtensions(&count, nullptr); - std::vector out(count); - m_context1->GetVulkanDeviceExtensions(&count, out.data()); - return out; -} - -void AMFContext::initialize(amf::AMFVulkanDevice *dev) -{ - if (!m_context1) { - throw "No Context1"; - } - - bool ok = m_context1->InitVulkan(dev) == AMF_OK; - - unsetenv("VK_DRIVER_FILES"); - unsetenv("VK_ICD_FILENAMES"); - - if (!ok) { - throw "Failed to initialize Vulkan AMF"; - } -} - -const wchar_t *AMFContext::resultString(AMF_RESULT res) -{ - return m_trace->GetResultText(res); -} - -AMFContext *AMFContext::get() -{ - static AMFContext *s = nullptr; - if (!s) { - s = new AMFContext; - } - return s; -} - -void AMFContext::init() -{ - void *amf_module = dlopen(AMF_DLL_NAMEA, RTLD_LAZY); - if (!amf_module) { - return; - } - - auto init = (AMFInit_Fn)dlsym(amf_module, AMF_INIT_FUNCTION_NAME); - if (!init) { - return; - } - - if (init(AMF_FULL_VERSION, &m_factory) != AMF_OK) { - return; - } - - if (m_factory->GetTrace(&m_trace) != AMF_OK) { - return; - } - - m_trace->EnableWriter(AMF_TRACE_WRITER_CONSOLE, false); - m_trace->EnableWriter(AMF_TRACE_WRITER_DEBUG_OUTPUT, false); - m_trace->RegisterWriter(L"alvr-amf-trace", new TraceWriter, true); - m_trace->SetWriterLevel(L"alvr-amf-trace", AMF_TRACE_WARNING); - - if (m_factory->CreateContext(&m_context) != AMF_OK) { - return; - } - - m_context1 = amf::AMFContext1Ptr(m_context); - - char *vk_icd_file = getenv("ALVR_AMF_ICD"); - if (!vk_icd_file || access(vk_icd_file, F_OK) != 0) { - return; - } - - setenv("VK_DRIVER_FILES", vk_icd_file, 1); - setenv("VK_ICD_FILENAMES", vk_icd_file, 1); - - m_valid = true; -} - -}; diff --git a/alvr/server/cpp/platform/linux/amf_helper.h b/alvr/server/cpp/platform/linux/amf_helper.h deleted file mode 100644 index 6b5ae118e5..0000000000 --- a/alvr/server/cpp/platform/linux/amf_helper.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "../../shared/amf/public/common/AMFFactory.h" -#include "../../shared/amf/public/include/core/VulkanAMF.h" - -namespace alvr -{ - -class AMFContext -{ -public: - bool isValid() const; - amf::AMFFactory *factory() const; - amf::AMFContextPtr context() const; - std::vector requiredDeviceExtensions() const; - void initialize(amf::AMFVulkanDevice *dev); - const wchar_t *resultString(AMF_RESULT res); - - static AMFContext *get(); - -private: - explicit AMFContext(); - - void init(); - - bool m_valid = false; - amf::AMFFactory *m_factory = nullptr; - amf::AMFTrace *m_trace = nullptr; - amf::AMFContextPtr m_context; - amf::AMFContext1Ptr m_context1; -}; - -}; diff --git a/wiki/Configuration-Information-and-Recommendations.md b/wiki/Configuration-Information-and-Recommendations.md index d451e888a7..2f6301a70d 100644 --- a/wiki/Configuration-Information-and-Recommendations.md +++ b/wiki/Configuration-Information-and-Recommendations.md @@ -57,7 +57,7 @@ Some points came from [FingrMastr](https://github.com/FingrMastr) # Linux ## Encoder requirements -ALVR uses FFmpeg for all encoders (except AMF), so you will need to make sure the encoder of your choice works with FFmpeg. +ALVR uses FFmpeg for all encoders, so you will need to make sure the encoder of your choice works with FFmpeg. Always consult Log tab in dashboard, it will tell you the reason why an encoder failed to initialize. ### VAAPI (AMD/Intel GPUs) @@ -100,19 +100,6 @@ ffmpeg -vaapi_device /dev/dri/renderD128 -f lavfi -i testsrc -t 30 -vf 'format=n ffmpeg -vaapi_device /dev/dri/renderD128 -f lavfi -i testsrc -t 30 -vf 'format=nv12,hwupload' -c:v hevc_vaapi vaapi-hevc.mp4 ``` -### AMF (AMD GPUs) - -AMF requires proprietary Vulkan driver amd-pro. Troubleshooting AMF installation on your system is out of scope here, but you -can use [amf-test](https://github.com/nowrep/amf-test-linux). HEVC is only supported on RDNA and newer GPUs. - -Make sure amf-test succeeds before you try to get it working with ALVR. -You will need to tell ALVR where to find amd-pro driver, edit your SteamVR launch command (change the path as appropriate -for your system): - - env ALVR_AMF_ICD=/path/to/amd_pro_icd64.json %command% - -ALVR should now be able to use AMF. - ### NVENC (NVidia) Requires *libcuda*.