package ffmpeg import ( "bytes" "errors" "io" "os" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) // Decoded independently so the specs do not mirror the production bit-twiddling. func readSampleRate(b []byte) int { return int(b[18])<<12 | int(b[19])<<4 | int(b[20])>>4 } func readTotalSamples(b []byte) uint64 { return uint64(b[21]&0x0F)<<32 | uint64(b[22])<<24 | uint64(b[23])<<16 | uint64(b[24])<<8 | uint64(b[25]) } var _ = Describe("patchFLACDuration", func() { var fileFLAC []byte // Zeroing total_samples reproduces what a piped transcode emits. pipedFLAC := func() []byte { b := bytes.Clone(fileFLAC) b[21] &= 0xF0 clear(b[22:26]) return b } readAll := func(in []byte, duration float32) []byte { out, err := io.ReadAll(patchFLACDuration(io.NopCloser(bytes.NewReader(in)), duration)) Expect(err).ToNot(HaveOccurred()) return out } BeforeEach(func() { var err error fileFLAC, err = os.ReadFile("tests/fixtures/test.flac") Expect(err).ToNot(HaveOccurred()) Expect(readSampleRate(fileFLAC)).To(Equal(44100)) // specs below hard-code this rate }) It("fills in total_samples from the duration", func() { out := readAll(pipedFLAC(), 1.0) Expect(readTotalSamples(out)).To(Equal(uint64(44100))) }) It("takes the sample rate from the header, not from the source file", func() { in := pipedFLAC() // Rewrite the header's rate to 48000, as -ar would. in[18], in[19] = 0x0B, 0xB8 in[20] &= 0x0F out := readAll(in, 2.0) Expect(readSampleRate(out)).To(Equal(48000)) Expect(readTotalSamples(out)).To(Equal(uint64(96000))) }) It("rounds to the nearest sample rather than truncating", func() { // float32(0.7)*44100 is 30869.9995, so truncation would lose a sample. out := readAll(pipedFLAC(), 0.7) Expect(readTotalSamples(out)).To(Equal(uint64(30870))) }) It("passes through when the duration overflows the 36-bit field", func() { in := pipedFLAC() Expect(readAll(in, 2e6)).To(Equal(in)) }) It("leaves everything after the header untouched", func() { in := pipedFLAC() out := readAll(in, 1.0) Expect(out).To(HaveLen(len(in))) Expect(out[26:]).To(Equal(in[26:])) Expect(out[:18]).To(Equal(in[:18])) }) It("leaves an already-populated total_samples alone", func() { out := readAll(fileFLAC, 99.0) Expect(out).To(Equal(fileFLAC)) }) It("passes through a stream that is not FLAC", func() { in := []byte("ID3\x04\x00\x00\x00\x00\x00\x00 not a flac stream at all, just bytes") Expect(readAll(in, 1.0)).To(Equal(in)) }) It("passes through when the first metadata block is not STREAMINFO", func() { in := pipedFLAC() in[4] = 0x04 // VORBIS_COMMENT Expect(readAll(in, 1.0)).To(Equal(in)) }) It("passes through a stream shorter than the STREAMINFO fields it patches", func() { in := pipedFLAC()[:20] Expect(readAll(in, 1.0)).To(Equal(in)) }) It("passes through an empty stream", func() { Expect(readAll(nil, 1.0)).To(BeEmpty()) }) It("passes through when the duration is zero or negative", func() { in := pipedFLAC() Expect(readAll(in, 0)).To(Equal(in)) Expect(readAll(in, -5)).To(Equal(in)) }) It("passes through when the header declares no sample rate", func() { in := pipedFLAC() in[18], in[19] = 0, 0 in[20] &= 0x0F Expect(readAll(in, 1.0)).To(Equal(in)) }) It("propagates a read error from the underlying stream", func() { _, err := io.ReadAll(patchFLACDuration(io.NopCloser(io.MultiReader( bytes.NewReader(pipedFLAC()[:10]), &errReader{})), 1.0)) Expect(err).To(MatchError("boom")) }) It("closes the underlying stream", func() { c := &closeSpy{Reader: bytes.NewReader(pipedFLAC())} Expect(patchFLACDuration(c, 1.0).Close()).To(Succeed()) Expect(c.closed).To(BeTrue()) }) }) type errReader struct{} func (e *errReader) Read([]byte) (int, error) { return 0, errors.New("boom") } type closeSpy struct { io.Reader closed bool } func (c *closeSpy) Close() error { c.closed = true; return nil }