Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request configuration headers from encoder #1385

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions alvr/client_core/src/c_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,12 @@ pub extern "C" fn alvr_request_idr() {
crate::request_idr();
}

/// Call only with external decoder
#[no_mangle]
pub extern "C" fn alvr_request_config_nal() {
crate::request_config_nal();
}

/// Call only with external decoder
#[no_mangle]
pub extern "C" fn alvr_report_frame_decoded(target_timestamp_ns: u64) {
Expand Down
2 changes: 1 addition & 1 deletion alvr/client_core/src/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ pub extern "C" fn push_nal(buffer: *const c_char, length: i32, timestamp_ns: u64
show_err(decoder.push_frame_nal(timestamp, &nal, Duration::from_millis(500)));
} else if let Some(sender) = &*crate::CONTROL_CHANNEL_SENDER.lock() {
sender
.send(alvr_sockets::ClientControlPacket::RequestIdr)
.send(alvr_sockets::ClientControlPacket::RequestConfigNAL)
.ok();
}
}
Expand Down
7 changes: 7 additions & 0 deletions alvr/client_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,13 @@ pub fn request_idr() {
}
}

/// Call only with external decoder
pub fn request_config_nal() {
if let Some(sender) = &*CONTROL_CHANNEL_SENDER.lock() {
sender.send(ClientControlPacket::RequestConfigNAL).ok();
}
}

