///|
pub(all) suberror MediaError {
InvalidCodec(String)
InvalidTrack(String)
InvalidMedia(String)
EndOfStream
} derive(Debug, Eq)
///|
pub(all) enum MediaKind {
Audio
Video
} derive(Debug, Eq)
///|
pub(all) enum TransceiverDirection {
SendRecv
SendOnly
RecvOnly
Inactive
Stopped
} derive(Debug, Eq)
///|
pub(all) struct CodecCapability {
mime_type : String
clock_rate : UInt
channels : UInt16
fmtp : String
rtcp_feedback : Array[String]
} derive(Debug, Eq)
///|
pub fn CodecCapability::new(
mime_type~ : String,
clock_rate~ : UInt,
channels? : UInt16 = 1,
fmtp? : String = "",
rtcp_feedback? : Array[String] = [],
) -> CodecCapability raise MediaError {
let normalized = mime_type.to_lower()
if !(normalized.has_prefix("audio/") || normalized.has_prefix("video/")) ||
normalized.length() <= 6 ||
clock_rate == 0U ||
channels == 0 {
raise InvalidCodec("invalid media codec capability")
}
if fmtp.contains("\r") ||
fmtp.contains("\n") ||
rtcp_feedback.any(value => {
value.is_empty() || value.contains("\r") || value.contains("\n")
}) {
raise InvalidCodec("codec parameters contain invalid line breaks")
}
{
mime_type: normalized,
clock_rate,
channels,
fmtp,
rtcp_feedback: rtcp_feedback.copy(),
}
}
///|
pub fn CodecCapability::mime_type(self : CodecCapability) -> String {
self.mime_type
}
///|
pub fn CodecCapability::encoding_name(self : CodecCapability) -> String {
match self.mime_type.split_once("/") {
Some((_, encoding)) => encoding.to_owned()
None => self.mime_type
}
}
///|
pub fn CodecCapability::clock_rate(self : CodecCapability) -> UInt {
self.clock_rate
}
///|
pub fn CodecCapability::channels(self : CodecCapability) -> UInt16 {
self.channels
}
///|
pub fn CodecCapability::fmtp(self : CodecCapability) -> String {
self.fmtp
}
///|
pub fn CodecCapability::rtcp_feedback(self : CodecCapability) -> Array[String] {
self.rtcp_feedback.copy()
}
///|
pub struct MediaTrack {
id : String
stream_id : String
kind : MediaKind
codec : CodecCapability
} derive(Debug)
///|
pub fn MediaTrack::new(
id~ : String,
stream_id~ : String,
kind~ : MediaKind,
codec~ : CodecCapability,
) -> MediaTrack {
{ id, stream_id, kind, codec, }
}
///|
pub fn MediaTrack::id(self : MediaTrack) -> String {
self.id
}
///|
pub fn MediaTrack::stream_id(self : MediaTrack) -> String {
self.stream_id
}
///|
pub fn MediaTrack::kind(self : MediaTrack) -> MediaKind {
self.kind
}
///|
pub fn MediaTrack::codec(self : MediaTrack) -> CodecCapability {
self.codec
}
///|
pub(all) struct RtpEncodingParameters {
ssrc : UInt?
payload_type : Byte?
rtx_ssrc : UInt?
rtx_payload_type : Byte?
rid : String?
active : Bool
max_bitrate : UInt64?
scale_resolution_down_by : Double?
scalability_mode : String?
} derive(Debug, Eq)
///|
pub fn RtpEncodingParameters::new(
ssrc? : UInt,
payload_type? : Byte,
rtx_ssrc? : UInt,
rtx_payload_type? : Byte,
rid? : String,
active? : Bool = true,
max_bitrate? : UInt64,
scale_resolution_down_by? : Double,
scalability_mode? : String,
) -> RtpEncodingParameters raise MediaError {
if ssrc == Some(0U) || rtx_ssrc == Some(0U) {
raise InvalidMedia("RTP encoding SSRC must be nonzero")
}
match rid {
Some(value) if value.is_empty() ||
value.contains(" ") ||
value.contains("\r") ||
value.contains("\n") => raise InvalidMedia("RTP encoding RID is invalid")
_ => ()
}
match max_bitrate {
Some(0UL) =>
raise InvalidMedia("RTP encoding maximum bitrate must be positive")
_ => ()
}
match scale_resolution_down_by {
Some(value) if value < 1.0 =>
raise InvalidMedia("RTP encoding resolution scale must be at least one")
_ => ()
}
match scalability_mode {
Some(value) if value.is_empty() ||
value.contains(" ") ||
value.contains("\r") ||
value.contains("\n") =>
raise InvalidMedia("RTP encoding scalability mode is invalid")
_ => ()
}
{
ssrc,
payload_type,
rtx_ssrc,
rtx_payload_type,
rid,
active,
max_bitrate,
scale_resolution_down_by,
scalability_mode,
}
}
///|
pub struct RtpParameters {
codec : CodecCapability
encodings : Array[RtpEncodingParameters]
} derive(Debug, Eq)
///|
pub fn RtpParameters::new(
codec~ : CodecCapability,
encodings? : Array[RtpEncodingParameters] = [],
) -> RtpParameters raise MediaError {
let rids : Array[String] = []
let ssrcs : Array[UInt] = []
for encoding in encodings {
match encoding.rid {
Some(rid) => {
if rids.contains(rid) {
raise InvalidMedia("RTP encoding RID is duplicated")
}
rids.push(rid)
}
None => ()
}
match encoding.ssrc {
Some(ssrc) => {
if ssrcs.contains(ssrc) {
raise InvalidMedia("RTP encoding SSRC is duplicated")
}
ssrcs.push(ssrc)
}
None => ()
}
}
{ codec, encodings: encodings.copy(), }
}
///|
pub fn RtpParameters::codec(self : RtpParameters) -> CodecCapability {
self.codec
}
///|
pub fn RtpParameters::encodings(
self : RtpParameters,
) -> Array[RtpEncodingParameters] {
self.encodings.copy()
}
///|
pub struct RtpSender {
kind : MediaKind
mut track : MediaTrack?
mut parameters : RtpParameters?
} derive(Debug)
///|
pub fn RtpSender::new(kind~ : MediaKind) -> RtpSender {
{ kind, track: None, parameters: None, }
}
///|
pub fn RtpSender::kind(self : RtpSender) -> MediaKind {
self.kind
}
///|
pub fn RtpSender::track(self : RtpSender) -> MediaTrack? {
self.track
}
///|
pub fn RtpSender::parameters(self : RtpSender) -> RtpParameters? {
self.parameters
}
///|
pub fn RtpSender::replace_track(
self : RtpSender,
track : MediaTrack?,
) -> Unit raise MediaError {
match track {
Some(next) if next.kind() != self.kind =>
raise InvalidTrack("replacement track kind does not match sender")
_ => self.track = track
}
}
///|
pub fn RtpSender::set_parameters(
self : RtpSender,
parameters : RtpParameters?,
) -> Unit {
self.parameters = parameters
}
///|
pub struct RtpReceiver {
kind : MediaKind
mut track : MediaTrack?
mut parameters : RtpParameters?
} derive(Debug)
///|
pub fn RtpReceiver::new(kind~ : MediaKind) -> RtpReceiver {
{ kind, track: None, parameters: None, }
}
///|
pub fn RtpReceiver::kind(self : RtpReceiver) -> MediaKind {
self.kind
}
///|
pub fn RtpReceiver::track(self : RtpReceiver) -> MediaTrack? {
self.track
}
///|
pub fn RtpReceiver::parameters(self : RtpReceiver) -> RtpParameters? {
self.parameters
}
///|
pub fn RtpReceiver::set_track(
self : RtpReceiver,
track : MediaTrack?,
) -> Unit raise MediaError {
match track {
Some(next) if next.kind() != self.kind =>
raise InvalidTrack("received track kind does not match receiver")
_ => self.track = track
}
}
///|
pub fn RtpReceiver::set_parameters(
self : RtpReceiver,
parameters : RtpParameters?,
) -> Unit {
self.parameters = parameters
}
///|
pub struct RtpTransceiver {
mut mid : String?
kind : MediaKind
mut direction : TransceiverDirection
mut current_direction : TransceiverDirection?
sender : RtpSender
receiver : RtpReceiver
} derive(Debug)
///|
pub fn RtpTransceiver::new(
kind~ : MediaKind,
direction? : TransceiverDirection = SendRecv,
) -> RtpTransceiver {
{
mid: None,
kind,
direction,
current_direction: None,
sender: RtpSender::new(kind~),
receiver: RtpReceiver::new(kind~),
}
}
///|
pub fn RtpTransceiver::mid(self : RtpTransceiver) -> String? {
self.mid
}
///|
pub fn RtpTransceiver::kind(self : RtpTransceiver) -> MediaKind {
self.kind
}
///|
pub fn RtpTransceiver::direction(self : RtpTransceiver) -> TransceiverDirection {
self.direction
}
///|
pub fn RtpTransceiver::current_direction(
self : RtpTransceiver,
) -> TransceiverDirection? {
self.current_direction
}
///|
pub fn RtpTransceiver::sender(self : RtpTransceiver) -> RtpSender {
self.sender
}
///|
pub fn RtpTransceiver::receiver(self : RtpTransceiver) -> RtpReceiver {
self.receiver
}
///|
pub fn RtpTransceiver::set_direction(
self : RtpTransceiver,
direction : TransceiverDirection,
) -> Unit raise MediaError {
if self.direction == Stopped {
raise InvalidTrack("cannot change the direction of a stopped transceiver")
}
self.direction = direction
}
///|
pub fn RtpTransceiver::assign_mid(
self : RtpTransceiver,
mid : String,
) -> Unit raise MediaError {
if mid.is_empty() || mid.contains(" ") {
raise InvalidTrack("invalid RTP transceiver MID")
}
match self.mid {
Some(previous) if previous != mid =>
raise InvalidTrack("RTP transceiver MID cannot be reassigned")
_ => self.mid = Some(mid)
}
}
///|
pub fn RtpTransceiver::set_current_direction(
self : RtpTransceiver,
direction : TransceiverDirection,
) -> Unit {
self.current_direction = Some(direction)
}
///|
pub fn RtpTransceiver::stop(self : RtpTransceiver) -> Unit {
self.direction = Stopped
self.current_direction = Some(Stopped)
self.sender.track = None
self.sender.parameters = None
self.receiver.track = None
self.receiver.parameters = None
}