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::{
11 ControlMsg, ControlReceiver, RequestKind, apply_finish, apply_one, drain_control,
12 wait_out_pause,
13 },
14 element::{Element, ElementType, Source, SourceElement, element_pp_log},
15 error::Result,
16 pad::SrcPad,
17};
18
19#[derive(Debug, ThisError)]
22pub enum AppSourceError {
23 #[error("AppSource has already ended (its Pipeline finished, or Eos was already pushed)")]
25 Closed,
26}
27
28pub struct AppSource {
51 pp_log: PpLog,
52 name: Arc<str>,
53 pad: SrcPad,
54 data_rx: Receiver<MediaBuffer>,
55}
56
57#[derive(Clone)]
61pub struct AppSourceHandle {
62 name: Arc<str>,
63 data_tx: Sender<MediaBuffer>,
64}
65
66impl AppSource {
67 pub fn new(name: impl Into<String>, capacity: usize) -> (Self, AppSourceHandle) {
71 let name: Arc<str> = name.into().into();
72 let pp_log = element_pp_log(ElementType::AppSource, &name, None);
73 pp_info!(pp_log: &pp_log, "created: capacity={capacity}");
74 let pad = SrcPad::new(format!("{name}_src"));
75 let (data_tx, data_rx) = bounded(capacity);
76 (
77 Self {
78 name: name.clone(),
79 pp_log,
80 pad,
81 data_rx,
82 },
83 AppSourceHandle { name, data_tx },
84 )
85 }
86}
87
88impl AppSourceHandle {
89 pub fn name(&self) -> Arc<str> {
91 self.name.clone()
92 }
93
94 pub fn push(&self, buf: MediaBuffer) -> Result<()> {
97 self.data_tx
98 .send(buf)
99 .map_err(|_| AppSourceError::Closed.into())
100 }
101
102 pub fn try_push(&self, buf: MediaBuffer) -> Result<bool> {
108 match self.data_tx.try_send(buf) {
109 Ok(()) => Ok(true),
110 Err(TrySendError::Full(_)) => Ok(false),
111 Err(TrySendError::Disconnected(_)) => Err(AppSourceError::Closed.into()),
112 }
113 }
114}
115
116impl Element for AppSource {
117 fn name(&self) -> Arc<str> {
118 self.name.clone()
119 }
120
121 fn element_type(&self) -> ElementType {
122 ElementType::AppSource
123 }
124
125 fn pp_log(&self) -> &PpLog {
126 &self.pp_log
127 }
128
129 fn pp_log_mut(&mut self) -> &mut PpLog {
130 &mut self.pp_log
131 }
132}
133
134impl Source for AppSource {
135 fn src_pads(&mut self) -> &mut [SrcPad] {
136 std::slice::from_mut(&mut self.pad)
137 }
138}
139
140impl SourceElement for AppSource {
141 fn is_live(&self) -> bool {
142 false
143 }
144
145 fn is_seekable(&self) -> bool {
146 false
147 }
148
149 fn run(&mut self, control: &ControlReceiver, bus: &Bus) -> Result<()> {
150 pp_info!(self, "started");
151 loop {
152 if drain_control(control, self, bus)?.stopped {
158 pp_info!(self, "stopped");
159 return Ok(());
160 }
161
162 select! {
163 recv(control.rx) -> req => {
164 match req {
165 Ok(req) => {
166 match req.kind {
167 RequestKind::Finish => {
168 apply_finish(self, bus, &req.ack);
169 pp_info!(self, "finished");
170 return Ok(());
171 }
172 RequestKind::Control(msg) => {
173 if apply_one(self, bus, &msg, &req.ack)? {
174 pp_info!(self, "stopped");
175 return Ok(());
176 }
177 if msg == ControlMsg::Pause
178 && wait_out_pause(control, self, bus)?
179 {
180 pp_info!(self, "stopped");
181 return Ok(());
182 }
183 }
184 }
185 }
186 Err(_) => {
188 pp_info!(self, "run: control channel gone, ending");
189 return Ok(());
190 }
191 }
192 }
193 recv(self.data_rx) -> buf => {
194 match buf {
195 Ok(buf) if buf.is_eos() => {
196 pp_info!(self, "event=eos phase=source_received");
197 break;
198 }
199 Ok(buf) => {
200 if let Err(error) = self.pad.push(buf) {
201 bus.post(
202 &self.pp_log,
203 BusEvent::Error {
204 element_type: ElementType::AppSource,
205 name: self.name.clone(),
206 error,
207 },
208 );
209 }
210 }
211 Err(_) => {
213 pp_info!(self, "run: every AppSourceHandle dropped, ending");
214 break;
215 }
216 }
217 }
218 }
219 }
220 self.pad.push_eos(&self.pp_log)
221 }
222
223 fn seek(&mut self, target: Duration) -> Result<Duration> {
229 Ok(target)
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 use std::{
236 sync::atomic::{AtomicUsize, Ordering},
237 thread,
238 };
239
240 use super::*;
241 use crate::pipeline::Pipeline;
242
243 struct CountingSink {
244 pp_log: PpLog,
245 count: Arc<AtomicUsize>,
246 }
247
248 impl Element for CountingSink {
249 fn name(&self) -> Arc<str> {
250 "counter".into()
251 }
252
253 fn element_type(&self) -> ElementType {
254 ElementType::Other
255 }
256
257 fn pp_log(&self) -> &PpLog {
258 &self.pp_log
259 }
260
261 fn pp_log_mut(&mut self) -> &mut PpLog {
262 &mut self.pp_log
263 }
264 }
265
266 impl crate::element::Sink for CountingSink {
267 fn consume(&mut self, buf: MediaBuffer) -> Result<()> {
268 if !buf.is_eos() {
269 self.count.fetch_add(1, Ordering::SeqCst);
270 }
271 Ok(())
272 }
273
274 fn control(&mut self, _msg: ControlMsg) -> Result<()> {
275 Ok(())
276 }
277 }
278
279 fn packet() -> MediaBuffer {
280 MediaBuffer::Packet(Arc::new(ffmpeg_next::Packet::empty()))
281 }
282
283 fn wire(source: AppSource, count: Arc<AtomicUsize>) -> Arc<Pipeline> {
284 let sink = CountingSink {
285 count,
286 pp_log: element_pp_log(ElementType::Other, "counter", None),
287 };
288 Pipeline::new("test", source, |source, ctx| {
289 let branch = ctx.branch().to(Box::new(sink))?;
290 ctx.attach(source, 0, branch)?;
291 Ok(())
292 })
293 .expect("test pipeline wiring must succeed")
294 }
295
296 #[test]
297 fn pushed_buffers_reach_downstream_then_eos_ends_it() {
298 let (source, handle) = AppSource::new("app-source", 4);
299 let count = Arc::new(AtomicUsize::new(0));
300 let pipeline = wire(source, count.clone());
301 pipeline.run().unwrap();
302
303 for _ in 0..5 {
304 handle.push(packet()).unwrap();
305 }
306 handle.push(MediaBuffer::Eos).unwrap();
307
308 let events: Vec<_> = pipeline.bus().iter().collect();
309 assert!(
310 !events.iter().any(|e| matches!(e, BusEvent::Error { .. })),
311 "unexpected error event(s): {events:?}"
312 );
313 assert!(events.iter().any(|e| matches!(e, BusEvent::Eos { .. })));
314 assert_eq!(count.load(Ordering::SeqCst), 5);
315 }
316
317 #[test]
318 fn dropping_every_handle_without_eos_still_ends_cleanly() {
319 let (source, handle) = AppSource::new("app-source", 4);
320 let count = Arc::new(AtomicUsize::new(0));
321 let pipeline = wire(source, count.clone());
322 pipeline.run().unwrap();
323
324 handle.push(packet()).unwrap();
325 handle.push(packet()).unwrap();
326 drop(handle); let events: Vec<_> = pipeline.bus().iter().collect();
329 assert!(
330 !events.iter().any(|e| matches!(e, BusEvent::Error { .. })),
331 "unexpected error event(s): {events:?}"
332 );
333 assert!(events.iter().any(|e| matches!(e, BusEvent::Eos { .. })));
334 assert_eq!(count.load(Ordering::SeqCst), 2);
335 }
336
337 #[test]
343 fn stop_ends_promptly_even_with_no_producer() {
344 let (source, _handle) = AppSource::new("app-source", 4);
345 let count = Arc::new(AtomicUsize::new(0));
346 let pipeline = wire(source, count.clone());
347 pipeline.run().unwrap();
348
349 thread::sleep(Duration::from_millis(50));
353 pipeline.stop();
354
355 let events: Vec<_> = pipeline.bus().iter().collect();
356 assert!(
357 !events.iter().any(|e| matches!(e, BusEvent::Error { .. })),
358 "unexpected error event(s): {events:?}"
359 );
360 assert_eq!(count.load(Ordering::SeqCst), 0);
361 }
362}