fixed the synchronized-audio corruption

This commit is contained in:
Dmitry Sergeev
2026-09-01 01:04:40 +03:00
parent 19e2e076e9
commit e830da4a32
5 changed files with 153 additions and 642 deletions
+38 -17
View File
@@ -598,8 +598,10 @@ func (s *SyncSource) Close() error {
return s.inst.Close()
}
// NextSync reads both at a synced timestamp. Returns video Frame + audio AudioFrame
func (s *SyncSource) NextSync(ctx context.Context, audioBatch uint64, timeout time.Duration) (Frame, AudioFrame, error) {
// NextSync reads one video frame and the audio interval between this video
// timestamp and the next. Deriving the interval for every frame preserves
// exact long-term timing for fractional video rates.
func (s *SyncSource) NextSync(ctx context.Context, timeout time.Duration) (Frame, AudioFrame, error) {
var timeouts int
for {
select {
@@ -635,7 +637,30 @@ func (s *SyncSource) NextSync(ctx context.Context, audioBatch uint64, timeout ti
}
// read audio at the same timestamp
aIdx := mxl.TimestampToIndex(s.aRate, ts)
nextTimestamp := mxl.IndexToTimestamp(s.rate, s.idx+1)
nextAudioIndex := mxl.TimestampToIndex(s.aRate, nextTimestamp)
if nextAudioIndex <= aIdx {
return Frame{}, AudioFrame{}, wrapError(
"calculate synchronized audio interval",
ErrorKindInvalidConfig,
fmt.Errorf("invalid audio interval: %d..%d", aIdx, nextAudioIndex),
)
}
audioBatch := nextAudioIndex - aIdx
av, aerr := s.ar.GetSamples(aIdx, int(audioBatch), 50*time.Millisecond)
if aerr != nil {
kind := ErrorKindUnavailable
if errors.Is(aerr, mxl.ErrTimeout) ||
errors.Is(aerr, mxl.ErrOutOfRangeEarly) ||
errors.Is(aerr, mxl.ErrOutOfRangeLate) {
kind = ErrorKindTemporary
}
return Frame{}, AudioFrame{}, wrapError(
"read synchronized audio",
kind,
aerr,
)
}
vFrame := Frame{
Index: g.Index, Width: s.width, Height: s.height,
Stride: s.stride, Size: g.GrainSize,
@@ -643,23 +668,19 @@ func (s *SyncSource) NextSync(ctx context.Context, audioBatch uint64, timeout ti
}
s.idx++
var aFrame AudioFrame
if aerr == nil {
samples := make([][]byte, s.chans)
for ch := uint64(0); ch < s.chans; ch++ {
f1, f2, _ := av.ChannelFragments(ch)
if len(f2) > 0 {
samples[ch] = append(f1, f2...)
} else {
samples[ch] = f1
}
}
aFrame = AudioFrame{
Index: aIdx, SampleCount: audioBatch,
Channels: s.chans, Samples: samples,
samples := make([][]byte, s.chans)
for ch := uint64(0); ch < s.chans; ch++ {
f1, f2, _ := av.ChannelFragments(ch)
if len(f2) > 0 {
samples[ch] = append(f1, f2...)
} else {
samples[ch] = f1
}
}
// even if audio failed, video returns
aFrame := AudioFrame{
Index: aIdx, SampleCount: audioBatch,
Channels: s.chans, Samples: samples,
}
return vFrame, aFrame, nil
case errors.Is(err, mxl.ErrTimeout), errors.Is(err, mxl.ErrOutOfRangeEarly), errors.Is(err, mxl.ErrOutOfRangeLate):
timeouts++