test mp4
This commit is contained in:
		| @@ -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]) | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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]) | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										446
									
								
								format/mp4m/demuxer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										446
									
								
								format/mp4m/demuxer.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										32
									
								
								format/mp4m/handler.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								format/mp4m/handler.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										3822
									
								
								format/mp4m/mp4io/atoms.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3822
									
								
								format/mp4m/mp4io/atoms.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1055
									
								
								format/mp4m/mp4io/gen/gen.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1055
									
								
								format/mp4m/mp4io/gen/gen.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										436
									
								
								format/mp4m/mp4io/gen/pattern.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										436
									
								
								format/mp4m/mp4io/gen/pattern.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| 		} | ||||
| 	})) | ||||
| } | ||||
							
								
								
									
										502
									
								
								format/mp4m/mp4io/mp4io.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										502
									
								
								format/mp4m/mp4io/mp4io.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										283
									
								
								format/mp4m/muxer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										283
									
								
								format/mp4m/muxer.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										59
									
								
								format/mp4m/stream.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								format/mp4m/stream.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Andrey Semochkin
					Andrey Semochkin