Skip to content

Commit

Permalink
feat. Added nvenc encoder. Added deps to dependencies. Change while l…
Browse files Browse the repository at this point in the history
…oop for wait to push frame to if stat with continue stat.
  • Loading branch information
Toxblh committed Jan 3, 2022
1 parent 2f74e2a commit 08876fb
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 3 deletions.
5 changes: 4 additions & 1 deletion alvr/server/cpp/platform/linux/CEncoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,10 @@ void CEncoder::Run() {
}

encoded_data.clear();
while (encode_pipeline->GetEncoded(encoded_data)) {}
// Encoders can req more then once frame, need to accumulate more data before sending it to the client
if (!encode_pipeline->GetEncoded(encoded_data)) {
continue;
}

m_listener->SendVideo(encoded_data.data(), encoded_data.size(), m_poseSubmitIndex + Settings::Instance().m_trackingFrameOffset);

Expand Down
7 changes: 7 additions & 0 deletions alvr/server/cpp/platform/linux/EncodePipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "alvr_server/Settings.h"
#include "EncodePipelineSW.h"
#include "EncodePipelineVAAPI.h"
#include "EncodePipelineNvEnc.h"
#include "ffmpeg_helper.h"

extern "C" {
Expand Down Expand Up @@ -75,6 +76,12 @@ std::unique_ptr<alvr::EncodePipeline> alvr::EncodePipeline::Create(std::vector<V
{
Info("failed to create VAAPI encoder");
}
try {
return std::make_unique<alvr::EncodePipelineNvEnc>(input_frames, vk_frame_ctx);
} catch (...)
{
Info("failed to create NvEnc encoder");
}
return std::make_unique<alvr::EncodePipelineSW>(input_frames, vk_frame_ctx);
}

Expand Down
108 changes: 108 additions & 0 deletions alvr/server/cpp/platform/linux/EncodePipelineNvEnc.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#include "EncodePipelineNvEnc.h"
#include "ALVR-common/packet_types.h"
#include "alvr_server/Settings.h"
#include "ffmpeg_helper.h"
#include <chrono>

extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include <libswscale/swscale.h>
}

namespace {

const char *encoder(ALVR_CODEC codec) {
switch (codec) {
case ALVR_CODEC_H264:
return "h264_nvenc";
case ALVR_CODEC_H265:
return "hevc_nvenc";
}
throw std::runtime_error("invalid codec " + std::to_string(codec));
}

} // namespace
alvr::EncodePipelineNvEnc::EncodePipelineNvEnc(std::vector<VkFrame> &input_frames,
VkFrameCtx &vk_frame_ctx) {
int err;
for (auto &input_frame : input_frames) {
vk_frames.push_back(input_frame.make_av_frame(vk_frame_ctx).release());
}

const auto &settings = Settings::Instance();

auto codec_id = ALVR_CODEC(settings.m_codec);
const char *encoder_name = encoder(codec_id);
AVCodec *codec = AVCODEC.avcodec_find_encoder_by_name(encoder_name);
if (codec == nullptr) {
throw std::runtime_error(std::string("Failed to find encoder ") + encoder_name);
}

encoder_ctx = AVCODEC.avcodec_alloc_context3(codec);
if (not encoder_ctx) {
throw std::runtime_error("failed to allocate NVEnc encoder");
}

switch (codec_id) {
case ALVR_CODEC_H264:
AVUTIL.av_opt_set(encoder_ctx, "preset", "llhq", 0);
AVUTIL.av_opt_set(encoder_ctx, "zerolatency", "1", 0);
break;
case ALVR_CODEC_H265:
AVUTIL.av_opt_set(encoder_ctx, "preset", "llhq", 0);
AVUTIL.av_opt_set(encoder_ctx, "zerolatency", "1", 0);
break;
}

/**
* We will recieve a frame from HW as AV_PIX_FMT_VULKAN which will converted to AV_PIX_FMT_BGRA
* as SW format when we get it from HW.
* But NVEnc support only BGR0 format and we easy can just to force it
* Because:
* AV_PIX_FMT_BGRA - 28 ///< packed BGRA 8:8:8:8, 32bpp, BGRABGRA...
* AV_PIX_FMT_BGR0 - 123 ///< packed BGR 8:8:8, 32bpp, BGRXBGRX... X=unused/undefined
*
* We just to ignore the alpha channel and it's done
*/
encoder_ctx->pix_fmt = AV_PIX_FMT_BGR0;
encoder_ctx->width = settings.m_renderWidth;
encoder_ctx->height = settings.m_renderHeight;
encoder_ctx->time_base = {std::chrono::steady_clock::period::num,
std::chrono::steady_clock::period::den};
encoder_ctx->framerate = AVRational{settings.m_refreshRate, 1};
encoder_ctx->sample_aspect_ratio = AVRational{1, 1};
encoder_ctx->max_b_frames = 0;
encoder_ctx->gop_size = 30;
encoder_ctx->bit_rate = settings.mEncodeBitrateMBs * 1000 * 1000;

err = AVCODEC.avcodec_open2(encoder_ctx, codec, NULL);
if (err < 0) {
throw alvr::AvException("Cannot open video encoder codec:", err);
}

hw_frame = AVUTIL.av_frame_alloc();
}

