Skip to content

Commit 2a1e029

Browse files
authored
Implement RPU parsing (#1)
Implement RPU parsing according to patent No. US 2019/0373290 A1 Allow processing the RPU when demuxing and extracting
1 parent df01aa9 commit 2a1e029

28 files changed

+2258
-197
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ structopt = "0.3.20"
1010
indicatif = "0.15.0"
1111
regex = "1.4.2"
1212
ansi_term = "0.12.1"
13+
bitvec = "0.19.4"
14+
crc = "2.0.0-rc.1"

README.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
1-
## Rust port of yusesope's python tool.
2-
## Credits goes to them.
1+
### Options
2+
`--mode`, `-m` Sets the mode for RPU processing. `--help` for more info
33

4-
## Commands
4+
### Commands
55

6-
### Demux
6+
#### demux
7+
Rust port of yusesope's python tool. Credits goes to them.
78
Demuxes single track dual layer Dolby Vision into Base layer and Enhancement layer files.
89

910
* `dovi_tool demux file.hevc`
1011
* `ffmpeg -i input.mkv -c:v copy -vbsf hevc_mp4toannexb -f hevc - | dovi_tool demux -`
1112

13+
#### extract-rpu
14+
Extracts Dolby Vision RPU from an HEVC encoded file.
15+
Supports profiles 5, 7, and 8.
16+
Input can be piped.
17+
18+
* `dovi_tool extract-rpu video.hevc`
19+
* FEL to MEL example: `dovi_tool -m 1 extract-rpu video.hevc`
20+
1221
More features may or may not be added in the future.

assets/data_before_crc32.bin

239 Bytes
Binary file not shown.

assets/fel_orig.bin

382 Bytes
Binary file not shown.

assets/fel_rpu.bin

382 Bytes
Binary file not shown.

assets/fel_to_81.bin

346 Bytes
Binary file not shown.

assets/fel_to_mel.bin

384 Bytes
Binary file not shown.

assets/mel_orig.bin

181 Bytes
Binary file not shown.

assets/mel_rpu.bin

201 Bytes
Binary file not shown.

assets/mel_to_81.bin

140 Bytes
Binary file not shown.

assets/mel_to_mel.bin

181 Bytes
Binary file not shown.

assets/profile5.bin

164 Bytes
Binary file not shown.

assets/profile8.bin

165 Bytes
Binary file not shown.

profiles.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Differentiating between Dolby Vision profiles.
2+
##### Profile 5
3+
`vdr_rpu_profile = 0`
4+
`bl_video_full_range_flag = 0`
5+
##### Profile 7
6+
`vdr_rpu_profile = 1`
7+
`el_spatial_resampling_filter_flag = 1`
8+
`disable_residual_flag = 0`
9+
##### Profile 8
10+
`vdr_rpu_profile = 1`
11+
`el_spatial_resampling_filter_flag = 0`

src/bits/bitvec_reader.rs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
use bitvec::prelude::*;
2+
use std::fmt;
3+
4+
#[derive(Default)]
5+
pub struct BitVecReader {
6+
bs: BitVec<Msb0, u8>,
7+
offset: usize,
8+
}
9+
10+
impl BitVecReader {
11+
pub fn new(data: Vec<u8>) -> Self {
12+
Self {
13+
bs: BitVec::from_vec(data),
14+
offset: 0,
15+
}
16+
}
17+
18+
#[inline(always)]
19+
pub fn get(&mut self) -> bool {
20+
let val = self.bs.get(self.offset).unwrap();
21+
self.offset += 1;
22+
23+
*val
24+
}
25+
26+
#[inline(always)]
27+
pub fn get_n<T: BitMemory>(&mut self, n: usize) -> T {
28+
let val = self.bs[self.offset..self.offset + n].load_be::<T>();
29+
self.offset += n;
30+
31+
val
32+
}
33+
34+
// bitstring.py implementation: https://github.com/scott-griffiths/bitstring/blob/master/bitstring.py#L1706
35+
#[inline(always)]
36+
pub fn get_ue(&mut self) -> u64 {
37+
let oldpos = self.offset;
38+
let mut pos = self.offset;
39+
40+
loop {
41+
match self.bs.get(pos) {
42+
Some(val) => {
43+
if !val {
44+
pos += 1;
45+
} else {
46+
break;
47+
}
48+
}
49+
None => panic!("Out of bounds index: {}", pos),
50+
}
51+
}
52+
53+
let leading_zeroes = pos - oldpos;
54+
let mut code_num = (1 << leading_zeroes) - 1;
55+
56+
if leading_zeroes > 0 {
57+
if pos + leading_zeroes + 1 > self.bs.len() {
58+
panic!("Out of bounds attempt");
59+
}
60+
61+
code_num += self.bs[pos + 1..pos + leading_zeroes + 1].load_be::<u64>();
62+
pos += leading_zeroes + 1;
63+
} else {
64+
assert_eq!(code_num, 0);
65+
pos += 1;
66+
}
67+
68+
self.offset = pos;
69+
70+
code_num
71+
}
72+
73+
// bitstring.py implementation: https://github.com/scott-griffiths/bitstring/blob/master/bitstring.py#L1767
74+
#[inline(always)]
75+
pub fn get_se(&mut self) -> i64 {
76+
let code_num = self.get_ue();
77+
78+
let m = ((code_num + 1) as f64 / 2.0).floor() as u64;
79+
80+
if code_num % 2 == 0 {
81+
-(m as i64)
82+
} else {
83+
m as i64
84+
}
85+
}
86+
87+
pub fn is_aligned(&self) -> bool {
88+
self.offset % 8 == 0
89+
}
90+
91+
pub fn available(&self) -> usize {
92+
self.bs.len() - self.offset
93+
}
94+
}
95+
96+
impl fmt::Debug for BitVecReader {
97+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98+
write!(
99+
f,
100+
"BitVecReader: {{offset: {}, len: {}}}",
101+
self.offset,
102+
self.bs.len()
103+
)
104+
}
105+
}

