Skip to content

Commit 8cc3d6b

Browse files
committed
Implement support for encoding Dolby Vision from RPU file
1 parent f8b95d9 commit 8cc3d6b

File tree

8 files changed

+173
-13
lines changed

8 files changed

+173
-13
lines changed

Cargo.lock

Lines changed: 78 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ new_debug_unreachable = "1.0.4"
108108
once_cell = "1.18.0"
109109
av1-grain = { version = "0.2.2", features = ["serialize"] }
110110
serde-big-array = { version = "0.5.1", optional = true }
111+
dolby_vision = { version = "3.2.0" }
111112

112113
[dependencies.image]
113114
version = "0.24.6"

src/api/config/encoder.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@
99

1010
use itertools::*;
1111

12-
use crate::api::color::*;
1312
use crate::api::config::GrainTableSegment;
13+
use crate::api::{color::*, T35};
1414
use crate::api::{Rational, SpeedSettings};
1515
use crate::encoder::Tune;
1616
use crate::serialize::{Deserialize, Serialize};
1717

18+
use std::collections::BTreeMap;
1819
use std::fmt;
1920

2021
// We add 1 to rdo_lookahead_frames in a bunch of places.
@@ -91,6 +92,8 @@ pub struct EncoderConfig {
9192
pub tune: Tune,
9293
/// Parameters for grain synthesis.
9394
pub film_grain_params: Option<Vec<GrainTableSegment>>,
95+
/// Dolby Vision T.35 metadata payload map, by input frame index.
96+
pub dovi_payloads: Option<BTreeMap<u64, T35>>,
9497
/// Number of tiles horizontally. Must be a power of two.
9598
///
9699
/// Overridden by [`tiles`], if present.
@@ -167,6 +170,7 @@ impl EncoderConfig {
167170
bitrate: 0,
168171
tune: Tune::default(),
169172
film_grain_params: None,
173+
dovi_payloads: None,
170174
tile_cols: 0,
171175
tile_rows: 0,
172176
tiles: 0,

src/api/test.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2127,6 +2127,7 @@ fn log_q_exp_overflow() {
21272127
bitrate: 1,
21282128
tune: Tune::Psychovisual,
21292129
film_grain_params: None,
2130+
dovi_payloads: None,
21302131
tile_cols: 0,
21312132
tile_rows: 0,
21322133
tiles: 0,
@@ -2204,6 +2205,7 @@ fn guess_frame_subtypes_assert() {
22042205
bitrate: 16384,
22052206
tune: Tune::Psychovisual,
22062207
film_grain_params: None,
2208+
dovi_payloads: None,
22072209
tile_cols: 0,
22082210
tile_rows: 0,
22092211
tiles: 0,

src/api/util.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,12 @@ impl fmt::Display for FrameType {
137137
}
138138
}
139139

140+
/// Dolby Vision T.35 metadata payload expected prefix.
141+
pub const T35_DOVI_PAYLOAD_PREFIX: &[u8] = &[
142+
0x00, 0x03B, // Dolby
143+
0x00, 0x00, 0x08, 0x00, 0x37, 0xCD, 0x08,
144+
];
145+
140146
/// A single T.35 metadata packet.
141147
#[derive(Clone, Debug, Default)]
142148
pub struct T35 {
@@ -299,3 +305,10 @@ impl<T: Pixel> IntoFrame<T> for (Frame<T>, Option<FrameParameters>) {
299305
(Some(Arc::new(self.0)), self.1)
300306
}
301307
}
308+
309+
impl T35 {
310+
/// Whether the T.35 metadata is Dolby Vision Metadata.
311+
pub fn is_dovi_metadata(&self) -> bool {
312+
self.country_code == 0xB5 && self.data.starts_with(T35_DOVI_PAYLOAD_PREFIX)
313+
}
314+
}

src/bin/common.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ use rav1e::prelude::*;
1818
use scan_fmt::scan_fmt;
1919

2020
use rav1e::config::CpuFeatureLevel;
21+
22+
use std::collections::BTreeMap;
2123
use std::fs::File;
2224
use std::io;
2325
use std::io::prelude::*;
@@ -195,6 +197,15 @@ pub struct CliOptions {
195197
help_heading = "ENCODE SETTINGS"
196198
)]
197199
pub film_grain_table: Option<PathBuf>,
200+
/// Uses a Dolby Vision RPU file to add as T.35 metadata to the encode.
201+
/// The RPU must be in the same format as for x265
202+
#[clap(
203+
long,
204+
alias = "dolby-vision-rpu",
205+
value_parser,
206+
help_heading = "ENCODE SETTINGS"
207+
)]
208+
pub dovi_rpu: Option<PathBuf>,
198209

199210
/// Pixel range
200211
#[clap(long, value_parser, help_heading = "VIDEO METADATA")]
@@ -684,6 +695,31 @@ fn parse_config(matches: &CliOptions) -> Result<EncoderConfig, CliError> {
684695
}
685696
}
686697

