diff --git a/mp4.go b/mp4.go index 4a4b142..3deed5d 100644 --- a/mp4.go +++ b/mp4.go @@ -6,6 +6,7 @@ package tag import ( "encoding/binary" + "errors" "fmt" "io" "os" @@ -69,9 +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 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 + ok := false err := binary.Read(r, binary.BigEndian, &size) if err != nil { if err == io.EOF { @@ -101,8 +155,25 @@ func (m metadataMP4) readAtoms(r io.ReadSeeker) error { return err } continue - case "mdat": // stop when we get to the data - return nil + case "mdat": // skip the data, the metadata can be at the end + _, err := r.Seek(int64(size-8), os.SEEK_CUR) + if err != nil { + return err + } + 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. + name, size, err = readCustomAtom(r, size) + if err != nil { + return err + } + ok = (name != "----") + default: + _, ok = atoms[name] } b, err := readBytes(r, int(size-8)) @@ -110,7 +181,7 @@ func (m metadataMP4) readAtoms(r io.ReadSeeker) error { return err } - _, ok := atoms[name] + // At this point, we allow all known atoms and the valid "----" atoms if !ok { continue }