Skip to content

Commit

Permalink
fix: correct AVC PicTiming SEI offset
Browse files Browse the repository at this point in the history
  • Loading branch information
tobbee committed Dec 11, 2023
1 parent 3e5f9af commit 1461127
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed

- Parsing of AVCDecoderConfigurationRecord
- Parsing of time offset in AVC PicTiming SEI

## [0.40.2] - 2023-11-17

Expand Down
10 changes: 10 additions & 0 deletions bits/aereader.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ func (r *AccErrReader) Read(n int) uint {
return value
}

// ReadSigned reads a 2-complemented signed int with n bits.
func (r *AccErrReader) ReadSigned(n int) int {
nr := int(r.Read(n))
firstBit := nr >> (n - 1)
if firstBit == 1 {
nr |= -1 << n
}
return nr
}

// ReadFlag - read 1 bit into flag. Return false if error now or previously
func (r *AccErrReader) ReadFlag() bool {
bit := r.Read(1)
Expand Down
29 changes: 29 additions & 0 deletions bits/aereader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,35 @@ func TestAccErrReader(t *testing.T) {
}
}

func TestAccErrReaderSigned(t *testing.T) {
input := []byte{0xff, 0x0c} // 1111 1111 0000 1100
rd := bytes.NewReader(input)
reader := NewAccErrReader(rd)

cases := []struct {
n int
want int
}{
{2, -1}, // 11
{3, -1}, // 111
{5, -4}, // 11100
{3, 1}, // 001
{3, -4}, // 100
}

for _, tc := range cases {
got := reader.ReadSigned(tc.n)

if got != tc.want {
t.Errorf("Read(%d)=%b, want=%b", tc.n, got, tc.want)
}
}
err := reader.AccError()
if err != nil {
t.Errorf("Got accumulated error: %s", err.Error())
}
}

func TestBadAccErrReader(t *testing.T) {
// Check that reading beyond EOF provides value = 0 after acc error
input := []byte{0xff, 0x0f} // 1111 1111 0000 1111
Expand Down
3 changes: 2 additions & 1 deletion sei/sei.go
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,8 @@ func WriteSEIMessages(w io.Writer, msgs []SEIMessage) error {
for _, msg := range msgs {
bw.WriteSEIValue(msg.Type())
bw.WriteSEIValue(msg.Size())
for _, b := range msg.Payload() {
pl := msg.Payload()
for _, b := range pl {
bw.Write(uint(b), 8)
}
}
Expand Down
5 changes: 3 additions & 2 deletions sei/sei1.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ func DecodePicTimingAvcSEIHRD(sd *SEIData, cbpDbpDelay *CbpDbpDelay, timeOffsetL
c := DecodeClockTSAvc(br, timeOffsetLen)
tc.Clocks = append(tc.Clocks, c)
}
tc.TimeOffsetLength = timeOffsetLen
return &tc, br.AccError()
}

Expand Down Expand Up @@ -186,7 +187,7 @@ func DecodeClockTSAvc(br *bits.AccErrReader, timeOffsetLen byte) ClockTSAvc {
}
}
if c.TimeOffsetLength > 0 {
c.TimeOffsetValue = int(c.TimeOffsetLength)
c.TimeOffsetValue = br.ReadSigned(int(c.TimeOffsetLength))
}
}
return c
Expand Down Expand Up @@ -246,7 +247,7 @@ func (c ClockTSAvc) WriteToSliceWriter(sw bits.SliceWriter) {
}
}
if c.TimeOffsetLength > 0 {
sw.WriteBits(uint(c.TimeOffsetLength), int(c.TimeOffsetLength))
sw.WriteBits(uint(c.TimeOffsetValue), int(c.TimeOffsetLength))
}
}
}
30 changes: 20 additions & 10 deletions sei/sei_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ func TestParseSEI(t *testing.T) {
},
[]uint{1},
[]string{
`SEIPicTimingType (1), size=15, time=20:40:09:46 offset=24`,
`SEIPicTimingType (1), size=15, time=20:40:09:46 offset=0`,
},
nil,
},
Expand Down Expand Up @@ -425,55 +425,65 @@ func TestWriteSEI(t *testing.T) {
}

func TestParseAVCPicTimingWithHRD(t *testing.T) {
sei1AVCEbsp := "010d00000300000300000300021208114de180"
sei1AVCEbsp := "011000000300000300000300021208114de10000030080"
cbpDelay := sei.CbpDbpDelay{
CpbRemovalDelay: 0,
DpbOutputDelay: 0,
InitialCpbRemovalDelayLengthMinus1: 26,
CpbRemovalDelayLengthMinus1: 30,
DpbOutputDelayLengthMinus1: 31,
}
timeOffsetLen := byte(0)
timeOffsetLen := byte(24)

testCases := []struct {
name string
codec sei.Codec
naluHex string
naluPayloadHex string
wantedTypes []uint
wantedStrings []string
expNonFatalErr error
}{
{"PicTimingWithHRD", sei.AVC, sei1AVCEbsp, []uint{1},
[]string{
`SEIPicTimingType (1), size=13, time=01:47:41:08 offset=0`,
`SEIPicTimingType (1), size=16, time=01:47:41:08 offset=0`,
},
sei.ErrRbspTrailingBitsMissing,
},
}

for _, tc := range testCases {
seiNALU, _ := hex.DecodeString(tc.naluHex)

rs := bytes.NewReader(seiNALU)

seis, err := sei.ExtractSEIData(rs)
seiNaluPayload, _ := hex.DecodeString(tc.naluPayloadHex)
r := bytes.NewReader(seiNaluPayload)
seis, err := sei.ExtractSEIData(r)
if err != nil && err != tc.expNonFatalErr {
t.Error(err)
}
if len(seis) != len(tc.wantedStrings) {
t.Errorf("%s: Not %d but %d sei messages found", tc.name, len(tc.wantedStrings), len(seis))
}
var messages []sei.SEIMessage
for i := range seis {
seiMessage, err := sei.DecodePicTimingAvcSEIHRD(&seis[i], &cbpDelay, timeOffsetLen)
if err != nil {
t.Error(err)
}
messages = append(messages, seiMessage)
if seiMessage.Type() != tc.wantedTypes[i] {
t.Errorf("%s: got SEI type %d instead of %d", tc.name, seiMessage.Type(), tc.wantedTypes[i])
}
if seiMessage.String() != tc.wantedStrings[i] {
t.Errorf("%s: got %q instead of %q", tc.name, seiMessage.String(), tc.wantedStrings[i])
}
}
buf := bytes.Buffer{}
err = sei.WriteSEIMessages(&buf, messages)
if err != nil {
t.Error(err)
}
output := buf.Bytes()
if !bytes.Equal(output, seiNaluPayload) {
t.Errorf("%s: wanted %s but got %s", tc.name,
tc.naluPayloadHex, hex.EncodeToString(output))
}
}
}

0 comments on commit 1461127

Please sign in to comment.