diff --git a/CHANGELOG.md b/CHANGELOG.md index f826ebf1..51b27309 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 AudioSpecificConfig fields (object type including the 31-escape, base sampling frequency, channel configuration) for every audio object type, not just the AAC-LC and HE-AAC types the full decoder supports +- `ElstEntry.MediaRateFixed32` and `SetMediaRateFixed32` combine and split + the media rate halves as one signed 16.16 fixed-point number, so callers + no longer need to know the bit layout ### Changed diff --git a/mp4/elst.go b/mp4/elst.go index 2693ed1d..ba09a80b 100644 --- a/mp4/elst.go +++ b/mp4/elst.go @@ -23,6 +23,18 @@ type ElstEntry struct { MediaRateFraction int16 } +// MediaRateFixed32 - the media rate as one signed 16.16 fixed-point number (65536 is the normal rate 1.0). +// The return type is a plain int32 rather than Fixed32, since Fixed32 is unsigned and the media rate is signed. +func (e ElstEntry) MediaRateFixed32() int32 { + return int32(e.MediaRateInteger)<<16 | int32(uint16(e.MediaRateFraction)) +} + +// SetMediaRateFixed32 - set the media rate from a signed 16.16 fixed-point number. +func (e *ElstEntry) SetMediaRateFixed32(rate int32) { + e.MediaRateInteger = int16(rate >> 16) + e.MediaRateFraction = int16(uint16(rate)) +} + // DecodeElst - box-specific decode func DecodeElst(hdr BoxHeader, startPos uint64, r io.Reader) (Box, error) { data, err := readBoxBody(r, hdr) diff --git a/mp4/elst_test.go b/mp4/elst_test.go index 8d061c98..c2384b7f 100644 --- a/mp4/elst_test.go +++ b/mp4/elst_test.go @@ -29,3 +29,31 @@ func TestElst(t *testing.T) { boxDiffAfterEncodeAndDecode(t, elst) } } + +func TestElstMediaRateFixed32(t *testing.T) { + cases := []struct { + integer int16 + fraction int16 + fixed int32 + }{ + {1, 0, 1 << 16}, // rate 1.0 + {0, 0x4000, 0x4000}, // rate 0.25 + {2, -0x8000, 2<<16 | 0x8000}, // rate 2.5 (fraction bits 0x8000) + {-1, 0, -65536}, // rate -1.0 + {-1, -0x8000, -0x8000}, // rate -0.5 + {-2, -0x8000, int32(-2)<<16 | 0x8000}, // negative with fraction bits + {0x7fff, -1, 0x7fff<<16 | 0xffff}, // extremes + } + for _, c := range cases { + entry := mp4.ElstEntry{MediaRateInteger: c.integer, MediaRateFraction: c.fraction} + if got := entry.MediaRateFixed32(); got != c.fixed { + t.Errorf("(%d, %d): got %#x, wanted %#x", c.integer, c.fraction, got, c.fixed) + } + var back mp4.ElstEntry + back.SetMediaRateFixed32(c.fixed) + if back.MediaRateInteger != c.integer || back.MediaRateFraction != c.fraction { + t.Errorf("%#x: got (%d, %d), wanted (%d, %d)", + c.fixed, back.MediaRateInteger, back.MediaRateFraction, c.integer, c.fraction) + } + } +}