From 4a2fd4bdcd599913fac7002185030971423f558b Mon Sep 17 00:00:00 2001 From: Kieran Date: Fri, 6 Jun 2025 19:31:57 +0100 Subject: [PATCH] fix: clearly distinguish between the completed segment and the next segment --- crates/core/src/mux/hls.rs | 53 +++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/crates/core/src/mux/hls.rs b/crates/core/src/mux/hls.rs index 6e74667..f328fa0 100644 --- a/crates/core/src/mux/hls.rs +++ b/crates/core/src/mux/hls.rs @@ -4,8 +4,8 @@ use anyhow::{bail, Result}; use ffmpeg_rs_raw::ffmpeg_sys_the_third::AVCodecID::AV_CODEC_ID_H264; use ffmpeg_rs_raw::ffmpeg_sys_the_third::AVMediaType::AVMEDIA_TYPE_VIDEO; use ffmpeg_rs_raw::ffmpeg_sys_the_third::{ - av_free, av_opt_set, av_q2d, av_write_frame, avio_close, - avio_flush, avio_open, AVPacket, AVStream, AVIO_FLAG_WRITE, AV_PKT_FLAG_KEY, + av_free, av_opt_set, av_q2d, av_write_frame, avio_close, avio_flush, avio_open, AVPacket, + AVStream, AVIO_FLAG_WRITE, AV_PKT_FLAG_KEY, }; use ffmpeg_rs_raw::{cstr, Encoder, Muxer}; use itertools::Itertools; @@ -24,7 +24,6 @@ pub enum SegmentType { FMP4, } - pub enum HlsVariantStream { Video { group: usize, @@ -225,21 +224,21 @@ impl HlsVariant { let pkt_stream = *(*self.mux.context()) .streams .add((*pkt).stream_index as usize); - + // Match FFmpeg's segmentation logic exactly let can_split = (*pkt).flags & AV_PKT_FLAG_KEY == AV_PKT_FLAG_KEY && (*(*pkt_stream).codecpar).codec_type == AVMEDIA_TYPE_VIDEO; - + if can_split { let pkt_q = av_q2d((*pkt).time_base); let pkt_time = (*pkt).pts as f32 * pkt_q as f32; let relative_time = pkt_time - self.pkt_start; - + // FFmpeg checks: pkt->pts - vs->end_pts > 0 to prevent zero duration // and av_compare_ts for target duration let has_positive_duration = relative_time > 0.0; let target_duration_reached = relative_time >= self.segment_length; - + if has_positive_duration && target_duration_reached { result = self.split_next_seg(pkt_time)?; } @@ -256,6 +255,7 @@ impl HlsVariant { /// Reset the muxer state and start the next segment unsafe fn split_next_seg(&mut self, pkt_time: f32) -> Result { + let completed_segment_idx = self.idx; self.idx += 1; // Manually reset muxer avio @@ -284,26 +284,27 @@ impl HlsVariant { let duration = pkt_time - self.pkt_start; // Log the completed segment (previous index), not the next one - let completed_seg_path = - Self::map_segment_path(&self.out_dir, &self.name, self.idx - 1, self.segment_type); - let segment_path = PathBuf::from(&completed_seg_path); - let segment_size = segment_path.metadata().map(|m| m.len()).unwrap_or(0); + let completed_seg_path = Self::map_segment_path( + &self.out_dir, + &self.name, + completed_segment_idx, + self.segment_type, + ); + let completed_segment_path = PathBuf::from(&completed_seg_path); + let segment_size = completed_segment_path + .metadata() + .map(|m| m.len()) + .unwrap_or(0); info!( - "Writing segment {} [{:.3}s, {} bytes]", - segment_path + "Finished segment {} [{:.3}s, {} bytes]", + completed_segment_path .file_name() .unwrap_or_default() .to_string_lossy(), duration, segment_size ); - if let Err(e) = self.push_segment(self.idx, duration) { - warn!("Failed to update playlist: {}", e); - } - /// Get the video variant for this group - /// since this could actually be audio which would not be useful for - /// [Overseer] impl let video_var_id = self .video_stream() .unwrap_or(self.streams.first().unwrap()) @@ -328,18 +329,16 @@ impl HlsVariant { .collect(); // emit result of the previously completed segment, - let prev_seg = self.idx - 1; let created = EgressSegment { variant: video_var_id, - idx: prev_seg, + idx: completed_segment_idx, duration, - path: PathBuf::from(Self::map_segment_path( - &self.out_dir, - &self.name, - prev_seg, - self.segment_type, - )), + path: completed_segment_path, }; + + if let Err(e) = self.push_segment(completed_segment_idx, duration) { + warn!("Failed to update playlist: {}", e); + } self.pkt_start = pkt_time; Ok(EgressResult::Segments { created: vec![created],