Implement audio master clock for A/V synchronization
Priority 3 fix from PLAYER_PERFORMANCE_ISSUES.md - addresses the root cause of A/V desync in player module. Changes: - Audio loop now tracks bytes written and updates master clock (audioTime) - Audio clock calculation: bytesWritten / (sampleRate × channels × bytesPerSample) - Video loop already syncs to audio master clock (from previous commit) - Master clock updates happen after each audio chunk write How it works: - Audio is the timing master, plays at natural rate - Video reads audio clock and adapts timing to stay in sync - If video >3 frames behind: drop frame and resync - If video >3 frames ahead: wait longer - Otherwise: adjust sleep duration for gradual sync Expected improvements: - Rock-solid A/V sync maintained over extended playback - No more drift between audio and video - Adaptive recovery from temporary slowdowns Combined with Priority 1 (larger audio buffers) and Priority 2 (FFmpeg volume control), this completes the core A/V sync architecture. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
4a09626e28
commit
a7b3452312
8
main.go
8
main.go
|
|
@ -9414,6 +9414,7 @@ func (p *playSession) runAudio(offset float64) {
|
|||
// Larger chunks reduce read frequency and improve performance
|
||||
chunk := make([]byte, 16384)
|
||||
loggedFirst := false
|
||||
bytesWritten := int64(0) // Track total audio bytes for master clock
|
||||
for {
|
||||
select {
|
||||
case <-p.stop:
|
||||
|
|
@ -9434,6 +9435,13 @@ func (p *playSession) runAudio(offset float64) {
|
|||
// Volume is now handled by FFmpeg, just write directly
|
||||
// This eliminates per-sample processing overhead
|
||||
localPlayer.Write(chunk[:n])
|
||||
|
||||
// Update audio master clock for A/V sync
|
||||
bytesWritten += int64(n)
|
||||
// Calculate elapsed audio time: bytes / (sampleRate * channels * bytesPerSample)
|
||||
elapsedTime := float64(bytesWritten) / float64(sampleRate*channels*bytesPerSample)
|
||||
currentAudioTime := offset + elapsedTime
|
||||
p.audioTime.Store(currentAudioTime)
|
||||
}
|
||||
if err != nil {
|
||||
if !errors.Is(err, io.EOF) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user