From 6752dafead73a5992af4e4e1de1fd4ad7b7d5687 Mon Sep 17 00:00:00 2001 From: nareix Date: Fri, 29 Jul 2016 01:05:47 +0800 Subject: [PATCH] add new mp4io interface --- format/mp4/mp4io/gen/gen.go | 1057 +++++++++++++++++++++++++++++++ format/mp4/mp4io/gen/pattern.go | 437 +++++++++++++ format/mp4/mp4io/mp4io.go | 503 +++++++++++++++ 3 files changed, 1997 insertions(+) create mode 100644 format/mp4/mp4io/gen/gen.go create mode 100644 format/mp4/mp4io/gen/pattern.go create mode 100644 format/mp4/mp4io/mp4io.go diff --git a/format/mp4/mp4io/gen/gen.go b/format/mp4/mp4io/gen/gen.go new file mode 100644 index 0000000..fb7cc6d --- /dev/null +++ b/format/mp4/mp4io/gen/gen.go @@ -0,0 +1,1057 @@ + +package main + +import ( + "strings" + "fmt" + "os" + "go/ast" + "go/parser" + "go/token" + "go/printer" +) + +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/nareix/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/mp4/mp4io/gen/pattern.go b/format/mp4/mp4io/gen/pattern.go new file mode 100644 index 0000000..127d458 --- /dev/null +++ b/format/mp4/mp4io/gen/pattern.go @@ -0,0 +1,437 @@ +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/mp4/mp4io/mp4io.go b/format/mp4/mp4io/mp4io.go new file mode 100644 index 0000000..1be5edd --- /dev/null +++ b/format/mp4/mp4io/mp4io.go @@ -0,0 +1,503 @@ + +package mp4io + +import ( + "github.com/nareix/pio" + "os" + "io" + "fmt" + "time" + "math" + "strings" +) + +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 +} +