diff --git a/format/mp4/mp4io/gen/gen.go b/format/mp4/mp4io/gen/gen.go index 1a2857f..34309f7 100644 --- a/format/mp4/mp4io/gen/gen.go +++ b/format/mp4/mp4io/gen/gen.go @@ -1,14 +1,13 @@ - package main import ( - "strings" "fmt" - "os" "go/ast" "go/parser" - "go/token" "go/printer" + "go/token" + "os" + "strings" ) func getexprs(e ast.Expr) string { @@ -37,7 +36,7 @@ func genatomdecl(origfn *ast.FuncDecl, origname, origtag string) (decls []ast.De if typ == "_unknowns" { fieldslist.List = append(fieldslist.List, &ast.Field{ Names: []*ast.Ident{ast.NewIdent("Unknowns")}, - Type: ast.NewIdent("[]Atom"), + Type: ast.NewIdent("[]Atom"), }) } continue @@ -67,24 +66,24 @@ func genatomdecl(origfn *ast.FuncDecl, origname, origtag string) (decls []ast.De case "bytesleft": typ = "[]byte" case "bytes": - typ = "["+name2+"]byte" + typ = "[" + name2 + "]byte" case "uint24": typ = "uint32" case "time64", "time32": typ = "time.Time" case "atom": - typ = "*"+name2 + typ = "*" + name2 case "atoms": - typ = "[]*"+name2 + typ = "[]*" + name2 case "slice": - typ = "[]"+name2 + typ = "[]" + name2 case "array": - typ = "["+len3+"]"+name2 + typ = "[" + len3 + "]" + name2 } fieldslist.List = append(fieldslist.List, &ast.Field{ Names: []*ast.Ident{ast.NewIdent(name)}, - Type: ast.NewIdent(typ), + Type: ast.NewIdent(typ), }) } @@ -135,7 +134,7 @@ func typegetlen(typ string) (n int) { func typegetlens(typ string) string { n := typegetlen(typ) if n == 0 { - return "Len"+typ + return "Len" + typ } else { return fmt.Sprint(n) } @@ -187,7 +186,7 @@ func typegetputfn(typ string) (fn string) { case "fixed16": fn = "PutFixed16" default: - fn = "Put"+typ + fn = "Put" + typ } return } @@ -218,7 +217,7 @@ func typegetgetfn(typ string) (fn string) { case "fixed16": fn = "GetFixed16" default: - fn = "Get"+typ + fn = "Get" + typ } return } @@ -237,14 +236,14 @@ func addn(n int) (stmts []ast.Stmt) { return addns(fmt.Sprint(n)) } -func simplecall(fun string, args... string) *ast.ExprStmt { +func simplecall(fun string, args ...string) *ast.ExprStmt { _args := []ast.Expr{} for _, s := range args { _args = append(_args, ast.NewIdent(s)) } return &ast.ExprStmt{ X: &ast.CallExpr{ - Fun: ast.NewIdent(fun), + Fun: ast.NewIdent(fun), Args: _args, }, } @@ -283,7 +282,7 @@ func newdecl(origname, name string, params, res []*ast.Field, stmts []ast.Stmt) List: []*ast.Field{ &ast.Field{ Names: []*ast.Ident{ast.NewIdent("self")}, - Type: ast.NewIdent(origname), + Type: ast.NewIdent(origname), }, }, }, @@ -319,7 +318,7 @@ func getstructputgetlenfn(origfn *ast.FuncDecl, origname string) (decls []ast.De getstmts = append(getstmts, &ast.ReturnStmt{}) decls = append(decls, &ast.FuncDecl{ - Name: ast.NewIdent("Get"+origname), + Name: ast.NewIdent("Get" + origname), Type: &ast.FuncType{ Params: &ast.FieldList{ List: []*ast.Field{ @@ -336,7 +335,7 @@ func getstructputgetlenfn(origfn *ast.FuncDecl, origname string) (decls []ast.De }) decls = append(decls, &ast.FuncDecl{ - Name: ast.NewIdent("Put"+origname), + Name: ast.NewIdent("Put" + origname), Type: &ast.FuncType{ Params: &ast.FieldList{ List: []*ast.Field{ @@ -352,7 +351,7 @@ func getstructputgetlenfn(origfn *ast.FuncDecl, origname string) (decls []ast.De Tok: token.CONST, Specs: []ast.Spec{ &ast.ValueSpec{ - Names: []*ast.Ident{ast.NewIdent("Len"+origname)}, + Names: []*ast.Ident{ast.NewIdent("Len" + origname)}, Values: []ast.Expr{ast.NewIdent(fmt.Sprint(totlen))}, }, }, @@ -371,7 +370,7 @@ func cc4decls(name string) (decls []ast.Decl) { }, Values: []ast.Expr{ &ast.CallExpr{ - Fun: ast.NewIdent("Tag"), + Fun: ast.NewIdent("Tag"), Args: []ast.Expr{&ast.BasicLit{Kind: token.INT, Value: fmt.Sprintf("0x%x", []byte(name))}}, }, }, @@ -431,7 +430,7 @@ func getatommarshalfn(origfn *ast.FuncDecl, callmarshal := func(name string) (stmts []ast.Stmt) { callexpr := &ast.CallExpr{ - Fun: ast.NewIdent(name+".Marshal"), + Fun: ast.NewIdent(name + ".Marshal"), Args: []ast.Expr{ast.NewIdent("b[n:]")}, } assign := &ast.AssignStmt{ @@ -446,7 +445,7 @@ func getatommarshalfn(origfn *ast.FuncDecl, callputstruct := func(typ, name string) (stmts []ast.Stmt) { stmts = append(stmts, &ast.ExprStmt{ X: &ast.CallExpr{ - Fun: ast.NewIdent(typegetputfn(typ)), + Fun: ast.NewIdent(typegetputfn(typ)), Args: []ast.Expr{ast.NewIdent("b[n:]"), ast.NewIdent(name)}, }, }) @@ -459,7 +458,7 @@ func getatommarshalfn(origfn *ast.FuncDecl, } calllenstruct := func(typ, name string) (stmts []ast.Stmt) { - inc := typegetlens(typ)+"*len("+name+")" + inc := typegetlens(typ) + "*len(" + name + ")" stmts = append(stmts, &ast.AssignStmt{ Tok: token.ADD_ASSIGN, Lhs: []ast.Expr{ast.NewIdent("n")}, @@ -470,7 +469,7 @@ func getatommarshalfn(origfn *ast.FuncDecl, calllen := func(name string) (stmts []ast.Stmt) { callexpr := &ast.CallExpr{ - Fun: ast.NewIdent(name+".Len"), + Fun: ast.NewIdent(name + ".Len"), Args: []ast.Expr{}, } assign := &ast.AssignStmt{ @@ -484,13 +483,13 @@ func getatommarshalfn(origfn *ast.FuncDecl, foreach := func(name, field string, block []ast.Stmt) (stmts []ast.Stmt) { rangestmt := &ast.RangeStmt{ - Key: ast.NewIdent("_"), + Key: ast.NewIdent("_"), Value: ast.NewIdent(name), Body: &ast.BlockStmt{ List: block, }, Tok: token.DEFINE, - X: ast.NewIdent(field), + X: ast.NewIdent(field), } stmts = append(stmts, rangestmt) return @@ -511,7 +510,7 @@ func getatommarshalfn(origfn *ast.FuncDecl, List: block, }, Tok: token.DEFINE, - X: ast.NewIdent(field), + X: ast.NewIdent(field), } stmts = append(stmts, rangestmt) return @@ -574,7 +573,7 @@ func getatommarshalfn(origfn *ast.FuncDecl, unmarshalatom := func(typ, init string) (stmts []ast.Stmt) { return []ast.Stmt{ &ast.AssignStmt{Tok: token.DEFINE, - Lhs: []ast.Expr{ast.NewIdent("atom")}, Rhs: []ast.Expr{ast.NewIdent("&"+typ+"{"+init+"}")}, + Lhs: []ast.Expr{ast.NewIdent("atom")}, Rhs: []ast.Expr{ast.NewIdent("&" + typ + "{" + init + "}")}, }, &ast.IfStmt{ Init: &ast.AssignStmt{ @@ -591,10 +590,10 @@ func getatommarshalfn(origfn *ast.FuncDecl, unmrashalatoms := func() (stmts []ast.Stmt) { blocks := []ast.Stmt{} - blocks = append(blocks, &ast.AssignStmt{ Tok: token.DEFINE, Lhs: []ast.Expr{ast.NewIdent("tag")}, + blocks = append(blocks, &ast.AssignStmt{Tok: token.DEFINE, Lhs: []ast.Expr{ast.NewIdent("tag")}, Rhs: []ast.Expr{ast.NewIdent("Tag(pio.U32BE(b[n+4:]))")}, }) - blocks = append(blocks, &ast.AssignStmt{ Tok: token.DEFINE, Lhs: []ast.Expr{ast.NewIdent("size")}, + blocks = append(blocks, &ast.AssignStmt{Tok: token.DEFINE, Lhs: []ast.Expr{ast.NewIdent("size")}, Rhs: []ast.Expr{ast.NewIdent("int(pio.U32BE(b[n:]))")}, }) blocks = append(blocks, &ast.IfStmt{ @@ -614,7 +613,7 @@ func getatommarshalfn(origfn *ast.FuncDecl, } for i, atom := range atomarrnames { - selfatom := "self."+atom + selfatom := "self." + atom cases = append(cases, &ast.CaseClause{ List: []ast.Expr{ast.NewIdent(strings.ToUpper(struct2tag(atomarrtypes[i])))}, Body: []ast.Stmt{&ast.BlockStmt{ @@ -635,7 +634,7 @@ func getatommarshalfn(origfn *ast.FuncDecl, } blocks = append(blocks, &ast.SwitchStmt{ - Tag: ast.NewIdent("tag"), + Tag: ast.NewIdent("tag"), Body: &ast.BlockStmt{List: cases}, }) @@ -659,9 +658,9 @@ func getatommarshalfn(origfn *ast.FuncDecl, ifnotnil := func(name string, block []ast.Stmt) (stmts []ast.Stmt) { stmts = append(stmts, &ast.IfStmt{ Cond: &ast.BinaryExpr{ - X: ast.NewIdent(name), + X: ast.NewIdent(name), Op: token.NEQ, - Y: ast.NewIdent("nil"), + Y: ast.NewIdent("nil"), }, Body: &ast.BlockStmt{List: block}, }) @@ -693,9 +692,9 @@ func getatommarshalfn(origfn *ast.FuncDecl, checkcurlen := func(inc, debug string) (stmts []ast.Stmt) { stmts = append(stmts, &ast.IfStmt{ Cond: &ast.BinaryExpr{ - X: ast.NewIdent("len(b)"), + X: ast.NewIdent("len(b)"), Op: token.LSS, - Y: ast.NewIdent("n+"+inc), + Y: ast.NewIdent("n+" + inc), }, Body: &ast.BlockStmt{List: parseerrreturn(debug)}, }) @@ -710,20 +709,20 @@ func getatommarshalfn(origfn *ast.FuncDecl, } checkstructlendo := func(typ, name, debug string, - foreach func(string,[]ast.Stmt)[]ast.Stmt, + foreach func(string, []ast.Stmt) []ast.Stmt, ) (stmts []ast.Stmt) { - inc := typegetlens(typ)+"*len("+name+")" + inc := typegetlens(typ) + "*len(" + name + ")" stmts = append(stmts, checkcurlen(inc, debug)...) stmts = append(stmts, foreach(name, append( []ast.Stmt{ &ast.AssignStmt{ Tok: token.ASSIGN, Lhs: []ast.Expr{ - ast.NewIdent(name+"[i]"), + ast.NewIdent(name + "[i]"), }, Rhs: []ast.Expr{ &ast.CallExpr{ - Fun: ast.NewIdent(typegetgetfn(typ)), + Fun: ast.NewIdent(typegetgetfn(typ)), Args: []ast.Expr{ast.NewIdent("b[n:]")}, }, }, @@ -967,7 +966,7 @@ func genatoms(filename, outfilename string) { &ast.GenDecl{ Tok: token.IMPORT, Specs: []ast.Spec{ - &ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"github.com/nareix/joy4/utils/bits/pio"`}}, + &ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"github.com/deepch/vdk/utils/bits/pio"`}}, }, }, &ast.GenDecl{ @@ -1054,4 +1053,3 @@ func main() { genatoms(os.Args[2], os.Args[3]) } } - diff --git a/format/mp4f/mp4fio/gen/gen.go b/format/mp4f/mp4fio/gen/gen.go index 1a2857f..34309f7 100644 --- a/format/mp4f/mp4fio/gen/gen.go +++ b/format/mp4f/mp4fio/gen/gen.go @@ -1,14 +1,13 @@ - package main import ( - "strings" "fmt" - "os" "go/ast" "go/parser" - "go/token" "go/printer" + "go/token" + "os" + "strings" ) func getexprs(e ast.Expr) string { @@ -37,7 +36,7 @@ func genatomdecl(origfn *ast.FuncDecl, origname, origtag string) (decls []ast.De if typ == "_unknowns" { fieldslist.List = append(fieldslist.List, &ast.Field{ Names: []*ast.Ident{ast.NewIdent("Unknowns")}, - Type: ast.NewIdent("[]Atom"), + Type: ast.NewIdent("[]Atom"), }) } continue @@ -67,24 +66,24 @@ func genatomdecl(origfn *ast.FuncDecl, origname, origtag string) (decls []ast.De case "bytesleft": typ = "[]byte" case "bytes": - typ = "["+name2+"]byte" + typ = "[" + name2 + "]byte" case "uint24": typ = "uint32" case "time64", "time32": typ = "time.Time" case "atom": - typ = "*"+name2 + typ = "*" + name2 case "atoms": - typ = "[]*"+name2 + typ = "[]*" + name2 case "slice": - typ = "[]"+name2 + typ = "[]" + name2 case "array": - typ = "["+len3+"]"+name2 + typ = "[" + len3 + "]" + name2 } fieldslist.List = append(fieldslist.List, &ast.Field{ Names: []*ast.Ident{ast.NewIdent(name)}, - Type: ast.NewIdent(typ), + Type: ast.NewIdent(typ), }) } @@ -135,7 +134,7 @@ func typegetlen(typ string) (n int) { func typegetlens(typ string) string { n := typegetlen(typ) if n == 0 { - return "Len"+typ + return "Len" + typ } else { return fmt.Sprint(n) } @@ -187,7 +186,7 @@ func typegetputfn(typ string) (fn string) { case "fixed16": fn = "PutFixed16" default: - fn = "Put"+typ + fn = "Put" + typ } return } @@ -218,7 +217,7 @@ func typegetgetfn(typ string) (fn string) { case "fixed16": fn = "GetFixed16" default: - fn = "Get"+typ + fn = "Get" + typ } return } @@ -237,14 +236,14 @@ func addn(n int) (stmts []ast.Stmt) { return addns(fmt.Sprint(n)) } -func simplecall(fun string, args... string) *ast.ExprStmt { +func simplecall(fun string, args ...string) *ast.ExprStmt { _args := []ast.Expr{} for _, s := range args { _args = append(_args, ast.NewIdent(s)) } return &ast.ExprStmt{ X: &ast.CallExpr{ - Fun: ast.NewIdent(fun), + Fun: ast.NewIdent(fun), Args: _args, }, } @@ -283,7 +282,7 @@ func newdecl(origname, name string, params, res []*ast.Field, stmts []ast.Stmt) List: []*ast.Field{ &ast.Field{ Names: []*ast.Ident{ast.NewIdent("self")}, - Type: ast.NewIdent(origname), + Type: ast.NewIdent(origname), }, }, }, @@ -319,7 +318,7 @@ func getstructputgetlenfn(origfn *ast.FuncDecl, origname string) (decls []ast.De getstmts = append(getstmts, &ast.ReturnStmt{}) decls = append(decls, &ast.FuncDecl{ - Name: ast.NewIdent("Get"+origname), + Name: ast.NewIdent("Get" + origname), Type: &ast.FuncType{ Params: &ast.FieldList{ List: []*ast.Field{ @@ -336,7 +335,7 @@ func getstructputgetlenfn(origfn *ast.FuncDecl, origname string) (decls []ast.De }) decls = append(decls, &ast.FuncDecl{ - Name: ast.NewIdent("Put"+origname), + Name: ast.NewIdent("Put" + origname), Type: &ast.FuncType{ Params: &ast.FieldList{ List: []*ast.Field{ @@ -352,7 +351,7 @@ func getstructputgetlenfn(origfn *ast.FuncDecl, origname string) (decls []ast.De Tok: token.CONST, Specs: []ast.Spec{ &ast.ValueSpec{ - Names: []*ast.Ident{ast.NewIdent("Len"+origname)}, + Names: []*ast.Ident{ast.NewIdent("Len" + origname)}, Values: []ast.Expr{ast.NewIdent(fmt.Sprint(totlen))}, }, }, @@ -371,7 +370,7 @@ func cc4decls(name string) (decls []ast.Decl) { }, Values: []ast.Expr{ &ast.CallExpr{ - Fun: ast.NewIdent("Tag"), + Fun: ast.NewIdent("Tag"), Args: []ast.Expr{&ast.BasicLit{Kind: token.INT, Value: fmt.Sprintf("0x%x", []byte(name))}}, }, }, @@ -431,7 +430,7 @@ func getatommarshalfn(origfn *ast.FuncDecl, callmarshal := func(name string) (stmts []ast.Stmt) { callexpr := &ast.CallExpr{ - Fun: ast.NewIdent(name+".Marshal"), + Fun: ast.NewIdent(name + ".Marshal"), Args: []ast.Expr{ast.NewIdent("b[n:]")}, } assign := &ast.AssignStmt{ @@ -446,7 +445,7 @@ func getatommarshalfn(origfn *ast.FuncDecl, callputstruct := func(typ, name string) (stmts []ast.Stmt) { stmts = append(stmts, &ast.ExprStmt{ X: &ast.CallExpr{ - Fun: ast.NewIdent(typegetputfn(typ)), + Fun: ast.NewIdent(typegetputfn(typ)), Args: []ast.Expr{ast.NewIdent("b[n:]"), ast.NewIdent(name)}, }, }) @@ -459,7 +458,7 @@ func getatommarshalfn(origfn *ast.FuncDecl, } calllenstruct := func(typ, name string) (stmts []ast.Stmt) { - inc := typegetlens(typ)+"*len("+name+")" + inc := typegetlens(typ) + "*len(" + name + ")" stmts = append(stmts, &ast.AssignStmt{ Tok: token.ADD_ASSIGN, Lhs: []ast.Expr{ast.NewIdent("n")}, @@ -470,7 +469,7 @@ func getatommarshalfn(origfn *ast.FuncDecl, calllen := func(name string) (stmts []ast.Stmt) { callexpr := &ast.CallExpr{ - Fun: ast.NewIdent(name+".Len"), + Fun: ast.NewIdent(name + ".Len"), Args: []ast.Expr{}, } assign := &ast.AssignStmt{ @@ -484,13 +483,13 @@ func getatommarshalfn(origfn *ast.FuncDecl, foreach := func(name, field string, block []ast.Stmt) (stmts []ast.Stmt) { rangestmt := &ast.RangeStmt{ - Key: ast.NewIdent("_"), + Key: ast.NewIdent("_"), Value: ast.NewIdent(name), Body: &ast.BlockStmt{ List: block, }, Tok: token.DEFINE, - X: ast.NewIdent(field), + X: ast.NewIdent(field), } stmts = append(stmts, rangestmt) return @@ -511,7 +510,7 @@ func getatommarshalfn(origfn *ast.FuncDecl, List: block, }, Tok: token.DEFINE, - X: ast.NewIdent(field), + X: ast.NewIdent(field), } stmts = append(stmts, rangestmt) return @@ -574,7 +573,7 @@ func getatommarshalfn(origfn *ast.FuncDecl, unmarshalatom := func(typ, init string) (stmts []ast.Stmt) { return []ast.Stmt{ &ast.AssignStmt{Tok: token.DEFINE, - Lhs: []ast.Expr{ast.NewIdent("atom")}, Rhs: []ast.Expr{ast.NewIdent("&"+typ+"{"+init+"}")}, + Lhs: []ast.Expr{ast.NewIdent("atom")}, Rhs: []ast.Expr{ast.NewIdent("&" + typ + "{" + init + "}")}, }, &ast.IfStmt{ Init: &ast.AssignStmt{ @@ -591,10 +590,10 @@ func getatommarshalfn(origfn *ast.FuncDecl, unmrashalatoms := func() (stmts []ast.Stmt) { blocks := []ast.Stmt{} - blocks = append(blocks, &ast.AssignStmt{ Tok: token.DEFINE, Lhs: []ast.Expr{ast.NewIdent("tag")}, + blocks = append(blocks, &ast.AssignStmt{Tok: token.DEFINE, Lhs: []ast.Expr{ast.NewIdent("tag")}, Rhs: []ast.Expr{ast.NewIdent("Tag(pio.U32BE(b[n+4:]))")}, }) - blocks = append(blocks, &ast.AssignStmt{ Tok: token.DEFINE, Lhs: []ast.Expr{ast.NewIdent("size")}, + blocks = append(blocks, &ast.AssignStmt{Tok: token.DEFINE, Lhs: []ast.Expr{ast.NewIdent("size")}, Rhs: []ast.Expr{ast.NewIdent("int(pio.U32BE(b[n:]))")}, }) blocks = append(blocks, &ast.IfStmt{ @@ -614,7 +613,7 @@ func getatommarshalfn(origfn *ast.FuncDecl, } for i, atom := range atomarrnames { - selfatom := "self."+atom + selfatom := "self." + atom cases = append(cases, &ast.CaseClause{ List: []ast.Expr{ast.NewIdent(strings.ToUpper(struct2tag(atomarrtypes[i])))}, Body: []ast.Stmt{&ast.BlockStmt{ @@ -635,7 +634,7 @@ func getatommarshalfn(origfn *ast.FuncDecl, } blocks = append(blocks, &ast.SwitchStmt{ - Tag: ast.NewIdent("tag"), + Tag: ast.NewIdent("tag"), Body: &ast.BlockStmt{List: cases}, }) @@ -659,9 +658,9 @@ func getatommarshalfn(origfn *ast.FuncDecl, ifnotnil := func(name string, block []ast.Stmt) (stmts []ast.Stmt) { stmts = append(stmts, &ast.IfStmt{ Cond: &ast.BinaryExpr{ - X: ast.NewIdent(name), + X: ast.NewIdent(name), Op: token.NEQ, - Y: ast.NewIdent("nil"), + Y: ast.NewIdent("nil"), }, Body: &ast.BlockStmt{List: block}, }) @@ -693,9 +692,9 @@ func getatommarshalfn(origfn *ast.FuncDecl, checkcurlen := func(inc, debug string) (stmts []ast.Stmt) { stmts = append(stmts, &ast.IfStmt{ Cond: &ast.BinaryExpr{ - X: ast.NewIdent("len(b)"), + X: ast.NewIdent("len(b)"), Op: token.LSS, - Y: ast.NewIdent("n+"+inc), + Y: ast.NewIdent("n+" + inc), }, Body: &ast.BlockStmt{List: parseerrreturn(debug)}, }) @@ -710,20 +709,20 @@ func getatommarshalfn(origfn *ast.FuncDecl, } checkstructlendo := func(typ, name, debug string, - foreach func(string,[]ast.Stmt)[]ast.Stmt, + foreach func(string, []ast.Stmt) []ast.Stmt, ) (stmts []ast.Stmt) { - inc := typegetlens(typ)+"*len("+name+")" + inc := typegetlens(typ) + "*len(" + name + ")" stmts = append(stmts, checkcurlen(inc, debug)...) stmts = append(stmts, foreach(name, append( []ast.Stmt{ &ast.AssignStmt{ Tok: token.ASSIGN, Lhs: []ast.Expr{ - ast.NewIdent(name+"[i]"), + ast.NewIdent(name + "[i]"), }, Rhs: []ast.Expr{ &ast.CallExpr{ - Fun: ast.NewIdent(typegetgetfn(typ)), + Fun: ast.NewIdent(typegetgetfn(typ)), Args: []ast.Expr{ast.NewIdent("b[n:]")}, }, }, @@ -967,7 +966,7 @@ func genatoms(filename, outfilename string) { &ast.GenDecl{ Tok: token.IMPORT, Specs: []ast.Spec{ - &ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"github.com/nareix/joy4/utils/bits/pio"`}}, + &ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"github.com/deepch/vdk/utils/bits/pio"`}}, }, }, &ast.GenDecl{ @@ -1054,4 +1053,3 @@ func main() { genatoms(os.Args[2], os.Args[3]) } } - diff --git a/format/mp4m/demuxer.go b/format/mp4m/demuxer.go new file mode 100644 index 0000000..2b361e3 --- /dev/null +++ b/format/mp4m/demuxer.go @@ -0,0 +1,446 @@ +package mp4 + +import ( + "errors" + "fmt" + "io" + "time" + + "github.com/deepch/vdk/av" + "github.com/deepch/vdk/codec/aacparser" + "github.com/deepch/vdk/codec/h264parser" + "github.com/deepch/vdk/format/mp4/mp4io" +) + +type Demuxer struct { + r io.ReadSeeker + streams []*Stream + movieAtom *mp4io.Movie +} + +func NewDemuxer(r io.ReadSeeker) *Demuxer { + return &Demuxer{ + r: r, + } +} + +func (self *Demuxer) Streams() (streams []av.CodecData, err error) { + if err = self.probe(); err != nil { + return + } + for _, stream := range self.streams { + streams = append(streams, stream.CodecData) + } + return +} + +func (self *Demuxer) readat(pos int64, b []byte) (err error) { + if _, err = self.r.Seek(pos, 0); err != nil { + return + } + if _, err = io.ReadFull(self.r, b); err != nil { + return + } + return +} + +func (self *Demuxer) probe() (err error) { + if self.movieAtom != nil { + return + } + + var moov *mp4io.Movie + var atoms []mp4io.Atom + + if atoms, err = mp4io.ReadFileAtoms(self.r); err != nil { + return + } + if _, err = self.r.Seek(0, 0); err != nil { + return + } + + for _, atom := range atoms { + if atom.Tag() == mp4io.MOOV { + moov = atom.(*mp4io.Movie) + } + } + + if moov == nil { + err = fmt.Errorf("mp4: 'moov' atom not found") + return + } + + self.streams = []*Stream{} + for i, atrack := range moov.Tracks { + stream := &Stream{ + trackAtom: atrack, + demuxer: self, + idx: i, + } + if atrack.Media != nil && atrack.Media.Info != nil && atrack.Media.Info.Sample != nil { + stream.sample = atrack.Media.Info.Sample + stream.timeScale = int64(atrack.Media.Header.TimeScale) + } else { + err = fmt.Errorf("mp4: sample table not found") + return + } + + if avc1 := atrack.GetAVC1Conf(); avc1 != nil { + if stream.CodecData, err = h264parser.NewCodecDataFromAVCDecoderConfRecord(avc1.Data); err != nil { + return + } + self.streams = append(self.streams, stream) + } else if esds := atrack.GetElemStreamDesc(); esds != nil { + if stream.CodecData, err = aacparser.NewCodecDataFromMPEG4AudioConfigBytes(esds.DecConfig); err != nil { + return + } + self.streams = append(self.streams, stream) + } + } + + self.movieAtom = moov + return +} + +func (self *Stream) setSampleIndex(index int) (err error) { + found := false + start := 0 + self.chunkGroupIndex = 0 + + for self.chunkIndex = range self.sample.ChunkOffset.Entries { + if self.chunkGroupIndex+1 < len(self.sample.SampleToChunk.Entries) && + uint32(self.chunkIndex+1) == self.sample.SampleToChunk.Entries[self.chunkGroupIndex+1].FirstChunk { + self.chunkGroupIndex++ + } + n := int(self.sample.SampleToChunk.Entries[self.chunkGroupIndex].SamplesPerChunk) + if index >= start && index < start+n { + found = true + self.sampleIndexInChunk = index - start + break + } + start += n + } + if !found { + err = fmt.Errorf("mp4: stream[%d]: cannot locate sample index in chunk", self.idx) + return + } + + if self.sample.SampleSize.SampleSize != 0 { + self.sampleOffsetInChunk = int64(self.sampleIndexInChunk) * int64(self.sample.SampleSize.SampleSize) + } else { + if index >= len(self.sample.SampleSize.Entries) { + err = fmt.Errorf("mp4: stream[%d]: sample index out of range", self.idx) + return + } + self.sampleOffsetInChunk = int64(0) + for i := index - self.sampleIndexInChunk; i < index; i++ { + self.sampleOffsetInChunk += int64(self.sample.SampleSize.Entries[i]) + } + } + + self.dts = int64(0) + start = 0 + found = false + self.sttsEntryIndex = 0 + for self.sttsEntryIndex < len(self.sample.TimeToSample.Entries) { + entry := self.sample.TimeToSample.Entries[self.sttsEntryIndex] + n := int(entry.Count) + if index >= start && index < start+n { + self.sampleIndexInSttsEntry = index - start + self.dts += int64(index-start) * int64(entry.Duration) + found = true + break + } + start += n + self.dts += int64(n) * int64(entry.Duration) + self.sttsEntryIndex++ + } + if !found { + err = fmt.Errorf("mp4: stream[%d]: cannot locate sample index in stts entry", self.idx) + return + } + + if self.sample.CompositionOffset != nil && len(self.sample.CompositionOffset.Entries) > 0 { + start = 0 + found = false + self.cttsEntryIndex = 0 + for self.cttsEntryIndex < len(self.sample.CompositionOffset.Entries) { + n := int(self.sample.CompositionOffset.Entries[self.cttsEntryIndex].Count) + if index >= start && index < start+n { + self.sampleIndexInCttsEntry = index - start + found = true + break + } + start += n + self.cttsEntryIndex++ + } + if !found { + err = fmt.Errorf("mp4: stream[%d]: cannot locate sample index in ctts entry", self.idx) + return + } + } + + if self.sample.SyncSample != nil { + self.syncSampleIndex = 0 + for self.syncSampleIndex < len(self.sample.SyncSample.Entries)-1 { + if self.sample.SyncSample.Entries[self.syncSampleIndex+1]-1 > uint32(index) { + break + } + self.syncSampleIndex++ + } + } + + if false { + fmt.Printf("mp4: stream[%d]: setSampleIndex chunkGroupIndex=%d chunkIndex=%d sampleOffsetInChunk=%d\n", + self.idx, self.chunkGroupIndex, self.chunkIndex, self.sampleOffsetInChunk) + } + + self.sampleIndex = index + return +} + +func (self *Stream) isSampleValid() bool { + if self.chunkIndex >= len(self.sample.ChunkOffset.Entries) { + return false + } + if self.chunkGroupIndex >= len(self.sample.SampleToChunk.Entries) { + return false + } + if self.sttsEntryIndex >= len(self.sample.TimeToSample.Entries) { + return false + } + if self.sample.CompositionOffset != nil && len(self.sample.CompositionOffset.Entries) > 0 { + if self.cttsEntryIndex >= len(self.sample.CompositionOffset.Entries) { + return false + } + } + if self.sample.SyncSample != nil { + if self.syncSampleIndex >= len(self.sample.SyncSample.Entries) { + return false + } + } + if self.sample.SampleSize.SampleSize != 0 { + if self.sampleIndex >= len(self.sample.SampleSize.Entries) { + return false + } + } + return true +} + +func (self *Stream) incSampleIndex() (duration int64) { + if false { + fmt.Printf("incSampleIndex sampleIndex=%d sampleOffsetInChunk=%d sampleIndexInChunk=%d chunkGroupIndex=%d chunkIndex=%d\n", + self.sampleIndex, self.sampleOffsetInChunk, self.sampleIndexInChunk, self.chunkGroupIndex, self.chunkIndex) + } + + self.sampleIndexInChunk++ + if uint32(self.sampleIndexInChunk) == self.sample.SampleToChunk.Entries[self.chunkGroupIndex].SamplesPerChunk { + self.chunkIndex++ + self.sampleIndexInChunk = 0 + self.sampleOffsetInChunk = int64(0) + } else { + if self.sample.SampleSize.SampleSize != 0 { + self.sampleOffsetInChunk += int64(self.sample.SampleSize.SampleSize) + } else { + self.sampleOffsetInChunk += int64(self.sample.SampleSize.Entries[self.sampleIndex]) + } + } + + if self.chunkGroupIndex+1 < len(self.sample.SampleToChunk.Entries) && + uint32(self.chunkIndex+1) == self.sample.SampleToChunk.Entries[self.chunkGroupIndex+1].FirstChunk { + self.chunkGroupIndex++ + } + + sttsEntry := self.sample.TimeToSample.Entries[self.sttsEntryIndex] + duration = int64(sttsEntry.Duration) + self.sampleIndexInSttsEntry++ + self.dts += duration + if uint32(self.sampleIndexInSttsEntry) == sttsEntry.Count { + self.sampleIndexInSttsEntry = 0 + self.sttsEntryIndex++ + } + + if self.sample.CompositionOffset != nil && len(self.sample.CompositionOffset.Entries) > 0 { + self.sampleIndexInCttsEntry++ + if uint32(self.sampleIndexInCttsEntry) == self.sample.CompositionOffset.Entries[self.cttsEntryIndex].Count { + self.sampleIndexInCttsEntry = 0 + self.cttsEntryIndex++ + } + } + + if self.sample.SyncSample != nil { + entries := self.sample.SyncSample.Entries + if self.syncSampleIndex+1 < len(entries) && entries[self.syncSampleIndex+1]-1 == uint32(self.sampleIndex+1) { + self.syncSampleIndex++ + } + } + + self.sampleIndex++ + return +} + +func (self *Stream) sampleCount() int { + if self.sample.SampleSize.SampleSize == 0 { + chunkGroupIndex := 0 + count := 0 + for chunkIndex := range self.sample.ChunkOffset.Entries { + n := int(self.sample.SampleToChunk.Entries[chunkGroupIndex].SamplesPerChunk) + count += n + if chunkGroupIndex+1 < len(self.sample.SampleToChunk.Entries) && + uint32(chunkIndex+1) == self.sample.SampleToChunk.Entries[chunkGroupIndex+1].FirstChunk { + chunkGroupIndex++ + } + } + return count + } else { + return len(self.sample.SampleSize.Entries) + } +} + +func (self *Demuxer) ReadPacket() (pkt av.Packet, err error) { + if err = self.probe(); err != nil { + return + } + if len(self.streams) == 0 { + err = errors.New("mp4: no streams available while trying to read a packet") + return + } + + var chosen *Stream + var chosenidx int + for i, stream := range self.streams { + if chosen == nil || stream.tsToTime(stream.dts) < chosen.tsToTime(chosen.dts) { + chosen = stream + chosenidx = i + } + } + if false { + fmt.Printf("ReadPacket: chosen index=%v time=%v\n", chosen.idx, chosen.tsToTime(chosen.dts)) + } + tm := chosen.tsToTime(chosen.dts) + if pkt, err = chosen.readPacket(); err != nil { + return + } + pkt.Time = tm + pkt.Idx = int8(chosenidx) + return +} + +func (self *Demuxer) CurrentTime() (tm time.Duration) { + if len(self.streams) > 0 { + stream := self.streams[0] + tm = stream.tsToTime(stream.dts) + } + return +} + +func (self *Demuxer) SeekToTime(tm time.Duration) (err error) { + for _, stream := range self.streams { + if stream.Type().IsVideo() { + if err = stream.seekToTime(tm); err != nil { + return + } + tm = stream.tsToTime(stream.dts) + break + } + } + + for _, stream := range self.streams { + if !stream.Type().IsVideo() { + if err = stream.seekToTime(tm); err != nil { + return + } + } + } + + return +} + +func (self *Stream) readPacket() (pkt av.Packet, err error) { + if !self.isSampleValid() { + err = io.EOF + return + } + //fmt.Println("readPacket", self.sampleIndex) + + chunkOffset := self.sample.ChunkOffset.Entries[self.chunkIndex] + sampleSize := uint32(0) + if self.sample.SampleSize.SampleSize != 0 { + sampleSize = self.sample.SampleSize.SampleSize + } else { + sampleSize = self.sample.SampleSize.Entries[self.sampleIndex] + } + + sampleOffset := int64(chunkOffset) + self.sampleOffsetInChunk + pkt.Data = make([]byte, sampleSize) + if err = self.demuxer.readat(sampleOffset, pkt.Data); err != nil { + return + } + + if self.sample.SyncSample != nil { + if self.sample.SyncSample.Entries[self.syncSampleIndex]-1 == uint32(self.sampleIndex) { + pkt.IsKeyFrame = true + } + } + + //println("pts/dts", self.ptsEntryIndex, self.dtsEntryIndex) + if self.sample.CompositionOffset != nil && len(self.sample.CompositionOffset.Entries) > 0 { + cts := int64(self.sample.CompositionOffset.Entries[self.cttsEntryIndex].Offset) + pkt.CompositionTime = self.tsToTime(cts) + } + + self.incSampleIndex() + + return +} + +func (self *Stream) seekToTime(tm time.Duration) (err error) { + index := self.timeToSampleIndex(tm) + if err = self.setSampleIndex(index); err != nil { + return + } + if false { + fmt.Printf("stream[%d]: seekToTime index=%v time=%v cur=%v\n", self.idx, index, tm, self.tsToTime(self.dts)) + } + return +} + +func (self *Stream) timeToSampleIndex(tm time.Duration) int { + targetTs := self.timeToTs(tm) + targetIndex := 0 + + startTs := int64(0) + endTs := int64(0) + startIndex := 0 + endIndex := 0 + found := false + for _, entry := range self.sample.TimeToSample.Entries { + endTs = startTs + int64(entry.Count*entry.Duration) + endIndex = startIndex + int(entry.Count) + if targetTs >= startTs && targetTs < endTs { + targetIndex = startIndex + int((targetTs-startTs)/int64(entry.Duration)) + found = true + } + startTs = endTs + startIndex = endIndex + } + if !found { + if targetTs < 0 { + targetIndex = 0 + } else { + targetIndex = endIndex - 1 + } + } + + if self.sample.SyncSample != nil { + entries := self.sample.SyncSample.Entries + for i := len(entries) - 1; i >= 0; i-- { + if entries[i]-1 < uint32(targetIndex) { + targetIndex = int(entries[i] - 1) + break + } + } + } + + return targetIndex +} diff --git a/format/mp4m/handler.go b/format/mp4m/handler.go new file mode 100644 index 0000000..22827f1 --- /dev/null +++ b/format/mp4m/handler.go @@ -0,0 +1,32 @@ +package mp4 + +import ( + "io" + + "github.com/deepch/vdk/av" + "github.com/deepch/vdk/av/avutil" +) + +var CodecTypes = []av.CodecType{av.H264, av.AAC} + +func Handler(h *avutil.RegisterHandler) { + h.Ext = ".mp4" + + h.Probe = func(b []byte) bool { + switch string(b[4:8]) { + case "moov", "ftyp", "free", "mdat", "moof": + return true + } + return false + } + + h.ReaderDemuxer = func(r io.Reader) av.Demuxer { + return NewDemuxer(r.(io.ReadSeeker)) + } + + h.WriterMuxer = func(w io.Writer) av.Muxer { + return NewMuxer(w.(io.WriteSeeker)) + } + + h.CodecTypes = CodecTypes +} diff --git a/format/mp4m/mp4io/atoms.go b/format/mp4m/mp4io/atoms.go new file mode 100644 index 0000000..dd3c3fb --- /dev/null +++ b/format/mp4m/mp4io/atoms.go @@ -0,0 +1,3822 @@ +package mp4io + +import ( + "time" + + "github.com/deepch/vdk/utils/bits/pio" +) + +const MOOF = Tag(0x6d6f6f66) + +func (self MovieFrag) Tag() Tag { + return MOOF +} + +const HDLR = Tag(0x68646c72) + +func (self HandlerRefer) Tag() Tag { + return HDLR +} + +const AVC1 = Tag(0x61766331) + +func (self AVC1Desc) Tag() Tag { + return AVC1 +} + +//0x31766568 +const HEV1 = Tag(0x68766331) + +func (self HV1Desc) Tag() Tag { + return HEV1 +} + +//const HVC1 = Tag(0x68766331) +//func (self HVC1Desc) Tag() Tag { +// return HVC1 +//} +const URL = Tag(0x75726c20) + +func (self DataReferUrl) Tag() Tag { + return URL +} + +const TREX = Tag(0x74726578) + +func (self TrackExtend) Tag() Tag { + return TREX +} + +const ESDS = Tag(0x65736473) + +func (self ElemStreamDesc) Tag() Tag { + return ESDS +} + +const MDHD = Tag(0x6d646864) + +func (self MediaHeader) Tag() Tag { + return MDHD +} + +const STTS = Tag(0x73747473) + +func (self TimeToSample) Tag() Tag { + return STTS +} + +const STSS = Tag(0x73747373) + +func (self SyncSample) Tag() Tag { + return STSS +} + +const MFHD = Tag(0x6d666864) + +func (self MovieFragHeader) Tag() Tag { + return MFHD +} + +const MVHD = Tag(0x6d766864) + +func (self MovieHeader) Tag() Tag { + return MVHD +} + +const MINF = Tag(0x6d696e66) + +func (self MediaInfo) Tag() Tag { + return MINF +} + +const MOOV = Tag(0x6d6f6f76) + +func (self Movie) Tag() Tag { + return MOOV +} + +const MVEX = Tag(0x6d766578) + +func (self MovieExtend) Tag() Tag { + return MVEX +} + +const STSD = Tag(0x73747364) + +func (self SampleDesc) Tag() Tag { + return STSD +} + +const MP4A = Tag(0x6d703461) + +func (self MP4ADesc) Tag() Tag { + return MP4A +} + +const CTTS = Tag(0x63747473) + +func (self CompositionOffset) Tag() Tag { + return CTTS +} + +const STCO = Tag(0x7374636f) + +func (self ChunkOffset) Tag() Tag { + return STCO +} + +const TRUN = Tag(0x7472756e) + +func (self TrackFragRun) Tag() Tag { + return TRUN +} + +const TRAK = Tag(0x7472616b) + +func (self Track) Tag() Tag { + return TRAK +} + +const MDIA = Tag(0x6d646961) + +func (self Media) Tag() Tag { + return MDIA +} + +const STSC = Tag(0x73747363) + +func (self SampleToChunk) Tag() Tag { + return STSC +} + +const VMHD = Tag(0x766d6864) + +func (self VideoMediaInfo) Tag() Tag { + return VMHD +} + +const STBL = Tag(0x7374626c) + +func (self SampleTable) Tag() Tag { + return STBL +} + +const AVCC = Tag(0x61766343) + +func (self AVC1Conf) Tag() Tag { + return AVCC +} + +const HVCC = Tag(0x68766343) + +func (self HV1Conf) Tag() Tag { + return HVCC +} + +const TFDT = Tag(0x74666474) + +func (self TrackFragDecodeTime) Tag() Tag { + return TFDT +} + +const DINF = Tag(0x64696e66) + +func (self DataInfo) Tag() Tag { + return DINF +} + +const DREF = Tag(0x64726566) + +func (self DataRefer) Tag() Tag { + return DREF +} + +const TRAF = Tag(0x74726166) + +func (self TrackFrag) Tag() Tag { + return TRAF +} + +const STSZ = Tag(0x7374737a) + +func (self SampleSize) Tag() Tag { + return STSZ +} + +const TFHD = Tag(0x74666864) + +func (self TrackFragHeader) Tag() Tag { + return TFHD +} + +const TKHD = Tag(0x746b6864) + +func (self TrackHeader) Tag() Tag { + return TKHD +} + +const SMHD = Tag(0x736d6864) + +func (self SoundMediaInfo) Tag() Tag { + return SMHD +} + +const MDAT = Tag(0x6d646174) + +type Movie struct { + Header *MovieHeader + MovieExtend *MovieExtend + Tracks []*Track + Unknowns []Atom + AtomPos +} + +func (self Movie) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(MOOV)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self Movie) marshal(b []byte) (n int) { + if self.Header != nil { + n += self.Header.Marshal(b[n:]) + } + if self.MovieExtend != nil { + n += self.MovieExtend.Marshal(b[n:]) + } + for _, atom := range self.Tracks { + n += atom.Marshal(b[n:]) + } + for _, atom := range self.Unknowns { + n += atom.Marshal(b[n:]) + } + return +} +func (self Movie) Len() (n int) { + n += 8 + if self.Header != nil { + n += self.Header.Len() + } + if self.MovieExtend != nil { + n += self.MovieExtend.Len() + } + for _, atom := range self.Tracks { + n += atom.Len() + } + for _, atom := range self.Unknowns { + n += atom.Len() + } + return +} +func (self *Movie) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + for n+8 < len(b) { + tag := Tag(pio.U32BE(b[n+4:])) + size := int(pio.U32BE(b[n:])) + if len(b) < n+size { + err = parseErr("TagSizeInvalid", n+offset, err) + return + } + switch tag { + case MVHD: + { + atom := &MovieHeader{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("mvhd", n+offset, err) + return + } + self.Header = atom + } + case MVEX: + { + atom := &MovieExtend{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("mvex", n+offset, err) + return + } + self.MovieExtend = atom + } + case TRAK: + { + atom := &Track{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("trak", n+offset, err) + return + } + self.Tracks = append(self.Tracks, atom) + } + default: + { + atom := &Dummy{Tag_: tag, Data: b[n : n+size]} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("", n+offset, err) + return + } + self.Unknowns = append(self.Unknowns, atom) + } + } + n += size + } + return +} +func (self Movie) Children() (r []Atom) { + if self.Header != nil { + r = append(r, self.Header) + } + if self.MovieExtend != nil { + r = append(r, self.MovieExtend) + } + for _, atom := range self.Tracks { + r = append(r, atom) + } + r = append(r, self.Unknowns...) + return +} + +type MovieHeader struct { + Version uint8 + Flags uint32 + CreateTime time.Time + ModifyTime time.Time + TimeScale int32 + Duration int32 + PreferredRate float64 + PreferredVolume float64 + Matrix [9]int32 + PreviewTime time.Time + PreviewDuration time.Time + PosterTime time.Time + SelectionTime time.Time + SelectionDuration time.Time + CurrentTime time.Time + NextTrackId int32 + AtomPos +} + +func (self MovieHeader) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(MVHD)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self MovieHeader) marshal(b []byte) (n int) { + pio.PutU8(b[n:], self.Version) + n += 1 + pio.PutU24BE(b[n:], self.Flags) + n += 3 + PutTime32(b[n:], self.CreateTime) + n += 4 + PutTime32(b[n:], self.ModifyTime) + n += 4 + pio.PutI32BE(b[n:], self.TimeScale) + n += 4 + pio.PutI32BE(b[n:], self.Duration) + n += 4 + PutFixed32(b[n:], self.PreferredRate) + n += 4 + PutFixed16(b[n:], self.PreferredVolume) + n += 2 + n += 10 + for _, entry := range self.Matrix { + pio.PutI32BE(b[n:], entry) + n += 4 + } + PutTime32(b[n:], self.PreviewTime) + n += 4 + PutTime32(b[n:], self.PreviewDuration) + n += 4 + PutTime32(b[n:], self.PosterTime) + n += 4 + PutTime32(b[n:], self.SelectionTime) + n += 4 + PutTime32(b[n:], self.SelectionDuration) + n += 4 + PutTime32(b[n:], self.CurrentTime) + n += 4 + pio.PutI32BE(b[n:], self.NextTrackId) + n += 4 + return +} +func (self MovieHeader) Len() (n int) { + n += 8 + n += 1 + n += 3 + n += 4 + n += 4 + n += 4 + n += 4 + n += 4 + n += 2 + n += 10 + n += 4 * len(self.Matrix[:]) + n += 4 + n += 4 + n += 4 + n += 4 + n += 4 + n += 4 + n += 4 + return +} +func (self *MovieHeader) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + if len(b) < n+1 { + err = parseErr("Version", n+offset, err) + return + } + self.Version = pio.U8(b[n:]) + n += 1 + if len(b) < n+3 { + err = parseErr("Flags", n+offset, err) + return + } + self.Flags = pio.U24BE(b[n:]) + n += 3 + if len(b) < n+4 { + err = parseErr("CreateTime", n+offset, err) + return + } + self.CreateTime = GetTime32(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("ModifyTime", n+offset, err) + return + } + self.ModifyTime = GetTime32(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("TimeScale", n+offset, err) + return + } + self.TimeScale = pio.I32BE(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("Duration", n+offset, err) + return + } + self.Duration = pio.I32BE(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("PreferredRate", n+offset, err) + return + } + self.PreferredRate = GetFixed32(b[n:]) + n += 4 + if len(b) < n+2 { + err = parseErr("PreferredVolume", n+offset, err) + return + } + self.PreferredVolume = GetFixed16(b[n:]) + n += 2 + n += 10 + if len(b) < n+4*len(self.Matrix) { + err = parseErr("Matrix", n+offset, err) + return + } + for i := range self.Matrix { + self.Matrix[i] = pio.I32BE(b[n:]) + n += 4 + } + if len(b) < n+4 { + err = parseErr("PreviewTime", n+offset, err) + return + } + self.PreviewTime = GetTime32(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("PreviewDuration", n+offset, err) + return + } + self.PreviewDuration = GetTime32(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("PosterTime", n+offset, err) + return + } + self.PosterTime = GetTime32(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("SelectionTime", n+offset, err) + return + } + self.SelectionTime = GetTime32(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("SelectionDuration", n+offset, err) + return + } + self.SelectionDuration = GetTime32(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("CurrentTime", n+offset, err) + return + } + self.CurrentTime = GetTime32(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("NextTrackId", n+offset, err) + return + } + self.NextTrackId = pio.I32BE(b[n:]) + n += 4 + return +} +func (self MovieHeader) Children() (r []Atom) { + return +} + +type Track struct { + Header *TrackHeader + Media *Media + Unknowns []Atom + AtomPos +} + +func (self Track) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(TRAK)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self Track) marshal(b []byte) (n int) { + if self.Header != nil { + n += self.Header.Marshal(b[n:]) + } + if self.Media != nil { + n += self.Media.Marshal(b[n:]) + } + for _, atom := range self.Unknowns { + n += atom.Marshal(b[n:]) + } + return +} +func (self Track) Len() (n int) { + n += 8 + if self.Header != nil { + n += self.Header.Len() + } + if self.Media != nil { + n += self.Media.Len() + } + for _, atom := range self.Unknowns { + n += atom.Len() + } + return +} +func (self *Track) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + for n+8 < len(b) { + tag := Tag(pio.U32BE(b[n+4:])) + size := int(pio.U32BE(b[n:])) + if len(b) < n+size { + err = parseErr("TagSizeInvalid", n+offset, err) + return + } + switch tag { + case TKHD: + { + atom := &TrackHeader{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("tkhd", n+offset, err) + return + } + self.Header = atom + } + case MDIA: + { + atom := &Media{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("mdia", n+offset, err) + return + } + self.Media = atom + } + default: + { + atom := &Dummy{Tag_: tag, Data: b[n : n+size]} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("", n+offset, err) + return + } + self.Unknowns = append(self.Unknowns, atom) + } + } + n += size + } + return +} +func (self Track) Children() (r []Atom) { + if self.Header != nil { + r = append(r, self.Header) + } + if self.Media != nil { + r = append(r, self.Media) + } + r = append(r, self.Unknowns...) + return +} + +type TrackHeader struct { + Version uint8 + Flags uint32 + CreateTime time.Time + ModifyTime time.Time + TrackId int32 + Duration int32 + Layer int16 + AlternateGroup int16 + Volume float64 + Matrix [9]int32 + TrackWidth float64 + TrackHeight float64 + AtomPos +} + +func (self TrackHeader) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(TKHD)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self TrackHeader) marshal(b []byte) (n int) { + pio.PutU8(b[n:], self.Version) + n += 1 + pio.PutU24BE(b[n:], self.Flags) + n += 3 + PutTime32(b[n:], self.CreateTime) + n += 4 + PutTime32(b[n:], self.ModifyTime) + n += 4 + pio.PutI32BE(b[n:], self.TrackId) + n += 4 + n += 4 + pio.PutI32BE(b[n:], self.Duration) + n += 4 + n += 8 + pio.PutI16BE(b[n:], self.Layer) + n += 2 + pio.PutI16BE(b[n:], self.AlternateGroup) + n += 2 + PutFixed16(b[n:], self.Volume) + n += 2 + n += 2 + for _, entry := range self.Matrix { + pio.PutI32BE(b[n:], entry) + n += 4 + } + PutFixed32(b[n:], self.TrackWidth) + n += 4 + PutFixed32(b[n:], self.TrackHeight) + n += 4 + return +} +func (self TrackHeader) Len() (n int) { + n += 8 + n += 1 + n += 3 + n += 4 + n += 4 + n += 4 + n += 4 + n += 4 + n += 8 + n += 2 + n += 2 + n += 2 + n += 2 + n += 4 * len(self.Matrix[:]) + n += 4 + n += 4 + return +} +func (self *TrackHeader) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + if len(b) < n+1 { + err = parseErr("Version", n+offset, err) + return + } + self.Version = pio.U8(b[n:]) + n += 1 + if len(b) < n+3 { + err = parseErr("Flags", n+offset, err) + return + } + self.Flags = pio.U24BE(b[n:]) + n += 3 + if len(b) < n+4 { + err = parseErr("CreateTime", n+offset, err) + return + } + self.CreateTime = GetTime32(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("ModifyTime", n+offset, err) + return + } + self.ModifyTime = GetTime32(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("TrackId", n+offset, err) + return + } + self.TrackId = pio.I32BE(b[n:]) + n += 4 + n += 4 + if len(b) < n+4 { + err = parseErr("Duration", n+offset, err) + return + } + self.Duration = pio.I32BE(b[n:]) + n += 4 + n += 8 + if len(b) < n+2 { + err = parseErr("Layer", n+offset, err) + return + } + self.Layer = pio.I16BE(b[n:]) + n += 2 + if len(b) < n+2 { + err = parseErr("AlternateGroup", n+offset, err) + return + } + self.AlternateGroup = pio.I16BE(b[n:]) + n += 2 + if len(b) < n+2 { + err = parseErr("Volume", n+offset, err) + return + } + self.Volume = GetFixed16(b[n:]) + n += 2 + n += 2 + if len(b) < n+4*len(self.Matrix) { + err = parseErr("Matrix", n+offset, err) + return + } + for i := range self.Matrix { + self.Matrix[i] = pio.I32BE(b[n:]) + n += 4 + } + if len(b) < n+4 { + err = parseErr("TrackWidth", n+offset, err) + return + } + self.TrackWidth = GetFixed32(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("TrackHeight", n+offset, err) + return + } + self.TrackHeight = GetFixed32(b[n:]) + n += 4 + return +} +func (self TrackHeader) Children() (r []Atom) { + return +} + +type HandlerRefer struct { + Version uint8 + Flags uint32 + Type [4]byte + SubType [4]byte + Name []byte + AtomPos +} + +func (self HandlerRefer) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(HDLR)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self HandlerRefer) marshal(b []byte) (n int) { + pio.PutU8(b[n:], self.Version) + n += 1 + pio.PutU24BE(b[n:], self.Flags) + n += 3 + copy(b[n:], self.Type[:]) + n += len(self.Type[:]) + copy(b[n:], self.SubType[:]) + n += len(self.SubType[:]) + copy(b[n:], self.Name[:]) + n += len(self.Name[:]) + return +} +func (self HandlerRefer) Len() (n int) { + n += 8 + n += 1 + n += 3 + n += len(self.Type[:]) + n += len(self.SubType[:]) + n += len(self.Name[:]) + return +} +func (self *HandlerRefer) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + if len(b) < n+1 { + err = parseErr("Version", n+offset, err) + return + } + self.Version = pio.U8(b[n:]) + n += 1 + if len(b) < n+3 { + err = parseErr("Flags", n+offset, err) + return + } + self.Flags = pio.U24BE(b[n:]) + n += 3 + if len(b) < n+len(self.Type) { + err = parseErr("Type", n+offset, err) + return + } + copy(self.Type[:], b[n:]) + n += len(self.Type) + if len(b) < n+len(self.SubType) { + err = parseErr("SubType", n+offset, err) + return + } + copy(self.SubType[:], b[n:]) + n += len(self.SubType) + self.Name = b[n:] + n += len(b[n:]) + return +} +func (self HandlerRefer) Children() (r []Atom) { + return +} + +type Media struct { + Header *MediaHeader + Handler *HandlerRefer + Info *MediaInfo + Unknowns []Atom + AtomPos +} + +func (self Media) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(MDIA)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self Media) marshal(b []byte) (n int) { + if self.Header != nil { + n += self.Header.Marshal(b[n:]) + } + if self.Handler != nil { + n += self.Handler.Marshal(b[n:]) + } + if self.Info != nil { + n += self.Info.Marshal(b[n:]) + } + for _, atom := range self.Unknowns { + n += atom.Marshal(b[n:]) + } + return +} +func (self Media) Len() (n int) { + n += 8 + if self.Header != nil { + n += self.Header.Len() + } + if self.Handler != nil { + n += self.Handler.Len() + } + if self.Info != nil { + n += self.Info.Len() + } + for _, atom := range self.Unknowns { + n += atom.Len() + } + return +} +func (self *Media) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + for n+8 < len(b) { + tag := Tag(pio.U32BE(b[n+4:])) + size := int(pio.U32BE(b[n:])) + if len(b) < n+size { + err = parseErr("TagSizeInvalid", n+offset, err) + return + } + switch tag { + case MDHD: + { + atom := &MediaHeader{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("mdhd", n+offset, err) + return + } + self.Header = atom + } + case HDLR: + { + atom := &HandlerRefer{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("hdlr", n+offset, err) + return + } + self.Handler = atom + } + case MINF: + { + atom := &MediaInfo{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("minf", n+offset, err) + return + } + self.Info = atom + } + default: + { + atom := &Dummy{Tag_: tag, Data: b[n : n+size]} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("", n+offset, err) + return + } + self.Unknowns = append(self.Unknowns, atom) + } + } + n += size + } + return +} +func (self Media) Children() (r []Atom) { + if self.Header != nil { + r = append(r, self.Header) + } + if self.Handler != nil { + r = append(r, self.Handler) + } + if self.Info != nil { + r = append(r, self.Info) + } + r = append(r, self.Unknowns...) + return +} + +type MediaHeader struct { + Version uint8 + Flags uint32 + CreateTime time.Time + ModifyTime time.Time + TimeScale int32 + Duration int32 + Language int16 + Quality int16 + AtomPos +} + +func (self MediaHeader) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(MDHD)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self MediaHeader) marshal(b []byte) (n int) { + pio.PutU8(b[n:], self.Version) + n += 1 + pio.PutU24BE(b[n:], self.Flags) + n += 3 + PutTime32(b[n:], self.CreateTime) + n += 4 + PutTime32(b[n:], self.ModifyTime) + n += 4 + pio.PutI32BE(b[n:], self.TimeScale) + n += 4 + pio.PutI32BE(b[n:], self.Duration) + n += 4 + pio.PutI16BE(b[n:], self.Language) + n += 2 + pio.PutI16BE(b[n:], self.Quality) + n += 2 + return +} +func (self MediaHeader) Len() (n int) { + n += 8 + n += 1 + n += 3 + n += 4 + n += 4 + n += 4 + n += 4 + n += 2 + n += 2 + return +} +func (self *MediaHeader) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + if len(b) < n+1 { + err = parseErr("Version", n+offset, err) + return + } + self.Version = pio.U8(b[n:]) + n += 1 + if len(b) < n+3 { + err = parseErr("Flags", n+offset, err) + return + } + self.Flags = pio.U24BE(b[n:]) + n += 3 + if len(b) < n+4 { + err = parseErr("CreateTime", n+offset, err) + return + } + self.CreateTime = GetTime32(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("ModifyTime", n+offset, err) + return + } + self.ModifyTime = GetTime32(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("TimeScale", n+offset, err) + return + } + self.TimeScale = pio.I32BE(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("Duration", n+offset, err) + return + } + self.Duration = pio.I32BE(b[n:]) + n += 4 + if len(b) < n+2 { + err = parseErr("Language", n+offset, err) + return + } + self.Language = pio.I16BE(b[n:]) + n += 2 + if len(b) < n+2 { + err = parseErr("Quality", n+offset, err) + return + } + self.Quality = pio.I16BE(b[n:]) + n += 2 + return +} +func (self MediaHeader) Children() (r []Atom) { + return +} + +type MediaInfo struct { + Sound *SoundMediaInfo + Video *VideoMediaInfo + Data *DataInfo + Sample *SampleTable + Unknowns []Atom + AtomPos +} + +func (self MediaInfo) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(MINF)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self MediaInfo) marshal(b []byte) (n int) { + if self.Sound != nil { + n += self.Sound.Marshal(b[n:]) + } + if self.Video != nil { + n += self.Video.Marshal(b[n:]) + } + if self.Data != nil { + n += self.Data.Marshal(b[n:]) + } + if self.Sample != nil { + n += self.Sample.Marshal(b[n:]) + } + for _, atom := range self.Unknowns { + n += atom.Marshal(b[n:]) + } + return +} +func (self MediaInfo) Len() (n int) { + n += 8 + if self.Sound != nil { + n += self.Sound.Len() + } + if self.Video != nil { + n += self.Video.Len() + } + if self.Data != nil { + n += self.Data.Len() + } + if self.Sample != nil { + n += self.Sample.Len() + } + for _, atom := range self.Unknowns { + n += atom.Len() + } + return +} +func (self *MediaInfo) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + for n+8 < len(b) { + tag := Tag(pio.U32BE(b[n+4:])) + size := int(pio.U32BE(b[n:])) + if len(b) < n+size { + err = parseErr("TagSizeInvalid", n+offset, err) + return + } + switch tag { + case SMHD: + { + atom := &SoundMediaInfo{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("smhd", n+offset, err) + return + } + self.Sound = atom + } + case VMHD: + { + atom := &VideoMediaInfo{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("vmhd", n+offset, err) + return + } + self.Video = atom + } + case DINF: + { + atom := &DataInfo{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("dinf", n+offset, err) + return + } + self.Data = atom + } + case STBL: + { + atom := &SampleTable{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("stbl", n+offset, err) + return + } + self.Sample = atom + } + default: + { + atom := &Dummy{Tag_: tag, Data: b[n : n+size]} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("", n+offset, err) + return + } + self.Unknowns = append(self.Unknowns, atom) + } + } + n += size + } + return +} +func (self MediaInfo) Children() (r []Atom) { + if self.Sound != nil { + r = append(r, self.Sound) + } + if self.Video != nil { + r = append(r, self.Video) + } + if self.Data != nil { + r = append(r, self.Data) + } + if self.Sample != nil { + r = append(r, self.Sample) + } + r = append(r, self.Unknowns...) + return +} + +type DataInfo struct { + Refer *DataRefer + Unknowns []Atom + AtomPos +} + +func (self DataInfo) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(DINF)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self DataInfo) marshal(b []byte) (n int) { + if self.Refer != nil { + n += self.Refer.Marshal(b[n:]) + } + for _, atom := range self.Unknowns { + n += atom.Marshal(b[n:]) + } + return +} +func (self DataInfo) Len() (n int) { + n += 8 + if self.Refer != nil { + n += self.Refer.Len() + } + for _, atom := range self.Unknowns { + n += atom.Len() + } + return +} +func (self *DataInfo) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + for n+8 < len(b) { + tag := Tag(pio.U32BE(b[n+4:])) + size := int(pio.U32BE(b[n:])) + if len(b) < n+size { + err = parseErr("TagSizeInvalid", n+offset, err) + return + } + switch tag { + case DREF: + { + atom := &DataRefer{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("dref", n+offset, err) + return + } + self.Refer = atom + } + default: + { + atom := &Dummy{Tag_: tag, Data: b[n : n+size]} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("", n+offset, err) + return + } + self.Unknowns = append(self.Unknowns, atom) + } + } + n += size + } + return +} +func (self DataInfo) Children() (r []Atom) { + if self.Refer != nil { + r = append(r, self.Refer) + } + r = append(r, self.Unknowns...) + return +} + +type DataRefer struct { + Version uint8 + Flags uint32 + Url *DataReferUrl + AtomPos +} + +func (self DataRefer) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(DREF)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self DataRefer) marshal(b []byte) (n int) { + pio.PutU8(b[n:], self.Version) + n += 1 + pio.PutU24BE(b[n:], self.Flags) + n += 3 + _childrenNR := 0 + if self.Url != nil { + _childrenNR++ + } + pio.PutI32BE(b[n:], int32(_childrenNR)) + n += 4 + if self.Url != nil { + n += self.Url.Marshal(b[n:]) + } + return +} +func (self DataRefer) Len() (n int) { + n += 8 + n += 1 + n += 3 + n += 4 + if self.Url != nil { + n += self.Url.Len() + } + return +} +func (self *DataRefer) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + if len(b) < n+1 { + err = parseErr("Version", n+offset, err) + return + } + self.Version = pio.U8(b[n:]) + n += 1 + if len(b) < n+3 { + err = parseErr("Flags", n+offset, err) + return + } + self.Flags = pio.U24BE(b[n:]) + n += 3 + n += 4 + for n+8 < len(b) { + tag := Tag(pio.U32BE(b[n+4:])) + size := int(pio.U32BE(b[n:])) + if len(b) < n+size { + err = parseErr("TagSizeInvalid", n+offset, err) + return + } + switch tag { + case URL: + { + atom := &DataReferUrl{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("url ", n+offset, err) + return + } + self.Url = atom + } + } + n += size + } + return +} +func (self DataRefer) Children() (r []Atom) { + if self.Url != nil { + r = append(r, self.Url) + } + return +} + +type DataReferUrl struct { + Version uint8 + Flags uint32 + AtomPos +} + +func (self DataReferUrl) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(URL)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self DataReferUrl) marshal(b []byte) (n int) { + pio.PutU8(b[n:], self.Version) + n += 1 + pio.PutU24BE(b[n:], self.Flags) + n += 3 + return +} +func (self DataReferUrl) Len() (n int) { + n += 8 + n += 1 + n += 3 + return +} +func (self *DataReferUrl) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + if len(b) < n+1 { + err = parseErr("Version", n+offset, err) + return + } + self.Version = pio.U8(b[n:]) + n += 1 + if len(b) < n+3 { + err = parseErr("Flags", n+offset, err) + return + } + self.Flags = pio.U24BE(b[n:]) + n += 3 + return +} +func (self DataReferUrl) Children() (r []Atom) { + return +} + +type SoundMediaInfo struct { + Version uint8 + Flags uint32 + Balance int16 + AtomPos +} + +func (self SoundMediaInfo) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(SMHD)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self SoundMediaInfo) marshal(b []byte) (n int) { + pio.PutU8(b[n:], self.Version) + n += 1 + pio.PutU24BE(b[n:], self.Flags) + n += 3 + pio.PutI16BE(b[n:], self.Balance) + n += 2 + n += 2 + return +} +func (self SoundMediaInfo) Len() (n int) { + n += 8 + n += 1 + n += 3 + n += 2 + n += 2 + return +} +func (self *SoundMediaInfo) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + if len(b) < n+1 { + err = parseErr("Version", n+offset, err) + return + } + self.Version = pio.U8(b[n:]) + n += 1 + if len(b) < n+3 { + err = parseErr("Flags", n+offset, err) + return + } + self.Flags = pio.U24BE(b[n:]) + n += 3 + if len(b) < n+2 { + err = parseErr("Balance", n+offset, err) + return + } + self.Balance = pio.I16BE(b[n:]) + n += 2 + n += 2 + return +} +func (self SoundMediaInfo) Children() (r []Atom) { + return +} + +type VideoMediaInfo struct { + Version uint8 + Flags uint32 + GraphicsMode int16 + Opcolor [3]int16 + AtomPos +} + +func (self VideoMediaInfo) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(VMHD)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self VideoMediaInfo) marshal(b []byte) (n int) { + pio.PutU8(b[n:], self.Version) + n += 1 + pio.PutU24BE(b[n:], self.Flags) + n += 3 + pio.PutI16BE(b[n:], self.GraphicsMode) + n += 2 + for _, entry := range self.Opcolor { + pio.PutI16BE(b[n:], entry) + n += 2 + } + return +} +func (self VideoMediaInfo) Len() (n int) { + n += 8 + n += 1 + n += 3 + n += 2 + n += 2 * len(self.Opcolor[:]) + return +} +func (self *VideoMediaInfo) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + if len(b) < n+1 { + err = parseErr("Version", n+offset, err) + return + } + self.Version = pio.U8(b[n:]) + n += 1 + if len(b) < n+3 { + err = parseErr("Flags", n+offset, err) + return + } + self.Flags = pio.U24BE(b[n:]) + n += 3 + if len(b) < n+2 { + err = parseErr("GraphicsMode", n+offset, err) + return + } + self.GraphicsMode = pio.I16BE(b[n:]) + n += 2 + if len(b) < n+2*len(self.Opcolor) { + err = parseErr("Opcolor", n+offset, err) + return + } + for i := range self.Opcolor { + self.Opcolor[i] = pio.I16BE(b[n:]) + n += 2 + } + return +} +func (self VideoMediaInfo) Children() (r []Atom) { + return +} + +type SampleTable struct { + SampleDesc *SampleDesc + TimeToSample *TimeToSample + CompositionOffset *CompositionOffset + SampleToChunk *SampleToChunk + SyncSample *SyncSample + ChunkOffset *ChunkOffset + SampleSize *SampleSize + AtomPos +} + +func (self SampleTable) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(STBL)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self SampleTable) marshal(b []byte) (n int) { + if self.SampleDesc != nil { + n += self.SampleDesc.Marshal(b[n:]) + } + if self.TimeToSample != nil { + n += self.TimeToSample.Marshal(b[n:]) + } + if self.CompositionOffset != nil { + n += self.CompositionOffset.Marshal(b[n:]) + } + if self.SampleToChunk != nil { + n += self.SampleToChunk.Marshal(b[n:]) + } + if self.SyncSample != nil { + n += self.SyncSample.Marshal(b[n:]) + } + if self.ChunkOffset != nil { + n += self.ChunkOffset.Marshal(b[n:]) + } + if self.SampleSize != nil { + n += self.SampleSize.Marshal(b[n:]) + } + return +} +func (self SampleTable) Len() (n int) { + n += 8 + if self.SampleDesc != nil { + n += self.SampleDesc.Len() + } + if self.TimeToSample != nil { + n += self.TimeToSample.Len() + } + if self.CompositionOffset != nil { + n += self.CompositionOffset.Len() + } + if self.SampleToChunk != nil { + n += self.SampleToChunk.Len() + } + if self.SyncSample != nil { + n += self.SyncSample.Len() + } + if self.ChunkOffset != nil { + n += self.ChunkOffset.Len() + } + if self.SampleSize != nil { + n += self.SampleSize.Len() + } + return +} +func (self *SampleTable) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + for n+8 < len(b) { + tag := Tag(pio.U32BE(b[n+4:])) + size := int(pio.U32BE(b[n:])) + if len(b) < n+size { + err = parseErr("TagSizeInvalid", n+offset, err) + return + } + switch tag { + case STSD: + { + atom := &SampleDesc{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("stsd", n+offset, err) + return + } + self.SampleDesc = atom + } + case STTS: + { + atom := &TimeToSample{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("stts", n+offset, err) + return + } + self.TimeToSample = atom + } + case CTTS: + { + atom := &CompositionOffset{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("ctts", n+offset, err) + return + } + self.CompositionOffset = atom + } + case STSC: + { + atom := &SampleToChunk{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("stsc", n+offset, err) + return + } + self.SampleToChunk = atom + } + case STSS: + { + atom := &SyncSample{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("stss", n+offset, err) + return + } + self.SyncSample = atom + } + case STCO: + { + atom := &ChunkOffset{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("stco", n+offset, err) + return + } + self.ChunkOffset = atom + } + case STSZ: + { + atom := &SampleSize{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("stsz", n+offset, err) + return + } + self.SampleSize = atom + } + } + n += size + } + return +} +func (self SampleTable) Children() (r []Atom) { + if self.SampleDesc != nil { + r = append(r, self.SampleDesc) + } + if self.TimeToSample != nil { + r = append(r, self.TimeToSample) + } + if self.CompositionOffset != nil { + r = append(r, self.CompositionOffset) + } + if self.SampleToChunk != nil { + r = append(r, self.SampleToChunk) + } + if self.SyncSample != nil { + r = append(r, self.SyncSample) + } + if self.ChunkOffset != nil { + r = append(r, self.ChunkOffset) + } + if self.SampleSize != nil { + r = append(r, self.SampleSize) + } + return +} + +type SampleDesc struct { + Version uint8 + AVC1Desc *AVC1Desc + HV1Desc *HV1Desc + MP4ADesc *MP4ADesc + Unknowns []Atom + AtomPos +} + +func (self SampleDesc) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(STSD)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self SampleDesc) marshal(b []byte) (n int) { + pio.PutU8(b[n:], self.Version) + n += 1 + n += 3 + _childrenNR := 0 + if self.AVC1Desc != nil { + _childrenNR++ + } + if self.HV1Desc != nil { + _childrenNR++ + } + if self.MP4ADesc != nil { + _childrenNR++ + } + _childrenNR += len(self.Unknowns) + pio.PutI32BE(b[n:], int32(_childrenNR)) + n += 4 + if self.AVC1Desc != nil { + n += self.AVC1Desc.Marshal(b[n:]) + } + if self.HV1Desc != nil { + n += self.HV1Desc.Marshal(b[n:]) + } + if self.MP4ADesc != nil { + n += self.MP4ADesc.Marshal(b[n:]) + } + for _, atom := range self.Unknowns { + n += atom.Marshal(b[n:]) + } + return +} +func (self SampleDesc) Len() (n int) { + n += 8 + n += 1 + n += 3 + n += 4 + if self.AVC1Desc != nil { + n += self.AVC1Desc.Len() + } + if self.HV1Desc != nil { + n += self.HV1Desc.Len() + } + if self.MP4ADesc != nil { + n += self.MP4ADesc.Len() + } + for _, atom := range self.Unknowns { + n += atom.Len() + } + return +} +func (self *SampleDesc) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + if len(b) < n+1 { + err = parseErr("Version", n+offset, err) + return + } + self.Version = pio.U8(b[n:]) + n += 1 + n += 3 + n += 4 + for n+8 < len(b) { + tag := Tag(pio.U32BE(b[n+4:])) + size := int(pio.U32BE(b[n:])) + if len(b) < n+size { + err = parseErr("TagSizeInvalid", n+offset, err) + return + } + switch tag { + case AVC1: + { + atom := &AVC1Desc{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("avc1", n+offset, err) + return + } + self.AVC1Desc = atom + } + case HEV1: + { + atom := &HV1Desc{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("hec1", n+offset, err) + return + } + self.HV1Desc = atom + } + case MP4A: + { + atom := &MP4ADesc{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("mp4a", n+offset, err) + return + } + self.MP4ADesc = atom + } + default: + { + atom := &Dummy{Tag_: tag, Data: b[n : n+size]} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("", n+offset, err) + return + } + self.Unknowns = append(self.Unknowns, atom) + } + } + n += size + } + return +} +func (self SampleDesc) Children() (r []Atom) { + if self.AVC1Desc != nil { + r = append(r, self.AVC1Desc) + } + if self.HV1Desc != nil { + r = append(r, self.HV1Desc) + } + if self.MP4ADesc != nil { + r = append(r, self.MP4ADesc) + } + r = append(r, self.Unknowns...) + return +} + +type MP4ADesc struct { + DataRefIdx int16 + Version int16 + RevisionLevel int16 + Vendor int32 + NumberOfChannels int16 + SampleSize int16 + CompressionId int16 + SampleRate float64 + Conf *ElemStreamDesc + Unknowns []Atom + AtomPos +} + +func (self MP4ADesc) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(MP4A)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self MP4ADesc) marshal(b []byte) (n int) { + n += 6 + pio.PutI16BE(b[n:], self.DataRefIdx) + n += 2 + pio.PutI16BE(b[n:], self.Version) + n += 2 + pio.PutI16BE(b[n:], self.RevisionLevel) + n += 2 + pio.PutI32BE(b[n:], self.Vendor) + n += 4 + pio.PutI16BE(b[n:], self.NumberOfChannels) + n += 2 + pio.PutI16BE(b[n:], self.SampleSize) + n += 2 + pio.PutI16BE(b[n:], self.CompressionId) + n += 2 + n += 2 + PutFixed32(b[n:], self.SampleRate) + n += 4 + if self.Conf != nil { + n += self.Conf.Marshal(b[n:]) + } + for _, atom := range self.Unknowns { + n += atom.Marshal(b[n:]) + } + return +} +func (self MP4ADesc) Len() (n int) { + n += 8 + n += 6 + n += 2 + n += 2 + n += 2 + n += 4 + n += 2 + n += 2 + n += 2 + n += 2 + n += 4 + if self.Conf != nil { + n += self.Conf.Len() + } + for _, atom := range self.Unknowns { + n += atom.Len() + } + return +} +func (self *MP4ADesc) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + n += 6 + if len(b) < n+2 { + err = parseErr("DataRefIdx", n+offset, err) + return + } + self.DataRefIdx = pio.I16BE(b[n:]) + n += 2 + if len(b) < n+2 { + err = parseErr("Version", n+offset, err) + return + } + self.Version = pio.I16BE(b[n:]) + n += 2 + if len(b) < n+2 { + err = parseErr("RevisionLevel", n+offset, err) + return + } + self.RevisionLevel = pio.I16BE(b[n:]) + n += 2 + if len(b) < n+4 { + err = parseErr("Vendor", n+offset, err) + return + } + self.Vendor = pio.I32BE(b[n:]) + n += 4 + if len(b) < n+2 { + err = parseErr("NumberOfChannels", n+offset, err) + return + } + self.NumberOfChannels = pio.I16BE(b[n:]) + n += 2 + if len(b) < n+2 { + err = parseErr("SampleSize", n+offset, err) + return + } + self.SampleSize = pio.I16BE(b[n:]) + n += 2 + if len(b) < n+2 { + err = parseErr("CompressionId", n+offset, err) + return + } + self.CompressionId = pio.I16BE(b[n:]) + n += 2 + n += 2 + if len(b) < n+4 { + err = parseErr("SampleRate", n+offset, err) + return + } + self.SampleRate = GetFixed32(b[n:]) + n += 4 + for n+8 < len(b) { + tag := Tag(pio.U32BE(b[n+4:])) + size := int(pio.U32BE(b[n:])) + if len(b) < n+size { + err = parseErr("TagSizeInvalid", n+offset, err) + return + } + switch tag { + case ESDS: + { + atom := &ElemStreamDesc{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("esds", n+offset, err) + return + } + self.Conf = atom + } + default: + { + atom := &Dummy{Tag_: tag, Data: b[n : n+size]} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("", n+offset, err) + return + } + self.Unknowns = append(self.Unknowns, atom) + } + } + n += size + } + return +} +func (self MP4ADesc) Children() (r []Atom) { + if self.Conf != nil { + r = append(r, self.Conf) + } + r = append(r, self.Unknowns...) + return +} + +type HV1Desc struct { + DataRefIdx int16 + Version int16 + Revision int16 + Vendor int32 + TemporalQuality int32 + SpatialQuality int32 + Width int16 + Height int16 + HorizontalResolution float64 + VorizontalResolution float64 + FrameCount int16 + CompressorName [32]byte + Depth int16 + ColorTableId int16 + Conf *HV1Conf + Unknowns []Atom + AtomPos +} +type AVC1Desc struct { + DataRefIdx int16 + Version int16 + Revision int16 + Vendor int32 + TemporalQuality int32 + SpatialQuality int32 + Width int16 + Height int16 + HorizontalResolution float64 + VorizontalResolution float64 + FrameCount int16 + CompressorName [32]byte + Depth int16 + ColorTableId int16 + Conf *AVC1Conf + Unknowns []Atom + AtomPos +} + +func (self AVC1Desc) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(AVC1)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self HV1Desc) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(HEV1)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self AVC1Desc) marshal(b []byte) (n int) { + n += 6 + pio.PutI16BE(b[n:], self.DataRefIdx) + n += 2 + pio.PutI16BE(b[n:], self.Version) + n += 2 + pio.PutI16BE(b[n:], self.Revision) + n += 2 + pio.PutI32BE(b[n:], self.Vendor) + n += 4 + pio.PutI32BE(b[n:], self.TemporalQuality) + n += 4 + pio.PutI32BE(b[n:], self.SpatialQuality) + n += 4 + pio.PutI16BE(b[n:], self.Width) + n += 2 + pio.PutI16BE(b[n:], self.Height) + n += 2 + PutFixed32(b[n:], self.HorizontalResolution) + n += 4 + PutFixed32(b[n:], self.VorizontalResolution) + n += 4 + n += 4 + pio.PutI16BE(b[n:], self.FrameCount) + n += 2 + copy(b[n:], self.CompressorName[:]) + n += len(self.CompressorName[:]) + pio.PutI16BE(b[n:], self.Depth) + n += 2 + pio.PutI16BE(b[n:], self.ColorTableId) + n += 2 + if self.Conf != nil { + n += self.Conf.Marshal(b[n:]) + } + for _, atom := range self.Unknowns { + n += atom.Marshal(b[n:]) + } + return +} +func (self HV1Desc) marshal(b []byte) (n int) { + n += 6 + pio.PutI16BE(b[n:], self.DataRefIdx) + n += 2 + pio.PutI16BE(b[n:], self.Version) + n += 2 + pio.PutI16BE(b[n:], self.Revision) + n += 2 + pio.PutI32BE(b[n:], self.Vendor) + n += 4 + pio.PutI32BE(b[n:], self.TemporalQuality) + n += 4 + pio.PutI32BE(b[n:], self.SpatialQuality) + n += 4 + pio.PutI16BE(b[n:], self.Width) + n += 2 + pio.PutI16BE(b[n:], self.Height) + n += 2 + PutFixed32(b[n:], self.HorizontalResolution) + n += 4 + PutFixed32(b[n:], self.VorizontalResolution) + n += 4 + n += 4 + pio.PutI16BE(b[n:], self.FrameCount) + n += 2 + copy(b[n:], self.CompressorName[:]) + n += len(self.CompressorName[:]) + pio.PutI16BE(b[n:], self.Depth) + n += 2 + pio.PutI16BE(b[n:], self.ColorTableId) + n += 2 + if self.Conf != nil { + n += self.Conf.Marshal(b[n:]) + } + for _, atom := range self.Unknowns { + n += atom.Marshal(b[n:]) + } + return +} +func (self AVC1Desc) Len() (n int) { + n += 8 + n += 6 + n += 2 + n += 2 + n += 2 + n += 4 + n += 4 + n += 4 + n += 2 + n += 2 + n += 4 + n += 4 + n += 4 + n += 2 + n += len(self.CompressorName[:]) + n += 2 + n += 2 + if self.Conf != nil { + n += self.Conf.Len() + } + for _, atom := range self.Unknowns { + n += atom.Len() + } + return +} +func (self HV1Desc) Len() (n int) { + n += 8 + n += 6 + n += 2 + n += 2 + n += 2 + n += 4 + n += 4 + n += 4 + n += 2 + n += 2 + n += 4 + n += 4 + n += 4 + n += 2 + n += len(self.CompressorName[:]) + n += 2 + n += 2 + if self.Conf != nil { + n += self.Conf.Len() + } + for _, atom := range self.Unknowns { + n += atom.Len() + } + return +} +func (self *AVC1Desc) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + n += 6 + if len(b) < n+2 { + err = parseErr("DataRefIdx", n+offset, err) + return + } + self.DataRefIdx = pio.I16BE(b[n:]) + n += 2 + if len(b) < n+2 { + err = parseErr("Version", n+offset, err) + return + } + self.Version = pio.I16BE(b[n:]) + n += 2 + if len(b) < n+2 { + err = parseErr("Revision", n+offset, err) + return + } + self.Revision = pio.I16BE(b[n:]) + n += 2 + if len(b) < n+4 { + err = parseErr("Vendor", n+offset, err) + return + } + self.Vendor = pio.I32BE(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("TemporalQuality", n+offset, err) + return + } + self.TemporalQuality = pio.I32BE(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("SpatialQuality", n+offset, err) + return + } + self.SpatialQuality = pio.I32BE(b[n:]) + n += 4 + if len(b) < n+2 { + err = parseErr("Width", n+offset, err) + return + } + self.Width = pio.I16BE(b[n:]) + n += 2 + if len(b) < n+2 { + err = parseErr("Height", n+offset, err) + return + } + self.Height = pio.I16BE(b[n:]) + n += 2 + if len(b) < n+4 { + err = parseErr("HorizontalResolution", n+offset, err) + return + } + self.HorizontalResolution = GetFixed32(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("VorizontalResolution", n+offset, err) + return + } + self.VorizontalResolution = GetFixed32(b[n:]) + n += 4 + n += 4 + if len(b) < n+2 { + err = parseErr("FrameCount", n+offset, err) + return + } + self.FrameCount = pio.I16BE(b[n:]) + n += 2 + if len(b) < n+len(self.CompressorName) { + err = parseErr("CompressorName", n+offset, err) + return + } + copy(self.CompressorName[:], b[n:]) + n += len(self.CompressorName) + if len(b) < n+2 { + err = parseErr("Depth", n+offset, err) + return + } + self.Depth = pio.I16BE(b[n:]) + n += 2 + if len(b) < n+2 { + err = parseErr("ColorTableId", n+offset, err) + return + } + self.ColorTableId = pio.I16BE(b[n:]) + n += 2 + for n+8 < len(b) { + tag := Tag(pio.U32BE(b[n+4:])) + size := int(pio.U32BE(b[n:])) + if len(b) < n+size { + err = parseErr("TagSizeInvalid", n+offset, err) + return + } + switch tag { + case AVCC: + { + atom := &AVC1Conf{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("avcC", n+offset, err) + return + } + self.Conf = atom + } + default: + { + atom := &Dummy{Tag_: tag, Data: b[n : n+size]} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("", n+offset, err) + return + } + self.Unknowns = append(self.Unknowns, atom) + } + } + n += size + } + return +} +func (self *HV1Desc) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + n += 6 + if len(b) < n+2 { + err = parseErr("DataRefIdx", n+offset, err) + return + } + self.DataRefIdx = pio.I16BE(b[n:]) + n += 2 + if len(b) < n+2 { + err = parseErr("Version", n+offset, err) + return + } + self.Version = pio.I16BE(b[n:]) + n += 2 + if len(b) < n+2 { + err = parseErr("Revision", n+offset, err) + return + } + self.Revision = pio.I16BE(b[n:]) + n += 2 + if len(b) < n+4 { + err = parseErr("Vendor", n+offset, err) + return + } + self.Vendor = pio.I32BE(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("TemporalQuality", n+offset, err) + return + } + self.TemporalQuality = pio.I32BE(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("SpatialQuality", n+offset, err) + return + } + self.SpatialQuality = pio.I32BE(b[n:]) + n += 4 + if len(b) < n+2 { + err = parseErr("Width", n+offset, err) + return + } + self.Width = pio.I16BE(b[n:]) + n += 2 + if len(b) < n+2 { + err = parseErr("Height", n+offset, err) + return + } + self.Height = pio.I16BE(b[n:]) + n += 2 + if len(b) < n+4 { + err = parseErr("HorizontalResolution", n+offset, err) + return + } + self.HorizontalResolution = GetFixed32(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("VorizontalResolution", n+offset, err) + return + } + self.VorizontalResolution = GetFixed32(b[n:]) + n += 4 + n += 4 + if len(b) < n+2 { + err = parseErr("FrameCount", n+offset, err) + return + } + self.FrameCount = pio.I16BE(b[n:]) + n += 2 + if len(b) < n+len(self.CompressorName) { + err = parseErr("CompressorName", n+offset, err) + return + } + copy(self.CompressorName[:], b[n:]) + n += len(self.CompressorName) + if len(b) < n+2 { + err = parseErr("Depth", n+offset, err) + return + } + self.Depth = pio.I16BE(b[n:]) + n += 2 + if len(b) < n+2 { + err = parseErr("ColorTableId", n+offset, err) + return + } + self.ColorTableId = pio.I16BE(b[n:]) + n += 2 + for n+8 < len(b) { + tag := Tag(pio.U32BE(b[n+4:])) + size := int(pio.U32BE(b[n:])) + if len(b) < n+size { + err = parseErr("TagSizeInvalid", n+offset, err) + return + } + switch tag { + case HVCC: + { + atom := &HV1Conf{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("hvcC", n+offset, err) + return + } + self.Conf = atom + } + default: + { + atom := &Dummy{Tag_: tag, Data: b[n : n+size]} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("", n+offset, err) + return + } + self.Unknowns = append(self.Unknowns, atom) + } + } + n += size + } + return +} +func (self AVC1Desc) Children() (r []Atom) { + if self.Conf != nil { + r = append(r, self.Conf) + } + r = append(r, self.Unknowns...) + return +} +func (self HV1Desc) Children() (r []Atom) { + if self.Conf != nil { + r = append(r, self.Conf) + } + r = append(r, self.Unknowns...) + return +} + +type AVC1Conf struct { + Data []byte + AtomPos +} + +type HV1Conf struct { + Data []byte + AtomPos +} + +func (self AVC1Conf) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(AVCC)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self AVC1Conf) marshal(b []byte) (n int) { + copy(b[n:], self.Data[:]) + n += len(self.Data[:]) + return +} +func (self AVC1Conf) Len() (n int) { + n += 8 + n += len(self.Data[:]) + return +} +func (self *AVC1Conf) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + self.Data = b[n:] + n += len(b[n:]) + return +} +func (self AVC1Conf) Children() (r []Atom) { + return +} + +/* +HVEC +*/ +func (self HV1Conf) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(HVCC)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self HV1Conf) marshal(b []byte) (n int) { + copy(b[n:], self.Data[:]) + n += len(self.Data[:]) + return +} +func (self HV1Conf) Len() (n int) { + n += 8 + n += len(self.Data[:]) + return +} +func (self *HV1Conf) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + self.Data = b[n:] + n += len(b[n:]) + return +} +func (self HV1Conf) Children() (r []Atom) { + return +} + +type TimeToSample struct { + Version uint8 + Flags uint32 + Entries []TimeToSampleEntry + AtomPos +} + +func (self TimeToSample) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(STTS)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self TimeToSample) marshal(b []byte) (n int) { + pio.PutU8(b[n:], self.Version) + n += 1 + pio.PutU24BE(b[n:], self.Flags) + n += 3 + pio.PutU32BE(b[n:], uint32(len(self.Entries))) + n += 4 + for _, entry := range self.Entries { + PutTimeToSampleEntry(b[n:], entry) + n += LenTimeToSampleEntry + } + return +} +func (self TimeToSample) Len() (n int) { + n += 8 + n += 1 + n += 3 + n += 4 + n += LenTimeToSampleEntry * len(self.Entries) + return +} +func (self *TimeToSample) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + if len(b) < n+1 { + err = parseErr("Version", n+offset, err) + return + } + self.Version = pio.U8(b[n:]) + n += 1 + if len(b) < n+3 { + err = parseErr("Flags", n+offset, err) + return + } + self.Flags = pio.U24BE(b[n:]) + n += 3 + var _len_Entries uint32 + _len_Entries = pio.U32BE(b[n:]) + n += 4 + self.Entries = make([]TimeToSampleEntry, _len_Entries) + if len(b) < n+LenTimeToSampleEntry*len(self.Entries) { + err = parseErr("TimeToSampleEntry", n+offset, err) + return + } + for i := range self.Entries { + self.Entries[i] = GetTimeToSampleEntry(b[n:]) + n += LenTimeToSampleEntry + } + return +} +func (self TimeToSample) Children() (r []Atom) { + return +} + +type TimeToSampleEntry struct { + Count uint32 + Duration uint32 +} + +func GetTimeToSampleEntry(b []byte) (self TimeToSampleEntry) { + self.Count = pio.U32BE(b[0:]) + self.Duration = pio.U32BE(b[4:]) + return +} +func PutTimeToSampleEntry(b []byte, self TimeToSampleEntry) { + pio.PutU32BE(b[0:], self.Count) + pio.PutU32BE(b[4:], self.Duration) +} + +const LenTimeToSampleEntry = 8 + +type SampleToChunk struct { + Version uint8 + Flags uint32 + Entries []SampleToChunkEntry + AtomPos +} + +func (self SampleToChunk) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(STSC)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self SampleToChunk) marshal(b []byte) (n int) { + pio.PutU8(b[n:], self.Version) + n += 1 + pio.PutU24BE(b[n:], self.Flags) + n += 3 + pio.PutU32BE(b[n:], uint32(len(self.Entries))) + n += 4 + for _, entry := range self.Entries { + PutSampleToChunkEntry(b[n:], entry) + n += LenSampleToChunkEntry + } + return +} +func (self SampleToChunk) Len() (n int) { + n += 8 + n += 1 + n += 3 + n += 4 + n += LenSampleToChunkEntry * len(self.Entries) + return +} +func (self *SampleToChunk) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + if len(b) < n+1 { + err = parseErr("Version", n+offset, err) + return + } + self.Version = pio.U8(b[n:]) + n += 1 + if len(b) < n+3 { + err = parseErr("Flags", n+offset, err) + return + } + self.Flags = pio.U24BE(b[n:]) + n += 3 + var _len_Entries uint32 + _len_Entries = pio.U32BE(b[n:]) + n += 4 + self.Entries = make([]SampleToChunkEntry, _len_Entries) + if len(b) < n+LenSampleToChunkEntry*len(self.Entries) { + err = parseErr("SampleToChunkEntry", n+offset, err) + return + } + for i := range self.Entries { + self.Entries[i] = GetSampleToChunkEntry(b[n:]) + n += LenSampleToChunkEntry + } + return +} +func (self SampleToChunk) Children() (r []Atom) { + return +} + +type SampleToChunkEntry struct { + FirstChunk uint32 + SamplesPerChunk uint32 + SampleDescId uint32 +} + +func GetSampleToChunkEntry(b []byte) (self SampleToChunkEntry) { + self.FirstChunk = pio.U32BE(b[0:]) + self.SamplesPerChunk = pio.U32BE(b[4:]) + self.SampleDescId = pio.U32BE(b[8:]) + return +} +func PutSampleToChunkEntry(b []byte, self SampleToChunkEntry) { + pio.PutU32BE(b[0:], self.FirstChunk) + pio.PutU32BE(b[4:], self.SamplesPerChunk) + pio.PutU32BE(b[8:], self.SampleDescId) +} + +const LenSampleToChunkEntry = 12 + +type CompositionOffset struct { + Version uint8 + Flags uint32 + Entries []CompositionOffsetEntry + AtomPos +} + +func (self CompositionOffset) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(CTTS)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self CompositionOffset) marshal(b []byte) (n int) { + pio.PutU8(b[n:], self.Version) + n += 1 + pio.PutU24BE(b[n:], self.Flags) + n += 3 + pio.PutU32BE(b[n:], uint32(len(self.Entries))) + n += 4 + for _, entry := range self.Entries { + PutCompositionOffsetEntry(b[n:], entry) + n += LenCompositionOffsetEntry + } + return +} +func (self CompositionOffset) Len() (n int) { + n += 8 + n += 1 + n += 3 + n += 4 + n += LenCompositionOffsetEntry * len(self.Entries) + return +} +func (self *CompositionOffset) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + if len(b) < n+1 { + err = parseErr("Version", n+offset, err) + return + } + self.Version = pio.U8(b[n:]) + n += 1 + if len(b) < n+3 { + err = parseErr("Flags", n+offset, err) + return + } + self.Flags = pio.U24BE(b[n:]) + n += 3 + var _len_Entries uint32 + _len_Entries = pio.U32BE(b[n:]) + n += 4 + self.Entries = make([]CompositionOffsetEntry, _len_Entries) + if len(b) < n+LenCompositionOffsetEntry*len(self.Entries) { + err = parseErr("CompositionOffsetEntry", n+offset, err) + return + } + for i := range self.Entries { + self.Entries[i] = GetCompositionOffsetEntry(b[n:]) + n += LenCompositionOffsetEntry + } + return +} +func (self CompositionOffset) Children() (r []Atom) { + return +} + +type CompositionOffsetEntry struct { + Count uint32 + Offset uint32 +} + +func GetCompositionOffsetEntry(b []byte) (self CompositionOffsetEntry) { + self.Count = pio.U32BE(b[0:]) + self.Offset = pio.U32BE(b[4:]) + return +} +func PutCompositionOffsetEntry(b []byte, self CompositionOffsetEntry) { + pio.PutU32BE(b[0:], self.Count) + pio.PutU32BE(b[4:], self.Offset) +} + +const LenCompositionOffsetEntry = 8 + +type SyncSample struct { + Version uint8 + Flags uint32 + Entries []uint32 + AtomPos +} + +func (self SyncSample) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(STSS)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self SyncSample) marshal(b []byte) (n int) { + pio.PutU8(b[n:], self.Version) + n += 1 + pio.PutU24BE(b[n:], self.Flags) + n += 3 + pio.PutU32BE(b[n:], uint32(len(self.Entries))) + n += 4 + for _, entry := range self.Entries { + pio.PutU32BE(b[n:], entry) + n += 4 + } + return +} +func (self SyncSample) Len() (n int) { + n += 8 + n += 1 + n += 3 + n += 4 + n += 4 * len(self.Entries) + return +} +func (self *SyncSample) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + if len(b) < n+1 { + err = parseErr("Version", n+offset, err) + return + } + self.Version = pio.U8(b[n:]) + n += 1 + if len(b) < n+3 { + err = parseErr("Flags", n+offset, err) + return + } + self.Flags = pio.U24BE(b[n:]) + n += 3 + var _len_Entries uint32 + _len_Entries = pio.U32BE(b[n:]) + n += 4 + self.Entries = make([]uint32, _len_Entries) + if len(b) < n+4*len(self.Entries) { + err = parseErr("uint32", n+offset, err) + return + } + for i := range self.Entries { + self.Entries[i] = pio.U32BE(b[n:]) + n += 4 + } + return +} +func (self SyncSample) Children() (r []Atom) { + return +} + +type ChunkOffset struct { + Version uint8 + Flags uint32 + Entries []uint32 + AtomPos +} + +func (self ChunkOffset) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(STCO)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self ChunkOffset) marshal(b []byte) (n int) { + pio.PutU8(b[n:], self.Version) + n += 1 + pio.PutU24BE(b[n:], self.Flags) + n += 3 + pio.PutU32BE(b[n:], uint32(len(self.Entries))) + n += 4 + for _, entry := range self.Entries { + pio.PutU32BE(b[n:], entry) + n += 4 + } + return +} +func (self ChunkOffset) Len() (n int) { + n += 8 + n += 1 + n += 3 + n += 4 + n += 4 * len(self.Entries) + return +} +func (self *ChunkOffset) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + if len(b) < n+1 { + err = parseErr("Version", n+offset, err) + return + } + self.Version = pio.U8(b[n:]) + n += 1 + if len(b) < n+3 { + err = parseErr("Flags", n+offset, err) + return + } + self.Flags = pio.U24BE(b[n:]) + n += 3 + var _len_Entries uint32 + _len_Entries = pio.U32BE(b[n:]) + n += 4 + self.Entries = make([]uint32, _len_Entries) + if len(b) < n+4*len(self.Entries) { + err = parseErr("uint32", n+offset, err) + return + } + for i := range self.Entries { + self.Entries[i] = pio.U32BE(b[n:]) + n += 4 + } + return +} +func (self ChunkOffset) Children() (r []Atom) { + return +} + +type MovieFrag struct { + Header *MovieFragHeader + Tracks []*TrackFrag + Unknowns []Atom + AtomPos +} + +func (self MovieFrag) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(MOOF)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self MovieFrag) marshal(b []byte) (n int) { + if self.Header != nil { + n += self.Header.Marshal(b[n:]) + } + for _, atom := range self.Tracks { + n += atom.Marshal(b[n:]) + } + for _, atom := range self.Unknowns { + n += atom.Marshal(b[n:]) + } + return +} +func (self MovieFrag) Len() (n int) { + n += 8 + if self.Header != nil { + n += self.Header.Len() + } + for _, atom := range self.Tracks { + n += atom.Len() + } + for _, atom := range self.Unknowns { + n += atom.Len() + } + return +} +func (self *MovieFrag) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + for n+8 < len(b) { + tag := Tag(pio.U32BE(b[n+4:])) + size := int(pio.U32BE(b[n:])) + if len(b) < n+size { + err = parseErr("TagSizeInvalid", n+offset, err) + return + } + switch tag { + case MFHD: + { + atom := &MovieFragHeader{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("mfhd", n+offset, err) + return + } + self.Header = atom + } + case TRAF: + { + atom := &TrackFrag{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("traf", n+offset, err) + return + } + self.Tracks = append(self.Tracks, atom) + } + default: + { + atom := &Dummy{Tag_: tag, Data: b[n : n+size]} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("", n+offset, err) + return + } + self.Unknowns = append(self.Unknowns, atom) + } + } + n += size + } + return +} +func (self MovieFrag) Children() (r []Atom) { + if self.Header != nil { + r = append(r, self.Header) + } + for _, atom := range self.Tracks { + r = append(r, atom) + } + r = append(r, self.Unknowns...) + return +} + +type MovieFragHeader struct { + Version uint8 + Flags uint32 + Seqnum uint32 + AtomPos +} + +func (self MovieFragHeader) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(MFHD)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self MovieFragHeader) marshal(b []byte) (n int) { + pio.PutU8(b[n:], self.Version) + n += 1 + pio.PutU24BE(b[n:], self.Flags) + n += 3 + pio.PutU32BE(b[n:], self.Seqnum) + n += 4 + return +} +func (self MovieFragHeader) Len() (n int) { + n += 8 + n += 1 + n += 3 + n += 4 + return +} +func (self *MovieFragHeader) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + if len(b) < n+1 { + err = parseErr("Version", n+offset, err) + return + } + self.Version = pio.U8(b[n:]) + n += 1 + if len(b) < n+3 { + err = parseErr("Flags", n+offset, err) + return + } + self.Flags = pio.U24BE(b[n:]) + n += 3 + if len(b) < n+4 { + err = parseErr("Seqnum", n+offset, err) + return + } + self.Seqnum = pio.U32BE(b[n:]) + n += 4 + return +} +func (self MovieFragHeader) Children() (r []Atom) { + return +} + +type TrackFrag struct { + Header *TrackFragHeader + DecodeTime *TrackFragDecodeTime + Run *TrackFragRun + Unknowns []Atom + AtomPos +} + +func (self TrackFrag) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(TRAF)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self TrackFrag) marshal(b []byte) (n int) { + if self.Header != nil { + n += self.Header.Marshal(b[n:]) + } + if self.DecodeTime != nil { + n += self.DecodeTime.Marshal(b[n:]) + } + if self.Run != nil { + n += self.Run.Marshal(b[n:]) + } + for _, atom := range self.Unknowns { + n += atom.Marshal(b[n:]) + } + return +} +func (self TrackFrag) Len() (n int) { + n += 8 + if self.Header != nil { + n += self.Header.Len() + } + if self.DecodeTime != nil { + n += self.DecodeTime.Len() + } + if self.Run != nil { + n += self.Run.Len() + } + for _, atom := range self.Unknowns { + n += atom.Len() + } + return +} +func (self *TrackFrag) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + for n+8 < len(b) { + tag := Tag(pio.U32BE(b[n+4:])) + size := int(pio.U32BE(b[n:])) + if len(b) < n+size { + err = parseErr("TagSizeInvalid", n+offset, err) + return + } + switch tag { + case TFHD: + { + atom := &TrackFragHeader{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("tfhd", n+offset, err) + return + } + self.Header = atom + } + case TFDT: + { + atom := &TrackFragDecodeTime{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("tfdt", n+offset, err) + return + } + self.DecodeTime = atom + } + case TRUN: + { + atom := &TrackFragRun{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("trun", n+offset, err) + return + } + self.Run = atom + } + default: + { + atom := &Dummy{Tag_: tag, Data: b[n : n+size]} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("", n+offset, err) + return + } + self.Unknowns = append(self.Unknowns, atom) + } + } + n += size + } + return +} +func (self TrackFrag) Children() (r []Atom) { + if self.Header != nil { + r = append(r, self.Header) + } + if self.DecodeTime != nil { + r = append(r, self.DecodeTime) + } + if self.Run != nil { + r = append(r, self.Run) + } + r = append(r, self.Unknowns...) + return +} + +type MovieExtend struct { + Tracks []*TrackExtend + Unknowns []Atom + AtomPos +} + +func (self MovieExtend) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(MVEX)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self MovieExtend) marshal(b []byte) (n int) { + for _, atom := range self.Tracks { + n += atom.Marshal(b[n:]) + } + for _, atom := range self.Unknowns { + n += atom.Marshal(b[n:]) + } + return +} +func (self MovieExtend) Len() (n int) { + n += 8 + for _, atom := range self.Tracks { + n += atom.Len() + } + for _, atom := range self.Unknowns { + n += atom.Len() + } + return +} +func (self *MovieExtend) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + for n+8 < len(b) { + tag := Tag(pio.U32BE(b[n+4:])) + size := int(pio.U32BE(b[n:])) + if len(b) < n+size { + err = parseErr("TagSizeInvalid", n+offset, err) + return + } + switch tag { + case TREX: + { + atom := &TrackExtend{} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("trex", n+offset, err) + return + } + self.Tracks = append(self.Tracks, atom) + } + default: + { + atom := &Dummy{Tag_: tag, Data: b[n : n+size]} + if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil { + err = parseErr("", n+offset, err) + return + } + self.Unknowns = append(self.Unknowns, atom) + } + } + n += size + } + return +} +func (self MovieExtend) Children() (r []Atom) { + for _, atom := range self.Tracks { + r = append(r, atom) + } + r = append(r, self.Unknowns...) + return +} + +type TrackExtend struct { + Version uint8 + Flags uint32 + TrackId uint32 + DefaultSampleDescIdx uint32 + DefaultSampleDuration uint32 + DefaultSampleSize uint32 + DefaultSampleFlags uint32 + AtomPos +} + +func (self TrackExtend) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(TREX)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self TrackExtend) marshal(b []byte) (n int) { + pio.PutU8(b[n:], self.Version) + n += 1 + pio.PutU24BE(b[n:], self.Flags) + n += 3 + pio.PutU32BE(b[n:], self.TrackId) + n += 4 + pio.PutU32BE(b[n:], self.DefaultSampleDescIdx) + n += 4 + pio.PutU32BE(b[n:], self.DefaultSampleDuration) + n += 4 + pio.PutU32BE(b[n:], self.DefaultSampleSize) + n += 4 + pio.PutU32BE(b[n:], self.DefaultSampleFlags) + n += 4 + return +} +func (self TrackExtend) Len() (n int) { + n += 8 + n += 1 + n += 3 + n += 4 + n += 4 + n += 4 + n += 4 + n += 4 + return +} +func (self *TrackExtend) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + if len(b) < n+1 { + err = parseErr("Version", n+offset, err) + return + } + self.Version = pio.U8(b[n:]) + n += 1 + if len(b) < n+3 { + err = parseErr("Flags", n+offset, err) + return + } + self.Flags = pio.U24BE(b[n:]) + n += 3 + if len(b) < n+4 { + err = parseErr("TrackId", n+offset, err) + return + } + self.TrackId = pio.U32BE(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("DefaultSampleDescIdx", n+offset, err) + return + } + self.DefaultSampleDescIdx = pio.U32BE(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("DefaultSampleDuration", n+offset, err) + return + } + self.DefaultSampleDuration = pio.U32BE(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("DefaultSampleSize", n+offset, err) + return + } + self.DefaultSampleSize = pio.U32BE(b[n:]) + n += 4 + if len(b) < n+4 { + err = parseErr("DefaultSampleFlags", n+offset, err) + return + } + self.DefaultSampleFlags = pio.U32BE(b[n:]) + n += 4 + return +} +func (self TrackExtend) Children() (r []Atom) { + return +} + +type SampleSize struct { + Version uint8 + Flags uint32 + SampleSize uint32 + Entries []uint32 + AtomPos +} + +func (self SampleSize) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(STSZ)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self SampleSize) marshal(b []byte) (n int) { + pio.PutU8(b[n:], self.Version) + n += 1 + pio.PutU24BE(b[n:], self.Flags) + n += 3 + pio.PutU32BE(b[n:], self.SampleSize) + n += 4 + if self.SampleSize != 0 { + return + } + pio.PutU32BE(b[n:], uint32(len(self.Entries))) + n += 4 + for _, entry := range self.Entries { + pio.PutU32BE(b[n:], entry) + n += 4 + } + return +} +func (self SampleSize) Len() (n int) { + n += 8 + n += 1 + n += 3 + n += 4 + if self.SampleSize != 0 { + return + } + n += 4 + n += 4 * len(self.Entries) + return +} +func (self *SampleSize) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + if len(b) < n+1 { + err = parseErr("Version", n+offset, err) + return + } + self.Version = pio.U8(b[n:]) + n += 1 + if len(b) < n+3 { + err = parseErr("Flags", n+offset, err) + return + } + self.Flags = pio.U24BE(b[n:]) + n += 3 + if len(b) < n+4 { + err = parseErr("SampleSize", n+offset, err) + return + } + self.SampleSize = pio.U32BE(b[n:]) + n += 4 + if self.SampleSize != 0 { + return + } + var _len_Entries uint32 + _len_Entries = pio.U32BE(b[n:]) + n += 4 + self.Entries = make([]uint32, _len_Entries) + if len(b) < n+4*len(self.Entries) { + err = parseErr("uint32", n+offset, err) + return + } + for i := range self.Entries { + self.Entries[i] = pio.U32BE(b[n:]) + n += 4 + } + return +} +func (self SampleSize) Children() (r []Atom) { + return +} + +type TrackFragRun struct { + Version uint8 + Flags uint32 + DataOffset uint32 + FirstSampleFlags uint32 + Entries []TrackFragRunEntry + AtomPos +} + +func (self TrackFragRun) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(TRUN)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self TrackFragRun) marshal(b []byte) (n int) { + pio.PutU8(b[n:], self.Version) + n += 1 + pio.PutU24BE(b[n:], self.Flags) + n += 3 + pio.PutU32BE(b[n:], uint32(len(self.Entries))) + n += 4 + if self.Flags&TRUN_DATA_OFFSET != 0 { + { + pio.PutU32BE(b[n:], self.DataOffset) + n += 4 + } + } + if self.Flags&TRUN_FIRST_SAMPLE_FLAGS != 0 { + { + pio.PutU32BE(b[n:], self.FirstSampleFlags) + n += 4 + } + } + + for i, entry := range self.Entries { + var flags uint32 + if i > 0 { + flags = self.Flags + } else { + flags = self.FirstSampleFlags + } + if flags&TRUN_SAMPLE_DURATION != 0 { + pio.PutU32BE(b[n:], entry.Duration) + n += 4 + } + if flags&TRUN_SAMPLE_SIZE != 0 { + pio.PutU32BE(b[n:], entry.Size) + n += 4 + } + if flags&TRUN_SAMPLE_FLAGS != 0 { + pio.PutU32BE(b[n:], entry.Flags) + n += 4 + } + if flags&TRUN_SAMPLE_CTS != 0 { + pio.PutU32BE(b[n:], entry.Cts) + n += 4 + } + } + return +} +func (self TrackFragRun) Len() (n int) { + n += 8 + n += 1 + n += 3 + n += 4 + if self.Flags&TRUN_DATA_OFFSET != 0 { + { + n += 4 + } + } + if self.Flags&TRUN_FIRST_SAMPLE_FLAGS != 0 { + { + n += 4 + } + } + + for i := range self.Entries { + var flags uint32 + if i > 0 { + flags = self.Flags + } else { + flags = self.FirstSampleFlags + } + if flags&TRUN_SAMPLE_DURATION != 0 { + n += 4 + } + if flags&TRUN_SAMPLE_SIZE != 0 { + n += 4 + } + if flags&TRUN_SAMPLE_FLAGS != 0 { + n += 4 + } + if flags&TRUN_SAMPLE_CTS != 0 { + n += 4 + } + } + return +} +func (self *TrackFragRun) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + if len(b) < n+1 { + err = parseErr("Version", n+offset, err) + return + } + self.Version = pio.U8(b[n:]) + n += 1 + if len(b) < n+3 { + err = parseErr("Flags", n+offset, err) + return + } + self.Flags = pio.U24BE(b[n:]) + n += 3 + var _len_Entries uint32 + _len_Entries = pio.U32BE(b[n:]) + n += 4 + self.Entries = make([]TrackFragRunEntry, _len_Entries) + if self.Flags&TRUN_DATA_OFFSET != 0 { + { + if len(b) < n+4 { + err = parseErr("DataOffset", n+offset, err) + return + } + self.DataOffset = pio.U32BE(b[n:]) + n += 4 + } + } + if self.Flags&TRUN_FIRST_SAMPLE_FLAGS != 0 { + { + if len(b) < n+4 { + err = parseErr("FirstSampleFlags", n+offset, err) + return + } + self.FirstSampleFlags = pio.U32BE(b[n:]) + n += 4 + } + } + + for i := 0; i < int(_len_Entries); i++ { + var flags uint32 + if i > 0 { + flags = self.Flags + } else { + flags = self.FirstSampleFlags + } + entry := &self.Entries[i] + if flags&TRUN_SAMPLE_DURATION != 0 { + entry.Duration = pio.U32BE(b[n:]) + n += 4 + } + if flags&TRUN_SAMPLE_SIZE != 0 { + entry.Size = pio.U32BE(b[n:]) + n += 4 + } + if flags&TRUN_SAMPLE_FLAGS != 0 { + entry.Flags = pio.U32BE(b[n:]) + n += 4 + } + if flags&TRUN_SAMPLE_CTS != 0 { + entry.Cts = pio.U32BE(b[n:]) + n += 4 + } + } + return +} +func (self TrackFragRun) Children() (r []Atom) { + return +} + +type TrackFragRunEntry struct { + Duration uint32 + Size uint32 + Flags uint32 + Cts uint32 +} + +func GetTrackFragRunEntry(b []byte) (self TrackFragRunEntry) { + self.Duration = pio.U32BE(b[0:]) + self.Size = pio.U32BE(b[4:]) + self.Flags = pio.U32BE(b[8:]) + self.Cts = pio.U32BE(b[12:]) + return +} +func PutTrackFragRunEntry(b []byte, self TrackFragRunEntry) { + pio.PutU32BE(b[0:], self.Duration) + pio.PutU32BE(b[4:], self.Size) + pio.PutU32BE(b[8:], self.Flags) + pio.PutU32BE(b[12:], self.Cts) +} + +const LenTrackFragRunEntry = 16 + +type TrackFragHeader struct { + Version uint8 + Flags uint32 + BaseDataOffset uint64 + StsdId uint32 + DefaultDuration uint32 + DefaultSize uint32 + DefaultFlags uint32 + AtomPos +} + +func (self TrackFragHeader) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(TFHD)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self TrackFragHeader) marshal(b []byte) (n int) { + pio.PutU8(b[n:], self.Version) + n += 1 + pio.PutU24BE(b[n:], self.Flags) + n += 3 + if self.Flags&TFHD_BASE_DATA_OFFSET != 0 { + { + pio.PutU64BE(b[n:], self.BaseDataOffset) + n += 8 + } + } + if self.Flags&TFHD_STSD_ID != 0 { + { + pio.PutU32BE(b[n:], self.StsdId) + n += 4 + } + } + if self.Flags&TFHD_DEFAULT_DURATION != 0 { + { + pio.PutU32BE(b[n:], self.DefaultDuration) + n += 4 + } + } + if self.Flags&TFHD_DEFAULT_SIZE != 0 { + { + pio.PutU32BE(b[n:], self.DefaultSize) + n += 4 + } + } + if self.Flags&TFHD_DEFAULT_FLAGS != 0 { + { + pio.PutU32BE(b[n:], self.DefaultFlags) + n += 4 + } + } + return +} +func (self TrackFragHeader) Len() (n int) { + n += 8 + n += 1 + n += 3 + if self.Flags&TFHD_BASE_DATA_OFFSET != 0 { + { + n += 8 + } + } + if self.Flags&TFHD_STSD_ID != 0 { + { + n += 4 + } + } + if self.Flags&TFHD_DEFAULT_DURATION != 0 { + { + n += 4 + } + } + if self.Flags&TFHD_DEFAULT_SIZE != 0 { + { + n += 4 + } + } + if self.Flags&TFHD_DEFAULT_FLAGS != 0 { + { + n += 4 + } + } + return +} +func (self *TrackFragHeader) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + if len(b) < n+1 { + err = parseErr("Version", n+offset, err) + return + } + self.Version = pio.U8(b[n:]) + n += 1 + if len(b) < n+3 { + err = parseErr("Flags", n+offset, err) + return + } + self.Flags = pio.U24BE(b[n:]) + n += 3 + if self.Flags&TFHD_BASE_DATA_OFFSET != 0 { + { + if len(b) < n+8 { + err = parseErr("BaseDataOffset", n+offset, err) + return + } + self.BaseDataOffset = pio.U64BE(b[n:]) + n += 8 + } + } + if self.Flags&TFHD_STSD_ID != 0 { + { + if len(b) < n+4 { + err = parseErr("StsdId", n+offset, err) + return + } + self.StsdId = pio.U32BE(b[n:]) + n += 4 + } + } + if self.Flags&TFHD_DEFAULT_DURATION != 0 { + { + if len(b) < n+4 { + err = parseErr("DefaultDuration", n+offset, err) + return + } + self.DefaultDuration = pio.U32BE(b[n:]) + n += 4 + } + } + if self.Flags&TFHD_DEFAULT_SIZE != 0 { + { + if len(b) < n+4 { + err = parseErr("DefaultSize", n+offset, err) + return + } + self.DefaultSize = pio.U32BE(b[n:]) + n += 4 + } + } + if self.Flags&TFHD_DEFAULT_FLAGS != 0 { + { + if len(b) < n+4 { + err = parseErr("DefaultFlags", n+offset, err) + return + } + self.DefaultFlags = pio.U32BE(b[n:]) + n += 4 + } + } + return +} +func (self TrackFragHeader) Children() (r []Atom) { + return +} + +type TrackFragDecodeTime struct { + Version uint8 + Flags uint32 + Time time.Time + AtomPos +} + +func (self TrackFragDecodeTime) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(TFDT)) + n += self.marshal(b[8:]) + 8 + pio.PutU32BE(b[0:], uint32(n)) + return +} +func (self TrackFragDecodeTime) marshal(b []byte) (n int) { + pio.PutU8(b[n:], self.Version) + n += 1 + pio.PutU24BE(b[n:], self.Flags) + n += 3 + if self.Version != 0 { + PutTime64(b[n:], self.Time) + n += 8 + } else { + + PutTime32(b[n:], self.Time) + n += 4 + } + return +} +func (self TrackFragDecodeTime) Len() (n int) { + n += 8 + n += 1 + n += 3 + if self.Version != 0 { + n += 8 + } else { + + n += 4 + } + return +} +func (self *TrackFragDecodeTime) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + if len(b) < n+1 { + err = parseErr("Version", n+offset, err) + return + } + self.Version = pio.U8(b[n:]) + n += 1 + if len(b) < n+3 { + err = parseErr("Flags", n+offset, err) + return + } + self.Flags = pio.U24BE(b[n:]) + n += 3 + if self.Version != 0 { + self.Time = GetTime64(b[n:]) + n += 8 + } else { + + self.Time = GetTime32(b[n:]) + n += 4 + } + return +} +func (self TrackFragDecodeTime) Children() (r []Atom) { + return +} diff --git a/format/mp4m/mp4io/gen/gen.go b/format/mp4m/mp4io/gen/gen.go new file mode 100644 index 0000000..34309f7 --- /dev/null +++ b/format/mp4m/mp4io/gen/gen.go @@ -0,0 +1,1055 @@ +package main + +import ( + "fmt" + "go/ast" + "go/parser" + "go/printer" + "go/token" + "os" + "strings" +) + +func getexprs(e ast.Expr) string { + if lit, ok := e.(*ast.BasicLit); ok { + return lit.Value + } + if ident, ok := e.(*ast.Ident); ok { + return ident.Name + } + return "" +} + +func genatomdecl(origfn *ast.FuncDecl, origname, origtag string) (decls []ast.Decl) { + fieldslist := &ast.FieldList{} + typespec := &ast.TypeSpec{ + Name: ast.NewIdent(origname), + Type: &ast.StructType{Fields: fieldslist}, + } + + for _, _stmt := range origfn.Body.List { + stmt := _stmt.(*ast.ExprStmt) + callexpr := stmt.X.(*ast.CallExpr) + typ := callexpr.Fun.(*ast.Ident).Name + + if strings.HasPrefix(typ, "_") { + if typ == "_unknowns" { + fieldslist.List = append(fieldslist.List, &ast.Field{ + Names: []*ast.Ident{ast.NewIdent("Unknowns")}, + Type: ast.NewIdent("[]Atom"), + }) + } + continue + } + + name := getexprs(callexpr.Args[0]) + + name2 := "" + if len(callexpr.Args) > 1 { + name2 = getexprs(callexpr.Args[1]) + } + + len3 := "" + if len(callexpr.Args) > 2 { + len3 = getexprs(callexpr.Args[2]) + } + + if strings.HasPrefix(name, "_") { + continue + } + + switch typ { + case "fixed16": + typ = "float64" + case "fixed32": + typ = "float64" + case "bytesleft": + typ = "[]byte" + case "bytes": + typ = "[" + name2 + "]byte" + case "uint24": + typ = "uint32" + case "time64", "time32": + typ = "time.Time" + case "atom": + typ = "*" + name2 + case "atoms": + typ = "[]*" + name2 + case "slice": + typ = "[]" + name2 + case "array": + typ = "[" + len3 + "]" + name2 + } + + fieldslist.List = append(fieldslist.List, &ast.Field{ + Names: []*ast.Ident{ast.NewIdent(name)}, + Type: ast.NewIdent(typ), + }) + } + + if origtag != "" { + fieldslist.List = append(fieldslist.List, &ast.Field{ + Type: ast.NewIdent("AtomPos"), + }) + } + + gendecl := &ast.GenDecl{ + Tok: token.TYPE, + Specs: []ast.Spec{ + typespec, + }, + } + decls = append(decls, gendecl) + return +} + +func typegetlen(typ string) (n int) { + switch typ { + case "uint8": + n = 1 + case "uint16": + n = 2 + case "uint24": + n = 3 + case "uint32": + n = 4 + case "int16": + n = 2 + case "int32": + n = 4 + case "uint64": + n = 8 + case "time32": + n = 4 + case "time64": + n = 8 + case "fixed32": + n = 4 + case "fixed16": + n = 2 + } + return +} + +func typegetlens(typ string) string { + n := typegetlen(typ) + if n == 0 { + return "Len" + typ + } else { + return fmt.Sprint(n) + } +} + +func typegetvartype(typ string) string { + switch typ { + case "uint8": + return "uint8" + case "uint16": + return "uint16" + case "uint24": + return "uint32" + case "uint32": + return "uint32" + case "uint64": + return "uint64" + case "int16": + return "int16" + case "int32": + return "int32" + } + return "" +} + +func typegetputfn(typ string) (fn string) { + fn = typ + switch typ { + case "uint8": + fn = "pio.PutU8" + case "uint16": + fn = "pio.PutU16BE" + case "uint24": + fn = "pio.PutU24BE" + case "uint32": + fn = "pio.PutU32BE" + case "int16": + fn = "pio.PutI16BE" + case "int32": + fn = "pio.PutI32BE" + case "uint64": + fn = "pio.PutU64BE" + case "time32": + fn = "PutTime32" + case "time64": + fn = "PutTime64" + case "fixed32": + fn = "PutFixed32" + case "fixed16": + fn = "PutFixed16" + default: + fn = "Put" + typ + } + return +} + +func typegetgetfn(typ string) (fn string) { + fn = typ + switch typ { + case "uint8": + fn = "pio.U8" + case "uint16": + fn = "pio.U16BE" + case "uint24": + fn = "pio.U24BE" + case "uint32": + fn = "pio.U32BE" + case "int16": + fn = "pio.I16BE" + case "int32": + fn = "pio.I32BE" + case "uint64": + fn = "pio.U64BE" + case "time32": + fn = "GetTime32" + case "time64": + fn = "GetTime64" + case "fixed32": + fn = "GetFixed32" + case "fixed16": + fn = "GetFixed16" + default: + fn = "Get" + typ + } + return +} + +func addns(n string) (stmts []ast.Stmt) { + assign := &ast.AssignStmt{ + Tok: token.ADD_ASSIGN, + Lhs: []ast.Expr{ast.NewIdent("n")}, + Rhs: []ast.Expr{&ast.BasicLit{Kind: token.INT, Value: n}}, + } + stmts = append(stmts, assign) + return +} + +func addn(n int) (stmts []ast.Stmt) { + return addns(fmt.Sprint(n)) +} + +func simplecall(fun string, args ...string) *ast.ExprStmt { + _args := []ast.Expr{} + for _, s := range args { + _args = append(_args, ast.NewIdent(s)) + } + return &ast.ExprStmt{ + X: &ast.CallExpr{ + Fun: ast.NewIdent(fun), + Args: _args, + }, + } +} + +func getxx(typ string, pos, name string, conv bool) (stmts []ast.Stmt) { + fn := typegetgetfn(typ) + assign := &ast.AssignStmt{ + Tok: token.ASSIGN, + Lhs: []ast.Expr{ast.NewIdent(name)}, + Rhs: []ast.Expr{simplecall(fn, "b["+pos+":]").X}, + } + stmts = append(stmts, assign) + return +} + +func putxx(typ string, pos, name string, conv bool) (stmts []ast.Stmt) { + if conv { + name = fmt.Sprintf("%s(%s)", typ, name) + } + fn := typegetputfn(typ) + stmts = append(stmts, simplecall(fn, "b["+pos+":]", name)) + return +} + +func putxxadd(fn string, name string, conv bool) (stmts []ast.Stmt) { + n := typegetlen(fn) + stmts = append(stmts, putxx(fn, "n", name, conv)...) + stmts = append(stmts, addn(n)...) + return +} + +func newdecl(origname, name string, params, res []*ast.Field, stmts []ast.Stmt) *ast.FuncDecl { + return &ast.FuncDecl{ + Recv: &ast.FieldList{ + List: []*ast.Field{ + &ast.Field{ + Names: []*ast.Ident{ast.NewIdent("self")}, + Type: ast.NewIdent(origname), + }, + }, + }, + Name: ast.NewIdent(name), + Type: &ast.FuncType{ + Params: &ast.FieldList{ + List: params, + }, + Results: &ast.FieldList{ + List: res, + }, + }, + Body: &ast.BlockStmt{List: stmts}, + } +} + +func getstructputgetlenfn(origfn *ast.FuncDecl, origname string) (decls []ast.Decl) { + getstmts := []ast.Stmt{} + putstmts := []ast.Stmt{} + totlen := 0 + + for _, _stmt := range origfn.Body.List { + stmt := _stmt.(*ast.ExprStmt) + callexpr := stmt.X.(*ast.CallExpr) + typ := callexpr.Fun.(*ast.Ident).Name + name := getexprs(callexpr.Args[0]) + + getstmts = append(getstmts, getxx(typ, fmt.Sprint(totlen), "self."+name, false)...) + putstmts = append(putstmts, putxx(typ, fmt.Sprint(totlen), "self."+name, false)...) + totlen += typegetlen(typ) + } + + getstmts = append(getstmts, &ast.ReturnStmt{}) + + decls = append(decls, &ast.FuncDecl{ + Name: ast.NewIdent("Get" + origname), + Type: &ast.FuncType{ + Params: &ast.FieldList{ + List: []*ast.Field{ + &ast.Field{Names: []*ast.Ident{ast.NewIdent("b")}, Type: ast.NewIdent("[]byte")}, + }, + }, + Results: &ast.FieldList{ + List: []*ast.Field{ + &ast.Field{Names: []*ast.Ident{ast.NewIdent("self")}, Type: ast.NewIdent(origname)}, + }, + }, + }, + Body: &ast.BlockStmt{List: getstmts}, + }) + + decls = append(decls, &ast.FuncDecl{ + Name: ast.NewIdent("Put" + origname), + Type: &ast.FuncType{ + Params: &ast.FieldList{ + List: []*ast.Field{ + &ast.Field{Names: []*ast.Ident{ast.NewIdent("b")}, Type: ast.NewIdent("[]byte")}, + &ast.Field{Names: []*ast.Ident{ast.NewIdent("self")}, Type: ast.NewIdent(origname)}, + }, + }, + }, + Body: &ast.BlockStmt{List: putstmts}, + }) + + decls = append(decls, &ast.GenDecl{ + Tok: token.CONST, + Specs: []ast.Spec{ + &ast.ValueSpec{ + Names: []*ast.Ident{ast.NewIdent("Len" + origname)}, + Values: []ast.Expr{ast.NewIdent(fmt.Sprint(totlen))}, + }, + }, + }) + + return +} + +func cc4decls(name string) (decls []ast.Decl) { + constdecl := &ast.GenDecl{ + Tok: token.CONST, + Specs: []ast.Spec{ + &ast.ValueSpec{ + Names: []*ast.Ident{ + ast.NewIdent(strings.ToUpper(name)), + }, + Values: []ast.Expr{ + &ast.CallExpr{ + Fun: ast.NewIdent("Tag"), + Args: []ast.Expr{&ast.BasicLit{Kind: token.INT, Value: fmt.Sprintf("0x%x", []byte(name))}}, + }, + }, + }, + }, + } + decls = append(decls, constdecl) + return +} + +func codeclonereplace(stmts []ast.Stmt, doit []ast.Stmt) (out []ast.Stmt) { + out = append([]ast.Stmt(nil), stmts...) + for i := range out { + if ifstmt, ok := out[i].(*ast.IfStmt); ok { + newifstmt := &ast.IfStmt{ + Cond: ifstmt.Cond, + Body: &ast.BlockStmt{ + List: codeclonereplace(ifstmt.Body.List, doit), + }, + } + if ifstmt.Else != nil { + newifstmt.Else = &ast.BlockStmt{ + List: codeclonereplace(ifstmt.Else.(*ast.BlockStmt).List, doit), + } + } + out[i] = newifstmt + } else if exprstmt, ok := out[i].(*ast.ExprStmt); ok { + if callexpr, ok := exprstmt.X.(*ast.CallExpr); ok { + if getexprs(callexpr.Fun) == "doit" { + out[i] = &ast.BlockStmt{List: doit} + } + } + } + } + return +} + +func getatommarshalfn(origfn *ast.FuncDecl, + origname, origtag string, + tagnamemap map[string]string, +) (decls []ast.Decl) { + marstmts := []ast.Stmt{} + unmarstmts := []ast.Stmt{} + lenstmts := []ast.Stmt{} + childrenstmts := []ast.Stmt{} + + parseerrreturn := func(debug string) (stmts []ast.Stmt) { + return []ast.Stmt{ + &ast.AssignStmt{ + Tok: token.ASSIGN, + Lhs: []ast.Expr{ast.NewIdent("err")}, + Rhs: []ast.Expr{ast.NewIdent(fmt.Sprintf(`parseErr("%s", n+offset, err)`, debug))}, + }, + &ast.ReturnStmt{}, + } + } + + callmarshal := func(name string) (stmts []ast.Stmt) { + callexpr := &ast.CallExpr{ + Fun: ast.NewIdent(name + ".Marshal"), + Args: []ast.Expr{ast.NewIdent("b[n:]")}, + } + assign := &ast.AssignStmt{ + Tok: token.ADD_ASSIGN, + Lhs: []ast.Expr{ast.NewIdent("n")}, + Rhs: []ast.Expr{callexpr}, + } + stmts = append(stmts, assign) + return + } + + callputstruct := func(typ, name string) (stmts []ast.Stmt) { + stmts = append(stmts, &ast.ExprStmt{ + X: &ast.CallExpr{ + Fun: ast.NewIdent(typegetputfn(typ)), + Args: []ast.Expr{ast.NewIdent("b[n:]"), ast.NewIdent(name)}, + }, + }) + stmts = append(stmts, &ast.AssignStmt{ + Tok: token.ADD_ASSIGN, + Lhs: []ast.Expr{ast.NewIdent("n")}, + Rhs: []ast.Expr{ast.NewIdent(typegetlens(typ))}, + }) + return + } + + calllenstruct := func(typ, name string) (stmts []ast.Stmt) { + inc := typegetlens(typ) + "*len(" + name + ")" + stmts = append(stmts, &ast.AssignStmt{ + Tok: token.ADD_ASSIGN, + Lhs: []ast.Expr{ast.NewIdent("n")}, + Rhs: []ast.Expr{ast.NewIdent(inc)}, + }) + return + } + + calllen := func(name string) (stmts []ast.Stmt) { + callexpr := &ast.CallExpr{ + Fun: ast.NewIdent(name + ".Len"), + Args: []ast.Expr{}, + } + assign := &ast.AssignStmt{ + Tok: token.ADD_ASSIGN, + Lhs: []ast.Expr{ast.NewIdent("n")}, + Rhs: []ast.Expr{callexpr}, + } + stmts = append(stmts, assign) + return + } + + foreach := func(name, field string, block []ast.Stmt) (stmts []ast.Stmt) { + rangestmt := &ast.RangeStmt{ + Key: ast.NewIdent("_"), + Value: ast.NewIdent(name), + Body: &ast.BlockStmt{ + List: block, + }, + Tok: token.DEFINE, + X: ast.NewIdent(field), + } + stmts = append(stmts, rangestmt) + return + } + + foreachatom := func(field string, block []ast.Stmt) (stmts []ast.Stmt) { + return foreach("atom", field, block) + } + + foreachentry := func(field string, block []ast.Stmt) (stmts []ast.Stmt) { + return foreach("entry", field, block) + } + + foreachi := func(field string, block []ast.Stmt) (stmts []ast.Stmt) { + rangestmt := &ast.RangeStmt{ + Key: ast.NewIdent("i"), + Body: &ast.BlockStmt{ + List: block, + }, + Tok: token.DEFINE, + X: ast.NewIdent(field), + } + stmts = append(stmts, rangestmt) + return + } + + foreachunknowns := func(block []ast.Stmt) (stmts []ast.Stmt) { + return foreachatom("self.Unknowns", block) + } + + declvar := func(name, typ string) (stmts []ast.Stmt) { + stmts = append(stmts, &ast.DeclStmt{ + Decl: &ast.GenDecl{ + Tok: token.VAR, + Specs: []ast.Spec{ + &ast.ValueSpec{ + Names: []*ast.Ident{ + ast.NewIdent(typ), + }, + Type: ast.NewIdent(name), + }, + }, + }, + }) + return + } + + makeslice := func(name, typ, size string) (stmts []ast.Stmt) { + stmts = append(stmts, &ast.ExprStmt{ + X: ast.NewIdent(fmt.Sprintf("%s = make([]%s, %s)", name, typ, size)), + }) + return + } + + simpleassign := func(tok token.Token, l, r string) *ast.AssignStmt { + return &ast.AssignStmt{ + Tok: tok, + Lhs: []ast.Expr{ast.NewIdent(l)}, + Rhs: []ast.Expr{ast.NewIdent(r)}, + } + } + + struct2tag := func(s string) string { + name := tagnamemap[s] + return name + } + + foreachatomsappendchildren := func(field string) (stmts []ast.Stmt) { + return foreachatom(field, []ast.Stmt{ + simpleassign(token.ASSIGN, "r", "append(r, atom)"), + }) + } + + var hasunknowns bool + var atomnames []string + var atomtypes []string + var atomarrnames []string + var atomarrtypes []string + slicenamemap := map[string]string{} + + unmarshalatom := func(typ, init string) (stmts []ast.Stmt) { + return []ast.Stmt{ + &ast.AssignStmt{Tok: token.DEFINE, + Lhs: []ast.Expr{ast.NewIdent("atom")}, Rhs: []ast.Expr{ast.NewIdent("&" + typ + "{" + init + "}")}, + }, + &ast.IfStmt{ + Init: &ast.AssignStmt{ + Tok: token.ASSIGN, + Lhs: []ast.Expr{ast.NewIdent("_"), ast.NewIdent("err")}, + Rhs: []ast.Expr{ast.NewIdent("atom.Unmarshal(b[n:n+size], offset+n)")}, + }, + Cond: ast.NewIdent("err != nil"), + Body: &ast.BlockStmt{List: parseerrreturn(struct2tag(typ))}, + }, + } + } + + unmrashalatoms := func() (stmts []ast.Stmt) { + blocks := []ast.Stmt{} + + blocks = append(blocks, &ast.AssignStmt{Tok: token.DEFINE, Lhs: []ast.Expr{ast.NewIdent("tag")}, + Rhs: []ast.Expr{ast.NewIdent("Tag(pio.U32BE(b[n+4:]))")}, + }) + blocks = append(blocks, &ast.AssignStmt{Tok: token.DEFINE, Lhs: []ast.Expr{ast.NewIdent("size")}, + Rhs: []ast.Expr{ast.NewIdent("int(pio.U32BE(b[n:]))")}, + }) + blocks = append(blocks, &ast.IfStmt{ + Cond: ast.NewIdent("len(b) < n+size"), + Body: &ast.BlockStmt{List: parseerrreturn("TagSizeInvalid")}, + }) + + cases := []ast.Stmt{} + + for i, atom := range atomnames { + cases = append(cases, &ast.CaseClause{ + List: []ast.Expr{ast.NewIdent(strings.ToUpper(struct2tag(atomtypes[i])))}, + Body: []ast.Stmt{&ast.BlockStmt{ + List: append(unmarshalatom(atomtypes[i], ""), simpleassign(token.ASSIGN, "self."+atom, "atom")), + }}, + }) + } + + for i, atom := range atomarrnames { + selfatom := "self." + atom + cases = append(cases, &ast.CaseClause{ + List: []ast.Expr{ast.NewIdent(strings.ToUpper(struct2tag(atomarrtypes[i])))}, + Body: []ast.Stmt{&ast.BlockStmt{ + List: append(unmarshalatom(atomarrtypes[i], ""), + simpleassign(token.ASSIGN, selfatom, "append("+selfatom+", atom)")), + }}, + }) + } + + if hasunknowns { + init := "Tag_: tag, Data: b[n:n+size]" + selfatom := "self.Unknowns" + cases = append(cases, &ast.CaseClause{ + Body: []ast.Stmt{&ast.BlockStmt{ + List: append(unmarshalatom("Dummy", init), simpleassign(token.ASSIGN, selfatom, "append("+selfatom+", atom)")), + }}, + }) + } + + blocks = append(blocks, &ast.SwitchStmt{ + Tag: ast.NewIdent("tag"), + Body: &ast.BlockStmt{List: cases}, + }) + + blocks = append(blocks, addns("size")...) + + stmts = append(stmts, &ast.ForStmt{ + Cond: ast.NewIdent("n+8 < len(b)"), + Body: &ast.BlockStmt{List: blocks}, + }) + return + } + + marshalwrapstmts := func() (stmts []ast.Stmt) { + stmts = append(stmts, putxx("uint32", "4", strings.ToUpper(origtag), true)...) + stmts = append(stmts, addns("self.marshal(b[8:])+8")...) + stmts = append(stmts, putxx("uint32", "0", "n", true)...) + stmts = append(stmts, &ast.ReturnStmt{}) + return + } + + ifnotnil := func(name string, block []ast.Stmt) (stmts []ast.Stmt) { + stmts = append(stmts, &ast.IfStmt{ + Cond: &ast.BinaryExpr{ + X: ast.NewIdent(name), + Op: token.NEQ, + Y: ast.NewIdent("nil"), + }, + Body: &ast.BlockStmt{List: block}, + }) + return + } + + getchildrennr := func(name string) (stmts []ast.Stmt) { + stmts = append(stmts, &ast.AssignStmt{ + Tok: token.DEFINE, + Lhs: []ast.Expr{ast.NewIdent(name)}, + Rhs: []ast.Expr{ast.NewIdent("0")}, + }) + for _, atom := range atomnames { + stmts = append(stmts, ifnotnil("self."+atom, []ast.Stmt{ + &ast.IncDecStmt{X: ast.NewIdent(name), Tok: token.INC}, + })...) + } + if hasunknowns { + assign := &ast.AssignStmt{ + Tok: token.ADD_ASSIGN, + Lhs: []ast.Expr{ast.NewIdent("_childrenNR")}, + Rhs: []ast.Expr{ast.NewIdent("len(self.Unknowns)")}, + } + stmts = append(stmts, assign) + } + return + } + + checkcurlen := func(inc, debug string) (stmts []ast.Stmt) { + stmts = append(stmts, &ast.IfStmt{ + Cond: &ast.BinaryExpr{ + X: ast.NewIdent("len(b)"), + Op: token.LSS, + Y: ast.NewIdent("n+" + inc), + }, + Body: &ast.BlockStmt{List: parseerrreturn(debug)}, + }) + return + } + + checklendo := func(typ, name, debug string) (stmts []ast.Stmt) { + stmts = append(stmts, checkcurlen(typegetlens(typ), debug)...) + stmts = append(stmts, getxx(typ, "n", name, false)...) + stmts = append(stmts, addns(typegetlens(typ))...) + return + } + + checkstructlendo := func(typ, name, debug string, + foreach func(string, []ast.Stmt) []ast.Stmt, + ) (stmts []ast.Stmt) { + inc := typegetlens(typ) + "*len(" + name + ")" + stmts = append(stmts, checkcurlen(inc, debug)...) + stmts = append(stmts, foreach(name, append( + []ast.Stmt{ + &ast.AssignStmt{ + Tok: token.ASSIGN, + Lhs: []ast.Expr{ + ast.NewIdent(name + "[i]"), + }, + Rhs: []ast.Expr{ + &ast.CallExpr{ + Fun: ast.NewIdent(typegetgetfn(typ)), + Args: []ast.Expr{ast.NewIdent("b[n:]")}, + }, + }, + }, + }, + addns(typegetlens(typ))..., + ))...) + return + } + + checklencopy := func(name string) (stmts []ast.Stmt) { + lens := fmt.Sprintf("len(self.%s)", name) + stmts = append(stmts, checkcurlen(lens, name)...) + stmts = append(stmts, simplecall("copy", fmt.Sprintf("self.%s[:]", name), "b[n:]")) + stmts = append(stmts, addns(lens)...) + return + } + + appendcode := func(args []ast.Expr, + marstmts *[]ast.Stmt, lenstmts *[]ast.Stmt, unmarstmts *[]ast.Stmt, + defmarstmts []ast.Stmt, deflenstmts []ast.Stmt, defunmarstmts []ast.Stmt, + ) { + bodylist := func(i int, doit []ast.Stmt) []ast.Stmt { + return codeclonereplace(args[i].(*ast.FuncLit).Body.List, doit) + } + if len(args) == 1 { + *marstmts = append(*marstmts, bodylist(0, defmarstmts)...) + *lenstmts = append(*lenstmts, bodylist(0, deflenstmts)...) + *unmarstmts = append(*unmarstmts, bodylist(0, defunmarstmts)...) + } else { + *marstmts = append(*marstmts, bodylist(0, defmarstmts)...) + *lenstmts = append(*lenstmts, bodylist(1, deflenstmts)...) + *unmarstmts = append(*unmarstmts, bodylist(2, defunmarstmts)...) + } + } + + getdefaultstmts := func( + typ, name, name2 string, + marstmts *[]ast.Stmt, lenstmts *[]ast.Stmt, + unmarstmts *[]ast.Stmt, childrenstmts *[]ast.Stmt, + ) { + switch typ { + case "bytes", "bytesleft": + *marstmts = append(*marstmts, simplecall("copy", "b[n:]", "self."+name+"[:]")) + *marstmts = append(*marstmts, addns(fmt.Sprintf("len(self.%s[:])", name))...) + *lenstmts = append(*lenstmts, addns(fmt.Sprintf("len(self.%s[:])", name))...) + if typ == "bytes" { + *unmarstmts = append(*unmarstmts, checklencopy(name)...) + } else { + *unmarstmts = append(*unmarstmts, simpleassign(token.ASSIGN, "self."+name, "b[n:]")) + *unmarstmts = append(*unmarstmts, addns("len(b[n:])")...) + } + + case "array": + *marstmts = append(*marstmts, foreachentry("self."+name, callputstruct(name2, "entry"))...) + *lenstmts = append(*lenstmts, calllenstruct(name2, "self."+name+"[:]")...) + *unmarstmts = append(*unmarstmts, checkstructlendo(name2, "self."+name, name, foreachi)...) + + case "atoms": + *marstmts = append(*marstmts, foreachatom("self."+name, callmarshal("atom"))...) + *lenstmts = append(*lenstmts, foreachatom("self."+name, calllen("atom"))...) + *childrenstmts = append(*childrenstmts, foreachatomsappendchildren("self."+name)...) + + case "slice": + *marstmts = append(*marstmts, foreachentry("self."+name, callputstruct(name2, "entry"))...) + *lenstmts = append(*lenstmts, calllenstruct(name2, "self."+name)...) + *unmarstmts = append(*unmarstmts, checkstructlendo(name2, "self."+name, name2, foreachi)...) + + case "atom": + *marstmts = append(*marstmts, ifnotnil("self."+name, callmarshal("self."+name))...) + *lenstmts = append(*lenstmts, ifnotnil("self."+name, calllen("self."+name))...) + *childrenstmts = append(*childrenstmts, ifnotnil("self."+name, []ast.Stmt{ + simpleassign(token.ASSIGN, "r", fmt.Sprintf("append(r, %s)", "self."+name)), + })...) + + default: + *marstmts = append(*marstmts, putxxadd(typ, "self."+name, false)...) + *lenstmts = append(*lenstmts, addn(typegetlen(typ))...) + *unmarstmts = append(*unmarstmts, checklendo(typ, "self."+name, name)...) + } + } + + for _, _stmt := range origfn.Body.List { + stmt := _stmt.(*ast.ExprStmt) + callexpr := stmt.X.(*ast.CallExpr) + typ := callexpr.Fun.(*ast.Ident).Name + if typ == "_unknowns" { + hasunknowns = true + } else if typ == "atom" { + name := getexprs(callexpr.Args[0]) + name2 := getexprs(callexpr.Args[1]) + atomnames = append(atomnames, name) + atomtypes = append(atomtypes, name2) + } else if typ == "atoms" { + name := getexprs(callexpr.Args[0]) + name2 := getexprs(callexpr.Args[1]) + atomarrnames = append(atomarrnames, name) + atomarrtypes = append(atomarrtypes, name2) + } else if typ == "slice" { + name := getexprs(callexpr.Args[0]) + name2 := getexprs(callexpr.Args[1]) + slicenamemap[name] = name2 + } + } + + lenstmts = append(lenstmts, addn(8)...) + unmarstmts = append(unmarstmts, simplecall("(&self.AtomPos).setPos", "offset", "len(b)")) + unmarstmts = append(unmarstmts, addn(8)...) + + for _, _stmt := range origfn.Body.List { + stmt := _stmt.(*ast.ExprStmt) + callexpr := stmt.X.(*ast.CallExpr) + typ := callexpr.Fun.(*ast.Ident).Name + + name := "" + if len(callexpr.Args) > 0 { + name = getexprs(callexpr.Args[0]) + } + + name2 := "" + if len(callexpr.Args) > 1 { + name2 = getexprs(callexpr.Args[1]) + } + + var defmarstmts, deflenstmts, defunmarstmts, defchildrenstmts []ast.Stmt + getdefaultstmts(typ, name, name2, + &defmarstmts, &deflenstmts, &defunmarstmts, &defchildrenstmts) + + var code []ast.Expr + for _, arg := range callexpr.Args { + if fn, ok := arg.(*ast.CallExpr); ok { + if getexprs(fn.Fun) == "_code" { + code = fn.Args + } + } + } + if code != nil { + appendcode(code, + &marstmts, &lenstmts, &unmarstmts, + defmarstmts, deflenstmts, defunmarstmts, + ) + continue + } + + if strings.HasPrefix(typ, "_") { + if typ == "_unknowns" { + marstmts = append(marstmts, foreachunknowns(callmarshal("atom"))...) + lenstmts = append(lenstmts, foreachunknowns(calllen("atom"))...) + childrenstmts = append(childrenstmts, simpleassign(token.ASSIGN, "r", "append(r, self.Unknowns...)")) + } + if typ == "_skip" { + marstmts = append(marstmts, addns(name)...) + lenstmts = append(lenstmts, addns(name)...) + unmarstmts = append(unmarstmts, addns(name)...) + } + if typ == "_code" { + appendcode(callexpr.Args, + &marstmts, &lenstmts, &unmarstmts, + defmarstmts, deflenstmts, defunmarstmts, + ) + } + continue + } + + if name == "_childrenNR" { + marstmts = append(marstmts, getchildrennr(name)...) + marstmts = append(marstmts, putxxadd(typ, name, true)...) + lenstmts = append(lenstmts, addn(typegetlen(typ))...) + unmarstmts = append(unmarstmts, addn(typegetlen(typ))...) + continue + } + + if strings.HasPrefix(name, "_len_") { + field := name[len("_len_"):] + marstmts = append(marstmts, putxxadd(typ, "len(self."+field+")", true)...) + lenstmts = append(lenstmts, addn(typegetlen(typ))...) + unmarstmts = append(unmarstmts, declvar(typegetvartype(typ), name)...) + unmarstmts = append(unmarstmts, getxx(typ, "n", name, false)...) + unmarstmts = append(unmarstmts, addn(typegetlen(typ))...) + unmarstmts = append(unmarstmts, makeslice("self."+field, slicenamemap[field], name)...) + continue + } + + marstmts = append(marstmts, defmarstmts...) + lenstmts = append(lenstmts, deflenstmts...) + unmarstmts = append(unmarstmts, defunmarstmts...) + childrenstmts = append(childrenstmts, defchildrenstmts...) + } + + if len(atomnames) > 0 || len(atomarrnames) > 0 || hasunknowns { + unmarstmts = append(unmarstmts, unmrashalatoms()...) + } + + marstmts = append(marstmts, &ast.ReturnStmt{}) + lenstmts = append(lenstmts, &ast.ReturnStmt{}) + unmarstmts = append(unmarstmts, &ast.ReturnStmt{}) + childrenstmts = append(childrenstmts, &ast.ReturnStmt{}) + + decls = append(decls, newdecl(origname, "Marshal", []*ast.Field{ + &ast.Field{Names: []*ast.Ident{ast.NewIdent("b")}, Type: ast.NewIdent("[]byte")}, + }, []*ast.Field{ + &ast.Field{Names: []*ast.Ident{ast.NewIdent("n")}, Type: ast.NewIdent("int")}, + }, marshalwrapstmts())) + + decls = append(decls, newdecl(origname, "marshal", []*ast.Field{ + &ast.Field{Names: []*ast.Ident{ast.NewIdent("b")}, Type: ast.NewIdent("[]byte")}, + }, []*ast.Field{ + &ast.Field{Names: []*ast.Ident{ast.NewIdent("n")}, Type: ast.NewIdent("int")}, + }, marstmts)) + + decls = append(decls, newdecl(origname, "Len", []*ast.Field{}, []*ast.Field{ + &ast.Field{Names: []*ast.Ident{ast.NewIdent("n")}, Type: ast.NewIdent("int")}, + }, lenstmts)) + + decls = append(decls, newdecl("*"+origname, "Unmarshal", []*ast.Field{ + &ast.Field{Names: []*ast.Ident{ast.NewIdent("b")}, Type: ast.NewIdent("[]byte")}, + &ast.Field{Names: []*ast.Ident{ast.NewIdent("offset")}, Type: ast.NewIdent("int")}, + }, []*ast.Field{ + &ast.Field{Names: []*ast.Ident{ast.NewIdent("n")}, Type: ast.NewIdent("int")}, + &ast.Field{Names: []*ast.Ident{ast.NewIdent("err")}, Type: ast.NewIdent("error")}, + }, unmarstmts)) + + decls = append(decls, newdecl(origname, "Children", []*ast.Field{}, []*ast.Field{ + &ast.Field{Names: []*ast.Ident{ast.NewIdent("r")}, Type: ast.NewIdent("[]Atom")}, + }, childrenstmts)) + + return +} + +func genatoms(filename, outfilename string) { + // Create the AST by parsing src. + fset := token.NewFileSet() // positions are relative to fset + file, err := parser.ParseFile(fset, filename, nil, 0) + if err != nil { + panic(err) + } + + gen := &ast.File{} + gen.Name = ast.NewIdent("mp4io") + gen.Decls = []ast.Decl{ + &ast.GenDecl{ + Tok: token.IMPORT, + Specs: []ast.Spec{ + &ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"github.com/deepch/vdk/utils/bits/pio"`}}, + }, + }, + &ast.GenDecl{ + Tok: token.IMPORT, + Specs: []ast.Spec{ + &ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"time"`}}, + }, + }, + } + + tagnamemap := map[string]string{} + tagnamemap["ElemStreamDesc"] = "esds" + + splittagname := func(fnname string) (ok bool, tag, name string) { + if len(fnname) > 5 && fnname[4] == '_' { + tag = fnname[0:4] + tag = strings.Replace(tag, "_", " ", 1) + name = fnname[5:] + ok = true + } else { + name = fnname + } + return + } + + for _, decl := range file.Decls { + if fndecl, ok := decl.(*ast.FuncDecl); ok { + ok, tag, name := splittagname(fndecl.Name.Name) + if ok { + tagnamemap[name] = tag + } + } + } + + tagfuncdecl := func(name, tag string) (decls ast.Decl) { + return newdecl(name, "Tag", []*ast.Field{}, []*ast.Field{ + &ast.Field{Type: ast.NewIdent("Tag")}, + }, []ast.Stmt{ + &ast.ReturnStmt{ + Results: []ast.Expr{ast.NewIdent(strings.ToUpper(tag))}}}) + } + + for k, v := range tagnamemap { + gen.Decls = append(gen.Decls, cc4decls(v)...) + gen.Decls = append(gen.Decls, tagfuncdecl(k, v)) + } + gen.Decls = append(gen.Decls, cc4decls("mdat")...) + + for _, decl := range file.Decls { + if fndecl, ok := decl.(*ast.FuncDecl); ok { + ok, tag, name := splittagname(fndecl.Name.Name) + if ok { + gen.Decls = append(gen.Decls, genatomdecl(fndecl, name, tag)...) + gen.Decls = append(gen.Decls, getatommarshalfn(fndecl, name, tag, tagnamemap)...) + } else { + gen.Decls = append(gen.Decls, genatomdecl(fndecl, name, tag)...) + gen.Decls = append(gen.Decls, getstructputgetlenfn(fndecl, name)...) + } + } + } + + outfile, _ := os.Create(outfilename) + printer.Fprint(outfile, fset, gen) + outfile.Close() +} + +func parse(filename, outfilename string) { + fset := token.NewFileSet() + file, err := parser.ParseFile(fset, filename, nil, 0) + if err != nil { + panic(err) + } + outfile, _ := os.Create(outfilename) + ast.Fprint(outfile, fset, file, nil) + outfile.Close() +} + +func main() { + switch os.Args[1] { + case "parse": + parse(os.Args[2], os.Args[3]) + + case "gen": + genatoms(os.Args[2], os.Args[3]) + } +} diff --git a/format/mp4m/mp4io/gen/pattern.go b/format/mp4m/mp4io/gen/pattern.go new file mode 100644 index 0000000..0c9fa8e --- /dev/null +++ b/format/mp4m/mp4io/gen/pattern.go @@ -0,0 +1,436 @@ +package main + +func moov_Movie() { + atom(Header, MovieHeader) + atom(MovieExtend, MovieExtend) + atoms(Tracks, Track) + _unknowns() +} + +func mvhd_MovieHeader() { + uint8(Version) + uint24(Flags) + time32(CreateTime) + time32(ModifyTime) + int32(TimeScale) + int32(Duration) + fixed32(PreferredRate) + fixed16(PreferredVolume) + _skip(10) + array(Matrix, int32, 9) + time32(PreviewTime) + time32(PreviewDuration) + time32(PosterTime) + time32(SelectionTime) + time32(SelectionDuration) + time32(CurrentTime) + int32(NextTrackId) +} + +func trak_Track() { + atom(Header, TrackHeader) + atom(Media, Media) + _unknowns() +} + +func tkhd_TrackHeader() { + uint8(Version) + uint24(Flags) + time32(CreateTime) + time32(ModifyTime) + int32(TrackId) + _skip(4) + int32(Duration) + _skip(8) + int16(Layer) + int16(AlternateGroup) + fixed16(Volume) + _skip(2) + array(Matrix, int32, 9) + fixed32(TrackWidth) + fixed32(TrackHeight) +} + +func hdlr_HandlerRefer() { + uint8(Version) + uint24(Flags) + bytes(Type, 4) + bytes(SubType, 4) + bytesleft(Name) +} + +func mdia_Media() { + atom(Header, MediaHeader) + atom(Handler, HandlerRefer) + atom(Info, MediaInfo) + _unknowns() +} + +func mdhd_MediaHeader() { + uint8(Version) + uint24(Flags) + time32(CreateTime) + time32(ModifyTime) + int32(TimeScale) + int32(Duration) + int16(Language) + int16(Quality) +} + +func minf_MediaInfo() { + atom(Sound, SoundMediaInfo) + atom(Video, VideoMediaInfo) + atom(Data, DataInfo) + atom(Sample, SampleTable) + _unknowns() +} + +func dinf_DataInfo() { + atom(Refer, DataRefer) + _unknowns() +} + +func dref_DataRefer() { + uint8(Version) + uint24(Flags) + int32(_childrenNR) + atom(Url, DataReferUrl) +} + +func url__DataReferUrl() { + uint8(Version) + uint24(Flags) +} + +func smhd_SoundMediaInfo() { + uint8(Version) + uint24(Flags) + int16(Balance) + _skip(2) +} + +func vmhd_VideoMediaInfo() { + uint8(Version) + uint24(Flags) + int16(GraphicsMode) + array(Opcolor, int16, 3) +} + +func stbl_SampleTable() { + atom(SampleDesc, SampleDesc) + atom(TimeToSample, TimeToSample) + atom(CompositionOffset, CompositionOffset) + atom(SampleToChunk, SampleToChunk) + atom(SyncSample, SyncSample) + atom(ChunkOffset, ChunkOffset) + atom(SampleSize, SampleSize) +} + +func stsd_SampleDesc() { + uint8(Version) + _skip(3) + int32(_childrenNR) + atom(AVC1Desc, AVC1Desc) + atom(MP4ADesc, MP4ADesc) + _unknowns() +} + +func mp4a_MP4ADesc() { + _skip(6) + int16(DataRefIdx) + int16(Version) + int16(RevisionLevel) + int32(Vendor) + int16(NumberOfChannels) + int16(SampleSize) + int16(CompressionId) + _skip(2) + fixed32(SampleRate) + atom(Conf, ElemStreamDesc) + _unknowns() +} + +func avc1_AVC1Desc() { + _skip(6) + int16(DataRefIdx) + int16(Version) + int16(Revision) + int32(Vendor) + int32(TemporalQuality) + int32(SpatialQuality) + int16(Width) + int16(Height) + fixed32(HorizontalResolution) + fixed32(VorizontalResolution) + _skip(4) + int16(FrameCount) + bytes(CompressorName, 32) + int16(Depth) + int16(ColorTableId) + atom(Conf, AVC1Conf) + _unknowns() +} + +func avcC_AVC1Conf() { + bytesleft(Data) +} + +func stts_TimeToSample() { + uint8(Version) + uint24(Flags) + uint32(_len_Entries) + slice(Entries, TimeToSampleEntry) +} + +func TimeToSampleEntry() { + uint32(Count) + uint32(Duration) +} + +func stsc_SampleToChunk() { + uint8(Version) + uint24(Flags) + uint32(_len_Entries) + slice(Entries, SampleToChunkEntry) +} + +func SampleToChunkEntry() { + uint32(FirstChunk) + uint32(SamplesPerChunk) + uint32(SampleDescId) +} + +func ctts_CompositionOffset() { + uint8(Version) + uint24(Flags) + uint32(_len_Entries) + slice(Entries, CompositionOffsetEntry) +} + +func CompositionOffsetEntry() { + uint32(Count) + uint32(Offset) +} + +func stss_SyncSample() { + uint8(Version) + uint24(Flags) + uint32(_len_Entries) + slice(Entries, uint32) +} + +func stco_ChunkOffset() { + uint8(Version) + uint24(Flags) + uint32(_len_Entries) + slice(Entries, uint32) +} + +func moof_MovieFrag() { + atom(Header, MovieFragHeader) + atoms(Tracks, TrackFrag) + _unknowns() +} + +func mfhd_MovieFragHeader() { + uint8(Version) + uint24(Flags) + uint32(Seqnum) +} + +func traf_TrackFrag() { + atom(Header, TrackFragHeader) + atom(DecodeTime, TrackFragDecodeTime) + atom(Run, TrackFragRun) + _unknowns() +} + +func mvex_MovieExtend() { + atoms(Tracks, TrackExtend) + _unknowns() +} + +func trex_TrackExtend() { + uint8(Version) + uint24(Flags) + uint32(TrackId) + uint32(DefaultSampleDescIdx) + uint32(DefaultSampleDuration) + uint32(DefaultSampleSize) + uint32(DefaultSampleFlags) +} + +func stsz_SampleSize() { + uint8(Version) + uint24(Flags) + uint32(SampleSize) + _code(func() { + if self.SampleSize != 0 { + return + } + }) + uint32(_len_Entries) + slice(Entries, uint32) +} + +func trun_TrackFragRun() { + uint8(Version) + uint24(Flags) + uint32(_len_Entries) + + uint32(DataOffset, _code(func() { + if self.Flags&TRUN_DATA_OFFSET != 0 { + doit() + } + })) + + uint32(FirstSampleFlags, _code(func() { + if self.Flags&TRUN_FIRST_SAMPLE_FLAGS != 0 { + doit() + } + })) + + slice(Entries, TrackFragRunEntry, _code(func() { + for i, entry := range self.Entries { + var flags uint32 + if i > 0 { + flags = self.Flags + } else { + flags = self.FirstSampleFlags + } + if flags&TRUN_SAMPLE_DURATION != 0 { + pio.PutU32BE(b[n:], entry.Duration) + n += 4 + } + if flags&TRUN_SAMPLE_SIZE != 0 { + pio.PutU32BE(b[n:], entry.Size) + n += 4 + } + if flags&TRUN_SAMPLE_FLAGS != 0 { + pio.PutU32BE(b[n:], entry.Flags) + n += 4 + } + if flags&TRUN_SAMPLE_CTS != 0 { + pio.PutU32BE(b[n:], entry.Cts) + n += 4 + } + } + }, func() { + for i := range self.Entries { + var flags uint32 + if i > 0 { + flags = self.Flags + } else { + flags = self.FirstSampleFlags + } + if flags&TRUN_SAMPLE_DURATION != 0 { + n += 4 + } + if flags&TRUN_SAMPLE_SIZE != 0 { + n += 4 + } + if flags&TRUN_SAMPLE_FLAGS != 0 { + n += 4 + } + if flags&TRUN_SAMPLE_CTS != 0 { + n += 4 + } + } + }, func() { + for i := 0; i < int(_len_Entries); i++ { + var flags uint32 + if i > 0 { + flags = self.Flags + } else { + flags = self.FirstSampleFlags + } + entry := &self.Entries[i] + if flags&TRUN_SAMPLE_DURATION != 0 { + entry.Duration = pio.U32BE(b[n:]) + n += 4 + } + if flags&TRUN_SAMPLE_SIZE != 0 { + entry.Size = pio.U32BE(b[n:]) + n += 4 + } + if flags&TRUN_SAMPLE_FLAGS != 0 { + entry.Flags = pio.U32BE(b[n:]) + n += 4 + } + if flags&TRUN_SAMPLE_CTS != 0 { + entry.Cts = pio.U32BE(b[n:]) + n += 4 + } + } + })) +} + +func TrackFragRunEntry() { + uint32(Duration) + uint32(Size) + uint32(Flags) + uint32(Cts) +} + +func tfhd_TrackFragHeader() { + uint8(Version) + uint24(Flags) + + uint64(BaseDataOffset, _code(func() { + if self.Flags&TFHD_BASE_DATA_OFFSET != 0 { + doit() + } + })) + + uint32(StsdId, _code(func() { + if self.Flags&TFHD_STSD_ID != 0 { + doit() + } + })) + + uint32(DefaultDuration, _code(func() { + if self.Flags&TFHD_DEFAULT_DURATION != 0 { + doit() + } + })) + + uint32(DefaultSize, _code(func() { + if self.Flags&TFHD_DEFAULT_SIZE != 0 { + doit() + } + })) + + uint32(DefaultFlags, _code(func() { + if self.Flags&TFHD_DEFAULT_FLAGS != 0 { + doit() + } + })) +} + +func tfdt_TrackFragDecodeTime() { + uint8(Version) + uint24(Flags) + time64(Time, _code(func() { + if self.Version != 0 { + PutTime64(b[n:], self.Time) + n += 8 + } else { + PutTime32(b[n:], self.Time) + n += 4 + } + }, func() { + if self.Version != 0 { + n += 8 + } else { + n += 4 + } + }, func() { + if self.Version != 0 { + self.Time = GetTime64(b[n:]) + n += 8 + } else { + self.Time = GetTime32(b[n:]) + n += 4 + } + })) +} diff --git a/format/mp4m/mp4io/mp4io.go b/format/mp4m/mp4io/mp4io.go new file mode 100644 index 0000000..dfa99d5 --- /dev/null +++ b/format/mp4m/mp4io/mp4io.go @@ -0,0 +1,502 @@ +package mp4io + +import ( + "fmt" + "io" + "math" + "os" + "strings" + "time" + + "github.com/deepch/vdk/utils/bits/pio" +) + +type ParseError struct { + Debug string + Offset int + prev *ParseError +} + +func (self *ParseError) Error() string { + s := []string{} + for p := self; p != nil; p = p.prev { + s = append(s, fmt.Sprintf("%s:%d", p.Debug, p.Offset)) + } + return "mp4io: parse error: " + strings.Join(s, ",") +} + +func parseErr(debug string, offset int, prev error) (err error) { + _prev, _ := prev.(*ParseError) + return &ParseError{Debug: debug, Offset: offset, prev: _prev} +} + +func GetTime32(b []byte) (t time.Time) { + sec := pio.U32BE(b) + t = time.Date(1904, time.January, 1, 0, 0, 0, 0, time.UTC) + t = t.Add(time.Second * time.Duration(sec)) + return +} + +func PutTime32(b []byte, t time.Time) { + dur := t.Sub(time.Date(1904, time.January, 1, 0, 0, 0, 0, time.UTC)) + sec := uint32(dur / time.Second) + pio.PutU32BE(b, sec) +} + +func GetTime64(b []byte) (t time.Time) { + sec := pio.U64BE(b) + t = time.Date(1904, time.January, 1, 0, 0, 0, 0, time.UTC) + t = t.Add(time.Second * time.Duration(sec)) + return +} + +func PutTime64(b []byte, t time.Time) { + dur := t.Sub(time.Date(1904, time.January, 1, 0, 0, 0, 0, time.UTC)) + sec := uint64(dur / time.Second) + pio.PutU64BE(b, sec) +} + +func PutFixed16(b []byte, f float64) { + intpart, fracpart := math.Modf(f) + b[0] = uint8(intpart) + b[1] = uint8(fracpart * 256.0) +} + +func GetFixed16(b []byte) float64 { + return float64(b[0]) + float64(b[1])/256.0 +} + +func PutFixed32(b []byte, f float64) { + intpart, fracpart := math.Modf(f) + pio.PutU16BE(b[0:2], uint16(intpart)) + pio.PutU16BE(b[2:4], uint16(fracpart*65536.0)) +} + +func GetFixed32(b []byte) float64 { + return float64(pio.U16BE(b[0:2])) + float64(pio.U16BE(b[2:4]))/65536.0 +} + +type Tag uint32 + +func (self Tag) String() string { + var b [4]byte + pio.PutU32BE(b[:], uint32(self)) + 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 (self AtomPos) Pos() (int, int) { + return self.Offset, self.Size +} + +func (self *AtomPos) setPos(offset int, size int) { + self.Offset, self.Size = offset, size +} + +type Dummy struct { + Data []byte + Tag_ Tag + AtomPos +} + +func (self Dummy) Children() []Atom { + return nil +} + +func (self Dummy) Tag() Tag { + return self.Tag_ +} + +func (self Dummy) Len() int { + return len(self.Data) +} + +func (self Dummy) Marshal(b []byte) int { + copy(b, self.Data) + return len(self.Data) +} + +func (self *Dummy) Unmarshal(b []byte, offset int) (n int, err error) { + (&self.AtomPos).setPos(offset, len(b)) + self.Data = b + n = len(b) + 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 +} + +const ( + TFHD_BASE_DATA_OFFSET = 0x01 + TFHD_STSD_ID = 0x02 + TFHD_DEFAULT_DURATION = 0x08 + TFHD_DEFAULT_SIZE = 0x10 + TFHD_DEFAULT_FLAGS = 0x20 + TFHD_DURATION_IS_EMPTY = 0x010000 + TFHD_DEFAULT_BASE_IS_MOOF = 0x020000 +) + +const ( + TRUN_DATA_OFFSET = 0x01 + TRUN_FIRST_SAMPLE_FLAGS = 0x04 + TRUN_SAMPLE_DURATION = 0x100 + TRUN_SAMPLE_SIZE = 0x200 + TRUN_SAMPLE_FLAGS = 0x400 + TRUN_SAMPLE_CTS = 0x800 +) + +const ( + MP4ESDescrTag = 3 + MP4DecConfigDescrTag = 4 + MP4DecSpecificDescrTag = 5 +) + +type ElemStreamDesc struct { + DecConfig []byte + TrackId uint16 + AtomPos +} + +func (self ElemStreamDesc) Children() []Atom { + return nil +} + +func (self ElemStreamDesc) fillLength(b []byte, length int) (n int) { + for i := 3; i > 0; i-- { + b[n] = uint8(length>>uint(7*i))&0x7f | 0x80 + n++ + } + b[n] = uint8(length & 0x7f) + n++ + return +} + +func (self ElemStreamDesc) lenDescHdr() (n int) { + return 5 +} + +func (self ElemStreamDesc) fillDescHdr(b []byte, tag uint8, datalen int) (n int) { + b[n] = tag + n++ + n += self.fillLength(b[n:], datalen) + return +} + +func (self ElemStreamDesc) lenESDescHdr() (n int) { + return self.lenDescHdr() + 3 +} + +func (self ElemStreamDesc) fillESDescHdr(b []byte, datalen int) (n int) { + n += self.fillDescHdr(b[n:], MP4ESDescrTag, datalen) + pio.PutU16BE(b[n:], self.TrackId) + n += 2 + b[n] = 0 // flags + n++ + return +} + +func (self ElemStreamDesc) lenDecConfigDescHdr() (n int) { + return self.lenDescHdr() + 2 + 3 + 4 + 4 + self.lenDescHdr() +} + +func (self ElemStreamDesc) fillDecConfigDescHdr(b []byte, datalen int) (n int) { + n += self.fillDescHdr(b[n:], MP4DecConfigDescrTag, datalen) + b[n] = 0x40 // objectid + n++ + b[n] = 0x15 // streamtype + n++ + // buffer size db + pio.PutU24BE(b[n:], 0) + n += 3 + // max bitrage + pio.PutU32BE(b[n:], uint32(200000)) + n += 4 + // avg bitrage + pio.PutU32BE(b[n:], uint32(0)) + n += 4 + n += self.fillDescHdr(b[n:], MP4DecSpecificDescrTag, datalen-n) + return +} + +func (self ElemStreamDesc) Len() (n int) { + return 8 + 4 + self.lenESDescHdr() + self.lenDecConfigDescHdr() + len(self.DecConfig) + self.lenDescHdr() + 1 +} + +// Version(4) +// ESDesc( +// MP4ESDescrTag +// ESID(2) +// ESFlags(1) +// DecConfigDesc( +// MP4DecConfigDescrTag +// objectId streamType bufSize avgBitrate +// DecSpecificDesc( +// MP4DecSpecificDescrTag +// decConfig +// ) +// ) +// ?Desc(lenDescHdr+1) +// ) + +func (self ElemStreamDesc) Marshal(b []byte) (n int) { + pio.PutU32BE(b[4:], uint32(ESDS)) + n += 8 + pio.PutU32BE(b[n:], 0) // Version + n += 4 + datalen := self.Len() + n += self.fillESDescHdr(b[n:], datalen-n-self.lenESDescHdr()) + n += self.fillDecConfigDescHdr(b[n:], datalen-n-self.lenDescHdr()-1) + copy(b[n:], self.DecConfig) + n += len(self.DecConfig) + n += self.fillDescHdr(b[n:], 0x06, datalen-n-self.lenDescHdr()) + b[n] = 0x02 + n++ + pio.PutU32BE(b[0:], uint32(n)) + return +} + +func (self *ElemStreamDesc) Unmarshal(b []byte, offset int) (n int, err error) { + if len(b) < n+12 { + err = parseErr("hdr", offset+n, err) + return + } + (&self.AtomPos).setPos(offset, len(b)) + n += 8 + n += 4 + return self.parseDesc(b[n:], offset+n) +} + +func (self *ElemStreamDesc) parseDesc(b []byte, offset int) (n int, err error) { + var hdrlen int + var datalen int + var tag uint8 + if hdrlen, tag, datalen, err = self.parseDescHdr(b, offset); err != nil { + return + } + n += hdrlen + + if len(b) < n+datalen { + err = parseErr("datalen", offset+n, err) + return + } + + switch tag { + case MP4ESDescrTag: + if len(b) < n+3 { + err = parseErr("MP4ESDescrTag", offset+n, err) + return + } + if _, err = self.parseDesc(b[n+3:], offset+n+3); err != nil { + return + } + + case MP4DecConfigDescrTag: + const size = 2 + 3 + 4 + 4 + if len(b) < n+size { + err = parseErr("MP4DecSpecificDescrTag", offset+n, err) + return + } + if _, err = self.parseDesc(b[n+size:], offset+n+size); err != nil { + return + } + + case MP4DecSpecificDescrTag: + self.DecConfig = b[n:] + } + + n += datalen + return +} + +func (self *ElemStreamDesc) parseLength(b []byte, offset int) (n int, length int, err error) { + for n < 4 { + if len(b) < n+1 { + err = parseErr("len", offset+n, err) + return + } + c := b[n] + n++ + length = (length << 7) | (int(c) & 0x7f) + if c&0x80 == 0 { + break + } + } + return +} + +func (self *ElemStreamDesc) parseDescHdr(b []byte, offset int) (n int, tag uint8, datalen int, err error) { + if len(b) < n+1 { + err = parseErr("tag", offset+n, err) + return + } + tag = b[n] + n++ + var lenlen int + if lenlen, datalen, err = self.parseLength(b[n:], offset+n); err != nil { + return + } + n += lenlen + return +} + +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 MOOV: + atom = &Movie{} + case MOOF: + atom = &MovieFrag{} + } + + 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) + } + } + return +} + +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) +} + +func (self MovieHeader) String() string { + return fmt.Sprintf("dur=%d", self.Duration) +} + +func (self TimeToSample) String() string { + return fmt.Sprintf("entries=%d", len(self.Entries)) +} + +func (self SampleToChunk) String() string { + return fmt.Sprintf("entries=%d", len(self.Entries)) +} + +func (self SampleSize) String() string { + return fmt.Sprintf("entries=%d", len(self.Entries)) +} + +func (self SyncSample) String() string { + return fmt.Sprintf("entries=%d", len(self.Entries)) +} + +func (self CompositionOffset) String() string { + return fmt.Sprintf("entries=%d", len(self.Entries)) +} + +func (self ChunkOffset) String() string { + return fmt.Sprintf("entries=%d", len(self.Entries)) +} + +func (self TrackFragRun) String() string { + return fmt.Sprintf("dataoffset=%d", self.DataOffset) +} + +func (self TrackFragHeader) String() string { + return fmt.Sprintf("basedataoffset=%d", self.BaseDataOffset) +} + +func (self ElemStreamDesc) String() string { + return fmt.Sprintf("configlen=%d", len(self.DecConfig)) +} + +func (self *Track) GetAVC1Conf() (conf *AVC1Conf) { + atom := FindChildren(self, AVCC) + conf, _ = atom.(*AVC1Conf) + return +} + +func (self *Track) GetElemStreamDesc() (esds *ElemStreamDesc) { + atom := FindChildren(self, ESDS) + esds, _ = atom.(*ElemStreamDesc) + return +} diff --git a/format/mp4m/muxer.go b/format/mp4m/muxer.go new file mode 100644 index 0000000..f7b2735 --- /dev/null +++ b/format/mp4m/muxer.go @@ -0,0 +1,283 @@ +package mp4 + +import ( + "bufio" + "fmt" + "io" + "time" + + "github.com/deepch/vdk/av" + "github.com/deepch/vdk/codec/aacparser" + "github.com/deepch/vdk/codec/h264parser" + "github.com/deepch/vdk/format/mp4/mp4io" + "github.com/deepch/vdk/utils/bits/pio" +) + +type Muxer struct { + w io.WriteSeeker + bufw *bufio.Writer + wpos int64 + streams []*Stream +} + +func NewMuxer(w io.WriteSeeker) *Muxer { + return &Muxer{ + w: w, + bufw: bufio.NewWriterSize(w, pio.RecommendBufioSize), + } +} + +func (self *Muxer) newStream(codec av.CodecData) (err error) { + switch codec.Type() { + case av.H264, av.AAC: + + default: + err = fmt.Errorf("mp4: codec type=%v is not supported", codec.Type()) + return + } + stream := &Stream{CodecData: codec} + + stream.sample = &mp4io.SampleTable{ + SampleDesc: &mp4io.SampleDesc{}, + TimeToSample: &mp4io.TimeToSample{}, + SampleToChunk: &mp4io.SampleToChunk{ + Entries: []mp4io.SampleToChunkEntry{ + { + FirstChunk: 1, + SampleDescId: 1, + SamplesPerChunk: 1, + }, + }, + }, + SampleSize: &mp4io.SampleSize{}, + ChunkOffset: &mp4io.ChunkOffset{}, + } + + stream.trackAtom = &mp4io.Track{ + Header: &mp4io.TrackHeader{ + TrackId: int32(len(self.streams) + 1), + Flags: 0x0003, // Track enabled | Track in movie + Duration: 0, // fill later + Matrix: [9]int32{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000}, + }, + Media: &mp4io.Media{ + Header: &mp4io.MediaHeader{ + TimeScale: 0, // fill later + Duration: 0, // fill later + Language: 21956, + }, + Info: &mp4io.MediaInfo{ + Sample: stream.sample, + Data: &mp4io.DataInfo{ + Refer: &mp4io.DataRefer{ + Url: &mp4io.DataReferUrl{ + Flags: 0x000001, // Self reference + }, + }, + }, + }, + }, + } + + switch codec.Type() { + case av.H264: + stream.sample.SyncSample = &mp4io.SyncSample{} + } + + stream.timeScale = 90000 + stream.muxer = self + self.streams = append(self.streams, stream) + + return +} + +func (self *Stream) fillTrackAtom() (err error) { + self.trackAtom.Media.Header.TimeScale = int32(self.timeScale) + self.trackAtom.Media.Header.Duration = int32(self.duration) + + if self.Type() == av.H264 { + codec := self.CodecData.(h264parser.CodecData) + width, height := codec.Width(), codec.Height() + self.sample.SampleDesc.AVC1Desc = &mp4io.AVC1Desc{ + DataRefIdx: 1, + HorizontalResolution: 72, + VorizontalResolution: 72, + Width: int16(width), + Height: int16(height), + FrameCount: 1, + Depth: 24, + ColorTableId: -1, + Conf: &mp4io.AVC1Conf{Data: codec.AVCDecoderConfRecordBytes()}, + } + self.trackAtom.Media.Handler = &mp4io.HandlerRefer{ + SubType: [4]byte{'v', 'i', 'd', 'e'}, + Name: []byte("Video Media Handler"), + } + self.trackAtom.Media.Info.Video = &mp4io.VideoMediaInfo{ + Flags: 0x000001, + } + self.trackAtom.Header.TrackWidth = float64(width) + self.trackAtom.Header.TrackHeight = float64(height) + + } else if self.Type() == av.AAC { + codec := self.CodecData.(aacparser.CodecData) + self.sample.SampleDesc.MP4ADesc = &mp4io.MP4ADesc{ + DataRefIdx: 1, + NumberOfChannels: int16(codec.ChannelLayout().Count()), + SampleSize: int16(codec.SampleFormat().BytesPerSample()), + SampleRate: float64(codec.SampleRate()), + Conf: &mp4io.ElemStreamDesc{ + DecConfig: codec.MPEG4AudioConfigBytes(), + }, + } + self.trackAtom.Header.Volume = 1 + self.trackAtom.Header.AlternateGroup = 1 + self.trackAtom.Media.Handler = &mp4io.HandlerRefer{ + SubType: [4]byte{'s', 'o', 'u', 'n'}, + Name: []byte("Sound Handler"), + } + self.trackAtom.Media.Info.Sound = &mp4io.SoundMediaInfo{} + + } else { + err = fmt.Errorf("mp4: codec type=%d invalid", self.Type()) + } + + return +} + +func (self *Muxer) WriteHeader(streams []av.CodecData) (err error) { + self.streams = []*Stream{} + for _, stream := range streams { + if err = self.newStream(stream); err != nil { + return + } + } + + taghdr := make([]byte, 8) + pio.PutU32BE(taghdr[4:], uint32(mp4io.MDAT)) + if _, err = self.w.Write(taghdr); err != nil { + return + } + self.wpos += 8 + + for _, stream := range self.streams { + if stream.Type().IsVideo() { + stream.sample.CompositionOffset = &mp4io.CompositionOffset{} + } + } + return +} + +func (self *Muxer) WritePacket(pkt av.Packet) (err error) { + stream := self.streams[pkt.Idx] + if stream.lastpkt != nil { + if err = stream.writePacket(*stream.lastpkt, pkt.Time-stream.lastpkt.Time); err != nil { + return + } + } + stream.lastpkt = &pkt + return +} + +func (self *Stream) writePacket(pkt av.Packet, rawdur time.Duration) (err error) { + if rawdur < 0 { + err = fmt.Errorf("mp4: stream#%d time=%v < lasttime=%v", pkt.Idx, pkt.Time, self.lastpkt.Time) + return + } + + if _, err = self.muxer.bufw.Write(pkt.Data); err != nil { + return + } + + if pkt.IsKeyFrame && self.sample.SyncSample != nil { + self.sample.SyncSample.Entries = append(self.sample.SyncSample.Entries, uint32(self.sampleIndex+1)) + } + + duration := uint32(self.timeToTs(rawdur)) + if self.sttsEntry == nil || duration != self.sttsEntry.Duration { + self.sample.TimeToSample.Entries = append(self.sample.TimeToSample.Entries, mp4io.TimeToSampleEntry{Duration: duration}) + self.sttsEntry = &self.sample.TimeToSample.Entries[len(self.sample.TimeToSample.Entries)-1] + } + self.sttsEntry.Count++ + + if self.sample.CompositionOffset != nil { + offset := uint32(self.timeToTs(pkt.CompositionTime)) + if self.cttsEntry == nil || offset != self.cttsEntry.Offset { + table := self.sample.CompositionOffset + table.Entries = append(table.Entries, mp4io.CompositionOffsetEntry{Offset: offset}) + self.cttsEntry = &table.Entries[len(table.Entries)-1] + } + self.cttsEntry.Count++ + } + + self.duration += int64(duration) + self.sampleIndex++ + self.sample.ChunkOffset.Entries = append(self.sample.ChunkOffset.Entries, uint32(self.muxer.wpos)) + self.sample.SampleSize.Entries = append(self.sample.SampleSize.Entries, uint32(len(pkt.Data))) + + self.muxer.wpos += int64(len(pkt.Data)) + return +} + +func (self *Muxer) WriteTrailer() (err error) { + for _, stream := range self.streams { + if stream.lastpkt != nil { + if err = stream.writePacket(*stream.lastpkt, 0); err != nil { + return + } + stream.lastpkt = nil + } + } + + moov := &mp4io.Movie{} + moov.Header = &mp4io.MovieHeader{ + PreferredRate: 1, + PreferredVolume: 1, + Matrix: [9]int32{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000}, + NextTrackId: 2, + } + + maxDur := time.Duration(0) + timeScale := int64(10000) + for _, stream := range self.streams { + if err = stream.fillTrackAtom(); err != nil { + return + } + dur := stream.tsToTime(stream.duration) + stream.trackAtom.Header.Duration = int32(timeToTs(dur, timeScale)) + if dur > maxDur { + maxDur = dur + } + moov.Tracks = append(moov.Tracks, stream.trackAtom) + } + moov.Header.TimeScale = int32(timeScale) + moov.Header.Duration = int32(timeToTs(maxDur, timeScale)) + + if err = self.bufw.Flush(); err != nil { + return + } + + var mdatsize int64 + if mdatsize, err = self.w.Seek(0, 1); err != nil { + return + } + if _, err = self.w.Seek(0, 0); err != nil { + return + } + taghdr := make([]byte, 4) + pio.PutU32BE(taghdr, uint32(mdatsize)) + if _, err = self.w.Write(taghdr); err != nil { + return + } + + if _, err = self.w.Seek(0, 2); err != nil { + return + } + b := make([]byte, moov.Len()) + moov.Marshal(b) + if _, err = self.w.Write(b); err != nil { + return + } + + return +} diff --git a/format/mp4m/stream.go b/format/mp4m/stream.go new file mode 100644 index 0000000..a8842d9 --- /dev/null +++ b/format/mp4m/stream.go @@ -0,0 +1,59 @@ +package mp4 + +import ( + "time" + + "github.com/deepch/vdk/av" + "github.com/deepch/vdk/format/mp4/mp4io" +) + +type Stream struct { + av.CodecData + + trackAtom *mp4io.Track + idx int + + lastpkt *av.Packet + + timeScale int64 + duration int64 + + muxer *Muxer + demuxer *Demuxer + + sample *mp4io.SampleTable + sampleIndex int + + sampleOffsetInChunk int64 + syncSampleIndex int + + dts int64 + sttsEntryIndex int + sampleIndexInSttsEntry int + + cttsEntryIndex int + sampleIndexInCttsEntry int + + chunkGroupIndex int + chunkIndex int + sampleIndexInChunk int + + sttsEntry *mp4io.TimeToSampleEntry + cttsEntry *mp4io.CompositionOffsetEntry +} + +func timeToTs(tm time.Duration, timeScale int64) int64 { + return int64(tm * time.Duration(timeScale) / time.Second) +} + +func tsToTime(ts int64, timeScale int64) time.Duration { + return time.Duration(ts) * time.Second / time.Duration(timeScale) +} + +func (self *Stream) timeToTs(tm time.Duration) int64 { + return int64(tm * time.Duration(self.timeScale) / time.Second) +} + +func (self *Stream) tsToTime(ts int64) time.Duration { + return time.Duration(ts) * time.Second / time.Duration(self.timeScale) +}