|
package main |
|
|
|
import ( |
|
"log" |
|
"os" |
|
"encoding/binary" |
|
"encoding/hex" |
|
"io" |
|
"fmt" |
|
) |
|
|
|
// atomName => padding size |
|
var boxAtomPaddings = map[string]int64{ |
|
"moov": 0, |
|
"trak": 0, |
|
"mdia": 0, |
|
"minf": 0, |
|
"stsd": 8, |
|
"stbl": 0, |
|
"mp4a": 28, |
|
} |
|
|
|
func main() { |
|
f, err := os.Open("a.m4a") |
|
if err != nil { |
|
log.Fatalln(err) |
|
} |
|
dumper := hex.Dumper(os.Stdout) |
|
defer dumper.Close() |
|
|
|
if _, err := readAllAtoms(f); err != nil { |
|
log.Fatalln(err) |
|
} |
|
} |
|
|
|
type Atom struct { |
|
Name string |
|
DataStartPos int64 |
|
DataLen int64 |
|
Children map[string]*Atom |
|
} |
|
|
|
func readAllAtoms(r io.ReadSeeker) (map[string]*Atom, error) { |
|
atoms := make(map[string]*Atom) |
|
for { |
|
atom, err := readAtom(r) |
|
if err != nil { |
|
if err != io.EOF { |
|
return nil, err |
|
} |
|
break |
|
} |
|
atoms[atom.Name] = atom |
|
} |
|
return atoms, nil |
|
} |
|
|
|
func readAtom(r io.ReadSeeker) (*Atom, error) { |
|
var length uint32 |
|
if err := binary.Read(r, binary.BigEndian, &length); err != nil { |
|
return nil, err |
|
} |
|
|
|
nameb := make([]byte, 4) |
|
if err := binary.Read(r, binary.BigEndian, &nameb); err != nil { |
|
return nil, err |
|
} |
|
name := string(nameb) |
|
|
|
startPos, err := r.Seek(0, io.SeekCurrent) |
|
if err != nil { |
|
return nil, err |
|
} |
|
dataLen := int64(length - 8) |
|
|
|
catoms := make(map[string]*Atom) |
|
pad, isBox := boxAtomPaddings[name] |
|
if isBox { |
|
r.Seek(pad, io.SeekCurrent) |
|
for { |
|
catom, err := readAtom(r) |
|
if err != nil { |
|
return nil, err |
|
} |
|
catoms[catom.Name] = catom |
|
|
|
cur, err := r.Seek(0, io.SeekCurrent) |
|
if err != nil { |
|
return nil, err |
|
} |
|
if cur >= startPos + dataLen { |
|
break |
|
} |
|
} |
|
} else { |
|
if _, err := r.Seek(dataLen, io.SeekCurrent); err != nil { |
|
return nil, err |
|
} |
|
} |
|
fmt.Printf("%s (start: %d, len: %d)\n", name, startPos, dataLen) |
|
|
|
return &Atom { |
|
Name: name, |
|
DataStartPos: startPos, |
|
DataLen: dataLen, |
|
Children: catoms, |
|
}, nil |
|
} |