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
|
// Larger chunks reduce read frequency and improve performance
|
||||||
chunk := make([]byte, 16384)
|
chunk := make([]byte, 16384)
|
||||||
loggedFirst := false
|
loggedFirst := false
|
||||||
|
bytesWritten := int64(0) // Track total audio bytes for master clock
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-p.stop:
|
case <-p.stop:
|
||||||
|
|
@ -9434,6 +9435,13 @@ func (p *playSession) runAudio(offset float64) {
|
||||||
// Volume is now handled by FFmpeg, just write directly
|
// Volume is now handled by FFmpeg, just write directly
|
||||||
// This eliminates per-sample processing overhead
|
// This eliminates per-sample processing overhead
|
||||||
localPlayer.Write(chunk[:n])
|
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 err != nil {
|
||||||
if !errors.Is(err, io.EOF) {
|
if !errors.Is(err, io.EOF) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user