698+
if let Some(rpu_file) = matches.dovi_rpu.as_ref() {
699+
let rpus = dolby_vision::rpu::utils::parse_rpu_file(rpu_file)
700+
.expect("Failed to read Dolby Vision RPU file");
701+
702+
let payloads: BTreeMap<u64, T35> = rpus
703+
.iter()
704+
.filter_map(|rpu| {
705+
rpu
706+
.write_av1_rpu_metadata_obu_t35_payload()
707+
.map(|payload| T35 {
708+
country_code: 0xB5,
709+
country_code_extension_byte: 0x00,
710+
data: payload.into_boxed_slice(),
711+
})
712+
.ok()
713+
})
714+
.zip(0u64..)
715+
.map(|(payload, frame_no)| (frame_no, payload))
716+
.collect();
717+
718+
if !payloads.is_empty() {
719+
cfg.dovi_payloads = Some(payloads);
720+
}
721+
}
722+
687723
if let Some(frame_rate) = matches.frame_rate {
688724
cfg.time_base = Rational::new(matches.time_scale, frame_rate);
689725
}

src/encoder.rs

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,6 +1284,19 @@ impl<T: Pixel> FrameInvariants<T> {
12841284
self.input_frameno * TIMESTAMP_BASE_UNIT * self.sequence.time_base.num
12851285
/ self.sequence.time_base.den
12861286
}
1287+
1288+
/// Dolby Vision Metadata as T.35 metadata from [`EncoderConfig`]
1289+
pub fn dovi_metadata(&self) -> Option<&T35> {
1290+
if !(self.show_frame || self.is_show_existing_frame()) {
1291+
return None;
1292+
}
1293+
1294+
self
1295+
.config
1296+
.dovi_payloads
1297+
.as_ref()
1298+
.and_then(|payloads| payloads.get(&self.input_frameno))
1299+
}
12871300
}
12881301

12891302
impl<T: Pixel> fmt::Display for FrameInvariants<T> {
@@ -3686,11 +3699,14 @@ pub fn encode_show_existing_frame<T: Pixel>(
36863699
}
36873700

36883701
for t35 in fi.t35_metadata.iter() {
3689-
let mut t35_buf = Vec::new();
3690-
let mut t35_bw = BitWriter::endian(&mut t35_buf, BigEndian);
3691-
t35_bw.write_t35_metadata_obu(t35).unwrap();
3692-
packet.write_all(&t35_buf).unwrap();
3693-
t35_buf.clear();
3702+
write_t35_metadata_packet(&mut packet, t35);
3703+
}
3704+
3705+
// HDR10+ Metadata OBU from config
3706+
if let Some(t35) = fi.dovi_metadata() {
3707+
if !fi.t35_metadata.iter().any(|t35| t35.is_dovi_metadata()) {
3708+
write_t35_metadata_packet(&mut packet, t35);
3709+
}
36943710
}
36953711

36963712
let mut buf1 = Vec::new();
@@ -3767,11 +3783,14 @@ pub fn encode_frame<T: Pixel>(
37673783
}
37683784

37693785
for t35 in fi.t35_metadata.iter() {
3770-
let mut t35_buf = Vec::new();
3771-
let mut t35_bw = BitWriter::endian(&mut t35_buf, BigEndian);
3772-
t35_bw.write_t35_metadata_obu(t35).unwrap();
3773-
packet.write_all(&t35_buf).unwrap();
3774-
t35_buf.clear();
3786+
write_t35_metadata_packet(&mut packet, t35);
3787+
}
3788+
3789+
// HDR10+ Metadata OBU from config
3790+
if let Some(t35) = fi.dovi_metadata() {
3791+
if !fi.t35_metadata.iter().any(|t35| t35.is_dovi_metadata()) {
3792+
write_t35_metadata_packet(&mut packet, t35);
3793+
}
37753794
}
37763795

37773796
let mut buf1 = Vec::new();
@@ -3827,6 +3846,14 @@ pub fn update_rec_buffer<T: Pixel>(
38273846
}
38283847
}
38293848

3849+
fn write_t35_metadata_packet(packet: &mut Vec<u8>, t35: &T35) {
3850+
let mut t35_buf = Vec::new();
3851+
let mut t35_bw = BitWriter::endian(&mut t35_buf, BigEndian);
3852+
t35_bw.write_t35_metadata_obu(t35).unwrap();
3853+
packet.write_all(&t35_buf).unwrap();
3854+
t35_buf.clear();
3855+
}
3856+
38303857
#[cfg(test)]
38313858
mod test {
38323859
use super::*;

src/fuzzing.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ impl Arbitrary<'_> for ArbitraryEncoder {
258258
switch_frame_interval: u.int_in_range(0..=3)?,
259259
tune: *u.choose(&[Tune::Psnr, Tune::Psychovisual])?,
260260
film_grain_params: None,
261+
dovi_payloads: None,
261262
};
262263

263264
let frame_count =

0 commit comments

Comments
 (0)