Mercurial > mplayer.hg
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; |