Skip to content

Commit

Permalink
--dolby-vision-rpu使用時にレターボックス部分をcropをした場合にそれを反映させるオプションを追加。 ( --dolby…
Browse files Browse the repository at this point in the history
…-vision-rpu-prm crop )
  • Loading branch information
rigaya committed Nov 20, 2024
1 parent fa3112d commit 81472bf
Show file tree
Hide file tree
Showing 14 changed files with 207 additions and 61 deletions.
16 changes: 16 additions & 0 deletions NVEncC_Options.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
- [--dolby-vision-profile \<string\> \[HEVC, AV1\]](#--dolby-vision-profile-string-hevc-av1)
- [--dolby-vision-rpu \<string\> \[HEVC, AV1\]](#--dolby-vision-rpu-string-hevc-av1)
- [--dolby-vision-rpu copy \[HEVC, AV1\]](#--dolby-vision-rpu-copy-hevc-av1)
- [--dolby-vision-rpu-prm \<param1\>=\<value1\>\[,\<param2\>=\<value2\>\]...](#--dolby-vision-rpu-prm-param1value1param2value2)
- [--aud \[H.264/HEVC\]](#--aud-h264hevc)
- [--repeat-headers](#--repeat-headers)
- [--pic-struct \[H.264/HEVC\]](#--pic-struct-h264hevc)
Expand Down Expand Up @@ -928,6 +929,21 @@ Interleave Dolby Vision RPU metadata copied from HEVC input file. Recommended to
Limitations for avhw reader: this option uses timestamps to reorder frames to decoded order to presentation order.
Therefore, input files without timestamps (such as raw ES), are not supported. Please try for avsw reader for that case.

### --dolby-vision-rpu-prm &lt;param1&gt;=&lt;value1&gt;[,&lt;param2&gt;=&lt;value2&gt;]...

Set parameters for ```--dolby-vision-rpu```.

- **parameters**

- crop=&lt;bool&gt;

Set active area offsets to 0 (no letterbox bars).

- Examples
```
Example: --dolby-vision-rpu-prm crop=true
```

### --aud [H.264/HEVC]
Insert Access Unit Delimiter NAL.

Expand Down
18 changes: 17 additions & 1 deletion NVEncC_Options.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@
- [--dolby-vision-profile \<string\> \[HEVC, AV1\]](#--dolby-vision-profile-string-hevc-av1)
- [--dolby-vision-rpu \<string\> \[HEVC, AV1\]](#--dolby-vision-rpu-string-hevc-av1)
- [--dolby-vision-rpu copy \[HEVC, AV1\]](#--dolby-vision-rpu-copy-hevc-av1)
- [--dolby-vision-rpu-prm \<param1\>=\<value1\>\[,\<param2\>=\<value2\>\]...](#--dolby-vision-rpu-prm-param1value1param2value2)
- [--aud \[H.264/HEVC\]](#--aud-h264hevc)
- [--repeat-headers](#--repeat-headers)
- [--pic-struct \[H.264/HEVC\]](#--pic-struct-h264hevc)
Expand Down Expand Up @@ -926,14 +927,29 @@ unset, copy, 5.0, 8.1, 8.2, 8.4
```

### --dolby-vision-rpu &lt;string&gt; [HEVC, AV1]
指定のrpuファイルに含まれるdolby visionのmetadataを出力ファイルに挿入します。
指定のrpuファイルに含まれるdolby visionのmetadataを出力ファイルに挿入します。[--dolby-vision-profile](#--dolby-vision-profile-string)との併用が推奨です。

### --dolby-vision-rpu copy [HEVC, AV1]
HEVCの入力ファイルから読み取ったdolby visionのmetadataを出力ファイルに挿入します。 [--dolby-vision-profile](#--dolby-vision-profile-string)との併用が推奨です。

avhw読み込みでは、フレームの並び替えにタイムスタンプを使用するため、タイムスタンプの取得できないraw ESのような入力ファイルでは使用できません。
こうした場合には、avsw読み込みを使用してください。

### --dolby-vision-rpu-prm &lt;param1&gt;=&lt;value1&gt;[,&lt;param2&gt;=&lt;value2&gt;]...

```--dolby-vision-rpu```用のパラメータを指定する。

- **パラメータ**

- crop=&lt;bool&gt;

RPUのactive area offsetsを0に設定する (レターボックスなしの意味)。

- 使用例
```
例: --dolby-vision-rpu-prm crop=true
```


### --aud [H.264/HEVC]
Access Unit Delimiter NALを挿入する。
Expand Down
70 changes: 59 additions & 11 deletions NVEncCore/rgy_bitstream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@

#include <regex>
#include "rgy_util.h"
#include "rgy_def.h"
#include "rgy_bitstream.h"
#include "rgy_memmem.h"
#include "rgy_libdovi.h"

std::vector<uint8_t> unnal(const uint8_t *ptr, size_t len) {
std::vector<uint8_t> data;
Expand Down Expand Up @@ -459,6 +461,52 @@ std::vector<uint8_t> RGYHDRMetadata::gen_obu() const {
return data;
}

int convert_dovi_rpu(std::vector<uint8_t>& data, const RGYDOVIProfile doviProfileDst, const RGYDOVIRpuConvertParam *prm) {
#if ENABLE_LIBDOVI
if (!prm) {
return 0;
}
if (data.size() == 0) {
return 0;
}
if (prm->convertProfile || prm->activeAreaOffsets.enable || prm->removeMapping) {
std::unique_ptr<DoviRpuOpaque, decltype(&dovi_rpu_free)> rpu(dovi_parse_rpu(data.data(), data.size()), dovi_rpu_free);
if (!rpu) {
return 1;
}
std::unique_ptr<const DoviRpuDataHeader, decltype(&dovi_rpu_free_header)> header(dovi_rpu_get_header(rpu.get()), dovi_rpu_free_header);
if (!header) {
return 1;
}
const auto dovi_profile = header->guessed_profile;
if (prm->convertProfile
&& dovi_profile == 7
&& (doviProfileDst == RGY_DOVI_PROFILE_81 || doviProfileDst == RGY_DOVI_PROFILE_COPY)) {
const int ret = dovi_convert_rpu_with_mode(rpu.get(), 2);
if (ret != 0) {
return 1;
}
}
if (prm->activeAreaOffsets.enable) {
dovi_rpu_set_active_area_offsets(rpu.get(),
prm->activeAreaOffsets.left, prm->activeAreaOffsets.right, prm->activeAreaOffsets.top, prm->activeAreaOffsets.bottom);
}
if (prm->removeMapping) {
dovi_rpu_remove_mapping(rpu.get());
}
std::unique_ptr<const DoviData, decltype(&dovi_data_free)> rpu_data(dovi_write_rpu(rpu.get()), dovi_data_free);
if (!rpu_data) {
return 1;
}
data.resize(rpu_data->len);
memcpy(data.data(), rpu_data->data, rpu_data->len);
}
return 0;
#else
return (prm) ? 1 : 0;
#endif // ENABLE_LIBDOVI
}

DOVIRpu::DOVIRpu() : m_find_header(get_find_header_func()), m_filepath(), m_fp(nullptr, fp_deleter()), m_buffer(), m_datasize(0), m_dataoffset(0), m_count(0), m_rpus() {};
DOVIRpu::~DOVIRpu() { m_fp.reset(); };

Expand Down Expand Up @@ -497,7 +545,7 @@ int DOVIRpu::fillBuffer() {
return bytes_read;
}

int DOVIRpu::get_next_rpu(std::vector<uint8_t>& bytes) {
int DOVIRpu::get_next_rpu(std::vector<uint8_t>& bytes, const RGYDOVIProfile doviProfileDst, const RGYDOVIRpuConvertParam *prm) {
if (m_datasize <= 4) {
if (fillBuffer() == 0) {
return 1; //EOF
Expand Down Expand Up @@ -532,14 +580,14 @@ int DOVIRpu::get_next_rpu(std::vector<uint8_t>& bytes) {
memcpy(bytes.data(), dataptr, next_size);
m_dataoffset += next_size;
m_datasize -= next_size;
return 0;
return convert_dovi_rpu(bytes, doviProfileDst, prm);
}

int DOVIRpu::get_next_rpu(std::vector<uint8_t>& bytes, const int64_t id) {
int DOVIRpu::get_next_rpu(std::vector<uint8_t>& bytes, const RGYDOVIProfile doviProfileDst, const RGYDOVIRpuConvertParam *prm, const int64_t id) {
bytes.clear();
for (; m_count <= id; m_count++) {
std::vector<uint8_t> rpu;
if (int ret = get_next_rpu(rpu); ret != 0) {
if (int ret = get_next_rpu(rpu, doviProfileDst, prm); ret != 0) {
return ret;
}
m_rpus[m_count] = rpu;
Expand All @@ -553,9 +601,9 @@ int DOVIRpu::get_next_rpu(std::vector<uint8_t>& bytes, const int64_t id) {
return 0;
}

int DOVIRpu::get_next_rpu_nal(std::vector<uint8_t>& bytes, const int64_t id) {
int DOVIRpu::get_next_rpu_nal(std::vector<uint8_t>& bytes, const RGYDOVIProfile doviProfileDst, const RGYDOVIRpuConvertParam *prm, const int64_t id) {
std::vector<uint8_t> rpu;
if (int ret = get_next_rpu(rpu, id); ret != 0) {
if (int ret = get_next_rpu(rpu, doviProfileDst, prm, id); ret != 0) {
return ret;
}
//to_nal(rpu); // get_next_rpuはすでにこの処理を実施済みのものを返す
Expand All @@ -573,9 +621,9 @@ int DOVIRpu::get_next_rpu_nal(std::vector<uint8_t>& bytes, const int64_t id) {
return 0;
}

int DOVIRpu::get_next_rpu_obu(std::vector<uint8_t>& bytes, const int64_t id) {
int DOVIRpu::get_next_rpu_obu(std::vector<uint8_t>& bytes, const RGYDOVIProfile doviProfileDst, const RGYDOVIRpuConvertParam *prm, const int64_t id) {
std::vector<uint8_t> tmp;
if (int ret = get_next_rpu(tmp, id); ret != 0) {
if (int ret = get_next_rpu(tmp, doviProfileDst, prm, id); ret != 0) {
return ret;
}

Expand All @@ -592,10 +640,10 @@ int DOVIRpu::get_next_rpu_obu(std::vector<uint8_t>& bytes, const int64_t id) {
return 0;
}

int DOVIRpu::get_next_rpu(std::vector<uint8_t>& bytes, const int64_t id, const RGY_CODEC codec) {
int DOVIRpu::get_next_rpu(std::vector<uint8_t>& bytes, const RGYDOVIProfile doviProfileDst, const RGYDOVIRpuConvertParam *prm, const int64_t id, const RGY_CODEC codec) {
switch (codec) {
case RGY_CODEC_HEVC: return get_next_rpu_nal(bytes, id);
case RGY_CODEC_AV1: return get_next_rpu_obu(bytes, id);
case RGY_CODEC_HEVC: return get_next_rpu_nal(bytes, doviProfileDst, prm, id);
case RGY_CODEC_AV1: return get_next_rpu_obu(bytes, doviProfileDst, prm, id);
default: return 1;
}
}
Expand Down
12 changes: 7 additions & 5 deletions NVEncCore/rgy_bitstream.h
Original file line number Diff line number Diff line change
Expand Up @@ -320,22 +320,24 @@ static const uint8_t av1_itut_t35_header_dovirpu[] = {

const DOVIProfile *getDOVIProfile(const int id);

int convert_dovi_rpu(std::vector<uint8_t>& data, const RGYDOVIProfile doviProfileDst, const RGYDOVIRpuConvertParam *prm);

class DOVIRpu {
public:
static const uint8_t rpu_header[4];

DOVIRpu();
~DOVIRpu();
int init(const TCHAR *rpu_file);
int get_next_rpu_nal(std::vector<uint8_t>& bytes, const int64_t id);
int get_next_rpu_obu(std::vector<uint8_t>& bytes, const int64_t id);
int get_next_rpu(std::vector<uint8_t>& bytes, const int64_t id, const RGY_CODEC codec);
int get_next_rpu_nal(std::vector<uint8_t>& bytes, const RGYDOVIProfile doviProfileDst, const RGYDOVIRpuConvertParam *prm, const int64_t id);
int get_next_rpu_obu(std::vector<uint8_t>& bytes, const RGYDOVIProfile doviProfileDst, const RGYDOVIRpuConvertParam *prm, const int64_t id);
int get_next_rpu(std::vector<uint8_t>& bytes, const RGYDOVIProfile doviProfileDst, const RGYDOVIRpuConvertParam *prm, const int64_t id, const RGY_CODEC codec);
const tstring& get_filepath() const;

protected:
int fillBuffer();
int get_next_rpu(std::vector<uint8_t>& bytes);
int get_next_rpu(std::vector<uint8_t>& bytes, const int64_t id);
int get_next_rpu(std::vector<uint8_t>& bytes, const RGYDOVIProfile doviProfileDst, const RGYDOVIRpuConvertParam *prm);
int get_next_rpu(std::vector<uint8_t>& bytes, const RGYDOVIProfile doviProfileDst, const RGYDOVIRpuConvertParam *prm, const int64_t id);

decltype(find_header_c)* m_find_header;
tstring m_filepath;
Expand Down
48 changes: 47 additions & 1 deletion NVEncCore/rgy_cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6155,6 +6155,37 @@ int parse_one_common_option(const TCHAR *option_name, const TCHAR *strInput[], i
}
return 0;
}
if (IS_OPTION("dolby-vision-rpu-prm")) {
i++;
const auto paramList = std::vector<std::string>{ "crop" };
for (const auto &param : split(strInput[i], _T(","))) {
auto pos = param.find_first_of(_T("="));
if (pos != std::string::npos) {
auto param_arg = tolowercase(param.substr(0, pos));
auto param_val = param.substr(pos + 1);
if (param_arg == _T("crop")) {
bool b = false;
if (!cmd_string_to_bool(&b, param_val)) {
common->doviRpuParams.activeAreaOffsets.enable = b;
} else {
print_cmd_error_invalid_value(tstring(option_name) + _T(" ") + param_arg + _T("="), param_val);
return 1;
}
continue;
}
print_cmd_error_unknown_opt_param(option_name, param_arg, paramList);
return 1;
} else {
if (param == _T("crop")) {
common->doviRpuParams.activeAreaOffsets.enable = true;
continue;
}
print_cmd_error_unknown_opt_param(option_name, param, paramList);
return 1;
}
}
return 0;
}
#endif //#if ENABLE_DOVI_METADATA_OPTIONS
if (IS_OPTION("timecode")) {
common->timecode = true;
Expand Down Expand Up @@ -8200,6 +8231,16 @@ tstring gen_cmd(const RGYParamCommon *param, const RGYParamCommon *defaultPrm, b
} else {
OPT_STR_PATH(_T("--dolby-vision-rpu"), doviRpuFile);
}

if (param->doviRpuParams != defaultPrm->doviRpuParams) {
tmp.str(tstring());
if (param->doviRpuParams.activeAreaOffsets.enable) {
tmp << ",crop=on";
}
if (!tmp.str().empty()) {
cmd << _T(" --dolby-vision-rpu-prm ") << tmp.str().substr(1);
}
}
if (param->timecode || param->timecodeFile.length() > 0) {
cmd << (param->timecode ? _T("--timecode ") : _T("--no-timecode "));
if (param->timecodeFile.length() > 0) {
Expand Down Expand Up @@ -8451,7 +8492,12 @@ tstring gen_cmd_help_common() {
str += print_list_options(_T("--dolby-vision-profile <int>"), list_dovi_profile, 0);
str += strsprintf(
_T(" --dolby-vision-rpu <string> Copy dolby vision metadata from input rpu file.\n")
_T(" --dolby-vision-rpu copy Copy dolby vision metadata from input file.\n"));
_T(" --dolby-vision-rpu copy Copy dolby vision metadata from input file.\n")
_T(" --dolby-vision-rpu-prm <param1>=<value>[,<param2>=<value>][...]\n")
_T(" parameters for --dolby-vision-rpu.\n")
_T(" params\n")
_T(" crop=<bool> Set active area offsets to 0 (no letterbox bars).\n")
);
#endif //#if ENABLE_DOVI_METADATA_OPTIONS
str += strsprintf(
_T(" --input-analyze <int> set time (sec) which reader analyze input file.\n")
Expand Down
33 changes: 33 additions & 0 deletions NVEncCore/rgy_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,39 @@ const CX_DESC list_dovi_profile_parse[] = {
{ NULL, 0 }
};

struct RGYDOVIRpuActiveAreaOffsets {
bool enable;
uint16_t left, top, right, bottom;

RGYDOVIRpuActiveAreaOffsets() : enable(false), left(0), top(0), right(0), bottom(0) {};
bool operator==(const RGYDOVIRpuActiveAreaOffsets &x) const {
return enable == x.enable
&& left == x.left
&& top == x.top
&& right == x.right
&& bottom == x.bottom;
}
bool operator!=(const RGYDOVIRpuActiveAreaOffsets &x) const {
return !(*this == x);
}
};

class RGYDOVIRpuConvertParam {
public:
bool convertProfile;
bool removeMapping;
RGYDOVIRpuActiveAreaOffsets activeAreaOffsets;
RGYDOVIRpuConvertParam() : convertProfile(true), removeMapping(false), activeAreaOffsets() {};
virtual ~RGYDOVIRpuConvertParam() {};
bool operator==(const RGYDOVIRpuConvertParam &x) const {
return convertProfile == x.convertProfile
&& removeMapping == x.removeMapping
&& activeAreaOffsets == x.activeAreaOffsets;
}
bool operator!=(const RGYDOVIRpuConvertParam &x) const {
return !(*this == x);
}
};

// 1st luma line > |X X ... |3 4 X ... X が輝度ピクセル位置
// | |1 2 1-6 are possible chroma positions
Expand Down
35 changes: 3 additions & 32 deletions NVEncCore/rgy_frame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
#if !CLFILTERS_AUF
#include "rgy_bitstream.h"
#endif
#include "rgy_libdovi.h"

const TCHAR *RGYFrameDataTypeToStr(const RGYFrameDataType type) {
switch (type) {
Expand Down Expand Up @@ -188,40 +187,12 @@ RGYFrameDataDOVIRpu::RGYFrameDataDOVIRpu(const uint8_t *data, size_t size, int64
RGYFrameDataDOVIRpu::~RGYFrameDataDOVIRpu() { }

RGY_ERR RGYFrameDataDOVIRpu::convert(const RGYFrameDataMetadataConvertParam *metadataprm) {
#if ENABLE_LIBDOVI
auto prm = dynamic_cast<const RGYFrameDataDOVIRpuConvertParam*>(metadataprm);
if (!prm || !prm->enable) {
return RGY_ERR_NONE;
}
if (prm->doviProfileDst != RGY_DOVI_PROFILE_81 && prm->doviProfileDst != RGY_DOVI_PROFILE_COPY) {
return RGY_ERR_NONE;
}
if (m_data.size() == 0) {
return RGY_ERR_NONE;
}
std::unique_ptr<DoviRpuOpaque, decltype(&dovi_rpu_free)> rpu(dovi_parse_rpu(m_data.data(), m_data.size()), dovi_rpu_free);
if (!rpu) {
return RGY_ERR_INVALID_BINARY;
}
std::unique_ptr<const DoviRpuDataHeader, decltype(&dovi_rpu_free_header)> header(dovi_rpu_get_header(rpu.get()), dovi_rpu_free_header);
if (!header) {

int ret = convert_dovi_rpu(m_data, prm->doviProfileDst, prm->doviRpu());
if (ret) {
return RGY_ERR_UNKNOWN;
}
const auto dovi_profile = header->guessed_profile;
if (dovi_profile != 7) {
return RGY_ERR_NONE;
}
const int ret = dovi_convert_rpu_with_mode(rpu.get(), 2);
if (ret != 0) {
return RGY_ERR_INVALID_OPERATION;
}
std::unique_ptr<const DoviData, decltype(&dovi_data_free)> rpu_data(dovi_write_rpu(rpu.get()), dovi_data_free);
if (!rpu_data) {
return RGY_ERR_NULL_PTR;
}
m_data.resize(rpu_data->len);
memcpy(m_data.data(), rpu_data->data, rpu_data->len);
#endif // ENABLE_LIBDOVI
return RGY_ERR_NONE;
}

Expand Down
Loading

0 comments on commit 81472bf

Please sign in to comment.