diff --git a/mp4.go b/mp4.go index 641bfe7..6acadc9 100644 --- a/mp4.go +++ b/mp4.go @@ -6,6 +6,7 @@ package tag import ( "encoding/binary" + "errors" "fmt" "io" "os" @@ -69,10 +70,62 @@ func ReadAtoms(r io.ReadSeeker) (Metadata, error) { return m, err } +func readCustomAtom(r io.ReadSeeker, size uint32) (string, uint32, error) { + var datasize uint32 + var datapos int64 + var name string + var mean string + + for size > 8 { + var subsize uint32 + err := binary.Read(r, binary.BigEndian, &subsize) + if err != nil { + return "----", size - 4, err + } + subname, err := readString(r, 4) + if err != nil { + return "----", size - 8, err + } + b, err := readBytes(r, int(subsize-8)) + if err != nil { + return "----", size - subsize, err + } + // Remove the size of the mean atom from the size counter + size -= subsize + + switch string(subname) { + case "mean": + mean = string(b[4:]) + case "name": + name = string(b[4:]) + case "data": + datapos, err = r.Seek(0, os.SEEK_CUR) + if err != nil { + return "----", size, err + } + datasize = subsize + datapos -= int64(subsize) + } + } + // there should remain only the header size + if size != 8 { + return "----", size, errors.New("---- atom out of bound") + } + if mean == "com.apple.iTunes" && datasize != 0 && name != "" { + // we jump just before the data subatom + _, err := r.Seek(datapos, os.SEEK_SET) + if err != nil { + return "----", size, err + } + return name, datasize + 8, nil + } + return "----", size, nil +} + func (m metadataMP4) readAtoms(r io.ReadSeeker) error { for { var size uint32 - var subname string + ok := false err := binary.Read(r, binary.BigEndian, &size) if err != nil { if err == io.EOF { @@ -109,69 +162,20 @@ func (m metadataMP4) readAtoms(r io.ReadSeeker) error { } continue case "----": - /* Generic atom. - Should have 3 sub atoms : mean, name and data. - We check that mean=="com.apple.iTunes" and we use the subname as - the name, and move to the data atom if anything goes wrong, we jump - at the end of the "----" atom. */ - - // let's read the mean atom - var subsize uint32 - err := binary.Read(r, binary.BigEndian, &subsize) + // Generic atom. + // Should have 3 sub atoms : mean, name and data. + // We check that mean=="com.apple.iTunes" and we use the subname as + // the name, and move to the data atom if anything goes wrong, + // we jump at the end of the "----" atom. + name, size, err = readCustomAtom(r, size) if err != nil { return err } - sub, err := readString(r, 4) - if err != nil { - return err + if name != "----" { + ok = true } - - if sub != "mean" { - // Something's wrong. Remove 8 read bytes from the size counter - // since "----" is not a known atom name, the whole data will - // be skipped - size -= 8 - break - } - - mean, err := readBytes(r, int(subsize-8)) - if err != nil { - return err - } - // Remove the size of the mean atom from the size counter - size -= subsize - - if string(mean[4:]) != "com.apple.iTunes" { - // Something's wrong, skip this atom - break - } - - // Let's read the name atom - err = binary.Read(r, binary.BigEndian, &subsize) - if err != nil { - return err - } - sub, err = readString(r, 4) - if err != nil { - return err - } - - if sub != "name" { - // Something's wrong - size -= 8 - break - } - - b, err := readBytes(r, int(subsize-8)) - if err != nil { - return err - } - /* Remove the size of the name atom from the size counter. - We should now be at the start of the data subatom and size should - be equal to the size of the data atom and its header */ - size -= subsize - - subname = string(b[4:]) + default: + _, ok = atoms[name] } b, err := readBytes(r, int(size-8)) @@ -179,14 +183,8 @@ func (m metadataMP4) readAtoms(r io.ReadSeeker) error { return err } - // Allow all known atoms and the valid "----" atoms - _, ok := atoms[name] - switch { - case name == "----" && subname == "": - continue - case name == "----": - name = subname - case !ok: + // At this point, we allow all known atoms and the valid "----" atoms + if !ok { continue }