media_pp/elements/source/
app_source.rs1use std::{sync::Arc, time::Duration};
2
3use crate::pp_log::{PpLog, pp_info};
4use crossbeam_channel::{Receiver, Sender, TrySendError, bounded, select};
5use thiserror::Error as ThisError;
6
7use crate::{
8 buffer::MediaBuffer,
9 bus::{Bus, BusEvent},
10 control::{ControlMsg, ControlReceiver, apply_one, drain_control, wait_out_pause},
11 element::{Element, ElementType, Source, SourceElement, element_pp_log},
12 error::Result,
13 pad::SrcPad,
14};
15
16#[derive(Debug, ThisError)]
19pub enum AppSourceError {
20 #[error("AppSource has already ended (its Pipeline finished, or Eos was already pushed)")]
21 Closed,
22}
23
24pub struct AppSource {
47 pp_log: PpLog,
48 name: Arc<str>,
49 pad: SrcPad,
50 data_rx: Receiver<MediaBuffer>,
51}
52
53#[derive(Clone)]
57pub struct AppSourceHandle {
58 name: Arc<str>,
59 data_tx: Sender<MediaBuffer>,
60}
61
62impl AppSource {
63 pub fn new(name: impl Into<String>, capacity: usize) -> (Self, AppSourceHandle) {
67 let name: Arc<str> = name.into().into();
68 let pp_log = element_pp_log(ElementType::AppSource, &name, None);
69 pp_info!(pp_log: &pp_log, "created: capacity={capacity}");
70 let pad = SrcPad::new(format!("{name}_src"));
71 let (data_tx, data_rx) = bounded(capacity);
72 (
73 Self {
74 name: name.clone(),
75 pp_log,
76 pad,
77 data_rx,
78 },
79 AppSourceHandle { name, data_tx },
80 )
81 }
82}
83
84impl AppSourceHandle {
85 pub fn name(&self) -> Arc<str> {
86 self.name.clone()
87 }
88
89 pub fn push(&self, buf: MediaBuffer) -> Result<()> {
92 self.data_tx
93 .send(buf)
94 .map_err(|_| AppSourceError::Closed.into())
95 }
96
97 pub fn try_push(&self, buf: MediaBuffer) -> Result<bool> {
103 match self.data_tx.try_send(buf) {
104 Ok(()) => Ok(true),
105 Err(TrySendError::Full(_)) => Ok(false),
106 Err(TrySendError::Disconnected(_)) => Err(AppSourceError::Closed.into()),
107 }
108 }
109}
110
111impl Element for AppSource {
112 fn name(&self) -> Arc<str> {
113 self.name.clone()
114 }
115
116 fn element_type(&self) -> ElementType {
117 ElementType::AppSource
118 }
119
120 fn pp_log(&self) -> &PpLog {
121 &self.pp_log
122 }
123
124 fn pp_log_mut(&mut self) -> &mut PpLog {
125 &mut self.pp_log
126 }
127}
128
129impl Source for AppSource {
130 fn src_pads(&mut self) -> &mut [SrcPad] {
131 std::slice::from_mut(&mut self.pad)
132 }
133}
134
135impl SourceElement for AppSource {
136 fn run(&mut self, control: &ControlReceiver, bus: &Bus) -> Result<()> {
137 pp_info!(self, "started");
138 loop {
139 if drain_control(control, self, bus)?.stopped {
145 pp_info!(self, "stopped");
146 return Ok(());
147 }
148
149 select! {
150 recv(control.rx) -> req => {
151 match req {
152 Ok(req) => {
153 if apply_one(self, bus, req.msg, &req.ack)? {
154 pp_info!(self, "stopped");
155 return Ok(());
156 }
157 if req.msg == ControlMsg::Pause
158 && wait_out_pause(control, self, bus)?
159 {
160 pp_info!(self, "stopped");
161 return Ok(());
162 }
163 }
164 Err(_) => {
166 pp_info!(self, "run: control channel gone, ending");
167 return Ok(());
168 }
169 }
170 }
171 recv(self.data_rx) -> buf => {
172 match buf {
173 Ok(buf) if buf.is_eos() => {
174 pp_info!(self, "event=eos phase=source_received");
175 break;
176 }
177 Ok(buf) => {
178 if let Err(error) = self.pad.push(buf) {
179 bus.post(
180 &self.pp_log,
181 BusEvent::Error {
182 element_type: ElementType::AppSource,
183 name: self.name.clone(),
184 error,
185 },
186 );
187 }
188 }
189 Err(_) => {
191 pp_info!(self, "run: every AppSourceHandle dropped, ending");
192 break;
193 }
194 }
195 }
196 }
197 }
198 self.pad.push_eos(&self.pp_log)
199 }
200
201 fn seek(&mut self, target: Duration) -> Result<Duration> {
207 Ok(target)
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use std::{
214 sync::atomic::{AtomicUsize, Ordering},
215 thread,
216 };
217
218 use super::*;
219 use crate::pipeline::Pipeline;
220
221 struct CountingSink {
222 pp_log: PpLog,
223 count: Arc<AtomicUsize>,
224 }
225
226 impl Element for CountingSink {
227 fn name(&self) -> Arc<str> {
228 "counter".into()
229 }
230
231 fn element_type(&self) -> ElementType {
232 ElementType::Other
233 }
234
235 fn pp_log(&self) -> &PpLog {
236 &self.pp_log
237 }
238
239 fn pp_log_mut(&mut self) -> &mut PpLog {
240 &mut self.pp_log
241 }
242 }
243
244 impl crate::element::Sink for CountingSink {
245 fn consume(&mut self, buf: MediaBuffer) -> Result<()> {
246 if !buf.is_eos() {
247 self.count.fetch_add(1, Ordering::SeqCst);
248 }
249 Ok(())
250 }
251
252 fn control(&mut self, _msg: ControlMsg) -> Result<()> {
253 Ok(())
254 }
255 }
256
257 fn packet() -> MediaBuffer {
258 MediaBuffer::Packet(Arc::new(ffmpeg_next::Packet::empty()))
259 }
260
261 fn wire(source: AppSource, count: Arc<AtomicUsize>) -> Arc<Pipeline> {
262 let sink = CountingSink {
263 count,
264 pp_log: element_pp_log(ElementType::Other, "counter", None),
265 };
266 Pipeline::new("test", source, |source, ctx| {
267 let branch = ctx.branch().to(Box::new(sink))?;
268 ctx.attach(source, 0, branch)?;
269 Ok(())
270 })
271 .expect("test pipeline wiring must succeed")
272 }
273
274 #[test]
275 fn pushed_buffers_reach_downstream_then_eos_ends_it() {
276 let (source, handle) = AppSource::new("app-source", 4);
277 let count = Arc::new(AtomicUsize::new(0));
278 let pipeline = wire(source, count.clone());
279 pipeline.run();
280
281 for _ in 0..5 {
282 handle.push(packet()).unwrap();
283 }
284 handle.push(MediaBuffer::Eos).unwrap();
285
286 let events: Vec<_> = pipeline.bus().iter().collect();
287 assert!(
288 !events.iter().any(|e| matches!(e, BusEvent::Error { .. })),
289 "unexpected error event(s): {events:?}"
290 );
291 assert!(events.iter().any(|e| matches!(e, BusEvent::Eos { .. })));
292 assert_eq!(count.load(Ordering::SeqCst), 5);
293 }
294
295 #[test]
296 fn dropping_every_handle_without_eos_still_ends_cleanly() {
297 let (source, handle) = AppSource::new("app-source", 4);
298 let count = Arc::new(AtomicUsize::new(0));
299 let pipeline = wire(source, count.clone());
300 pipeline.run();
301
302 handle.push(packet()).unwrap();
303 handle.push(packet()).unwrap();
304 drop(handle); let events: Vec<_> = pipeline.bus().iter().collect();
307 assert!(
308 !events.iter().any(|e| matches!(e, BusEvent::Error { .. })),
309 "unexpected error event(s): {events:?}"
310 );
311 assert!(events.iter().any(|e| matches!(e, BusEvent::Eos { .. })));
312 assert_eq!(count.load(Ordering::SeqCst), 2);
313 }
314
315 #[test]
321 fn stop_ends_promptly_even_with_no_producer() {
322 let (source, _handle) = AppSource::new("app-source", 4);
323 let count = Arc::new(AtomicUsize::new(0));
324 let pipeline = wire(source, count.clone());
325 pipeline.run();
326
327 thread::sleep(Duration::from_millis(50));
331 pipeline.stop();
332
333 let events: Vec<_> = pipeline.bus().iter().collect();
334 assert!(
335 !events.iter().any(|e| matches!(e, BusEvent::Error { .. })),
336 "unexpected error event(s): {events:?}"
337 );
338 assert_eq!(count.load(Ordering::SeqCst), 0);
339 }
340}