ref: d314994309c751e5a983797760e8386d82ae60eb
dir: /extra/mkvdump.c/
#include "../ebml.c"
#define min(a,b) ((a)<=(b)?(a):(b))
static vlong stack[128];
static void
usage(void)
{
fprint(2, "usage: %s [-d] [-h hxsz] [file.mkv]\n", argv0);
exits("usage");
}
void
main(int argc, char **argv)
{
int n, sti, i, hxsz, lacing, fd, debug;
vlong sz, off, x;
uchar *b, t[3];
Biobuf in, out;
Elspec s;
double d;
Tm tm;
debug = 0;
hxsz = 32;
ARGBEGIN{
case 'd':
debug++;
break;
case 'h':
if((hxsz = atoi(EARGF(usage()))) > 0)
break;
default:
usage();
}ARGEND
fd = 0;
if(argc == 1){
if((fd = open(*argv, OREAD)) < 0)
sysfatal("%r");
}else if(argc != 0)
usage();
if((b = malloc(hxsz)) == nil)
sysfatal("memory");
quotefmtinstall();
fmtinstall('H', encodefmt);
tmfmtinstall();
Binit(&in, fd, OREAD);
Binit(&out, 1, OWRITE);
sti = 0;
for(;;){
off = Boffset(&in);
if((n = ebmlel(&in, 0x7fffffffffffffffLL, &s, &sz)) < 0){
werrstr("invalid ebml: %r at %#llx (size %lld)", off, sz);
goto err;
}
if(s.type < 0)
continue;
if(n == 0) /* eof */
break;
while(sti > 0 && off >= stack[sti-1])
sti--;
if(s.name){
for(i = 0; i < sti; i++)
Bputc(&out, '\t');
Bprint(&out, "%s ", s.name);
if(debug)
Bprint(&out, " (id %#llx) ", s.id);
if(s.type == Emaster && sti < nelem(stack))
stack[sti++] = off+n+sz;
}else{
Bprint(&out, "%#llx ", s.id);
}
if(debug)
Bprint(&out, "(%lld bytes) ", sz);
switch(s.type){
case Ebinary:
if(s.id == ESimpleBlock || s.id == EBlock){
if((n = ebmluint(&in, sz, &x)) < 0){
werrstr("block: %r");
goto err;
}
sz -= n;
if(Bread(&in, t, 3) != 3){
werrstr("block: %r");
goto err;
}
sz -= 3;
lacing = (t[2] >> 1) & 3;
Bprint(&out, "(track %lld, timecode %d) ", x, (s16int)(t[0]<<8 | t[1]));
if(s.id == ESimpleBlock){
if(t[2] & 0x80)
Bprint(&out, "(key) ");
if(t[2] & 0x01)
Bprint(&out, "(discard) ");
}
if(t[2] & 0x08)
Bprint(&out, "(hidden) ");
if(lacing != 0)
Bprint(&out, "(lacing %d) ", lacing);
}
n = min(hxsz, sz);
if(Bread(&in, b, n) != n){
werrstr("binary: %r");
goto err;
}
Bprint(&out, "[%.*H%s] (%lld bytes)", n, b, sz > n ? "..." : "", sz);
sz -= n;
break;
case Efloat:
if(ebmlfloat(&in, sz, &d) < 0)
goto err;
Bprint(&out, "%g", d);
sz = 0;
break;
case Eunsigned:
if(ebmlrawuint(&in, sz, &x) < 0)
goto err;
Bprint(&out, "%llud", (uvlong)x);
sz = 0;
break;
case Esigned:
if(ebmlrawsint(&in, sz, &x) < 0)
goto err;
Bprint(&out, "%lld", x);
sz = 0;
break;
case Eunicode:
case Eascii:
n = min(hxsz, sz);
if(Bread(&in, b, n) != n){
werrstr("string: %r");
goto err;
}
Bprint(&out, "%#.*q%s", n, (char*)b, sz > n ? "..." : "");
sz -= n;
break;
case Etimestamp:
if(ebmlrawsint(&in, sz, &x) < 0)
goto err;
x /= 1000000000LL;
x += 978307200LL;
Bprint(&out, "%τ", tmfmt(tmtime(&tm, x, nil), nil));
sz = 0;
break;
case Emaster:
break;
default:
Bprint(&out, "???");
}
if(s.id == ETrackType)
Bprint(&out, " (%s)", ebmltracktype(x));
if(Bputc(&out, '\n') < 0)
break;
if(s.type != Emaster && sz > 0 && Bseek(&in, sz, 1) < 0)
goto err;
}
Bterm(&out);
Bterm(&in);
exits(nil);
err:
fprint(2, "%s: %r\n", fd == 0 ? "stdin" : *argv);
exits("error");
}