Commit 7f81d949 authored by Vittorio Giovara's avatar Vittorio Giovara Committed by Thomas Daede

Implement OBU metadata with mastering display and content light level

parent 7de7e030
......@@ -12,7 +12,7 @@ repl = ["rustyline", "binaries"]
comparative_bench = ["aom"]
decode_test = ["bindgen", "aom"]
decode_test_dav1d = ["dav1d-sys"]
binaries = ["y4m", "clap"]
binaries = ["y4m", "clap", "scan_fmt"]
default = ["binaries", "nasm"]
aom = ["cmake"]
nasm = ["nasm-rs"]
......@@ -30,6 +30,7 @@ quote = "^0.6.10" # hack for proc-macro-hack
num-traits = "0.2"
paste = "0.1"
dav1d-sys = { version = "0.1.2", optional = true }
scan_fmt = { version = "0.1.3", optional = true }
[build-dependencies]
cmake = { version = "0.1", optional = true }
......
......@@ -61,6 +61,13 @@ impl Rational {
}
}
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct Point {
pub x: u16,
pub y: u16
}
#[derive(Copy, Clone, Debug)]
pub struct EncoderConfig {
/// The *minimum* interval between two keyframes
......@@ -72,6 +79,8 @@ pub struct EncoderConfig {
pub tune: Tune,
pub pixel_range: PixelRange,
pub color_description: Option<ColorDescription>,
pub mastering_display: Option<MasteringDisplay>,
pub content_light: Option<ContentLight>,
pub speed_settings: SpeedSettings,
pub show_psnr: bool,
}
......@@ -93,6 +102,8 @@ impl EncoderConfig {
tune: Tune::Psnr,
pixel_range: PixelRange::Unspecified,
color_description: None,
mastering_display: None,
content_light: None,
speed_settings: SpeedSettings::from_preset(speed),
show_psnr: false,
}
......@@ -292,6 +303,20 @@ pub struct ColorDescription {
pub matrix_coefficients: MatrixCoefficients
}
#[derive(Copy, Clone, Debug)]
pub struct MasteringDisplay {
pub primaries: [Point; 3],
pub white_point: Point,
pub max_luminance: u32,
pub min_luminance: u32,
}
#[derive(Copy, Clone, Debug)]
pub struct ContentLight {
pub max_content_light_level: u16,
pub max_frame_average_light_level: u16,
}
/// Contain all the encoder configuration
#[derive(Clone, Copy, Debug)]
pub struct Config {
......@@ -480,6 +505,8 @@ impl Context {
let mut seq = Sequence::new(&self.config.video_info);
seq.pixel_range = self.config.enc.pixel_range;
seq.color_description = self.config.enc.color_description;
seq.mastering_display = self.config.enc.mastering_display;
seq.content_light = self.config.enc.content_light;
// The first frame will always be a key frame
let fi = FrameInvariants::new_key_frame(
......
......@@ -127,6 +127,18 @@ pub fn parse_cli() -> CliOptions {
.possible_values(&MatrixCoefficients::variants())
.default_value("unspecified")
.case_insensitive(true)
).arg(
Arg::with_name("MASTERING_DISPLAY")
.help("Mastering display primaries in the form of G(x,y)B(x,y)R(x,y)WP(x,y)L(max,min).")
.long("mastering_display")
.default_value("unspecified")
.case_insensitive(true)
).arg(
Arg::with_name("CONTENT_LIGHT")
.help("Content light level used to describe content luminosity (cll,fall).")
.long("content_light")
.default_value("0,0")
.case_insensitive(true)
).arg(
Arg::with_name("VERBOSE")
.help("verbose logging, output info for every frame")
......@@ -210,6 +222,42 @@ fn parse_config(matches: &ArgMatches) -> EncoderConfig {
cfg.quantizer = quantizer;
cfg.show_psnr = matches.is_present("PSNR");
let mastering_display_opt = matches.value_of("MASTERING_DISPLAY").unwrap();
cfg.mastering_display = if mastering_display_opt == "unspecified" { None } else {
let (g_x, g_y, b_x, b_y, r_x, r_y, wp_x, wp_y, max_lum, min_lum) = scan_fmt!(mastering_display_opt, "G({},{})B({},{})R({},{})WP({},{})L({},{})", f64, f64, f64, f64, f64, f64, f64, f64, f64, f64);
Some(MasteringDisplay {
primaries: [
Point{
x: (r_x.unwrap() * ((1 << 16) as f64)).round() as u16,
y: (r_y.unwrap() * ((1 << 16) as f64)).round() as u16,
},
Point{
x: (g_x.unwrap() * ((1 << 16) as f64)).round() as u16,
y: (g_y.unwrap() * ((1 << 16) as f64)).round() as u16,
},
Point{
x: (b_x.unwrap() * ((1 << 16) as f64)).round() as u16,
y: (b_y.unwrap() * ((1 << 16) as f64)).round() as u16,
}
],
white_point: Point{
x: (wp_x.unwrap() * ((1 << 16) as f64)).round() as u16,
y: (wp_y.unwrap() * ((1 << 16) as f64)).round() as u16,
},
max_luminance: (max_lum.unwrap() * ((1 << 8) as f64)).round() as u32,
min_luminance: (min_lum.unwrap() * ((1 << 14) as f64)).round() as u32,
})
};
let content_light_opt = matches.value_of("CONTENT_LIGHT").unwrap();
let (cll, fall) = scan_fmt!(content_light_opt, "{},{}", u16, u16);
cfg.content_light = if cll.unwrap() == 0 && fall.unwrap() == 0 { None } else {
Some(ContentLight {
max_content_light_level : cll.unwrap(),
max_frame_average_light_level : fall.unwrap()
})
};
cfg
}
......
......@@ -10,6 +10,8 @@
extern crate clap;
extern crate rav1e;
extern crate y4m;
#[macro_use]
extern crate scan_fmt;
mod common;
mod decoder;
......
......@@ -238,6 +238,8 @@ pub struct Sequence {
pub chroma_sample_position: ChromaSamplePosition,
pub pixel_range: PixelRange,
pub color_description: Option<ColorDescription>,
pub mastering_display: Option<MasteringDisplay>,
pub content_light: Option<ContentLight>,
pub max_frame_width: u32,
pub max_frame_height: u32,
pub frame_id_numbers_present_flag: bool,
......@@ -322,6 +324,8 @@ impl Sequence {
chroma_sample_position: info.chroma_sample_position,
pixel_range: PixelRange::Unspecified,
color_description: None,
mastering_display: None,
content_light: None,
max_frame_width: info.width as u32,
max_frame_height: info.height as u32,
frame_id_numbers_present_flag: false,
......@@ -894,6 +898,9 @@ trait UncompressedHeader {
fn write_obu_header(
&mut self, obu_type: OBU_Type, obu_extension: u32
) -> io::Result<()>;
fn write_metadata_obu(
&mut self, obu_meta_type: ObuMetaType, seq: Sequence
) -> io::Result<()>;
fn write_sequence_header_obu(
&mut self, fi: &mut FrameInvariants
) -> io::Result<()>;
......@@ -955,6 +962,57 @@ impl<W: io::Write> UncompressedHeader for BitWriter<W, BigEndian> {
Ok(())
}
fn write_metadata_obu(
&mut self, obu_meta_type: ObuMetaType, seq: Sequence
) -> io::Result<()> {
// header
self.write_obu_header(OBU_Type::OBU_METADATA, 0)?;
// uleb128() - length
// we use a constant value to avoid computing the OBU size every time
// since it is fixed (depending on the metadata)
{
let mut coded_payload_length = [0 as u8; 8];
let leb_size = aom_uleb_encode(obu_meta_type.size(), &mut coded_payload_length);
for i in 0..leb_size {
self.write(8, coded_payload_length[i]).unwrap();
}
}
// uleb128() - metadata_type (1 byte)
{
let mut coded_payload_length = [0 as u8; 8];
let leb_size = aom_uleb_encode(obu_meta_type as u64, &mut coded_payload_length);
for i in 0..leb_size {
self.write(8, coded_payload_length[i]).unwrap();
}
}
match obu_meta_type {
ObuMetaType::OBU_META_HDR_CLL => {
let cll = seq.content_light.unwrap();
self.write(16, cll.max_content_light_level)?;
self.write(16, cll.max_frame_average_light_level)?;
},
ObuMetaType::OBU_META_HDR_MDCV => {
let mdcv = seq.mastering_display.unwrap();
for i in 0..3 {
self.write(16, mdcv.primaries[i].x)?;
self.write(16, mdcv.primaries[i].y)?;
}
self.write(16, mdcv.white_point.x)?;
self.write(16, mdcv.white_point.y)?;
self.write(32, mdcv.max_luminance)?;
self.write(32, mdcv.min_luminance)?;
},
_ => {}
}
Ok(())
}
fn write_sequence_header_obu(
&mut self, fi: &mut FrameInvariants
) -> io::Result<()> {
......@@ -1647,6 +1705,27 @@ pub enum OBU_Type {
OBU_PADDING = 15,
}
#[derive(Clone,Copy)]
#[allow(non_camel_case_types)]
pub enum ObuMetaType {
OBU_META_HDR_CLL = 1,
OBU_META_HDR_MDCV = 2,
OBU_META_SCALABILITY = 3,
OBU_META_ITUT_T35 = 4,
OBU_META_TIMECODE = 5,
}
impl ObuMetaType {
fn size(&self) -> u64 {
use self::ObuMetaType::*;
match self {
OBU_META_HDR_CLL => 5,
OBU_META_HDR_MDCV => 25,
_ => 0,
}
}
}
// NOTE from libaom:
// Disallow values larger than 32-bits to ensure consistent behavior on 32 and
// 64 bit targets: value is typically used to determine buffer allocation size
......@@ -1721,6 +1800,26 @@ fn write_obus(
packet.write_all(&buf2).unwrap();
buf2.clear();
if fi.sequence.content_light.is_some() {
{
let mut bw1 = BitWriter::endian(&mut buf1, BigEndian);
bw1.write_metadata_obu(ObuMetaType::OBU_META_HDR_CLL, fi.sequence)?;
bw1.byte_align()?;
}
packet.write_all(&buf1).unwrap();
buf1.clear();
}
if fi.sequence.mastering_display.is_some() {
{
let mut bw1 = BitWriter::endian(&mut buf1, BigEndian);
bw1.write_metadata_obu(ObuMetaType::OBU_META_HDR_MDCV, fi.sequence)?;
bw1.byte_align()?;
}
packet.write_all(&buf1).unwrap();
buf1.clear();
}
}
let mut buf2 = Vec::new();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment