fmt all
This commit is contained in:
@@ -1,10 +1,10 @@
|
|||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use rubato::{audioadapter_buffers::direct::InterleavedSlice, Fft, Resampler};
|
use rubato::{Fft, Resampler, audioadapter_buffers::direct::InterleavedSlice};
|
||||||
use symphonia::core::{
|
use symphonia::core::{
|
||||||
audio::SampleBuffer,
|
audio::SampleBuffer,
|
||||||
codecs::{CodecParameters, Decoder, DecoderOptions, CODEC_TYPE_AAC},
|
codecs::{CODEC_TYPE_AAC, CodecParameters, Decoder, DecoderOptions},
|
||||||
formats::Packet,
|
formats::Packet,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,9 @@ impl Av1CodecParser {
|
|||||||
let has_size = (header >> 1) & 1 != 0;
|
let has_size = (header >> 1) & 1 != 0;
|
||||||
i += 1;
|
i += 1;
|
||||||
if has_extension {
|
if has_extension {
|
||||||
if i >= data.len() { return false; }
|
if i >= data.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
if obu_type == 1 {
|
if obu_type == 1 {
|
||||||
@@ -61,13 +63,19 @@ impl Av1CodecParser {
|
|||||||
let mut size: usize = 0;
|
let mut size: usize = 0;
|
||||||
let mut shift = 0;
|
let mut shift = 0;
|
||||||
loop {
|
loop {
|
||||||
if i >= data.len() { return false; }
|
if i >= data.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
let b = data[i] as usize;
|
let b = data[i] as usize;
|
||||||
i += 1;
|
i += 1;
|
||||||
size |= (b & 0x7F) << shift;
|
size |= (b & 0x7F) << shift;
|
||||||
shift += 7;
|
shift += 7;
|
||||||
if b & 0x80 == 0 { break; }
|
if b & 0x80 == 0 {
|
||||||
if shift > 32 { return false; }
|
break;
|
||||||
|
}
|
||||||
|
if shift > 32 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
i += size;
|
i += size;
|
||||||
} else {
|
} else {
|
||||||
@@ -78,7 +86,11 @@ impl Av1CodecParser {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn obus_for_frame(&mut self, payload: &[u8], rtmp_is_keyframe: bool) -> Option<(Vec<u8>, bool)> {
|
fn obus_for_frame(
|
||||||
|
&mut self,
|
||||||
|
payload: &[u8],
|
||||||
|
rtmp_is_keyframe: bool,
|
||||||
|
) -> Option<(Vec<u8>, bool)> {
|
||||||
if payload.is_empty() {
|
if payload.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@@ -95,8 +107,7 @@ impl Av1CodecParser {
|
|||||||
|
|
||||||
self.first_coded_frame = false;
|
self.first_coded_frame = false;
|
||||||
|
|
||||||
if is_keyframe
|
if is_keyframe && let Some(config) = &self.config_obus {
|
||||||
&& let Some(config) = &self.config_obus {
|
|
||||||
let mut out = Vec::with_capacity(config.len() + payload.len());
|
let mut out = Vec::with_capacity(config.len() + payload.len());
|
||||||
out.extend_from_slice(config);
|
out.extend_from_slice(config);
|
||||||
out.extend_from_slice(payload);
|
out.extend_from_slice(payload);
|
||||||
@@ -129,7 +140,11 @@ impl CodecParser for Av1CodecParser {
|
|||||||
// FourCC — enhanced RTMP only defines CTS for hvc1 CodedFrames.
|
// FourCC — enhanced RTMP only defines CTS for hvc1 CodedFrames.
|
||||||
let payload = data.get(5..)?;
|
let payload = data.get(5..)?;
|
||||||
let (obus, is_keyframe) = self.obus_for_frame(payload, rtmp_is_keyframe)?;
|
let (obus, is_keyframe) = self.obus_for_frame(payload, rtmp_is_keyframe)?;
|
||||||
Some(VideoFrame { data: Bytes::from(obus), is_keyframe, timestamp_ms })
|
Some(VideoFrame {
|
||||||
|
data: Bytes::from(obus),
|
||||||
|
is_keyframe,
|
||||||
|
timestamp_ms,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,12 +53,20 @@ impl H264CodecParser {
|
|||||||
}
|
}
|
||||||
let pts_ms = Self::pts_ms(timestamp_ms, &bytes[5..8]);
|
let pts_ms = Self::pts_ms(timestamp_ms, &bytes[5..8]);
|
||||||
let data = self.avcc_to_annexb(bytes.get(8..)?, is_keyframe)?;
|
let data = self.avcc_to_annexb(bytes.get(8..)?, is_keyframe)?;
|
||||||
Some(VideoFrame { data: Bytes::from(data), is_keyframe, timestamp_ms: pts_ms })
|
Some(VideoFrame {
|
||||||
|
data: Bytes::from(data),
|
||||||
|
is_keyframe,
|
||||||
|
timestamp_ms: pts_ms,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
3 => {
|
3 => {
|
||||||
// CodedFramesX: no CTS field, bytes 5+ = AVCC NALUs
|
// CodedFramesX: no CTS field, bytes 5+ = AVCC NALUs
|
||||||
let data = self.avcc_to_annexb(bytes.get(5..)?, is_keyframe)?;
|
let data = self.avcc_to_annexb(bytes.get(5..)?, is_keyframe)?;
|
||||||
Some(VideoFrame { data: Bytes::from(data), is_keyframe, timestamp_ms })
|
Some(VideoFrame {
|
||||||
|
data: Bytes::from(data),
|
||||||
|
is_keyframe,
|
||||||
|
timestamp_ms,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@@ -77,7 +85,11 @@ impl H264CodecParser {
|
|||||||
// PTS = DTS + CTS — see note on CodedFrames above.
|
// PTS = DTS + CTS — see note on CodedFrames above.
|
||||||
let pts_ms = Self::pts_ms(timestamp_ms, &bytes[2..5]);
|
let pts_ms = Self::pts_ms(timestamp_ms, &bytes[2..5]);
|
||||||
let data = self.avcc_to_annexb(&bytes[5..], is_keyframe)?;
|
let data = self.avcc_to_annexb(&bytes[5..], is_keyframe)?;
|
||||||
Some(VideoFrame { data: Bytes::from(data), is_keyframe, timestamp_ms: pts_ms })
|
Some(VideoFrame {
|
||||||
|
data: Bytes::from(data),
|
||||||
|
is_keyframe,
|
||||||
|
timestamp_ms: pts_ms,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@@ -148,8 +160,7 @@ impl H264CodecParser {
|
|||||||
|
|
||||||
// Prepend SPS+PPS before every keyframe so str0m's packetizer
|
// Prepend SPS+PPS before every keyframe so str0m's packetizer
|
||||||
// can bundle them into a STAP-A alongside the IDR NALU.
|
// can bundle them into a STAP-A alongside the IDR NALU.
|
||||||
if is_keyframe
|
if is_keyframe && let (Some(sps), Some(pps)) = (&self.sps, &self.pps) {
|
||||||
&& let (Some(sps), Some(pps)) = (&self.sps, &self.pps) {
|
|
||||||
out.extend_from_slice(&[0, 0, 0, 1]);
|
out.extend_from_slice(&[0, 0, 0, 1]);
|
||||||
out.extend_from_slice(sps);
|
out.extend_from_slice(sps);
|
||||||
out.extend_from_slice(&[0, 0, 0, 1]);
|
out.extend_from_slice(&[0, 0, 0, 1]);
|
||||||
|
|||||||
@@ -74,8 +74,8 @@ impl H265CodecParser {
|
|||||||
fn hvcc_to_annexb(&self, payload: &[u8], is_keyframe: bool) -> Option<Vec<u8>> {
|
fn hvcc_to_annexb(&self, payload: &[u8], is_keyframe: bool) -> Option<Vec<u8>> {
|
||||||
let mut out = Vec::with_capacity(payload.len());
|
let mut out = Vec::with_capacity(payload.len());
|
||||||
|
|
||||||
if is_keyframe
|
if is_keyframe && let (Some(vps), Some(sps), Some(pps)) = (&self.vps, &self.sps, &self.pps)
|
||||||
&& let (Some(vps), Some(sps), Some(pps)) = (&self.vps, &self.sps, &self.pps) {
|
{
|
||||||
out.extend_from_slice(&[0, 0, 0, 1]);
|
out.extend_from_slice(&[0, 0, 0, 1]);
|
||||||
out.extend_from_slice(vps);
|
out.extend_from_slice(vps);
|
||||||
out.extend_from_slice(&[0, 0, 0, 1]);
|
out.extend_from_slice(&[0, 0, 0, 1]);
|
||||||
@@ -128,12 +128,20 @@ impl CodecParser for H265CodecParser {
|
|||||||
let cts = i32::from_be_bytes([0, data[5], data[6], data[7]]) << 8 >> 8;
|
let cts = i32::from_be_bytes([0, data[5], data[6], data[7]]) << 8 >> 8;
|
||||||
let pts_ms = (timestamp_ms as i64 + cts as i64).max(0) as u32;
|
let pts_ms = (timestamp_ms as i64 + cts as i64).max(0) as u32;
|
||||||
let annexb = self.hvcc_to_annexb(data.get(8..)?, is_keyframe)?;
|
let annexb = self.hvcc_to_annexb(data.get(8..)?, is_keyframe)?;
|
||||||
Some(VideoFrame { data: Bytes::from(annexb), is_keyframe, timestamp_ms: pts_ms })
|
Some(VideoFrame {
|
||||||
|
data: Bytes::from(annexb),
|
||||||
|
is_keyframe,
|
||||||
|
timestamp_ms: pts_ms,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
3 => {
|
3 => {
|
||||||
// CodedFramesX: no CTS, bytes 5+ = HVCC
|
// CodedFramesX: no CTS, bytes 5+ = HVCC
|
||||||
let annexb = self.hvcc_to_annexb(data.get(5..)?, is_keyframe)?;
|
let annexb = self.hvcc_to_annexb(data.get(5..)?, is_keyframe)?;
|
||||||
Some(VideoFrame { data: Bytes::from(annexb), is_keyframe, timestamp_ms })
|
Some(VideoFrame {
|
||||||
|
data: Bytes::from(annexb),
|
||||||
|
is_keyframe,
|
||||||
|
timestamp_ms,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ use axum_extra::extract::{CookieJar, cookie::Cookie};
|
|||||||
/// dashes and apostrophes — no spaces. Mirrors the frontend
|
/// dashes and apostrophes — no spaces. Mirrors the frontend
|
||||||
/// `/^[A-Za-z0-9'-]{0,67}$/` used by the keys-page popups.
|
/// `/^[A-Za-z0-9'-]{0,67}$/` used by the keys-page popups.
|
||||||
fn valid_charset(s: &str) -> bool {
|
fn valid_charset(s: &str) -> bool {
|
||||||
s.len() <= 67 && s.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '\'')
|
s.len() <= 67
|
||||||
|
&& s.chars()
|
||||||
|
.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '\'')
|
||||||
}
|
}
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
|
|||||||
@@ -16,10 +16,7 @@ use tracing_subscriber::EnvFilter;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
audio::OpusAudioFrame,
|
audio::OpusAudioFrame, codec::VideoFrame, http::HttpServer, webrtc_proxy::WebrtcProxy,
|
||||||
codec::VideoFrame,
|
|
||||||
http::HttpServer,
|
|
||||||
webrtc_proxy::WebrtcProxy,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const SERVER_VERSION: &str = env!("CARGO_PKG_VERSION");
|
const SERVER_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|||||||
@@ -71,7 +71,13 @@ pub async fn handle_whip_injest_delete(
|
|||||||
state.appstate.lock().await.stream_sessions.remove(&key.id);
|
state.appstate.lock().await.stream_sessions.remove(&key.id);
|
||||||
// Drop the trickle channel too, so PATCHes to a deleted session 404 and
|
// Drop the trickle channel too, so PATCHes to a deleted session 404 and
|
||||||
// the lingering detach task's own cleanup can't reach a newer session.
|
// the lingering detach task's own cleanup can't reach a newer session.
|
||||||
state.appstate.lock().await.webrtc_proxy.trickle_tx.remove(&key.id);
|
state
|
||||||
|
.appstate
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.webrtc_proxy
|
||||||
|
.trickle_tx
|
||||||
|
.remove(&key.id);
|
||||||
|
|
||||||
Ok(StatusCode::OK)
|
Ok(StatusCode::OK)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,9 @@ impl WebrtcProxy {
|
|||||||
ip
|
ip
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
warn!("no non-loopback IPv4 interface found, falling back to 127.0.0.1");
|
warn!(
|
||||||
|
"no non-loopback IPv4 interface found, falling back to 127.0.0.1"
|
||||||
|
);
|
||||||
IpAddr::from([127, 0, 0, 1])
|
IpAddr::from([127, 0, 0, 1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,9 +202,9 @@ fn stun_attributes(data: &[u8]) -> impl Iterator<Item = (u16, &[u8])> {
|
|||||||
/// connect() only does a route lookup (no packets sent), so the kernel binds
|
/// connect() only does a route lookup (no packets sent), so the kernel binds
|
||||||
/// the source address the OS would use for outbound traffic.
|
/// the source address the OS would use for outbound traffic.
|
||||||
fn default_iface_ipv4() -> Option<IpAddr> {
|
fn default_iface_ipv4() -> Option<IpAddr> {
|
||||||
let sock = std::net::UdpSocket::bind("0.0.0.0:0").ok()?;
|
let sock = std::net::UdpSocket::bind("0.0.0.0:0").ok()?;
|
||||||
sock.connect("8.8.8.8:9").ok()?;
|
sock.connect("8.8.8.8:9").ok()?;
|
||||||
sock.local_addr().ok().map(|a| a.ip())
|
sock.local_addr().ok().map(|a| a.ip())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn resolve_domain(domain: &str) -> Result<std::net::IpAddr, Box<dyn Error>> {
|
async fn resolve_domain(domain: &str) -> Result<std::net::IpAddr, Box<dyn Error>> {
|
||||||
@@ -254,7 +256,9 @@ fn parse_xor_mapped_address(data: &[u8]) -> Option<std::net::IpAddr> {
|
|||||||
// byte 0: reserved, byte 1: family (0x01=IPv4, 0x02=IPv6)
|
// byte 0: reserved, byte 1: family (0x01=IPv4, 0x02=IPv6)
|
||||||
if value[1] == 0x01 {
|
if value[1] == 0x01 {
|
||||||
let x_addr = u32::from_be_bytes(value[4..8].try_into().ok()?);
|
let x_addr = u32::from_be_bytes(value[4..8].try_into().ok()?);
|
||||||
Some(std::net::IpAddr::V4(std::net::Ipv4Addr::from(x_addr ^ magic)))
|
Some(std::net::IpAddr::V4(std::net::Ipv4Addr::from(
|
||||||
|
x_addr ^ magic,
|
||||||
|
)))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//! whether the string-fixups in webrtc_ingest.rs actually match.
|
//! whether the string-fixups in webrtc_ingest.rs actually match.
|
||||||
use std::{net::SocketAddr, time::Instant};
|
use std::{net::SocketAddr, time::Instant};
|
||||||
|
|
||||||
use str0m::{change::SdpOffer, net::Protocol, Candidate, Rtc};
|
use str0m::{Candidate, Rtc, change::SdpOffer, net::Protocol};
|
||||||
|
|
||||||
fn obs_like_offer() -> String {
|
fn obs_like_offer() -> String {
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
|
|||||||
Reference in New Issue
Block a user