This repository has no description
1use super::Video;
2use crate::Timestamp;
3use crate::ui::{Log, Pretty};
4use crate::video::encoders::Encoder;
5use crate::video::engine::{EngineControl, EngineController, EngineOutput};
6use anyhow::{Result, anyhow};
7use measure_time::debug_time;
8use std::path::PathBuf;
9use std::thread;
10
11impl<C: Default> Video<C> {
12 pub fn encode(
13 &mut self,
14 output_file: impl Into<PathBuf> + Clone,
15 ) -> Result<std::time::Duration> {
16 let actual_ms_range = self.constrained_ms_range();
17 if actual_ms_range != self.total_ms_range() {
18 self.progress_bars.rendering.log(
19 "Constrained",
20 &Timestamp::from_ms_range(&actual_ms_range).pretty(),
21 );
22 }
23
24 self.encode_controlled(output_file, &move |ctx| {
25 if actual_ms_range.contains(&ctx.ms) {
26 EngineControl::Render
27 } else if ctx.ms > actual_ms_range.end {
28 EngineControl::Stop
29 } else {
30 EngineControl::Skip
31 }
32 })
33 }
34
35 pub fn encode_controlled(
36 &mut self,
37 output_file: impl Into<PathBuf> + Clone,
38 engine_controller: &EngineController<C>,
39 ) -> Result<std::time::Duration> {
40 debug_time!("encode");
41
42 let encoder = self.setup_encoder(output_file.clone())?;
43 let encoder_name = encoder.name();
44
45 let result = self.encode_with(encoder, engine_controller);
46
47 let _ = notify_rust::Notification::new()
48 .appname("Shapemaker")
49 .summary(&match result {
50 Ok(_) => format!("{} is ready", &output_file.into().pretty()),
51 Err(_) => format!("{} failed", &output_file.into().pretty()),
52 })
53 .body(&match result {
54 Err(ref e) => format!("Encoding failed: {e}"),
55 Ok(time_taken) => format!(
56 "Encoded with {encoder_name} in {}",
57 time_taken.pretty()
58 ),
59 })
60 .show();
61
62 result
63 }
64
65 pub fn encode_with(
66 &mut self,
67 mut encoder: Box<dyn Encoder + Send>,
68 engine_controller: &EngineController<C>,
69 ) -> Result<std::time::Duration> {
70 debug_time!("encode_with");
71
72 self.progress.remove(&self.progress_bars.loading);
73
74 let pb = self.progress_bars.encoding.clone();
75
76 pb.set_length(self.ms_to_frames(self.duration_ms()) as _);
77 pb.set_message("");
78
79 let (tx, rx) = std::sync::mpsc::sync_channel::<EngineOutput>(1_000);
80
81 let encoder_thread =
82 thread::spawn(move || -> Result<std::time::Duration> {
83 for output in rx.iter() {
84 match output {
85 EngineOutput::Finished => break,
86 EngineOutput::Frame { .. } => {
87 pb.inc(1);
88 pb.set_message(encoder.progress_message(
89 pb.position(),
90 pb.length().unwrap(),
91 ));
92 }
93 }
94
95 encoder.encode_frame(output)?;
96 }
97
98 let time_taken = pb.elapsed();
99 let finish_message = encoder.finish_message(time_taken);
100
101 encoder.finish()?;
102
103 pb.finish();
104 pb.log("Encoded", &finish_message);
105
106 Ok(time_taken)
107 });
108
109 self.render(tx, engine_controller)?;
110
111 let time_taken = encoder_thread
112 .join()
113 .map_err(|e| anyhow!("Encoder thread panicked: {e:?}"))
114 .flatten()?;
115
116 let _ = self.progress.clear();
117
118 Ok(time_taken)
119 }
120
121 #[allow(dead_code)]
122 fn add_audio_track(&mut self, _output_file: String) -> Result<()> {
123 todo!(
124 "Look into https://github.com/zmwangx/rust-ffmpeg/blob/master/examples/transcode-x264.rs and maybe contribute to video-rs (see https://github.com/oddity-ai/video-rs/issues/44)"
125 );
126 }
127}