205 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			205 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package fmp4io
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"os"
 | |
| 	"strings"
 | |
| 
 | |
| 	"git.r-2.top/kunmeng/vdk/utils/bits/pio"
 | |
| )
 | |
| 
 | |
| type Tag uint32
 | |
| 
 | |
| func (a Tag) String() string {
 | |
| 	var b [4]byte
 | |
| 	pio.PutU32BE(b[:], uint32(a))
 | |
| 	for i := 0; i < 4; i++ {
 | |
| 		if b[i] == 0 {
 | |
| 			b[i] = ' '
 | |
| 		}
 | |
| 	}
 | |
| 	return string(b[:])
 | |
| }
 | |
| 
 | |
| type Atom interface {
 | |
| 	Pos() (int, int)
 | |
| 	Tag() Tag
 | |
| 	Marshal([]byte) int
 | |
| 	Unmarshal([]byte, int) (int, error)
 | |
| 	Len() int
 | |
| 	Children() []Atom
 | |
| }
 | |
| 
 | |
| type AtomPos struct {
 | |
| 	Offset int
 | |
| 	Size   int
 | |
| }
 | |
| 
 | |
| func (a AtomPos) Pos() (int, int) {
 | |
| 	return a.Offset, a.Size
 | |
| }
 | |
| 
 | |
| func (a *AtomPos) setPos(offset int, size int) {
 | |
| 	a.Offset, a.Size = offset, size
 | |
| }
 | |
| 
 | |
| type Dummy struct {
 | |
| 	Data []byte
 | |
| 	Tag_ Tag
 | |
| 	AtomPos
 | |
| }
 | |
| 
 | |
| func (a Dummy) Children() []Atom {
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (a Dummy) Tag() Tag {
 | |
| 	return a.Tag_
 | |
| }
 | |
| 
 | |
| func (a Dummy) Len() int {
 | |
| 	return len(a.Data)
 | |
| }
 | |
| 
 | |
| func (a Dummy) Marshal(b []byte) int {
 | |
| 	copy(b, a.Data)
 | |
| 	return len(a.Data)
 | |
| }
 | |
| 
 | |
| func (a *Dummy) Unmarshal(b []byte, offset int) (n int, err error) {
 | |
| 	(&a.AtomPos).setPos(offset, len(b))
 | |
| 	a.Data = b
 | |
| 	n = len(b)
 | |
| 	return
 | |
| }
 | |
| 
 | |
| type FullAtom struct {
 | |
| 	Version uint8
 | |
| 	Flags   uint32
 | |
| 	AtomPos
 | |
| }
 | |
| 
 | |
| func (f FullAtom) marshalAtom(b []byte, tag Tag) (n int) {
 | |
| 	pio.PutU32BE(b[4:], uint32(tag))
 | |
| 	pio.PutU8(b[8:], f.Version)
 | |
| 	pio.PutU24BE(b[9:], f.Flags)
 | |
| 	return 12
 | |
| }
 | |
| 
 | |
| func (f FullAtom) atomLen() int {
 | |
| 	return 12
 | |
| }
 | |
| 
 | |
| func (f *FullAtom) unmarshalAtom(b []byte, offset int) (n int, err error) {
 | |
| 	f.AtomPos.setPos(offset, len(b))
 | |
| 	n = 8
 | |
| 	if len(b) < n+4 {
 | |
| 		return 0, parseErr("fullAtom", offset, nil)
 | |
| 	}
 | |
| 	f.Version = pio.U8(b[n:])
 | |
| 	f.Flags = pio.U24BE(b[n+1:])
 | |
| 	n += 4
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func StringToTag(tag string) Tag {
 | |
| 	var b [4]byte
 | |
| 	copy(b[:], []byte(tag))
 | |
| 	return Tag(pio.U32BE(b[:]))
 | |
| }
 | |
| 
 | |
| func FindChildrenByName(root Atom, tag string) Atom {
 | |
| 	return FindChildren(root, StringToTag(tag))
 | |
| }
 | |
| 
 | |
| func FindChildren(root Atom, tag Tag) Atom {
 | |
| 	if root.Tag() == tag {
 | |
| 		return root
 | |
| 	}
 | |
| 	for _, child := range root.Children() {
 | |
| 		if r := FindChildren(child, tag); r != nil {
 | |
| 			return r
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func ReadFileAtoms(r io.ReadSeeker) (atoms []Atom, err error) {
 | |
| 	for {
 | |
| 		offset, _ := r.Seek(0, 1)
 | |
| 		taghdr := make([]byte, 8)
 | |
| 		if _, err = io.ReadFull(r, taghdr); err != nil {
 | |
| 			if err == io.EOF {
 | |
| 				err = nil
 | |
| 			}
 | |
| 			return
 | |
| 		}
 | |
| 		size := pio.U32BE(taghdr[0:])
 | |
| 		tag := Tag(pio.U32BE(taghdr[4:]))
 | |
| 
 | |
| 		var atom Atom
 | |
| 		switch tag {
 | |
| 		case FTYP:
 | |
| 			atom = &FileType{}
 | |
| 		case STYP:
 | |
| 			atom = &SegmentType{}
 | |
| 		case MOOV:
 | |
| 			atom = &Movie{}
 | |
| 		case MOOF:
 | |
| 			atom = &MovieFrag{}
 | |
| 		case SIDX:
 | |
| 			atom = &SegmentIndex{}
 | |
| 		}
 | |
| 
 | |
| 		if atom != nil {
 | |
| 			b := make([]byte, int(size))
 | |
| 			if _, err = io.ReadFull(r, b[8:]); err != nil {
 | |
| 				return
 | |
| 			}
 | |
| 			copy(b, taghdr)
 | |
| 			if _, err = atom.Unmarshal(b, int(offset)); err != nil {
 | |
| 				return
 | |
| 			}
 | |
| 			atoms = append(atoms, atom)
 | |
| 		} else {
 | |
| 			dummy := &Dummy{Tag_: tag}
 | |
| 			dummy.setPos(int(offset), int(size))
 | |
| 			if _, err = r.Seek(int64(size)-8, 1); err != nil {
 | |
| 				return
 | |
| 			}
 | |
| 			atoms = append(atoms, dummy)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func printatom(out io.Writer, root Atom, depth int) {
 | |
| 	offset, size := root.Pos()
 | |
| 
 | |
| 	type stringintf interface {
 | |
| 		String() string
 | |
| 	}
 | |
| 
 | |
| 	fmt.Fprintf(out,
 | |
| 		"%s%s offset=%d size=%d",
 | |
| 		strings.Repeat(" ", depth*2), root.Tag(), offset, size,
 | |
| 	)
 | |
| 	if str, ok := root.(stringintf); ok {
 | |
| 		fmt.Fprint(out, " ", str.String())
 | |
| 	}
 | |
| 	fmt.Fprintln(out)
 | |
| 
 | |
| 	children := root.Children()
 | |
| 	for _, child := range children {
 | |
| 		printatom(out, child, depth+1)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func FprintAtom(out io.Writer, root Atom) {
 | |
| 	printatom(out, root, 0)
 | |
| }
 | |
| 
 | |
| func PrintAtom(root Atom) {
 | |
| 	FprintAtom(os.Stdout, root)
 | |
| }
 | 
