1mod builder;
10mod driver;
11
12pub use builder::EngineBuilder;
13
14use crate::auth::{Credentials, StreamAuthenticator};
15use crate::bus::{
16 Application, EventBus, PlaybackRegistry, PublishRegistry, StreamEvent, StreamHandle,
17};
18use crate::observe::Observer;
19use crate::{AppName, Result, StreamError, StreamId, StreamKey};
20use async_trait::async_trait;
21use dashmap::DashMap;
22use std::sync::atomic::{AtomicUsize, Ordering};
23use std::sync::Arc;
24use tokio::sync::broadcast;
25use tracing::info;
26
27#[derive(Debug, Clone)]
42pub struct AppSpec {
43 pub name: AppName,
45 pub broadcast_capacity: usize,
47 pub gop_capacity: usize,
50 pub gop_byte_capacity: usize,
53 pub subscriber_max_lag: Option<u64>,
56}
57
58impl AppSpec {
59 pub fn new(name: impl Into<AppName>) -> Self {
61 Self {
62 name: name.into(),
63 broadcast_capacity: 4096,
64 gop_capacity: 0,
65 gop_byte_capacity: 0,
66 subscriber_max_lag: None,
67 }
68 }
69
70 pub fn broadcast_capacity(mut self, n: usize) -> Self {
72 self.broadcast_capacity = n;
73 self
74 }
75
76 pub fn gop_cache(mut self, frames: usize) -> Self {
80 self.gop_capacity = frames;
81 self
82 }
83
84 pub fn gop_cache_bytes(mut self, bytes: usize) -> Self {
89 self.gop_byte_capacity = bytes;
90 self
91 }
92
93 pub fn subscriber_max_lag(mut self, frames: u64) -> Self {
100 self.subscriber_max_lag = Some(frames);
101 self
102 }
103}
104
105#[derive(Debug, Clone)]
108pub struct EngineConfig {
109 pub max_publishers: usize,
111 pub idle_timeout: Option<std::time::Duration>,
114}
115
116impl Default for EngineConfig {
117 fn default() -> Self {
118 Self {
119 max_publishers: 10_000,
120 idle_timeout: None,
121 }
122 }
123}
124
125pub struct Engine {
128 apps: DashMap<AppName, Arc<Application>>,
129 config: EngineConfig,
130 active_publishers: AtomicUsize,
132 observer: Arc<dyn Observer>,
133 authenticator: Arc<dyn StreamAuthenticator>,
134 pending_protocols: std::sync::Mutex<Vec<Box<dyn crate::inbound::InboundProtocol>>>,
138}
139
140impl Engine {
141 pub fn builder() -> EngineBuilder {
143 EngineBuilder::new()
144 }
145
146 pub(crate) fn from_parts(
148 config: EngineConfig,
149 specs: Vec<AppSpec>,
150 observer: Arc<dyn Observer>,
151 authenticator: Arc<dyn StreamAuthenticator>,
152 protocols: Vec<Box<dyn crate::inbound::InboundProtocol>>,
153 ) -> Arc<Self> {
154 let apps = DashMap::new();
155 for spec in specs {
156 let app = Application::new(
157 spec.name.clone(),
158 spec.broadcast_capacity,
159 spec.gop_capacity,
160 spec.gop_byte_capacity,
161 spec.subscriber_max_lag,
162 Arc::clone(&observer),
163 );
164 apps.insert(spec.name.clone(), app);
165 info!(app = %spec.name, "Application registered");
166 }
167 Arc::new(Self {
168 apps,
169 config,
170 active_publishers: AtomicUsize::new(0),
171 observer,
172 authenticator,
173 pending_protocols: std::sync::Mutex::new(protocols),
174 })
175 }
176
177 pub fn register_app(&self, spec: AppSpec) -> Result<()> {
186 use dashmap::mapref::entry::Entry;
187 match self.apps.entry(spec.name.clone()) {
188 Entry::Occupied(_) => Err(StreamError::AppAlreadyRegistered(spec.name.to_string())),
189 Entry::Vacant(slot) => {
190 let app = Application::new(
191 spec.name.clone(),
192 spec.broadcast_capacity,
193 spec.gop_capacity,
194 spec.gop_byte_capacity,
195 spec.subscriber_max_lag,
196 Arc::clone(&self.observer),
197 );
198 info!(app = %spec.name, "Application registered");
199 slot.insert(app);
200 Ok(())
201 }
202 }
203 }
204
205 pub fn list_apps(&self) -> Vec<AppName> {
207 self.apps.iter().map(|r| r.key().clone()).collect()
208 }
209
210 pub fn total_stream_count(&self) -> usize {
212 self.active_publishers.load(Ordering::Acquire)
213 }
214
215 fn get_app(&self, app: &AppName) -> Result<Arc<Application>> {
216 self.apps
217 .get(app)
218 .map(|r| r.clone())
219 .ok_or_else(|| StreamError::AppNotFound(app.to_string()))
220 }
221
222 pub async fn start_publish_authorized(
227 &self,
228 key: &StreamKey,
229 creds: &Credentials,
230 ) -> Result<StreamHandle> {
231 self.authenticator.authorize_publish(key, creds).await?;
232 self.start_publish(key).await
233 }
234
235 pub async fn open_playback_authorized(
238 &self,
239 key: &StreamKey,
240 creds: &Credentials,
241 ) -> Result<StreamHandle> {
242 self.authenticator.authorize_play(key, creds).await?;
243 self.get_stream(key)
244 }
245}
246
247#[async_trait]
248impl PublishRegistry for Engine {
249 async fn start_publish_checked(
253 &self,
254 key: &StreamKey,
255 creds: &Credentials,
256 ) -> Result<StreamHandle> {
257 self.start_publish_authorized(key, creds).await
258 }
259
260 #[tracing::instrument(skip(self), fields(app = %key.app, stream = %key.stream_id))]
261 async fn start_publish(&self, key: &StreamKey) -> Result<StreamHandle> {
262 let prev = self.active_publishers.fetch_add(1, Ordering::AcqRel);
265 if prev >= self.config.max_publishers {
266 self.active_publishers.fetch_sub(1, Ordering::AcqRel);
267 return Err(StreamError::PublisherLimitReached {
268 limit: self.config.max_publishers,
269 });
270 }
271
272 let application = match self.get_app(&key.app) {
273 Ok(app) => app,
274 Err(e) => {
275 self.active_publishers.fetch_sub(1, Ordering::AcqRel);
276 return Err(e);
277 }
278 };
279
280 match application.start_publish(key.stream_id.clone()).await {
281 Ok(handle) => Ok(handle),
282 Err(e) => {
283 self.active_publishers.fetch_sub(1, Ordering::AcqRel);
284 Err(e)
285 }
286 }
287 }
288
289 #[tracing::instrument(skip(self), fields(app = %key.app, stream = %key.stream_id))]
290 async fn end_publish(&self, key: &StreamKey) -> Result<()> {
291 let application = self.get_app(&key.app)?;
292 if application.end_publish(&key.stream_id).await? {
293 self.active_publishers.fetch_sub(1, Ordering::AcqRel);
294 }
295 Ok(())
296 }
297}
298
299impl PlaybackRegistry for Engine {
300 fn get_stream(&self, key: &StreamKey) -> Result<StreamHandle> {
301 let application = self.get_app(&key.app)?;
302 application
303 .get_stream(&key.stream_id)
304 .ok_or_else(|| StreamError::StreamNotFound {
305 app: key.app.to_string(),
306 stream_id: key.stream_id.to_string(),
307 })
308 }
309
310 fn list_streams(&self, app: &AppName) -> Result<Vec<StreamId>> {
311 Ok(self.get_app(app)?.active_streams())
312 }
313}
314
315impl EventBus for Engine {
316 fn subscribe_events(&self, app: &AppName) -> Result<broadcast::Receiver<StreamEvent>> {
317 Ok(self.get_app(app)?.subscribe_events())
318 }
319}
320
321impl std::fmt::Debug for Engine {
322 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
323 f.debug_struct("Engine")
324 .field("app_count", &self.apps.len())
325 .field("active_publishers", &self.total_stream_count())
326 .finish()
327 }
328}