refactor: 删除部分多余的代码和注释

This commit is contained in:
mofeng-git
2026-05-01 17:31:04 +08:00
parent 74035f8e12
commit d8e7de74a6
165 changed files with 2960 additions and 9917 deletions

View File

@@ -1,20 +1,4 @@
//! Universal video track for WebRTC streaming
//!
//! Supports multiple codecs: H264, H265, VP8, VP9
//!
//! # Architecture
//!
//! ```text
//! Encoded Frame (H264/H265/VP8/VP9)
//! |
//! v
//! UniversalVideoTrack
//! - H264/VP8/VP9: TrackLocalStaticSample (built-in payloader)
//! - H265: TrackLocalStaticRTP (rtp crate HevcPayloader)
//! |
//! v
//! WebRTC PeerConnection
//! ```
//! Multiplex H264/VP8/VP9 (`TrackLocalStaticSample`) vs H265 (`TrackLocalStaticRTP` + [`H265Payloader`]).
use bytes::Bytes;
use std::sync::Arc;
@@ -27,33 +11,23 @@ use webrtc::track::track_local::track_local_static_rtp::TrackLocalStaticRTP;
use webrtc::track::track_local::track_local_static_sample::TrackLocalStaticSample;
use webrtc::track::track_local::{TrackLocal, TrackLocalWriter};
// Use our custom H265Payloader that handles ALL NAL types correctly
// The rtp crate's HevcPayloader has bugs:
// 1. It drops the IDR frame after emitting the AP packet
// 2. It ignores NAL type 20 (IDR_N_LP)
// rtp `HevcPayloader` mishandles AP+IDR and NAL 20 (`IDR_N_LP`).
use super::h265_payloader::H265Payloader;
use crate::error::Result;
use crate::video::format::Resolution;
use crate::video::types::Resolution;
/// Default MTU for RTP packets
const RTP_MTU: usize = 1200;
/// Video codec type for WebRTC
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum VideoCodec {
/// H.264/AVC
H264,
/// H.265/HEVC
H265,
/// VP8
VP8,
/// VP9
VP9,
}
impl VideoCodec {
/// Get MIME type for SDP
pub fn mime_type(&self) -> &'static str {
match self {
VideoCodec::H264 => "video/H264",
@@ -63,12 +37,10 @@ impl VideoCodec {
}
}
/// Get RTP clock rate (always 90kHz for video)
pub fn clock_rate(&self) -> u32 {
90000
}
/// Get default RTP payload type
pub fn default_payload_type(&self) -> u8 {
match self {
VideoCodec::H264 => 96,
@@ -78,14 +50,12 @@ impl VideoCodec {
}
}
/// Get SDP fmtp parameters
pub fn sdp_fmtp(&self) -> String {
match self {
VideoCodec::H264 => {
"level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f".to_string()
}
VideoCodec::H265 => {
// Match Chrome's H.265 fmtp format: level-id=180 (Level 6.0), profile-id=1 (Main), tier-flag=0, tx-mode=SRST
"level-id=180;profile-id=1;tier-flag=0;tx-mode=SRST".to_string()
}
VideoCodec::VP8 => String::new(),
@@ -93,7 +63,6 @@ impl VideoCodec {
}
}
/// Get display name
pub fn display_name(&self) -> &'static str {
match self {
VideoCodec::H264 => "H.264",
@@ -110,20 +79,13 @@ impl std::fmt::Display for VideoCodec {
}
}
/// Universal video track configuration
#[derive(Debug, Clone)]
pub struct UniversalVideoTrackConfig {
/// Track ID
pub track_id: String,
/// Stream ID
pub stream_id: String,
/// Video codec
pub codec: VideoCodec,
/// Resolution
pub resolution: Resolution,
/// Target bitrate in kbps
pub bitrate_kbps: u32,
/// Frames per second
pub fps: u32,
}
@@ -141,7 +103,6 @@ impl Default for UniversalVideoTrackConfig {
}
impl UniversalVideoTrackConfig {
/// Create H264 config
pub fn h264(resolution: Resolution, bitrate_kbps: u32, fps: u32) -> Self {
Self {
codec: VideoCodec::H264,
@@ -152,7 +113,6 @@ impl UniversalVideoTrackConfig {
}
}
/// Create H265 config
pub fn h265(resolution: Resolution, bitrate_kbps: u32, fps: u32) -> Self {
Self {
codec: VideoCodec::H265,
@@ -163,7 +123,6 @@ impl UniversalVideoTrackConfig {
}
}
/// Create VP8 config
pub fn vp8(resolution: Resolution, bitrate_kbps: u32, fps: u32) -> Self {
Self {
codec: VideoCodec::VP8,
@@ -174,7 +133,6 @@ impl UniversalVideoTrackConfig {
}
}
/// Create VP9 config
pub fn vp9(resolution: Resolution, bitrate_kbps: u32, fps: u32) -> Self {
Self {
codec: VideoCodec::VP9,
@@ -186,40 +144,26 @@ impl UniversalVideoTrackConfig {
}
}
/// Track type wrapper to support different underlying track implementations
enum TrackType {
/// Sample-based track with built-in payloader (H264, VP8, VP9)
Sample(Arc<TrackLocalStaticSample>),
/// RTP-based track with custom payloader (H265)
Rtp(Arc<TrackLocalStaticRTP>),
}
/// H265-specific RTP state
struct H265RtpState {
/// H265 payloader (custom implementation that handles all NAL types)
payloader: H265Payloader,
/// Current sequence number
sequence_number: u16,
/// Current RTP timestamp
timestamp: u32,
/// Timestamp increment per frame (90000 / fps)
timestamp_increment: u32,
}
/// Universal video track supporting H264/H265/VP8/VP9
pub struct UniversalVideoTrack {
/// Underlying WebRTC track (Sample or RTP based)
track: TrackType,
/// Codec type
codec: VideoCodec,
/// Configuration
config: UniversalVideoTrackConfig,
/// H265 RTP state (only used for H265)
h265_state: Option<Mutex<H265RtpState>>,
}
impl UniversalVideoTrack {
/// Create a new universal video track
pub fn new(config: UniversalVideoTrackConfig) -> Self {
let codec_capability = RTCRtpCodecCapability {
mime_type: config.codec.mime_type().to_string(),
@@ -229,16 +173,13 @@ impl UniversalVideoTrack {
rtcp_feedback: vec![],
};
// Use different track types for different codecs
let (track, h265_state) = if config.codec == VideoCodec::H265 {
// H265 uses TrackLocalStaticRTP with official rtp crate HevcPayloader
let rtp_track = Arc::new(TrackLocalStaticRTP::new(
codec_capability,
config.track_id.clone(),
config.stream_id.clone(),
));
// Create H265 RTP state with custom H265Payloader
let h265_state = H265RtpState {
payloader: H265Payloader::new(),
sequence_number: rand::random::<u16>(),
@@ -248,7 +189,6 @@ impl UniversalVideoTrack {
(TrackType::Rtp(rtp_track), Some(Mutex::new(h265_state)))
} else {
// H264/VP8/VP9 use TrackLocalStaticSample with built-in payloader
let sample_track = Arc::new(TrackLocalStaticSample::new(
codec_capability,
config.track_id.clone(),
@@ -266,7 +206,6 @@ impl UniversalVideoTrack {
}
}
/// Get track as TrackLocal for peer connection
pub fn as_track_local(&self) -> Arc<dyn TrackLocal + Send + Sync> {
match &self.track {
TrackType::Sample(t) => t.clone(),
@@ -274,23 +213,14 @@ impl UniversalVideoTrack {
}
}
/// Get codec type
pub fn codec(&self) -> VideoCodec {
self.codec
}
/// Get configuration
pub fn config(&self) -> &UniversalVideoTrackConfig {
&self.config
}
/// Get current statistics
///
/// Write an encoded frame to the track
///
/// Handles codec-specific processing:
/// - H264/H265: NAL unit parsing, parameter caching
/// - VP8/VP9: Direct frame sending
pub async fn write_frame_bytes(&self, data: Bytes, is_keyframe: bool) -> Result<()> {
if data.is_empty() {
return Ok(());
@@ -309,17 +239,8 @@ impl UniversalVideoTrack {
.await
}
/// Write H264 frame (Annex B format)
///
/// Sends the entire Annex B frame as a single Sample to allow the
/// H264Payloader to aggregate SPS+PPS into STAP-A packets.
/// One Annex-B AU per sample so the stack can STAP/FU internally.
async fn write_h264_frame(&self, data: Bytes, _is_keyframe: bool) -> Result<()> {
// Send entire Annex B frame as one Sample
// The H264Payloader in rtp crate will:
// 1. Parse NAL units from Annex B format
// 2. Cache SPS and PPS
// 3. Aggregate SPS+PPS+IDR into STAP-A when possible
// 4. Fragment large NALs using FU-A
let frame_duration = Duration::from_micros(1_000_000 / self.config.fps.max(1) as u64);
let sample = Sample {
data,
@@ -341,19 +262,11 @@ impl UniversalVideoTrack {
Ok(())
}
/// Write H265 frame (Annex B format)
///
/// Pass raw Annex B data directly to the official HevcPayloader.
/// The payloader handles NAL parsing, VPS/SPS/PPS caching, AP generation, and FU fragmentation.
async fn write_h265_frame(&self, data: Bytes, is_keyframe: bool) -> Result<()> {
// Pass raw Annex B data directly to the official HevcPayloader
self.send_h265_rtp(data, is_keyframe).await
}
/// Write VP8 frame
async fn write_vp8_frame(&self, data: Bytes, _is_keyframe: bool) -> Result<()> {
// VP8 frames are sent directly without NAL parsing
// Calculate frame duration based on configured FPS
let frame_duration = Duration::from_micros(1_000_000 / self.config.fps.max(1) as u64);
let sample = Sample {
data,
@@ -375,10 +288,7 @@ impl UniversalVideoTrack {
Ok(())
}
/// Write VP9 frame
async fn write_vp9_frame(&self, data: Bytes, _is_keyframe: bool) -> Result<()> {
// VP9 frames are sent directly without NAL parsing
// Calculate frame duration based on configured FPS
let frame_duration = Duration::from_micros(1_000_000 / self.config.fps.max(1) as u64);
let sample = Sample {
data,
@@ -400,7 +310,6 @@ impl UniversalVideoTrack {
Ok(())
}
/// Send H265 NAL units via custom H265Payloader
async fn send_h265_rtp(&self, payload: Bytes, _is_keyframe: bool) -> Result<()> {
let rtp_track = match &self.track {
TrackType::Rtp(t) => t,
@@ -418,11 +327,10 @@ impl UniversalVideoTrack {
}
};
// Minimize lock hold time: only hold lock during payload generation and state update
// Lock only around payloader + seq/ts bump, not RTP write.
let (payloads, timestamp, seq_start, num_payloads) = {
let mut state = h265_state.lock().await;
// Use custom H265Payloader to fragment the data
let payloads = state.payloader.payload(RTP_MTU, &payload);
if payloads.is_empty() {
@@ -433,19 +341,16 @@ impl UniversalVideoTrack {
let num_payloads = payloads.len();
let seq_start = state.sequence_number;
// Pre-increment sequence number and timestamp
state.sequence_number = state.sequence_number.wrapping_add(num_payloads as u16);
state.timestamp = state.timestamp.wrapping_add(state.timestamp_increment);
(payloads, timestamp, seq_start, num_payloads)
}; // Lock released here, before network I/O
};
// Send RTP packets without holding the lock
for (i, payload_data) in payloads.into_iter().enumerate() {
let seq = seq_start.wrapping_add(i as u16);
let is_last = i == num_payloads - 1;
// Build RTP packet
let packet = rtp::packet::Packet {
header: rtp::header::Header {
version: 2,