Skip to content

Commit

Permalink
Merge pull request #4 from castaneai/feature/hdlr-quicktime
Browse files Browse the repository at this point in the history
Implement unmarshaling of the "HandlerName" from HDLR atom in the QuickTime specs
  • Loading branch information
sunfish-shogi authored Jul 15, 2020
2 parents 9f03216 + f82a7c0 commit a050800
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 0 deletions.
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

0 comments on commit a050800

Please sign in to comment.