use std::time::Duration;
use crossbeam_channel::Sender;
use ffmpeg_next as ffmpeg;
use str0m::{
RtcError,
change::{SdpAnswer, SdpOffer},
format::Codec,
media::{Direction, MediaKind, Mid},
};
use thiserror::Error as ThisError;
use crate::buffer::MediaBuffer;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TrackId(pub(super) u64);
#[derive(Debug, ThisError)]
pub enum WebRtcError {
#[error("str0m error: {0}")]
Str0m(#[from] RtcError),
#[error("network error: {0}")]
Io(#[from] std::io::Error),
#[error(
"WebRtcTrackSink only accepts already-encoded Packet buffers \
(an encoder's output), got a {0}"
)]
UnsupportedBuffer(&'static str),
#[error("WebRTC packet has no PTS")]
MissingPacketPts,
#[error("WebRTC packet has a negative PTS: {0}")]
NegativePacketPts(i64),
#[error("WebRTC packet timestamp normalization overflows: value={value}, offset={offset}")]
PacketTimestampNormalizationOverflow {
value: i64,
offset: i64,
},
#[error("WebRTC packet has an invalid time base: {numerator}/{denominator}")]
InvalidPacketTimeBase {
numerator: i32,
denominator: i32,
},
#[error(
"WebRTC packet timestamp overflows MediaTime: pts={pts}, time_base={numerator}/{denominator}"
)]
PacketTimestampOverflow {
pts: u64,
numerator: i32,
denominator: i32,
},
#[error(
"track {0:?} has no outbound codec declaration; call \
WebRtcTrackSink::set_source_parameters with what feeds it, or \
set_codec when there are no parameters to hand"
)]
OutboundCodecNotDeclared(TrackId),
#[error("codec parameters describe {0:?}, which WebRTC does not carry")]
SourceCodecUnsupported(ffmpeg::codec::Id),
#[error(
"track {track_id:?} was given {codec:?} configuration that is not Annex-B parameter sets"
)]
ParameterSetsNotSupported {
track_id: TrackId,
codec: Codec,
},
#[error(
"track {0:?} sent a keyframe with no SPS/PPS and has none to prepend; \
call WebRtcTrackSink::set_source_parameters with the encoder's parameters"
)]
MissingParameterSets(TrackId),
#[error(
"codec {codec:?} is not negotiated for track {track_id:?}; negotiated codecs: {negotiated:?}"
)]
OutboundCodecNotNegotiated {
track_id: TrackId,
codec: Codec,
negotiated: Vec<Codec>,
},
#[error(
"track {0:?} was pushed a length-prefixed H.264 packet and has no avcC configuration; \
push an encoder's output, or call WebRtcTrackSink::set_source_parameters with the \
demuxer's stream parameters first"
)]
NotAnnexB(TrackId),
#[error("track {0:?} was pushed a malformed length-prefixed H.264 packet")]
MalformedLengthPrefixedPacket(TrackId),
#[error("timed out after {timeout:?} waiting for stream info on track {track_id:?}")]
StreamInfoTimeout {
track_id: TrackId,
timeout: Duration,
},
#[error("WebRTC codec {0:?} cannot be converted to FFmpeg codec parameters")]
UnsupportedCodecParameters(Codec),
#[error("H.264 SPS/PPS have not been received yet")]
H264ParameterSetsNotReceived,
#[error("received H.264 {0} is invalid")]
InvalidH264ParameterSet(&'static str),
#[error("codec configuration is too large: {size} bytes")]
CodecConfigurationTooLarge {
size: usize,
},
#[error("failed to allocate {size} bytes for FFmpeg codec parameters")]
CodecParametersAllocationFailed {
size: usize,
},
#[error("WebRTC codec {codec:?} has a clock rate FFmpeg cannot represent: {clock_rate}")]
InvalidStreamClockRate {
codec: Codec,
clock_rate: u32,
},
#[error("WebRTC codec {codec:?} has an invalid audio channel count: {channels}")]
InvalidStreamChannelCount {
codec: Codec,
channels: u8,
},
#[error("track {mid} renegotiated its direction from {from:?} to {to:?} after attaching")]
DirectionChanged {
mid: Mid,
from: Direction,
to: Direction,
},
#[error("WebRtcPeer's run() has already ended")]
Closed,
}
pub(super) enum Command {
AddTrack(TrackId, MediaKind, Direction, Codec),
Push(TrackId, Option<Codec>, MediaBuffer),
SetAnswer(SdpAnswer),
AcceptOffer(
SdpOffer,
Sender<std::result::Result<SdpAnswer, WebRtcError>>,
),
}
pub(super) enum TrackOutState {
ToOpen(MediaKind, Direction),
Negotiating(Mid),
Open(Mid),
}
impl TrackOutState {
pub(super) fn mid(&self) -> Option<Mid> {
match self {
TrackOutState::ToOpen(..) => None,
TrackOutState::Negotiating(mid) | TrackOutState::Open(mid) => Some(*mid),
}
}
}