🏡 index : quine-global/quintodrome.git

package ffmpeg

import (
	"bytes"
	"encoding/binary"
	"errors"
	"io"
	"math"
)

const (
	flacPrefixLen       = 26 // through the last total_samples byte
	flacMaxTotalSamples = 1<<36 - 1
)

// patchFLACDuration fills in the STREAMINFO total_samples that ffmpeg leaves at 0
// when writing to a pipe, since a decoder cannot seek a cached FLAC without it.
func patchFLACDuration(r io.ReadCloser, duration float32) io.ReadCloser {
	if duration <= 0 {
		return r
	}
	return &flacPatcher{ReadCloser: r, duration: duration}
}

type flacPatcher struct {
	io.ReadCloser
	duration float32
	// Peeking here rather than in the constructor keeps Transcode from blocking
	// until ffmpeg has emitted its first bytes.
	stream io.Reader
}

func (f *flacPatcher) Read(p []byte) (int, error) {
	if f.stream == nil {
		prefix := make([]byte, flacPrefixLen)
		n, err := io.ReadFull(f.ReadCloser, prefix)
		if err != nil && !errors.Is(err, io.EOF) && !errors.Is(err, io.ErrUnexpectedEOF) {
			return 0, err
		}
		prefix = prefix[:n]
		if err == nil {
			setFLACTotalSamples(prefix, f.duration)
		}
		f.stream = io.MultiReader(bytes.NewReader(prefix), f.ReadCloser)
	}
	return f.stream.Read(p)
}

// setFLACTotalSamples takes the rate from the header rather than the transcode
// options, so a resampled (-ar) output still gets the right count.
func setFLACTotalSamples(prefix []byte, duration float32) {
	if string(prefix[:4]) != "fLaC" || prefix[4]&0x7F != 0 {
		return
	}
	// 20-bit rate | 3-bit channels | 5-bit depth | 36-bit total_samples
	info := binary.BigEndian.Uint64(prefix[18:])
	rate := info >> 44
	if rate == 0 || info&flacMaxTotalSamples != 0 {
		return
	}
	total := math.Round(float64(duration) * float64(rate))
	if total > flacMaxTotalSamples {
		return
	}
	binary.BigEndian.PutUint64(prefix[18:], info|uint64(total))
}