src/bits/bitvec_writer.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use super::signed_to_unsigned;
2+
use bitvec::prelude::*;
3+
4+
#[derive(Debug)]
5+
pub struct BitVecWriter {
6+
bs: BitVec<Msb0, u8>,
7+
offset: usize,
8+
}
9+
10+
impl BitVecWriter {
11+
pub fn new() -> Self {
12+
Self {
13+
bs: BitVec::new(),
14+
offset: 0,
15+
}
16+
}
17+
18+
#[inline(always)]
19+
pub fn write(&mut self, v: bool) {
20+
self.bs.push(v);
21+
self.offset += 1;
22+
}
23+
24+
#[inline(always)]
25+
pub fn write_n(&mut self, v: &[u8], n: usize) {
26+
let slice = v.view_bits();
27+
28+
self.bs.extend_from_bitslice(&slice[slice.len() - n..]);
29+
30+
self.offset += n;
31+
}
32+
33+
#[inline(always)]
34+
pub fn write_ue(&mut self, v: u64) {
35+
if v == 0 {
36+
self.bs.push(true);
37+
self.offset += 1;
38+
} else {
39+
let mut vec: BitVec<Msb0, u8> = BitVec::new();
40+
let mut tmp = v + 1;
41+
let mut leading_zeroes: i64 = -1;
42+
43+
while tmp > 0 {
44+
tmp >>= 1;
45+
leading_zeroes += 1;
46+
}
47+
48+
let remaining = (v + 1 - (1 << leading_zeroes)).to_be_bytes();
49+
50+
for _ in 0..leading_zeroes {
51+
vec.push(false);
52+
}
53+
54+
vec.push(true);
55+
56+
self.bs.extend_from_bitslice(&vec);
57+
self.offset += vec.len();
58+
59+
self.write_n(&remaining, leading_zeroes as usize);
60+
}
61+
}
62+
63+
#[inline(always)]
64+
pub fn write_se(&mut self, v: i64) {
65+
self.write_ue(signed_to_unsigned(v));
66+
}
67+
68+
pub fn is_aligned(&self) -> bool {
69+
self.offset % 8 == 0
70+
}
71+
72+
pub fn as_slice(&self) -> &[u8] {
73+
self.bs.as_slice()
74+
}
75+
}

src/bits/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
pub mod bitvec_reader;
2+
pub mod bitvec_writer;
3+
4+
pub(crate) fn signed_to_unsigned(v: i64) -> u64 {
5+
let u = if v.is_positive() { v } else { -2 * v };
6+
7+
u as u64
8+
}

0 commit comments

Comments
 (0)