diff --git a/atom/genStruct.js b/atom/genStruct.js index 6c68689..61630c0 100644 --- a/atom/genStruct.js +++ b/atom/genStruct.js @@ -71,11 +71,16 @@ var atoms = { ], }, + handlerRefer: { + cc4: 'hdlr', + }, + media: { cc4: 'mdia', atoms: [ ['header', '*mediaHeader'], ['info', '*mediaInfo'], + ['hdlr', '*handlerRefer'], ], }, @@ -182,7 +187,7 @@ var atoms = { fields: [ ['version', 'int8'], ['flags', 'int24'], - ['entries', '[int32]int32'], + ['entries', '[int32]compositionOffsetEntry'], ], }, @@ -207,6 +212,7 @@ var atoms = { fields: [ ['version', 'int8'], ['flags', 'int24'], + ['sampleSize', 'int32'], ['entries', '[int32]int32'], ], }, @@ -225,7 +231,7 @@ var atoms = { var DeclReadFunc = (opts) => { var stmts = []; - var DebugStmt = type => StrStmt(`// ${JSON.stringify(type)}`); + var DebugStmt = type => `// ${JSON.stringify(type)}`; var ReadArr = (name, type) => { return [ @@ -234,7 +240,7 @@ var DeclReadFunc = (opts) => { type.varcount && [ DeclVar('count', 'int'), CallCheckAssign('ReadInt', ['r', type.varcount], ['count']), - StrStmt(`${name} = make(${typeStr(type)}, count)`), + `${name} = make(${typeStr(type)}, count)`, ], For(RangeN('i', type.varcount ? 'count' : type.count), [ ReadCommnType(name+'[i]', type), @@ -244,8 +250,7 @@ var DeclReadFunc = (opts) => { var elemTypeStr = type => typeStr(Object.assign({}, type, {arr: false})); var ReadAtoms = () => [ - StrStmt(`// ReadAtoms`), - For(StrStmt(`r.N > 0`), [ + For(`r.N > 0`, [ DeclVar('cc4', 'string'), DeclVar('ar', '*io.LimitedReader'), CallCheckAssign('ReadAtomHeader', ['r', '""'], ['ar', 'cc4']), @@ -254,12 +259,12 @@ var DeclReadFunc = (opts) => { field.type.arr ? [ DeclVar('item', elemTypeStr(field.type)), CallCheckAssign('Read'+field.type.Struct, ['ar'], ['item']), - StrStmt(`self.${field.name} = append(self.${field.name}, item)`), + `self.${field.name} = append(self.${field.name}, item)`, ] : [ CallCheckAssign('Read'+field.type.Struct, ['ar'], [`self.${field.name}`]), ], ] - ]), showlog && [StrStmt(`log.Println("skip", cc4)`)]), + ]), showlog && [`log.Println("skip", cc4)`]), CallCheckAssign('ReadDummy', ['ar', 'int(ar.N)'], ['_']), ]) ]; @@ -296,9 +301,70 @@ var DeclReadFunc = (opts) => { [['r', '*io.LimitedReader']], [[ptr?'res':'self', (ptr?'*':'')+opts.type], ['err', 'error']], [ - ptr && StrStmt(`self := &${opts.type}{}`), + ptr && `self := &${opts.type}{}`, !opts.atoms ? ReadFields() : ReadAtoms(), - ptr && StrStmt(`res = self`), + ptr && `res = self`, + ] + ); +}; + +var DeclWriteFunc = (opts) => { + var SavePos = [ + DeclVar('aw', '*Writer'), + CallCheckAssign('WriteAtomHeader', ['w', `"${opts.cc4}"`], ['aw']), + `w = aw`, + ]; + + var RestorePosSetSize = [ + CallCheckAssign('aw.Close', [], []), + ]; + + var WriteAtoms = () => opts.fields.map(field => { + var name = 'self.'+field.name; + return [ + `if ${name} != nil {`, + field.type.arr ? WriteArr(name, field.type) : WriteCommnType(name, field.type), + `}`, + ]; + }); + + var WriteArr = (name, type) => { + return [ + type.varcount && CallCheckAssign('WriteInt', ['w', `len(${name})`, type.varcount], []), + For(`_, elem := range ${name}`, [ + WriteCommnType('elem', type), + ]), + ]; + }; + + var WriteCommnType = (name, type) => { + if (type.struct) + return CallCheckAssign( + 'Write'+type.Struct, ['w', name], []); + return [ + CallCheckAssign( + 'Write'+type.fn, ['w', name, type.len].nonull(), []), + ] + }; + + var WriteField = (name, type) => { + if (name == '_') + return CallCheckAssign('WriteDummy', ['w', type.len], []); + if (type.arr && type.fn != 'Bytes') + return WriteArr('self.'+name, type); + return WriteCommnType('self.'+name, type); + }; + + var WriteFields = () => opts.fields.map(field => WriteField(field.name, field.type)); + + return Func( + 'Write'+opts.type, + [['w', 'io.WriteSeeker'], ['self', (opts.cc4?'*':'')+opts.type]], + [['err', 'error']], + [ + opts.cc4 && SavePos, + opts.atoms ? WriteAtoms() : WriteFields(), + opts.cc4 && RestorePosSetSize, ] ); }; @@ -320,7 +386,7 @@ D('DeclStruct', 'name', 'body'); D('StrStmt', 'content'); D('Switch', 'cond', 'cases', 'default'); -var showlog = false; +var showlog = true; var S = s => s && s || ''; var dumpFn = f => { @@ -334,7 +400,9 @@ var dumpFn = f => { var dumpStmts = stmts => { var dumpStmt = stmt => { - if (stmt instanceof Array) { + if (typeof(stmt) == 'string') { + return stmt; + } else if (stmt instanceof Array) { return dumpStmts(stmt); } if (stmt.cls == 'CallCheckAssign') { return `if ${stmt.rets.concat(['err']).join(',')} = ${stmt.fn}(${stmt.args.join(',')}); err != nil { @@ -433,9 +501,13 @@ var allStmts = () => { for (var k in atoms) { var atom = atoms[k]; - var name = uc(k); - var fields = (atom.fields || atom.atoms).map(field => { + + var fields = (atom.fields || atom.atoms); + if (fields == null) + continue; + + fields = fields.map(field => { return { name: uc(field[0]), type: parseType(field[1]), @@ -454,6 +526,13 @@ var allStmts = () => { cc4: atom.cc4, atoms: atom.atoms != null, }), + + DeclWriteFunc({ + type: name, + fields: fields, + cc4: atom.cc4, + atoms: atom.atoms != null, + }), ]); } diff --git a/atom/otherStruct.go b/atom/otherStruct.go new file mode 100644 index 0000000..633fd28 --- /dev/null +++ b/atom/otherStruct.go @@ -0,0 +1,114 @@ + +package atom + +import ( + "io" +) + +type SampleDescEntry struct { + Format string + DataRefIndex int + Data []byte +} + +func ReadSampleDescEntry(r *io.LimitedReader) (res *SampleDescEntry, err error) { + self := &SampleDescEntry{} + if r, self.Format, err = ReadAtomHeader(r, ""); err != nil { + return + } + if _, err = ReadDummy(r, 6); err != nil { + return + } + if self.DataRefIndex, err = ReadInt(r, 2); err != nil { + return + } + if self.Data, err = ReadBytes(r, int(r.N)); err != nil { + return + } + res = self + return +} + +func WriteSampleDescEntry(w io.WriteSeeker, self *SampleDescEntry) (err error) { + var aw *Writer + if aw, err = WriteAtomHeader(w, self.Format); err != nil { + return + } + w = aw + if err = WriteDummy(w, 6); err != nil { + return + } + if err = WriteInt(w, self.DataRefIndex, 2); err != nil { + return + } + if err = WriteBytes(w, self.Data); err != nil { + return + } + if err = aw.Close(); err != nil { + return + } + return +} + +type HandlerRefer struct { + Version int + Flags int + Type string + SubType string + Name string +} + +func ReadHandlerRefer(r *io.LimitedReader) (res *HandlerRefer, err error) { + self := &HandlerRefer{} + if self.Version, err = ReadInt(r, 3); err != nil { + return + } + if self.Flags, err = ReadInt(r, 1); err != nil { + return + } + if self.Type, err = ReadString(r, 4); err != nil { + return + } + if self.SubType, err = ReadString(r, 4); err != nil { + return + } + if _, err = ReadDummy(r, 12); err != nil { + return + } + if self.Name, err = ReadString(r, int(r.N)); err != nil { + return + } + res = self + return +} + +func WriteHandlerRefer(w io.WriteSeeker, self *HandlerRefer) (err error) { + var aw *Writer + if aw, err = WriteAtomHeader(w, "hdlr"); err != nil { + return + } + w = aw + if err = WriteInt(w, self.Version, 3); err != nil { + return + } + if err = WriteInt(w, self.Flags, 1); err != nil { + return + } + if err = WriteString(w, self.Type); err != nil { + return + } + if err = WriteString(w, self.SubType); err != nil { + return + } + if err = WriteDummy(w, 12); err != nil { + return + } + if err = WriteString(w, self.Name); err != nil { + return + } + if err = aw.Close(); err != nil { + return + } + return +} + diff --git a/atom/struct.go b/atom/struct.go index 07b80a5..b167f7a 100644 --- a/atom/struct.go +++ b/atom/struct.go @@ -3,18 +3,32 @@ package atom import ( "io" + "log" ) type FileType struct { } func ReadFileType(r *io.LimitedReader) (res *FileType, err error) { - + log.Println("ReadFileType") self := &FileType{} res = self return } +func WriteFileType(w io.WriteSeeker, self *FileType) (err error) { + log.Println("WriteFileType") + var aw *Writer + if aw, err = WriteAtomHeader(w, "ftyp"); err != nil { + return + } + w = aw + + if err = aw.Close(); err != nil { + return + } + return +} type Movie struct { Header *MovieHeader @@ -22,9 +36,8 @@ type Movie struct { } func ReadMovie(r *io.LimitedReader) (res *Movie, err error) { - + log.Println("ReadMovie") self := &Movie{} - // ReadAtoms for r.N > 0 { var cc4 string var ar *io.LimitedReader @@ -46,7 +59,10 @@ func ReadMovie(r *io.LimitedReader) (res *Movie, err error) { } self.Tracks = append(self.Tracks, item) } - + default: + { + log.Println("skip", cc4) + } } if _, err = ReadDummy(ar, int(ar.N)); err != nil { return @@ -55,6 +71,30 @@ func ReadMovie(r *io.LimitedReader) (res *Movie, err error) { res = self return } +func WriteMovie(w io.WriteSeeker, self *Movie) (err error) { + log.Println("WriteMovie") + var aw *Writer + if aw, err = WriteAtomHeader(w, "moov"); err != nil { + return + } + w = aw + if self.Header != nil { + if err = WriteMovieHeader(w, self.Header); err != nil { + return + } + } + if self.Tracks != nil { + for _, elem := range self.Tracks { + if err = WriteTrack(w, elem); err != nil { + return + } + } + } + if err = aw.Close(); err != nil { + return + } + return +} type MovieHeader struct { Version int @@ -76,7 +116,7 @@ type MovieHeader struct { } func ReadMovieHeader(r *io.LimitedReader) (res *MovieHeader, err error) { - + log.Println("ReadMovieHeader") self := &MovieHeader{} if self.Version, err = ReadInt(r, 1); err != nil { return @@ -134,6 +174,71 @@ func ReadMovieHeader(r *io.LimitedReader) (res *MovieHeader, err error) { res = self return } +func WriteMovieHeader(w io.WriteSeeker, self *MovieHeader) (err error) { + log.Println("WriteMovieHeader") + var aw *Writer + if aw, err = WriteAtomHeader(w, "mvhd"); err != nil { + return + } + w = aw + if err = WriteInt(w, self.Version, 1); err != nil { + return + } + if err = WriteInt(w, self.Flags, 3); err != nil { + return + } + if err = WriteTimeStamp(w, self.CTime, 4); err != nil { + return + } + if err = WriteTimeStamp(w, self.MTime, 4); err != nil { + return + } + if err = WriteTimeStamp(w, self.TimeScale, 4); err != nil { + return + } + if err = WriteTimeStamp(w, self.Duration, 4); err != nil { + return + } + if err = WriteInt(w, self.PreferredRate, 4); err != nil { + return + } + if err = WriteInt(w, self.PreferredVolume, 2); err != nil { + return + } + if err = WriteDummy(w, 10); err != nil { + return + } + for _, elem := range self.Matrix { + if err = WriteInt(w, elem, 4); err != nil { + return + } + } + if err = WriteTimeStamp(w, self.PreviewTime, 4); err != nil { + return + } + if err = WriteTimeStamp(w, self.PreviewDuration, 4); err != nil { + return + } + if err = WriteTimeStamp(w, self.PosterTime, 4); err != nil { + return + } + if err = WriteTimeStamp(w, self.SelectionTime, 4); err != nil { + return + } + if err = WriteTimeStamp(w, self.SelectionDuration, 4); err != nil { + return + } + if err = WriteTimeStamp(w, self.CurrentTime, 4); err != nil { + return + } + if err = WriteInt(w, self.NextTrackId, 4); err != nil { + return + } + if err = aw.Close(); err != nil { + return + } + return +} type Track struct { Header *TrackHeader @@ -141,9 +246,8 @@ type Track struct { } func ReadTrack(r *io.LimitedReader) (res *Track, err error) { - + log.Println("ReadTrack") self := &Track{} - // ReadAtoms for r.N > 0 { var cc4 string var ar *io.LimitedReader @@ -163,7 +267,10 @@ func ReadTrack(r *io.LimitedReader) (res *Track, err error) { return } } - + default: + { + log.Println("skip", cc4) + } } if _, err = ReadDummy(ar, int(ar.N)); err != nil { return @@ -172,6 +279,28 @@ func ReadTrack(r *io.LimitedReader) (res *Track, err error) { res = self return } +func WriteTrack(w io.WriteSeeker, self *Track) (err error) { + log.Println("WriteTrack") + var aw *Writer + if aw, err = WriteAtomHeader(w, "trak"); err != nil { + return + } + w = aw + if self.Header != nil { + if err = WriteTrackHeader(w, self.Header); err != nil { + return + } + } + if self.Media != nil { + if err = WriteMedia(w, self.Media); err != nil { + return + } + } + if err = aw.Close(); err != nil { + return + } + return +} type TrackHeader struct { Version int @@ -189,7 +318,7 @@ type TrackHeader struct { } func ReadTrackHeader(r *io.LimitedReader) (res *TrackHeader, err error) { - + log.Println("ReadTrackHeader") self := &TrackHeader{} if self.Version, err = ReadInt(r, 1); err != nil { return @@ -241,16 +370,75 @@ func ReadTrackHeader(r *io.LimitedReader) (res *TrackHeader, err error) { res = self return } +func WriteTrackHeader(w io.WriteSeeker, self *TrackHeader) (err error) { + log.Println("WriteTrackHeader") + var aw *Writer + if aw, err = WriteAtomHeader(w, "tkhd"); err != nil { + return + } + w = aw + if err = WriteInt(w, self.Version, 1); err != nil { + return + } + if err = WriteInt(w, self.Flags, 3); err != nil { + return + } + if err = WriteTimeStamp(w, self.CTime, 4); err != nil { + return + } + if err = WriteTimeStamp(w, self.MTime, 4); err != nil { + return + } + if err = WriteTimeStamp(w, self.TrackId, 4); err != nil { + return + } + if err = WriteDummy(w, 4); err != nil { + return + } + if err = WriteTimeStamp(w, self.Duration, 4); err != nil { + return + } + if err = WriteDummy(w, 8); err != nil { + return + } + if err = WriteInt(w, self.Layer, 2); err != nil { + return + } + if err = WriteInt(w, self.AlternateGroup, 2); err != nil { + return + } + if err = WriteInt(w, self.Volume, 2); err != nil { + return + } + if err = WriteDummy(w, 2); err != nil { + return + } + for _, elem := range self.Matrix { + if err = WriteInt(w, elem, 4); err != nil { + return + } + } + if err = WriteInt(w, self.TrackWidth, 4); err != nil { + return + } + if err = WriteInt(w, self.TrackHeader, 4); err != nil { + return + } + if err = aw.Close(); err != nil { + return + } + return +} type Media struct { Header *MediaHeader Info *MediaInfo + Hdlr *HandlerRefer } func ReadMedia(r *io.LimitedReader) (res *Media, err error) { - + log.Println("ReadMedia") self := &Media{} - // ReadAtoms for r.N > 0 { var cc4 string var ar *io.LimitedReader @@ -270,7 +458,16 @@ func ReadMedia(r *io.LimitedReader) (res *Media, err error) { return } } - + case "hdlr": + { + if self.Hdlr, err = ReadHandlerRefer(ar); err != nil { + return + } + } + default: + { + log.Println("skip", cc4) + } } if _, err = ReadDummy(ar, int(ar.N)); err != nil { return @@ -279,6 +476,33 @@ func ReadMedia(r *io.LimitedReader) (res *Media, err error) { res = self return } +func WriteMedia(w io.WriteSeeker, self *Media) (err error) { + log.Println("WriteMedia") + var aw *Writer + if aw, err = WriteAtomHeader(w, "mdia"); err != nil { + return + } + w = aw + if self.Header != nil { + if err = WriteMediaHeader(w, self.Header); err != nil { + return + } + } + if self.Info != nil { + if err = WriteMediaInfo(w, self.Info); err != nil { + return + } + } + if self.Hdlr != nil { + if err = WriteHandlerRefer(w, self.Hdlr); err != nil { + return + } + } + if err = aw.Close(); err != nil { + return + } + return +} type MediaHeader struct { Version int @@ -292,7 +516,7 @@ type MediaHeader struct { } func ReadMediaHeader(r *io.LimitedReader) (res *MediaHeader, err error) { - + log.Println("ReadMediaHeader") self := &MediaHeader{} if self.Version, err = ReadInt(r, 1); err != nil { return @@ -321,6 +545,42 @@ func ReadMediaHeader(r *io.LimitedReader) (res *MediaHeader, err error) { res = self return } +func WriteMediaHeader(w io.WriteSeeker, self *MediaHeader) (err error) { + log.Println("WriteMediaHeader") + var aw *Writer + if aw, err = WriteAtomHeader(w, "mdhd"); err != nil { + return + } + w = aw + if err = WriteInt(w, self.Version, 1); err != nil { + return + } + if err = WriteInt(w, self.Flags, 3); err != nil { + return + } + if err = WriteInt(w, self.CTime, 4); err != nil { + return + } + if err = WriteInt(w, self.MTime, 4); err != nil { + return + } + if err = WriteInt(w, self.TimeScale, 4); err != nil { + return + } + if err = WriteInt(w, self.Duration, 4); err != nil { + return + } + if err = WriteInt(w, self.Language, 2); err != nil { + return + } + if err = WriteInt(w, self.Quality, 2); err != nil { + return + } + if err = aw.Close(); err != nil { + return + } + return +} type MediaInfo struct { Sound *SoundMediaInfo @@ -329,9 +589,8 @@ type MediaInfo struct { } func ReadMediaInfo(r *io.LimitedReader) (res *MediaInfo, err error) { - + log.Println("ReadMediaInfo") self := &MediaInfo{} - // ReadAtoms for r.N > 0 { var cc4 string var ar *io.LimitedReader @@ -357,7 +616,10 @@ func ReadMediaInfo(r *io.LimitedReader) (res *MediaInfo, err error) { return } } - + default: + { + log.Println("skip", cc4) + } } if _, err = ReadDummy(ar, int(ar.N)); err != nil { return @@ -366,6 +628,33 @@ func ReadMediaInfo(r *io.LimitedReader) (res *MediaInfo, err error) { res = self return } +func WriteMediaInfo(w io.WriteSeeker, self *MediaInfo) (err error) { + log.Println("WriteMediaInfo") + var aw *Writer + if aw, err = WriteAtomHeader(w, "minf"); err != nil { + return + } + w = aw + if self.Sound != nil { + if err = WriteSoundMediaInfo(w, self.Sound); err != nil { + return + } + } + if self.Video != nil { + if err = WriteVideoMediaInfo(w, self.Video); err != nil { + return + } + } + if self.Sample != nil { + if err = WriteSampleTable(w, self.Sample); err != nil { + return + } + } + if err = aw.Close(); err != nil { + return + } + return +} type SoundMediaInfo struct { Version int @@ -374,7 +663,7 @@ type SoundMediaInfo struct { } func ReadSoundMediaInfo(r *io.LimitedReader) (res *SoundMediaInfo, err error) { - + log.Println("ReadSoundMediaInfo") self := &SoundMediaInfo{} if self.Version, err = ReadInt(r, 1); err != nil { return @@ -391,6 +680,30 @@ func ReadSoundMediaInfo(r *io.LimitedReader) (res *SoundMediaInfo, err error) { res = self return } +func WriteSoundMediaInfo(w io.WriteSeeker, self *SoundMediaInfo) (err error) { + log.Println("WriteSoundMediaInfo") + var aw *Writer + if aw, err = WriteAtomHeader(w, "smhd"); err != nil { + return + } + w = aw + if err = WriteInt(w, self.Version, 1); err != nil { + return + } + if err = WriteInt(w, self.Flags, 3); err != nil { + return + } + if err = WriteInt(w, self.Balance, 2); err != nil { + return + } + if err = WriteDummy(w, 2); err != nil { + return + } + if err = aw.Close(); err != nil { + return + } + return +} type VideoMediaInfo struct { Version int @@ -400,7 +713,7 @@ type VideoMediaInfo struct { } func ReadVideoMediaInfo(r *io.LimitedReader) (res *VideoMediaInfo, err error) { - + log.Println("ReadVideoMediaInfo") self := &VideoMediaInfo{} if self.Version, err = ReadInt(r, 1); err != nil { return @@ -419,6 +732,32 @@ func ReadVideoMediaInfo(r *io.LimitedReader) (res *VideoMediaInfo, err error) { res = self return } +func WriteVideoMediaInfo(w io.WriteSeeker, self *VideoMediaInfo) (err error) { + log.Println("WriteVideoMediaInfo") + var aw *Writer + if aw, err = WriteAtomHeader(w, "vmhd"); err != nil { + return + } + w = aw + if err = WriteInt(w, self.Version, 1); err != nil { + return + } + if err = WriteInt(w, self.Flags, 3); err != nil { + return + } + if err = WriteInt(w, self.GraphicsMode, 2); err != nil { + return + } + for _, elem := range self.Opcolor { + if err = WriteInt(w, elem, 2); err != nil { + return + } + } + if err = aw.Close(); err != nil { + return + } + return +} type SampleTable struct { SampleDesc *SampleDesc @@ -431,9 +770,8 @@ type SampleTable struct { } func ReadSampleTable(r *io.LimitedReader) (res *SampleTable, err error) { - + log.Println("ReadSampleTable") self := &SampleTable{} - // ReadAtoms for r.N > 0 { var cc4 string var ar *io.LimitedReader @@ -483,7 +821,10 @@ func ReadSampleTable(r *io.LimitedReader) (res *SampleTable, err error) { return } } - + default: + { + log.Println("skip", cc4) + } } if _, err = ReadDummy(ar, int(ar.N)); err != nil { return @@ -492,6 +833,53 @@ func ReadSampleTable(r *io.LimitedReader) (res *SampleTable, err error) { res = self return } +func WriteSampleTable(w io.WriteSeeker, self *SampleTable) (err error) { + log.Println("WriteSampleTable") + var aw *Writer + if aw, err = WriteAtomHeader(w, "stbl"); err != nil { + return + } + w = aw + if self.SampleDesc != nil { + if err = WriteSampleDesc(w, self.SampleDesc); err != nil { + return + } + } + if self.TimeToSample != nil { + if err = WriteTimeToSample(w, self.TimeToSample); err != nil { + return + } + } + if self.CompositionOffset != nil { + if err = WriteCompositionOffset(w, self.CompositionOffset); err != nil { + return + } + } + if self.SampleToChunk != nil { + if err = WriteSampleToChunk(w, self.SampleToChunk); err != nil { + return + } + } + if self.SyncSample != nil { + if err = WriteSyncSample(w, self.SyncSample); err != nil { + return + } + } + if self.ChunkOffset != nil { + if err = WriteChunkOffset(w, self.ChunkOffset); err != nil { + return + } + } + if self.SampleSize != nil { + if err = WriteSampleSize(w, self.SampleSize); err != nil { + return + } + } + if err = aw.Close(); err != nil { + return + } + return +} type SampleDesc struct { Version int @@ -500,7 +888,7 @@ type SampleDesc struct { } func ReadSampleDesc(r *io.LimitedReader) (res *SampleDesc, err error) { - + log.Println("ReadSampleDesc") self := &SampleDesc{} if self.Version, err = ReadInt(r, 1); err != nil { return @@ -521,6 +909,32 @@ func ReadSampleDesc(r *io.LimitedReader) (res *SampleDesc, err error) { res = self return } +func WriteSampleDesc(w io.WriteSeeker, self *SampleDesc) (err error) { + log.Println("WriteSampleDesc") + var aw *Writer + if aw, err = WriteAtomHeader(w, "stsd"); err != nil { + return + } + w = aw + if err = WriteInt(w, self.Version, 1); err != nil { + return + } + if err = WriteInt(w, self.Flags, 3); err != nil { + return + } + if err = WriteInt(w, len(self.Entries), 4); err != nil { + return + } + for _, elem := range self.Entries { + if err = WriteSampleDescEntry(w, elem); err != nil { + return + } + } + if err = aw.Close(); err != nil { + return + } + return +} type TimeToSample struct { Version int @@ -529,7 +943,7 @@ type TimeToSample struct { } func ReadTimeToSample(r *io.LimitedReader) (res *TimeToSample, err error) { - + log.Println("ReadTimeToSample") self := &TimeToSample{} if self.Version, err = ReadInt(r, 1); err != nil { return @@ -550,6 +964,32 @@ func ReadTimeToSample(r *io.LimitedReader) (res *TimeToSample, err error) { res = self return } +func WriteTimeToSample(w io.WriteSeeker, self *TimeToSample) (err error) { + log.Println("WriteTimeToSample") + var aw *Writer + if aw, err = WriteAtomHeader(w, "stts"); err != nil { + return + } + w = aw + if err = WriteInt(w, self.Version, 1); err != nil { + return + } + if err = WriteInt(w, self.Flags, 3); err != nil { + return + } + if err = WriteInt(w, len(self.Entries), 4); err != nil { + return + } + for _, elem := range self.Entries { + if err = WriteTimeToSampleEntry(w, elem); err != nil { + return + } + } + if err = aw.Close(); err != nil { + return + } + return +} type TimeToSampleEntry struct { Count int @@ -557,7 +997,7 @@ type TimeToSampleEntry struct { } func ReadTimeToSampleEntry(r *io.LimitedReader) (self TimeToSampleEntry, err error) { - + log.Println("ReadTimeToSampleEntry") if self.Count, err = ReadInt(r, 4); err != nil { return } @@ -566,6 +1006,16 @@ func ReadTimeToSampleEntry(r *io.LimitedReader) (self TimeToSampleEntry, err err } return } +func WriteTimeToSampleEntry(w io.WriteSeeker, self TimeToSampleEntry) (err error) { + log.Println("WriteTimeToSampleEntry") + if err = WriteInt(w, self.Count, 4); err != nil { + return + } + if err = WriteInt(w, self.Duration, 4); err != nil { + return + } + return +} type SampleToChunk struct { Version int @@ -574,7 +1024,7 @@ type SampleToChunk struct { } func ReadSampleToChunk(r *io.LimitedReader) (res *SampleToChunk, err error) { - + log.Println("ReadSampleToChunk") self := &SampleToChunk{} if self.Version, err = ReadInt(r, 1); err != nil { return @@ -595,6 +1045,32 @@ func ReadSampleToChunk(r *io.LimitedReader) (res *SampleToChunk, err error) { res = self return } +func WriteSampleToChunk(w io.WriteSeeker, self *SampleToChunk) (err error) { + log.Println("WriteSampleToChunk") + var aw *Writer + if aw, err = WriteAtomHeader(w, "stsc"); err != nil { + return + } + w = aw + if err = WriteInt(w, self.Version, 1); err != nil { + return + } + if err = WriteInt(w, self.Flags, 3); err != nil { + return + } + if err = WriteInt(w, len(self.Entries), 4); err != nil { + return + } + for _, elem := range self.Entries { + if err = WriteSampleToChunkEntry(w, elem); err != nil { + return + } + } + if err = aw.Close(); err != nil { + return + } + return +} type SampleToChunkEntry struct { FirstChunk int @@ -603,7 +1079,7 @@ type SampleToChunkEntry struct { } func ReadSampleToChunkEntry(r *io.LimitedReader) (self SampleToChunkEntry, err error) { - + log.Println("ReadSampleToChunkEntry") if self.FirstChunk, err = ReadInt(r, 4); err != nil { return } @@ -615,15 +1091,28 @@ func ReadSampleToChunkEntry(r *io.LimitedReader) (self SampleToChunkEntry, err e } return } +func WriteSampleToChunkEntry(w io.WriteSeeker, self SampleToChunkEntry) (err error) { + log.Println("WriteSampleToChunkEntry") + if err = WriteInt(w, self.FirstChunk, 4); err != nil { + return + } + if err = WriteInt(w, self.SamplesPerChunk, 4); err != nil { + return + } + if err = WriteInt(w, self.SampleDescId, 4); err != nil { + return + } + return +} type CompositionOffset struct { Version int Flags int - Entries []int + Entries []CompositionOffsetEntry } func ReadCompositionOffset(r *io.LimitedReader) (res *CompositionOffset, err error) { - + log.Println("ReadCompositionOffset") self := &CompositionOffset{} if self.Version, err = ReadInt(r, 1); err != nil { return @@ -635,15 +1124,41 @@ func ReadCompositionOffset(r *io.LimitedReader) (res *CompositionOffset, err err if count, err = ReadInt(r, 4); err != nil { return } - self.Entries = make([]int, count) + self.Entries = make([]CompositionOffsetEntry, count) for i := 0; i < count; i++ { - if self.Entries[i], err = ReadInt(r, 4); err != nil { + if self.Entries[i], err = ReadCompositionOffsetEntry(r); err != nil { return } } res = self return } +func WriteCompositionOffset(w io.WriteSeeker, self *CompositionOffset) (err error) { + log.Println("WriteCompositionOffset") + var aw *Writer + if aw, err = WriteAtomHeader(w, "ctts"); err != nil { + return + } + w = aw + if err = WriteInt(w, self.Version, 1); err != nil { + return + } + if err = WriteInt(w, self.Flags, 3); err != nil { + return + } + if err = WriteInt(w, len(self.Entries), 4); err != nil { + return + } + for _, elem := range self.Entries { + if err = WriteCompositionOffsetEntry(w, elem); err != nil { + return + } + } + if err = aw.Close(); err != nil { + return + } + return +} type CompositionOffsetEntry struct { Count int @@ -651,7 +1166,7 @@ type CompositionOffsetEntry struct { } func ReadCompositionOffsetEntry(r *io.LimitedReader) (self CompositionOffsetEntry, err error) { - + log.Println("ReadCompositionOffsetEntry") if self.Count, err = ReadInt(r, 4); err != nil { return } @@ -660,6 +1175,16 @@ func ReadCompositionOffsetEntry(r *io.LimitedReader) (self CompositionOffsetEntr } return } +func WriteCompositionOffsetEntry(w io.WriteSeeker, self CompositionOffsetEntry) (err error) { + log.Println("WriteCompositionOffsetEntry") + if err = WriteInt(w, self.Count, 4); err != nil { + return + } + if err = WriteInt(w, self.Offset, 4); err != nil { + return + } + return +} type SyncSample struct { Version int @@ -668,7 +1193,7 @@ type SyncSample struct { } func ReadSyncSample(r *io.LimitedReader) (res *SyncSample, err error) { - + log.Println("ReadSyncSample") self := &SyncSample{} if self.Version, err = ReadInt(r, 1); err != nil { return @@ -689,15 +1214,42 @@ func ReadSyncSample(r *io.LimitedReader) (res *SyncSample, err error) { res = self return } +func WriteSyncSample(w io.WriteSeeker, self *SyncSample) (err error) { + log.Println("WriteSyncSample") + var aw *Writer + if aw, err = WriteAtomHeader(w, "stss"); err != nil { + return + } + w = aw + if err = WriteInt(w, self.Version, 1); err != nil { + return + } + if err = WriteInt(w, self.Flags, 3); err != nil { + return + } + if err = WriteInt(w, len(self.Entries), 4); err != nil { + return + } + for _, elem := range self.Entries { + if err = WriteInt(w, elem, 4); err != nil { + return + } + } + if err = aw.Close(); err != nil { + return + } + return +} type SampleSize struct { - Version int - Flags int - Entries []int + Version int + Flags int + SampleSize int + Entries []int } func ReadSampleSize(r *io.LimitedReader) (res *SampleSize, err error) { - + log.Println("ReadSampleSize") self := &SampleSize{} if self.Version, err = ReadInt(r, 1); err != nil { return @@ -705,6 +1257,9 @@ func ReadSampleSize(r *io.LimitedReader) (res *SampleSize, err error) { if self.Flags, err = ReadInt(r, 3); err != nil { return } + if self.SampleSize, err = ReadInt(r, 4); err != nil { + return + } var count int if count, err = ReadInt(r, 4); err != nil { return @@ -718,6 +1273,35 @@ func ReadSampleSize(r *io.LimitedReader) (res *SampleSize, err error) { res = self return } +func WriteSampleSize(w io.WriteSeeker, self *SampleSize) (err error) { + log.Println("WriteSampleSize") + var aw *Writer + if aw, err = WriteAtomHeader(w, "stsz"); err != nil { + return + } + w = aw + if err = WriteInt(w, self.Version, 1); err != nil { + return + } + if err = WriteInt(w, self.Flags, 3); err != nil { + return + } + if err = WriteInt(w, self.SampleSize, 4); err != nil { + return + } + if err = WriteInt(w, len(self.Entries), 4); err != nil { + return + } + for _, elem := range self.Entries { + if err = WriteInt(w, elem, 4); err != nil { + return + } + } + if err = aw.Close(); err != nil { + return + } + return +} type ChunkOffset struct { Version int @@ -726,7 +1310,7 @@ type ChunkOffset struct { } func ReadChunkOffset(r *io.LimitedReader) (res *ChunkOffset, err error) { - + log.Println("ReadChunkOffset") self := &ChunkOffset{} if self.Version, err = ReadInt(r, 1); err != nil { return @@ -747,3 +1331,29 @@ func ReadChunkOffset(r *io.LimitedReader) (res *ChunkOffset, err error) { res = self return } +func WriteChunkOffset(w io.WriteSeeker, self *ChunkOffset) (err error) { + log.Println("WriteChunkOffset") + var aw *Writer + if aw, err = WriteAtomHeader(w, "stco"); err != nil { + return + } + w = aw + if err = WriteInt(w, self.Version, 1); err != nil { + return + } + if err = WriteInt(w, self.Flags, 3); err != nil { + return + } + if err = WriteInt(w, len(self.Entries), 4); err != nil { + return + } + for _, elem := range self.Entries { + if err = WriteInt(w, elem, 4); err != nil { + return + } + } + if err = aw.Close(); err != nil { + return + } + return +} diff --git a/atom/writer.go b/atom/writer.go new file mode 100644 index 0000000..325df54 --- /dev/null +++ b/atom/writer.go @@ -0,0 +1,80 @@ + +package atom + +import ( + "io" + "log" +) + +func WriteBytes(w io.Writer, b []byte) (err error) { + _, err = w.Write(b) + return +} + +func WriteUInt(w io.Writer, val uint, n int) (err error) { + var b [8]byte + for i := n-1; i >= 0; i-- { + b[i] = byte(val) + val >>= 8 + } + return WriteBytes(w, b[0:n]) +} + +func WriteInt(w io.Writer, val int, n int) (err error) { + return WriteUInt(w, uint(val), n) +} + +func WriteTimeStamp(w io.Writer, ts TimeStamp, n int) (err error) { + return WriteUInt(w, uint(ts), n) +} + +func WriteString(w io.Writer, val string) (err error) { + return WriteBytes(w, []byte(val)) +} + +func WriteDummy(w io.Writer, n int) (err error) { + return WriteBytes(w, make([]byte, n)) +} + +type Writer struct { + io.WriteSeeker + sizePos int64 +} + +func (self *Writer) Close() (err error) { + var curPos int64 + if curPos, err = self.Seek(0, 1); err != nil { + return + } + if _, err = self.Seek(self.sizePos, 0); err != nil { + return + } + if err = WriteInt(self, int(curPos - self.sizePos), 4); err != nil { + return + } + if _, err = self.Seek(curPos, 0); err != nil { + return + } + if false { + log.Println("writeback", self.sizePos, curPos, curPos-self.sizePos) + } + return +} + +func WriteAtomHeader(w io.WriteSeeker, cc4 string) (res *Writer, err error) { + self := &Writer{WriteSeeker: w} + + if self.sizePos, err = w.Seek(0, 1); err != nil { + return + } + if err = WriteDummy(self, 4); err != nil { + return + } + if err = WriteString(self, cc4); err != nil { + return + } + + res = self + return +} + diff --git a/mp4.go b/mp4.go index 73bfb3d..b26cf05 100644 --- a/mp4.go +++ b/mp4.go @@ -45,24 +45,47 @@ func Open(filename string) (file *File, err error) { lr := &io.LimitedReader{R: osfile, N: finfo.Size()} - for lr.N > 0 { - var r *io.LimitedReader - var cc4 string - if r, cc4, err = atom.ReadAtomHeader(lr, ""); err != nil { - return - } - if cc4 == "moov" { - var moov *atom.Movie - if moov, err = atom.ReadMovie(r); err != nil { - return - } - log.Println("tracks nr", len(moov.Tracks)) - } - log.Println("atom", cc4, "left", lr.N) - atom.ReadDummy(r, int(r.N)) + var outfile *os.File + if outfile, err = os.Create(filename+".out.mp4"); err != nil { + return } - if _, err = os.Create(filename+".out.mp4"); err != nil { + for lr.N > 0 { + var ar *io.LimitedReader + + var cc4 string + if ar, cc4, err = atom.ReadAtomHeader(lr, ""); err != nil { + return + } + + if cc4 == "moov" { + var moov *atom.Movie + if moov, err = atom.ReadMovie(ar); err != nil { + return + } + log.Println("regen moov", "tracks nr", len(moov.Tracks)) + if err = atom.WriteMovie(outfile, moov); err != nil { + return + } + } else { + var aw *atom.Writer + if aw, err = atom.WriteAtomHeader(outfile, cc4); err != nil { + return + } + log.Println("copy", cc4) + if _, err = io.CopyN(aw, ar, ar.N); err != nil { + return + } + if err = aw.Close(); err != nil { + return + } + } + + //log.Println("atom", cc4, "left", lr.N) + //atom.ReadDummy(ar, int(ar.N)) + } + + if err = outfile.Close(); err != nil { return }