1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
use super::*;
use crate::{
    model::payload::{ClientConnect, ClientDisconnect, Speaking},
    tracks::{TrackHandle, TrackState},
};
use discortp::{rtcp::Rtcp, rtp::Rtp};

/// Information about which tracks or data fired an event.
///
/// [`Track`] events may be local or global, and have no tracks
/// if fired on the global context via [`Driver::add_global_event`].
///
/// [`Track`]: crate::tracks::Track
/// [`Driver::add_global_event`]: crate::driver::Driver::add_global_event
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum EventContext<'a> {
    /// Track event context, passed to events created via [`TrackHandle::add_event`],
    /// [`EventStore::add_event`], or relevant global events.
    ///
    /// [`EventStore::add_event`]: EventStore::add_event
    /// [`TrackHandle::add_event`]: TrackHandle::add_event
    Track(&'a [(&'a TrackState, &'a TrackHandle)]),
    /// Speaking state update, typically describing how another voice
    /// user is transmitting audio data. Clients must send at least one such
    /// packet to allow SSRC/UserID matching.
    SpeakingStateUpdate(Speaking),
    /// Speaking state transition, describing whether a given source has started/stopped
    /// transmitting. This fires in response to a silent burst, or the first packet
    /// breaking such a burst.
    SpeakingUpdate {
        /// Synchronisation Source of the user who has begun speaking.
        ///
        /// This must be combined with another event class to map this back to
        /// its original UserId.
        ssrc: u32,
        /// Whether this user is currently speaking.
        speaking: bool,
    },
    /// Opus audio packet, received from another stream (detailed in `packet`).
    /// `payload_offset` contains the true payload location within the raw packet's `payload()`,
    /// if extensions or raw packet data are required.
    /// if `audio.len() == 0`, then this packet arrived out-of-order.
    VoicePacket {
        /// Decoded audio from this packet.
        audio: &'a Option<Vec<i16>>,
        /// Raw RTP packet data.
        ///
        /// Includes the SSRC (i.e., sender) of this packet.
        packet: &'a Rtp,
        /// Byte index into the packet body (after headers) for where the payload begins.
        payload_offset: usize,
        /// Number of bytes at the end of the packet to discard.
        payload_end_pad: usize,
    },
    /// Telemetry/statistics packet, received from another stream (detailed in `packet`).
    /// `payload_offset` contains the true payload location within the raw packet's `payload()`,
    /// to allow manual decoding of `Rtcp` packet bodies.
    RtcpPacket {
        /// Raw RTCP packet data.
        packet: &'a Rtcp,
        /// Byte index into the packet body (after headers) for where the payload begins.
        payload_offset: usize,
        /// Number of bytes at the end of the packet to discard.
        payload_end_pad: usize,
    },
    /// Fired whenever a client connects to a call for the first time, allowing SSRC/UserID
    /// matching.
    ClientConnect(ClientConnect),
    /// Fired whenever a client disconnects.
    ClientDisconnect(ClientDisconnect),
}

#[derive(Clone, Debug)]
pub enum CoreContext {
    SpeakingStateUpdate(Speaking),
    SpeakingUpdate {
        ssrc: u32,
        speaking: bool,
    },
    VoicePacket {
        audio: Option<Vec<i16>>,
        packet: Rtp,
        payload_offset: usize,
        payload_end_pad: usize,
    },
    RtcpPacket {
        packet: Rtcp,
        payload_offset: usize,
        payload_end_pad: usize,
    },
    ClientConnect(ClientConnect),
    ClientDisconnect(ClientDisconnect),
}

impl<'a> CoreContext {
    pub(crate) fn to_user_context(&'a self) -> EventContext<'a> {
        use CoreContext::*;

        match self {
            SpeakingStateUpdate(evt) => EventContext::SpeakingStateUpdate(*evt),
            SpeakingUpdate { ssrc, speaking } => EventContext::SpeakingUpdate {
                ssrc: *ssrc,
                speaking: *speaking,
            },
            VoicePacket {
                audio,
                packet,
                payload_offset,
                payload_end_pad,
            } => EventContext::VoicePacket {
                audio,
                packet,
                payload_offset: *payload_offset,
                payload_end_pad: *payload_end_pad,
            },
            RtcpPacket {
                packet,
                payload_offset,
                payload_end_pad,
            } => EventContext::RtcpPacket {
                packet,
                payload_offset: *payload_offset,
                payload_end_pad: *payload_end_pad,
            },
            ClientConnect(evt) => EventContext::ClientConnect(*evt),
            ClientDisconnect(evt) => EventContext::ClientDisconnect(*evt),
        }
    }
}

impl EventContext<'_> {
    /// Retreive the event class for an event (i.e., when matching)
    /// an event against the registered listeners.
    pub fn to_core_event(&self) -> Option<CoreEvent> {
        use EventContext::*;

        match self {
            SpeakingStateUpdate { .. } => Some(CoreEvent::SpeakingStateUpdate),
            SpeakingUpdate { .. } => Some(CoreEvent::SpeakingUpdate),
            VoicePacket { .. } => Some(CoreEvent::VoicePacket),
            RtcpPacket { .. } => Some(CoreEvent::RtcpPacket),
            ClientConnect { .. } => Some(CoreEvent::ClientConnect),
            ClientDisconnect { .. } => Some(CoreEvent::ClientDisconnect),
            _ => None,
        }
    }
}