/// Call only with external decoder
pub fn report_frame_decoded(target_timestamp: Duration) {
if let Some(stats) = &mut *STATISTICS_MANAGER.lock() {
Expand Down
53 changes: 0 additions & 53 deletions alvr/server/cpp/alvr_server/ClientConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@
#include "Utils.h"
#include "Settings.h"

static const uint8_t NAL_TYPE_SPS = 7;
static const uint8_t H265_NAL_TYPE_VPS = 32;

ClientConnection::ClientConnection() {

m_Statistics = std::make_shared<Statistics>();
Expand All @@ -21,31 +18,6 @@ ClientConnection::ClientConnection() {
m_fecPercentage = INITIAL_FEC_PERCENTAGE;
}

int findVPSSPS(const uint8_t *frameBuffer, int frameByteSize) {
int zeroes = 0;
int foundNals = 0;
for (int i = 0; i < frameByteSize; i++) {
if (frameBuffer[i] == 0) {
zeroes++;
} else if (frameBuffer[i] == 1) {
if (zeroes >= 2) {
foundNals++;
if (Settings::Instance().m_codec == ALVR_CODEC_H264 && foundNals >= 3) {
// Find end of SPS+PPS on H.264.
return i - 3;
} else if (Settings::Instance().m_codec == ALVR_CODEC_H265 && foundNals >= 4) {
// Find end of VPS+SPS+PPS on H.264.
return i - 3;
}
}
zeroes = 0;
} else {
zeroes = 0;
}
}
return -1;
}

void ClientConnection::FECSend(uint8_t *buf, int len, uint64_t targetTimestampNs, uint64_t videoFrameIndex) {
int shardPackets = CalculateFECShardPackets(len, m_fecPercentage);

Expand Down Expand Up @@ -133,31 +105,6 @@ void ClientConnection::SendVideo(uint8_t *buf, int len, uint64_t targetTimestamp
// Report before the frame is packetized
ReportEncoded(targetTimestampNs);

uint8_t NALType;
if (Settings::Instance().m_codec == ALVR_CODEC_H264)
NALType = buf[4] & 0x1F;
else
NALType = (buf[4] >> 1) & 0x3F;

if ((Settings::Instance().m_codec == ALVR_CODEC_H264 && NALType == NAL_TYPE_SPS) ||
(Settings::Instance().m_codec == ALVR_CODEC_H265 && NALType == H265_NAL_TYPE_VPS)) {
// This frame contains (VPS + )SPS + PPS + IDR on NVENC H.264 (H.265) stream.
// (VPS + )SPS + PPS has short size (8bytes + 28bytes in some environment), so we can
// assume SPS + PPS is contained in first fragment.

int end = findVPSSPS(buf, len);
if (end == -1) {
// Invalid frame.
return;
}

InitializeDecoder((const unsigned char *)buf, end);

// move the cursor forward excluding config NALs
buf = &buf[end];
len = len - end;
}

if (Settings::Instance().m_enableFec) {
FECSend(buf, len, targetTimestampNs, mVideoFrameIndex);
} else {
Expand Down
6 changes: 6 additions & 0 deletions alvr/server/cpp/alvr_server/alvr_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,12 @@ void RequestIDR() {
}
}

void RequestConfigNAL() {
if (g_driver_provider.hmd && g_driver_provider.hmd->m_encoder) {
g_driver_provider.hmd->m_encoder->GetConfigNAL();
}
}

void SetTracking(unsigned long long targetTimestampNs,
float controllerPoseTimeOffsetS,
const AlvrDeviceMotion *deviceMotions,
Expand Down
1 change: 1 addition & 0 deletions alvr/server/cpp/alvr_server/bindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ extern "C" void InitializeStreaming();
extern "C" void DeinitializeStreaming();
extern "C" void SendVSync(float frameIntervalS);
extern "C" void RequestIDR();
extern "C" void RequestConfigNAL();
extern "C" void SetTracking(unsigned long long targetTimestampNs,
float controllerPoseTimeOffsetS,
const AlvrDeviceMotion *deviceMotions,
Expand Down
12 changes: 7 additions & 5 deletions alvr/server/cpp/platform/linux/CEncoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ void CEncoder::Run() {

alvr::VkFrameCtx vk_frame_ctx(vk_ctx, output.imageInfo);
alvr::VkFrame frame(vk_ctx, output.image, output.imageInfo, output.size, output.memory, output.drm);
auto encode_pipeline = alvr::EncodePipeline::Create(&render, vk_ctx, frame, vk_frame_ctx, render.GetEncodingWidth(), render.GetEncodingHeight());
m_encodePipeline = alvr::EncodePipeline::Create(&render, vk_ctx, frame, vk_frame_ctx, render.GetEncodingWidth(), render.GetEncodingHeight());

fprintf(stderr, "CEncoder starting to read present packets");
present_packet frame_info;
Expand All @@ -229,7 +229,7 @@ void CEncoder::Run() {
read_latest(client, (char *)&frame_info, sizeof(frame_info), m_exiting);

if (m_listener->GetStatistics()->CheckBitrateUpdated()) {
encode_pipeline->SetBitrate(m_listener->GetStatistics()->GetBitrate() * 1000000L); // in bits;
m_encodePipeline->SetBitrate(m_listener->GetStatistics()->GetBitrate() * 1000000L); // in bits;
}

auto pose = m_poseHistory->GetBestPoseMatch((const vr::HmdMatrix34_t&)frame_info.pose);
Expand All @@ -246,19 +246,19 @@ void CEncoder::Run() {

render.Render(frame_info.image, frame_info.semaphore_value);

encode_pipeline->PushFrame(pose->targetTimestampNs, m_scheduler.CheckIDRInsertion());
m_encodePipeline->PushFrame(pose->targetTimestampNs, m_scheduler.CheckIDRInsertion());

static_assert(sizeof(frame_info.pose) == sizeof(vr::HmdMatrix34_t&));

encoded_data.clear();
uint64_t pts;
if (!encode_pipeline->GetEncoded(encoded_data, &pts)) {
if (!m_encodePipeline->GetEncoded(encoded_data, &pts)) {
Error("Failed to get encoded data!");
continue;
}

auto render_timestamps = render.GetTimestamps();
auto encode_timestamp = encode_pipeline->GetTimestamp();
auto encode_timestamp = m_encodePipeline->GetTimestamp();

uint64_t present_offset = render_timestamps.now - render_timestamps.renderBegin;
uint64_t composed_offset = 0;
Expand Down Expand Up @@ -306,4 +306,6 @@ void CEncoder::OnPacketLoss() { m_scheduler.OnPacketLoss(); }

void CEncoder::InsertIDR() { m_scheduler.InsertIDR(); }

void CEncoder::GetConfigNAL() { m_encodePipeline->GetConfigNAL(); }

void CEncoder::CaptureFrame() { m_captureFrame = true; }
2 changes: 2 additions & 0 deletions alvr/server/cpp/platform/linux/CEncoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ class CEncoder : public CThread {
void Stop();
void OnPacketLoss();
void InsertIDR();
void GetConfigNAL();
bool IsConnected() { return m_connected; }
void CaptureFrame();

private:
void GetFds(int client, int (*fds)[6]);
std::shared_ptr<ClientConnection> m_listener;
std::shared_ptr<PoseHistory> m_poseHistory;
std::unique_ptr<alvr::EncodePipeline> m_encodePipeline;
std::atomic_bool m_exiting{false};
IDRScheduler m_scheduler;
pollfd m_socket;
Expand Down
1 change: 1 addition & 0 deletions alvr/server/cpp/platform/linux/EncodePipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class EncodePipeline

virtual void PushFrame(uint64_t targetTimestampNs, bool idr) = 0;
virtual bool GetEncoded(std::vector<uint8_t> & out, uint64_t *pts);
virtual void GetConfigNAL() = 0;
virtual Timestamp GetTimestamp() { return timestamp; }

virtual void SetBitrate(int64_t bitrate);
Expand Down
17 changes: 11 additions & 6 deletions alvr/server/cpp/platform/linux/EncodePipelineAMF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -500,18 +500,12 @@ void EncodePipelineAMF::ApplyFrameProperties(const amf::AMFSurfacePtr &surface,
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;
Expand All @@ -520,4 +514,15 @@ void EncodePipelineAMF::ApplyFrameProperties(const amf::AMFSurfacePtr &surface,
}
}

void EncodePipelineAMF::GetConfigNAL() {
amf::AMFVariant var;
if (m_codec == ALVR_CODEC_H264) {
m_amfComponents.back()->GetProperty(AMF_VIDEO_ENCODER_EXTRADATA, &var);
} else {
m_amfComponents.back()->GetProperty(AMF_VIDEO_ENCODER_HEVC_EXTRADATA, &var);
}
amf::AMFBufferPtr buffer(var.pInterface);
InitializeDecoder(reinterpret_cast<unsigned char *>(buffer->GetNative()), buffer->GetSize());
}

};
2 changes: 1 addition & 1 deletion alvr/server/cpp/platform/linux/EncodePipelineAMF.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class EncodePipelineAMF : public EncodePipeline
void PushFrame(uint64_t targetTimestampNs, bool idr) override;
bool GetEncoded(std::vector<uint8_t> &out, uint64_t *pts) override;
void SetBitrate(int64_t bitrate) override;

void GetConfigNAL() 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);
Expand Down
6 changes: 6 additions & 0 deletions alvr/server/cpp/platform/linux/EncodePipelineNvEnc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ alvr::EncodePipelineNvEnc::EncodePipelineNvEnc(Renderer *render,
encoder_ctx->gop_size = INT16_MAX;
encoder_ctx->bit_rate = settings.mEncodeBitrateMBs * 1000 * 1000;

encoder_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

err = avcodec_open2(encoder_ctx, codec, NULL);
if (err < 0) {
throw alvr::AvException("Cannot open video encoder codec:", err);
Expand Down Expand Up @@ -148,3 +150,7 @@ void alvr::EncodePipelineNvEnc::PushFrame(uint64_t targetTimestampNs, bool idr)
throw alvr::AvException("avcodec_send_frame failed:", err);
}
}

void alvr::EncodePipelineNvEnc::GetConfigNAL() {
InitializeDecoder(encoder_ctx->extradata, encoder_ctx->extradata_size);
}
1 change: 1 addition & 0 deletions alvr/server/cpp/platform/linux/EncodePipelineNvEnc.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class EncodePipelineNvEnc: public EncodePipeline
EncodePipelineNvEnc(Renderer *render, VkFrame &input_frame, VkFrameCtx& vk_frame_ctx, uint32_t width, uint32_t height);

void PushFrame(uint64_t targetTimestampNs, bool idr) override;
void GetConfigNAL() override;

private:
Renderer *r = nullptr;
Expand Down
6 changes: 6 additions & 0 deletions alvr/server/cpp/platform/linux/EncodePipelineSW.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ alvr::EncodePipelineSW::EncodePipelineSW(Renderer *render, uint32_t width, uint3
encoder_ctx->thread_type = FF_THREAD_SLICE;
encoder_ctx->thread_count = settings.m_swThreadCount;

encoder_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

int err = avcodec_open2(encoder_ctx, codec, &opt);
if (err < 0) {
throw alvr::AvException("Cannot open video encoder codec:", err);
Expand Down Expand Up @@ -127,3 +129,7 @@ void alvr::EncodePipelineSW::PushFrame(uint64_t targetTimestampNs, bool idr)
throw alvr::AvException("avcodec_send_frame failed:", err);
}
}

void alvr::EncodePipelineSW::GetConfigNAL() {
InitializeDecoder(encoder_ctx->extradata, encoder_ctx->extradata_size);
}
1 change: 1 addition & 0 deletions alvr/server/cpp/platform/linux/EncodePipelineSW.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class EncodePipelineSW: public EncodePipeline
EncodePipelineSW(Renderer *render, uint32_t width, uint32_t height);

void PushFrame(uint64_t targetTimestampNs, bool idr) override;
void GetConfigNAL() override;

private:
AVFrame *encoder_frame = nullptr;
Expand Down
6 changes: 6 additions & 0 deletions alvr/server/cpp/platform/linux/EncodePipelineVAAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ alvr::EncodePipelineVAAPI::EncodePipelineVAAPI(Renderer *render, VkContext &vk_c
encoder_ctx->rc_max_rate = encoder_ctx->bit_rate;
encoder_ctx->rc_buffer_size = encoder_ctx->bit_rate / settings.m_refreshRate;

encoder_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
Vixea marked this conversation as resolved.
Show resolved Hide resolved

vlVaQualityBits quality = {};
quality.valid_setting = 1;
quality.vbaq_mode = Settings::Instance().m_enableVbaq; //No noticable performance difference and should improve subjective quality by allocating more bits to smooth areas
Expand Down Expand Up @@ -318,3 +320,7 @@ void alvr::EncodePipelineVAAPI::PushFrame(uint64_t targetTimestampNs, bool idr)
}
av_frame_unref(encoder_frame);
}

void alvr::EncodePipelineVAAPI::GetConfigNAL() {
InitializeDecoder(encoder_ctx->extradata, encoder_ctx->extradata_size);
}
1 change: 1 addition & 0 deletions alvr/server/cpp/platform/linux/EncodePipelineVAAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class EncodePipelineVAAPI: public EncodePipeline
EncodePipelineVAAPI(Renderer *render, VkContext &vk_ctx, VkFrame &input_frame, uint32_t width, uint32_t height);

void PushFrame(uint64_t targetTimestampNs, bool idr) override;
void GetConfigNAL() override;

private:
Renderer *r = nullptr;
Expand Down
4 changes: 4 additions & 0 deletions alvr/server/cpp/platform/win32/CEncoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,5 +137,9 @@
m_scheduler.InsertIDR();
}

void CEncoder::GetConfigNAL() {
m_videoEncoder->GetConfigNAL();
}

void CEncoder::CaptureFrame() {
}
2 changes: 2 additions & 0 deletions alvr/server/cpp/platform/win32/CEncoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@

void CaptureFrame();

void GetConfigNAL();

private:
CThreadEvent m_newFrameReady, m_encodeFinished;
std::shared_ptr<VideoEncoder> m_videoEncoder;
Expand Down
1 change: 1 addition & 0 deletions alvr/server/cpp/platform/win32/VideoEncoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ class VideoEncoder
virtual void Shutdown() = 0;

virtual void Transmit(ID3D11Texture2D *pTexture, uint64_t presentationTime, uint64_t targetTimestampNs, bool insertIDR) = 0;
virtual void GetConfigNAL() = 0;
};
8 changes: 7 additions & 1 deletion alvr/server/cpp/platform/win32/VideoEncoderNVENC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,4 +268,10 @@ void VideoEncoderNVENC::FillEncodeConfig(NV_ENC_INITIALIZE_PARAMS &initializePar
if (Settings::Instance().m_nvencRcAverageBitrate != -1) {
encodeConfig.rcParams.averageBitRate = Settings::Instance().m_nvencRcAverageBitrate;
}
}
}

void VideoEncoderNVENC::GetConfigNAL() {
std::vector<uint8_t> header;
m_NvNecoder->GetSequenceParams(header);
InitializeDecoder(header.data(), header.size());
}
2 changes: 2 additions & 0 deletions alvr/server/cpp/platform/win32/VideoEncoderNVENC.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class VideoEncoderNVENC : public VideoEncoder
void Shutdown();

void Transmit(ID3D11Texture2D *pTexture, uint64_t presentationTime, uint64_t targetTimestampNs, bool insertIDR);

void GetConfigNAL();
private:
void FillEncodeConfig(NV_ENC_INITIALIZE_PARAMS &initializeParams, int refreshRate, int renderWidth, int renderHeight, uint64_t bitrateBits);

Expand Down
7 changes: 5 additions & 2 deletions alvr/server/cpp/platform/win32/VideoEncoderSW.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,7 @@ void VideoEncoderSW::Initialize() {
av_dict_set(&opt, "nal-hrd", "vbr", 0);
break;
}
m_codecContext->rc_max_rate = Settings::Instance().mEncodeBitrateMBs * 1'000'000L;
m_codecContext->thread_count = Settings::Instance().m_swThreadCount;
m_codecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

if((err = avcodec_open2(m_codecContext, codec, &opt))) throw MakeException("Cannot open video encoder codec: %d", err);

Expand Down Expand Up @@ -297,4 +296,8 @@ AVCodecID VideoEncoderSW::ToFFMPEGCodec(ALVR_CODEC codec) {
}
}

void VideoEncoderSW::GetConfigNAL() {
InitializeDecoder(m_codecContext->extradata, m_codecContext->extradata_size);
}

#endif // ALVR_GPL
2 changes: 2 additions & 0 deletions alvr/server/cpp/platform/win32/VideoEncoderSW.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ class VideoEncoderSW : public VideoEncoder
void Transmit(ID3D11Texture2D *pTexture, uint64_t presentationTime, uint64_t targetTimestampNs, bool insertIDR);
HRESULT SetupStagingTexture(ID3D11Texture2D *pTexture);
HRESULT CopyTexture(ID3D11Texture2D *pTexture);

void GetConfigNAL();
private:
std::shared_ptr<CD3DRender> m_d3dRender;
std::shared_ptr<ClientConnection> m_Listener;
Expand Down
Loading