### Example: Building a Muxer with MuxerConfig Source: https://docs.rs/muxide/0.2.5/muxide/api/struct.MuxerConfig.html Demonstrates how to create a MuxerBuilder from a MuxerConfig and build a muxer instance for writing an MP4 file. ```rust use muxide::api::{MuxerConfig, MuxerBuilder, VideoCodec}; use std::fs::File; let file = File::create("out.mp4").unwrap(); let config = MuxerConfig::new(1920, 1080, 30.0); let muxer = config.into_builder(file, VideoCodec::H264).build().unwrap(); ``` -------------------------------- ### Create Basic MuxerConfig Source: https://docs.rs/muxide/0.2.5/src/muxide/api.rs.html Initializes a MuxerConfig with video dimensions and framerate. Defaults to fast start enabled and no audio or metadata. ```rust #[test] fn muxer_config_new_creates_basic_config() { let config = MuxerConfig::new(1920, 1080, 30.0); assert_eq!(config.width, 1920); assert_eq!(config.height, 1080); assert_eq!(config.framerate, 30.0); assert!(config.audio.is_none()); assert!(config.metadata.is_none()); assert!(config.fast_start); } ``` -------------------------------- ### Initialize Muxer with Video Configuration Source: https://docs.rs/muxide/0.2.5/src/muxide/lib.rs.html Demonstrates how to create a new Muxer instance, configure video settings (codec, resolution, framerate), and build the muxer. This is the initial setup for recording. ```rust use muxide::api::{MuxerBuilder, VideoCodec}; use std::fs::File; # fn main() -> Result<(), Box> { let file = File::create("out.mp4")?; let mut muxer = MuxerBuilder::new(file) .video(VideoCodec::H264, 1920, 1080, 30.0) .build()?; // Write frames (encoded elsewhere). // muxer.write_video(pts_secs, annex_b_bytes, is_keyframe)?; let _stats = muxer.finish_with_stats()?; # Ok(()) # } ``` -------------------------------- ### Basic MP4 Muxer Setup Source: https://docs.rs/muxide/0.2.5/index.html This snippet shows how to initialize a Muxide muxer for H.264 video. It requires creating a file and then building the muxer with video codec, resolution, and frame rate. The commented-out line indicates where encoded video frames would be written. ```rust use muxide::api::{MuxerBuilder, VideoCodec}; use std::fs::File; let file = File::create("out.mp4")?; let mut muxer = MuxerBuilder::new(file) .video(VideoCodec::H264, 1920, 1080, 30.0) .build()?; // Write frames (encoded elsewhere). // muxer.write_video(pts_secs, annex_b_bytes, is_keyframe)?; let _stats = muxer.finish_with_stats()?; ``` -------------------------------- ### Configure Muxer with Fast Start Source: https://docs.rs/muxide/0.2.5/src/muxide/api.rs.html Shows how to configure the MuxerConfig to disable the fast start option. Asserts that the fast_start field is correctly set to false. ```rust fn muxer_config_with_fast_start_sets_fast_start() { let config = MuxerConfig::new(1920, 1080, 30.0).with_fast_start(false); assert!(!config.fast_start); } ``` -------------------------------- ### Chaining Muxer Configuration Options Source: https://docs.rs/muxide/0.2.5/src/muxide/api.rs.html Illustrates chaining multiple configuration methods for MuxerConfig, including setting dimensions, framerate, audio codec, metadata, and fast start. Verifies all set parameters. ```rust fn muxer_config_chaining_works() { let metadata = Metadata::new() .with_title("Chained Test") .with_language("eng"); let config = MuxerConfig::new(1280, 720, 24.0) .with_audio(AudioCodec::Opus, 48000, 1) .with_metadata(metadata) .with_fast_start(false); assert_eq!(config.width, 1280); assert_eq!(config.height, 720); assert_eq!(config.framerate, 24.0); assert!(config.audio.is_some()); assert!(config.metadata.is_some()); assert!(!config.fast_start); let audio = config.audio.unwrap(); assert!(matches!(audio.codec, AudioCodec::Opus)); let metadata = config.metadata.unwrap(); assert_eq!(metadata.title, Some("Chained Test".to_string())); assert_eq!(metadata.language, Some("eng".to_string())); } ``` -------------------------------- ### Get Init Segment and Write Video Frames Source: https://docs.rs/muxide/0.2.5/src/muxide/fragmented.rs.html Shows how to retrieve the initialization segment and write video frames to the fragmented muxer. The init segment should be sent once at the beginning of the stream. ```rust // Get init segment (write once at start) let init_segment = muxer.init_segment(); // Write frames... let data = vec![0u8; 100]; // Your frame data muxer.write_video(0, 0, &data, true)?; ``` -------------------------------- ### init_segment Source: https://docs.rs/muxide/0.2.5/src/muxide/fragmented.rs.html Get the initialization segment (ftyp + moov). This should be sent once at the start of a stream. ```APIDOC ## init_segment ### Description Returns the initialization segment, which includes the ftyp and moov boxes. This segment is essential and should be transmitted at the beginning of the media stream. ### Method `init_segment(&mut self) -> Vec` ### Parameters None ### Returns - `Vec`: A vector of bytes representing the initialization segment. ``` -------------------------------- ### H.264 Annex B Input Format Example Source: https://docs.rs/muxide/0.2.5/muxide/codec/h264/index.html Input data for H.264 processing must be in Annex B format, which includes start codes (0x00000001) preceding each NAL unit. ```text [0x00 0x00 0x00 0x01][NAL unit][0x00 0x00 0x00 0x01][NAL unit]... ``` -------------------------------- ### Get Type ID Source: https://docs.rs/muxide/0.2.5/muxide/api/struct.Metadata.html Gets the TypeId of the Metadata instance. ```rust fn type_id(&self) -> TypeId ``` -------------------------------- ### Find Annex B Start Code Source: https://docs.rs/muxide/0.2.5/muxide/codec/common/fn.find_start_code.html Searches for the next Annex B start code (4-byte or 3-byte) in the provided data slice, starting from a given index. Returns the position and length of the start code if found, otherwise None. 4-byte codes are checked first. ```rust pub fn find_start_code(data: &[u8], from: usize) -> Option<(usize, usize)> ``` -------------------------------- ### Initial Sample Table Calculation (Video and Audio) Source: https://docs.rs/muxide/0.2.5/src/muxide/muxer/mp4.rs.html Calculates initial sample tables for both video and audio tracks, assuming interleaving. ```rust let video_tables = SampleTables::from_samples( &self.video_samples, video_offsets, 1, self.video_last_delta, ); let audio_tables = SampleTables::from_samples( &self.audio_samples, audio_offsets, 1, self.audio_last_delta, ); (video_tables, Some(audio_tables)) ``` -------------------------------- ### Build VP09 (VP9) Sample Entry Box Source: https://docs.rs/muxide/0.2.5/src/muxide/muxer/mp4.rs.html Constructs the VP09 sample entry box for MP4 files containing VP9 video. It includes essential video track information and asserts that width and height fit within 16-bit values. ```rust fn build_vp09_box(video: &Mp4VideoTrack, vp9_config: &Vp9Config) -> Vec { // INV-002: Width/height must fit in 16-bit for visual sample entry assert_invariant!( video.width <= u16::MAX as u32, "Width must fit in 16-bit", "build_vp09_box" ); assert_invariant!( video.height <= u16::MAX as u32, "Height must fit in 16-bit", "build_vp09_box" ); let mut payload = Vec::new(); // Reserved (6 bytes) payload.extend_from_slice(&[0u8; 6]); // Data reference index payload.extend_from_slice(&1u16.to_be_bytes()); // Pre-defined + reserved payload.extend_from_slice(&0u16.to_be_bytes()); payload.extend_from_slice(&0u16.to_be_bytes()); payload.extend_from_slice(&0u32.to_be_bytes()); payload.extend_from_slice(&0u32.to_be_bytes()); payload.extend_from_slice(&0u32.to_be_bytes()); // Width and height are 16-bit values in the visual sample entry payload.extend_from_slice(&(video.width as u16).to_be_bytes()); payload.extend_from_slice(&(video.height as u16).to_be_bytes()); // Horizontal/vertical resolution (72 dpi fixed point) payload.extend_from_slice(&0x0048_0000_u32.to_be_bytes()); payload.extend_from_slice(&0x0048_0000_u32.to_be_bytes()); // Reserved payload.extend_from_slice(&0u32.to_be_bytes()); // Frame count payload.extend_from_slice(&1u16.to_be_bytes()); // Compressor name (32 bytes, empty) payload.extend_from_slice(&[0u8; 32]); // Depth (24-bit) payload.extend_from_slice(&0x0018u16.to_be_bytes()); // Pre-defined (-1) payload.extend_from_slice(&0xffffu16.to_be_bytes()); // vpcC box let vpcc_box = build_vpcc_box(vp9_config); payload.extend_from_slice(&vpcc_box); build_box(b"vp09", &payload) } ``` -------------------------------- ### Find Start Code Source: https://docs.rs/muxide/0.2.5/src/muxide/codec/mod.rs.html Finds the start code delimiter in a byte slice. ```APIDOC ## find_start_code ### Description Finds the start code delimiter (0x000001 or 0x00000001) in a byte slice. ### Function Signature `find_start_code(data: &[u8]) -> Option` ### Parameters * `data` (slice of u8) - The byte slice to search within. ### Returns An `Option` containing the starting index of the start code if found, otherwise `None`. ``` -------------------------------- ### Build Muxer with Video and Audio Source: https://docs.rs/muxide/0.2.5/src/muxide/api.rs.html Demonstrates building a Muxer with H.264 video and AAC audio configurations. Encodes a keyframe and audio data, then finishes the muxer. ```rust #[test] fn simple_api_works() -> Result<(), MuxerError> { let mut buffer = Vec::new(); let mut muxer = MuxerBuilder::new(&mut buffer) .video(VideoCodec::H264, 1920, 1080, 30.0) .audio(AudioCodec::Aac(AacProfile::Lc), 48000, 2) .build()?; let video_data = make_h264_keyframe(); muxer.encode_video(&video_data, 33)?; let audio_data = vec![0xff, 0xf1, 0x4c, 0x80, 0x01, 0x3f, 0xfc, 0xaa, 0xbb]; // ADTS muxer.encode_audio(&audio_data, 1024)?; muxer.finish()?; assert!(!buffer.is_empty()); Ok(()) } ``` -------------------------------- ### find_start_code Source: https://docs.rs/muxide/0.2.5/muxide/codec/common/fn.find_start_code.html Finds the next Annex B start code in the data starting from a specified offset. It prioritizes 4-byte start codes over 3-byte ones to prevent incorrect matching within longer sequences. ```APIDOC ## Function find_start_code ### Description Finds the next Annex B start code in the data starting from `from`. Returns the position and length of the start code. Supports: - 4-byte: `0x00 0x00 0x00 0x01` (length = 4) - 3-byte: `0x00 0x00 0x01` (length = 3) 4-byte start codes are checked first to avoid matching `0x00 0x00 0x01` within a `0x00 0x00 0x00 0x01` sequence. ### Parameters * `data` (&[u8]): The byte slice to search within. * `from` (usize): The starting index for the search. ### Returns * `Some((usize, usize))`: A tuple containing the position and length of the found start code. * `None`: If no start code is found from the `from` index onwards. ``` -------------------------------- ### Test find_start_code with offset Source: https://docs.rs/muxide/0.2.5/src/muxide/codec/common.rs.html Tests find_start_code's ability to find start codes when searching from an offset within the data. It also checks cases where the offset might fall within a start code sequence and when no start code is found after the offset. ```rust let data = [0xAB, 0xCD, 0x00, 0x00, 0x00, 0x01, 0x67]; assert_eq!(find_start_code(&data, 0), Some((2, 4))); // When starting from position 3, we find the start code at position 3 // (this is the middle of a 4-byte start code, but also valid as 3-byte) assert_eq!(find_start_code(&data, 3), Some((3, 3))); assert_eq!(find_start_code(&data, 6), None); ``` -------------------------------- ### Build AV1 Sample Entry Box ('av01') Source: https://docs.rs/muxide/0.2.5/src/muxide/muxer/mp4.rs.html Constructs the 'av01' sample entry box for AV1 video tracks in an MP4 container. It includes assertions to ensure video dimensions fit within 16-bit values. ```rust /// Build an av01 sample entry box for AV1 video. fn build_av01_box(video: &Mp4VideoTrack, av1_config: &Av1Config) -> Vec { // INV-002: Width/height must fit in 16-bit for visual sample entry assert_invariant!( video.width <= u16::MAX as u32, "Width must fit in 16-bit", "build_av01_box" ); assert_invariant!( video.height <= u16::MAX as u32, "Height must fit in 16-bit", "build_av01_box" ); let mut payload = Vec::new(); // Reserved (6 bytes) payload.extend_from_slice(&[0u8; 6]); // Data reference index payload.extend_from_slice(&1u16.to_be_bytes()); // Pre-defined + reserved payload.extend_from_slice(&0u16.to_be_bytes()); payload.extend_from_slice(&0u16.to_be_bytes()); payload.extend_from_slice(&0u32.to_be_bytes()); payload.extend_from_slice(&0u32.to_be_bytes()); payload.extend_from_slice(&0u32.to_be_bytes()); // Width and height are 16-bit values in the visual sample entry payload.extend_from_slice(&(video.width as u16).to_be_bytes()); payload.extend_from_slice(&(video.height as u16).to_be_bytes()); // Horizontal/vertical resolution (72 dpi fixed point) payload.extend_from_slice(&0x0048_0000_u32.to_be_bytes()); payload.extend_from_slice(&0x0048_0000_u32.to_be_bytes()); // Reserved payload.extend_from_slice(&0u32.to_be_bytes()); // Frame count payload.extend_from_slice(&1u16.to_be_bytes()); // Compressor name (32 bytes, empty) payload.extend_from_slice(&[0u8; 32]); // Depth (24-bit) payload.extend_from_slice(&0x0018u16.to_be_bytes()); // Pre-defined (-1) payload.extend_from_slice(&0xffffu16.to_be_bytes()); // av1C box let av1c_box = build_av1c_box(av1_config); // ... (rest of the function would follow) } ``` -------------------------------- ### Build Sample Description Box (stsd) for fMP4 Source: https://docs.rs/muxide/0.2.5/src/muxide/fragmented.rs.html Creates the sample description box, dynamically selecting the appropriate codec-specific sample entry (AV1, VP9, HEVC, or AVC) based on the configuration. ```rust fn build_stsd_fmp4(config: &FragmentConfig) -> Vec { let sample_entry = if config.av1_sequence_header.is_some() { build_av01_fmp4(config) } else if config.vp9_config.is_some() { build_vp09_fmp4(config) } else if config.vps.is_some() { build_hvc1_fmp4(config) } else { build_avc1_fmp4(config) }; let mut payload = Vec::new(); payload.extend_from_slice(&0u32.to_be_bytes()); // Version + flags payload.extend_from_slice(&1u32.to_be_bytes()); // Entry count payload.extend_from_slice(&sample_entry); build_box(b"stsd", &payload) } ``` -------------------------------- ### Test AnnexB NAL Iterator with No Start Code Source: https://docs.rs/muxide/0.2.5/src/muxide/codec/common.rs.html Verifies that AnnexBNalIter returns an empty iterator when the input data does not contain any Annex B start codes. ```rust #[test] fn test_annexb_iter_no_start_code() { let data = [0x67, 0x42, 0x00, 0x1f]; let nals: Vec<_> = AnnexBNalIter::new(&data).collect(); assert!(nals.is_empty()); } ``` -------------------------------- ### Initial Sample Table Calculation (Video-Only) Source: https://docs.rs/muxide/0.2.5/src/muxide/muxer/mp4.rs.html Calculates initial sample tables for a video-only track. Assumes all samples are in a single chunk. ```rust // Video-only: all samples in one chunk let chunk_offsets = if self.video_samples.is_empty() { Vec::new() } else { vec![0u32] // Single placeholder chunk offset (will be replaced with real value) }; let samples_per_chunk = if self.video_samples.is_empty() { 0 } else { self.video_samples.len() as u32 }; let video_tables = SampleTables::from_samples( &self.video_samples, chunk_offsets, samples_per_chunk, self.video_last_delta, ); ``` -------------------------------- ### Test find_start_code for 3-byte start code Source: https://docs.rs/muxide/0.2.5/src/muxide/codec/common.rs.html Verifies that find_start_code correctly identifies a 3-byte start code (`0x00 0x00 0x01`) at the beginning of the data slice. ```rust let data = [0x00, 0x00, 0x01, 0x67]; assert_eq!(find_start_code(&data, 0), Some((0, 3))); ``` -------------------------------- ### ready_to_flush Source: https://docs.rs/muxide/0.2.5/src/muxide/fragmented.rs.html Check if we have enough samples to make a fragment. ```APIDOC ## ready_to_flush ### Description Determines if enough samples have been buffered to create a complete media fragment according to the configured fragment duration. ### Method `ready_to_flush(&self) -> bool` ### Parameters None ### Returns - `bool`: True if there are at least two samples and the buffered duration meets or exceeds the fragment duration, false otherwise. ``` -------------------------------- ### Test find_start_code for 4-byte start code Source: https://docs.rs/muxide/0.2.5/src/muxide/codec/common.rs.html Verifies that find_start_code correctly identifies a 4-byte start code (`0x00 0x00 0x00 0x01`) at the beginning of the data slice. ```rust let data = [0x00, 0x00, 0x00, 0x01, 0x67]; assert_eq!(find_start_code(&data, 0), Some((0, 4))); ``` -------------------------------- ### Test HEVC Keyframe Detection (No Panic on No Start Codes) Source: https://docs.rs/muxide/0.2.5/src/muxide/codec/h265.rs.html Ensures `is_hevc_keyframe` does not panic when processing data without Annex B start codes, returning `false` instead. ```rust let raw = [0x42, 0x01, 0x01, 0x21, 0x80, 0x00]; assert!(!is_hevc_keyframe(&raw), "non-Annex-B data must return false, not panic"); ``` -------------------------------- ### Build AVC1 Sample Entry for fMP4 Source: https://docs.rs/muxide/0.2.5/src/muxide/fragmented.rs.html Constructs the AVC1 (H.264) sample entry for the sample description box, including video dimensions, resolution, and compressor name. ```rust fn build_avc1_fmp4(config: &FragmentConfig) -> Vec { let mut payload = Vec::new(); payload.extend_from_slice(&[0u8; 6]); // Reserved payload.extend_from_slice(&1u16.to_be_bytes()); // Data reference index payload.extend_from_slice(&0u16.to_be_bytes()); // Pre-defined payload.extend_from_slice(&0u16.to_be_bytes()); // Reserved payload.extend_from_slice(&[0u8; 12]); // Pre-defined payload.extend_from_slice(&(config.width as u16).to_be_bytes()); payload.extend_from_slice(&(config.height as u16).to_be_bytes()); payload.extend_from_slice(&0x0048_0000_u32.to_be_bytes()); // Horizontal resolution (72 dpi) payload.extend_from_slice(&0x0048_0000_u32.to_be_bytes()); // Vertical resolution (72 dpi) payload.extend_from_slice(&0u32.to_be_bytes()); // Reserved payload.extend_from_slice(&1u16.to_be_bytes()); // Frame count payload.extend_from_slice(&[0u8; 32]); // Compressor name payload.extend_from_slice(&0x0018_u16.to_be_bytes()); // Depth: 24-bit color } ``` -------------------------------- ### Test AnnexB NAL Iterator with Mixed Start Codes Source: https://docs.rs/muxide/0.2.5/src/muxide/codec/common.rs.html Tests AnnexBNalIter with a mix of 3-byte and 4-byte Annex B start codes to ensure correct NAL unit extraction. ```rust #[test] fn test_annexb_iter_mixed_start_codes() { // Mix of 3-byte and 4-byte start codes let data = [ 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, // 4-byte 0x00, 0x00, 0x01, 0x68, 0xCE, // 3-byte ]; let nals: Vec<_> = AnnexBNalIter::new(&data).collect(); assert_eq!(nals.len(), 2); assert_eq!(nals[0], &[0x67, 0x42]); assert_eq!(nals[1], &[0x68, 0xCE]); } ``` -------------------------------- ### Test find_start_code when no start code exists Source: https://docs.rs/muxide/0.2.5/src/muxide/codec/common.rs.html Confirms that find_start_code returns `None` when the provided data slice does not contain any valid Annex B start codes from the specified offset onwards. ```rust let data = [0x00, 0x00, 0x02, 0x67]; assert_eq!(find_start_code(&data, 0), None); ``` -------------------------------- ### Set H.265 VPS for Fragmented MP4 Source: https://docs.rs/muxide/0.2.5/muxide/api/struct.MuxerBuilder.html Provide Video Parameter Set data, essential for initializing H.265 fragmented MP4. ```rust pub fn with_vps(self, vps: Vec) -> Self ``` -------------------------------- ### Test HEVC Keyframe Detection (No Panic on Consecutive Start Codes) Source: https://docs.rs/muxide/0.2.5/src/muxide/codec/h265.rs.html Verifies that `is_hevc_keyframe` handles consecutive Annex B start codes without panicking, which would otherwise create an empty NAL slice. ```rust let consecutive = [ 0x00, 0x00, 0x00, 0x01, // start code 0x00, 0x00, 0x00, 0x01, // consecutive start code → empty NAL between them ]; ``` -------------------------------- ### Create Fragmented Muxer and Write Segments Source: https://docs.rs/muxide/0.2.5/muxide/fragmented/index.html Demonstrates how to initialize a fragmented muxer with video codec, SPS, and PPS, then generate init and media segments. Use this for setting up fragmented MP4 streams. ```rust use muxide::api::{MuxerBuilder, VideoCodec}; // Create a fragmented muxer using the builder API let mut muxer = MuxerBuilder::new(Vec::::new()) .video(VideoCodec::H264, 1920, 1080, 30.0) .with_sps(vec![0x67, 0x42, 0x00, 0x1e, 0xda, 0x02, 0x80, 0x2d, 0x8b, 0x11]) // SPS .with_pps(vec![0x68, 0xce, 0x38, 0x80]) // PPS .new_with_fragment()?; // Get init segment (write once at start) let init_segment = muxer.init_segment(); // Write frames... let data = vec![0u8; 100]; // Your frame data muxer.write_video(0, 0, &data, true)?; // Get media segment when ready if let Some(segment) = muxer.flush_segment() { // Send segment to client } ``` -------------------------------- ### Test Annex B to AVCC Conversion (No Start Codes) Source: https://docs.rs/muxide/0.2.5/src/muxide/codec/h264.rs.html Tests the conversion of H.264 Annex B data to AVCC format when the input data lacks start codes, ensuring it's treated as a single NAL unit. ```rust #[test] fn test_annexb_to_avcc_no_start_codes() { // Data without start codes - treated as single NAL let data = [0x65, 0x88, 0x84]; let avcc = annexb_to_avcc(&data); assert_eq!(&avcc[0..4], &[0x00, 0x00, 0x00, 0x03]); } ``` -------------------------------- ### with_vps Source: https://docs.rs/muxide/0.2.5/src/muxide/api.rs.html Sets the VPS (Video Parameter Set) data for H.265 fragmented MP4. This is required for proper initialization. ```APIDOC ## with_vps ### Description Sets the VPS (Video Parameter Set) data for H.265 fragmented MP4. This is required for proper initialization. ### Method `with_vps(mut self, vps: Vec) -> Self` ``` -------------------------------- ### current_fragment_duration_ms Source: https://docs.rs/muxide/0.2.5/src/muxide/fragmented.rs.html Get current fragment duration in milliseconds. ```APIDOC ## current_fragment_duration_ms ### Description Retrieves the duration, in milliseconds, of the media currently buffered and ready to be flushed into a fragment. ### Method `current_fragment_duration_ms(&self) -> u64` ### Parameters None ### Returns - `u64`: The duration of the buffered samples in milliseconds. ``` -------------------------------- ### MuxerConfig::new Source: https://docs.rs/muxide/0.2.5/muxide/api/struct.MuxerConfig.html Creates a new MuxerConfig with specified video dimensions and frame rate. Audio is disabled by default, and fast-start is enabled. ```APIDOC ## MuxerConfig::new ### Description Create a new config with the given video dimensions and frame rate. Audio is disabled by default; use `with_audio` to enable it. Fast-start is enabled by default. ### Signature `pub fn new(width: u32, height: u32, framerate: f64) -> Self` ``` -------------------------------- ### Build MP4 Media Header Box (Version 1) Source: https://docs.rs/muxide/0.2.5/src/muxide/muxer/mp4.rs.html Constructs the 'mdhd' box with version 1, supporting 64-bit duration and timestamps. Includes timescale, duration, and language code. ```rust fn build_mdhd_box_with_timescale_and_duration( timescale: u32, duration: u64, language: Option<&str>, ) -> Vec { let mut payload = Vec::new(); if duration > u32::MAX as u64 { // Version 1: 64-bit creation/modification time and duration. payload.extend_from_slice(&0x0100_0000u32.to_be_bytes()); // version=1, flags=0 payload.extend_from_slice(&0u64.to_be_bytes()); // creation_time (u64) payload.extend_from_slice(&0u64.to_be_bytes()); // modification_time (u64) payload.extend_from_slice(×cale.to_be_bytes()); payload.extend_from_slice(&duration.to_be_bytes()); // duration (u64) } else { // Version 0: 32-bit creation/modification time and duration. payload.extend_from_slice(&0u32.to_be_bytes()); // version=0, flags=0 payload.extend_from_slice(&0u32.to_be_bytes()); // creation_time payload.extend_from_slice(&0u32.to_be_bytes()); // modification_time payload.extend_from_slice(×cale.to_be_bytes()); payload.extend_from_slice(&(duration as u32).to_be_bytes()); // duration } payload.extend_from_slice(&encode_language_code(language.unwrap_or("und"))); // language payload.extend_from_slice(&0u16.to_be_bytes()); // pre_defined build_box(b"mdhd", &payload) } ``` -------------------------------- ### Convert HEVC Annex B to HVCC Format Source: https://docs.rs/muxide/0.2.5/src/muxide/codec/h265.rs.html Converts HEVC NAL units from Annex B format (start codes) to HVCC format (length-prefixed). Each NAL unit is preceded by its 4-byte big-endian length. If no start codes are found, the entire input is treated as a single NAL. ```rust pub fn hevc_annexb_to_hvcc(data: &[u8]) -> Vec { let mut out = Vec::new(); for nal in AnnexBNalIter::new(data) { if nal.is_empty() { continue; } let len = nal.len() as u32; out.extend_from_slice(&len.to_be_bytes()); out.extend_from_slice(nal); } // Fallback: if no start codes found, treat entire input as single NAL if out.is_empty() && !data.is_empty() { let len = data.len() as u32; out.extend_from_slice(&len.to_be_bytes()); out.extend_from_slice(data); } out } ``` -------------------------------- ### OpusFrameDuration Methods Source: https://docs.rs/muxide/0.2.5/src/muxide/codec/opus.rs.html Provides methods to get the duration in samples (at 48kHz) and seconds for each Opus frame duration. ```rust impl OpusFrameDuration { /// Get the duration in samples at 48kHz. pub fn samples(self) -> u32 { match self { OpusFrameDuration::Ms2_5 => 120, OpusFrameDuration::Ms5 => 240, OpusFrameDuration::Ms10 => 480, OpusFrameDuration::Ms20 => 960, OpusFrameDuration::Ms40 => 1920, OpusFrameDuration::Ms60 => 2880, } } /// Get the duration in seconds. pub fn seconds(self) -> f64 { self.samples() as f64 / OPUS_SAMPLE_RATE as f64 } } ``` -------------------------------- ### Mp4Writer::new Source: https://docs.rs/muxide/0.2.5/src/muxide/muxer/mp4.rs.html Initializes a new MP4 writer instance. It takes a writer and the video codec type as arguments. ```APIDOC ## Mp4Writer::new ### Description Wraps the provided writer for MP4 container output. ### Signature ```rust pub fn new(writer: Writer, video_codec: VideoCodec) -> Self ``` ### Parameters - **writer**: The underlying writer to which MP4 data will be written. - **video_codec**: The type of video codec being used (e.g., H264, VP9, AV1). ``` -------------------------------- ### OpusConfig Builder Methods Source: https://docs.rs/muxide/0.2.5/src/muxide/codec/opus.rs.html Provides convenient methods to create Opus configurations for mono, stereo, or with custom pre-skip and channel counts. ```rust impl OpusConfig { /// Create a mono Opus configuration. pub fn mono() -> Self { Self { output_channel_count: 1, ..Default::default() } } /// Create a stereo Opus configuration. pub fn stereo() -> Self { Self { output_channel_count: 2, ..Default::default() } } /// Create configuration with custom pre-skip. pub fn with_pre_skip(mut self, pre_skip: u16) -> Self { self.pre_skip = pre_skip; self } /// Create configuration with custom channel count. pub fn with_channels(mut self, channels: u8) -> Self { self.output_channel_count = channels; if channels > 2 { // For > 2 channels, need mapping family 1 or higher self.channel_mapping_family = 1; } self } } ``` -------------------------------- ### Get Current Fragment Duration Source: https://docs.rs/muxide/0.2.5/muxide/fragmented/struct.FragmentedMuxer.html Retrieves the duration of the current fragment being built, in milliseconds. This can be useful for monitoring fragment progress. ```rust pub fn current_fragment_duration_ms(&self) -> u64 ``` -------------------------------- ### Get Opus Frame Duration in Seconds Source: https://docs.rs/muxide/0.2.5/muxide/codec/opus/enum.OpusFrameDuration.html Calculates the duration of an Opus frame in seconds. This is helpful for time-based calculations or display. ```rust pub fn seconds(self) -> f64 ``` -------------------------------- ### Build Video Media Header Box (vmhd) Source: https://docs.rs/muxide/0.2.5/src/muxide/fragmented.rs.html Constructs the video media header box with version, flags, graphics mode, and op color. ```rust fn build_vmhd() -> Vec { let mut payload = Vec::new(); payload.extend_from_slice(&0x0000_0001_u32.to_be_bytes()); // Version 0, flags: 1 payload.extend_from_slice(&[0u8; 8]); // Graphics mode + op color build_box(b"vmhd", &payload) } ``` -------------------------------- ### Create MuxerConfig Source: https://docs.rs/muxide/0.2.5/muxide/api/struct.MuxerConfig.html Initializes a new MuxerConfig with specified video dimensions and frame rate. Audio is disabled by default, and fast-start is enabled. ```rust pub fn new(width: u32, height: u32, framerate: f64) -> Self ``` -------------------------------- ### Build Muxer Instance Source: https://docs.rs/muxide/0.2.5/src/muxide/api.rs.html Finalizes the builder and produces a `Muxer` instance. Returns an error if required configuration is missing or invalid. Video configuration is required; audio is optional. ```rust pub fn build(self) -> Result, MuxerError> where Writer: Write, { // Video configuration is required. Audio is optional and omitting it // produces a video-only MP4 (no audio track box in moov). let (codec, width, height, framerate) = self.video.ok_or(MuxerError::MissingVideoConfig)?; let video_track = VideoTrackConfig { codec, width, height, framerate, }; let audio_track = self.audio.and_then(|(codec, sample_rate, channels)| { if codec == AudioCodec::None { None } else { Some(AudioTrackConfig { codec, sample_rate, channels, }) } }); let mut writer = Mp4Writer::new(self.writer, video_track.codec); if let Some(audio) = &audio_track { writer.enable_audio(Mp4AudioTrack { sample_rate: audio.sample_rate, channels: audio.channels, codec: audio.codec, }); } Ok(Muxer { writer, video_track, audio_track, metadata: self.metadata, fast_start: self.fast_start, first_video_pts: None, last_video_pts: None, last_video_dts: None, last_audio_pts: None, video_frame_count: 0, audio_frame_count: 0, finished: false, current_video_pts: 0.0, current_audio_pts: 0.0, }) } ``` -------------------------------- ### AvcConfig::new Source: https://docs.rs/muxide/0.2.5/src/muxide/codec/h264.rs.html Creates a new AvcConfig instance with provided SPS and PPS data. ```APIDOC ## fn new(sps: Vec, pps: Vec) -> Self Create a new AVC configuration from SPS and PPS data. ``` -------------------------------- ### Flush Media Segment Source: https://docs.rs/muxide/0.2.5/src/muxide/fragmented.rs.html Illustrates how to flush the fragmented muxer to get a media segment when it's ready. This segment can then be sent to the client. ```rust // Get media segment when ready if let Some(segment) = muxer.flush_segment() { // Send segment to client } ``` -------------------------------- ### Get Initialization Segment Source: https://docs.rs/muxide/0.2.5/muxide/fragmented/struct.FragmentedMuxer.html Retrieves the initialization segment (ftyp + moov) for the fragmented MP4 stream. This segment must be sent once at the beginning of the stream. ```rust pub fn init_segment(&mut self) -> Vec ``` -------------------------------- ### Create SampleTables from Samples Source: https://docs.rs/muxide/0.2.5/src/muxide/muxer/mp4.rs.html Constructs SampleTables from a slice of SampleInfo, calculating durations, sizes, keyframes, and composition time offsets. ```rust impl SampleTables { fn from_samples( samples: &[SampleInfo], chunk_offsets: Vec, samples_per_chunk: u32, fallback_duration: Option, ) -> Self { let mut durations = Vec::with_capacity(samples.len()); for (idx, sample) in samples.iter().enumerate() { let duration = sample.duration.unwrap_or_else(|| { if idx == samples.len() - 1 { fallback_duration.unwrap_or(1) } else { 1 } }); durations.push(duration); } let sizes = samples .iter() .map(|sample| sample.data.len() as u32) .collect(); let keyframes = samples .iter() .enumerate() .filter_map(|(idx, sample)| { if sample.is_keyframe { Some(idx as u32 + 1) } else { None } }) .collect(); // Compute composition time offsets (cts = pts - dts) let mut has_bframes = false; let cts_offsets: Vec = samples .iter() .map(|sample| { let offset = (sample.pts as i64 - sample.dts as i64) as i32; if offset != 0 { has_bframes = true; } offset }) .collect(); Self { durations, sizes, keyframes, chunk_offsets, samples_per_chunk, cts_offsets, has_bframes, } } /// Calculate total duration in media timescale units fn total_duration(&self) -> u64 { self.durations.iter().map(|&d| d as u64).sum() } } ``` -------------------------------- ### Consume Muxer, Finalize, and Get Stats Source: https://docs.rs/muxide/0.2.5/muxide/api/struct.Muxer.html Consumes the muxer, finalizes the container, and returns muxing statistics. Ensures the muxer cannot be used after finalization. ```rust pub fn finish_with_stats(self) -> Result ``` -------------------------------- ### Sample Information Structure Source: https://docs.rs/muxide/0.2.5/src/muxide/muxer/mp4.rs.html Stores details for each individual media sample, including presentation and decode timestamps, data, keyframe status, and duration. ```rust struct SampleInfo { pts: u64, dts: u64, // Decode time (for B-frames: dts != pts) data: Vec, is_keyframe: bool, duration: Option, } ``` -------------------------------- ### Test Valid VP9 Frame Marker Source: https://docs.rs/muxide/0.2.5/src/muxide/codec/vp9.rs.html Tests `is_valid_vp9_frame` with a frame starting with `0x82`, which has the correct VP9 frame marker (10 in binary). ```rust let valid_frame = [0x82, 0x49, 0x83, 0x42, 0x00, 0x00]; assert!(is_valid_vp9_frame(&valid_frame)); ``` -------------------------------- ### Configure Audio Track Source: https://docs.rs/muxide/0.2.5/muxide/api/struct.MuxerBuilder.html Set up the audio track parameters including codec, sample rate, and channel count. ```rust pub fn audio(self, codec: AudioCodec, sample_rate: u32, channels: u16) -> Self ``` -------------------------------- ### MuxerBuilder::with_vps Source: https://docs.rs/muxide/0.2.5/muxide/api/struct.MuxerBuilder.html Sets the VPS (Video Parameter Set) data, required for H.265 fragmented MP4 initialization. ```APIDOC ## MuxerBuilder::with_vps ### Description Set VPS (Video Parameter Set) data for H.265 fragmented MP4. Required for proper H.265 fragmented MP4 initialization. ### Signature ```rust pub fn with_vps(self, vps: Vec) -> Self ``` ``` -------------------------------- ### Build MP4 Data Reference Box Source: https://docs.rs/muxide/0.2.5/src/muxide/muxer/mp4.rs.html Constructs the 'dref' box, which contains one or more data references. This example includes a URL data reference. ```rust fn build_dref_box() -> Vec { let url_box = build_url_box(); let mut payload = Vec::new(); payload.extend_from_slice(&0u32.to_be_bytes()); payload.extend_from_slice(&1u32.to_be_bytes()); payload.extend_from_slice(&url_box); build_box(b"dref", &payload) } ``` -------------------------------- ### Get Opus Frame Duration in Samples Source: https://docs.rs/muxide/0.2.5/muxide/codec/opus/enum.OpusFrameDuration.html Retrieves the duration of an Opus frame in samples, assuming a 48kHz sample rate. This is useful for low-level audio processing. ```rust pub fn samples(self) -> u32 ``` -------------------------------- ### Configure Video Track Source: https://docs.rs/muxide/0.2.5/muxide/api/struct.MuxerBuilder.html Set up the video track parameters including codec, dimensions, and framerate. ```rust pub fn video( self, codec: VideoCodec, width: u32, height: u32, framerate: f64, ) -> Self ``` -------------------------------- ### Extract Frame Count from Opus Packet Source: https://docs.rs/muxide/0.2.5/muxide/codec/opus/fn.opus_frame_count.html Use this function to get the number of frames and VBR status from an Opus packet. Returns None if the packet is invalid. ```rust pub fn opus_frame_count(packet: &[u8]) -> Option<(u8, bool)> ``` -------------------------------- ### Build MP4A Box Source: https://docs.rs/muxide/0.2.5/src/muxide/muxer/mp4.rs.html Constructs the 'mp4a' (MPEG-4 Audio) sample entry box, including basic audio properties and the ESDS box. ```rust fn build_mp4a_box(audio: &Mp4AudioTrack) -> Vec { let mut payload = Vec::new(); payload.extend_from_slice(&[0u8; 6]); payload.extend_from_slice(&1u16.to_be_bytes()); payload.extend_from_slice(&0u32.to_be_bytes()); payload.extend_from_slice(&0u32.to_be_bytes()); payload.extend_from_slice(&audio.channels.to_be_bytes()); payload.extend_from_slice(&16u16.to_be_bytes()); payload.extend_from_slice(&0u16.to_be_bytes()); payload.extend_from_slice(&0u16.to_be_bytes()); let rate_fixed = audio.sample_rate << 16; payload.extend_from_slice(&rate_fixed.to_be_bytes()); let esds = build_esds_box(audio); payload.extend_from_slice(&esds); build_box(b"mp4a", &payload) } ``` -------------------------------- ### Matching NonMonotonicDts Error Source: https://docs.rs/muxide/0.2.5/muxide/fragmented/enum.FragmentedError.html Example of how to match the NonMonotonicDts error variant. Due to the non-exhaustive nature of FragmentedError, a wildcard arm `_ => {}` is necessary to handle any future variants. ```rust match error { FragmentedError::NonMonotonicDts { prev_dts, curr_dts } => { // Handle non-monotonic DTS error } _ => { // Handle other or future errors } } ``` -------------------------------- ### Build MP4 'minf' Box for Audio Source: https://docs.rs/muxide/0.2.5/src/muxide/muxer/mp4.rs.html Creates the 'minf' (media information) box for an audio track, which includes 'smhd' (sound media header), 'dinf' (data information), and 'stbl' (sample table) boxes. ```rust fn build_audio_minf_box(audio: &Mp4AudioTrack, tables: &SampleTables) -> Vec { let smhd_box = build_smhd_box(); let dinf_box = build_dinf_box(); let stbl_box = build_audio_stbl_box(audio, tables); let mut payload = Vec::new(); payload.extend_from_slice(&smhd_box); payload.extend_from_slice(&dinf_box); payload.extend_from_slice(&stbl_box); build_box(b"minf", &payload) } ``` -------------------------------- ### Compute Interleave Schedule for Fast Start Source: https://docs.rs/muxide/0.2.5/src/muxide/muxer/mp4.rs.html Computes the interleave schedule for samples when audio is present, which is a prerequisite for calculating placeholder offsets for the 'moov' box in fast-start mode. ```rust // For fast-start with audio, we need to compute interleaved offsets // First, compute the interleave schedule let schedule = self.compute_interleave_schedule(); // Placeholder offsets - will be recalculated after we know moov size let mut video_offsets = Vec::with_capacity(self.video_samples.len()); let mut audio_offsets = Vec::with_capacity(self.audio_samples.len()); let mut cursor = 0u32; for (_, kind, _) in &schedule { match kind { TrackKind::Video => { video_offsets.push(cursor); cursor += 1; // placeholder } TrackKind::Audio => { audio_offsets.push(cursor); cursor += 1; // placeholder } } } let video_tables = SampleTables::from_samples( &self.video_samples, video_offsets, 1, ```