comparison libmpdemux/aviheader.c @ 12036:846ed866f86c

OpenDML read/write support
author ranma
date Wed, 17 Mar 2004 14:50:37 +0000
parents 9767665d49e0
children cdf62dc6d6a0
comparison
equal deleted inserted replaced
12035:b3512c7b26ce 12036:846ed866f86c
23 extern void print_avih_flags(MainAVIHeader *h); 23 extern void print_avih_flags(MainAVIHeader *h);
24 extern void print_strh(AVIStreamHeader *h); 24 extern void print_strh(AVIStreamHeader *h);
25 extern void print_wave_header(WAVEFORMATEX *h); 25 extern void print_wave_header(WAVEFORMATEX *h);
26 extern void print_video_header(BITMAPINFOHEADER *h); 26 extern void print_video_header(BITMAPINFOHEADER *h);
27 extern void print_index(AVIINDEXENTRY *idx,int idx_size); 27 extern void print_index(AVIINDEXENTRY *idx,int idx_size);
28 extern void print_avistdindex_chunk(avistdindex_chunk *h);
29 extern void print_avisuperindex_chunk(avisuperindex_chunk *h);
30
31 static int odml_get_vstream_id(int id, unsigned char res[])
32 {
33 unsigned char *p = (unsigned char *)&id;
34 id = le2me_32(id);
35
36 if (p[2] == 'd') {
37 if (res) {
38 res[0] = p[0];
39 res[1] = p[1];
40 }
41 return 1;
42 }
43 return 0;
44 }
45
46 /*
47 * Simple quicksort for AVIINDEXENTRYs
48 */
49 static void avi_idx_quicksort(AVIINDEXENTRY *idx, int from, int to)
50 {
51 AVIINDEXENTRY temp;
52 int lo = to;
53 int hi = from;
54 off_t pivot_ofs = AVI_IDX_OFFSET(&idx[(from + to) / 2]);
55 do {
56 while(pivot_ofs < AVI_IDX_OFFSET(&idx[lo])) lo--;
57 while(pivot_ofs > AVI_IDX_OFFSET(&idx[hi])) hi++;
58 if(hi <= lo) {
59 if (hi != lo) {
60 memcpy(&temp, &idx[lo], sizeof(temp));
61 memcpy(&idx[lo], &idx[hi], sizeof(temp));
62 memcpy(&idx[hi], &temp, sizeof(temp));
63 }
64 lo--; hi++;
65 }
66 } while (lo >= hi);
67 if (from < lo) avi_idx_quicksort(idx, from, lo);
68 if (to > hi) avi_idx_quicksort(idx, hi, to);
69 }
28 70
29 void read_avi_header(demuxer_t *demuxer,int index_mode){ 71 void read_avi_header(demuxer_t *demuxer,int index_mode){
30 sh_audio_t *sh_audio=NULL; 72 sh_audio_t *sh_audio=NULL;
31 sh_video_t *sh_video=NULL; 73 sh_video_t *sh_video=NULL;
32 int stream_id=-1; 74 int stream_id=-1;
177 memcpy(&sh_audio->audio,&h,sizeof(h)); 219 memcpy(&sh_audio->audio,&h,sizeof(h));
178 } 220 }
179 last_fccType=h.fccType; 221 last_fccType=h.fccType;
180 if(verbose>=1) print_strh(&h); 222 if(verbose>=1) print_strh(&h);
181 break; } 223 break; }
224 case mmioFOURCC('i', 'n', 'd', 'x'): {
225 DWORD i;
226 unsigned msize = 0;
227 avisuperindex_chunk *s;
228 priv->suidx_size++;
229 priv->suidx = realloc(priv->suidx, priv->suidx_size * sizeof (avisuperindex_chunk));
230 s = &priv->suidx[priv->suidx_size-1];
231
232 chunksize-=24;
233 memcpy(s->fcc, "indx", 4);
234 s->dwSize = size2;
235 s->wLongsPerEntry = stream_read_word_le(demuxer->stream);
236 s->bIndexSubType = stream_read_char(demuxer->stream);
237 s->bIndexType = stream_read_char(demuxer->stream);
238 s->nEntriesInUse = stream_read_dword_le(demuxer->stream);
239 *(uint32_t *)s->dwChunkId = stream_read_dword_le(demuxer->stream);
240 stream_read(demuxer->stream, (char *)s->dwReserved, 3*4);
241 memset(s->dwReserved, 0, 3*4);
242
243 print_avisuperindex_chunk(s);
244
245 msize = sizeof (uint32_t) * s->wLongsPerEntry * s->nEntriesInUse;
246 s->aIndex = malloc(msize);
247 memset (s->aIndex, 0, msize);
248 s->stdidx = malloc (s->nEntriesInUse * sizeof (avistdindex_chunk));
249 memset (s->stdidx, 0, s->nEntriesInUse * sizeof (avistdindex_chunk));
250
251 // now the real index of indices
252 for (i=0; i<s->nEntriesInUse; i++) {
253 chunksize-=16;
254 s->aIndex[i].qwOffset = stream_read_dword_le(demuxer->stream) & 0xffffffff;
255 s->aIndex[i].qwOffset |= ((uint64_t)stream_read_dword_le(demuxer->stream) & 0xffffffff)<<32;
256 s->aIndex[i].dwSize = stream_read_dword_le(demuxer->stream);
257 s->aIndex[i].dwDuration = stream_read_dword_le(demuxer->stream);
258 mp_msg (MSGT_HEADER, MSGL_V, "ODML (%.4s): [%d] 0x%016llx 0x%04lx %ld\n",
259 (s->dwChunkId), i,
260 (uint64_t)s->aIndex[i].qwOffset, s->aIndex[i].dwSize, s->aIndex[i].dwDuration);
261 }
262 priv->isodml++;
263
264 break; }
182 case ckidSTREAMFORMAT: { // read 'strf' 265 case ckidSTREAMFORMAT: { // read 'strf'
183 if(last_fccType==streamtypeVIDEO){ 266 if(last_fccType==streamtypeVIDEO){
184 sh_video->bih=calloc((chunksize<sizeof(BITMAPINFOHEADER))?sizeof(BITMAPINFOHEADER):chunksize,1); 267 sh_video->bih=calloc((chunksize<sizeof(BITMAPINFOHEADER))?sizeof(BITMAPINFOHEADER):chunksize,1);
185 // sh_video->bih=malloc(chunksize); memset(sh_video->bih,0,chunksize); 268 // sh_video->bih=malloc(chunksize); memset(sh_video->bih,0,chunksize);
186 mp_msg(MSGT_HEADER,MSGL_V,"found 'bih', %u bytes of %d\n",chunksize,sizeof(BITMAPINFOHEADER)); 269 mp_msg(MSGT_HEADER,MSGL_V,"found 'bih', %u bytes of %d\n",chunksize,sizeof(BITMAPINFOHEADER));
244 ++priv->audio_streams; 327 ++priv->audio_streams;
245 // if(demuxer->audio->id==-1) demuxer->audio->id=stream_id; 328 // if(demuxer->audio->id==-1) demuxer->audio->id=stream_id;
246 } 329 }
247 break; 330 break;
248 } 331 }
332 case mmioFOURCC('v', 'p', 'r', 'p'): {
333 VideoPropHeader *vprp = malloc(chunksize);
334 int i;
335 stream_read(demuxer->stream, (void*)vprp, chunksize);
336 le2me_VideoPropHeader(vprp);
337 chunksize -= sizeof(*vprp)-sizeof(vprp->FieldInfo);
338 chunksize /= sizeof(VIDEO_FIELD_DESC);
339 if (vprp->nbFieldPerFrame > chunksize) {
340 vprp->nbFieldPerFrame = chunksize;
341 }
342 chunksize = 0;
343 for (i=0; i<vprp->nbFieldPerFrame; i++) {
344 le2me_VIDEO_FIELD_DESC(&vprp->FieldInfo[i]);
345 }
346 if (sh_video) {
347 sh_video->aspect = GET_AVI_ASPECT(vprp->dwFrameAspectRatio);
348 }
349 if(verbose>=1) print_vprp(vprp);
350 break;
351 }
352 case mmioFOURCC('d', 'm', 'l', 'h'): {
353 // dmlh 00 00 00 04 frms
354 unsigned int total_frames = stream_read_dword_le(demuxer->stream);
355 mp_msg(MSGT_HEADER,MSGL_V,"AVI: dmlh found (size=%d) (total_frames=%d)\n", chunksize, total_frames);
356 stream_skip(demuxer->stream, chunksize-4);
357 chunksize = 0;
358 }
359 break;
249 case ckidAVINEWINDEX: 360 case ckidAVINEWINDEX:
250 if(demuxer->movi_end>stream_tell(demuxer->stream)) 361 if(demuxer->movi_end>stream_tell(demuxer->stream))
251 demuxer->movi_end=stream_tell(demuxer->stream); // fixup movi-end 362 demuxer->movi_end=stream_tell(demuxer->stream); // fixup movi-end
252 if(index_mode){ 363 if(index_mode && !priv->isodml){
253 int i; 364 int i;
365 off_t base = 0;
366 uint32_t last_off = 0;
254 priv->idx_size=size2>>4; 367 priv->idx_size=size2>>4;
255 mp_msg(MSGT_HEADER,MSGL_V,"Reading INDEX block, %d chunks for %ld frames (fpos=%p)\n", 368 mp_msg(MSGT_HEADER,MSGL_V,"Reading INDEX block, %d chunks for %ld frames (fpos=%p)\n",
256 priv->idx_size,avih.dwTotalFrames, stream_tell(demuxer->stream)); 369 priv->idx_size,avih.dwTotalFrames, stream_tell(demuxer->stream));
257 priv->idx=malloc(priv->idx_size<<4); 370 priv->idx=malloc(priv->idx_size<<4);
258 // printf("\nindex to %p !!!!! (priv=%p)\n",priv->idx,priv); 371 // printf("\nindex to %p !!!!! (priv=%p)\n",priv->idx,priv);
259 stream_read(demuxer->stream,(char*)priv->idx,priv->idx_size<<4); 372 stream_read(demuxer->stream,(char*)priv->idx,priv->idx_size<<4);
260 for (i = 0; i < priv->idx_size; i++) // swap index to machine endian 373 for (i = 0; i < priv->idx_size; i++) // swap index to machine endian
261 le2me_AVIINDEXENTRY((AVIINDEXENTRY*)priv->idx + i); 374 le2me_AVIINDEXENTRY((AVIINDEXENTRY*)priv->idx + i);
262 chunksize-=priv->idx_size<<4; 375 chunksize-=priv->idx_size<<4;
263 if(verbose>=2) print_index(priv->idx,priv->idx_size); 376 if(verbose>=2) print_index(priv->idx,priv->idx_size);
264 break; 377 /*
265 } 378 * Fixup index for files >4GB
379 */
380 for (i = 0; i < priv->idx_size; i++) {
381 AVIINDEXENTRY *idx = (AVIINDEXENTRY*)priv->idx + i;
382 idx->dwFlags &= 0xffff;
383 if (idx->dwChunkOffset < last_off) {
384 mp_msg(MSGT_HEADER,MSGL_WARN,"Index offset going backwards (last=%08X, now=%08X), compensating...\n", last_off, idx->dwChunkOffset);
385 base += 0x100000000LL;
386 }
387 idx->dwFlags |= base >> 16;
388 last_off = idx->dwChunkOffset;
389 }
390 }
391 break;
266 /* added May 2002 */ 392 /* added May 2002 */
267 case mmioFOURCC('R','I','F','F'): { 393 case mmioFOURCC('R','I','F','F'): {
268 char riff_type[4]; 394 char riff_type[4];
269 395
270 mp_msg(MSGT_HEADER, MSGL_V, "additional RIFF header...\n"); 396 mp_msg(MSGT_HEADER, MSGL_V, "additional RIFF header...\n");
273 mp_msg(MSGT_HEADER, MSGL_WARN, 399 mp_msg(MSGT_HEADER, MSGL_WARN,
274 "** warning: this is no extended AVI header..\n"); 400 "** warning: this is no extended AVI header..\n");
275 chunksize = 0; 401 chunksize = 0;
276 list_end = 0; /* a new list will follow */ 402 list_end = 0; /* a new list will follow */
277 break; } 403 break; }
404 case ckidAVIPADDING:
405 stream_skip(demuxer->stream, chunksize);
406 chunksize = 0;
407 break;
278 } 408 }
279 if(hdr){ 409 if(hdr){
280 mp_msg(MSGT_HEADER,MSGL_V,"hdr=%s size=%u\n",hdr,size2); 410 mp_msg(MSGT_HEADER,MSGL_V,"hdr=%s size=%u\n",hdr,size2);
281 if(size2==3) 411 if(size2==3)
282 chunksize=1; // empty 412 chunksize=1; // empty
291 } 421 }
292 } 422 }
293 mp_msg(MSGT_HEADER,MSGL_DBG2,"list_end=0x%X pos=0x%X chunksize=0x%X next=0x%X\n", 423 mp_msg(MSGT_HEADER,MSGL_DBG2,"list_end=0x%X pos=0x%X chunksize=0x%X next=0x%X\n",
294 (int)list_end, (int)stream_tell(demuxer->stream), 424 (int)list_end, (int)stream_tell(demuxer->stream),
295 chunksize, (int)chunksize+stream_tell(demuxer->stream)); 425 chunksize, (int)chunksize+stream_tell(demuxer->stream));
426 if(list_end>0 &&
427 chunksize+stream_tell(demuxer->stream) == list_end) list_end=0;
296 if(list_end>0 && chunksize+stream_tell(demuxer->stream)>list_end){ 428 if(list_end>0 && chunksize+stream_tell(demuxer->stream)>list_end){
297 mp_msg(MSGT_HEADER,MSGL_V,"Broken chunk? chunksize=%d (id=%.4s)\n",chunksize,(char *) &id); 429 mp_msg(MSGT_HEADER,MSGL_V,"Broken chunk? chunksize=%d (id=%.4s)\n",chunksize,(char *) &id);
298 stream_seek(demuxer->stream,list_end); 430 stream_seek(demuxer->stream,list_end);
299 list_end=0; 431 list_end=0;
300 } else 432 } else
301 if(chunksize>0) stream_skip(demuxer->stream,chunksize); else 433 if(chunksize>0) stream_skip(demuxer->stream,chunksize); else
302 if((int)chunksize<0) mp_msg(MSGT_HEADER,MSGL_WARN,"chunksize=%u (id=%.4s)\n",chunksize,(char *) &id); 434 if((int)chunksize<0) mp_msg(MSGT_HEADER,MSGL_WARN,"chunksize=%u (id=%.4s)\n",chunksize,(char *) &id);
303 435
436 }
437
438 if (priv->isodml && (index_mode==-1 || index_mode==0)) {
439 int i, j, k;
440 int safety=1000;
441
442 avisuperindex_chunk *cx;
443 AVIINDEXENTRY *idx;
444
445
446 if (priv->idx_size) free(priv->idx);
447 priv->idx_size = 0;
448 priv->idx_offset = 0;
449 priv->idx = NULL;
450
451 mp_msg(MSGT_HEADER, MSGL_INFO,
452 "AVI: ODML: Building odml index (%d superindexchunks)\n", priv->suidx_size);
453
454 // read the standard indices
455 for (cx = &priv->suidx[0], i=0; i<priv->suidx_size; cx++, i++) {
456 stream_reset(demuxer->stream);
457 for (j=0; j<cx->nEntriesInUse; j++) {
458 int ret1, ret2;
459 memset(&cx->stdidx[j], 0, 32);
460 ret1 = stream_seek(demuxer->stream, (off_t)cx->aIndex[j].qwOffset);
461 ret2 = stream_read(demuxer->stream, (char *)&cx->stdidx[j], 32);
462 if (ret1 != 1 || ret2 != 32 || cx->stdidx[j].nEntriesInUse==0) {
463 // this is a broken file (probably incomplete) let the standard
464 // gen_index routine handle this
465 priv->isodml = 0;
466 priv->idx_size = 0;
467 mp_msg(MSGT_HEADER, MSGL_WARN,
468 "AVI: ODML: Broken (incomplete?) file detected. Will use traditional index\n");
469 goto freeout;
470 }
471
472 le2me_AVISTDIDXCHUNK(&cx->stdidx[j]);
473 print_avistdindex_chunk(&cx->stdidx[j]);
474 priv->idx_size += cx->stdidx[j].nEntriesInUse;
475 cx->stdidx[j].aIndex = malloc(cx->stdidx[j].nEntriesInUse*sizeof(avistdindex_entry));
476 stream_read(demuxer->stream, (char *)cx->stdidx[j].aIndex,
477 cx->stdidx[j].nEntriesInUse*sizeof(avistdindex_entry));
478 for (k=0;k<cx->stdidx[j].nEntriesInUse; k++)
479 le2me_AVISTDIDXENTRY(&cx->stdidx[j].aIndex[k]);
480
481 cx->stdidx[j].dwReserved3 = 0;
482
483 }
484 }
485
486 /*
487 * We convert the index by translating all entries into AVIINDEXENTRYs
488 * and sorting them by offset. The result should be the same index
489 * we would get with -forceidx.
490 */
491
492 idx = priv->idx = malloc(priv->idx_size * sizeof (AVIINDEXENTRY));
493
494 for (cx = priv->suidx; cx != &priv->suidx[priv->suidx_size]; cx++) {
495 avistdindex_chunk *sic;
496 for (sic = cx->stdidx; sic != &cx->stdidx[cx->nEntriesInUse]; sic++) {
497 avistdindex_entry *sie;
498 for (sie = sic->aIndex; sie != &sic->aIndex[sic->nEntriesInUse]; sie++) {
499 uint64_t off = sic->qwBaseOffset + sie->dwOffset - 8;
500 memcpy(&idx->ckid, sic->dwChunkId, 4);
501 idx->dwChunkOffset = off;
502 idx->dwFlags = (off >> 32) << 16;
503 idx->dwChunkLength = sie->dwSize & 0x7fffffff;
504 idx->dwFlags |= (sie->dwSize&0x80000000)?0x0:AVIIF_KEYFRAME; // bit 31 denotes !keyframe
505 idx++;
506 }
507 }
508 }
509 avi_idx_quicksort(priv->idx, 0, priv->idx_size-1);
510
511 /*
512 Hack to work around a "wrong" index in some divx odml files
513 (processor_burning.avi as an example)
514 They have ##dc on non keyframes but the ix00 tells us they are ##db.
515 Read the fcc of a non-keyframe vid frame and check it.
516 */
517
518 {
519 uint32_t id;
520 uint32_t db = 0;
521 stream_reset (demuxer->stream);
522
523 // find out the video stream id. I have seen files with 01db.
524 for (idx = &((AVIINDEXENTRY *)priv->idx)[0], i=0; i<priv->idx_size; i++, idx++){
525 unsigned char res[2];
526 if (odml_get_vstream_id(idx->ckid, res)) {
527 db = mmioFOURCC(res[0], res[1], 'd', 'b');
528 break;
529 }
530 }
531
532 // find first non keyframe
533 for (idx = &((AVIINDEXENTRY *)priv->idx)[0], i=0; i<priv->idx_size; i++, idx++){
534 if (!(idx->dwFlags & AVIIF_KEYFRAME) && idx->ckid == db) break;
535 }
536 if (i<priv->idx_size && db) {
537 stream_seek(demuxer->stream, AVI_IDX_OFFSET(idx));
538 id = stream_read_dword_le(demuxer->stream);
539 if (id && id != db) // index fcc and real fcc differ? fix it.
540 for (idx = &((AVIINDEXENTRY *)priv->idx)[0], i=0; i<priv->idx_size; i++, idx++){
541 if (!(idx->dwFlags & AVIIF_KEYFRAME) && idx->ckid == db)
542 idx->ckid = id;
543 }
544 }
545 }
546
547 if (verbose>=2) print_index(priv->idx, priv->idx_size);
548
549 demuxer->movi_end=demuxer->stream->end_pos;
550
551 freeout:
552
553 // free unneeded stuff
554 cx = &priv->suidx[0];
555 do {
556 for (j=0;j<cx->nEntriesInUse;j++)
557 if (cx->stdidx[j].nEntriesInUse) free(cx->stdidx[j].aIndex);
558 free(cx->stdidx);
559
560 } while (cx++ != &priv->suidx[priv->suidx_size-1]);
561 free(priv->suidx);
562
304 } 563 }
305 564
306 /* Read a saved index file */ 565 /* Read a saved index file */
307 if (index_file_load) { 566 if (index_file_load) {
308 FILE *fp; 567 FILE *fp;
374 if(!priv->idx){priv->idx_pos=0; break;} // error! 633 if(!priv->idx){priv->idx_pos=0; break;} // error!
375 } 634 }
376 idx=&((AVIINDEXENTRY *)priv->idx)[priv->idx_pos++]; 635 idx=&((AVIINDEXENTRY *)priv->idx)[priv->idx_pos++];
377 idx->ckid=id; 636 idx->ckid=id;
378 idx->dwFlags=AVIIF_KEYFRAME; // FIXME 637 idx->dwFlags=AVIIF_KEYFRAME; // FIXME
638 idx->dwFlags|=(demuxer->filepos>>16)&0xffff0000U;
379 idx->dwChunkOffset=(unsigned long)demuxer->filepos; 639 idx->dwChunkOffset=(unsigned long)demuxer->filepos;
380 idx->dwChunkLength=len; 640 idx->dwChunkLength=len;
381 641
382 c=stream_read_dword(demuxer->stream); 642 c=stream_read_dword(demuxer->stream);
383 643
384 // Fix keyframes for DivX files: 644 // Fix keyframes for DivX files:
385 if(idxfix_divx) 645 if(idxfix_divx)
386 if(avi_stream_id(id)==idxfix_videostream){ 646 if(avi_stream_id(id)==idxfix_videostream){
387 switch(idxfix_divx){ 647 switch(idxfix_divx){
388 case 3: c=stream_read_dword(demuxer->stream)<<5; //skip 32+5 bits for m$mpeg4v1 648 case 3: c=stream_read_dword(demuxer->stream)<<5; //skip 32+5 bits for m$mpeg4v1
389 case 1: if(c&0x40000000) idx->dwFlags=0;break; // divx 3 649 case 1: if(c&0x40000000) idx->dwFlags&=~AVIIF_KEYFRAME;break; // divx 3
390 case 2: if(c==0x1B6) idx->dwFlags=0;break; // divx 4 650 case 2: if(c==0x1B6) idx->dwFlags&=~AVIIF_KEYFRAME;break; // divx 4
391 } 651 }
392 } 652 }
393 653
394 // update status line: 654 // update status line:
395 { static off_t lastpos; 655 { static off_t lastpos;