| @@ -4,6 +4,7 @@ · 3 unmodified lines |
| 4 | 4 | |
| 5 | 5 | typedef struct Audio Audio; |
| 6 | 6 | typedef struct Box Box; |
| 7 | +typedef struct Moof Moof; |
| 7 | 8 | typedef struct RunSample RunSample; |
| 8 | 9 | typedef struct SampleToChunk SampleToChunk; |
| 9 | 10 | typedef struct Track Track; |
| @@ -139,6 +140,11 @@ struct Box { · 129 unmodified lines |
| 139 | 140 | u32int handlertype; |
| 140 | 141 | char *name; |
| 141 | 142 | }hdlr; |
| 143 | + |
| 144 | + struct { |
| 145 | + int id; |
| 146 | + int flags; |
| 147 | + }esds; |
| 142 | 148 | }; |
| 143 | 149 | }; |
| 144 | 150 | |
| @@ -147,6 +153,7 @@ struct RunSample { · 2 unmodified lines |
| 147 | 153 | u32int size; |
| 148 | 154 | u32int flags; |
| 149 | 155 | vlong timeoffset; |
| 156 | + vlong offset; |
| 150 | 157 | }; |
| 151 | 158 | |
| 152 | 159 | struct SampleToChunk { |
| @@ -155,30 +162,12 @@ struct SampleToChunk { · 2 unmodified lines |
| 155 | 162 | u32int sdt; |
| 156 | 163 | }; |
| 157 | 164 | |
| 158 | | −struct Track { |
| 159 | | − u32int handlertype; |
| 160 | | − |
| 161 | | − u64int *chunkoffset; |
| 162 | | − u32int numchunks; |
| 163 | | − |
| 164 | | − u32int samplesizeeach; |
| 165 | | − u32int *samplesize; |
| 166 | | − u32int numsamples; |
| 167 | | − |
| 168 | | − SampleToChunk *stc; |
| 169 | | − u32int numstc; |
| 170 | | − |
| 171 | | − Audio audio; |
| 172 | | − Video video; |
| 173 | | − |
| 174 | | − int id; |
| 175 | | −}; |
| 176 | | − |
| 177 | 165 | enum { |
| 178 | 166 | HandlerVideo = 0x76696465u, |
| 179 | 167 | HandlerAudio = 0x736f756eu, |
| 180 | 168 | |
| 181 | 169 | FmtMp4a = 0x6d703461u, |
| 170 | + FmtAv01 = 0x61763031u, |
| 182 | 171 | |
| 183 | 172 | BoxUuid = 0x75756964u, |
| 184 | 173 | BoxFtyp = 0x66747970u, |
| @@ -230,6 +219,36 @@ enum { · 45 unmodified lines |
| 230 | 219 | |
| 231 | 220 | #define eBread(sz, e) if(Bread(f, d, (sz)) != (sz)){ werrstr(e); goto err; } |
| 232 | 221 | |
| 222 | +struct Moof { |
| 223 | + Box; |
| 224 | + Box mfhd; |
| 225 | + Box tfhd; |
| 226 | + Box trun; |
| 227 | +}; |
| 228 | + |
| 229 | +struct Track { |
| 230 | + u32int handlertype; |
| 231 | + u32int timescale; |
| 232 | + |
| 233 | + u64int *chunkoffset; |
| 234 | + u32int numchunks; |
| 235 | + |
| 236 | + u32int samplesizeeach; |
| 237 | + u32int *samplesize; |
| 238 | + u32int numsamples; |
| 239 | + |
| 240 | + SampleToChunk *stc; |
| 241 | + u32int numstc; |
| 242 | + |
| 243 | + Moof *moof; |
| 244 | + int nmoof; |
| 245 | + |
| 246 | + Audio audio; |
| 247 | + Video video; |
| 248 | + |
| 249 | + int id; |
| 250 | +}; |
| 251 | + |
| 233 | 252 | static int dflag; |
| 234 | 253 | static int dind; |
| 235 | 254 | static u32int defsamplesize; |
| @@ -237,6 +256,9 @@ static u32int defsamplesize; · 1 unmodified line |
| 237 | 256 | static Track track; |
| 238 | 257 | static Track *tracks; |
| 239 | 258 | static int ntracks; |
| 259 | +static Biobuf stderr; |
| 260 | + |
| 261 | +static Moof moof; |
| 240 | 262 | |
| 241 | 263 | static int parsebox(Biobuf *f, Box *b, int *eof); |
| 242 | 264 | |
| @@ -267,40 +289,40 @@ printbox(Box *b) · 24 unmodified lines |
| 267 | 289 | if(dflag == 0) |
| 268 | 290 | return; |
| 269 | 291 | |
| 270 | | − fprint(2, "%.*s%T\n", dind, ind, b->type); |
| 292 | + Bprint(&stderr, "%.*s%T\n", dind, ind, b->type); |
| 271 | 293 | |
| 272 | 294 | /* full box */ |
| 273 | 295 | if(isfullbox(b)){ |
| 274 | | − fprint(2, "\t%.*sversion\t%d\n", dind, ind, b->version); |
| 275 | | − fprint(2, "\t%.*sflags\t0x%ux\n", dind, ind, b->flags); |
| 296 | + Bprint(&stderr, "\t%.*sversion\t%d\n", dind, ind, b->version); |
| 297 | + Bprint(&stderr, "\t%.*sflags\t0x%ux\n", dind, ind, b->flags); |
| 276 | 298 | } |
| 277 | 299 | if(dflag > 1){ |
| 278 | | − fprint(2, "\t%.*soffset\t%zd\n", dind, ind, b->offset); |
| 279 | | − fprint(2, "\t%.*sdstart\t%zd\n", dind, ind, b->dstart); |
| 280 | | − fprint(2, "\t%.*sdsize\t%zd\n", dind, ind, b->dsz); |
| 300 | + Bprint(&stderr, "\t%.*soffset\t%zd\n", dind, ind, b->offset); |
| 301 | + Bprint(&stderr, "\t%.*sdstart\t%zd\n", dind, ind, b->dstart); |
| 302 | + Bprint(&stderr, "\t%.*sdsize\t%zd\n", dind, ind, b->dsz); |
| 281 | 303 | } |
| 282 | 304 | |
| 283 | 305 | if(b->type == BoxFtyp){ |
| 284 | | − fprint(2, "\t%.*sbrand\t%T\n", dind, ind, b->ftyp.brand); |
| 285 | | − fprint(2, "\t%.*sversion\t%d\n", dind, ind, b->ftyp.version); |
| 286 | | − fprint(2, "\t%.*scompat", dind, ind); |
| 306 | + Bprint(&stderr, "\t%.*sbrand\t%T\n", dind, ind, b->ftyp.brand); |
| 307 | + Bprint(&stderr, "\t%.*sversion\t%d\n", dind, ind, b->ftyp.version); |
| 308 | + Bprint(&stderr, "\t%.*scompat", dind, ind); |
| 287 | 309 | for(i = 0; i < b->ftyp.ncompat; i++) |
| 288 | | − fprint(2, "\t%.*s%T", dind, ind, b->ftyp.compat[i]); |
| 289 | | − fprint(2, "\n"); |
| 310 | + Bprint(&stderr, "\t%.*s%T", dind, ind, b->ftyp.compat[i]); |
| 311 | + Bprint(&stderr, "\n"); |
| 290 | 312 | }else if(b->type == BoxMdhd){ |
| 291 | | − fprint(2, "\t%.*screation\t%zd\n", dind, ind, b->mdhd.creation); |
| 292 | | − fprint(2, "\t%.*smodification\t%zd\n", dind, ind, b->mdhd.modification); |
| 293 | | − fprint(2, "\t%.*stimescale\t%ud\n", dind, ind, b->mdhd.timescale); |
| 294 | | − fprint(2, "\t%.*sduration\t%zd\n", dind, ind, b->mdhd.duration); |
| 313 | + Bprint(&stderr, "\t%.*screation\t%zd\n", dind, ind, b->mdhd.creation); |
| 314 | + Bprint(&stderr, "\t%.*smodification\t%zd\n", dind, ind, b->mdhd.modification); |
| 315 | + Bprint(&stderr, "\t%.*stimescale\t%ud\n", dind, ind, b->mdhd.timescale); |
| 316 | + Bprint(&stderr, "\t%.*sduration\t%zd\n", dind, ind, b->mdhd.duration); |
| 295 | 317 | }else if(b->type == BoxMvhd){ |
| 296 | | − fprint(2, "\t%.*screation\t%zd\n", dind, ind, b->mvhd.creation); |
| 297 | | − fprint(2, "\t%.*smodification\t%zd\n", dind, ind, b->mvhd.modification); |
| 298 | | − fprint(2, "\t%.*stimescale\t%ud\n", dind, ind, b->mvhd.timescale); |
| 299 | | − fprint(2, "\t%.*sduration\t%zd\n", dind, ind, b->mvhd.duration); |
| 300 | | − fprint(2, "\t%.*srate\t0x%ux\n", dind, ind, b->mvhd.rate); |
| 301 | | − fprint(2, "\t%.*svolume\t0x%ux\n", dind, ind, b->mvhd.volume); |
| 302 | | − fprint(2, "\t%.*snexttrack\t0x%ux\n", dind, ind, b->mvhd.nexttrack); |
| 303 | | − fprint(2, "\t%.*smatrix\t0x%ux 0x%ux 0x%ux 0x%ux 0x%ux 0x%ux 0x%ux 0x%ux 0x%ux\n", dind, ind, |
| 318 | + Bprint(&stderr, "\t%.*screation\t%zd\n", dind, ind, b->mvhd.creation); |
| 319 | + Bprint(&stderr, "\t%.*smodification\t%zd\n", dind, ind, b->mvhd.modification); |
| 320 | + Bprint(&stderr, "\t%.*stimescale\t%ud\n", dind, ind, b->mvhd.timescale); |
| 321 | + Bprint(&stderr, "\t%.*sduration\t%zd\n", dind, ind, b->mvhd.duration); |
| 322 | + Bprint(&stderr, "\t%.*srate\t0x%ux\n", dind, ind, b->mvhd.rate); |
| 323 | + Bprint(&stderr, "\t%.*svolume\t0x%ux\n", dind, ind, b->mvhd.volume); |
| 324 | + Bprint(&stderr, "\t%.*snexttrack\t0x%ux\n", dind, ind, b->mvhd.nexttrack); |
| 325 | + Bprint(&stderr, "\t%.*smatrix\t0x%ux 0x%ux 0x%ux 0x%ux 0x%ux 0x%ux 0x%ux 0x%ux 0x%ux\n", dind, ind, |
| 304 | 326 | b->mvhd.matrix[0], |
| 305 | 327 | b->mvhd.matrix[1], |
| 306 | 328 | b->mvhd.matrix[2], |
| @@ -312,99 +334,99 @@ printbox(Box *b) · 5 unmodified lines |
| 312 | 334 | b->mvhd.matrix[8] |
| 313 | 335 | ); |
| 314 | 336 | }else if(b->type == BoxTrex){ |
| 315 | | − fprint(2, "\t%.*strackid\t0x%ux\n", dind, ind, b->trex.trackid); |
| 316 | | − fprint(2, "\t%.*sdefsample\n", dind, ind); |
| 317 | | − fprint(2, "\t\t%.*sdescrindex\t0x%ux\n", dind, ind, b->trex.defsample.descrindex); |
| 318 | | − fprint(2, "\t\t%.*sduration\t%ud\n", dind, ind, b->trex.defsample.duration); |
| 319 | | − fprint(2, "\t\t%.*ssize\t0x%ux\n", dind, ind, b->trex.defsample.size); |
| 320 | | − fprint(2, "\t\t%.*sflags\t0x%ux\n", dind, ind, b->trex.defsample.flags); |
| 337 | + Bprint(&stderr, "\t%.*strackid\t0x%ux\n", dind, ind, b->trex.trackid); |
| 338 | + Bprint(&stderr, "\t%.*sdefsample\n", dind, ind); |
| 339 | + Bprint(&stderr, "\t\t%.*sdescrindex\t0x%ux\n", dind, ind, b->trex.defsample.descrindex); |
| 340 | + Bprint(&stderr, "\t\t%.*sduration\t%ud\n", dind, ind, b->trex.defsample.duration); |
| 341 | + Bprint(&stderr, "\t\t%.*ssize\t0x%ux\n", dind, ind, b->trex.defsample.size); |
| 342 | + Bprint(&stderr, "\t\t%.*sflags\t0x%ux\n", dind, ind, b->trex.defsample.flags); |
| 321 | 343 | }else if(b->type == BoxMfhd){ |
| 322 | | − fprint(2, "\t%.*sseqnumber\t%ud\n", dind, ind, b->mfhd.seqnumber); |
| 344 | + Bprint(&stderr, "\t%.*sseqnumber\t%ud\n", dind, ind, b->mfhd.seqnumber); |
| 323 | 345 | }else if(b->type == BoxTfhd){ |
| 324 | | − fprint(2, "\t%.*strackid\t0x%ux\n", dind, ind, b->tfhd.trackid); |
| 346 | + Bprint(&stderr, "\t%.*strackid\t%d\n", dind, ind, b->tfhd.trackid); |
| 325 | 347 | if(b->flags & 1) |
| 326 | | − fprint(2, "\t%.*sbaseoffset\t%zd\n", dind, ind, b->tfhd.baseoffset); |
| 327 | | − fprint(2, "\t%.*sdefsample\n", dind, ind); |
| 348 | + Bprint(&stderr, "\t%.*sbaseoffset\t%zd\n", dind, ind, b->tfhd.baseoffset); |
| 349 | + Bprint(&stderr, "\t%.*sdefsample\n", dind, ind); |
| 328 | 350 | if(b->flags & 2) |
| 329 | | − fprint(2, "\t\t%.*sdescrindex\t0x%ux\n", dind, ind, b->tfhd.defsample.descrindex); |
| 351 | + Bprint(&stderr, "\t\t%.*sdescrindex\t0x%ux\n", dind, ind, b->tfhd.defsample.descrindex); |
| 330 | 352 | if(b->flags & 8) |
| 331 | | − fprint(2, "\t\t%.*sduration\t%ud\n", dind, ind, b->tfhd.defsample.duration); |
| 353 | + Bprint(&stderr, "\t\t%.*sduration\t%ud\n", dind, ind, b->tfhd.defsample.duration); |
| 332 | 354 | if(b->flags & 16) |
| 333 | | − fprint(2, "\t\t%.*ssize\t0x%ux\n", dind, ind, b->tfhd.defsample.size); |
| 355 | + Bprint(&stderr, "\t\t%.*ssize\t0x%ux\n", dind, ind, b->tfhd.defsample.size); |
| 334 | 356 | if(b->flags & 32) |
| 335 | | − fprint(2, "\t\t%.*sflags\t0x%ux\n", dind, ind, b->tfhd.defsample.flags); |
| 357 | + Bprint(&stderr, "\t\t%.*sflags\t0x%ux\n", dind, ind, b->tfhd.defsample.flags); |
| 336 | 358 | if(b->flags & 0x10000) |
| 337 | | − fprint(2, "\t%.*sduration is empty\n", dind, ind); |
| 359 | + Bprint(&stderr, "\t%.*sduration is empty\n", dind, ind); |
| 338 | 360 | if(b->flags & 0x20000) |
| 339 | | − fprint(2, "\t%.*sdefault base is moof\n", dind, ind); |
| 361 | + Bprint(&stderr, "\t%.*sdefault base is moof\n", dind, ind); |
| 340 | 362 | }else if(b->type == BoxTfdt){ |
| 341 | | − fprint(2, "\t%.*sdecodetime\t%zd\n", dind, ind, b->tfdt.decodetime); |
| 363 | + Bprint(&stderr, "\t%.*sdecodetime\t%zd\n", dind, ind, b->tfdt.decodetime); |
| 342 | 364 | }else if(b->type == BoxTrun){ |
| 343 | | − fprint(2, "\t%.*ssamplecount\t%ud\n", dind, ind, b->trun.samplecount); |
| 365 | + Bprint(&stderr, "\t%.*ssamplecount\t%ud\n", dind, ind, b->trun.samplecount); |
| 344 | 366 | if(b->flags & 1) |
| 345 | | − fprint(2, "\t%.*sdataoffset\t%d\n", dind, ind, b->trun.dataoffset); |
| 367 | + Bprint(&stderr, "\t%.*sdataoffset\t%d\n", dind, ind, b->trun.dataoffset); |
| 346 | 368 | if(b->flags & 2) |
| 347 | | − fprint(2, "\t%.*sfirstsampleflags\t0x%ux\n", dind, ind, b->trun.firstsampleflags); |
| 369 | + Bprint(&stderr, "\t%.*sfirstsampleflags\t0x%ux\n", dind, ind, b->trun.firstsampleflags); |
| 348 | 370 | for(u = 0; dflag > 1 && u < b->trun.samplecount; u++){ |
| 349 | | − fprint(2, "\t%.*ssamples[%zd]\n", dind, ind, u); |
| 350 | | − if(b->flags & 0x100) |
| 351 | | − fprint(2, "\t\t%.*sduration\t%ud\n", dind, ind, b->trun.samples[u].duration); |
| 352 | | − if(b->flags & 0x200) |
| 353 | | − fprint(2, "\t\t%.*ssize\t%ud\n", dind, ind, b->trun.samples[u].size); |
| 354 | | − if(b->flags & 0x400) |
| 355 | | − fprint(2, "\t\t%.*sflags\t0x%ux\n", dind, ind, b->trun.samples[u].flags); |
| 371 | + Bprint(&stderr, "\t%.*ssamples[%zd]\n", dind, ind, u); |
| 372 | + Bprint(&stderr, "\t\t%.*sduration\t%ud\n", dind, ind, b->trun.samples[u].duration); |
| 373 | + Bprint(&stderr, "\t\t%.*ssize\t%ud\n", dind, ind, b->trun.samples[u].size); |
| 374 | + Bprint(&stderr, "\t\t%.*sflags\t0x%ux\n", dind, ind, b->trun.samples[u].flags); |
| 356 | 375 | if(b->flags & 0x800) |
| 357 | | − fprint(2, "\t\t%.*stimeoffset\t%zd\n", dind, ind, b->trun.samples[u].timeoffset); |
| 376 | + Bprint(&stderr, "\t\t%.*stimeoffset\t%zd\n", dind, ind, b->trun.samples[u].timeoffset); |
| 358 | 377 | } |
| 359 | 378 | }else if(b->type == BoxStsd){ |
| 360 | | − fprint(2, "\t%.*sentry_count\t%ud\n", dind, ind, b->stsd.entrycount); |
| 379 | + Bprint(&stderr, "\t%.*sentry_count\t%ud\n", dind, ind, b->stsd.entrycount); |
| 361 | 380 | if(b->stsd.video.format != 0){ |
| 362 | | − fprint(2, "\t\t%.*svideo\t%T\n", dind, ind, b->stsd.video.format); |
| 363 | | − fprint(2, "\t\t\t%.*swidth\t%d\n", dind, ind, b->stsd.video.width); |
| 364 | | − fprint(2, "\t\t\t%.*sheight\t%d\n", dind, ind, b->stsd.video.height); |
| 381 | + Bprint(&stderr, "\t\t%.*svideo\t%T\n", dind, ind, b->stsd.video.format); |
| 382 | + Bprint(&stderr, "\t\t\t%.*swidth\t%d\n", dind, ind, b->stsd.video.width); |
| 383 | + Bprint(&stderr, "\t\t\t%.*sheight\t%d\n", dind, ind, b->stsd.video.height); |
| 365 | 384 | } |
| 366 | 385 | if(b->stsd.audio.format != 0){ |
| 367 | | − fprint(2, "\t\t%.*saudio\t%T\n", dind, ind, b->stsd.audio.format); |
| 368 | | − fprint(2, "\t\t\t%.*schannels\t%d\n", dind, ind, b->stsd.audio.channels); |
| 369 | | − fprint(2, "\t\t\t%.*ssample_rate\t%d\n", dind, ind, b->stsd.audio.samplerate); |
| 386 | + Bprint(&stderr, "\t\t%.*saudio\t%T\n", dind, ind, b->stsd.audio.format); |
| 387 | + Bprint(&stderr, "\t\t\t%.*schannels\t%d\n", dind, ind, b->stsd.audio.channels); |
| 388 | + Bprint(&stderr, "\t\t\t%.*ssample_rate\t%d\n", dind, ind, b->stsd.audio.samplerate); |
| 370 | 389 | } |
| 371 | 390 | }else if(b->type == BoxStts){ |
| 372 | | − fprint(2, "\t%.*sentry_count\t%ud\n", dind, ind, b->stts.entrycount); |
| 391 | + Bprint(&stderr, "\t%.*sentry_count\t%ud\n", dind, ind, b->stts.entrycount); |
| 373 | 392 | }else if(b->type == BoxStss){ |
| 374 | | − fprint(2, "\t%.*sentry_count\t%ud\n", dind, ind, b->stss.entrycount); |
| 393 | + Bprint(&stderr, "\t%.*sentry_count\t%ud\n", dind, ind, b->stss.entrycount); |
| 375 | 394 | }else if(b->type == BoxStsc){ |
| 376 | | − fprint(2, "\t%.*sentry_count\t%ud\n", dind, ind, b->stss.entrycount); |
| 395 | + Bprint(&stderr, "\t%.*sentry_count\t%ud\n", dind, ind, b->stss.entrycount); |
| 377 | 396 | for(u = 0; dflag > 1 && u < b->stsc.entrycount; u++){ |
| 378 | | − fprint(2, "\t%.*sentry[%zd]\n", dind, ind, u); |
| 379 | | − fprint(2, "\t\t%.*sfirst_chunk\t%ud\n", dind, ind, b->stsc.entry[u].firstchunk); |
| 380 | | − fprint(2, "\t\t%.*ssamples_per_chunk\t%ud\n", dind, ind, b->stsc.entry[u].samplesperchunk); |
| 381 | | − fprint(2, "\t\t%.*ssample_description_table\t%ud\n", dind, ind, b->stsc.entry[u].sdt); |
| 397 | + Bprint(&stderr, "\t%.*sentry[%zd]\n", dind, ind, u); |
| 398 | + Bprint(&stderr, "\t\t%.*sfirst_chunk\t%ud\n", dind, ind, b->stsc.entry[u].firstchunk); |
| 399 | + Bprint(&stderr, "\t\t%.*ssamples_per_chunk\t%ud\n", dind, ind, b->stsc.entry[u].samplesperchunk); |
| 400 | + Bprint(&stderr, "\t\t%.*ssample_description_table\t%ud\n", dind, ind, b->stsc.entry[u].sdt); |
| 382 | 401 | } |
| 383 | 402 | }else if(b->type == BoxStsz){ |
| 384 | | − fprint(2, "\t%.*ssample_size\t%ud\n", dind, ind, b->stsz.samplesizeeach); |
| 385 | | − fprint(2, "\t%.*ssample_count\t%ud\n", dind, ind, b->stsz.samplecount); |
| 403 | + Bprint(&stderr, "\t%.*ssample_size\t%ud\n", dind, ind, b->stsz.samplesizeeach); |
| 404 | + Bprint(&stderr, "\t%.*ssample_count\t%ud\n", dind, ind, b->stsz.samplecount); |
| 386 | 405 | if(dflag > 1 && b->stsz.samplesizeeach == 0){ |
| 387 | 406 | for(u = 0; u < b->stsz.samplecount; u++) |
| 388 | | − fprint(2, "\t%.*ssamplesize[%zd]\t%ud\n", dind, ind, u, b->stsz.samplesize[u]); |
| 407 | + Bprint(&stderr, "\t%.*ssamplesize[%zd]\t%ud\n", dind, ind, u, b->stsz.samplesize[u]); |
| 389 | 408 | } |
| 390 | 409 | }else if(b->type == BoxTkhd){ |
| 391 | | − fprint(2, "\t%.*screation_time\t%zd\n", dind, ind, b->tkhd.creattime); |
| 392 | | − fprint(2, "\t%.*smodification_timetime\t%zd\n", dind, ind, b->tkhd.modtime); |
| 393 | | − fprint(2, "\t%.*strack_id\t%ud\n", dind, ind, b->tkhd.trackid); |
| 394 | | − fprint(2, "\t%.*sduration\t%zd\n", dind, ind, b->tkhd.duration); |
| 395 | | − fprint(2, "\t%.*swidth\t%ud\n", dind, ind, b->tkhd.width); |
| 396 | | − fprint(2, "\t%.*sheight\t%ud\n", dind, ind, b->tkhd.height); |
| 410 | + Bprint(&stderr, "\t%.*screation_time\t%zd\n", dind, ind, b->tkhd.creattime); |
| 411 | + Bprint(&stderr, "\t%.*smodification_time\t%zd\n", dind, ind, b->tkhd.modtime); |
| 412 | + Bprint(&stderr, "\t%.*strack_id\t%ud\n", dind, ind, b->tkhd.trackid); |
| 413 | + Bprint(&stderr, "\t%.*sduration\t%zd\n", dind, ind, b->tkhd.duration); |
| 414 | + Bprint(&stderr, "\t%.*swidth\t%ud\n", dind, ind, b->tkhd.width); |
| 415 | + Bprint(&stderr, "\t%.*sheight\t%ud\n", dind, ind, b->tkhd.height); |
| 397 | 416 | }else if(b->type == BoxHdlr){ |
| 398 | | − fprint(2, "\t%.*shandler_type\t%T\n", dind, ind, b->hdlr.handlertype); |
| 399 | | − fprint(2, "\t%.*sname\t%s\n", dind, ind, b->hdlr.name); |
| 417 | + Bprint(&stderr, "\t%.*shandler_type\t%T\n", dind, ind, b->hdlr.handlertype); |
| 418 | + Bprint(&stderr, "\t%.*sname\t%s\n", dind, ind, b->hdlr.name); |
| 400 | 419 | }else if(b->type == BoxStco || b->type == BoxCo64){ |
| 401 | | − fprint(2, "\t%.*sentry_count\t%ud\n", dind, ind, b->stss.entrycount); |
| 420 | + Bprint(&stderr, "\t%.*sentry_count\t%ud\n", dind, ind, b->stss.entrycount); |
| 402 | 421 | for(u = 0; dflag > 1 && u < b->stco_co64.entrycount; u++) |
| 403 | | − fprint(2, "\t%.*schunkoffset[%zd]\t%zd\n", dind, ind, u, b->stco_co64.chunkoffset[u]); |
| 422 | + Bprint(&stderr, "\t%.*schunkoffset[%zd]\t%zd\n", dind, ind, u, b->stco_co64.chunkoffset[u]); |
| 423 | + }else if(b->type == BoxEsds){ |
| 424 | + Bprint(&stderr, "\t%.*sid\t%d\n", dind, ind, b->esds.id); |
| 425 | + Bprint(&stderr, "\t%.*sflags\t0x%x\n", dind, ind, b->esds.flags); |
| 404 | 426 | }else if(dflag < 2){ |
| 405 | | − fprint(2, "\t%.*soffset\t%zd\n", dind, ind, b->offset); |
| 406 | | − fprint(2, "\t%.*sdstart\t%zd\n", dind, ind, b->dstart); |
| 407 | | − fprint(2, "\t%.*sdsize\t%zd\n", dind, ind, b->dsz); |
| 427 | + Bprint(&stderr, "\t%.*soffset\t%zd\n", dind, ind, b->offset); |
| 428 | + Bprint(&stderr, "\t%.*sdstart\t%zd\n", dind, ind, b->dstart); |
| 429 | + Bprint(&stderr, "\t%.*sdsize\t%zd\n", dind, ind, b->dsz); |
| 408 | 430 | } |
| 409 | 431 | } |
| 410 | 432 | |
| @@ -415,6 +437,38 @@ addtrack(void) · 4 unmodified lines |
| 415 | 437 | memmove(&tracks[ntracks++], &track, sizeof(track)); |
| 416 | 438 | } |
| 417 | 439 | |
| 440 | +static Track * |
| 441 | +gettrack(int id) |
| 442 | +{ |
| 443 | + Track *t; |
| 444 | + int i; |
| 445 | + |
| 446 | + for(t = tracks, i = 0; i < ntracks && t->id != id; i++, t++){ |
| 447 | + fprint(2, "%d != %d\n", t->id, id); |
| 448 | + } |
| 449 | + if(i >= ntracks){ |
| 450 | + werrstr("no track %d", id); |
| 451 | + t = nil; |
| 452 | + } |
| 453 | + |
| 454 | + return t; |
| 455 | +} |
| 456 | + |
| 457 | +static void |
| 458 | +addtrun(Box *trun) |
| 459 | +{ |
| 460 | + Track *t; |
| 461 | + Moof *m; |
| 462 | + |
| 463 | + if((t = gettrack(moof.tfhd.tfhd.trackid)) == nil) |
| 464 | + sysfatal("trun without a track"); |
| 465 | + |
| 466 | + t->moof = realloc(t->moof, (t->nmoof+1)*sizeof(Moof)); |
| 467 | + m = &t->moof[t->nmoof++]; |
| 468 | + memmove(&moof.trun, trun, sizeof(moof.trun)); |
| 469 | + memmove(m, &moof, sizeof(moof)); |
| 470 | +} |
| 471 | + |
| 418 | 472 | static int srate2mpeg4fi[] = { |
| 419 | 473 | 96000, |
| 420 | 474 | 88200, |
| @@ -432,51 +486,14 @@ static int srate2mpeg4fi[] = { · 11 unmodified lines |
| 432 | 486 | }; |
| 433 | 487 | |
| 434 | 488 | static int |
| 435 | | −dumptrack(Biobuf *f, int id) |
| 489 | +dumpstc(Biobuf *f, Biobuf *out, Track *t, u8int *frame) |
| 436 | 490 | { |
| 437 | 491 | SampleToChunk *stc; |
| 438 | 492 | u32int si, ch, nextch; |
| 439 | 493 | u32int samplelast, sample, rawsz, samplesz; |
| 440 | 494 | u64int o, wo; |
| 441 | | − int i; |
| 442 | 495 | u8int *raw; |
| 443 | | − Biobuf out; |
| 444 | | − Track *t; |
| 445 | | − u8int frame[7]; |
| 446 | | − |
| 447 | | − for(t = tracks, i = 0; i < ntracks && t->id != id; i++, t++); |
| 448 | | − if(i >= ntracks){ |
| 449 | | − werrstr("no track %d", id); |
| 450 | | − return -1; |
| 451 | | − } |
| 452 | 496 | |
| 453 | | − fprint( |
| 454 | | − 2, |
| 455 | | − "track %d: handler=%T numstc=%ud chunks=%ud samples=%ud\n", t->id, t->handlertype, t->numstc, t->numchunks, t->numsamples |
| 456 | | − ); |
| 457 | | − if(t->audio.format != 0){ |
| 458 | | − fprint(2, "audio: format=%T channels=%d samplerate=%d\n", t->audio.format, t->audio.channels, t->audio.samplerate); |
| 459 | | − if(t->audio.format == FmtMp4a){ |
| 460 | | − for(i = 0; i < nelem(srate2mpeg4fi) && srate2mpeg4fi[i] != t->audio.samplerate; i++); |
| 461 | | − if(i >= nelem(srate2mpeg4fi)){ |
| 462 | | − werrstr("audio: mpeg4: invalid sample rate %d", t->audio.samplerate); |
| 463 | | − return -1; |
| 464 | | − } |
| 465 | | − frame[0] = 0xff; /* syncword */ |
| 466 | | − frame[1] = 0xf1; /* syncword, mpeg4, no crc */ |
| 467 | | − frame[2] = 1<<6 | i<<2 | t->audio.channels>>2; /* AAC(LC)??? FIXME, frequency index, channels */ |
| 468 | | − }else{ |
| 469 | | − werrstr("audio: unknown format %T\n", t->audio.format); |
| 470 | | − return -1; |
| 471 | | − } |
| 472 | | − } |
| 473 | | − if(t->video.format != 0){ |
| 474 | | − fprint(2, "video: format=%T resolution=%dx%d\n", t->video.format, t->video.width, t->video.height); |
| 475 | | − //werrstr("video: unknown format %T", t->video.format); |
| 476 | | − //return -1; |
| 477 | | − } |
| 478 | | − |
| 479 | | − Binit(&out, 1, OWRITE); |
| 480 | 497 | raw = nil; |
| 481 | 498 | rawsz = 0; |
| 482 | 499 | sample = samplelast = 0; |
| @@ -515,14 +532,14 @@ dumptrack(Biobuf *f, int id) · 32 unmodified lines |
| 515 | 532 | frame[5] = (samplesz&7)<<5 | 0x1f; /* frame length, fullness */ |
| 516 | 533 | frame[6] = 0xfc; /* fullness, number of frames */ |
| 517 | 534 | samplesz -= 7; |
| 518 | | − if(Bwrite(&out, frame, 7) != 7){ /* EOF */ |
| 535 | + if(Bwrite(out, frame, 7) != 7){ /* EOF */ |
| 519 | 536 | werrstr("eof"); |
| 520 | 537 | break; |
| 521 | 538 | } |
| 522 | 539 | wo += 7; |
| 523 | 540 | o += 7; |
| 524 | 541 | } |
| 525 | | − if(Bwrite(&out, raw, samplesz) != samplesz){ /* EOF? */ |
| 542 | + if(Bwrite(out, raw, samplesz) != samplesz){ /* EOF? */ |
| 526 | 543 | werrstr("eof"); |
| 527 | 544 | break; |
| 528 | 545 | } |
| @@ -532,10 +549,167 @@ dumptrack(Biobuf *f, int id) · 3 unmodified lines |
| 532 | 549 | samplelast = sample; |
| 533 | 550 | } |
| 534 | 551 | } |
| 535 | | − Bterm(&out); |
| 536 | 552 | free(raw); |
| 537 | 553 | |
| 538 | 554 | return 0; |
| 555 | +} |
| 556 | + |
| 557 | +static int |
| 558 | +dumptrun(Biobuf *f, Biobuf *out, Track *t, u8int *frame) |
| 559 | +{ |
| 560 | + int i, j; |
| 561 | + u64int ts; |
| 562 | + u8int *raw; |
| 563 | + int maxsz; |
| 564 | + RunSample *s; |
| 565 | + Moof *m; |
| 566 | + |
| 567 | + USED(frame); |
| 568 | + |
| 569 | + if(t->video.format == 0) |
| 570 | + sysfatal("trun dump only works with IVF atm"); /* FIXME */ |
| 571 | + |
| 572 | + ts = 0; |
| 573 | + maxsz = 0; |
| 574 | + raw = nil; |
| 575 | + for(i = 0, m = t->moof; i < t->nmoof; i++, m++){ |
| 576 | + for(j = 0; j < m->trun.trun.samplecount; j++){ |
| 577 | + s = &m->trun.trun.samples[j]; |
| 578 | + if(12 + s->size > maxsz){ |
| 579 | + maxsz = 12 + s->size; |
| 580 | + raw = realloc(raw, maxsz); |
| 581 | + } |
| 582 | + raw[0] = s->size; |
| 583 | + raw[1] = s->size >> 8; |
| 584 | + raw[2] = s->size >> 16; |
| 585 | + raw[3] = s->size >> 24; |
| 586 | + raw[4] = ts; |
| 587 | + raw[5] = ts >> 8; |
| 588 | + raw[6] = ts >> 16; |
| 589 | + raw[7] = ts >> 24; |
| 590 | + raw[8] = ts >> 32; |
| 591 | + raw[9] = ts >> 40; |
| 592 | + raw[10] = ts >> 48; |
| 593 | + raw[11] = ts >> 56; |
| 594 | + if(Bseek(f, s->offset, 0) != s->offset){ |
| 595 | + werrstr("couldn't seek to offset %zd", s->offset); |
| 596 | + return -1; |
| 597 | + } |
| 598 | + if(Bread(f, raw+12, s->size) != s->size){ |
| 599 | + werrstr("couldn't read sample (%d bytes)", s->size); |
| 600 | + return -1; |
| 601 | + } |
| 602 | + if(Bwrite(out, raw, 12 + s->size) != 12 + s->size) /* eof? */ |
| 603 | + break; |
| 604 | + ts += s->duration; /* sample's "time offset" is ignored here */ |
| 605 | + } |
| 606 | + } |
| 607 | + |
| 608 | + return 0; |
| 609 | +} |
| 610 | + |
| 611 | +static int |
| 612 | +dumptrack(Biobuf *f, int id) |
| 613 | +{ |
| 614 | + int i, j, res; |
| 615 | + Biobuf out; |
| 616 | + u64int dur; |
| 617 | + Track *t; |
| 618 | + Moof *m; |
| 619 | + u8int frame[0x20]; |
| 620 | + |
| 621 | + if((t = gettrack(id)) == nil) |
| 622 | + return -1; |
| 623 | + |
| 624 | + if(dflag) |
| 625 | + Bprint(&stderr, "track %d: handler=%T ", t->id, t->handlertype); |
| 626 | + if(t->numstc > 0){ |
| 627 | + if(dflag) |
| 628 | + Bprint(&stderr, "numstc=%ud chunks=%ud samples=%ud\n", t->numstc, t->numchunks, t->numsamples); |
| 629 | + }else if(t->nmoof > 0){ |
| 630 | + if(dflag) |
| 631 | + Bprint(&stderr, "numtrun: %d\n", t->nmoof); |
| 632 | + }else{ |
| 633 | + if(dflag) |
| 634 | + Bprint(&stderr, "no samples\n"); |
| 635 | + werrstr("track %d has no samples", t->id); |
| 636 | + return -1; |
| 637 | + } |
| 638 | + |
| 639 | + Binit(&out, 1, OWRITE); |
| 640 | + |
| 641 | + if(t->audio.format != 0){ |
| 642 | + Bprint(&stderr, "audio: format=%T channels=%d samplerate=%d\n", t->audio.format, t->audio.channels, t->audio.samplerate); |
| 643 | + if(t->audio.format == FmtMp4a){ |
| 644 | + for(i = 0; i < nelem(srate2mpeg4fi) && srate2mpeg4fi[i] != t->audio.samplerate; i++); |
| 645 | + if(i >= nelem(srate2mpeg4fi)){ |
| 646 | + werrstr("audio: mpeg4: invalid sample rate %d", t->audio.samplerate); |
| 647 | + return -1; |
| 648 | + } |
| 649 | + frame[0] = 0xff; /* syncword */ |
| 650 | + frame[1] = 0xf1; /* syncword, mpeg4, no crc */ |
| 651 | + frame[2] = 1<<6 | i<<2 | t->audio.channels>>2; /* AAC(LC)??? FIXME, frequency index, channels */ |
| 652 | + }else{ |
| 653 | + werrstr("audio: unknown format %T\n", t->audio.format); |
| 654 | + return -1; |
| 655 | + } |
| 656 | + }else if(t->video.format != 0){ |
| 657 | + Bprint(&stderr, "video: format=%T resolution=%dx%d\n", t->video.format, t->video.width, t->video.height); |
| 658 | + /* video is remuxed into IVF */ |
| 659 | + memset(frame, 0, 0x20); |
| 660 | + memmove(frame, "DKIF", 4); |
| 661 | + frame[4] = 0; |
| 662 | + frame[5] = 0; |
| 663 | + frame[6] = 0x20; |
| 664 | + frame[7] = 0; |
| 665 | + if(t->video.format == FmtAv01){ |
| 666 | + memmove(frame+8, "AV01", 4); |
| 667 | + frame[12] = t->video.width; |
| 668 | + frame[13] = t->video.width >> 8; |
| 669 | + frame[14] = t->video.height; |
| 670 | + frame[15] = t->video.height >> 8; |
| 671 | + /* timebase denum */ |
| 672 | + frame[16] = t->timescale; |
| 673 | + frame[17] = t->timescale >> 8; |
| 674 | + frame[18] = t->timescale >> 16; |
| 675 | + frame[19] = t->timescale >> 24; |
| 676 | + /* timebase num */ |
| 677 | + frame[20] = 1; |
| 678 | + frame[21] = 0; |
| 679 | + frame[22] = 0; |
| 680 | + frame[23] = 0; |
| 681 | + /* FIXME is it a "number of frames" or "total duration?" */ |
| 682 | + dur = 0; |
| 683 | + for(i = 0, m = t->moof; i < t->nmoof; i++, m++){ |
| 684 | + for(j = 0; j < m->trun.trun.samplecount; j++){ |
| 685 | + dur += m->trun.trun.samples[j].duration; |
| 686 | + } |
| 687 | + } |
| 688 | + frame[24] = dur; |
| 689 | + frame[25] = dur >> 8; |
| 690 | + frame[26] = dur >> 16; |
| 691 | + frame[27] = dur >> 24; |
| 692 | + frame[28] = dur >> 32; /* FIXME is it 64 bits? */ |
| 693 | + frame[29] = dur >> 40; |
| 694 | + frame[30] = dur >> 48; |
| 695 | + frame[31] = dur >> 56; |
| 696 | + |
| 697 | + Bwrite(&out, frame, 0x20); |
| 698 | + }else{ |
| 699 | + werrstr("video: unknown format %T", t->video.format); |
| 700 | + return -1; |
| 701 | + } |
| 702 | + } |
| 703 | + |
| 704 | + res = -1; |
| 705 | + if(t->numstc > 0) |
| 706 | + res = dumpstc(f, &out, t, frame); |
| 707 | + else if(t->nmoof > 0) |
| 708 | + res = dumptrun(f, &out, t, frame); |
| 709 | + |
| 710 | + Bterm(&out); |
| 711 | + |
| 712 | + return res; |
| 539 | 713 | }; |
| 540 | 714 | |
| 541 | 715 | int |
| @@ -565,7 +739,7 @@ sampleentry(Biobuf *f, Box *b, u32int fmt, int n) · 23 unmodified lines |
| 565 | 739 | memmove(&track.audio, &b->stsd.audio, sizeof(Audio)); |
| 566 | 740 | /* FIXME do we care about the rest? */ |
| 567 | 741 | }else{ |
| 568 | | − fprint(2, "SampleEntry: unknown handler type %T\n", track.handlertype); |
| 742 | + Bprint(&stderr, "SampleEntry: unknown handler type %T\n", track.handlertype); |
| 569 | 743 | } |
| 570 | 744 | |
| 571 | 745 | return n; |
| @@ -579,8 +753,8 @@ parseboxdata(Biobuf *f, Box *b) · 7 unmodified lines |
| 579 | 753 | u8int d[128], *p; |
| 580 | 754 | Box inner; |
| 581 | 755 | int i, n, eof; |
| 582 | | − s32int s32i; |
| 583 | | − u64int u; |
| 756 | + u64int u, off; |
| 757 | + RunSample *s; |
| 584 | 758 | |
| 585 | 759 | if(b->type == BoxFtyp){ |
| 586 | 760 | eBread(8, "brand and version"); |
| @@ -601,6 +775,10 @@ parseboxdata(Biobuf *f, Box *b) · 14 unmodified lines |
| 601 | 775 | b->type == BoxMdia || b->type == BoxMinf || b->type == BoxStbl || |
| 602 | 776 | b->type == BoxMoof || b->type == BoxTraf || b->type == BoxEdts || |
| 603 | 777 | b->type == BoxDinf){ |
| 778 | + if(b->type == BoxMoof){ |
| 779 | + memset(&moof, 0, sizeof(moof)); |
| 780 | + memmove(&moof, b, sizeof(Box)); |
| 781 | + } |
| 604 | 782 | printbox(b); |
| 605 | 783 | dind++; |
| 606 | 784 | for(;;){ |
| @@ -616,6 +794,8 @@ parseboxdata(Biobuf *f, Box *b) · 9 unmodified lines |
| 616 | 794 | memset(&track, 0, sizeof(track)); |
| 617 | 795 | } |
| 618 | 796 | dind--; |
| 797 | + if(b->type == BoxMoof) |
| 798 | + memset(&moof, 0, sizeof(moof)); |
| 619 | 799 | }else if(b->type == BoxMdhd){ |
| 620 | 800 | n = b->version == 0 ? 16 : 28; |
| 621 | 801 | eBread(n, "short read"); |
| @@ -632,6 +812,7 @@ parseboxdata(Biobuf *f, Box *b) · 10 unmodified lines |
| 632 | 812 | } |
| 633 | 813 | |
| 634 | 814 | b->mdhd.timescale = bu32(p); p += 4; |
| 815 | + track.timescale = b->mdhd.timescale; /* it's used for IVF timebase */ |
| 635 | 816 | |
| 636 | 817 | b->mdhd.duration = bu32(p); p += 4; |
| 637 | 818 | if(b->version == 1){ |
| @@ -683,6 +864,7 @@ parseboxdata(Biobuf *f, Box *b) · 45 unmodified lines |
| 683 | 864 | }else if(b->type == BoxMfhd){ |
| 684 | 865 | eBread(4, "short read"); |
| 685 | 866 | b->mfhd.seqnumber = bu32(d); |
| 867 | + memmove(&moof.mfhd, b, sizeof(*b)); |
| 686 | 868 | printbox(b); |
| 687 | 869 | }else if(b->type == BoxTfhd){ |
| 688 | 870 | eBread(4, "track_id"); |
| @@ -708,6 +890,7 @@ parseboxdata(Biobuf *f, Box *b) · 19 unmodified lines |
| 708 | 890 | eBread(4, "default_sample_flags"); |
| 709 | 891 | b->tfhd.defsample.flags = bu32(d); |
| 710 | 892 | } |
| 893 | + memmove(&moof.tfhd, b, sizeof(*b)); |
| 711 | 894 | printbox(b); |
| 712 | 895 | }else if(b->type == BoxTfdt){ |
| 713 | 896 | if(b->version == 1){ |
| @@ -731,32 +914,53 @@ parseboxdata(Biobuf *f, Box *b) · 17 unmodified lines |
| 731 | 914 | } |
| 732 | 915 | /* FIXME free those */ |
| 733 | 916 | b->trun.samples = calloc(b->trun.samplecount, sizeof(RunSample)); |
| 917 | + off = 0; |
| 734 | 918 | for(u = 0; u < b->trun.samplecount; u++){ |
| 919 | + s = &b->trun.samples[u]; |
| 735 | 920 | if(b->flags & 0x100){ |
| 736 | 921 | eBread(4, "sample_duration"); |
| 737 | | − b->trun.samples[u].duration = bu32(d); |
| 738 | | − } |
| 922 | + s->duration = bu32(d); |
| 923 | + }else if(moof.tfhd.flags & 8) |
| 924 | + s->duration = moof.tfhd.tfhd.defsample.duration; |
| 925 | + |
| 739 | 926 | if(b->flags & 0x200){ |
| 740 | 927 | eBread(4, "sample_size"); |
| 741 | | − b->trun.samples[u].size = bu32(d); |
| 742 | | − } |
| 928 | + s->size = bu32(d); |
| 929 | + }else if(moof.tfhd.flags & 16) |
| 930 | + s->size = moof.tfhd.tfhd.defsample.size; |
| 931 | + |
| 743 | 932 | if(b->flags & 0x400){ |
| 744 | 933 | eBread(4, "sample_flags"); |
| 745 | | − b->trun.samples[u].flags = bu32(d); |
| 746 | | − } |
| 934 | + s->flags = bu32(d); |
| 935 | + }else if((b->flags & 4) && u == 0){ |
| 936 | + // FIXME default flags??? |
| 937 | + s->flags = b->trun.firstsampleflags; |
| 938 | + }else if(moof.tfhd.flags & 32) |
| 939 | + s->flags = moof.tfhd.tfhd.defsample.flags; |
| 940 | + |
| 747 | 941 | if(b->flags & 0x800){ |
| 748 | 942 | eBread(4, "sample_composition_time_offset"); |
| 749 | | − s32i = bu32(d); |
| 750 | 943 | if(b->version == 0) |
| 751 | | − b->trun.samples[u].timeoffset = bu32(d); |
| 944 | + s->timeoffset = bu32(d); |
| 752 | 945 | else |
| 753 | | − b->trun.samples[u].timeoffset = s32i; |
| 946 | + s->timeoffset = (s32int)bu32(d); |
| 754 | 947 | } |
| 948 | + |
| 949 | + s->offset = b->trun.dataoffset + off; |
| 950 | + if(moof.tfhd.flags & 1) |
| 951 | + Bprint(&stderr, "tfhd has base-data-offset-present set, this isn't supported atm\n"); |
| 952 | + else if(moof.tfhd.flags & 0x20000) |
| 953 | + s->offset += moof.offset + moof.tfhd.tfhd.baseoffset; |
| 954 | + else |
| 955 | + Bprint(&stderr, "couldn't calculate sample offset\n"); |
| 956 | + off += s->size; |
| 755 | 957 | } |
| 958 | + addtrun(b); |
| 756 | 959 | printbox(b); |
| 757 | 960 | }else if(b->type == BoxEsds){ |
| 758 | 961 | eBread(3, "es id"); |
| 759 | | − fprint(2, "# es id: %ud\n# es flags: 0x%02x\n", bu16(d+1), d[3]); |
| 962 | + b->esds.id = bu16(d+1); |
| 963 | + b->esds.flags = d[3]; |
| 760 | 964 | printbox(b); |
| 761 | 965 | }else if(b->type == BoxStsd){ |
| 762 | 966 | eBread(4, "entry_count"); |
| @@ -950,7 +1154,7 @@ err: · 187 unmodified lines |
| 950 | 1154 | static void |
| 951 | 1155 | usage(void) |
| 952 | 1156 | { |
| 953 | | − fprint(2, "usage: iso [-d] [-t TRACK] FILE\n"); |
| 1157 | + Bprint(&stderr, "usage: iso [-d] [-t TRACK] FILE\n"); |
| 954 | 1158 | exits("usage"); |
| 955 | 1159 | } |
| 956 | 1160 | |
| @@ -975,6 +1179,8 @@ main(int argc, char **argv) · 18 unmodified lines |
| 975 | 1179 | |
| 976 | 1180 | fmtinstall('T', typefmt); |
| 977 | 1181 | |
| 1182 | + Binit(&stderr, 2, OWRITE); |
| 1183 | + |
| 978 | 1184 | status = nil; |
| 979 | 1185 | for(; *argv && status == nil; argv++){ |
| 980 | 1186 | if((f = Bopen(*argv, OREAD)) == nil) |
| @@ -992,12 +1198,14 @@ main(int argc, char **argv) · 11 unmodified lines |
| 992 | 1198 | } |
| 993 | 1199 | |
| 994 | 1200 | if(trackdump >= 0 && dumptrack(f, trackdump) != 0){ |
| 995 | | − fprint(2, "%s: %r\n", *argv); |
| 1201 | + Bprint(&stderr, "%s: %r\n", *argv); |
| 996 | 1202 | status = "dump"; |
| 997 | 1203 | } |
| 998 | 1204 | |
| 999 | 1205 | Bterm(f); |
| 1000 | 1206 | } |
| 1001 | 1207 | |
| 1208 | + Bterm(&stderr); |
| 1209 | + |
| 1002 | 1210 | exits(status); |
| 1003 | 1211 | } |