ISOBMFF/MP4 container library with fragmented MP4 support.
go get github.com/tetsuo/mp4Scanner walks the top-level boxes of a file without loading their contents.
Load only the boxes you need, then use Reader to descend into a box and read
typed fields.
f, _ := os.Open("video.mp4")
defer f.Close()
sc := mp4.NewScanner(f)
for sc.Next() {
e := sc.Entry()
fmt.Printf("%s size=%d offset=%d\n", e.Type, e.Size, e.Offset)
if e.Type == mp4.TypeMoov {
buf := make([]byte, e.DataSize())
if err := sc.ReadBody(buf); err != nil {
log.Fatal(err)
}
r := mp4.NewReader(buf)
r.Enter() // descend into moov
for r.Next() {
if r.Type() == mp4.TypeMvhd {
timescale, duration, _ := r.ReadMvhd()
fmt.Printf("timescale=%d duration=%d\n", timescale, duration)
}
}
r.Exit()
}
}
if err := sc.Err(); err != nil {
log.Fatal(err)
}Writer encodes boxes into a caller-provided buffer. StartBox and EndBox
handle size backpatching for nested boxes:
buf := make([]byte, 0, 1024)
w := mp4.NewWriter(buf)
w.WriteFtyp([4]byte{'i', 's', 'o', 'm'}, 0, [][4]byte{{'m', 'p', '4', '2'}})
w.StartBox(mp4.TypeMoov)
w.WriteMvhd(1000, 30000, 3)
w.EndBox()
if err := w.Err(); err != nil {
log.Fatal(err)
}
output := w.Bytes()The track package resolves the sample tables inside a moov box into a flat
list of samples. Each sample carries its byte offset, size, decode and
presentation timestamps, and sync (keyframe) flag.
tracks, duration, err := track.ParseTracks(moovBuf)
if err != nil {
log.Fatal(err)
}
for _, t := range tracks {
fmt.Printf("track %d: codec=%s samples=%d\n", t.ID, t.Codec(), len(t.Samples))
for _, s := range t.Samples {
_ = s.Offset // byte offset in the file
_ = s.Size() // sample size in bytes
_ = s.PTS() // presentation timestamp
_ = s.IsSync() // keyframe
}
}The fragment package reads a standard MP4 file and produces an init segment
(ftyp+moov) followed by media fragments (moof+mdat pairs), suitable for
HLS or DASH.
f, _ := os.Open("video.mp4")
defer f.Close()
reader, initSeg, err := fragment.NewReader(f)
if err != nil {
log.Fatal(err)
}
writer := fragment.NewWriter(os.Stdout)
if err := writer.WriteInit(initSeg); err != nil {
log.Fatal(err)
}
for {
frag, err := reader.ReadFragment()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
if err := writer.WriteFragment(frag, f); err != nil {
log.Fatal(err)
}
}Use Reader.SetTimeRange to limit output to a time window, or Reader.Seek to
reposition before reading fragments.
The cmd directory contains small programs built on these packages:
mp4dumpprints the box structure of a file as text or JSON.mp4probedisplays track and fragment information.mp4remuxremuxes a file into fragmented MP4.
goos: darwin
goarch: arm64
pkg: github.com/tetsuo/mp4
cpu: Apple M4 Pro
BenchmarkReaderParse
BenchmarkReaderParse-12 3547731 317.4 ns/op 58353.33 MB/s 0 B/op 0 allocs/op
BenchmarkStszIter
BenchmarkStszIter-12 897819 1230.0 ns/op 2348.81 MB/s 0 B/op 0 allocs/op
BenchmarkWriterBuild
BenchmarkWriterBuild-12 15768681 76.26 ns/op 0 B/op 0 allocs/op
BenchmarkScannerParse
BenchmarkScannerParse-12 343438 3512.0 ns/op 5274.38 MB/s 0 B/op 0 allocs/op
PASS
ok github.com/tetsuo/mp4 5.023s