void print_index(AVIINDEXENTRY *idx, int idx_size, int verbose_level){ int i; unsigned int pos[256]; unsigned int num[256]; for(i=0;i<256;i++) num[i]=pos[i]=0; for(i=0;i<idx_size;i++){ int id=avi_stream_id(idx[i].ckid); if(id<0 || id>255) id=255; mp_msg(MSGT_HEADER, verbose_level, "%5d: %.4s %4X %016llX len:%6ld pos:%7d->%7.3f %7d->%7.3f\n",i, (char *)&idx[i].ckid, (unsigned int)idx[i].dwFlags&0xffff, (uint64_t)AVI_IDX_OFFSET(&idx[i]), // idx[i].dwChunkOffset+demuxer->movi_start, idx[i].dwChunkLength, pos[id],(float)pos[id]/18747.0f, num[id],(float)num[id]/23.976f ); pos[id]+=idx[i].dwChunkLength; ++num[id]; } }
static demuxer_t* demux_open_avi(demuxer_t* demuxer){ demux_stream_t *d_audio=demuxer->audio; demux_stream_t *d_video=demuxer->video; sh_audio_t *sh_audio=NULL; sh_video_t *sh_video=NULL; avi_priv_t* priv=calloc(1, sizeof(avi_priv_t)); demuxer->priv=(void*)priv; //---- AVI header: read_avi_header(demuxer,(demuxer->stream->flags & MP_STREAM_SEEK_BW)?index_mode:-2); if(demuxer->audio->id>=0 && !demuxer->a_streams[demuxer->audio->id]){ mp_tmsg(MSGT_DEMUX,MSGL_WARN,"AVI: invalid audio stream ID: %d - ignoring (nosound)\n",demuxer->audio->id); demuxer->audio->id=-2; // disabled } if(demuxer->video->id>=0 && !demuxer->v_streams[demuxer->video->id]){ mp_tmsg(MSGT_DEMUX,MSGL_WARN,"AVI: invalid video stream ID: %d - ignoring (using default)\n",demuxer->video->id); demuxer->video->id=-1; // autodetect } stream_reset(demuxer->stream); stream_seek(demuxer->stream,demuxer->movi_start); if(priv->idx_size>1){ // decide index format: #if 1 if((AVI_IDX_OFFSET(&((AVIINDEXENTRY *)priv->idx)[0])<demuxer->movi_start || AVI_IDX_OFFSET(&((AVIINDEXENTRY *)priv->idx)[1])<demuxer->movi_start )&& !priv->isodml) priv->idx_offset=demuxer->movi_start-4; #else if(AVI_IDX_OFFSET(&((AVIINDEXENTRY *)priv->idx)[0])<demuxer->movi_start) priv->idx_offset=demuxer->movi_start-4; #endif mp_msg(MSGT_DEMUX,MSGL_V,"AVI index offset: 0x%X (movi=0x%X idx0=0x%X idx1=0x%X)\n", (int)priv->idx_offset,(int)demuxer->movi_start, (int)((AVIINDEXENTRY *)priv->idx)[0].dwChunkOffset, (int)((AVIINDEXENTRY *)priv->idx)[1].dwChunkOffset); } if(priv->idx_size>0){ // check that file is non-interleaved: int i; off_t a_pos=-1; off_t v_pos=-1; for(i=0;i<priv->idx_size;i++){ AVIINDEXENTRY* idx=&((AVIINDEXENTRY *)priv->idx)[i]; demux_stream_t* ds=demux_avi_select_stream(demuxer,idx->ckid); off_t pos = priv->idx_offset + AVI_IDX_OFFSET(idx); if(a_pos==-1 && ds==demuxer->audio){ a_pos=pos; if(v_pos!=-1) break; } if(v_pos==-1 && ds==demuxer->video){ v_pos=pos; if(a_pos!=-1) break; } } if(v_pos==-1){ mp_msg(MSGT_DEMUX, MSGL_ERR, "AVI_NI: %s", mp_gtext("No video stream found.\n")); return NULL; } if(a_pos==-1){ d_audio->sh=sh_audio=NULL; } else { if(force_ni || abs(a_pos-v_pos)>0x100000){ // distance > 1MB mp_tmsg(MSGT_DEMUX,MSGL_INFO,"%s NON-INTERLEAVED AVI file format.\n",force_ni?"Forced":"Detected"); demuxer->type=DEMUXER_TYPE_AVI_NI; // HACK!!!! demuxer->desc=&demuxer_desc_avi_ni; // HACK!!!! pts_from_bps=1; // force BPS sync! } } } else { // no index if(force_ni){ mp_tmsg(MSGT_DEMUX,MSGL_INFO,"Using NON-INTERLEAVED broken AVI file format.\n"); demuxer->type=DEMUXER_TYPE_AVI_NINI; // HACK!!!! demuxer->desc=&demuxer_desc_avi_nini; // HACK!!!! priv->idx_pos_a= priv->idx_pos_v=demuxer->movi_start; pts_from_bps=1; // force BPS sync! } demuxer->seekable=0; } if(!ds_fill_buffer(d_video)){ mp_msg(MSGT_DEMUX, MSGL_ERR, "AVI: %s", mp_gtext("Missing video stream!? Contact the author, " "it may be a bug :(\n")); return NULL; } sh_video=d_video->sh;sh_video->ds=d_video; if(d_audio->id!=-2){ mp_msg(MSGT_DEMUX,MSGL_V,"AVI: Searching for audio stream (id:%d)\n",d_audio->id); if(!priv->audio_streams || !ds_fill_buffer(d_audio)){ mp_msg(MSGT_DEMUX, MSGL_INFO, "AVI: %s", mp_gtext("No audio stream found -> no sound.\n")); d_audio->sh=sh_audio=NULL; } else { sh_audio=d_audio->sh;sh_audio->ds=d_audio; } } // calculating audio/video bitrate: if(priv->idx_size>0){ // we have index, let's count 'em! AVIINDEXENTRY *idx = priv->idx; int64_t vsize=0; int64_t asize=0; size_t vsamples=0; size_t asamples=0; int i; for(i=0;i<priv->idx_size;i++){ int id=avi_stream_id(idx[i].ckid); unsigned len=idx[i].dwChunkLength; if(sh_video->ds->id == id) { vsize+=len; ++vsamples; } else if(sh_audio && sh_audio->ds->id == id) { asize+=len; asamples+=(len+priv->audio_block_size-1)/priv->audio_block_size; } } mp_msg(MSGT_DEMUX,MSGL_V,"AVI video size=%"PRId64" (%u) audio size=%"PRId64" (%u)\n",vsize,vsamples,asize,asamples); priv->numberofframes=vsamples; sh_video->i_bps=((float)vsize/(float)vsamples)*(float)sh_video->video.dwRate/(float)sh_video->video.dwScale; if(sh_audio) sh_audio->i_bps=((float)asize/(float)asamples)*(float)sh_audio->audio.dwRate/(float)sh_audio->audio.dwScale; } else { // guessing, results may be inaccurate: int64_t vsize; int64_t asize=0; if((priv->numberofframes=sh_video->video.dwLength)<=1) // bad video header, try to get number of frames from audio if(sh_audio && sh_audio->wf->nAvgBytesPerSec) priv->numberofframes=sh_video->fps*sh_audio->audio.dwLength/sh_audio->audio.dwRate*sh_audio->audio.dwScale; if(priv->numberofframes<=1){ mp_tmsg(MSGT_SEEK,MSGL_WARN,"Could not determine number of frames (for absolute seek).\n"); priv->numberofframes=0; } if(sh_audio){ if(sh_audio->wf->nAvgBytesPerSec && sh_audio->audio.dwSampleSize!=1){ asize=(float)sh_audio->wf->nAvgBytesPerSec*sh_audio->audio.dwLength*sh_audio->audio.dwScale/sh_audio->audio.dwRate; } else { asize=sh_audio->audio.dwLength; sh_audio->i_bps=(float)asize/(sh_video->frametime*priv->numberofframes); } } vsize=demuxer->movi_end-demuxer->movi_start-asize-8*priv->numberofframes; mp_msg(MSGT_DEMUX,MSGL_V,"AVI video size=%"PRId64" (%u) audio size=%"PRId64"\n",vsize,priv->numberofframes,asize); sh_video->i_bps=(float)vsize/(sh_video->frametime*priv->numberofframes); } return demuxer; }
// return value: // 0 = EOF or no stream found // 1 = successfully read a packet static int demux_avi_fill_buffer_ni(demuxer_t *demux, demux_stream_t *ds) { avi_priv_t *priv=demux->priv; unsigned int id=0; unsigned int len; int ret=0; do{ int flags=1; AVIINDEXENTRY *idx=NULL; int idx_pos=0; demux->filepos=stream_tell(demux->stream); if(ds==demux->video) idx_pos=priv->idx_pos_v++; else if(ds==demux->audio) idx_pos=priv->idx_pos_a++; else idx_pos=priv->idx_pos++; if(priv->idx_size>0 && idx_pos<priv->idx_size){ off_t pos; idx=&((AVIINDEXENTRY *)priv->idx)[idx_pos]; if(idx->dwFlags&AVIIF_LIST){ if (!valid_stream_id(idx->ckid)) // LIST continue; if (!priv->warned_unaligned) mp_msg(MSGT_DEMUX, MSGL_WARN, "Looks like unaligned chunk in index, broken AVI file!\n"); priv->warned_unaligned = 1; } if(ds && demux_avi_select_stream(demux,idx->ckid)!=ds){ mp_dbg(MSGT_DEMUX,MSGL_DBG3,"Skip chunk %.4s (0x%X) \n",(char *)&idx->ckid,(unsigned int)idx->ckid); continue; // skip this chunk } pos = priv->idx_offset+AVI_IDX_OFFSET(idx); if((pos<demux->movi_start || pos>=demux->movi_end) && (demux->movi_end>demux->movi_start)){ mp_msg(MSGT_DEMUX,MSGL_V,"ChunkOffset out of range! current=0x%"PRIX64" idx=0x%"PRIX64" \n",(int64_t)demux->filepos,(int64_t)pos); continue; } stream_seek(demux->stream,pos); id=stream_read_dword_le(demux->stream); if(stream_eof(demux->stream)) return 0; if(id!=idx->ckid){ mp_msg(MSGT_DEMUX,MSGL_V,"ChunkID mismatch! raw=%.4s idx=%.4s \n",(char *)&id,(char *)&idx->ckid); if(valid_fourcc(idx->ckid)) id=idx->ckid; // use index if valid else if(!valid_fourcc(id)) continue; // drop chunk if both id and idx bad } len=stream_read_dword_le(demux->stream); if((len!=idx->dwChunkLength)&&((len+1)!=idx->dwChunkLength)){ mp_msg(MSGT_DEMUX,MSGL_V,"ChunkSize mismatch! raw=%d idx=%d \n",len,idx->dwChunkLength); if(len>0x200000 && idx->dwChunkLength>0x200000) continue; // both values bad :( len=choose_chunk_len(idx->dwChunkLength,len); } if(!(idx->dwFlags&AVIIF_KEYFRAME)) flags=0; } else return 0; ret=demux_avi_read_packet(demux,demux_avi_select_stream(demux,id),id,len,idx_pos,flags); } while(ret!=1); return 1; }
// return value: // 0 = EOF or no stream found // 1 = successfully read a packet static int demux_avi_fill_buffer(demuxer_t *demux, demux_stream_t *dsds){ avi_priv_t *priv=demux->priv; unsigned int id=0; unsigned int len; int ret=0; demux_stream_t *ds; do{ int flags=1; AVIINDEXENTRY *idx=NULL; if(priv->idx_size>0 && priv->idx_pos<priv->idx_size){ off_t pos; idx=&((AVIINDEXENTRY *)priv->idx)[priv->idx_pos++]; if(idx->dwFlags&AVIIF_LIST){ if (!valid_stream_id(idx->ckid)) // LIST continue; if (!priv->warned_unaligned) mp_msg(MSGT_DEMUX, MSGL_WARN, "Looks like unaligned chunk in index, broken AVI file!\n"); priv->warned_unaligned = 1; } if(!demux_avi_select_stream(demux,idx->ckid)){ mp_dbg(MSGT_DEMUX,MSGL_DBG3,"Skip chunk %.4s (0x%X) \n",(char *)&idx->ckid,(unsigned int)idx->ckid); continue; // skip this chunk } pos = (off_t)priv->idx_offset+AVI_IDX_OFFSET(idx); if((pos<demux->movi_start || pos>=demux->movi_end) && (demux->movi_end>demux->movi_start) && (demux->stream->flags & MP_STREAM_SEEK)){ mp_msg(MSGT_DEMUX,MSGL_V,"ChunkOffset out of range! idx=0x%"PRIX64" \n",(int64_t)pos); continue; } stream_seek(demux->stream,pos); demux->filepos=stream_tell(demux->stream); id=stream_read_dword_le(demux->stream); if(stream_eof(demux->stream)) return 0; // EOF! if(id!=idx->ckid){ mp_msg(MSGT_DEMUX,MSGL_V,"ChunkID mismatch! raw=%.4s idx=%.4s \n",(char *)&id,(char *)&idx->ckid); if(valid_fourcc(idx->ckid)) id=idx->ckid; // use index if valid else if(!valid_fourcc(id)) continue; // drop chunk if both id and idx bad } len=stream_read_dword_le(demux->stream); if((len!=idx->dwChunkLength)&&((len+1)!=idx->dwChunkLength)){ mp_msg(MSGT_DEMUX,MSGL_V,"ChunkSize mismatch! raw=%d idx=%d \n",len,idx->dwChunkLength); if(len>0x200000 && idx->dwChunkLength>0x200000) continue; // both values bad :( len=choose_chunk_len(idx->dwChunkLength,len); } if(!(idx->dwFlags&AVIIF_KEYFRAME)) flags=0; } else { demux->filepos=stream_tell(demux->stream); if(demux->filepos>=demux->movi_end && demux->movi_end>demux->movi_start && (demux->stream->flags & MP_STREAM_SEEK)){ demux->stream->eof=1; return 0; } id=avi_find_id(demux->stream); len=stream_read_dword_le(demux->stream); if(stream_eof(demux->stream)) return 0; // EOF! if(id==mmioFOURCC('L','I','S','T') || id==mmioFOURCC('R', 'I', 'F', 'F')){ id=stream_read_dword_le(demux->stream); // list or RIFF type continue; } } ds=demux_avi_select_stream(demux,id); if(ds) if(ds->packs+1>=MAX_PACKS || ds->bytes+len>=MAX_PACK_BYTES){ // this packet will cause a buffer overflow, switch to -ni mode!!! mp_tmsg(MSGT_DEMUX,MSGL_WARN,"\nBadly interleaved AVI file detected - switching to -ni mode...\n"); if(priv->idx_size>0){ // has index demux->type=DEMUXER_TYPE_AVI_NI; demux->desc=&demuxer_desc_avi_ni; --priv->idx_pos; // hack } else { // no index demux->type=DEMUXER_TYPE_AVI_NINI; demux->desc=&demuxer_desc_avi_nini; priv->idx_pos=demux->filepos; // hack } priv->idx_pos_v=priv->idx_pos_a=priv->idx_pos; // quit now, we can't even (no enough buffer memory) read this packet :( return -1; } ret=demux_avi_read_packet(demux,ds,id,len,priv->idx_pos-1,flags); } while(ret!=1); return 1; }
int avi_idx_cmp(const void *elem1,const void *elem2) { register off_t a = AVI_IDX_OFFSET((AVIINDEXENTRY *)elem1); register off_t b = AVI_IDX_OFFSET((AVIINDEXENTRY *)elem2); return (a > b) - (b > a); }