add new mp4io interface

This commit is contained in:
nareix 2016-07-29 01:05:47 +08:00
parent 1c1d351104
commit 6752dafead
3 changed files with 1997 additions and 0 deletions

1057
format/mp4/mp4io/gen/gen.go Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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
}
}))
}

503
format/mp4/mp4io/mp4io.go Normal file
View File

@ -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
}