1use std::{collections::HashMap, sync::Arc};
2
3use crate::pp_log::{PpLog, pp_trace};
4
5use crate::{
6 buffer::MediaBuffer,
7 bus::{Bus, BusEvent},
8 contract::{InputContract, OutputContract},
9 control::ControlMsg,
10 element::{Context, Element, ElementType, Filter, Sink, Source, element_pp_log},
11 error::Result,
12 graph::{
13 BranchId, BranchPlan, ElementId, GraphError, Incoming, NodeInfo, PlannedEdge,
14 PortContracts, PortRef, ResolvedFlow,
15 },
16 pad::SrcPad,
17 queue::{OverflowPolicy, Queue},
18};
19
20pub struct ChainBuilder {
29 context: Arc<Context>,
30 elements: Vec<Box<dyn StageBuilder>>,
31 planned: Vec<PlannedNode>,
34 error: Option<GraphError>,
35}
36
37struct PlannedNode {
38 info: NodeInfo,
39 output_port: Arc<str>,
40 input: InputContract,
41 output: OutputContract,
42}
43
44pub struct DetachedBranch {
47 pub(crate) root: Box<dyn Sink>,
48 pub(crate) plan: BranchPlan,
49}
50
51impl DetachedBranch {
52 pub fn root_id(&self) -> ElementId {
57 self.plan.root
58 }
59}
60
61trait StageBuilder: Send {
62 fn wrap(
70 self: Box<Self>,
71 downstream: Box<dyn Sink>,
72 context: &Arc<Context>,
73 ) -> Result<Box<dyn Sink>>;
74}
75
76struct DirectStage<T>(T);
77
78struct FlowTracer<T> {
81 inner: T,
82}
83
84impl<T: Element> Element for FlowTracer<T> {
85 fn name(&self) -> Arc<str> {
86 self.inner.name()
87 }
88
89 fn element_type(&self) -> ElementType {
90 self.inner.element_type()
91 }
92
93 fn graph_id(&self) -> Option<ElementId> {
94 self.inner.graph_id()
95 }
96
97 fn pp_log(&self) -> &PpLog {
98 self.inner.pp_log()
99 }
100
101 fn pp_log_mut(&mut self) -> &mut PpLog {
102 self.inner.pp_log_mut()
103 }
104}
105
106impl<T: Source> Source for FlowTracer<T> {
107 fn src_pads(&mut self) -> &mut [SrcPad] {
108 self.inner.src_pads()
109 }
110}
111
112impl<T: Filter> Sink for FlowTracer<T> {
113 fn ready_consume(&mut self) -> bool {
114 self.inner.ready_consume() && self.inner.src_pads().iter_mut().all(SrcPad::ready_consume)
115 }
116
117 fn input_contract(&self) -> InputContract {
118 self.inner.input_contract()
119 }
120
121 fn consume(&mut self, buf: MediaBuffer) -> Result<()> {
122 let is_eos = buf.is_eos();
123 if is_eos {
124 pp_trace!(pp_log: self.inner.pp_log(), "event=eos phase=received");
125 }
126 let result = self.inner.consume(buf);
127 if is_eos {
128 match &result {
129 Ok(()) => pp_trace!(
130 pp_log: self.inner.pp_log(),
131 "event=eos phase=completed outcome=ok"
132 ),
133 Err(error) => pp_trace!(
134 pp_log: self.inner.pp_log(),
135 "event=eos phase=completed outcome=error error={error}"
136 ),
137 }
138 }
139 result
140 }
141
142 fn control(&mut self, msg: ControlMsg) -> Result<()> {
143 pp_trace!(
144 pp_log: self.inner.pp_log(),
145 "event=control control={msg:?} phase=received"
146 );
147 let result = self.inner.control(msg.clone());
148 match &result {
149 Ok(()) => pp_trace!(
150 pp_log: self.inner.pp_log(),
151 "event=control control={msg:?} phase=completed outcome=ok"
152 ),
153 Err(error) => pp_trace!(
154 pp_log: self.inner.pp_log(),
155 "event=control control={msg:?} phase=completed outcome=error error={error}"
156 ),
157 }
158 result
159 }
160}
161
162impl<T> StageBuilder for DirectStage<T>
163where
164 T: Filter + 'static,
165{
166 fn wrap(
167 self: Box<Self>,
168 downstream: Box<dyn Sink>,
169 context: &Arc<Context>,
170 ) -> Result<Box<dyn Sink>> {
171 let mut element = self.0;
172 *element.pp_log_mut() = element_pp_log(
173 element.element_type(),
174 &element.name(),
175 Some(&context.pipeline_id),
176 );
177 element.attach_context(context);
178 element.src_pads()[0].link(downstream);
179 Ok(Box::new(FlowTracer { inner: element }))
180 }
181}
182
183struct QueueStage {
184 id: ElementId,
185 name: String,
186 capacity: usize,
187 policy: OverflowPolicy,
188}
189
190struct TerminalTracer {
199 bus: Bus,
200 id: ElementId,
201 inner: Box<dyn Sink>,
202 paused: bool,
203 preroll: Option<Arc<crate::control::PrerollContext>>,
204}
205
206impl Element for TerminalTracer {
207 fn name(&self) -> Arc<str> {
208 self.inner.name()
209 }
210
211 fn element_type(&self) -> ElementType {
212 self.inner.element_type()
213 }
214
215 fn graph_id(&self) -> Option<ElementId> {
216 self.inner.graph_id()
217 }
218
219 fn pp_log(&self) -> &PpLog {
220 self.inner.pp_log()
221 }
222
223 fn pp_log_mut(&mut self) -> &mut PpLog {
224 self.inner.pp_log_mut()
225 }
226}
227
228impl Sink for TerminalTracer {
229 fn ready_consume(&mut self) -> bool {
230 if self.paused {
231 return false;
232 }
233 if self
238 .preroll
239 .as_ref()
240 .is_some_and(|context| context.is_ready(self.id))
241 {
242 return false;
243 }
244 self.inner.ready_consume()
245 }
246
247 fn input_contract(&self) -> InputContract {
248 self.inner.input_contract()
249 }
250
251 fn consume(&mut self, buf: MediaBuffer) -> Result<()> {
252 let is_eos = buf.is_eos();
253 if is_eos {
254 pp_trace!(pp_log: self.inner.pp_log(), "event=eos phase=received");
255 }
256 let result = self.inner.consume(buf);
257 if result.is_ok()
261 && let Some(context) = &self.preroll
262 {
263 if is_eos {
264 context.mark_eos(self.id);
265 } else {
266 context.mark_ready(self.id);
267 }
268 }
269 if is_eos {
270 match &result {
271 Ok(()) => {
272 pp_trace!(
273 pp_log: self.inner.pp_log(),
274 "event=eos phase=completed outcome=ok"
275 );
276 self.bus.post(
277 self.inner.pp_log(),
278 BusEvent::Eos {
279 element_type: self.inner.element_type(),
280 name: self.inner.name(),
281 },
282 );
283 }
284 Err(error) => pp_trace!(
285 pp_log: self.inner.pp_log(),
286 "event=eos phase=completed outcome=error error={error}"
287 ),
288 }
289 }
290 result
291 }
292
293 fn control(&mut self, msg: ControlMsg) -> Result<()> {
294 pp_trace!(
295 pp_log: self.inner.pp_log(),
296 "event=control control={msg:?} phase=received"
297 );
298 let result = self.inner.control(msg.clone());
299 if result.is_ok() {
300 match &msg {
301 ControlMsg::Pause => {
302 self.paused = true;
303 self.preroll = None;
304 }
305 ControlMsg::Resume => {
306 self.paused = false;
307 self.preroll = None;
308 }
309 ControlMsg::Preroll(context) => {
310 self.paused = false;
311 self.preroll = Some(Arc::clone(context));
312 }
313 ControlMsg::Stop => {
314 if let Some(context) = self.preroll.take() {
315 context.cancel();
316 }
317 self.paused = true;
318 }
319 ControlMsg::Flush | ControlMsg::CheckSeek(_) | ControlMsg::Seek(_) => {}
320 }
321 }
322 match &result {
323 Ok(()) => pp_trace!(
324 pp_log: self.inner.pp_log(),
325 "event=control control={msg:?} phase=completed outcome=ok"
326 ),
327 Err(error) => pp_trace!(
328 pp_log: self.inner.pp_log(),
329 "event=control control={msg:?} phase=completed outcome=error error={error}"
330 ),
331 }
332 result
333 }
334}
335
336impl StageBuilder for QueueStage {
337 fn wrap(
338 self: Box<Self>,
339 downstream: Box<dyn Sink>,
340 context: &Arc<Context>,
341 ) -> Result<Box<dyn Sink>> {
342 Ok(Box::new(Queue::spawn_with_policy(
346 self.name,
347 self.capacity,
348 downstream,
349 context.bus.for_element(self.id),
350 self.policy,
351 Some(&context.pipeline_id),
352 )?))
353 }
354}
355
356impl ChainBuilder {
357 pub fn new(context: Arc<Context>) -> Self {
361 Self {
362 context,
363 elements: Vec::new(),
364 planned: Vec::new(),
365 error: None,
366 }
367 }
368
369 pub fn pipe<T: Filter + 'static>(mut self, mut element: T) -> Self {
374 let name = element.name();
375 let pad_count = element.src_pads().len();
376 if pad_count != 1 && self.error.is_none() {
377 self.error = Some(GraphError::NotSingleOutput {
378 name: name.clone(),
379 count: pad_count,
380 });
381 }
382 let (output_port, output) = element
383 .src_pads()
384 .first()
385 .map(|pad| (Arc::<str>::from(pad.name()), pad.contract()))
386 .unwrap_or_else(|| ("src".into(), OutputContract::Unknown));
387 self.planned.push(PlannedNode {
388 info: NodeInfo {
389 id: self.context.graph.reserve_element_id(),
390 element_type: element.element_type(),
391 name,
392 },
393 output_port,
394 input: element.input_contract(),
395 output,
396 });
397 self.elements.push(Box::new(DirectStage(element)));
398 self
399 }
400
401 pub fn queue(self, name: impl Into<String>, capacity: usize) -> Self {
405 self.queue_with_policy(name, capacity, OverflowPolicy::default())
406 }
407
408 pub fn queue_with_policy(
412 mut self,
413 name: impl Into<String>,
414 capacity: usize,
415 policy: OverflowPolicy,
416 ) -> Self {
417 let name: Arc<str> = name.into().into();
418 let id = self.context.graph.reserve_element_id();
419 self.planned.push(PlannedNode {
420 info: NodeInfo {
421 id,
422 element_type: ElementType::Queue,
423 name: name.clone(),
424 },
425 output_port: format!("{name}_src").into(),
426 input: InputContract::Any,
431 output: OutputContract::Passthrough,
432 });
433 self.elements.push(Box::new(QueueStage {
434 id,
435 name: name.to_string(),
436 capacity,
437 policy,
438 }));
439 self
440 }
441
442 pub fn to(self, mut terminal: Box<dyn Sink>) -> Result<DetachedBranch> {
447 if let Some(error) = self.error {
448 return Err(error.into());
449 }
450 *terminal.pp_log_mut() = element_pp_log(
451 terminal.element_type(),
452 &terminal.name(),
453 Some(&self.context.pipeline_id),
454 );
455 terminal.attach_context(&self.context);
456 let terminal_info = NodeInfo {
457 id: terminal
458 .graph_id()
459 .unwrap_or_else(|| self.context.graph.reserve_element_id()),
460 element_type: terminal.element_type(),
461 name: terminal.name(),
462 };
463 let terminal_id = terminal_info.id;
464
465 let mut contracts: HashMap<_, _> = self
467 .planned
468 .iter()
469 .map(|node| {
470 (
471 node.info.id,
472 PortContracts {
473 input: node.input,
474 output: node.output,
475 },
476 )
477 })
478 .collect();
479 contracts.insert(
480 terminal_id,
481 PortContracts {
482 input: terminal.input_contract(),
483 output: OutputContract::Unknown,
484 },
485 );
486
487 let mut nodes: Vec<_> = self.planned.iter().map(|node| node.info.clone()).collect();
488 nodes.push(terminal_info);
489 let edges = nodes
490 .windows(2)
491 .enumerate()
492 .map(|(index, pair)| PlannedEdge {
493 from: PortRef {
494 element: pair[0].id,
495 port: self.planned[index].output_port.clone(),
496 },
497 to: PortRef {
498 element: pair[1].id,
499 port: "sink".into(),
500 },
501 })
502 .collect();
503 let root_id = nodes.first().expect("terminal always supplies one node").id;
504 let terminal: Box<dyn Sink> = Box::new(TerminalTracer {
505 bus: self.context.bus.for_element(terminal_id),
506 id: terminal_id,
507 inner: terminal,
508 paused: false,
509 preroll: None,
510 });
511 let plan = BranchPlan {
512 nodes,
513 edges,
514 contracts,
515 root: root_id,
516 };
517 plan.resolve(None)?;
522
523 let root = self
524 .elements
525 .into_iter()
526 .rev()
527 .try_fold(terminal, |downstream, stage| {
528 stage.wrap(downstream, &self.context)
529 })?;
530 Ok(DetachedBranch { root, plan })
531 }
532
533 pub fn to_branch(self, downstream: DetachedBranch) -> Result<DetachedBranch> {
554 if let Some(error) = self.error {
555 return Err(error.into());
556 }
557 let DetachedBranch {
558 root: sink,
559 mut plan,
560 } = downstream;
561
562 let mut nodes: Vec<_> = self.planned.iter().map(|node| node.info.clone()).collect();
565 let mut edges = Vec::with_capacity(self.planned.len() + plan.edges.len());
566 for (index, node) in self.planned.iter().enumerate() {
567 let consumer = self
570 .planned
571 .get(index + 1)
572 .map_or(plan.root, |next| next.info.id);
573 edges.push(PlannedEdge {
574 from: PortRef {
575 element: node.info.id,
576 port: node.output_port.clone(),
577 },
578 to: PortRef {
579 element: consumer,
580 port: "sink".into(),
581 },
582 });
583 plan.contracts.insert(
584 node.info.id,
585 PortContracts {
586 input: node.input,
587 output: node.output,
588 },
589 );
590 }
591 let root_id = nodes.first().map_or(plan.root, |node| node.id);
594 edges.append(&mut plan.edges);
595 nodes.append(&mut plan.nodes);
596 let plan = BranchPlan {
597 nodes,
598 edges,
599 contracts: plan.contracts,
600 root: root_id,
601 };
602 plan.resolve(None)?;
605
606 let root = self
607 .elements
608 .into_iter()
609 .rev()
610 .try_fold(sink, |downstream, stage| {
611 stage.wrap(downstream, &self.context)
612 })?;
613 Ok(DetachedBranch { root, plan })
614 }
615
616 pub fn build(self, terminal: Box<dyn Sink>) -> Result<DetachedBranch> {
619 self.to(terminal)
620 }
621}
622
623impl Context {
624 pub fn branch(self: &Arc<Self>) -> ChainBuilder {
629 ChainBuilder::new(self.clone())
630 }
631
632 pub fn attach<S: Source>(
641 &self,
642 source: &mut S,
643 pad_index: usize,
644 branch: DetachedBranch,
645 ) -> Result<BranchId> {
646 let pads = source.src_pads();
647 let pad_count = pads.len();
648 let pad = pads.get_mut(pad_index).ok_or(GraphError::PadOutOfRange {
649 index: pad_index,
650 pad_count,
651 })?;
652 self.attach_pad(pad, branch)
653 }
654
655 pub(crate) fn attach_pad(&self, pad: &mut SrcPad, branch: DetachedBranch) -> Result<BranchId> {
656 let _operation = match self.operation.try_lock() {
657 Ok(guard) => guard,
658 Err(std::sync::TryLockError::Poisoned(poisoned)) => poisoned.into_inner(),
659 Err(std::sync::TryLockError::WouldBlock) => {
660 return Err(GraphError::TimelineOperationInProgress.into());
661 }
662 };
663 if pad.is_linked() {
664 return Err(GraphError::PadAlreadyLinked(pad.name().to_owned()).into());
665 }
666 let from_port: Arc<str> = pad.name().into();
667 let incoming = Incoming::Known(incoming_from(pad));
675 let DetachedBranch { root, plan } = branch;
676 Ok(self
677 .graph
678 .attach_with(self.source_id, from_port, incoming, plan, |_| {
679 pad.link(root);
680 Ok(())
681 })?)
682 }
683}
684
685pub(crate) fn incoming_from(pad: &SrcPad) -> Option<ResolvedFlow> {
693 match pad.contract() {
694 OutputContract::Fixed(contract) => Some(ResolvedFlow {
695 producer: pad.name().into(),
696 contract,
697 }),
698 OutputContract::Passthrough | OutputContract::Unknown => None,
699 }
700}
701
702#[cfg(test)]
703mod tests {
704 use std::sync::{
705 Arc,
706 atomic::{AtomicUsize, Ordering},
707 };
708
709 use super::*;
710 use crate::{control::PrerollContext, element::element_pp_log};
711
712 struct CountingTerminal {
713 count: Arc<AtomicUsize>,
714 pp_log: PpLog,
715 }
716
717 impl Element for CountingTerminal {
718 fn name(&self) -> Arc<str> {
719 "terminal".into()
720 }
721
722 fn element_type(&self) -> ElementType {
723 ElementType::Other
724 }
725
726 fn pp_log(&self) -> &PpLog {
727 &self.pp_log
728 }
729
730 fn pp_log_mut(&mut self) -> &mut PpLog {
731 &mut self.pp_log
732 }
733 }
734
735 impl Sink for CountingTerminal {
736 fn consume(&mut self, _buf: MediaBuffer) -> Result<()> {
737 self.count.fetch_add(1, Ordering::SeqCst);
738 Ok(())
739 }
740
741 fn control(&mut self, _msg: ControlMsg) -> Result<()> {
742 Ok(())
743 }
744 }
745
746 fn terminal(id: ElementId, count: Arc<AtomicUsize>) -> TerminalTracer {
747 let (bus, _rx) = Bus::new();
748 TerminalTracer {
749 bus: bus.for_element(id),
750 id,
751 inner: Box::new(CountingTerminal {
752 count,
753 pp_log: element_pp_log(ElementType::Other, "terminal", None),
754 }),
755 paused: false,
756 preroll: None,
757 }
758 }
759
760 #[test]
765 fn terminal_readiness_closes_on_its_own_sample_not_the_whole_preroll() {
766 let first = ElementId::for_test(1);
767 let second = ElementId::for_test(2);
768 let context = Arc::new(PrerollContext::new([first, second]));
769 let mut first_terminal = terminal(first, Arc::new(AtomicUsize::new(0)));
770 let mut second_terminal = terminal(second, Arc::new(AtomicUsize::new(0)));
771
772 first_terminal
773 .control(ControlMsg::Pause)
774 .expect("pause first terminal");
775 assert!(!first_terminal.ready_consume());
776 first_terminal
777 .control(ControlMsg::Preroll(Arc::clone(&context)))
778 .expect("preroll first terminal");
779 second_terminal
780 .control(ControlMsg::Preroll(Arc::clone(&context)))
781 .expect("preroll second terminal");
782
783 first_terminal
784 .consume(MediaBuffer::Packet(Arc::new(ffmpeg_next::Packet::empty())))
785 .expect("consume first terminal sample");
786 assert!(
787 !first_terminal.ready_consume(),
788 "the first terminal stops as soon as it has its own sample"
789 );
790 assert!(
791 second_terminal.ready_consume(),
792 "the second is still owed one"
793 );
794 assert!(!context.is_complete());
795
796 second_terminal
797 .consume(MediaBuffer::Packet(Arc::new(ffmpeg_next::Packet::empty())))
798 .expect("consume second terminal sample");
799 assert!(context.is_complete());
800 assert!(!first_terminal.ready_consume());
801 assert!(!second_terminal.ready_consume());
802
803 first_terminal
804 .control(ControlMsg::Resume)
805 .expect("resume first terminal");
806 assert!(first_terminal.ready_consume());
807 }
808}