Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement unmarshaling of the "HandlerName" from HDLR atom in the QuickTime specs #4

Merged
merged 1 commit into from
Jul 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions hdlr.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package mp4

import "io"

func BoxTypeHdlr() BoxType { return StrToBoxType("hdlr") }

func init() {
Expand All @@ -19,3 +21,30 @@ type Hdlr struct {
func (*Hdlr) GetType() BoxType {
return BoxTypeHdlr()
}

const (
hdlrSizeWithoutName = 24 // FullBox header(4bytes) + PreDefined(4bytes) + HandlerType(4bytes) + Reserved(12bytes)
)

// handle a special case: the QuickTime files have a pascal
// string here, but ISO MP4 files have a C string.
// we try to detect a pascal encoding and correct it.
func (hdlr *Hdlr) unmarshalHandlerName(u *unmarshaller) error {
nameSize := u.size - hdlrSizeWithoutName
if nameSize <= 0 {
return nil
}
if _, err := u.reader.Seek(-int64(nameSize), io.SeekCurrent); err != nil {
return err
}
u.rbytes = hdlrSizeWithoutName
nameb := make([]byte, nameSize)
if _, err := io.ReadFull(u.reader, nameb); err != nil {
return err
}
u.rbytes += nameSize
if nameb[0] != 0x00 && nameb[0] == byte(nameSize-1) {
hdlr.Name = string(nameb[1:])
}
return nil
}
45 changes: 45 additions & 0 deletions hdlr_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package mp4

import (
"bytes"
"testing"

"github.com/stretchr/testify/assert"
)

func TestHdlrUnmarshalHandlerName(t *testing.T) {
testCases := []struct {
name string
bytes []byte
want string
}{
{name: "NormalString", bytes: []byte("abema"), want: "abema"},
{name: "EmptyString", bytes: nil, want: ""},
{name: "AppleQuickTimePascalString", bytes: []byte{5, 'a', 'b', 'e', 'm', 'a'}, want: "abema"},
{name: "AppleQuickTimePascalStringWithEmpty", bytes: []byte{0x00, 0x00}, want: ""},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
bin := []byte{
0, // version
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x00, // predefined
'v', 'i', 'd', 'e', // handler type
0x00, 0x00, 0x00, 0x00, // reserved
0x00, 0x00, 0x00, 0x00, // reserved
0x00, 0x00, 0x00, 0x00, // reserved
}
bin = append(bin, tc.bytes...)

// unmarshal
dst := Hdlr{}
r := bytes.NewReader(bin)
n, err := Unmarshal(r, uint64(len(bin)), &dst)
assert.NoError(t, err)
assert.Equal(t, uint64(len(bin)), n)
assert.Equal(t, [4]byte{'v', 'i', 'd', 'e'}, dst.HandlerType)
assert.Equal(t, tc.want, dst.Name)
})
}
}
8 changes: 8 additions & 0 deletions marshaller.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,14 @@ func Unmarshal(r io.ReadSeeker, payloadSize uint64, dst IBox) (n uint64, err err
return 0, fmt.Errorf("overrun error: type=%s, size=%d, readBytes=%d, width=%d", dst.GetType().String(), u.size, u.rbytes, u.width)
}

// for Apple Quick Time (hdlr handlerName)
if dst.GetType() == BoxTypeHdlr() {
hdlr := dst.(*Hdlr)
if err := hdlr.unmarshalHandlerName(u); err != nil {
return 0, err
}
}

// for Apple Quick Time
if dst.GetType() == BoxTypeMeta() && (dst.GetVersion() != 0 || dst.GetFlags() != 0) {
if _, err := r.Seek(-4, io.SeekCurrent); err != nil {
Expand Down