1212//! @ai-context: 判定粒度=屏幕采样拍(1s);音频槽只记"最后有声时刻"。标称
1313//! 停检 ~2 拍、恢复 ≤~1.5s,阈值与滞回一次真机 A/B 标定(验收项)。
1414
15- /// 疑似暂停所需连续"无声且无画面动"拍数(采样拍≈1s)。
16- pub const SUSPECT_AFTER_TICKS : u32 = 1 ;
17- /// 确认暂停所需连续拍数(suspect 后再一拍无证即确认——总 ~2s)。
15+ /// 疑似暂停所需连续"无声且无画面动"拍数(采样拍≈1s;审查 F4:1 拍过锐,
16+ /// 静音+静止的真实讲解停顿 2-4s 即误停——标定前置为 2,总确认 ~3-4s)。
17+ pub const SUSPECT_AFTER_TICKS : u32 = 2 ;
18+ /// 确认暂停所需连续拍数(suspect 后一拍无证即确认)。
1819pub const PAUSE_CONFIRM_TICKS : u32 = 1 ;
1920/// "有任何声音"的 RMS 阈值(区别于 VAD 语音阈值 0.005——音乐/环境声也算;
2021/// 需真机标定,验收项)。
@@ -59,7 +60,7 @@ impl MediaDetector {
5960 ///
6061 /// 规则(与设计 §2.10 一致):
6162 /// - Playing:有声或画面动 → 计数清零;无声且无动 → silent_ticks++,
62- /// ≥ SUSPECT_AFTER_TICKS 进入 Suspect;静音但有画面动=无音轨视频,
63+ /// ≥ SUSPECT_AFTER_TICKS(2) 进入 Suspect;静音但有画面动=无音轨视频,
6364 /// 计数清零不升(撤销路径同样在 Suspect 态生效)。
6465 /// - Suspect:声或画任一恢复 → 回 Playing(撤销);仍双静默且
6566 /// ≥ PAUSE_CONFIRM_TICKS → Paused(产出 Suspend)。
@@ -134,9 +135,10 @@ mod tests {
134135 }
135136
136137 #[ test]
137- fn silent_still_two_ticks_confirms_suspend ( ) {
138+ fn silent_still_three_ticks_confirms_suspend ( ) {
138139 let mut m = MediaDetector :: new ( ) ;
139- // 拍 1:无声无动 → suspect;拍 2:仍无声无动 → Suspend
140+ // 拍 1-2:无声无动 → suspect(第 2 拍);拍 3:仍无声无动 → Suspend
141+ assert_eq ! ( m. tick( false , false ) , MediaDecision :: None ) ;
140142 assert_eq ! ( m. tick( false , false ) , MediaDecision :: None ) ;
141143 assert_eq ! ( m. phase, MediaPhase :: Suspect ) ;
142144 assert_eq ! ( m. tick( false , false ) , MediaDecision :: Suspend ) ;
@@ -146,11 +148,14 @@ mod tests {
146148 #[ test]
147149 fn suspect_revoked_by_brief_motion_then_reconfirms ( ) {
148150 let mut m = MediaDetector :: new ( ) ;
151+ assert_eq ! ( m. tick( false , false ) , MediaDecision :: None ) ;
149152 assert_eq ! ( m. tick( false , false ) , MediaDecision :: None ) ; // suspect
153+ assert_eq ! ( m. phase, MediaPhase :: Suspect ) ;
150154 // 缓冲动画一拍 → 撤销回 playing
151155 assert_eq ! ( m. tick( false , true ) , MediaDecision :: None ) ;
152156 assert_eq ! ( m. phase, MediaPhase :: Playing ) ;
153- // 再两拍静默 → 正常确认暂停
157+ // 再三段静默 → 正常确认暂停
158+ assert_eq ! ( m. tick( false , false ) , MediaDecision :: None ) ;
154159 assert_eq ! ( m. tick( false , false ) , MediaDecision :: None ) ;
155160 assert_eq ! ( m. tick( false , false ) , MediaDecision :: Suspend ) ;
156161 }
@@ -159,12 +164,14 @@ mod tests {
159164 fn paused_resumes_on_either_channel ( ) {
160165 let mut m = MediaDetector :: new ( ) ;
161166 assert_eq ! ( m. tick( false , false ) , MediaDecision :: None ) ;
167+ assert_eq ! ( m. tick( false , false ) , MediaDecision :: None ) ;
162168 assert_eq ! ( m. tick( false , false ) , MediaDecision :: Suspend ) ;
163169 // 画面恢复(用户点播放瞬间帧到)
164170 assert_eq ! ( m. tick( false , true ) , MediaDecision :: Resume ) ;
165171 assert_eq ! ( m. phase, MediaPhase :: Playing ) ;
166172 // 再停再走,声音恢复路径
167173 assert_eq ! ( m. tick( false , false ) , MediaDecision :: None ) ;
174+ assert_eq ! ( m. tick( false , false ) , MediaDecision :: None ) ;
168175 assert_eq ! ( m. tick( false , false ) , MediaDecision :: Suspend ) ;
169176 assert_eq ! ( m. tick( true , false ) , MediaDecision :: Resume ) ;
170177 }
0 commit comments