Skip to main content

viser_contextaware/
lib.rs

1mod profile;
2
3pub use profile::*;
4
5use serde::{Deserialize, Serialize};
6use std::time::{Duration, Instant};
7use viser_encoding::{Config as EncodingConfig, ProgressSender};
8use viser_hull::{Hull, Point};
9use viser_ladder::{self, Ladder};
10
11/// Config for context-aware analysis.
12#[derive(Debug, Clone)]
13pub struct Config {
14    pub profiles: Vec<Profile>,
15    pub crf_values: Vec<i32>,
16    pub preset: String,
17    pub subsample: i32,
18    pub parallel: i32,
19}
20
21impl Default for Config {
22    fn default() -> Self {
23        Self {
24            profiles: all_profiles(),
25            crf_values: vec![18, 22, 26, 30, 34, 38, 42],
26            preset: "veryfast".into(),
27            subsample: 5,
28            parallel: 2,
29        }
30    }
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct DeviceResult {
35    pub profile: Profile,
36    pub hull: Hull,
37    pub ladder: Ladder,
38    pub points: Vec<Point>,
39    pub trial_count: usize,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct Result {
44    pub source: String,
45    pub devices: Vec<DeviceResult>,
46    pub duration: Duration,
47}
48
49#[derive(Debug, Clone)]
50pub struct Progress {
51    pub device_done: usize,
52    pub device_total: usize,
53    pub device_name: String,
54}
55
56/// Runs per-title analysis for each device profile.
57pub async fn analyze(
58    source: &str,
59    cfg: Config,
60    progress_tx: Option<tokio::sync::mpsc::Sender<Progress>>,
61) -> anyhow::Result<Result> {
62    let start = Instant::now();
63    let mut devices = Vec::new();
64    let sender = ProgressSender::new(progress_tx);
65
66    for (i, profile) in cfg.profiles.iter().enumerate() {
67        let pt_cfg = viser_pertitle::Config {
68            encoding: EncodingConfig {
69                resolutions: profile.resolutions.clone(),
70                crf_values: cfg.crf_values.clone(),
71                codecs: profile.codecs.clone(),
72                preset: cfg.preset.clone(),
73                subsample: cfg.subsample,
74                parallel: cfg.parallel,
75                ..Default::default()
76            },
77            ladder_opts: profile.ladder_opts.clone(),
78            vmaf_model: profile.vmaf_model.clone(),
79            checkpoint_path: String::new(),
80            allow_hdr: false,
81        };
82
83        let pt_result = viser_pertitle::analyze(source, pt_cfg, None)
84            .await
85            .map_err(|e| anyhow::anyhow!("analysis for {} failed: {e}", profile.name))?;
86
87        let device_ladder = viser_ladder::select(&pt_result.hull, &profile.ladder_opts);
88
89        devices.push(DeviceResult {
90            profile: profile.clone(),
91            hull: pt_result.hull,
92            ladder: device_ladder,
93            points: pt_result.points,
94            trial_count: pt_result.trial_count,
95        });
96
97        sender.send(Progress {
98            device_done: i + 1,
99            device_total: cfg.profiles.len(),
100            device_name: profile.name.clone(),
101        });
102    }
103
104    Ok(Result { source: source.to_string(), devices, duration: start.elapsed() })
105}