|
1 | 1 | use crossbeam::channel::{Receiver, Sender, bounded}; |
2 | 2 | use derive_builder::Builder; |
3 | 3 | use fdk_aac::enc::{BitRate, ChannelMode, Encoder, EncoderParams, Transport}; |
| 4 | +use h264_reader::{ |
| 5 | + annexb::AnnexBReader, |
| 6 | + nal::{Nal, RefNal, UnitType}, |
| 7 | + push::NalInterest, |
| 8 | +}; |
4 | 9 | use hound::WavSpec; |
5 | 10 | use mp4::{ |
6 | 11 | AacConfig, AvcConfig, ChannelConfig, Mp4Config, Mp4Sample, Mp4Writer, SampleFreqIndex, |
7 | 12 | TrackConfig, TrackType, |
8 | 13 | }; |
9 | | -use std::{fs::File, io::BufWriter, path::PathBuf}; |
| 14 | +use std::{ |
| 15 | + fs::File, |
| 16 | + io::{BufWriter, Read}, |
| 17 | + path::PathBuf, |
| 18 | +}; |
10 | 19 | use thiserror::Error; |
11 | 20 |
|
| 21 | +const DEFAULT_PPS: [u8; 6] = [0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0]; |
| 22 | +const DEFAULT_SPS: [u8; 25] = [ |
| 23 | + 0x67, 0x64, 0x00, 0x1e, 0xac, 0xd9, 0x40, 0xa0, 0x2f, 0xf9, 0x70, 0x11, 0x00, 0x00, 0x03, 0x03, |
| 24 | + 0xe9, 0x00, 0x00, 0xea, 0x60, 0x0f, 0x16, 0x2d, 0x96, |
| 25 | +]; |
| 26 | + |
12 | 27 | pub enum VideoFrameType { |
13 | 28 | Frame(Vec<u8>), |
14 | 29 | End, |
@@ -221,18 +236,72 @@ impl Mp4Processor { |
221 | 236 | .map_err(|e| Mp4ProcessorError::Mp4(e.to_string())) |
222 | 237 | } |
223 | 238 |
|
| 239 | + fn extract_sps_pps_from_headers( |
| 240 | + &self, |
| 241 | + headers_data: &[u8], |
| 242 | + ) -> Result<(Vec<u8>, Vec<u8>), Mp4ProcessorError> { |
| 243 | + let mut sps = None; |
| 244 | + let mut pps = None; |
| 245 | + |
| 246 | + let mut reader = AnnexBReader::accumulate(|nal: RefNal<'_>| { |
| 247 | + let nal_unit_type = nal.header().unwrap().nal_unit_type(); |
| 248 | + |
| 249 | + // Read all data from the NAL unit |
| 250 | + let mut reader = nal.reader(); |
| 251 | + let mut data = Vec::new(); |
| 252 | + if let Ok(_) = reader.read_to_end(&mut data) { |
| 253 | + match nal_unit_type { |
| 254 | + UnitType::SeqParameterSet => { |
| 255 | + sps = Some(data); |
| 256 | + } |
| 257 | + UnitType::PicParameterSet => { |
| 258 | + pps = Some(data); |
| 259 | + } |
| 260 | + _ => {} |
| 261 | + } |
| 262 | + } |
| 263 | + |
| 264 | + NalInterest::Buffer |
| 265 | + }); |
| 266 | + |
| 267 | + reader.push(headers_data); |
| 268 | + reader.reset(); |
| 269 | + |
| 270 | + match (sps, pps) { |
| 271 | + (Some(sps_data), Some(pps_data)) => { |
| 272 | + log::info!( |
| 273 | + "Successfully extracted SPS ({} bytes) and PPS ({} bytes) from headers", |
| 274 | + sps_data.len(), |
| 275 | + pps_data.len() |
| 276 | + ); |
| 277 | + log::debug!( |
| 278 | + "SPS first 10 bytes: {:02x?}", |
| 279 | + &sps_data[..sps_data.len().min(10)] |
| 280 | + ); |
| 281 | + log::debug!( |
| 282 | + "PPS first 10 bytes: {:02x?}", |
| 283 | + &pps_data[..pps_data.len().min(10)] |
| 284 | + ); |
| 285 | + Ok((sps_data, pps_data)) |
| 286 | + } |
| 287 | + _ => { |
| 288 | + log::warn!("Failed to extract SPS/PPS from headers, using fallback"); |
| 289 | + Ok((DEFAULT_SPS.to_vec(), DEFAULT_PPS.to_vec())) |
| 290 | + } |
| 291 | + } |
| 292 | + } |
| 293 | + |
224 | 294 | fn setup_video_track( |
225 | 295 | &self, |
226 | 296 | mp4_writer: &mut Mp4Writer<BufWriter<File>>, |
227 | 297 | video_config: &VideoConfig, |
| 298 | + headers_data: Option<&[u8]>, |
228 | 299 | ) -> Result<(), Mp4ProcessorError> { |
229 | | - // Setup video track with minimal SPS/PPS for H.264 |
230 | | - // These are basic parameters that should work for most cases |
231 | | - let sps = vec![ |
232 | | - 0x67, 0x64, 0x00, 0x1e, 0xac, 0xd9, 0x40, 0xa0, 0x2f, 0xf9, 0x70, 0x11, 0x00, 0x00, |
233 | | - 0x03, 0x03, 0xe9, 0x00, 0x00, 0xea, 0x60, 0x0f, 0x16, 0x2d, 0x96, |
234 | | - ]; |
235 | | - let pps = vec![0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0]; |
| 300 | + let (sps, pps) = if let Some(headers) = headers_data { |
| 301 | + self.extract_sps_pps_from_headers(headers)? |
| 302 | + } else { |
| 303 | + (DEFAULT_SPS.to_vec(), DEFAULT_PPS.to_vec()) |
| 304 | + }; |
236 | 305 |
|
237 | 306 | let video_track_config = TrackConfig { |
238 | 307 | track_type: TrackType::Video, |
@@ -318,9 +387,16 @@ impl Mp4Processor { |
318 | 387 | Ok(audio_track_ids) |
319 | 388 | } |
320 | 389 |
|
321 | | - pub fn run_processing_loop(&mut self) -> Result<(), Mp4ProcessorError> { |
| 390 | + pub fn run_processing_loop( |
| 391 | + &mut self, |
| 392 | + headers_data: Option<Vec<u8>>, |
| 393 | + ) -> Result<(), Mp4ProcessorError> { |
322 | 394 | let mut mp4_writer = self.setup_mp4_writer()?; |
323 | | - self.setup_video_track(&mut mp4_writer, &self.config.video_config)?; |
| 395 | + self.setup_video_track( |
| 396 | + &mut mp4_writer, |
| 397 | + &self.config.video_config, |
| 398 | + headers_data.as_deref(), |
| 399 | + )?; |
324 | 400 | let audio_track_ids = self.setup_audio_tracks(&mut mp4_writer)?; |
325 | 401 |
|
326 | 402 | let mut video_timestamp = 0u64; |
@@ -398,7 +474,7 @@ impl Mp4Processor { |
398 | 474 |
|
399 | 475 | match self.encode_samples_to_aac(track_index, chunk) { |
400 | 476 | Ok(aac_data) => { |
401 | | - log::info!("aac_data len: {} bytes", aac_data.len()); |
| 477 | + // log::info!("aac_data len: {} bytes", aac_data.len()); |
402 | 478 |
|
403 | 479 | let samples_per_channel = chunk.len() / channels; |
404 | 480 |
|
@@ -459,11 +535,11 @@ impl Mp4Processor { |
459 | 535 | ) { |
460 | 536 | for track_index in 0..self.audio_buffer_cache.len() { |
461 | 537 | if !self.audio_buffer_cache[track_index].is_empty() { |
462 | | - log::info!( |
463 | | - "Flushing cached audio data for track {}: {} samples", |
464 | | - track_index, |
465 | | - self.audio_buffer_cache[track_index].len() |
466 | | - ); |
| 538 | + // log::info!( |
| 539 | + // "Flushing cached audio data for track {}: {} samples", |
| 540 | + // track_index, |
| 541 | + // self.audio_buffer_cache[track_index].len() |
| 542 | + // ); |
467 | 543 |
|
468 | 544 | // Process the remaining cached data |
469 | 545 | let cached_data = std::mem::take(&mut self.audio_buffer_cache[track_index]); |
|
0 commit comments