Skip to content

Commit

Permalink
fix: elng should be full box
Browse files Browse the repository at this point in the history
The elng box was errononously not reading/writing full box headers.
This is now changed, but one can still read the old errononous type.
  • Loading branch information
tobbee committed Jul 7, 2024
1 parent 49da44b commit efcb4f1
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed

- Overflow in calculating sample decode time
- elng box errononously did not include full box headers

## [0.45.0] - 2024-06-06

Expand Down
12 changes: 10 additions & 2 deletions mp4/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,11 @@ func DecodeContainerChildren(hdr BoxHeader, startPos, endPos uint64, r io.Reader
if pos == endPos {
return children, nil
} else if pos > endPos {
return nil, fmt.Errorf("non-matching children box sizes")
msg := ""
for _, c := range children {
msg += fmt.Sprintf("%s:%d ", c.Type(), c.Size())
}
return nil, fmt.Errorf("non-matching children box sizes, parentSize=%d, %s", endPos-startPos, msg)
}
}
}
Expand All @@ -123,7 +127,11 @@ func DecodeContainerChildrenSR(hdr BoxHeader, startPos, endPos uint64, sr bits.S
initPos := sr.GetPos()
for {
if pos > endPos {
return nil, fmt.Errorf("non matching children box sizes")
msg := ""
for _, c := range children {
msg += fmt.Sprintf("%s:%d ", c.Type(), c.Size())
}
return nil, fmt.Errorf("non-matching children box sizes, parentSize=%d, %s", endPos-startPos, msg)
}
if pos == endPos {
break
Expand Down
61 changes: 50 additions & 11 deletions mp4/elng.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,36 @@
package mp4

import (
"fmt"
"io"

"github.com/Eyevinn/mp4ff/bits"
)

// ElngBox - Extended Language Box
// Defined in ISO/IEC 14496-12 Section 8.4.6
// It should be a full box, but was erronously implemented
// as a normal box. For backwards compatibility, the
// erronous box without full header can still be decoded.
// The method MissingFullBoxBytes() returns true if that is the case.
type ElngBox struct {
Language string
missingFullBox bool
Version byte
Flags uint32
Language string
}

// MissingFullBoxBytes indicates that the box is errornously not including the 4 full box header bytes
func (b *ElngBox) MissingFullBoxBytes() bool {
return b.missingFullBox
}

// CreateElng - Create an Extended Language Box
func CreateElng(language string) *ElngBox {
return &ElngBox{Language: language}
return &ElngBox{
Version: 0,
Flags: 0,
Language: language}
}

// DecodeElng - box-specific decode
Expand All @@ -22,18 +39,27 @@ func DecodeElng(hdr BoxHeader, startPos uint64, r io.Reader) (Box, error) {
if err != nil {
return nil, err
}
b := &ElngBox{
Language: string(data[:len(data)-1]),
}
return b, nil
sr := bits.NewFixedSliceReader(data)
return DecodeElngSR(hdr, startPos, sr)
}

// DecodeElngSR - box-specific decode
func DecodeElngSR(hdr BoxHeader, startPos uint64, sr bits.SliceReader) (Box, error) {
b := &ElngBox{
Language: string(sr.ReadZeroTerminatedString(hdr.payloadLen())),
b := ElngBox{}
plLen := hdr.payloadLen()
if plLen < 7 { // Less than 4 byte flag and version + 2 letters + 0 termination
b.missingFullBox = true
b.Language = string(sr.ReadZeroTerminatedString(plLen))
return &b, nil
}
versionAndFlags := sr.ReadUint32()
if versionAndFlags != 0 {
return nil, fmt.Errorf("version and flags are not zero")
}
return b, sr.AccError()
b.Version = byte(versionAndFlags >> 24)
b.Flags = versionAndFlags & 0xffffff
b.Language = string(sr.ReadZeroTerminatedString(plLen - 4))
return &b, sr.AccError()
}

// Type - box type
Expand All @@ -43,7 +69,16 @@ func (b *ElngBox) Type() string {

// Size - calculated size of box
func (b *ElngBox) Size() uint64 {
return uint64(boxHeaderSize + len(b.Language) + 1)
size := uint64(boxHeaderSize + 4 + len(b.Language) + 1)
if b.missingFullBox {
size -= 4
}
return size
}

// FixMissingFullBoxBytes adds missing bytes version and flags bytes.
func (b *ElngBox) FixMissingFullBoxBytes() {
b.missingFullBox = false
}

// Encode - write box to w
Expand All @@ -63,13 +98,17 @@ func (b *ElngBox) EncodeSW(sw bits.SliceWriter) error {
if err != nil {
return err
}
if !b.missingFullBox {
versionAndFlags := uint32(b.Version)<<24 | b.Flags
sw.WriteUint32(versionAndFlags)
}
sw.WriteString(b.Language, true)
return sw.AccError()
}

// Info - write box-specific information
func (b *ElngBox) Info(w io.Writer, specificBoxLevels, indent, indentStep string) error {
bd := newInfoDumper(w, indent, b, -1, 0)
bd := newInfoDumper(w, indent, b, int(b.Version), b.Flags)
bd.write(" - language: %s", b.Language)
return bd.err
}
46 changes: 46 additions & 0 deletions mp4/elng_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package mp4

import (
"bytes"
"testing"
)

Expand All @@ -9,3 +10,48 @@ func TestDecodeElng(t *testing.T) {
elng := &ElngBox{Language: "en-US"}
boxDiffAfterEncodeAndDecode(t, elng)
}

// TestElngWithoutFullBox tests erronous case where full box headers are not present.
func TestElngWithoutFullBox(t *testing.T) {
data := []byte("\x00\x00\x00\x0belngdk\x00")
bufIn := bytes.NewBuffer(data)
box, err := DecodeBox(0, bufIn)
if err != nil {
t.Errorf("could not decode elng")
}
elng := box.(*ElngBox)
if !elng.MissingFullBoxBytes() {
t.Errorf("missing full box not set")
}
bufOut := bytes.Buffer{}
err = elng.Encode(&bufOut)
if err != nil {
t.Errorf("error encoding elng")
}
if !bytes.Equal(bufOut.Bytes(), data) {
t.Errorf("encoded elng differs from input")
}
}

func TestFixElngMissingFullBoxBytes(t *testing.T) {
dataIn := []byte("\x00\x00\x00\x0belngdk\x00")
dataOut := []byte("\x00\x00\x00\x0felng\x00\x00\x00\x00dk\x00")
bufIn := bytes.NewBuffer(dataIn)
box, err := DecodeBox(0, bufIn)
if err != nil {
t.Errorf("could not decode elng")
}
elng := box.(*ElngBox)
if !elng.MissingFullBoxBytes() {
t.Errorf("missing full box not set")
}
outBuf := bytes.Buffer{}
elng.FixMissingFullBoxBytes()
err = elng.Encode(&outBuf)
if err != nil {
t.Errorf("error encoding elng")
}
if !bytes.Equal(outBuf.Bytes(), dataOut) {
t.Errorf("encoded elng differs from input")
}
}

0 comments on commit efcb4f1

Please sign in to comment.