Commit 50269cb6 authored by Josh Holmer's avatar Josh Holmer Committed by Thomas Daede

Output JSON stats file with frame types

More data will likely be added to this stats file in the future.
Closes #898
parent 6266822c
......@@ -12,7 +12,7 @@ autobins = false
comparative_bench = ["aom"]
decode_test = ["bindgen", "aom"]
decode_test_dav1d = ["dav1d-sys"]
binaries = ["ivf", "y4m", "clap", "scan_fmt"]
binaries = ["ivf", "y4m", "clap", "scan_fmt", "serde_json"]
default = ["binaries", "nasm"]
aom = ["cmake"]
nasm = ["nasm-rs"]
......@@ -29,6 +29,9 @@ syn = "^0.15.20"
quote = "^0.6.10" # hack for proc-macro-hack
num-traits = "0.2"
paste = "0.1"
serde = "1.0"
serde_derive = "1.0"
serde_json = { version = "1.0", optional = true }
dav1d-sys = { version = "0.1.2", optional = true }
scan_fmt = { version = "0.1.3", optional = true }
ivf = { path = "ivf/", optional = true }
......
......@@ -19,6 +19,7 @@ use std::{cmp, fmt, io};
use std::collections::BTreeMap;
use std::sync::Arc;
use std::collections::BTreeSet;
use std::path::PathBuf;
const LOOKAHEAD_FRAMES: u64 = 10;
......@@ -43,7 +44,7 @@ pub struct Point {
pub y: u16
}
#[derive(Copy, Clone, Debug)]
#[derive(Clone, Debug)]
pub struct EncoderConfig {
// output size
pub width: usize,
......@@ -68,7 +69,10 @@ pub struct EncoderConfig {
pub quantizer: usize,
pub tune: Tune,
pub speed_settings: SpeedSettings,
/// `None` for one-pass encode. `Some(1)` or `Some(2)` for two-pass encoding.
pub pass: Option<u8>,
pub show_psnr: bool,
pub stats_file: Option<PathBuf>,
}
impl Default for EncoderConfig {
......@@ -99,7 +103,9 @@ impl EncoderConfig {
quantizer: 100,
tune: Tune::Psnr,
speed_settings: SpeedSettings::from_preset(speed),
pass: None,
show_psnr: false,
stats_file: None,
}
}
}
......@@ -236,7 +242,7 @@ impl Default for MatrixCoefficients {
}
arg_enum!{
#[derive(Debug,Clone,Copy,PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub enum ColorPrimaries {
BT709 = 1,
......@@ -261,7 +267,7 @@ impl Default for ColorPrimaries {
}
arg_enum!{
#[derive(Debug,Clone,Copy,PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub enum TransferCharacteristics {
BT1886 = 1,
......@@ -312,7 +318,7 @@ pub struct ContentLight {
}
/// Contain all the encoder configuration
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Debug)]
pub struct Config {
pub enc: EncoderConfig
}
......@@ -354,8 +360,11 @@ impl Config {
segment_start_idx: 0,
segment_start_frame: 0,
keyframe_detector: SceneChangeDetector::new(self.enc.bit_depth),
config: *self,
rc_state: RCState::new()
config: self.clone(),
rc_state: RCState::new(),
first_pass_data: FirstPassData {
frames: Vec::new(),
},
}
}
}
......@@ -379,7 +388,8 @@ pub struct Context {
segment_start_frame: u64,
keyframe_detector: SceneChangeDetector,
pub config: Config,
rc_state: RCState
rc_state: RCState,
pub first_pass_data: FirstPassData,
}
#[derive(Clone, Copy, Debug)]
......@@ -505,7 +515,7 @@ impl Context {
// The first frame will always be a key frame
let fi = FrameInvariants::new_key_frame(
&FrameInvariants::new(
self.config.enc,
self.config.enc.clone(),
seq
),
0
......@@ -669,6 +679,10 @@ impl Context {
}
}
if self.config.enc.pass == Some(1) {
self.first_pass_data.frames.push(FirstPassFrame::from(fi));
}
self.frames_processed += 1;
Ok(Packet {
data,
......@@ -732,3 +746,23 @@ impl Context {
FrameType::INTER
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FirstPassData {
frames: Vec<FirstPassFrame>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct FirstPassFrame {
number: u64,
frame_type: FrameType,
}
impl From<&FrameInvariants> for FirstPassFrame {
fn from(fi: &FrameInvariants) -> FirstPassFrame {
FirstPassFrame {
number: fi.number,
frame_type: fi.frame_type,
}
}
}
\ No newline at end of file
......@@ -14,6 +14,7 @@ use rav1e::*;
use std::{fmt, io};
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
use std::time::Instant;
pub struct EncoderIO {
......@@ -45,6 +46,20 @@ pub fn parse_cli() -> CliOptions {
.long("output")
.required(true)
.takes_value(true)
).arg(
Arg::with_name("PASS")
.help("Specify first-pass or second-pass to run as a two-pass encode")
.short("p")
.long("pass")
.takes_value(true)
.possible_values(&["1", "2"])
)
.arg(
Arg::with_name("STATS_FILE")
.help("Custom location for first-pass stats file")
.long("stats")
.takes_value(true)
.default_value("rav1e_stats.json")
).arg(
Arg::with_name("RECONSTRUCTION")
.short("r")
......@@ -222,6 +237,12 @@ fn parse_config(matches: &ArgMatches<'_>) -> EncoderConfig {
};
cfg.quantizer = quantizer;
cfg.show_psnr = matches.is_present("PSNR");
cfg.pass = matches.value_of("PASS").map(|pass| pass.parse().unwrap());
cfg.stats_file = if cfg.pass.is_some() {
Some(PathBuf::from(matches.value_of("STATS_FILE").unwrap()))
} else {
None
};
let mastering_display_opt = matches.value_of("MASTERING_DISPLAY").unwrap();
cfg.mastering_display = if mastering_display_opt == "unspecified" { None } else {
......@@ -383,7 +404,7 @@ impl ProgressInfo {
.sum()
}
pub fn print_stats(&self) -> String {
pub fn print_summary(&self) -> String {
let (key, key_size) = (
self.get_frame_type_count(FrameType::KEY),
self.get_frame_type_size(FrameType::KEY)
......
......@@ -22,9 +22,12 @@ use rav1e::*;
use std::io;
use std::io::Write;
use std::io::Read;
use std::path::Path;
use std::sync::Arc;
use crate::decoder::Decoder;
use crate::decoder::VideoDetails;
use std::fs::File;
use std::io::BufWriter;
fn read_frame_batch<D: Decoder>(ctx: &mut Context, decoder: &mut D, video_info: VideoDetails) {
loop {
......@@ -56,9 +59,10 @@ fn read_frame_batch<D: Decoder>(ctx: &mut Context, decoder: &mut D, video_info:
// Encode and write a frame.
// Returns frame information in a `Result`.
fn process_frame(
ctx: &mut Context, output_file: &mut dyn Write,
ctx: &mut Context,
output_file: &mut dyn Write,
y4m_dec: &mut y4m::Decoder<'_, Box<dyn Read>>,
mut y4m_enc: Option<&mut y4m::Encoder<'_, Box<dyn Write>>>
mut y4m_enc: Option<&mut y4m::Encoder<'_, Box<dyn Write>>>,
) -> Result<Vec<FrameSummary>, ()> {
let y4m_details = y4m_dec.get_video_details();
let mut frame_summaries = Vec::new();
......@@ -74,6 +78,13 @@ fn process_frame(
Ok(frame_summaries)
}
fn write_stats_file(ctx: &Context, filename: &Path) -> Result<(), io::Error> {
let file = File::create(filename)?;
let writer = BufWriter::new(file);
serde_json::to_writer(writer, &ctx.first_pass_data).expect("Serialization should not fail");
Ok(())
}
fn main() {
let mut cli = parse_cli();
let mut y4m_dec = y4m::decode(&mut cli.io.input).expect("input is not a y4m file");
......@@ -153,5 +164,10 @@ fn main() {
cli.io.output.flush().unwrap();
}
let _ = write!(err, "\n{}\n", progress.print_stats());
if cfg.enc.pass == Some(1) {
if let Err(e) = write_stats_file(&ctx, cfg.enc.stats_file.as_ref().unwrap()) {
let _ = writeln!(err, "\nError: Failed to write stats file! {}\n", e);
}
}
let _ = write!(err, "\n{}\n", progress.print_summary());
}
......@@ -648,7 +648,6 @@ impl FrameInvariants {
cdef_y_strengths: [0*4+0, 1*4+0, 2*4+1, 3*4+1, 5*4+2, 7*4+3, 10*4+3, 13*4+3],
cdef_uv_strengths: [0*4+0, 1*4+0, 2*4+1, 3*4+1, 5*4+2, 7*4+3, 10*4+3, 13*4+3],
delta_q_present: false,
config,
ref_frames: [0; INTER_REFS_PER_FRAME],
ref_frame_sign_bias: [false; INTER_REFS_PER_FRAME],
rec_buffer: ReferenceFramesSet::new(),
......@@ -661,6 +660,7 @@ impl FrameInvariants {
use_tx_domain_distortion,
inter_cfg: None,
enable_early_exit: true,
config,
}
}
......@@ -875,8 +875,8 @@ pub struct InterPropsConfig {
pub group_idx: u64,
}
#[allow(dead_code,non_camel_case_types)]
#[derive(Debug,PartialEq,Clone,Copy)]
#[allow(dead_code, non_camel_case_types)]
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]
#[repr(C)]
pub enum FrameType {
KEY,
......
......@@ -9,6 +9,9 @@
#![allow(safe_extern_statics)]
#[macro_use]
extern crate serde_derive;
#[cfg(all(test, feature="decode_test_dav1d"))]
extern crate dav1d_sys;
......
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