Skip to main content

livekit_datatrack/local/
manager.rs

1// Copyright 2025 LiveKit, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use super::{
16    events::*,
17    pipeline::{Pipeline, PipelineOptions},
18    LocalTrackInner,
19};
20use crate::{
21    api::{DataTrackFrame, DataTrackInfo, DataTrackOptions, InternalError, PublishError},
22    e2ee::EncryptionProvider,
23    local::LocalDataTrack,
24    packet::{self, Handle},
25};
26use anyhow::{anyhow, Context};
27use futures_core::Stream;
28use std::{
29    collections::HashMap,
30    pin::Pin,
31    sync::Arc,
32    task::{Context as TaskContext, Poll},
33    time::Duration,
34};
35use tokio::sync::{mpsc, oneshot, watch};
36use tokio_stream::wrappers::ReceiverStream;
37
38/// Options for creating a [`Manager`].
39#[derive(Debug)]
40pub struct ManagerOptions {
41    /// Provider to use for encrypting outgoing frame payloads.
42    ///
43    /// If none, end-to-end encryption will be disabled for all published tracks.
44    ///
45    pub encryption_provider: Option<Arc<dyn EncryptionProvider>>,
46}
47
48/// An actor that manages the state of data tracks published by the local participant.
49pub struct Manager {
50    encryption_provider: Option<Arc<dyn EncryptionProvider>>,
51    event_in_tx: mpsc::Sender<InputEvent>,
52    event_in_rx: mpsc::Receiver<InputEvent>,
53    event_out_tx: mpsc::Sender<OutputEvent>,
54    handle_allocator: packet::HandleAllocator,
55    descriptors: HashMap<Handle, Descriptor>,
56}
57
58impl Manager {
59    /// Creates a new manager.
60    ///
61    /// Returns a tuple containing the following:
62    ///
63    /// - The manager itself to be spawned by the caller (see [`Manager::run`]).
64    /// - Channel for sending [`InputEvent`]s to be processed by the manager.
65    /// - Stream for receiving [`OutputEvent`]s produced by the manager.
66    ///
67    pub fn new(options: ManagerOptions) -> (Self, ManagerInput, ManagerOutput) {
68        let (event_in_tx, event_in_rx) = mpsc::channel(Self::EVENT_BUFFER_COUNT);
69        let (event_out_tx, event_out_rx) = mpsc::channel(Self::EVENT_BUFFER_COUNT);
70
71        let event_in = ManagerInput::new(event_in_tx.clone());
72        let manager = Manager {
73            encryption_provider: options.encryption_provider,
74            event_in_tx,
75            event_in_rx,
76            event_out_tx,
77            handle_allocator: packet::HandleAllocator::default(),
78            descriptors: HashMap::new(),
79        };
80
81        let event_out = ManagerOutput(ReceiverStream::new(event_out_rx));
82        (manager, event_in, event_out)
83    }
84
85    /// Run the manager task, consuming self.
86    ///
87    /// The manager will continue running until receiving [`InputEvent::Shutdown`].
88    ///
89    pub async fn run(mut self) {
90        log::debug!("Task started");
91        while let Some(event) = self.event_in_rx.recv().await {
92            log::debug!("Input event: {:?}", event);
93            match event {
94                InputEvent::PublishRequest(event) => self.on_publish_request(event).await,
95                InputEvent::PublishCancelled(event) => self.on_publish_cancelled(event).await,
96                InputEvent::QueryPublished(event) => self.on_query_published(event).await,
97                InputEvent::UnpublishRequest(event) => self.on_unpublish_request(event).await,
98                InputEvent::SfuPublishResponse(event) => self.on_sfu_publish_response(event).await,
99                InputEvent::SfuUnpublishResponse(event) => {
100                    self.on_sfu_unpublish_response(event).await
101                }
102                InputEvent::RepublishTracks => self.on_republish_tracks().await,
103                InputEvent::Shutdown => break,
104            }
105        }
106        self.shutdown().await;
107        log::debug!("Task ended");
108    }
109
110    async fn on_publish_request(&mut self, event: PublishRequest) {
111        if let Err(error) = crate::schema::validate_schema(
112            event.options.frame_encoding.as_ref(),
113            event.options.schema.as_ref().map(|id| id.encoding()),
114        ) {
115            _ = event.result_tx.send(Err(PublishError::InvalidSchema(error)));
116            return;
117        }
118
119        let Some(handle) = self.handle_allocator.get() else {
120            _ = event.result_tx.send(Err(PublishError::LimitReached));
121            return;
122        };
123
124        if self.descriptors.contains_key(&handle) {
125            _ = event.result_tx.send(Err(PublishError::Internal(
126                anyhow!("Descriptor for handle already exists").into(),
127            )));
128            return;
129        }
130
131        let (result_tx, result_rx) = oneshot::channel();
132        self.descriptors.insert(handle, Descriptor::Pending(result_tx));
133
134        livekit_runtime::spawn(Self::forward_publish_result(
135            handle,
136            result_rx,
137            event.result_tx,
138            self.event_in_tx.downgrade(),
139        ));
140
141        let event = SfuPublishRequest {
142            handle,
143            name: event.options.name,
144            uses_e2ee: self.encryption_provider.is_some(),
145            schema: event.options.schema,
146            frame_encoding: event.options.frame_encoding,
147        };
148        _ = self.event_out_tx.send(event.into()).await;
149    }
150
151    /// Task that awaits a pending publish result.
152    ///
153    /// Forwards the result to the user, or notifies the manager if the receiver
154    /// is dropped (e.g., due to timeout) so it can remove the pending publication.
155    ///
156    async fn forward_publish_result(
157        handle: Handle,
158        result_rx: oneshot::Receiver<Result<LocalDataTrack, PublishError>>,
159        mut forward_result_tx: oneshot::Sender<Result<LocalDataTrack, PublishError>>,
160        event_in_tx: mpsc::WeakSender<InputEvent>,
161    ) {
162        tokio::select! {
163            biased;
164            Ok(result) = result_rx => {
165                _ = forward_result_tx.send(result);
166            }
167            _ = forward_result_tx.closed() => {
168                let Some(tx) = event_in_tx.upgrade() else { return };
169                let event = PublishCancelled { handle };
170                _ = tx.try_send(event.into());
171            }
172        }
173    }
174
175    async fn on_publish_cancelled(&mut self, event: PublishCancelled) {
176        if self.descriptors.remove(&event.handle).is_none() {
177            log::warn!("No descriptor for {}", event.handle);
178        }
179    }
180
181    async fn on_query_published(&self, event: QueryPublished) {
182        let published_info: Vec<_> = self
183            .descriptors
184            .iter()
185            .filter_map(|descriptor| {
186                let (_, Descriptor::Active { info, .. }) = descriptor else {
187                    return None;
188                };
189                info.clone().into()
190            })
191            .collect();
192        _ = event.result_tx.send(published_info);
193    }
194
195    async fn on_unpublish_request(&mut self, event: UnpublishRequest) {
196        self.remove_descriptor(event.handle);
197
198        let event = SfuUnpublishRequest { handle: event.handle };
199        _ = self.event_out_tx.send(event.into()).await;
200    }
201
202    async fn on_sfu_publish_response(&mut self, event: SfuPublishResponse) {
203        let Some(descriptor) = self.descriptors.remove(&event.handle) else {
204            // This can occur if a publish request is cancelled before the SFU responds,
205            // send an unpublish request to ensure consistent SFU state.
206            _ = self.event_out_tx.send(SfuUnpublishRequest { handle: event.handle }.into()).await;
207            return;
208        };
209        match descriptor {
210            Descriptor::Pending(result_tx) => {
211                // SFU accepted initial publication request
212                if result_tx.is_closed() {
213                    return;
214                }
215                let result = event.result.map(|track_info| self.create_local_track(track_info));
216                _ = result_tx.send(result);
217                return;
218            }
219            Descriptor::Active { ref state_tx, ref info, .. } => {
220                if *state_tx.borrow() != PublishState::Republishing {
221                    log::warn!("Track {} already active", event.handle);
222                    return;
223                }
224                let Ok(updated_info) = event.result else {
225                    log::warn!("Republish failed for track {}", event.handle);
226                    return;
227                };
228
229                log::debug!("Track {} republished", event.handle);
230                {
231                    let mut sid = info.sid.write().unwrap();
232                    *sid = updated_info.sid();
233                }
234                _ = state_tx.send(PublishState::Published);
235                self.descriptors.insert(event.handle, descriptor);
236            }
237        }
238    }
239
240    fn create_local_track(&mut self, info: DataTrackInfo) -> LocalDataTrack {
241        let info = Arc::new(info);
242        let encryption_provider =
243            if info.uses_e2ee() { self.encryption_provider.as_ref().map(Arc::clone) } else { None };
244
245        let pipeline_opts = PipelineOptions { info: info.clone(), encryption_provider };
246        let pipeline = Pipeline::new(pipeline_opts);
247
248        let (frame_tx, frame_rx) = mpsc::channel(Self::FRAME_BUFFER_COUNT);
249        let (state_tx, state_rx) = watch::channel(PublishState::Published);
250
251        let track_task = TrackTask {
252            info: info.clone(),
253            pipeline,
254            state_rx,
255            frame_rx,
256            event_in_tx: self.event_in_tx.clone(),
257            event_out_tx: self.event_out_tx.clone(),
258        };
259        let task_handle = livekit_runtime::spawn(track_task.run());
260
261        self.descriptors.insert(
262            info.pub_handle,
263            Descriptor::Active { info: info.clone(), state_tx: state_tx.clone(), task_handle },
264        );
265
266        let inner = LocalTrackInner { frame_tx, state_tx };
267        LocalDataTrack::new(info, inner)
268    }
269
270    async fn on_sfu_unpublish_response(&mut self, event: SfuUnpublishResponse) {
271        self.remove_descriptor(event.handle);
272    }
273
274    fn remove_descriptor(&mut self, handle: Handle) {
275        let Some(descriptor) = self.descriptors.remove(&handle) else {
276            return;
277        };
278        let Descriptor::Active { state_tx, .. } = descriptor else {
279            return;
280        };
281        if *state_tx.borrow() != PublishState::Unpublished {
282            _ = state_tx.send(PublishState::Unpublished);
283        }
284    }
285
286    async fn on_republish_tracks(&mut self) {
287        let descriptors = std::mem::take(&mut self.descriptors);
288        for (handle, descriptor) in descriptors {
289            match descriptor {
290                Descriptor::Pending(result_tx) => {
291                    // TODO: support republish for pending publications
292                    _ = result_tx.send(Err(PublishError::Disconnected));
293                }
294                Descriptor::Active { ref info, ref state_tx, .. } => {
295                    let event = SfuPublishRequest {
296                        handle: info.pub_handle,
297                        name: info.name.clone(),
298                        uses_e2ee: info.uses_e2ee,
299                        schema: info.schema.clone(),
300                        frame_encoding: info.frame_encoding.clone(),
301                    };
302                    _ = state_tx.send(PublishState::Republishing);
303                    _ = self.event_out_tx.send(event.into()).await;
304                    self.descriptors.insert(handle, descriptor);
305                }
306            }
307        }
308    }
309
310    /// Performs cleanup before the task ends.
311    async fn shutdown(self) {
312        for (_, descriptor) in self.descriptors {
313            match descriptor {
314                Descriptor::Pending(result_tx) => {
315                    _ = result_tx.send(Err(PublishError::Disconnected))
316                }
317                Descriptor::Active { state_tx, task_handle, .. } => {
318                    _ = state_tx.send(PublishState::Unpublished);
319                    task_handle.await;
320                }
321            }
322        }
323    }
324
325    /// Maximum number of outgoing frames to buffer per track.
326    const FRAME_BUFFER_COUNT: usize = 16;
327
328    /// Maximum number of input and output events to buffer.
329    const EVENT_BUFFER_COUNT: usize = 16;
330}
331
332/// Task for an individual published data track.
333struct TrackTask {
334    info: Arc<DataTrackInfo>,
335    pipeline: Pipeline,
336    state_rx: watch::Receiver<PublishState>,
337    frame_rx: mpsc::Receiver<DataTrackFrame>,
338    event_in_tx: mpsc::Sender<InputEvent>,
339    event_out_tx: mpsc::Sender<OutputEvent>,
340}
341
342impl TrackTask {
343    async fn run(mut self) {
344        let sid = self.info.sid();
345        log::debug!("Track task started: sid={}", sid);
346
347        let mut state = *self.state_rx.borrow();
348        while state != PublishState::Unpublished {
349            tokio::select! {
350                _ = self.state_rx.changed() => {
351                    state = *self.state_rx.borrow();
352                }
353                Some(frame) = self.frame_rx.recv() => {
354                    if state == PublishState::Republishing {
355                        // Drop frames while republishing.
356                        continue;
357                    }
358                    self.process_and_send(frame);
359                }
360            }
361        }
362
363        let event = UnpublishRequest { handle: self.info.pub_handle };
364        _ = self.event_in_tx.send(event.into()).await;
365
366        log::debug!("Track task ended: sid={}", sid);
367    }
368
369    fn process_and_send(&mut self, frame: DataTrackFrame) {
370        let Ok(packets) = self
371            .pipeline
372            .process_frame(frame)
373            .inspect_err(|err| log::debug!("Process failed: {}", err))
374        else {
375            return;
376        };
377        let packets: Vec<_> = packets.into_iter().map(|packet| packet.serialize()).collect();
378        _ = self
379            .event_out_tx
380            .try_send(packets.into())
381            .inspect_err(|err| log::debug!("Cannot send packets to transport: {}", err));
382    }
383}
384
385#[derive(Debug)]
386enum Descriptor {
387    /// Publication is awaiting SFU response.
388    ///
389    /// The associated channel is used to send a result to the user,
390    /// either the local track or a publish error.
391    ///
392    Pending(oneshot::Sender<Result<LocalDataTrack, PublishError>>),
393    /// Publication is active.
394    ///
395    /// The associated channel is used to end the track task.
396    ///
397    Active {
398        info: Arc<DataTrackInfo>,
399        state_tx: watch::Sender<PublishState>,
400        task_handle: livekit_runtime::JoinHandle<()>,
401    },
402}
403
404#[derive(Debug, Clone, Copy, PartialEq, Eq)]
405pub(crate) enum PublishState {
406    /// Track is published.
407    Published,
408    /// Track is being republished.
409    Republishing,
410    /// Track is no longer published.
411    Unpublished,
412}
413
414/// Channel for sending [`InputEvent`]s to [`Manager`].
415#[derive(Debug, Clone)]
416pub struct ManagerInput {
417    event_in_tx: mpsc::Sender<InputEvent>,
418    _drop_guard: Arc<DropGuard>,
419}
420
421/// Stream of [`OutputEvent`]s produced by [`Manager`].
422#[derive(Debug)]
423pub struct ManagerOutput(ReceiverStream<OutputEvent>);
424
425impl Stream for ManagerOutput {
426    type Item = OutputEvent;
427
428    fn poll_next(mut self: Pin<&mut Self>, cx: &mut TaskContext<'_>) -> Poll<Option<Self::Item>> {
429        Pin::new(&mut self.0).poll_next(cx)
430    }
431}
432
433/// Guard that sends shutdown event when the last reference is dropped.
434#[derive(Debug)]
435struct DropGuard {
436    event_in_tx: mpsc::Sender<InputEvent>,
437}
438
439impl Drop for DropGuard {
440    fn drop(&mut self) {
441        _ = self.event_in_tx.try_send(InputEvent::Shutdown);
442    }
443}
444
445impl ManagerInput {
446    fn new(event_in_tx: mpsc::Sender<InputEvent>) -> Self {
447        Self { event_in_tx: event_in_tx.clone(), _drop_guard: DropGuard { event_in_tx }.into() }
448    }
449
450    /// Sends an input event to the manager's task to be processed.
451    pub fn send(&self, event: InputEvent) -> Result<(), InternalError> {
452        Ok(self.event_in_tx.try_send(event).context("Failed to handle input event")?)
453    }
454
455    /// Publishes a data track with given options.
456    pub async fn publish_track(
457        &self,
458        options: DataTrackOptions,
459    ) -> Result<LocalDataTrack, PublishError> {
460        let (result_tx, result_rx) = oneshot::channel();
461
462        let event = PublishRequest { options, result_tx };
463        self.event_in_tx.try_send(event.into()).map_err(|_| PublishError::Disconnected)?;
464
465        let track = tokio::time::timeout(Self::PUBLISH_TIMEOUT, result_rx)
466            .await
467            .map_err(|_| PublishError::Timeout)?
468            .map_err(|_| PublishError::Disconnected)??;
469
470        Ok(track)
471    }
472
473    /// Get information about all currently published tracks.
474    ///
475    /// This does not include publications that are still pending.
476    ///
477    pub async fn query_tracks(&self) -> Vec<Arc<DataTrackInfo>> {
478        let (result_tx, result_rx) = oneshot::channel();
479
480        let event = QueryPublished { result_tx };
481        if self.event_in_tx.send(event.into()).await.is_err() {
482            return vec![];
483        }
484
485        result_rx.await.unwrap_or_default()
486    }
487
488    /// How long to wait for before timeout.
489    const PUBLISH_TIMEOUT: Duration = Duration::from_secs(10);
490}
491
492#[cfg(test)]
493mod tests {
494    use super::*;
495    use crate::{
496        api::DataTrackSid,
497        e2ee::{EncryptedPayload, EncryptionError, EncryptionProvider},
498        packet::Packet,
499        utils::testing::expect_event,
500    };
501    use bytes::Bytes;
502    use fake::{Fake, Faker};
503    use futures_util::StreamExt;
504    use livekit_runtime::{sleep, timeout};
505    use std::sync::RwLock;
506
507    #[derive(Debug)]
508    struct PrefixingEncryptor;
509
510    impl EncryptionProvider for PrefixingEncryptor {
511        fn encrypt(&self, payload: Bytes) -> Result<EncryptedPayload, EncryptionError> {
512            let mut output = Vec::with_capacity(4 + payload.len());
513            output.extend_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF]);
514            output.extend_from_slice(&payload);
515            Ok(EncryptedPayload { payload: output.into(), iv: [0; 12], key_index: 0 })
516        }
517    }
518
519    #[tokio::test]
520    async fn test_task_shutdown() {
521        let options = ManagerOptions { encryption_provider: None };
522        let (manager, input, _) = Manager::new(options);
523
524        let join_handle = livekit_runtime::spawn(manager.run());
525        _ = input.send(InputEvent::Shutdown);
526
527        timeout(Duration::from_secs(1), join_handle).await.unwrap();
528    }
529
530    #[tokio::test]
531    async fn test_publish() {
532        let payload_size = 256;
533        let packet_count = 10;
534
535        let track_name: String = Faker.fake();
536        let track_sid: DataTrackSid = Faker.fake();
537        let pub_handle: Handle = Faker.fake();
538
539        let options = ManagerOptions { encryption_provider: None };
540        let (manager, input, mut output) = Manager::new(options);
541        livekit_runtime::spawn(manager.run());
542
543        let track_name_clone = track_name.clone();
544        let handle_events = async {
545            let mut packets_sent = 0;
546            while let Some(event) = output.next().await {
547                match event {
548                    OutputEvent::SfuPublishRequest(event) => {
549                        assert!(!event.uses_e2ee);
550                        assert_eq!(event.name, track_name_clone);
551
552                        // SFU accepts publication
553                        let info = DataTrackInfo {
554                            sid: RwLock::new(track_sid.clone()).into(),
555                            pub_handle,
556                            name: event.name,
557                            uses_e2ee: event.uses_e2ee,
558                            schema: None,
559                            frame_encoding: None,
560                        };
561                        let event = SfuPublishResponse { handle: event.handle, result: Ok(info) };
562                        _ = input.send(event.into());
563                    }
564                    OutputEvent::PacketsAvailable(packets) => {
565                        let packet = packets.into_iter().nth(0).unwrap();
566                        let payload = Packet::deserialize(packet).unwrap().payload;
567                        assert_eq!(payload.len(), payload_size);
568                        packets_sent += 1;
569                    }
570                    OutputEvent::SfuUnpublishRequest(event) => {
571                        assert_eq!(event.handle, pub_handle);
572                        assert_eq!(packets_sent, packet_count);
573                        break;
574                    }
575                }
576            }
577        };
578        let publish_track = async {
579            let track_options = DataTrackOptions::new(track_name.clone());
580            let track = input.publish_track(track_options).await.unwrap();
581            assert!(!track.info().uses_e2ee());
582            assert_eq!(track.info().name(), track_name);
583            assert_eq!(track.info().sid(), track_sid);
584
585            for _ in 0..packet_count {
586                track.try_push(vec![0xFA; payload_size].into()).unwrap();
587                sleep(Duration::from_millis(10)).await;
588            }
589            // Only reference to track dropped here (unpublish)
590        };
591        timeout(Duration::from_secs(1), async { tokio::join!(publish_track, handle_events) })
592            .await
593            .unwrap();
594    }
595
596    #[tokio::test]
597    async fn test_publish_sfu_error() {
598        let options = ManagerOptions { encryption_provider: None };
599        let (manager, input, mut output) = Manager::new(options);
600        livekit_runtime::spawn(manager.run());
601
602        let (result_tx, result_rx) = oneshot::channel();
603        let event = PublishRequest { options: DataTrackOptions::new("test"), result_tx };
604        input.send(event.into()).unwrap();
605
606        // SFU rejects publication
607        let event = expect_event!(output, OutputEvent::SfuPublishRequest);
608        let event =
609            SfuPublishResponse { handle: event.handle, result: Err(PublishError::LimitReached) };
610        input.send(event.into()).unwrap();
611
612        assert!(result_rx.await.unwrap().is_err());
613    }
614
615    #[tokio::test]
616    async fn test_publish_cancelled() {
617        let options = ManagerOptions { encryption_provider: None };
618        let (manager, input, mut output) = Manager::new(options);
619        livekit_runtime::spawn(manager.run());
620
621        let (result_tx, result_rx) = oneshot::channel();
622        let event = PublishRequest { options: DataTrackOptions::new("test"), result_tx };
623        input.send(event.into()).unwrap();
624
625        let event = expect_event!(output, OutputEvent::SfuPublishRequest);
626        let handle = event.handle;
627
628        // Caller drops receiver before SFU responds
629        drop(result_rx);
630        sleep(Duration::from_millis(50)).await;
631
632        // Late SFU response arrives after cancellation
633        let track_sid: DataTrackSid = Faker.fake();
634        let info = DataTrackInfo {
635            sid: RwLock::new(track_sid).into(),
636            pub_handle: handle,
637            name: "test".into(),
638            uses_e2ee: false,
639            schema: None,
640            frame_encoding: None,
641        };
642        let event = SfuPublishResponse { handle, result: Ok(info) };
643        input.send(event.into()).unwrap();
644
645        // Manager sends unpublish for the orphaned handle
646        let event = expect_event!(output, OutputEvent::SfuUnpublishRequest);
647        assert_eq!(event.handle, handle);
648    }
649
650    #[tokio::test]
651    async fn test_publish_with_e2ee() {
652        let options = ManagerOptions { encryption_provider: Some(Arc::new(PrefixingEncryptor)) };
653        let (manager, input, mut output) = Manager::new(options);
654        livekit_runtime::spawn(manager.run());
655
656        let (result_tx, result_rx) = oneshot::channel();
657        let event = PublishRequest { options: DataTrackOptions::new("secure"), result_tx };
658        input.send(event.into()).unwrap();
659
660        // SFU publish request should indicate e2ee
661        let event = expect_event!(output, OutputEvent::SfuPublishRequest);
662        assert!(event.uses_e2ee);
663
664        // SFU accepts publication with e2ee
665        let track_sid: DataTrackSid = Faker.fake();
666        let info = DataTrackInfo {
667            sid: RwLock::new(track_sid).into(),
668            pub_handle: event.handle,
669            name: "secure".into(),
670            uses_e2ee: true,
671            schema: None,
672            frame_encoding: None,
673        };
674        let event = SfuPublishResponse { handle: event.handle, result: Ok(info) };
675        input.send(event.into()).unwrap();
676
677        let track = result_rx.await.unwrap().unwrap();
678        assert!(track.info().uses_e2ee());
679
680        // Push a frame and verify encryption was applied
681        track.try_push(vec![1, 2, 3, 4, 5].into()).unwrap();
682
683        let packets = expect_event!(output, OutputEvent::PacketsAvailable);
684        let packet = Packet::deserialize(packets.into_iter().next().unwrap()).unwrap();
685        assert_eq!(&packet.payload[..4], &[0xDE, 0xAD, 0xBE, 0xEF]);
686        assert_eq!(&packet.payload[4..], &[1, 2, 3, 4, 5]);
687        assert!(packet.header.extensions.e2ee.is_some());
688    }
689
690    #[tokio::test]
691    async fn test_republish_tracks() {
692        let options = ManagerOptions { encryption_provider: None };
693        let (manager, input, mut output) = Manager::new(options);
694        livekit_runtime::spawn(manager.run());
695
696        // Publish a track through the full flow
697        let track_name: String = Faker.fake();
698        let track_sid: DataTrackSid = Faker.fake();
699
700        let (result_tx, result_rx) = oneshot::channel();
701        let event =
702            PublishRequest { options: DataTrackOptions::new(track_name.clone()), result_tx };
703        input.send(event.into()).unwrap();
704
705        let event = expect_event!(output, OutputEvent::SfuPublishRequest);
706        let handle = event.handle;
707
708        let info = DataTrackInfo {
709            sid: RwLock::new(track_sid.clone()).into(),
710            pub_handle: handle,
711            name: track_name.clone(),
712            uses_e2ee: false,
713            schema: None,
714            frame_encoding: None,
715        };
716        let event = SfuPublishResponse { handle, result: Ok(info) };
717        input.send(event.into()).unwrap();
718
719        let track = result_rx.await.unwrap().unwrap();
720        assert_eq!(track.info().sid(), track_sid);
721
722        // Simulate reconnect
723        input.send(InputEvent::RepublishTracks).unwrap();
724        sleep(Duration::from_millis(50)).await;
725
726        // try_push should fail while republishing
727        assert!(track.try_push(vec![0xFF].into()).is_err());
728
729        // SFU re-publishes with a new SID
730        let event = expect_event!(output, OutputEvent::SfuPublishRequest);
731        assert_eq!(event.handle, handle);
732        assert_eq!(event.name, track_name);
733
734        let new_sid: DataTrackSid = Faker.fake();
735        let info = DataTrackInfo {
736            sid: RwLock::new(new_sid.clone()).into(),
737            pub_handle: handle,
738            name: track_name.clone(),
739            uses_e2ee: false,
740            schema: None,
741            frame_encoding: None,
742        };
743        let event = SfuPublishResponse { handle, result: Ok(info) };
744        input.send(event.into()).unwrap();
745        sleep(Duration::from_millis(50)).await;
746
747        // SID updated in place, pushes succeed again
748        assert_eq!(track.info().sid(), new_sid);
749        assert!(track.try_push(vec![0xFF].into()).is_ok());
750    }
751
752    #[tokio::test]
753    async fn test_query_published() {
754        let options = ManagerOptions { encryption_provider: None };
755        let (manager, input, mut output) = Manager::new(options);
756        livekit_runtime::spawn(manager.run());
757
758        // Publish two tracks
759        let mut tracks = Vec::new();
760        for name in ["track_a", "track_b"] {
761            let (result_tx, result_rx) = oneshot::channel();
762            let event = PublishRequest { options: DataTrackOptions::new(name), result_tx };
763            input.send(event.into()).unwrap();
764
765            let event = expect_event!(output, OutputEvent::SfuPublishRequest);
766            let info = DataTrackInfo {
767                sid: RwLock::new(Faker.fake()).into(),
768                pub_handle: event.handle,
769                name: name.into(),
770                uses_e2ee: false,
771                schema: None,
772                frame_encoding: None,
773            };
774            let event = SfuPublishResponse { handle: event.handle, result: Ok(info) };
775            input.send(event.into()).unwrap();
776
777            tracks.push(result_rx.await.unwrap().unwrap());
778        }
779
780        let published = input.query_tracks().await;
781        assert_eq!(published.len(), 2);
782
783        let names: Vec<&str> = published.iter().map(|i| i.name()).collect();
784        assert!(names.contains(&"track_a"));
785        assert!(names.contains(&"track_b"));
786    }
787
788    #[tokio::test]
789    async fn test_shutdown_with_pending_and_active() {
790        let options = ManagerOptions { encryption_provider: None };
791        let (manager, input, mut output) = Manager::new(options);
792        livekit_runtime::spawn(manager.run());
793
794        // Pending publication (no SFU response sent)
795        let (result_tx, pending_rx) = oneshot::channel();
796        let event = PublishRequest { options: DataTrackOptions::new("pending"), result_tx };
797        input.send(event.into()).unwrap();
798
799        expect_event!(output, OutputEvent::SfuPublishRequest);
800
801        // Active publication (fully published)
802        let (result_tx, result_rx) = oneshot::channel();
803        let event = PublishRequest { options: DataTrackOptions::new("active"), result_tx };
804        input.send(event.into()).unwrap();
805
806        let event = expect_event!(output, OutputEvent::SfuPublishRequest);
807        let info = DataTrackInfo {
808            sid: RwLock::new(Faker.fake()).into(),
809            pub_handle: event.handle,
810            name: "active".into(),
811            uses_e2ee: false,
812            schema: None,
813            frame_encoding: None,
814        };
815        let event = SfuPublishResponse { handle: event.handle, result: Ok(info) };
816        input.send(event.into()).unwrap();
817
818        let active_track = result_rx.await.unwrap().unwrap();
819        assert!(active_track.is_published());
820
821        // Shutdown the manager
822        input.send(InputEvent::Shutdown).unwrap();
823        sleep(Duration::from_millis(50)).await;
824
825        // Pending publish receives disconnected error
826        let pending_result = pending_rx.await.unwrap();
827        assert!(pending_result.is_err());
828
829        // Active track is no longer published
830        assert!(!active_track.is_published());
831    }
832}