alvr::EncodePipelineNvEnc::~EncodePipelineNvEnc() {
for (auto &vk_frame : vk_frames)
AVUTIL.av_frame_free(&vk_frame);
AVUTIL.av_buffer_unref(&hw_ctx);
AVUTIL.av_frame_free(&hw_frame);
}

void alvr::EncodePipelineNvEnc::PushFrame(uint32_t frame_index, bool idr) {
assert(frame_index < vk_frames.size());

int err = AVUTIL.av_hwframe_transfer_data(hw_frame, vk_frames[frame_index], 0);
if (err) {
throw alvr::AvException("av_hwframe_transfer_data", err);
}

hw_frame->pict_type = idr ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_NONE;
hw_frame->pts = std::chrono::steady_clock::now().time_since_epoch().count();

if ((err = AVCODEC.avcodec_send_frame(encoder_ctx, hw_frame)) < 0) {
throw alvr::AvException("avcodec_send_frame failed:", err);
}
}
25 changes: 25 additions & 0 deletions alvr/server/cpp/platform/linux/EncodePipelineNvEnc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#pragma once

#include "EncodePipeline.h"

extern "C" struct AVBufferRef;
extern "C" struct AVCodecContext;
extern "C" struct AVFrame;

namespace alvr
{

class EncodePipelineNvEnc: public EncodePipeline
{
public:
~EncodePipelineNvEnc();
EncodePipelineNvEnc(std::vector<VkFrame> &input_frames, VkFrameCtx& vk_frame_ctx);

void PushFrame(uint32_t frame_index, bool idr) override;

private:
AVBufferRef *hw_ctx = nullptr;
std::vector<AVFrame *> vk_frames;
AVFrame * hw_frame = nullptr;
};
}
5 changes: 3 additions & 2 deletions alvr/xtask/src/dependencies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,11 @@ pub fn build_ffmpeg_linux() -> std::path::PathBuf {
"--disable-network",
"--enable-lto",
format!(
"--disable-everything {} {} {} {}",
"--disable-everything {} {} {} {} {}",
"--enable-encoder=h264_nvenc --enable-encoder=hevc_nvenc --enable-nonfree --enable-nvenc --enable-cuda --enable-cuda-nvcc --enable-libnpp --nvccflags=\"-gencode arch=compute_52,code=sm_52 -O2\" --extra-cflags=-I/usr/local/cuda/include/ --extra-ldflags=-L/usr/local/cuda/lib64/",
"--enable-encoder=h264_vaapi --enable-encoder=hevc_vaapi",
"--enable-encoder=libx264 --enable-encoder=libx264rgb --enable-encoder=libx265",
"--enable-hwaccel=h264_vaapi --enable-hwaccel=hevc_vaapi",
"--enable-hwaccel=h264_vaapi --enable-hwaccel=hevc_vaapi --enable-hwaccel=h264_nvenc --enable-hwaccel=hevc_nvenc",
"--enable-filter=scale --enable-filter=scale_vaapi",
),
"--enable-libx264 --enable-libx265 --enable-vulkan",
Expand Down

0 comments on commit 08876fb

Please sign in to comment.