rewrite genStruct.js

This commit is contained in:
nareix 2015-11-21 14:09:02 +08:00
parent dff1b98e9d
commit 3bbc27f470
5 changed files with 873 additions and 531 deletions

View File

@ -4,227 +4,302 @@ Array.prototype.nonull = function () {
return this.filter(x => x); return this.filter(x => x);
}; };
var Int = (name,len) => {return {name:uc(name),len,type:'int',fn:'Int'}};
var Str = (name,len) => {return {name:uc(name),len,type:'string',fn:'String'}};
var TimeStamp = (name,len) => {return {name:uc(name),len,type:'TimeStamp',fn:'TimeStamp'}};
var Bytes = (name,len) => {return {name:uc(name),len,type:'[]byte',fn:'Bytes'}};
var BytesLeft = (name) => {return {name:uc(name),type:'[]byte',fn:'BytesLeft'}};
var Fixed32 = (name,len) => {return {name:uc(name),len,type:'Fixed32',fn:'Fixed32'}};
var Atom = (type,name) => {return {name:uc(name),type:uc(type)+'Atom',fn:uc(type)+'Atom'}};
var AtomPtr = (type,name) => {return {name:uc(name),type:'*'+uc(type)+'Atom',fn:uc(type)+'Atom'}};
var Struct = (type,name) => {return {name:uc(name),type:uc(type),fn:uc(type)}};
var StructPtr = (type,name) => {return {name:uc(name),type:'*'+uc(type),fn:uc(type)}};
var Arr = (name,elem,count) => {return {name:uc(name),elem,count,type:'[]'+elem.type}};
var LenArr = (sizelen,name,elem) => {return {sizelen,name:uc(name),elem,type:'[]'+elem.type}};
var Size = (len) => {return {len,hide:true,fn:'Int'}};
var _ = (len) => {return {len,hide:true,fn:'Dummy'}};
var atoms = { var atoms = {
fileType: [ fileType: {
'ftyp', cc4: 'ftyp',
AtomPtr('movie', 'movie'), fields: [
], ],
},
movie: [ movie: {
'moov', cc4: 'moov',
AtomPtr('movieHeader', 'header'), atoms: [
Arr('tracks', AtomPtr('track')), ['header', '*movieHeader'],
], ['tracks', '[]*track'],
],
},
movieHeader: [ movieHeader: {
'mvhd', cc4: 'mvhd',
Int('version', 1), fields: [
Int('flags', 3), ['version', 'int8'],
TimeStamp('cTime', 4), ['flags', 'int24'],
TimeStamp('mTime', 4), ['cTime', 'TimeStamp32'],
Int('timeScale', 4), ['mTime', 'TimeStamp32'],
Int('duration', 4), ['timeScale', 'TimeStamp32'],
Int('preferredRate', 4), ['duration', 'TimeStamp32'],
Int('preferredVolume', 2), ['preferredRate', 'int32'],
_(10), ['preferredVolume', 'int16'],
Bytes('matrix', 36), ['_', '[10]byte'],
TimeStamp('previewTime', 4), ['matrix', '[9]int32'],
TimeStamp('previewDuration', 4), ['previewTime', 'TimeStamp32'],
TimeStamp('posterTime', 4), ['previewDuration', 'TimeStamp32'],
TimeStamp('selectionTime', 4), ['posterTime', 'TimeStamp32'],
TimeStamp('selectionDuration', 4), ['selectionTime', 'TimeStamp32'],
TimeStamp('currentTime', 4), ['selectionDuration', 'TimeStamp32'],
Int('nextTrackId', 4), ['currentTime', 'TimeStamp32'],
], ['nextTrackId', 'int32'],
],
},
track: [ track: {
'trak', cc4: 'trak',
AtomPtr('trackHeader', 'header'), atoms: [
AtomPtr('media', 'media'), ['header', '*trackHeader'],
], ['media', '*media'],
],
},
trackHeader: [ trackHeader: {
'tkhd', cc4: 'tkhd',
Int('version', 1), fields: [
Int('flags', 3), ['version', 'int8'],
TimeStamp('cTime', 4), ['flags', 'int24'],
TimeStamp('mTime', 4), ['cTime', 'TimeStamp32'],
Int('trackId', 4), ['mTime', 'TimeStamp32'],
_(4), ['trackId', 'TimeStamp32'],
Int('duration', 4), ['_', '[4]byte'],
_(8), ['duration', 'TimeStamp32'],
Int('layer', 2), ['_', '[8]byte'],
Int('alternateGroup', 2), ['layer', 'int16'],
Int('volume', 2), ['alternateGroup', 'int16'],
_(2), ['volume', 'int16'],
Bytes('matrix', 36), ['_', '[2]byte'],
Fixed32('trackWidth', 4), ['matrix', '[9]int32'],
Fixed32('trackHeight', 4), ['trackWidth', 'int32'],
], ['trackHeader', 'int32'],
],
},
media: [ media: {
'mdia', cc4: 'mdia',
AtomPtr('mediaHeader', 'header'), atoms: [
AtomPtr('mediaInfo', 'info'), ['header', '*mediaHeader'],
], ['info', '*mediaInfo'],
],
},
mediaHeader: [ mediaHeader: {
'mdhd', cc4: 'mdhd',
Int('version', 1), fields: [
Int('flags', 3), ['version', 'int8'],
TimeStamp('cTime', 4), ['flags', 'int24'],
TimeStamp('mTime', 4), ['cTime', 'int32'],
Int('timeScale', 4), ['mTime', 'int32'],
Int('duration', 4), ['timeScale', 'int32'],
Int('language', 2), ['duration', 'int32'],
Int('quality', 2), ['language', 'int16'],
], ['quality', 'int16'],
],
},
mediaInfo: [ mediaInfo: {
'minf', cc4: 'minf',
AtomPtr('videoMediaInfo', 'video'), atoms: [
AtomPtr('sampleTable', 'sample'), ['sound', '*soundMediaInfo'],
], ['video', '*videoMediaInfo'],
['sample', '*sampleTable'],
],
},
videoMediaInfo: [ soundMediaInfo: {
'vmhd', cc4: 'smhd',
Int('version', 1), fields: [
Int('flags', 3), ['version', 'int8'],
Int('graphicsMode', 2), ['flags', 'int24'],
Arr('opcolor', Int('', 2), 3), ['balance', 'int16'],
], ['_', 'int16'],
],
},
sampleTable: [ videoMediaInfo: {
'stbl', cc4: 'vmhd',
AtomPtr('sampleDesc', 'sampleDesc'), fields: [
AtomPtr('timeToSample', 'timeToSample'), ['version', 'int8'],
AtomPtr('compositionOffset', 'compositionOffset'), ['flags', 'int24'],
AtomPtr('syncSample', 'syncSample'), ['graphicsMode', 'int16'],
AtomPtr('sampleSize', 'sampleSize'), ['opcolor', '[3]int16'],
AtomPtr('chunkOffset', 'chunkOffset'), ],
], },
sampleDesc: [ sampleTable: {
'stsd', cc4: 'stbl',
Int('version', 1), atoms: [
Int('flags', 3), ['sampleDesc', '*sampleDesc'],
LenArr(4, 'entries', Struct('sampleDescEntry')), ['timeToSample', '*timeToSample'],
], ['compositionOffset', '*compositionOffset'],
['sampleToChunk', '*sampleToChunk'],
['syncSample', '*syncSample'],
['chunkOffset', '*chunkOffset'],
['sampleSize', '*sampleSize'],
],
},
timeToSample: [ sampleDesc: {
'stts', cc4: 'stsd',
Int('version', 1), fields: [
Int('flags', 3), ['version', 'int8'],
LenArr(4, 'entries', Struct('timeToSampleEntry')), ['flags', 'int24'],
], ['entries', '[int32]*sampleDescEntry'],
],
},
compositionOffset: [ timeToSample: {
'ctts', cc4: 'stts',
Int('version', 1), fields: [
Int('flags', 3), ['version', 'int8'],
LenArr(4, 'entries', Struct('compositionOffsetEntry')), ['flags', 'int24'],
], ['entries', '[int32]timeToSampleEntry'],
],
},
syncSample: [ timeToSampleEntry: {
'stss', fields: [
Int('version', 1), ['count', 'int32'],
Int('flags', 3), ['duration', 'int32'],
LenArr(4, 'entries', Int('', 4)), ],
], },
sampleSize: [ sampleToChunk: {
'stsz', cc4: 'stsc',
Int('version', 1), fields: [
Int('flags', 3), ['version', 'int8'],
LenArr(4, 'entries', Int('', 4)), ['flags', 'int24'],
], ['entries', '[int32]sampleToChunkEntry'],
],
},
sampleToChunkEntry: {
fields: [
['firstChunk', 'int32'],
['samplesPerChunk', 'int32'],
['sampleDescId', 'int32'],
],
},
compositionOffset: {
cc4: 'ctts',
fields: [
['version', 'int8'],
['flags', 'int24'],
['entries', '[int32]int32'],
],
},
compositionOffsetEntry: {
fields: [
['count', 'int32'],
['offset', 'int32'],
],
},
syncSample: {
cc4: 'stss',
fields: [
['version', 'int8'],
['flags', 'int24'],
['entries', '[int32]int32'],
],
},
sampleSize: {
cc4: 'stsz',
fields: [
['version', 'int8'],
['flags', 'int24'],
['entries', '[int32]int32'],
],
},
chunkOffset: {
cc4: 'stco',
fields: [
['version', 'int8'],
['flags', 'int24'],
['entries', '[int32]int32'],
],
},
chunkOffset: [
'stco',
Int('version', 1),
Int('flags', 3),
LenArr(4, 'entries', Int('', 4)),
],
}; };
var structs = { var DeclReadFunc = (opts) => {
sampleDescEntry: [
Size(4),
Str('format', 4),
_(6),
Int('dataRefIdx', 2),
BytesLeft('data'),
],
timeToSampleEntry: [
Int('count', 4),
Int('duration', 4),
],
compositionOffsetEntry: [
Int('count', 4),
Int('offset', 4),
],
};
var genReadStmts = (opts) => {
var stmts = []; var stmts = [];
if (opts.resIsPtr) var DebugStmt = type => StrStmt(`// ${JSON.stringify(type)}`);
stmts = stmts.concat([StrStmt(`self := &${opts.atomType}{}`)]);
var readElemStmts = field => { var ReadArr = (name, type) => {
var arr = 'self.'+field.name;
return [ return [
DeclVar('item', field.elem.type), //StrStmt('// ReadArr'),
CallCheckAssign('Read'+field.elem.fn, ['r', field.elem.len].nonull(), ['item']), //DebugStmt(type),
StrStmt(`${arr} = append(${arr}, item)`), type.varcount && [
DeclVar('count', 'int'),
CallCheckAssign('ReadInt', ['r', type.varcount], ['count']),
StrStmt(`${name} = make(${typeStr(type)}, count)`),
],
For(RangeN('i', type.varcount ? 'count' : type.count), [
ReadCommnType(name+'[i]', type),
]),
];
};
var elemTypeStr = type => typeStr(Object.assign({}, type, {arr: false}));
var ReadAtoms = () => [
StrStmt(`// ReadAtoms`),
For(StrStmt(`r.N > 0`), [
DeclVar('cc4', 'string'),
DeclVar('ar', '*io.LimitedReader'),
CallCheckAssign('ReadAtomHeader', ['r', '""'], ['ar', 'cc4']),
Switch('cc4', opts.fields.map(field => [
`"${atoms[field.type.struct].cc4}"`, [
field.type.arr ? [
DeclVar('item', elemTypeStr(field.type)),
CallCheckAssign('Read'+field.type.Struct, ['ar'], ['item']),
StrStmt(`self.${field.name} = append(self.${field.name}, item)`),
] : [
CallCheckAssign('Read'+field.type.Struct, ['ar'], [`self.${field.name}`]),
],
]
]), showlog && [StrStmt(`log.Println("skip", cc4)`)]),
CallCheckAssign('ReadDummy', ['ar', 'int(ar.N)'], ['_']),
])
];
var ReadCommnType = (name, type) => {
if (type.struct)
return CallCheckAssign(
'Read'+type.Struct, ['r'], [name]);
return [
//DebugStmt(type),
CallCheckAssign(
'Read'+type.fn, ['r', type.len].nonull(), [name]),
] ]
}; };
stmts = stmts.concat(opts.fields.map(field => { var ReadField = (name, type) => {
if (field.sizelen) { if (name == '_')
var arr = 'self.'+field.name; return CallCheckAssign('ReadDummy', ['r', type.len], ['_']);
return [ if (type.arr && type.fn != 'Bytes')
DeclVar('count', 'int'), return ReadArr('self.'+name, type);
CallCheckAssign('ReadInt', ['r', field.sizelen], ['count']), return ReadCommnType('self.'+name, type);
For(RangeN('i', 'count'), readElemStmts(field)), };
];
} else if (field.elem) {
var cond = field.count ? RangeN('i', field.count) : StrStmt('r.N > 0');
return For(cond, readElemStmts(field));
} else if (!field.hide) {
return CallCheckAssign('Read'+field.fn, ['r', field.len].nonull(), ['self.'+field.name]);
}
}).nonull());
if (opts.resIsPtr) var ReadFields = () => opts.fields.map(field => {
stmts = stmts.concat([StrStmt(`res = self`)]); var name = field.name;
var type = field.type;
return ReadField(name, type);
}).nonull();
var ptr = opts.cc4;
return Func( return Func(
'Read'+opts.fnName, 'Read'+opts.type,
[['r', '*io.LimitedReader']], [['r', '*io.LimitedReader']],
[[opts.resIsPtr?'res':'self', (opts.resIsPtr?'*':'')+opts.atomType], ['err', 'error']], [[ptr?'res':'self', (ptr?'*':'')+opts.type], ['err', 'error']],
stmts [
ptr && StrStmt(`self := &${opts.type}{}`),
!opts.atoms ? ReadFields() : ReadAtoms(),
ptr && StrStmt(`res = self`),
]
); );
}; };
@ -237,16 +312,21 @@ var D = (cls, ...fields) => {
}; };
D('Func', 'name', 'args', 'rets', 'body'); D('Func', 'name', 'args', 'rets', 'body');
D('CallCheckAssign', 'fn', 'args', 'rets'); D('CallCheckAssign', 'fn', 'args', 'rets', 'action');
D('DeclVar', 'name', 'type'); D('DeclVar', 'name', 'type');
D('For', 'cond', 'body'); D('For', 'cond', 'body');
D('RangeN', 'i', 'n'); D('RangeN', 'i', 'n');
D('DeclStruct', 'name', 'body'); D('DeclStruct', 'name', 'body');
D('StrStmt', 'content'); D('StrStmt', 'content');
D('Switch', 'cond', 'cases', 'default');
var showlog = false;
var S = s => s && s || '';
var dumpFn = f => { var dumpFn = f => {
var dumpArgs = x => x.map(x => x.join(' ')).join(','); var dumpArgs = x => x.map(x => x.join(' ')).join(',');
return `func ${f.name}(${dumpArgs(f.args)}) (${dumpArgs(f.rets)}) { return `func ${f.name}(${dumpArgs(f.args)}) (${dumpArgs(f.rets)}) {
${S(showlog && 'log.Println("'+f.name+'")')}
${dumpStmts(f.body)} ${dumpStmts(f.body)}
return return
}`; }`;
@ -258,7 +338,7 @@ var dumpStmts = stmts => {
return dumpStmts(stmt); return dumpStmts(stmt);
} if (stmt.cls == 'CallCheckAssign') { } if (stmt.cls == 'CallCheckAssign') {
return `if ${stmt.rets.concat(['err']).join(',')} = ${stmt.fn}(${stmt.args.join(',')}); err != nil { return `if ${stmt.rets.concat(['err']).join(',')} = ${stmt.fn}(${stmt.args.join(',')}); err != nil {
return ${stmt.action ? stmt.action : 'return'}
}`; }`;
} else if (stmt.cls == 'DeclVar') { } else if (stmt.cls == 'DeclVar') {
return `var ${stmt.name} ${stmt.type}`; return `var ${stmt.name} ${stmt.type}`;
@ -276,68 +356,103 @@ var dumpStmts = stmts => {
return dumpFn(stmt); return dumpFn(stmt);
} else if (stmt.cls == 'StrStmt') { } else if (stmt.cls == 'StrStmt') {
return stmt.content; return stmt.content;
} else if (stmt.cls == 'Switch') {
var dumpCase = c => `case ${c[0]}: { ${dumpStmts(c[1])} }`;
var dumpDefault = c => `default: { ${dumpStmts(c)} }`;
return `switch ${stmt.cond} {
${stmt.cases.map(dumpCase).join('\n')}
${stmt.default && dumpDefault(stmt.default) || ''}
}`;
} }
}; };
return stmts.map(dumpStmt).join('\n') return stmts.nonull().map(dumpStmt).join('\n')
}; };
(() => { var parseType = s => {
var len = 3; var r = {};
var f = Func('Readxx', [['f', '*io.LimitedReader']], [['res', '*xx'], ['err', 'error']], [ var bracket = /^\[(.*)\]/;
CallCheckAssign('ReadInt', ['f', len], ['self.xx']), if (s.match(bracket)) {
CallCheckAssign('WriteInt', ['f', len], ['self.xx']), var count = s.match(bracket)[1];
DeclVar('n', 'int'), if (count.substr(0,3) == 'int') {
For(RangeN('i', 'n'), [ r.varcount = +count.substr(3)/8;
CallCheckAssign('WriteInt', ['f', len], ['self.xx']), } else {
DeclStruct('hi', [['a', 'b'], ['c', 'd'], ['e', 'f']]), r.count = +count;
]), }
]); r.arr = true;
console.log(dumpFn(f)); s = s.replace(bracket, '');
}); }
if (s.substr(0,1) == '*') {
r.ptr = true;
s = s.slice(1);
}
var types = /^(int|TimeStamp|byte|cc)/;
if (s.match(types)) {
r.type = s.match(types)[0];
r.fn = uc(r.type);
s = s.replace(types, '');
}
if (r.type == 'byte' && r.arr) {
r.len = r.count;
r.fn = 'Bytes';
}
var lenDiv = 8;
if (r.type == 'cc') {
r.fn = 'String';
r.type = 'string';
lenDiv = 1;
}
var number = /[0-9]+/;
if (s.match(number)) {
r.len = +s.match(number)[0]/lenDiv;
s = s.replace(number, '');
}
if (s != '') {
r.struct = s;
r.Struct = uc(s);
}
return r;
};
var typeStr = (t) => {
var s = '';
if (t.arr)
s += '['+(t.count||'')+']';
if (t.ptr)
s += '*';
if (t.struct)
s += t.Struct;
if (t.type)
s += t.type;
return s;
};
var nameShouldHide = (name) => name == '_'
var allStmts = () => { var allStmts = () => {
var stmts = []; var stmts = [];
var convStructFields = fields => {
var typeStr = field => (
field.cls == 'AtomPtr' || field.cls == 'StructPtr') ? '*'+field.type : field.type;
return fields.filter(field => !field.hide)
.map(field => {
if (field.cls == 'Arr' || field.cls == 'LenArr')
return [field.name, '[]'+typeStr(field.elem)];
return [field.name, typeStr(field)];
});
};
for (var k in atoms) { for (var k in atoms) {
var list = atoms[k]; var atom = atoms[k];
var name = uc(k)+'Atom';
var cc4 = list[0];
var fields = list.slice(1);
stmts = stmts.concat([
DeclStruct(name, convStructFields(fields)),
genReadStmts({
cc4: cc4,
fields: fields,
fnName: name,
atomType: name,
resIsPtr: true,
}),
]);
}
for (var k in structs) {
var fields = structs[k];
var name = uc(k); var name = uc(k);
var fields = (atom.fields || atom.atoms).map(field => {
return {
name: uc(field[0]),
type: parseType(field[1]),
};
});
stmts = stmts.concat([ stmts = stmts.concat([
DeclStruct(name, convStructFields(fields)), DeclStruct(name, fields.map(field => !nameShouldHide(field.name) && [
genReadStmts({ uc(field.name),
typeStr(field.type),
]).nonull()),
DeclReadFunc({
type: name,
fields: fields, fields: fields,
fnName: name, cc4: atom.cc4,
atomType: name, atoms: atom.atoms != null,
}), }),
]); ]);
} }
@ -345,10 +460,12 @@ var allStmts = () => {
return stmts; return stmts;
}; };
console.log(`// THIS FILE IS AUTO GENERATED console.log(`
// THIS FILE IS AUTO GENERATED
package atom package atom
import ( import (
"io" "io"
${showlog && '"log"' || ''}
) )
`, dumpStmts(allStmts())); `, dumpStmts(allStmts()));

View File

@ -4,6 +4,7 @@ package atom
import ( import (
"io" "io"
"io/ioutil" "io/ioutil"
"log"
) )
type Fixed32 uint32 type Fixed32 uint32
@ -17,10 +18,6 @@ func ReadBytes(r io.Reader, n int) (res []byte, err error) {
return return
} }
func ReadBytesLeft(r *io.LimitedReader) (res []byte, err error) {
return ReadBytes(r, int(r.N))
}
func ReadUInt(r io.Reader, n int) (res uint, err error) { func ReadUInt(r io.Reader, n int) (res uint, err error) {
var b []byte var b []byte
if b, err = ReadBytes(r, n); err != nil { if b, err = ReadBytes(r, n); err != nil {
@ -74,42 +71,38 @@ func ReadDummy(r io.Reader, n int) (res int, err error) {
return return
} }
/* func ReadAtomHeader(r io.Reader, targetCC4 string) (res *io.LimitedReader, cc4 string, err error) {
func (self Reader) ReadAtom(atom Atom) (res Atom, err error) {
for { for {
var size int var size int
if size, err = self.ReadInt(4); err != nil { if size, err = ReadInt(r, 4); err != nil {
return return
} }
if size == 0 { if size == 0 {
continue continue
} }
var cc4 string if cc4, err = ReadString(r, 4); err != nil {
if cc4, err = self.ReadString(4); err != nil {
return return
} }
if atom.CC4() != cc4 { size = size - 8
if err = self.Skip(size); err != nil {
if false {
log.Println(cc4, targetCC4, size, cc4 == targetCC4)
}
if targetCC4 != "" && cc4 != targetCC4 {
log.Println("ReadAtomHeader skip:", cc4)
if _, err = ReadDummy(r, size); err != nil {
return return
} }
continue continue
} }
reader := &io.LimitedReader{ res = &io.LimitedReader{
R: self.Reader, R: r,
N: int64(size - 8), N: int64(size),
} }
if err = atom.Read(Reader{reader}); err != nil {
return
}
if err = self.Skip(int(reader.N)); err != nil {
return
}
res = atom
return return
} }
} }
*/

View File

@ -5,50 +5,67 @@ import (
"io" "io"
) )
type FileTypeAtom struct { type FileType struct {
Movie *MovieAtom
} }
func ReadFileTypeAtom(r *io.LimitedReader) (res *FileTypeAtom, err error) { func ReadFileType(r *io.LimitedReader) (res *FileType, err error) {
self := &FileTypeAtom{}
if self.Movie, err = ReadMovieAtom(r); err != nil { self := &FileType{}
return
}
res = self res = self
return return
} }
type MovieAtom struct { type Movie struct {
Header *MovieHeaderAtom Header *MovieHeader
Tracks []*TrackAtom Tracks []*Track
} }
func ReadMovieAtom(r *io.LimitedReader) (res *MovieAtom, err error) { func ReadMovie(r *io.LimitedReader) (res *Movie, err error) {
self := &MovieAtom{}
if self.Header, err = ReadMovieHeaderAtom(r); err != nil { self := &Movie{}
return // ReadAtoms
}
for r.N > 0 { for r.N > 0 {
var item *TrackAtom var cc4 string
if item, err = ReadTrackAtom(r); err != nil { var ar *io.LimitedReader
if ar, cc4, err = ReadAtomHeader(r, ""); err != nil {
return
}
switch cc4 {
case "mvhd":
{
if self.Header, err = ReadMovieHeader(ar); err != nil {
return
}
}
case "trak":
{
var item *Track
if item, err = ReadTrack(ar); err != nil {
return
}
self.Tracks = append(self.Tracks, item)
}
}
if _, err = ReadDummy(ar, int(ar.N)); err != nil {
return return
} }
self.Tracks = append(self.Tracks, item)
} }
res = self res = self
return return
} }
type MovieHeaderAtom struct { type MovieHeader struct {
Version int Version int
Flags int Flags int
CTime TimeStamp CTime TimeStamp
MTime TimeStamp MTime TimeStamp
TimeScale int TimeScale TimeStamp
Duration int Duration TimeStamp
PreferredRate int PreferredRate int
PreferredVolume int PreferredVolume int
Matrix []byte Matrix [9]int
PreviewTime TimeStamp PreviewTime TimeStamp
PreviewDuration TimeStamp PreviewDuration TimeStamp
PosterTime TimeStamp PosterTime TimeStamp
@ -58,8 +75,9 @@ type MovieHeaderAtom struct {
NextTrackId int NextTrackId int
} }
func ReadMovieHeaderAtom(r *io.LimitedReader) (res *MovieHeaderAtom, err error) { func ReadMovieHeader(r *io.LimitedReader) (res *MovieHeader, err error) {
self := &MovieHeaderAtom{}
self := &MovieHeader{}
if self.Version, err = ReadInt(r, 1); err != nil { if self.Version, err = ReadInt(r, 1); err != nil {
return return
} }
@ -72,10 +90,10 @@ func ReadMovieHeaderAtom(r *io.LimitedReader) (res *MovieHeaderAtom, err error)
if self.MTime, err = ReadTimeStamp(r, 4); err != nil { if self.MTime, err = ReadTimeStamp(r, 4); err != nil {
return return
} }
if self.TimeScale, err = ReadInt(r, 4); err != nil { if self.TimeScale, err = ReadTimeStamp(r, 4); err != nil {
return return
} }
if self.Duration, err = ReadInt(r, 4); err != nil { if self.Duration, err = ReadTimeStamp(r, 4); err != nil {
return return
} }
if self.PreferredRate, err = ReadInt(r, 4); err != nil { if self.PreferredRate, err = ReadInt(r, 4); err != nil {
@ -84,9 +102,14 @@ func ReadMovieHeaderAtom(r *io.LimitedReader) (res *MovieHeaderAtom, err error)
if self.PreferredVolume, err = ReadInt(r, 2); err != nil { if self.PreferredVolume, err = ReadInt(r, 2); err != nil {
return return
} }
if self.Matrix, err = ReadBytes(r, 36); err != nil { if _, err = ReadDummy(r, 10); err != nil {
return return
} }
for i := 0; i < 9; i++ {
if self.Matrix[i], err = ReadInt(r, 4); err != nil {
return
}
}
if self.PreviewTime, err = ReadTimeStamp(r, 4); err != nil { if self.PreviewTime, err = ReadTimeStamp(r, 4); err != nil {
return return
} }
@ -112,40 +135,62 @@ func ReadMovieHeaderAtom(r *io.LimitedReader) (res *MovieHeaderAtom, err error)
return return
} }
type TrackAtom struct { type Track struct {
Header *TrackHeaderAtom Header *TrackHeader
Media *MediaAtom Media *Media
} }
func ReadTrackAtom(r *io.LimitedReader) (res *TrackAtom, err error) { func ReadTrack(r *io.LimitedReader) (res *Track, err error) {
self := &TrackAtom{}
if self.Header, err = ReadTrackHeaderAtom(r); err != nil { self := &Track{}
return // ReadAtoms
} for r.N > 0 {
if self.Media, err = ReadMediaAtom(r); err != nil { var cc4 string
return var ar *io.LimitedReader
if ar, cc4, err = ReadAtomHeader(r, ""); err != nil {
return
}
switch cc4 {
case "tkhd":
{
if self.Header, err = ReadTrackHeader(ar); err != nil {
return
}
}
case "mdia":
{
if self.Media, err = ReadMedia(ar); err != nil {
return
}
}
}
if _, err = ReadDummy(ar, int(ar.N)); err != nil {
return
}
} }
res = self res = self
return return
} }
type TrackHeaderAtom struct { type TrackHeader struct {
Version int Version int
Flags int Flags int
CTime TimeStamp CTime TimeStamp
MTime TimeStamp MTime TimeStamp
TrackId int TrackId TimeStamp
Duration int Duration TimeStamp
Layer int Layer int
AlternateGroup int AlternateGroup int
Volume int Volume int
Matrix []byte Matrix [9]int
TrackWidth Fixed32 TrackWidth int
TrackHeight Fixed32 TrackHeader int
} }
func ReadTrackHeaderAtom(r *io.LimitedReader) (res *TrackHeaderAtom, err error) { func ReadTrackHeader(r *io.LimitedReader) (res *TrackHeader, err error) {
self := &TrackHeaderAtom{}
self := &TrackHeader{}
if self.Version, err = ReadInt(r, 1); err != nil { if self.Version, err = ReadInt(r, 1); err != nil {
return return
} }
@ -158,10 +203,16 @@ func ReadTrackHeaderAtom(r *io.LimitedReader) (res *TrackHeaderAtom, err error)
if self.MTime, err = ReadTimeStamp(r, 4); err != nil { if self.MTime, err = ReadTimeStamp(r, 4); err != nil {
return return
} }
if self.TrackId, err = ReadInt(r, 4); err != nil { if self.TrackId, err = ReadTimeStamp(r, 4); err != nil {
return return
} }
if self.Duration, err = ReadInt(r, 4); err != nil { if _, err = ReadDummy(r, 4); err != nil {
return
}
if self.Duration, err = ReadTimeStamp(r, 4); err != nil {
return
}
if _, err = ReadDummy(r, 8); err != nil {
return return
} }
if self.Layer, err = ReadInt(r, 2); err != nil { if self.Layer, err = ReadInt(r, 2); err != nil {
@ -173,59 +224,86 @@ func ReadTrackHeaderAtom(r *io.LimitedReader) (res *TrackHeaderAtom, err error)
if self.Volume, err = ReadInt(r, 2); err != nil { if self.Volume, err = ReadInt(r, 2); err != nil {
return return
} }
if self.Matrix, err = ReadBytes(r, 36); err != nil { if _, err = ReadDummy(r, 2); err != nil {
return return
} }
if self.TrackWidth, err = ReadFixed32(r, 4); err != nil { for i := 0; i < 9; i++ {
if self.Matrix[i], err = ReadInt(r, 4); err != nil {
return
}
}
if self.TrackWidth, err = ReadInt(r, 4); err != nil {
return return
} }
if self.TrackHeight, err = ReadFixed32(r, 4); err != nil { if self.TrackHeader, err = ReadInt(r, 4); err != nil {
return return
} }
res = self res = self
return return
} }
type MediaAtom struct { type Media struct {
Header *MediaHeaderAtom Header *MediaHeader
Info *MediaInfoAtom Info *MediaInfo
} }
func ReadMediaAtom(r *io.LimitedReader) (res *MediaAtom, err error) { func ReadMedia(r *io.LimitedReader) (res *Media, err error) {
self := &MediaAtom{}
if self.Header, err = ReadMediaHeaderAtom(r); err != nil { self := &Media{}
return // ReadAtoms
} for r.N > 0 {
if self.Info, err = ReadMediaInfoAtom(r); err != nil { var cc4 string
return var ar *io.LimitedReader
if ar, cc4, err = ReadAtomHeader(r, ""); err != nil {
return
}
switch cc4 {
case "mdhd":
{
if self.Header, err = ReadMediaHeader(ar); err != nil {
return
}
}
case "minf":
{
if self.Info, err = ReadMediaInfo(ar); err != nil {
return
}
}
}
if _, err = ReadDummy(ar, int(ar.N)); err != nil {
return
}
} }
res = self res = self
return return
} }
type MediaHeaderAtom struct { type MediaHeader struct {
Version int Version int
Flags int Flags int
CTime TimeStamp CTime int
MTime TimeStamp MTime int
TimeScale int TimeScale int
Duration int Duration int
Language int Language int
Quality int Quality int
} }
func ReadMediaHeaderAtom(r *io.LimitedReader) (res *MediaHeaderAtom, err error) { func ReadMediaHeader(r *io.LimitedReader) (res *MediaHeader, err error) {
self := &MediaHeaderAtom{}
self := &MediaHeader{}
if self.Version, err = ReadInt(r, 1); err != nil { if self.Version, err = ReadInt(r, 1); err != nil {
return return
} }
if self.Flags, err = ReadInt(r, 3); err != nil { if self.Flags, err = ReadInt(r, 3); err != nil {
return return
} }
if self.CTime, err = ReadTimeStamp(r, 4); err != nil { if self.CTime, err = ReadInt(r, 4); err != nil {
return return
} }
if self.MTime, err = ReadTimeStamp(r, 4); err != nil { if self.MTime, err = ReadInt(r, 4); err != nil {
return return
} }
if self.TimeScale, err = ReadInt(r, 4); err != nil { if self.TimeScale, err = ReadInt(r, 4); err != nil {
@ -244,32 +322,86 @@ func ReadMediaHeaderAtom(r *io.LimitedReader) (res *MediaHeaderAtom, err error)
return return
} }
type MediaInfoAtom struct { type MediaInfo struct {
Video *VideoMediaInfoAtom Sound *SoundMediaInfo
Sample *SampleTableAtom Video *VideoMediaInfo
Sample *SampleTable
} }
func ReadMediaInfoAtom(r *io.LimitedReader) (res *MediaInfoAtom, err error) { func ReadMediaInfo(r *io.LimitedReader) (res *MediaInfo, err error) {
self := &MediaInfoAtom{}
if self.Video, err = ReadVideoMediaInfoAtom(r); err != nil { self := &MediaInfo{}
// ReadAtoms
for r.N > 0 {
var cc4 string
var ar *io.LimitedReader
if ar, cc4, err = ReadAtomHeader(r, ""); err != nil {
return
}
switch cc4 {
case "smhd":
{
if self.Sound, err = ReadSoundMediaInfo(ar); err != nil {
return
}
}
case "vmhd":
{
if self.Video, err = ReadVideoMediaInfo(ar); err != nil {
return
}
}
case "stbl":
{
if self.Sample, err = ReadSampleTable(ar); err != nil {
return
}
}
}
if _, err = ReadDummy(ar, int(ar.N)); err != nil {
return
}
}
res = self
return
}
type SoundMediaInfo struct {
Version int
Flags int
Balance int
}
func ReadSoundMediaInfo(r *io.LimitedReader) (res *SoundMediaInfo, err error) {
self := &SoundMediaInfo{}
if self.Version, err = ReadInt(r, 1); err != nil {
return return
} }
if self.Sample, err = ReadSampleTableAtom(r); err != nil { if self.Flags, err = ReadInt(r, 3); err != nil {
return
}
if self.Balance, err = ReadInt(r, 2); err != nil {
return
}
if _, err = ReadDummy(r, 2); err != nil {
return return
} }
res = self res = self
return return
} }
type VideoMediaInfoAtom struct { type VideoMediaInfo struct {
Version int Version int
Flags int Flags int
GraphicsMode int GraphicsMode int
Opcolor []int Opcolor [3]int
} }
func ReadVideoMediaInfoAtom(r *io.LimitedReader) (res *VideoMediaInfoAtom, err error) { func ReadVideoMediaInfo(r *io.LimitedReader) (res *VideoMediaInfo, err error) {
self := &VideoMediaInfoAtom{}
self := &VideoMediaInfo{}
if self.Version, err = ReadInt(r, 1); err != nil { if self.Version, err = ReadInt(r, 1); err != nil {
return return
} }
@ -280,57 +412,96 @@ func ReadVideoMediaInfoAtom(r *io.LimitedReader) (res *VideoMediaInfoAtom, err e
return return
} }
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
var item int if self.Opcolor[i], err = ReadInt(r, 2); err != nil {
if item, err = ReadInt(r, 2); err != nil {
return return
} }
self.Opcolor = append(self.Opcolor, item)
} }
res = self res = self
return return
} }
type SampleTableAtom struct { type SampleTable struct {
SampleDesc *SampleDescAtom SampleDesc *SampleDesc
TimeToSample *TimeToSampleAtom TimeToSample *TimeToSample
CompositionOffset *CompositionOffsetAtom CompositionOffset *CompositionOffset
SyncSample *SyncSampleAtom SampleToChunk *SampleToChunk
SampleSize *SampleSizeAtom SyncSample *SyncSample
ChunkOffset *ChunkOffsetAtom ChunkOffset *ChunkOffset
SampleSize *SampleSize
} }
func ReadSampleTableAtom(r *io.LimitedReader) (res *SampleTableAtom, err error) { func ReadSampleTable(r *io.LimitedReader) (res *SampleTable, err error) {
self := &SampleTableAtom{}
if self.SampleDesc, err = ReadSampleDescAtom(r); err != nil { self := &SampleTable{}
return // ReadAtoms
} for r.N > 0 {
if self.TimeToSample, err = ReadTimeToSampleAtom(r); err != nil { var cc4 string
return var ar *io.LimitedReader
} if ar, cc4, err = ReadAtomHeader(r, ""); err != nil {
if self.CompositionOffset, err = ReadCompositionOffsetAtom(r); err != nil { return
return }
} switch cc4 {
if self.SyncSample, err = ReadSyncSampleAtom(r); err != nil { case "stsd":
return {
} if self.SampleDesc, err = ReadSampleDesc(ar); err != nil {
if self.SampleSize, err = ReadSampleSizeAtom(r); err != nil { return
return }
} }
if self.ChunkOffset, err = ReadChunkOffsetAtom(r); err != nil { case "stts":
return {
if self.TimeToSample, err = ReadTimeToSample(ar); err != nil {
return
}
}
case "ctts":
{
if self.CompositionOffset, err = ReadCompositionOffset(ar); err != nil {
return
}
}
case "stsc":
{
if self.SampleToChunk, err = ReadSampleToChunk(ar); err != nil {
return
}
}
case "stss":
{
if self.SyncSample, err = ReadSyncSample(ar); err != nil {
return
}
}
case "stco":
{
if self.ChunkOffset, err = ReadChunkOffset(ar); err != nil {
return
}
}
case "stsz":
{
if self.SampleSize, err = ReadSampleSize(ar); err != nil {
return
}
}
}
if _, err = ReadDummy(ar, int(ar.N)); err != nil {
return
}
} }
res = self res = self
return return
} }
type SampleDescAtom struct { type SampleDesc struct {
Version int Version int
Flags int Flags int
Entries []SampleDescEntry Entries []*SampleDescEntry
} }
func ReadSampleDescAtom(r *io.LimitedReader) (res *SampleDescAtom, err error) { func ReadSampleDesc(r *io.LimitedReader) (res *SampleDesc, err error) {
self := &SampleDescAtom{}
self := &SampleDesc{}
if self.Version, err = ReadInt(r, 1); err != nil { if self.Version, err = ReadInt(r, 1); err != nil {
return return
} }
@ -341,25 +512,25 @@ func ReadSampleDescAtom(r *io.LimitedReader) (res *SampleDescAtom, err error) {
if count, err = ReadInt(r, 4); err != nil { if count, err = ReadInt(r, 4); err != nil {
return return
} }
self.Entries = make([]*SampleDescEntry, count)
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
var item SampleDescEntry if self.Entries[i], err = ReadSampleDescEntry(r); err != nil {
if item, err = ReadSampleDescEntry(r); err != nil {
return return
} }
self.Entries = append(self.Entries, item)
} }
res = self res = self
return return
} }
type TimeToSampleAtom struct { type TimeToSample struct {
Version int Version int
Flags int Flags int
Entries []TimeToSampleEntry Entries []TimeToSampleEntry
} }
func ReadTimeToSampleAtom(r *io.LimitedReader) (res *TimeToSampleAtom, err error) { func ReadTimeToSample(r *io.LimitedReader) (res *TimeToSample, err error) {
self := &TimeToSampleAtom{}
self := &TimeToSample{}
if self.Version, err = ReadInt(r, 1); err != nil { if self.Version, err = ReadInt(r, 1); err != nil {
return return
} }
@ -370,158 +541,23 @@ func ReadTimeToSampleAtom(r *io.LimitedReader) (res *TimeToSampleAtom, err error
if count, err = ReadInt(r, 4); err != nil { if count, err = ReadInt(r, 4); err != nil {
return return
} }
self.Entries = make([]TimeToSampleEntry, count)
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
var item TimeToSampleEntry if self.Entries[i], err = ReadTimeToSampleEntry(r); err != nil {
if item, err = ReadTimeToSampleEntry(r); err != nil {
return return
} }
self.Entries = append(self.Entries, item)
} }
res = self res = self
return return
} }
type CompositionOffsetAtom struct {
Version int
Flags int
Entries []CompositionOffsetEntry
}
func ReadCompositionOffsetAtom(r *io.LimitedReader) (res *CompositionOffsetAtom, err error) {
self := &CompositionOffsetAtom{}
if self.Version, err = ReadInt(r, 1); err != nil {
return
}
if self.Flags, err = ReadInt(r, 3); err != nil {
return
}
var count int
if count, err = ReadInt(r, 4); err != nil {
return
}
for i := 0; i < count; i++ {
var item CompositionOffsetEntry
if item, err = ReadCompositionOffsetEntry(r); err != nil {
return
}
self.Entries = append(self.Entries, item)
}
res = self
return
}
type SyncSampleAtom struct {
Version int
Flags int
Entries []int
}
func ReadSyncSampleAtom(r *io.LimitedReader) (res *SyncSampleAtom, err error) {
self := &SyncSampleAtom{}
if self.Version, err = ReadInt(r, 1); err != nil {
return
}
if self.Flags, err = ReadInt(r, 3); err != nil {
return
}
var count int
if count, err = ReadInt(r, 4); err != nil {
return
}
for i := 0; i < count; i++ {
var item int
if item, err = ReadInt(r, 4); err != nil {
return
}
self.Entries = append(self.Entries, item)
}
res = self
return
}
type SampleSizeAtom struct {
Version int
Flags int
Entries []int
}
func ReadSampleSizeAtom(r *io.LimitedReader) (res *SampleSizeAtom, err error) {
self := &SampleSizeAtom{}
if self.Version, err = ReadInt(r, 1); err != nil {
return
}
if self.Flags, err = ReadInt(r, 3); err != nil {
return
}
var count int
if count, err = ReadInt(r, 4); err != nil {
return
}
for i := 0; i < count; i++ {
var item int
if item, err = ReadInt(r, 4); err != nil {
return
}
self.Entries = append(self.Entries, item)
}
res = self
return
}
type ChunkOffsetAtom struct {
Version int
Flags int
Entries []int
}
func ReadChunkOffsetAtom(r *io.LimitedReader) (res *ChunkOffsetAtom, err error) {
self := &ChunkOffsetAtom{}
if self.Version, err = ReadInt(r, 1); err != nil {
return
}
if self.Flags, err = ReadInt(r, 3); err != nil {
return
}
var count int
if count, err = ReadInt(r, 4); err != nil {
return
}
for i := 0; i < count; i++ {
var item int
if item, err = ReadInt(r, 4); err != nil {
return
}
self.Entries = append(self.Entries, item)
}
res = self
return
}
type SampleDescEntry struct {
Format string
DataRefIdx int
Data []byte
}
func ReadSampleDescEntry(r *io.LimitedReader) (self SampleDescEntry, err error) {
if self.Format, err = ReadString(r, 4); err != nil {
return
}
if self.DataRefIdx, err = ReadInt(r, 2); err != nil {
return
}
if self.Data, err = ReadBytesLeft(r); err != nil {
return
}
return
}
type TimeToSampleEntry struct { type TimeToSampleEntry struct {
Count int Count int
Duration int Duration int
} }
func ReadTimeToSampleEntry(r *io.LimitedReader) (self TimeToSampleEntry, err error) { func ReadTimeToSampleEntry(r *io.LimitedReader) (self TimeToSampleEntry, err error) {
if self.Count, err = ReadInt(r, 4); err != nil { if self.Count, err = ReadInt(r, 4); err != nil {
return return
} }
@ -531,12 +567,91 @@ func ReadTimeToSampleEntry(r *io.LimitedReader) (self TimeToSampleEntry, err err
return return
} }
type SampleToChunk struct {
Version int
Flags int
Entries []SampleToChunkEntry
}
func ReadSampleToChunk(r *io.LimitedReader) (res *SampleToChunk, err error) {
self := &SampleToChunk{}
if self.Version, err = ReadInt(r, 1); err != nil {
return
}
if self.Flags, err = ReadInt(r, 3); err != nil {
return
}
var count int
if count, err = ReadInt(r, 4); err != nil {
return
}
self.Entries = make([]SampleToChunkEntry, count)
for i := 0; i < count; i++ {
if self.Entries[i], err = ReadSampleToChunkEntry(r); err != nil {
return
}
}
res = self
return
}
type SampleToChunkEntry struct {
FirstChunk int
SamplesPerChunk int
SampleDescId int
}
func ReadSampleToChunkEntry(r *io.LimitedReader) (self SampleToChunkEntry, err error) {
if self.FirstChunk, err = ReadInt(r, 4); err != nil {
return
}
if self.SamplesPerChunk, err = ReadInt(r, 4); err != nil {
return
}
if self.SampleDescId, err = ReadInt(r, 4); err != nil {
return
}
return
}
type CompositionOffset struct {
Version int
Flags int
Entries []int
}
func ReadCompositionOffset(r *io.LimitedReader) (res *CompositionOffset, err error) {
self := &CompositionOffset{}
if self.Version, err = ReadInt(r, 1); err != nil {
return
}
if self.Flags, err = ReadInt(r, 3); err != nil {
return
}
var count int
if count, err = ReadInt(r, 4); err != nil {
return
}
self.Entries = make([]int, count)
for i := 0; i < count; i++ {
if self.Entries[i], err = ReadInt(r, 4); err != nil {
return
}
}
res = self
return
}
type CompositionOffsetEntry struct { type CompositionOffsetEntry struct {
Count int Count int
Offset int Offset int
} }
func ReadCompositionOffsetEntry(r *io.LimitedReader) (self CompositionOffsetEntry, err error) { func ReadCompositionOffsetEntry(r *io.LimitedReader) (self CompositionOffsetEntry, err error) {
if self.Count, err = ReadInt(r, 4); err != nil { if self.Count, err = ReadInt(r, 4); err != nil {
return return
} }
@ -545,3 +660,90 @@ func ReadCompositionOffsetEntry(r *io.LimitedReader) (self CompositionOffsetEntr
} }
return return
} }
type SyncSample struct {
Version int
Flags int
Entries []int
}
func ReadSyncSample(r *io.LimitedReader) (res *SyncSample, err error) {
self := &SyncSample{}
if self.Version, err = ReadInt(r, 1); err != nil {
return
}
if self.Flags, err = ReadInt(r, 3); err != nil {
return
}
var count int
if count, err = ReadInt(r, 4); err != nil {
return
}
self.Entries = make([]int, count)
for i := 0; i < count; i++ {
if self.Entries[i], err = ReadInt(r, 4); err != nil {
return
}
}
res = self
return
}
type SampleSize struct {
Version int
Flags int
Entries []int
}
func ReadSampleSize(r *io.LimitedReader) (res *SampleSize, err error) {
self := &SampleSize{}
if self.Version, err = ReadInt(r, 1); err != nil {
return
}
if self.Flags, err = ReadInt(r, 3); err != nil {
return
}
var count int
if count, err = ReadInt(r, 4); err != nil {
return
}
self.Entries = make([]int, count)
for i := 0; i < count; i++ {
if self.Entries[i], err = ReadInt(r, 4); err != nil {
return
}
}
res = self
return
}
type ChunkOffset struct {
Version int
Flags int
Entries []int
}
func ReadChunkOffset(r *io.LimitedReader) (res *ChunkOffset, err error) {
self := &ChunkOffset{}
if self.Version, err = ReadInt(r, 1); err != nil {
return
}
if self.Flags, err = ReadInt(r, 3); err != nil {
return
}
var count int
if count, err = ReadInt(r, 4); err != nil {
return
}
self.Entries = make([]int, count)
for i := 0; i < count; i++ {
if self.Entries[i], err = ReadInt(r, 4); err != nil {
return
}
}
res = self
return
}

15
example/read.go Normal file
View File

@ -0,0 +1,15 @@
package main
import (
mp4 "./.."
"log"
)
func main() {
if _, err := mp4.Open("tiny2-avconv.mp4"); err != nil {
log.Println(err)
return
}
}

35
mp4.go
View File

@ -4,11 +4,11 @@ package mp4
import ( import (
"./atom" "./atom"
"os" "os"
"io"
"log"
) )
type File struct { type File struct {
moov *atom.Moov
ftyp *atom.Ftyp
} }
func (self *File) AddAvcc(avcc *Avcc) { func (self *File) AddAvcc(avcc *Avcc) {
@ -37,19 +37,34 @@ func Open(filename string) (file *File, err error) {
return return
} }
var entry atom.Atom var finfo os.FileInfo
file = &File{} if finfo, err = osfile.Stat(); err != nil {
r := atom.Reader{osfile}
if entry, err = r.ReadAtom(&atom.Ftyp{}); err != nil {
return return
} }
file.ftyp = entry.(*atom.Ftyp) log.Println("filesize", finfo.Size())
if entry, err = r.ReadAtom(&atom.Moov{}); err != nil { 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))
}
if _, err = os.Create(filename+".out.mp4"); err != nil {
return return
} }
file.moov = entry.(*atom.Moov)
return return
} }