Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package util
- import (
- "encoding/json"
- "fmt"
- "math"
- "os"
- "strings"
- "time"
- )
- func Dump(filepath string) {
- file, _ := os.Open(filepath)
- defer file.Close()
- mp4Box := NewEmptyBox("mp4", 0, math.MaxInt64, file)
- mp4Box.readChildBoxes()
- fmt.Print(mp4Box)
- }
- func (b *BoxReader) readChildBoxes() {
- child := b.readChildBox()
- for child != nil {
- switch child.name {
- case "ftyp":
- child.readFileTypeBox()
- case "moov":
- child.readChildBoxes()
- case "trak":
- child.readChildBoxes()
- case "mvhd":
- child.readMovieHeaderBox()
- case "tkhd":
- child.readTrackHeaderBox()
- case "mdia":
- child.readChildBoxes()
- case "mdhd":
- child.readMediaHeaderBox()
- case "hdlr":
- child.readHandlerReferenceBox()
- case "minf":
- child.readChildBoxes()
- case "stbl":
- child.readChildBoxes()
- case "stsd":
- child.readSampleDescriptionBox()
- case "stsz":
- child.readSampleSizeBox()
- case "stts":
- child.readSampleTableBox()
- case "udta":
- child.readChildBoxes()
- case "dinf":
- child.readChildBoxes()
- case "dref":
- child.readDataReferenceBox()
- case "meta":
- child.readMetaBox()
- }
- child = b.readChildBox()
- }
- }
- var byteCount int64
- type BoxReader struct {
- name string
- start int64
- length int64
- bytes []byte
- pos int64
- data map[string]interface{}
- version int64
- parent *BoxReader
- children []*BoxReader
- file *os.File
- }
- func NewEmptyBox(name string, start, length int64, file *os.File) *BoxReader {
- byteCount = 8
- return &BoxReader{
- name: name,
- start: start,
- length: length,
- children: []*BoxReader{},
- data: map[string]interface{}{},
- file: file,
- }
- }
- func (b *BoxReader) readChildBox() *BoxReader {
- var child *BoxReader
- if b.pos < (b.start + b.length) {
- buffer := make([]byte, 8)
- _, err := b.file.ReadAt(buffer, b.pos)
- if err == nil {
- child = NewEmptyBox(string(buffer[4:]), b.pos, convertBytesToInt(buffer[0:4]), b.file)
- child.parent = b
- child.pos = b.pos + 8
- b.children = append(b.children, child)
- b.pos += child.length
- }
- }
- return child
- }
- func (b *BoxReader) readSampleEntryBox() *BoxReader {
- length := b.readInt("length", 4)
- name := b.readString("name", 4)
- parentPos := b.pos - 8
- bytes := b.bytes[parentPos : parentPos+length]
- sampleEntry := &BoxReader{
- name: name,
- start: b.start + parentPos,
- pos: 8,
- length: length,
- bytes: bytes,
- parent: b,
- children: []*BoxReader{},
- data: map[string]interface{}{},
- }
- b.pos += length
- b.children = append(b.children, sampleEntry)
- return sampleEntry
- }
- func (b *BoxReader) readBytes() {
- if b.bytes == nil {
- b.bytes = make([]byte, b.length)
- b.file.ReadAt(b.bytes, b.start)
- b.pos = 8
- }
- }
- func (b *BoxReader) readFullBox() {
- b.version = b.readInt("version", 1)
- b.readInt("flags", 3)
- }
- func (b *BoxReader) ReadFullDateBox() {
- b.readFullBox()
- if b.version == 0 {
- b.readDate("created", 4)
- b.readDate("modified", 4)
- } else {
- b.readDate("created", 8)
- b.readDate("modified", 8)
- }
- }
- func (b *BoxReader) readFileTypeBox() {
- b.readBytes()
- b.readString("majorBrand", 4)
- b.readInt("minorVersion", 4)
- b.readString("compatibleBrands", b.length-b.pos)
- }
- // mvhd
- func (b *BoxReader) readMovieHeaderBox() {
- b.readBytes()
- b.ReadFullDateBox()
- b.readInt("timeScale", 4)
- b.readInt("duration", 4)
- b.readInt("preferredRate", 4)
- b.readInt("preferredVolume", 2)
- b.readNil("reserved", 10)
- b.readNil("matrix", 36)
- b.readInt("previewTime", 4)
- b.readInt("previewDuration", 4)
- b.readInt("posterTime", 4)
- b.readInt("selectionTime", 4)
- b.readInt("selectionDuration", 4)
- b.readInt("currentTime", 4)
- b.readInt("nextTrackID", 4)
- duration := (time.Second * time.Duration(b.data["duration"].(int64)/b.data["timeScale"].(int64)))
- b.data["durationSec"] = duration.Seconds()
- }
- // trak
- func (b *BoxReader) readTrackHeaderBox() {
- b.readBytes()
- b.ReadFullDateBox()
- if b.version == 0 {
- b.readInt("tid", 4)
- b.readNil("reserved1", 4)
- b.readInt("duration", 4)
- } else {
- b.readInt("tid", 4)
- b.readNil("reserved1", 4)
- b.readInt("duration", 8)
- }
- b.readNil("reserved2", 8)
- b.readInt("layer", 2)
- b.readInt("altGroup", 2)
- b.readInt("volume", 2)
- b.readNil("reserved3", 2) // If you don't read this then the width and height are correct
- b.readNil("matrix", 36)
- b.readInt("width", 4)
- b.readInt("height", 4)
- }
- // mdhd
- func (b *BoxReader) readMediaHeaderBox() {
- b.readBytes()
- b.ReadFullDateBox()
- if b.version == 0 {
- b.readInt("timeScale", 4)
- b.readInt("duration", 4)
- } else {
- b.readInt("timeScale", 4)
- b.readInt("duration", 8)
- }
- b.readNil("language", 2)
- b.readNil("preDefined", 2)
- }
- // hdlr
- func (b *BoxReader) readHandlerReferenceBox() {
- b.readBytes()
- b.readFullBox()
- b.readNil("predefined", 4)
- b.readString("handlerType", 4)
- b.readNil("reserved", 12)
- b.ReadTerminatedString("name")
- b.parent.parent.data["handlerType"] = b.data["handlerType"]
- }
- // stsd
- func (b *BoxReader) readSampleDescriptionBox() {
- b.readBytes()
- b.readFullBox()
- entryCount := int(b.readInt("entryCount", 4))
- for i := 0; i < entryCount; i++ {
- entryBox := b.readSampleEntryBox()
- switch b.parent.parent.parent.parent.data["handlerType"] {
- case "soun":
- entryBox.readNil("reserved1", 8)
- entryBox.readInt("channelcount", 2)
- entryBox.readInt("sampleSize", 2)
- entryBox.readNil("predefined1", 2)
- entryBox.readNil("reserved2", 2)
- entryBox.readInt("sampleRate", 4) // timescale of media << 16
- case "vide":
- entryBox.readNil("reserved", 6)
- entryBox.readInt("dataRefIndex", 2)
- entryBox.readNil("predefined1", 2)
- entryBox.readNil("reserved1", 2)
- entryBox.readNil("predefined2", 12)
- entryBox.readInt("width", 2)
- entryBox.readInt("height", 2)
- entryBox.readInt("hrez", 4)
- entryBox.readInt("vrez", 4)
- entryBox.readInt("reserved2", 4)
- entryBox.readInt("frameCount", 2)
- entryBox.readString("compressorName", 4)
- entryBox.readInt("depth", 2)
- entryBox.readInt("predefined3", 2)
- case "hint":
- }
- }
- }
- // stsz
- func (b *BoxReader) readSampleSizeBox() {
- b.readBytes()
- b.readFullBox()
- b.readInt("sampleSize", 4)
- b.readInt("sampleCount", 4)
- }
- // stts
- func (b *BoxReader) readSampleTableBox() {
- b.readBytes()
- b.readFullBox()
- b.readInt("entryCount", 4)
- }
- // dref - Nothing of interest here.
- func (b *BoxReader) readDataReferenceBox() {
- }
- // meta
- func (b *BoxReader) readMetaBox() {
- b.readBytes()
- b.readFullBox()
- for b.pos < b.length {
- child := NewEmptyBox(string(b.bytes[b.pos+4:b.pos+8]), b.pos, convertBytesToInt(b.bytes[b.pos:b.pos+4]), b.file)
- child.parent = b
- child.pos = b.pos + 8
- b.children = append(b.children, child)
- b.pos += child.length
- }
- }
- func (b *BoxReader) String() string {
- var buf strings.Builder
- b.Write(0, &buf)
- return buf.String()
- }
- func (b *BoxReader) Write(depth int, buf *strings.Builder) {
- WriteIndent(depth, buf)
- buf.WriteString(fmt.Sprintf("%v [%v]\n", b.name, b.length))
- if len(b.data) > 0 {
- j, err := json.Marshal(b.data)
- if err == nil {
- WriteIndent(depth+1, buf)
- buf.WriteString(string(j))
- buf.WriteRune('\n')
- }
- }
- for _, child := range b.children {
- child.Write(depth+1, buf)
- }
- }
- func WriteIndent(indent int, buf *strings.Builder) {
- for x := 0; x < indent; x++ {
- buf.WriteString(" ")
- }
- }
- func (b *BoxReader) GetBytes(name string, length int64) []byte {
- oldPos := b.pos
- b.pos = oldPos + length
- result := b.bytes[oldPos:b.pos]
- byteCount += length
- return result
- }
- func (b *BoxReader) readNil(name string, length int64) {
- b.GetBytes(name, length)
- }
- func (b *BoxReader) readDate(name string, length int64) int64 {
- value := convertBytesToInt(b.GetBytes(name, length))
- value = value - 2082844800
- b.data[name] = time.Unix(value, 0).UTC().Format(time.RFC3339)
- return value
- }
- func (b *BoxReader) readInt(name string, length int64) int64 {
- value := convertBytesToInt(b.GetBytes(name, length))
- b.data[name] = value
- return value
- }
- func (b *BoxReader) readString(name string, length int64) string {
- value := string(b.GetBytes(name, length))
- b.data[name] = value
- return value
- }
- func (b *BoxReader) ReadTerminatedString(name string) string {
- var buf strings.Builder
- for b.bytes[b.pos] != 0 && b.bytes[b.pos+1] != 0 {
- buf.WriteByte(b.bytes[b.pos])
- buf.WriteByte(b.bytes[b.pos+1])
- b.pos += 2
- }
- value := buf.String()
- b.data[name] = value
- return value
- }
- func convertBytesToInt(buf []byte) int64 {
- res := 0
- for i := len(buf) - 1; i >= 0; i-- {
- b := int(buf[i])
- shift := uint((len(buf) - 1 - i) * 8)
- res += b << shift
- }
- return int64(res)
- }
Add Comment
Please, Sign In to add comment