Skip to content

Commit

Permalink
--level指定時のエラーを可能な限り回避するよう、最大ビットレートやrefのチェックを追加。
Browse files Browse the repository at this point in the history
  • Loading branch information
rigaya committed Jan 19, 2025
1 parent 19e127b commit 962e140
Show file tree
Hide file tree
Showing 14 changed files with 351 additions and 69 deletions.
4 changes: 3 additions & 1 deletion NVEnc/NVEnc_readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,9 @@ NVIDIA グラフィックドライバ 551.23

【メモ】
2025.01.19 (7.82)
- AV1の4Kでのqvbrはなぜか624230kbpsを超えるとよくわからない挙動を示し、qvbr51でも高ビットレートになってしまうので対策。 ( #486 )
- AV1のlevelを指定可能に。
- --level指定時のエラーを可能な限り回避するよう、最大ビットレートやrefのチェックを追加。
- AV1の4Kでのqvbrはなぜか624230kbpsを超えるとよくわからない挙動を示し、qvbr51でも高ビットレートになってしまうので対策。
- より詳細なエンコーダのエラー表示を可能に。
- AACを--audio-copyしてmkv出力すると、音声が再生できないファイルができる問題を修正。
- 固定品質の設定ができない問題を修正。
Expand Down
104 changes: 76 additions & 28 deletions NVEncCore/NVEncCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,8 @@
#include "rgy_chapter.h"
#include "rgy_timecode.h"
#include "rgy_aspect_ratio.h"
#include "rgy_level_h264.h"
#include "rgy_level.h"
#include "rgy_level_hevc.h"
#include "rgy_level_av1.h"
#include "rgy_device_info_cache.h"
#include "NVEncParam.h"
#include "NVEncUtil.h"
Expand Down Expand Up @@ -1586,7 +1585,7 @@ NVENCSTATUS NVEncCore::SetInputParam(InEncodeVideoParam *inputParam) {
m_stCodecGUID = codec_guid_rgy_to_enc(inputParam->codec_rgy);
auto codecFeature = m_dev->encoder()->getCodecFeature(m_stCodecGUID);
if (codecFeature == nullptr) {
PrintMes(RGY_LOG_ERROR, FOR_AUO ? _T("指定されたコーデックはサポートされていません。\n") : _T("Selected codec is not supported.\n"));
PrintMes(RGY_LOG_ERROR, _T("Selected codec (%s) is not supported.\n"), CodecToStr(inputParam->codec_rgy).c_str());
return NV_ENC_ERR_UNSUPPORTED_PARAM;
}

Expand All @@ -1610,7 +1609,7 @@ NVENCSTATUS NVEncCore::SetInputParam(InEncodeVideoParam *inputParam) {
}
if (!codecFeature->checkProfileSupported(m_stEncConfig.profileGUID)) {
m_stEncConfig.profileGUID = NV_ENC_CODEC_PROFILE_AUTOSELECT_GUID;
PrintMes(RGY_LOG_WARN, _T("Selected profile is not supported, profile will be auto selected by NVENC API!\n"));
PrintMes(RGY_LOG_WARN, _T("Selected profile is not supported, profile will be auto selected by NVENC API!\n"), get_name_from_guid(m_stEncConfig.profileGUID, get_codec_profile_list(inputParam->codec_rgy)));
}

//プリセットのチェック
Expand Down Expand Up @@ -1926,46 +1925,95 @@ NVENCSTATUS NVEncCore::SetInputParam(InEncodeVideoParam *inputParam) {
set_enableLTR(m_stEncConfig.encodeCodecConfig, inputParam->codec_rgy, numRefFrames(m_stEncConfig.encodeCodecConfig, inputParam->codec_rgy));
}

auto codecLevel = createCodecLevel(inputParam->codec_rgy);
const int codecProfile = get_value_from_guid(m_stEncConfig.profileGUID, get_codec_profile_list(inputParam->codec_rgy));
const bool hevc_high_tier = (inputParam->codec_rgy == RGY_CODEC_HEVC) ? m_stEncConfig.encodeCodecConfig.hevcConfig.tier == NV_ENC_TIER_HEVC_HIGH : false;
const int av1_tile_col = (inputParam->codec_rgy == RGY_CODEC_AV1) ? m_stEncConfig.encodeCodecConfig.av1Config.numTileColumns : 1;
const int av1_tile_row = (inputParam->codec_rgy == RGY_CODEC_AV1) ? m_stEncConfig.encodeCodecConfig.av1Config.numTileRows : 1;
// levelのチェック
{
const int level = get_level(m_stEncConfig.encodeCodecConfig, inputParam->codec_rgy);
if (level != codecLevel->level_auto()) {
const int required_level = codecLevel->calc_auto_level(m_uEncWidth, m_uEncHeight, 0, is_interlaced(m_stPicStruct),
m_encFps.n(), m_encFps.d(), codecProfile, hevc_high_tier, 0, 0, 1, 1);
if (level < required_level) {
PrintMes(RGY_LOG_WARN, _T("Level %s does not support the current settings (%s @ %s, %dx%d, %d/%d fps), switching level selection to auto.\n"),
get_codec_level_name(inputParam->codec_rgy, level).c_str(),
CodecToStr(inputParam->codec_rgy).c_str(), get_name_from_guid(m_stEncConfig.profileGUID, get_codec_profile_list(inputParam->codec_rgy)),
m_uEncWidth, m_uEncHeight, m_encFps.n(), m_encFps.d());
set_level(m_stEncConfig.encodeCodecConfig, inputParam->codec_rgy, get_cx_value(get_level_list(inputParam->codec_rgy), _T("auto")));
} else {
// refの制限を超えている場合、refを下げる
const int maxRef = codecLevel->get_max_ref(m_uEncWidth, m_uEncHeight, level, codecProfile);
if ((int)numRefFrames(m_stEncConfig.encodeCodecConfig, inputParam->codec_rgy) > maxRef) {
PrintMes(RGY_LOG_WARN, _T("Ref frames is lowered %d -> %d due to level %s restriction.\n"), numRefFrames(m_stEncConfig.encodeCodecConfig, inputParam->codec_rgy), maxRef, get_codec_level_name(inputParam->codec_rgy, level).c_str());
numRefFrames(m_stEncConfig.encodeCodecConfig, inputParam->codec_rgy) = maxRef;
}
// 最大bitrateの制限を超えている場合、最大bitrateを下げる
if (m_stEncConfig.rcParams.rateControlMode != NV_ENC_PARAMS_RC_CONSTQP) {
int max_bitrate_kbps = codecLevel->get_max_bitrate(level, codecProfile, hevc_high_tier);
if (inputParam->codec_rgy == RGY_CODEC_H264) {
if (codecProfile >= 100) {
//なぜかhigh profileではぎりぎりを指定するとエラー終了するので、すこし減らす
max_bitrate_kbps = (int)(max_bitrate_kbps * 0.96 + 0.5);
}
} else if (inputParam->codec_rgy == RGY_CODEC_HEVC) {
//なぜかぎりぎりを指定するとエラー終了するので、すこし減らす
max_bitrate_kbps = (int)(max_bitrate_kbps * 0.96 + 0.5);
} else if (inputParam->codec_rgy == RGY_CODEC_AV1) {
//なぜか制限値の2/3を指定する必要がある (そうしないとエラーになる)
max_bitrate_kbps = (int)(max_bitrate_kbps * 2 / 3);
}
if ((int)m_stEncConfig.rcParams.averageBitRate > max_bitrate_kbps * 1000) {
PrintMes(RGY_LOG_WARN, _T("Bitrate is lowered %d -> %d due to level %s restriction.\n"), m_stEncConfig.rcParams.averageBitRate / 1000, max_bitrate_kbps, get_codec_level_name(inputParam->codec_rgy, level).c_str());
m_stEncConfig.rcParams.averageBitRate = max_bitrate_kbps * 1000;
}
if ((int)m_stEncConfig.rcParams.maxBitRate > max_bitrate_kbps * 1000) {
PrintMes(RGY_LOG_WARN, _T("Max bitrate is lowered %d -> %d due to level %s restriction.\n"), m_stEncConfig.rcParams.maxBitRate / 1000, max_bitrate_kbps, get_codec_level_name(inputParam->codec_rgy, level).c_str());
m_stEncConfig.rcParams.maxBitRate = max_bitrate_kbps * 1000;
}
if (inputParam->codec_rgy == RGY_CODEC_H264) {
int max_vbv_buffer_size = codecLevel->get_max_vbv_buf(level, codecProfile);
if (codecProfile >= 100) {
//なぜかhigh profileではぎりぎりを指定するとエラー終了するので、すこし減らす
max_vbv_buffer_size = (int)(max_vbv_buffer_size * 0.96 + 0.5);
}
if ((int)m_stEncConfig.rcParams.vbvBufferSize > max_vbv_buffer_size * 1000) {
PrintMes(RGY_LOG_WARN, _T("VBV buffer size is lowered %d -> %d due to level %s restriction.\n"), m_stEncConfig.rcParams.vbvBufferSize / 1000, max_vbv_buffer_size, get_codec_level_name(inputParam->codec_rgy, level).c_str());
m_stEncConfig.rcParams.vbvBufferSize = max_vbv_buffer_size * 1000;
}
}
}
}
}
}

//最大ビットレート自動
if (m_stEncConfig.rcParams.rateControlMode == NV_ENC_PARAMS_RC_CONSTQP) {
//CQPモードでは、最大ビットレートの指定は不要
m_stEncConfig.rcParams.maxBitRate = 0;
} else if (m_stEncConfig.rcParams.maxBitRate == 0) {
//指定されたビットレートの1.5倍は最大ビットレートを確保する
const int prefered_bitrate_kbps = m_stEncConfig.rcParams.averageBitRate * 3 / 2 / 1000;
int level = get_level(m_stEncConfig.encodeCodecConfig, inputParam->codec_rgy);
if (level == codecLevel->level_auto()) {
level = codecLevel->calc_auto_level(m_uEncWidth, m_uEncHeight, numRefFrames(m_stEncConfig.encodeCodecConfig, inputParam->codec_rgy), is_interlaced(m_stPicStruct),
m_encFps.n(), m_encFps.d(), codecProfile, hevc_high_tier, prefered_bitrate_kbps, m_stEncConfig.rcParams.vbvBufferSize / 1000,
av1_tile_col, av1_tile_row);
}
if (inputParam->codec_rgy == RGY_CODEC_H264) {
const int profile = get_value_from_guid(m_stEncConfig.profileGUID, h264_profile_names);
int level = get_level(m_stEncConfig.encodeCodecConfig, inputParam->codec_rgy);
if (level == 0) {
level = calc_auto_level_h264(m_uEncWidth, m_uEncHeight, numRefFrames(m_stEncConfig.encodeCodecConfig, inputParam->codec_rgy), is_interlaced(m_stPicStruct),
m_encFps.n(), m_encFps.d(), profile, prefered_bitrate_kbps, m_stEncConfig.rcParams.vbvBufferSize / 1000);
}
int max_bitrate_kbps = 0, vbv_bufsize_kbps = 0;
get_vbv_value_h264(&max_bitrate_kbps, &vbv_bufsize_kbps, level, profile);
if (profile >= 100) {
int max_bitrate_kbps = codecLevel->get_max_bitrate(level, codecProfile);
if (codecProfile >= 100) {
//なぜかhigh profileではぎりぎりを指定するとエラー終了するので、すこし減らす
max_bitrate_kbps = (int)(max_bitrate_kbps * 0.96 + 0.5);
vbv_bufsize_kbps = (int)(vbv_bufsize_kbps * 0.96 + 0.5);
}
m_stEncConfig.rcParams.maxBitRate = max_bitrate_kbps * 1000;
} else if (inputParam->codec_rgy == RGY_CODEC_HEVC) {
const bool high_tier = m_stEncConfig.encodeCodecConfig.hevcConfig.tier == NV_ENC_TIER_HEVC_HIGH;
int level = get_level(m_stEncConfig.encodeCodecConfig, inputParam->codec_rgy);
if (level == 0) {
level = calc_auto_level_hevc(m_uEncWidth, m_uEncHeight, numRefFrames(m_stEncConfig.encodeCodecConfig, inputParam->codec_rgy),
m_encFps.n(), m_encFps.d(), high_tier, prefered_bitrate_kbps);
}
//なぜかぎりぎりを指定するとエラー終了するので、すこし減らす
m_stEncConfig.rcParams.maxBitRate = get_max_bitrate_hevc(level, high_tier) * 960;
m_stEncConfig.rcParams.maxBitRate = codecLevel->get_max_bitrate(level, codecProfile, hevc_high_tier) * 960;
} else if (inputParam->codec_rgy == RGY_CODEC_AV1) {
const int profile = get_value_from_guid(m_stEncConfig.profileGUID, av1_profile_names);
int level = get_level(m_stEncConfig.encodeCodecConfig, inputParam->codec_rgy);
if (level == 0) {
level = calc_auto_level_av1(m_uEncWidth, m_uEncHeight, numRefFrames(m_stEncConfig.encodeCodecConfig, inputParam->codec_rgy),
m_encFps.n(), m_encFps.d(), profile, prefered_bitrate_kbps, m_stEncConfig.encodeCodecConfig.av1Config.numTileColumns, m_stEncConfig.encodeCodecConfig.av1Config.numTileRows);
}
//なぜか制限値の2/3を指定する必要がある (そうしないとエラーになる)
m_stEncConfig.rcParams.maxBitRate = (uint32_t)(get_max_bitrate_av1(level, profile) * 2.0 / 3.0);
m_stEncConfig.rcParams.maxBitRate = (uint32_t)((int64_t)codecLevel->get_max_bitrate(level, codecProfile) * 1000 * 2 / 3);
} else {
m_stEncConfig.rcParams.maxBitRate = DEFAULT_MAX_BITRATE;
}
Expand Down
2 changes: 2 additions & 0 deletions NVEncCore/NVEncCore.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -996,6 +996,7 @@ if exist rgy_rev.h.%PID%.tmp del rgy_rev.h.%PID%.tmp &gt; nul 2&gt;&amp;1</Comma
</ClCompile>
<ClCompile Include="rgy_filter.cpp" />
<ClCompile Include="rgy_frame_info.cpp" />
<ClCompile Include="rgy_level.cpp" />
<ClCompile Include="rgy_level_h264.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
Expand Down Expand Up @@ -1440,6 +1441,7 @@ if exist rgy_rev.h.%PID%.tmp del rgy_rev.h.%PID%.tmp &gt; nul 2&gt;&amp;1</Comma
<ClInclude Include="rgy_faw.h" />
<ClInclude Include="rgy_filter.h" />
<ClInclude Include="rgy_frame_info.h" />
<ClInclude Include="rgy_level.h" />
<ClInclude Include="rgy_level_h264.h" />
<ClInclude Include="rgy_level_hevc.h" />
<ClInclude Include="logo.h" />
Expand Down
6 changes: 6 additions & 0 deletions NVEncCore/NVEncCore.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,9 @@
<ClCompile Include="rgy_device_info_cache.cpp">
<Filter>ソース ファイル</Filter>
</ClCompile>
<ClCompile Include="rgy_level.cpp">
<Filter>ソース ファイル</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="gpu_info.h">
Expand Down Expand Up @@ -644,6 +647,9 @@
<ClInclude Include="rgy_device_info_cache.h">
<Filter>ヘッダー ファイル</Filter>
</ClInclude>
<ClInclude Include="rgy_level.h">
<Filter>ヘッダー ファイル</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CudaCompile Include="NVEncFilterCrop.cu">
Expand Down
9 changes: 9 additions & 0 deletions NVEncCore/NVEncParam.h
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,15 @@ static int get_value_from_guid(GUID guid, const guid_desc (&desc)[count]) {
return 0;
};

static int get_value_from_guid(GUID guid, const std::vector<guid_desc>& desc) {
for (size_t i = 0; i < desc.size(); i++) {
if (0 == memcmp(&desc[i].id, &guid, sizeof(GUID))) {
return desc[i].value;
}
}
return 0;
};

template<size_t count>
static GUID get_guid_from_value(int value, const guid_desc (&desc)[count]) {
for (size_t i = 0; i < count; i++) {
Expand Down
50 changes: 50 additions & 0 deletions NVEncCore/rgy_level.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// -----------------------------------------------------------------------------------------
// NVEnc by rigaya
// -----------------------------------------------------------------------------------------
// The MIT License
//
// Copyright (c) 2014-2016 rigaya
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// --------------------------------------------------------------------------------------------

#include "rgy_level_h264.h"
#include "rgy_level_hevc.h"
#include "rgy_level_av1.h"
#if ENCODER_NVENC
#include "NVEncParam.h"
#endif

std::unique_ptr<RGYCodecLevel> createCodecLevel(RGY_CODEC codec) {
switch (codec) {
case RGY_CODEC_H264:
return std::unique_ptr<RGYCodecLevel>(new RGYCodecLevelH264());
case RGY_CODEC_HEVC:
return std::unique_ptr<RGYCodecLevel>(new RGYCodecLevelHEVC());
case RGY_CODEC_AV1:
return std::unique_ptr<RGYCodecLevel>(new RGYCodecLevelAV1());
default:
return nullptr;
}
}

int RGYCodecLevel::level_auto() {
return get_cx_value(get_level_list(m_codec), _T("auto"));
}
50 changes: 50 additions & 0 deletions NVEncCore/rgy_level.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// -----------------------------------------------------------------------------------------
// NVEnc by rigaya
// -----------------------------------------------------------------------------------------
// The MIT License
//
// Copyright (c) 2014-2016 rigaya
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// --------------------------------------------------------------------------------------------

#ifndef __RGY_LEVEL_H__
#define __RGY_LEVEL_H__

#include <memory>
#include "rgy_def.h"


class RGYCodecLevel {
public:
RGYCodecLevel() : m_codec(RGY_CODEC_UNKNOWN) {};
virtual ~RGYCodecLevel() {};
virtual int calc_auto_level(int width, int height, int ref, bool interlaced, int fps_num, int fps_den, int profile, bool high_tier, int max_bitrate, int vbv_buf, int tile_col, int tile_row) = 0;
virtual int get_max_bitrate(int level, int profile, bool high_tier = false) = 0;
virtual int get_max_vbv_buf(int level, int profile) = 0;
virtual int get_max_ref(int width, int height, int level, bool interlaced) = 0;
virtual int level_auto();
protected:
RGY_CODEC m_codec;
};

std::unique_ptr<RGYCodecLevel> createCodecLevel(RGY_CODEC codec);

#endif //__RGY_LEVEL_AV1_H__
Loading

0 comments on commit 962e140

Please sign in to comment.