1use crate::staging::Staging;
29use crate::{
30 container::Muxer,
31 output::{Destination, PreparedOutput, Stats},
32 packet::Packet,
33 session::{CodecConfig, Receive, SendStatus, VideoEncoder},
34 track::TrackInfo,
35};
36use alloc::boxed::Box;
37use core::{error::Error as StdError, fmt};
38
39use zencodec::{
40 At,
41 frame::{Frame, FrameInterpretation},
42 operation::{
43 FrameConversionOffer, FrameFacts, FrameRoute, FrameRouteOffer, FrameRouteState,
44 OperationPolicy, OperationReport, PolicySet, resolve_frame_route,
45 },
46 storage::StorageBudget,
47};
48
49pub type Cause = Box<dyn StdError + Send + Sync>;
51
52#[derive(Clone, Copy, Debug, PartialEq, Eq)]
54#[non_exhaustive]
55pub enum Stage {
56 Prepare,
58 Policy,
60 Convert,
62 Encode,
64 Finish,
66 Publish,
68 Terminal,
70}
71pub struct Failure {
74 pub stage: Stage,
76 pub emitted: u64,
79 pub report: Box<OperationReport>,
81 cause: Cause,
82}
83impl fmt::Debug for Failure {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 f.debug_struct("StagedEncodeFailure")
86 .field("stage", &self.stage)
87 .field("emitted", &self.emitted)
88 .field("report", &self.report)
89 .finish_non_exhaustive()
90 }
91}
92impl fmt::Display for Failure {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 write!(f, "staged encode failed during {:?}", self.stage)
95 }
96}
97impl StdError for Failure {
98 fn source(&self) -> Option<&(dyn StdError + 'static)> {
99 Some(self.cause.as_ref())
100 }
101}
102#[derive(Clone, Copy, Debug, PartialEq, Eq)]
104#[non_exhaustive]
105pub enum Refusal {
106 ConfigurationUnavailable,
108 VideoConfigurationRequired,
110 OfferMismatch,
112 ConversionMismatch,
114 ReceivePendingWithoutPacket,
116 EarlyEnd,
118 ConfigurationChanged,
120 InputAfterEnd,
122 EmptyPacket,
124 Terminal,
126 NoAdmittedFrames,
128 PolicyChangedAfterInput,
130}
131impl fmt::Display for Refusal {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 write!(f, "staged encode refusal: {self:?}")
134 }
135}
136impl StdError for Refusal {}
137fn invalid(reason: Refusal) -> Cause {
138 Box::new(reason)
139}
140fn check(pulse: &dyn zencodec::Pulse) -> Result<(), Cause> {
141 pulse
142 .check()
143 .map_err(|reason| At::wrap(zencodec::CodecError::new(None, reason.into())).into())
144}
145fn same_config(a: &CodecConfig, b: &CodecConfig) -> bool {
146 a.codec == b.codec
147 && a.private == b.private
148 && a.epoch == b.epoch
149 && a.time_base == b.time_base
150 && a.video == b.video
151}
152struct State<E, M, D> {
153 encoder: E,
154 output: PreparedOutput<M, D>,
155 config: CodecConfig,
156 fidelity: Option<zencodec::encode::Fidelity>,
157 packets: Staging,
158 previous: Option<FrameFacts>,
159}
160#[derive(Debug)]
162pub struct Completed {
163 pub stats: Stats,
165 pub report: OperationReport,
167}
168
169pub struct StagedEncode<E, M, D, R> {
177 state: Option<State<E, M, D>>,
178 resolver: R,
179 preset: PolicySet,
180 report: OperationReport,
181 policy_changed_after_input: bool,
182}
183impl<E: VideoEncoder, M: Muxer, D: Destination, R: OperationPolicy> StagedEncode<E, M, D, R> {
184 pub fn new(
190 encoder: E,
191 mux: M,
192 destination: D,
193 resolver: R,
194 storage: StorageBudget,
195 pulse: Option<&dyn zencodec::Pulse>,
196 ) -> Result<Self, At<Failure>> {
197 Self::with_staging(
198 encoder,
199 mux,
200 destination,
201 resolver,
202 Staging::memory(storage),
203 pulse,
204 )
205 }
206 pub fn with_staging(
209 encoder: E,
210 mux: M,
211 destination: D,
212 resolver: R,
213 staging: Staging,
214 pulse: Option<&dyn zencodec::Pulse>,
215 ) -> Result<Self, At<Failure>> {
216 let local_pulse = pulse.unwrap_or(&zencodec::NoPulse);
217 let mut stage = Stage::Prepare;
218 let prepare = || -> Result<_, Cause> {
219 check(local_pulse)?;
220 stage = Stage::Policy;
221 if let Some(required) = resolver.requirements() {
222 required.check_workspace(None)?;
225 }
226 stage = Stage::Prepare;
227 let config = encoder
228 .output_config()
229 .ok_or_else(|| invalid(Refusal::ConfigurationUnavailable))?
230 .clone();
231 let video = config
232 .video
233 .ok_or_else(|| invalid(Refusal::VideoConfigurationRequired))?;
234 let mut track = TrackInfo::video(0, config.codec.clone(), config.time_base, video)
235 .with_config_epoch(config.epoch);
236 if let Some(private) = &config.private {
237 track = track.with_codec_private(private.clone());
238 }
239 let output = PreparedOutput::new(mux, destination, &[track.build()?], pulse)?;
240 let fidelity = encoder.output_fidelity();
241 Ok(State {
242 encoder,
243 fidelity,
244 output,
245 config,
246 packets: staging,
247 previous: None,
248 })
249 };
250 let state = prepare().map_err(|cause| {
251 At::wrap(Failure {
252 stage,
253 emitted: 0,
254 report: Box::default(),
255 cause,
256 })
257 })?;
258 Ok(Self {
259 state: Some(state),
260 resolver,
261 preset: PolicySet::new(),
262 report: OperationReport::default(),
263 policy_changed_after_input: false,
264 })
265 }
266 #[deprecated(note = "Pass PolicySet or BoundPolicy as the operation policy argument")]
272 pub fn with_policy_set(mut self, preset: PolicySet) -> Self {
273 self.policy_changed_after_input |= self
274 .state
275 .as_ref()
276 .is_some_and(|state| state.previous.is_some());
277 self.preset = preset;
278 self
279 }
280 pub fn report(&self) -> &OperationReport {
282 &self.report
283 }
284 pub fn is_failed(&self) -> bool {
286 self.state.is_none()
287 }
288 fn failure(&self, stage: Stage, cause: Cause) -> At<Failure> {
289 self.failure_after(stage, cause, 0)
290 }
291 fn failure_after(&self, stage: Stage, cause: Cause, emitted: u64) -> At<Failure> {
292 let emitted = zencodec::find_cause::<crate::output::Failure>(cause.as_ref())
293 .map_or(emitted, |failure| failure.emitted);
294 At::wrap(Failure {
295 stage,
296 emitted,
297 report: Box::new(self.report.clone()),
298 cause,
299 })
300 }
301 pub fn push(
304 &mut self,
305 input: Frame,
306 pulse: Option<&dyn zencodec::Pulse>,
307 ) -> Result<(), At<Failure>> {
308 let target = input.interpretation().clone();
309 let mut offer = FrameConversionOffer::new(FrameFacts::new(&target, input.timing()));
310 offer.preserves_samples = true;
311 offer.preserves_extended_range = true;
312 self.push_converted(input, &target, offer, Ok, pulse)
313 }
314 pub fn push_converted(
319 &mut self,
320 input: Frame,
321 target: &FrameInterpretation,
322 conversion: FrameConversionOffer,
323 execute: impl FnOnce(Frame) -> Result<Frame, Cause>,
324 pulse: Option<&dyn zencodec::Pulse>,
325 ) -> Result<(), At<Failure>> {
326 let local_pulse = pulse.unwrap_or(&zencodec::NoPulse);
327 let mut state = self
328 .state
329 .take()
330 .ok_or_else(|| self.failure(Stage::Terminal, invalid(Refusal::Terminal)))?;
331 let mut stage = Stage::Policy;
332 let run = || -> Result<(), Cause> {
333 check(local_pulse)?;
334 if self.policy_changed_after_input {
335 return Err(invalid(Refusal::PolicyChangedAfterInput));
336 }
337 if state.encoder.output_fidelity() != state.fidelity {
338 return Err(invalid(Refusal::ConfigurationChanged));
339 }
340 if conversion.encoder_input != FrameFacts::new(target, conversion.encoder_input.timing)
341 {
342 return Err(invalid(Refusal::OfferMismatch));
343 }
344 let mut offer = FrameRouteOffer::new(if state.previous.is_none() {
345 FrameRoute::BeginSegment
346 } else {
347 FrameRoute::Continue
348 });
349 offer.conversion = Some(conversion);
350 offer.fidelity = state.fidelity;
351 offer.destination = Some(state.output.facts());
352 offer.admitted_encoded_bytes = state.packets.admitted_memory();
353 offer.staging = Some(state.packets.facts());
354 offer.output_schedule = Some(zencodec::operation::OutputScheduleOffer::new(
355 zencodec::operation::OutputSchedule::FinishFirst,
356 ));
357 let mut progress = FrameRouteState::default();
358 progress.previous = state.previous;
359 match resolve_frame_route(
360 input.interpretation(),
361 input.timing(),
362 progress,
363 &[offer],
364 &mut self.resolver,
365 self.preset,
366 pulse,
367 ) {
368 Ok((_, record)) => self.report.record(record),
369 Err(error) => {
370 self.report.record(error.error().record());
371 return Err(error.into());
372 }
373 }
374 stage = Stage::Convert;
375 let previous = FrameFacts::new(input.interpretation(), input.timing());
376 let converted = execute(input)?;
377 check(local_pulse)?;
378 if converted.interpretation().as_ref() != target
379 || converted.timing() != conversion.encoder_input.timing
380 {
381 return Err(invalid(Refusal::ConversionMismatch));
382 }
383 stage = Stage::Encode;
384 loop {
385 check(local_pulse)?;
386 match state.encoder.send(&converted, pulse)? {
387 SendStatus::Accepted => break,
388 SendStatus::ReceivePending => match state.encoder.receive(pulse)? {
389 Receive::Ready(packet) => {
390 stage_packet(&mut state.packets, packet, &state.output, local_pulse)?
391 }
392 _ => return Err(invalid(Refusal::ReceivePendingWithoutPacket)),
393 },
394 }
395 }
396 loop {
397 check(local_pulse)?;
398 match state.encoder.receive(pulse)? {
399 Receive::Ready(packet) => {
400 stage_packet(&mut state.packets, packet, &state.output, local_pulse)?
401 }
402 Receive::NeedInput => break,
403 Receive::EndOfStream => return Err(invalid(Refusal::EarlyEnd)),
404 }
405 }
406 if !state
407 .encoder
408 .output_config()
409 .is_some_and(|config| same_config(config, &state.config))
410 || state.encoder.output_fidelity() != state.fidelity
411 {
412 return Err(invalid(Refusal::ConfigurationChanged));
413 }
414 state.previous = Some(previous);
415 check(local_pulse)
416 };
417 run().map_err(|cause| self.failure(stage, cause))?;
418 self.state = Some(state);
419 Ok(())
420 }
421 pub fn finish(mut self, pulse: Option<&dyn zencodec::Pulse>) -> Result<Completed, At<Failure>> {
427 let local_pulse = pulse.unwrap_or(&zencodec::NoPulse);
428 let mut state = self
429 .state
430 .take()
431 .ok_or_else(|| self.failure(Stage::Terminal, invalid(Refusal::Terminal)))?;
432 let mut drain = || -> Result<(), Cause> {
433 check(local_pulse)?;
434 if self.policy_changed_after_input {
435 return Err(invalid(Refusal::PolicyChangedAfterInput));
436 }
437 if state.previous.is_none() {
438 return Err(invalid(Refusal::NoAdmittedFrames));
439 }
440 state.encoder.end_input()?;
441 loop {
442 check(local_pulse)?;
443 match state.encoder.receive(pulse)? {
444 Receive::Ready(packet) => {
445 stage_packet(&mut state.packets, packet, &state.output, local_pulse)?
446 }
447 Receive::EndOfStream => break,
448 Receive::NeedInput => {
449 return Err(invalid(Refusal::InputAfterEnd));
450 }
451 }
452 }
453 if !state
454 .encoder
455 .output_config()
456 .is_some_and(|config| same_config(config, &state.config))
457 || state.encoder.output_fidelity() != state.fidelity
458 {
459 return Err(invalid(Refusal::ConfigurationChanged));
460 }
461 check(local_pulse)
462 };
463 drain().map_err(|cause| self.failure(Stage::Finish, cause))?;
464 let State {
465 encoder,
466 output,
467 mut packets,
468 ..
469 } = state;
470 drop(encoder);
471 let mut emitted = 0;
472 let publish = || -> Result<Stats, Cause> {
473 packets.begin_replay(local_pulse)?;
474 let mut packet = packets.next(local_pulse)?;
476 let mut output = output.publish(pulse)?;
477 emitted = output.stats().emitted;
478 while let Some(held) = packet {
479 output.send(held, pulse)?;
480 emitted = output.stats().emitted;
481 packet = packets.next(local_pulse)?;
482 }
483 Ok(output.finish(pulse)?)
484 };
485 let stats =
486 publish().map_err(|cause| self.failure_after(Stage::Publish, cause, emitted))?;
487 Ok(Completed {
488 stats,
489 report: self.report,
490 })
491 }
492}
493fn stage_packet<M: Muxer, D: Destination>(
494 packets: &mut Staging,
495 packet: Packet,
496 output: &PreparedOutput<M, D>,
497 pulse: &dyn zencodec::Pulse,
498) -> Result<(), Cause> {
499 output.validate_packet(&packet)?;
500 if packet.is_empty() {
501 return Err(invalid(Refusal::EmptyPacket));
502 }
503 packets.push(packet, pulse)
504}