Fix:
[mplayer/glamo.git] / libmpdemux / demux_avi.c
blobbda1f17f9b18aaee2cb6ff8ca238834078f49c33
1 // AVI file parser for DEMUXER v2.9 by A'rpi/ESP-team
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <unistd.h>
7 #include "config.h"
8 #include "mp_msg.h"
9 #include "help_mp.h"
11 #include "stream.h"
12 #include "demuxer.h"
13 #include "stheader.h"
15 #include "aviheader.h"
17 extern demuxer_t* init_avi_with_ogg(demuxer_t* demuxer);
18 extern int demux_ogg_open(demuxer_t* demuxer);
20 extern demuxer_desc_t demuxer_desc_avi_ni;
21 extern demuxer_desc_t demuxer_desc_avi_nini;
23 // PTS: 0=interleaved 1=BPS-based
24 int pts_from_bps=1;
26 // Select ds from ID
27 demux_stream_t* demux_avi_select_stream(demuxer_t *demux,unsigned int id){
28 int stream_id=avi_stream_id(id);
30 // printf("demux_avi_select_stream(%d) {a:%d/v:%d}\n",stream_id,
31 // demux->audio->id,demux->video->id);
33 if(demux->video->id==-1)
34 if(demux->v_streams[stream_id])
35 demux->video->id=stream_id;
37 if(demux->audio->id==-1)
38 if(demux->a_streams[stream_id])
39 demux->audio->id=stream_id;
41 if(stream_id==demux->audio->id){
42 if(!demux->audio->sh){
43 sh_audio_t* sh;
44 avi_priv_t *priv=demux->priv;
45 sh=demux->audio->sh=demux->a_streams[stream_id];
46 mp_msg(MSGT_DEMUX,MSGL_V,"Auto-selected AVI audio ID = %d\n",demux->audio->id);
47 if(sh->wf){
48 priv->audio_block_size=sh->wf->nBlockAlign;
49 if(!priv->audio_block_size){
50 // for PCM audio we can calculate the blocksize:
51 if(sh->format==1)
52 priv->audio_block_size=sh->wf->nChannels*(sh->wf->wBitsPerSample/8);
53 else
54 priv->audio_block_size=1; // hope the best...
55 } else {
56 // workaround old mencoder's bug:
57 if(sh->audio.dwSampleSize==1 && sh->audio.dwScale==1 &&
58 (sh->wf->nBlockAlign==1152 || sh->wf->nBlockAlign==576)){
59 mp_msg(MSGT_DEMUX,MSGL_WARN,"AVI: Workarounding CBR-MP3 nBlockAlign header bug!\n");
60 priv->audio_block_size=1;
63 } else {
64 priv->audio_block_size=sh->audio.dwSampleSize;
66 // printf("&&&&& setting blocksize to %d &&&&&\n",priv->audio_block_size);
68 return demux->audio;
70 if(stream_id==demux->video->id){
71 if(!demux->video->sh){
72 demux->video->sh=demux->v_streams[stream_id];
73 mp_msg(MSGT_DEMUX,MSGL_V,"Auto-selected AVI video ID = %d\n",demux->video->id);
75 return demux->video;
77 if(id!=mmioFOURCC('J','U','N','K')){
78 // unknown
79 mp_msg(MSGT_DEMUX,MSGL_DBG2,"Unknown chunk: %.4s (%X)\n",(char *) &id,id);
80 //abort();
82 return NULL;
85 static int valid_fourcc(unsigned int id){
86 unsigned char* fcc=(unsigned char*)(&id);
87 #define FCC_CHR_CHECK(x) (x<48 || x>=96)
88 if(FCC_CHR_CHECK(fcc[0])) return 0;
89 if(FCC_CHR_CHECK(fcc[1])) return 0;
90 if(FCC_CHR_CHECK(fcc[2])) return 0;
91 if(FCC_CHR_CHECK(fcc[3])) return 0;
92 return 1;
93 #undef FCC_CHR_CHECK
96 static int choose_chunk_len(unsigned int len1,unsigned int len2){
97 // len1 has a bit more priority than len2. len1!=len2
98 // Note: this is a first-idea-logic, may be wrong. comments welcomed.
100 // prefer small frames rather than 0
101 if(!len1) return (len2>0x80000) ? len1 : len2;
102 if(!len2) return (len1>0x100000) ? len2 : len1;
104 // choose the smaller value:
105 return (len1<len2)? len1 : len2;
108 static int demux_avi_read_packet(demuxer_t *demux,demux_stream_t *ds,unsigned int id,unsigned int len,int idxpos,int flags){
109 avi_priv_t *priv=demux->priv;
110 int skip;
111 float pts=0;
113 mp_dbg(MSGT_DEMUX,MSGL_DBG3,"demux_avi.read_packet: %X\n",id);
115 if(ds==demux->audio){
116 if(priv->pts_corrected==0){
117 // printf("\rYYY-A A: %5.3f V: %5.3f \n",priv->avi_audio_pts,priv->avi_video_pts);
118 if(priv->pts_has_video){
119 // we have video pts now
120 float delay=0;
121 if(((sh_audio_t*)(ds->sh))->wf->nAvgBytesPerSec)
122 delay=(float)priv->pts_corr_bytes/((sh_audio_t*)(ds->sh))->wf->nAvgBytesPerSec;
123 mp_msg(MSGT_DEMUX,MSGL_V,"XXX initial v_pts=%5.3f a_pos=%d (%5.3f) \n",priv->avi_audio_pts,priv->pts_corr_bytes,delay);
124 //priv->pts_correction=-priv->avi_audio_pts+delay;
125 priv->pts_correction=delay-priv->avi_audio_pts;
126 priv->avi_audio_pts+=priv->pts_correction;
127 priv->pts_corrected=1;
128 } else
129 priv->pts_corr_bytes+=len;
131 if(pts_from_bps){
132 pts = priv->audio_block_no *
133 (float)((sh_audio_t*)demux->audio->sh)->audio.dwScale /
134 (float)((sh_audio_t*)demux->audio->sh)->audio.dwRate;
135 } else
136 pts=priv->avi_audio_pts; //+priv->pts_correction;
137 priv->avi_audio_pts=0;
138 // update blockcount:
139 priv->audio_block_no+=priv->audio_block_size ?
140 ((len+priv->audio_block_size-1)/priv->audio_block_size) : 1;
141 // printf("\raudio_block_no=%d \n",priv->audio_block_no);
142 } else
143 if(ds==demux->video){
144 // video
145 if(priv->skip_video_frames>0){
146 // drop frame (seeking)
147 --priv->skip_video_frames;
148 ds=NULL;
151 pts = priv->avi_video_pts = priv->video_pack_no *
152 (float)((sh_video_t*)demux->video->sh)->video.dwScale /
153 (float)((sh_video_t*)demux->video->sh)->video.dwRate;
154 // printf("\rYYY-V A: %5.3f V: %5.3f \n",priv->avi_audio_pts,priv->avi_video_pts);
156 priv->avi_audio_pts=priv->avi_video_pts+priv->pts_correction;
157 priv->pts_has_video=1;
159 if(ds) ++priv->video_pack_no;
161 //printf("read pack_no: %d pts %5.3f \n",demux->video->pack_no+demux->video->packs,pts);
164 skip=(len+1)&(~1); // total bytes in this chunk
166 if(ds){
167 mp_dbg(MSGT_DEMUX,MSGL_DBG2,"DEMUX_AVI: Read %d data bytes from packet %04X\n",len,id);
168 // printf("READ[%c] %5.3f (%d) \n",ds==demux->video?'V':'A',pts,len);
169 ds_read_packet(ds,demux->stream,len,pts,idxpos,flags);
170 skip-=len;
172 if(skip){
173 mp_dbg(MSGT_DEMUX,MSGL_DBG2,"DEMUX_AVI: Skipping %d bytes from packet %04X\n",skip,id);
174 stream_skip(demux->stream,skip);
176 return ds?1:0;
179 // return value:
180 // 0 = EOF or no stream found
181 // 1 = successfully read a packet
182 static int demux_avi_fill_buffer(demuxer_t *demux, demux_stream_t *dsds){
183 avi_priv_t *priv=demux->priv;
184 unsigned int id=0;
185 unsigned int len;
186 //int max_packs=128;
187 int ret=0;
188 demux_stream_t *ds;
191 int flags=1;
192 AVIINDEXENTRY *idx=NULL;
193 #if 0
194 demux->filepos=stream_tell(demux->stream);
195 if(demux->filepos>=demux->movi_end){
196 demux->stream->eof=1;
197 return 0;
199 if(stream_eof(demux->stream)) return 0;
200 #endif
201 if(priv->idx_size>0 && priv->idx_pos<priv->idx_size){
202 off_t pos;
204 //if(priv->idx_pos<0) printf("Fatal! idx_pos=%d\n",priv->idx_pos);
206 idx=&((AVIINDEXENTRY *)priv->idx)[priv->idx_pos++];
208 //printf("[%d]",priv->idx_pos);fflush(stdout);
210 //stream_seek(demux->stream,idx.dwChunkOffset);
211 //printf("IDX pos=%X idx.pos=%X idx.size=%X idx.flags=%X\n",demux->filepos,
212 // pos-4,idx->dwChunkLength,idx->dwFlags);
213 if(idx->dwFlags&AVIIF_LIST){
214 // LIST
215 continue;
217 if(!demux_avi_select_stream(demux,idx->ckid)){
218 mp_dbg(MSGT_DEMUX,MSGL_DBG3,"Skip chunk %.4s (0x%X) \n",(char *)&idx->ckid,(unsigned int)idx->ckid);
219 continue; // skip this chunk
222 pos = (off_t)priv->idx_offset+AVI_IDX_OFFSET(idx);
223 if((pos<demux->movi_start || pos>=demux->movi_end) && (demux->movi_end>demux->movi_start) && (demux->stream->flags & STREAM_SEEK)){
224 mp_msg(MSGT_DEMUX,MSGL_V,"ChunkOffset out of range! idx=0x%"PRIX64" \n",(int64_t)pos);
225 continue;
227 #if 0
228 if(pos!=demux->filepos){
229 mp_msg(MSGT_DEMUX,MSGL_V,"Warning! pos=0x%X idx.pos=0x%X diff=%d \n",demux->filepos,pos,pos-demux->filepos);
231 #endif
232 stream_seek(demux->stream,pos);
233 demux->filepos=stream_tell(demux->stream);
234 id=stream_read_dword_le(demux->stream);
235 if(stream_eof(demux->stream)) return 0; // EOF!
237 if(id!=idx->ckid){
238 mp_msg(MSGT_DEMUX,MSGL_V,"ChunkID mismatch! raw=%.4s idx=%.4s \n",(char *)&id,(char *)&idx->ckid);
239 if(valid_fourcc(idx->ckid))
240 id=idx->ckid; // use index if valid
241 else
242 if(!valid_fourcc(id)) continue; // drop chunk if both id and idx bad
244 len=stream_read_dword_le(demux->stream);
245 // if((len&(~1))!=(idx->dwChunkLength&(~1))){
246 // if((len)!=(idx->dwChunkLength)){
247 if((len!=idx->dwChunkLength)&&((len+1)!=idx->dwChunkLength)){
248 mp_msg(MSGT_DEMUX,MSGL_V,"ChunkSize mismatch! raw=%d idx=%d \n",len,idx->dwChunkLength);
249 if(len>0x200000 && idx->dwChunkLength>0x200000) continue; // both values bad :(
250 len=choose_chunk_len(idx->dwChunkLength,len);
252 if(!(idx->dwFlags&AVIIF_KEYFRAME)) flags=0;
253 } else {
254 demux->filepos=stream_tell(demux->stream);
255 if(demux->filepos>=demux->movi_end && demux->movi_end>demux->movi_start && (demux->stream->flags & STREAM_SEEK)){
256 demux->stream->eof=1;
257 return 0;
259 id=stream_read_dword_le(demux->stream);
260 len=stream_read_dword_le(demux->stream);
261 if(stream_eof(demux->stream)) return 0; // EOF!
263 if(id==mmioFOURCC('L','I','S','T') || id==mmioFOURCC('R', 'I', 'F', 'F')){
264 id=stream_read_dword_le(demux->stream); // list or RIFF type
265 continue;
269 ds=demux_avi_select_stream(demux,id);
270 if(ds)
271 if(ds->packs+1>=MAX_PACKS || ds->bytes+len>=MAX_PACK_BYTES){
272 // this packet will cause a buffer overflow, switch to -ni mode!!!
273 mp_msg(MSGT_DEMUX,MSGL_WARN,MSGTR_SwitchToNi);
274 if(priv->idx_size>0){
275 // has index
276 demux->type=DEMUXER_TYPE_AVI_NI;
277 demux->desc=&demuxer_desc_avi_ni;
278 --priv->idx_pos; // hack
279 } else {
280 // no index
281 demux->type=DEMUXER_TYPE_AVI_NINI;
282 demux->desc=&demuxer_desc_avi_nini;
283 priv->idx_pos=demux->filepos; // hack
285 priv->idx_pos_v=priv->idx_pos_a=priv->idx_pos;
286 // quit now, we can't even (no enough buffer memory) read this packet :(
287 return -1;
290 ret=demux_avi_read_packet(demux,ds,id,len,priv->idx_pos-1,flags);
291 // if(!ret && priv->skip_video_frames<=0)
292 // if(--max_packs==0){
293 // demux->stream->eof=1;
294 // mp_msg(MSGT_DEMUX,MSGL_ERR,MSGTR_DoesntContainSelectedStream);
295 // return 0;
296 // }
297 } while(ret!=1);
298 return 1;
302 // return value:
303 // 0 = EOF or no stream found
304 // 1 = successfully read a packet
305 int demux_avi_fill_buffer_ni(demuxer_t *demux,demux_stream_t* ds){
306 avi_priv_t *priv=demux->priv;
307 unsigned int id=0;
308 unsigned int len;
309 //int max_packs=128;
310 int ret=0;
313 int flags=1;
314 AVIINDEXENTRY *idx=NULL;
315 int idx_pos=0;
316 demux->filepos=stream_tell(demux->stream);
318 if(ds==demux->video) idx_pos=priv->idx_pos_v++; else
319 if(ds==demux->audio) idx_pos=priv->idx_pos_a++; else
320 idx_pos=priv->idx_pos++;
322 if(priv->idx_size>0 && idx_pos<priv->idx_size){
323 off_t pos;
324 idx=&((AVIINDEXENTRY *)priv->idx)[idx_pos];
325 // idx=&priv->idx[idx_pos];
327 if(idx->dwFlags&AVIIF_LIST){
328 // LIST
329 continue;
331 if(ds && demux_avi_select_stream(demux,idx->ckid)!=ds){
332 mp_dbg(MSGT_DEMUX,MSGL_DBG3,"Skip chunk %.4s (0x%X) \n",(char *)&idx->ckid,(unsigned int)idx->ckid);
333 continue; // skip this chunk
336 pos = priv->idx_offset+AVI_IDX_OFFSET(idx);
337 if((pos<demux->movi_start || pos>=demux->movi_end) && (demux->movi_end>demux->movi_start)){
338 mp_msg(MSGT_DEMUX,MSGL_V,"ChunkOffset out of range! current=0x%"PRIX64" idx=0x%"PRIX64" \n",(int64_t)demux->filepos,(int64_t)pos);
339 continue;
341 #if 0
342 if(pos!=demux->filepos){
343 mp_msg(MSGT_DEMUX,MSGL_V,"Warning! pos=0x%X idx.pos=0x%X diff=%d \n",demux->filepos,pos,pos-demux->filepos);
345 #endif
346 stream_seek(demux->stream,pos);
348 id=stream_read_dword_le(demux->stream);
350 if(stream_eof(demux->stream)) return 0;
352 if(id!=idx->ckid){
353 mp_msg(MSGT_DEMUX,MSGL_V,"ChunkID mismatch! raw=%.4s idx=%.4s \n",(char *)&id,(char *)&idx->ckid);
354 if(valid_fourcc(idx->ckid))
355 id=idx->ckid; // use index if valid
356 else
357 if(!valid_fourcc(id)) continue; // drop chunk if both id and idx bad
359 len=stream_read_dword_le(demux->stream);
360 if((len!=idx->dwChunkLength)&&((len+1)!=idx->dwChunkLength)){
361 mp_msg(MSGT_DEMUX,MSGL_V,"ChunkSize mismatch! raw=%d idx=%d \n",len,idx->dwChunkLength);
362 if(len>0x200000 && idx->dwChunkLength>0x200000) continue; // both values bad :(
363 len=choose_chunk_len(idx->dwChunkLength,len);
365 if(!(idx->dwFlags&AVIIF_KEYFRAME)) flags=0;
366 } else return 0;
367 ret=demux_avi_read_packet(demux,demux_avi_select_stream(demux,id),id,len,idx_pos,flags);
368 // if(!ret && priv->skip_video_frames<=0)
369 // if(--max_packs==0){
370 // demux->stream->eof=1;
371 // mp_msg(MSGT_DEMUX,MSGL_ERR,MSGTR_DoesntContainSelectedStream);
372 // return 0;
373 // }
374 } while(ret!=1);
375 return 1;
379 // return value:
380 // 0 = EOF or no stream found
381 // 1 = successfully read a packet
382 int demux_avi_fill_buffer_nini(demuxer_t *demux,demux_stream_t* ds){
383 avi_priv_t *priv=demux->priv;
384 unsigned int id=0;
385 unsigned int len;
386 int ret=0;
387 off_t *fpos=NULL;
389 if(ds==demux->video) fpos=&priv->idx_pos_v; else
390 if(ds==demux->audio) fpos=&priv->idx_pos_a; else
391 return 0;
393 stream_seek(demux->stream,fpos[0]);
397 demux->filepos=stream_tell(demux->stream);
398 if(demux->filepos>=demux->movi_end && (demux->movi_end>demux->movi_start)){
399 //demux->stream->eof=1;
400 ds->eof=1;
401 return 0;
404 id=stream_read_dword_le(demux->stream);
405 len=stream_read_dword_le(demux->stream);
407 if(stream_eof(demux->stream)) return 0;
409 if(id==mmioFOURCC('L','I','S','T')){
410 id=stream_read_dword_le(demux->stream); // list type
411 continue;
414 if(id==mmioFOURCC('R','I','F','F')){
415 mp_msg(MSGT_DEMUX,MSGL_V,"additional RIFF header...\n");
416 id=stream_read_dword_le(demux->stream); // "AVIX"
417 continue;
420 if(ds==demux_avi_select_stream(demux,id)){
421 // read it!
422 ret=demux_avi_read_packet(demux,ds,id,len,priv->idx_pos-1,0);
423 } else {
424 // skip it!
425 int skip=(len+1)&(~1); // total bytes in this chunk
426 stream_skip(demux->stream,skip);
429 } while(ret!=1);
430 fpos[0]=stream_tell(demux->stream);
431 return 1;
434 // AVI demuxer parameters:
435 int index_mode=-1; // -1=untouched 0=don't use index 1=use (geneate) index
436 char *index_file_save = NULL, *index_file_load = NULL;
437 int force_ni=0; // force non-interleaved AVI parsing
439 void read_avi_header(demuxer_t *demuxer,int index_mode);
441 static demuxer_t* demux_open_avi(demuxer_t* demuxer){
442 demux_stream_t *d_audio=demuxer->audio;
443 demux_stream_t *d_video=demuxer->video;
444 sh_audio_t *sh_audio=NULL;
445 sh_video_t *sh_video=NULL;
446 avi_priv_t* priv=malloc(sizeof(avi_priv_t));
448 // priv struct:
449 priv->avi_audio_pts=priv->avi_video_pts=0.0f;
450 priv->pts_correction=0.0f;
451 priv->skip_video_frames=0;
452 priv->pts_corr_bytes=0;
453 priv->pts_has_video=priv->pts_corrected=0;
454 priv->video_pack_no=0;
455 priv->audio_block_no=0;
456 priv->audio_block_size=0;
457 priv->isodml = 0;
458 priv->suidx_size = 0;
459 priv->suidx = NULL;
461 demuxer->priv=(void*)priv;
463 //---- AVI header:
464 read_avi_header(demuxer,(demuxer->stream->flags & STREAM_SEEK_BW)?index_mode:-2);
466 if(demuxer->audio->id>=0 && !demuxer->a_streams[demuxer->audio->id]){
467 mp_msg(MSGT_DEMUX,MSGL_WARN,"AVI: invalid audio stream ID: %d - ignoring (nosound)\n",demuxer->audio->id);
468 demuxer->audio->id=-2; // disabled
470 if(demuxer->video->id>=0 && !demuxer->v_streams[demuxer->video->id]){
471 mp_msg(MSGT_DEMUX,MSGL_WARN,"AVI: invalid video stream ID: %d - ignoring (using default)\n",demuxer->video->id);
472 demuxer->video->id=-1; // autodetect
475 stream_reset(demuxer->stream);
476 stream_seek(demuxer->stream,demuxer->movi_start);
477 priv->idx_pos=0;
478 priv->idx_pos_a=0;
479 priv->idx_pos_v=0;
480 if(priv->idx_size>1){
481 // decide index format:
482 #if 1
483 if((AVI_IDX_OFFSET(&((AVIINDEXENTRY *)priv->idx)[0])<demuxer->movi_start ||
484 AVI_IDX_OFFSET(&((AVIINDEXENTRY *)priv->idx)[1])<demuxer->movi_start )&& !priv->isodml)
485 priv->idx_offset=demuxer->movi_start-4;
486 else
487 priv->idx_offset=0;
488 #else
489 if(AVI_IDX_OFFSET(&((AVIINDEXENTRY *)priv->idx)[0])<demuxer->movi_start)
490 priv->idx_offset=demuxer->movi_start-4;
491 else
492 priv->idx_offset=0;
493 #endif
494 mp_msg(MSGT_DEMUX,MSGL_V,"AVI index offset: 0x%X (movi=0x%X idx0=0x%X idx1=0x%X)\n",
495 (int)priv->idx_offset,(int)demuxer->movi_start,
496 (int)((AVIINDEXENTRY *)priv->idx)[0].dwChunkOffset,
497 (int)((AVIINDEXENTRY *)priv->idx)[1].dwChunkOffset);
499 // demuxer->endpos=avi_header.movi_end;
501 if(priv->idx_size>0){
502 // check that file is non-interleaved:
503 int i;
504 off_t a_pos=-1;
505 off_t v_pos=-1;
506 for(i=0;i<priv->idx_size;i++){
507 AVIINDEXENTRY* idx=&((AVIINDEXENTRY *)priv->idx)[i];
508 demux_stream_t* ds=demux_avi_select_stream(demuxer,idx->ckid);
509 off_t pos = priv->idx_offset + AVI_IDX_OFFSET(idx);
510 if(a_pos==-1 && ds==demuxer->audio){
511 a_pos=pos;
512 if(v_pos!=-1) break;
514 if(v_pos==-1 && ds==demuxer->video){
515 v_pos=pos;
516 if(a_pos!=-1) break;
519 if(v_pos==-1){
520 mp_msg(MSGT_DEMUX,MSGL_ERR,"AVI_NI: " MSGTR_MissingVideoStream);
521 return NULL;
523 if(a_pos==-1){
524 mp_msg(MSGT_DEMUX,MSGL_INFO,"AVI_NI: " MSGTR_MissingAudioStream);
525 sh_audio=NULL;
526 } else {
527 if(force_ni || abs(a_pos-v_pos)>0x100000){ // distance > 1MB
528 mp_msg(MSGT_DEMUX,MSGL_INFO,MSGTR_NI_Message,force_ni?MSGTR_NI_Forced:MSGTR_NI_Detected);
529 demuxer->type=DEMUXER_TYPE_AVI_NI; // HACK!!!!
530 demuxer->desc=&demuxer_desc_avi_ni; // HACK!!!!
531 pts_from_bps=1; // force BPS sync!
534 } else {
535 // no index
536 if(force_ni){
537 mp_msg(MSGT_DEMUX,MSGL_INFO,MSGTR_UsingNINI);
538 demuxer->type=DEMUXER_TYPE_AVI_NINI; // HACK!!!!
539 demuxer->desc=&demuxer_desc_avi_nini; // HACK!!!!
540 priv->idx_pos_a=
541 priv->idx_pos_v=demuxer->movi_start;
542 pts_from_bps=1; // force BPS sync!
544 demuxer->seekable=0;
546 if(!ds_fill_buffer(d_video)){
547 mp_msg(MSGT_DEMUX,MSGL_ERR,"AVI: " MSGTR_MissingVideoStreamBug);
548 return NULL;
550 sh_video=d_video->sh;sh_video->ds=d_video;
551 if(d_audio->id!=-2){
552 mp_msg(MSGT_DEMUX,MSGL_V,"AVI: Searching for audio stream (id:%d)\n",d_audio->id);
553 if(!priv->audio_streams || !ds_fill_buffer(d_audio)){
554 mp_msg(MSGT_DEMUX,MSGL_INFO,"AVI: " MSGTR_MissingAudioStream);
555 sh_audio=NULL;
556 } else {
557 sh_audio=d_audio->sh;sh_audio->ds=d_audio;
558 sh_audio->format=sh_audio->wf->wFormatTag;
561 // calc. FPS:
562 sh_video->fps=(float)sh_video->video.dwRate/(float)sh_video->video.dwScale;
563 sh_video->frametime=(float)sh_video->video.dwScale/(float)sh_video->video.dwRate;
565 // calculating audio/video bitrate:
566 if(priv->idx_size>0){
567 // we have index, let's count 'em!
568 int64_t vsize=0;
569 int64_t asize=0;
570 size_t vsamples=0;
571 size_t asamples=0;
572 int i;
573 for(i=0;i<priv->idx_size;i++){
574 int id=avi_stream_id(((AVIINDEXENTRY *)priv->idx)[i].ckid);
575 int len=((AVIINDEXENTRY *)priv->idx)[i].dwChunkLength;
576 if(sh_video->ds->id == id) {
577 vsize+=len;
578 ++vsamples;
580 else if(sh_audio && sh_audio->ds->id == id) {
581 asize+=len;
582 asamples+=(len+priv->audio_block_size-1)/priv->audio_block_size;
585 mp_msg(MSGT_DEMUX,MSGL_V,"AVI video size=%"PRId64" (%u) audio size=%"PRId64" (%u)\n",vsize,vsamples,asize,asamples);
586 priv->numberofframes=vsamples;
587 sh_video->i_bps=((float)vsize/(float)vsamples)*(float)sh_video->video.dwRate/(float)sh_video->video.dwScale;
588 if(sh_audio) sh_audio->i_bps=((float)asize/(float)asamples)*(float)sh_audio->audio.dwRate/(float)sh_audio->audio.dwScale;
589 } else {
590 // guessing, results may be inaccurate:
591 int64_t vsize;
592 int64_t asize=0;
594 if((priv->numberofframes=sh_video->video.dwLength)<=1)
595 // bad video header, try to get number of frames from audio
596 if(sh_audio && sh_audio->wf->nAvgBytesPerSec) priv->numberofframes=sh_video->fps*sh_audio->audio.dwLength/sh_audio->audio.dwRate*sh_audio->audio.dwScale;
597 if(priv->numberofframes<=1){
598 mp_msg(MSGT_SEEK,MSGL_WARN,MSGTR_CouldntDetFNo);
599 priv->numberofframes=0;
602 if(sh_audio){
603 if(sh_audio->wf->nAvgBytesPerSec && sh_audio->audio.dwSampleSize!=1){
604 asize=(float)sh_audio->wf->nAvgBytesPerSec*sh_audio->audio.dwLength*sh_audio->audio.dwScale/sh_audio->audio.dwRate;
605 sh_audio->i_bps=sh_audio->wf->nAvgBytesPerSec;
606 } else {
607 asize=sh_audio->audio.dwLength;
608 sh_audio->i_bps=(float)asize/(sh_video->frametime*priv->numberofframes);
611 vsize=demuxer->movi_end-demuxer->movi_start-asize-8*priv->numberofframes;
612 mp_msg(MSGT_DEMUX,MSGL_V,"AVI video size=%"PRId64" (%u) audio size=%"PRId64"\n",vsize,priv->numberofframes,asize);
613 sh_video->i_bps=(float)vsize/(sh_video->frametime*priv->numberofframes);
616 if (sh_video)
617 sh_video->stream_delay = (float)sh_video->video.dwStart * sh_video->video.dwScale/sh_video->video.dwRate;
618 if (sh_audio)
619 sh_audio->stream_delay = (float)sh_audio->audio.dwStart * sh_audio->audio.dwScale/sh_audio->audio.dwRate;
621 return demuxer;
625 //extern float initial_pts_delay;
627 void demux_seek_avi(demuxer_t *demuxer,float rel_seek_secs,float audio_delay,int flags){
628 avi_priv_t *priv=demuxer->priv;
629 demux_stream_t *d_audio=demuxer->audio;
630 demux_stream_t *d_video=demuxer->video;
631 sh_audio_t *sh_audio=d_audio->sh;
632 sh_video_t *sh_video=d_video->sh;
633 float skip_audio_secs=0;
635 //FIXME: OFF_T - Didn't check AVI case yet (avi files can't be >2G anyway?)
636 //================= seek in AVI ==========================
637 int rel_seek_frames=rel_seek_secs*sh_video->fps;
638 int video_chunk_pos=d_video->pos;
639 int i;
641 if(flags&1){
642 // seek absolute
643 video_chunk_pos=0;
646 if(flags&2){
647 rel_seek_frames=rel_seek_secs*priv->numberofframes;
650 priv->skip_video_frames=0;
651 priv->avi_audio_pts=0;
653 // ------------ STEP 1: find nearest video keyframe chunk ------------
654 // find nearest video keyframe chunk pos:
655 if(rel_seek_frames>0){
656 // seek forward
657 while(video_chunk_pos<priv->idx_size-1){
658 int id=((AVIINDEXENTRY *)priv->idx)[video_chunk_pos].ckid;
659 if(avi_stream_id(id)==d_video->id){ // video frame
660 if((--rel_seek_frames)<0 && ((AVIINDEXENTRY *)priv->idx)[video_chunk_pos].dwFlags&AVIIF_KEYFRAME) break;
662 ++video_chunk_pos;
664 } else {
665 // seek backward
666 while(video_chunk_pos>0){
667 int id=((AVIINDEXENTRY *)priv->idx)[video_chunk_pos].ckid;
668 if(avi_stream_id(id)==d_video->id){ // video frame
669 if((++rel_seek_frames)>0 && ((AVIINDEXENTRY *)priv->idx)[video_chunk_pos].dwFlags&AVIIF_KEYFRAME) break;
671 --video_chunk_pos;
674 priv->idx_pos_a=priv->idx_pos_v=priv->idx_pos=video_chunk_pos;
676 // re-calc video pts:
677 d_video->pack_no=0;
678 for(i=0;i<video_chunk_pos;i++){
679 int id=((AVIINDEXENTRY *)priv->idx)[i].ckid;
680 if(avi_stream_id(id)==d_video->id) ++d_video->pack_no;
682 priv->video_pack_no=
683 sh_video->num_frames=sh_video->num_frames_decoded=d_video->pack_no;
684 priv->avi_video_pts=d_video->pack_no*(float)sh_video->video.dwScale/(float)sh_video->video.dwRate;
685 d_video->pos=video_chunk_pos;
687 mp_msg(MSGT_SEEK,MSGL_DBG2,"V_SEEK: pack=%d pts=%5.3f chunk=%d \n",d_video->pack_no,priv->avi_video_pts,video_chunk_pos);
689 // ------------ STEP 2: seek audio, find the right chunk & pos ------------
691 d_audio->pack_no=0;
692 // d_audio->block_no=0;
693 priv->audio_block_no=0;
694 d_audio->dpos=0;
696 if(sh_audio){
697 int i;
698 int len=0;
699 int skip_audio_bytes=0;
700 int curr_audio_pos=-1;
701 int audio_chunk_pos=-1;
702 int chunk_max=(demuxer->type==DEMUXER_TYPE_AVI)?video_chunk_pos:priv->idx_size;
704 if(sh_audio->audio.dwSampleSize){
705 // constant rate audio stream
706 #if 0
707 int align;
708 curr_audio_pos=(priv->avi_video_pts) * sh_audio->wf->nAvgBytesPerSec;
709 if(curr_audio_pos<0)curr_audio_pos=0;
710 align=sh_audio->audio.dwSampleSize;
711 if(sh_audio->wf->nBlockAlign>align) align=sh_audio->wf->nBlockAlign;
712 curr_audio_pos/=align;
713 curr_audio_pos*=align;
714 #else
715 /* immediate seeking to audio position, including when streams are delayed */
716 curr_audio_pos=(priv->avi_video_pts + audio_delay)*(float)sh_audio->audio.dwRate/(float)sh_audio->audio.dwScale;
717 curr_audio_pos*=sh_audio->audio.dwSampleSize;
718 #endif
720 // find audio chunk pos:
721 for(i=0;i<chunk_max;i++){
722 int id=((AVIINDEXENTRY *)priv->idx)[i].ckid;
723 if(avi_stream_id(id)==d_audio->id){
724 len=((AVIINDEXENTRY *)priv->idx)[i].dwChunkLength;
725 if(d_audio->dpos<=curr_audio_pos && curr_audio_pos<(d_audio->dpos+len)){
726 break;
728 ++d_audio->pack_no;
729 priv->audio_block_no+=priv->audio_block_size ?
730 ((len+priv->audio_block_size-1)/priv->audio_block_size) : 1;
731 d_audio->dpos+=len;
734 audio_chunk_pos=i;
735 skip_audio_bytes=curr_audio_pos-d_audio->dpos;
737 mp_msg(MSGT_SEEK,MSGL_V,"SEEK: i=%d (max:%d) dpos=%d (wanted:%d) \n",
738 i,chunk_max,(int)d_audio->dpos,curr_audio_pos);
740 } else {
741 // VBR audio
742 /* immediate seeking to audio position, including when streams are delayed */
743 int chunks=(priv->avi_video_pts + audio_delay)*(float)sh_audio->audio.dwRate/(float)sh_audio->audio.dwScale;
744 audio_chunk_pos=0;
746 // find audio chunk pos:
747 for(i=0;i<priv->idx_size && chunks>0;i++){
748 int id=((AVIINDEXENTRY *)priv->idx)[i].ckid;
749 if(avi_stream_id(id)==d_audio->id){
750 len=((AVIINDEXENTRY *)priv->idx)[i].dwChunkLength;
751 if(i>chunk_max){
752 skip_audio_bytes+=len;
753 } else {
754 ++d_audio->pack_no;
755 priv->audio_block_no+=priv->audio_block_size ?
756 ((len+priv->audio_block_size-1)/priv->audio_block_size) : 1;
757 d_audio->dpos+=len;
758 audio_chunk_pos=i;
760 // --chunks;
761 if(priv->audio_block_size)
762 chunks-=(len+priv->audio_block_size-1)/priv->audio_block_size;
765 //if(audio_chunk_pos>chunk_max) audio_chunk_pos=chunk_max;
767 // printf("VBR seek: %5.3f -> chunk_no %d -> chunk_idx %d + skip %d \n",
768 // priv->avi_video_pts, audio_chunk_pos, );
772 // Now we have:
773 // audio_chunk_pos = chunk no in index table (it's <=chunk_max)
774 // skip_audio_bytes = bytes to be skipped after chunk seek
775 // d-audio->pack_no = chunk_no in stream at audio_chunk_pos
776 // d_audio->dpos = bytepos in stream at audio_chunk_pos
777 // let's seek!
779 // update stream position:
780 d_audio->pos=audio_chunk_pos;
781 // d_audio->dpos=apos;
782 // d_audio->pts=initial_pts_delay+(float)apos/(float)sh_audio->wf->nAvgBytesPerSec;
784 if(demuxer->type==DEMUXER_TYPE_AVI){
785 // interleaved stream:
786 if(audio_chunk_pos<video_chunk_pos){
787 // calc priv->skip_video_frames & adjust video pts counter:
788 for(i=audio_chunk_pos;i<video_chunk_pos;i++){
789 int id=((AVIINDEXENTRY *)priv->idx)[i].ckid;
790 if(avi_stream_id(id)==d_video->id) ++priv->skip_video_frames;
792 // requires for correct audio pts calculation (demuxer):
793 priv->avi_video_pts-=priv->skip_video_frames*(float)sh_video->video.dwScale/(float)sh_video->video.dwRate;
794 priv->avi_audio_pts=priv->avi_video_pts;
795 // set index position:
796 priv->idx_pos_a=priv->idx_pos_v=priv->idx_pos=audio_chunk_pos;
798 } else {
799 // non-interleaved stream:
800 priv->idx_pos_a=audio_chunk_pos;
801 priv->idx_pos_v=video_chunk_pos;
802 priv->idx_pos=(audio_chunk_pos<video_chunk_pos)?audio_chunk_pos:video_chunk_pos;
807 mp_msg(MSGT_SEEK,MSGL_V,"SEEK: idx=%d (a:%d v:%d) v.skip=%d a.skip=%d/%4.3f \n",
808 (int)priv->idx_pos,audio_chunk_pos,video_chunk_pos,
809 (int)priv->skip_video_frames,skip_audio_bytes,skip_audio_secs);
811 if(skip_audio_bytes){
812 demux_read_data(d_audio,NULL,skip_audio_bytes);
813 //d_audio->pts=0; // PTS is outdated because of the raw data skipping
816 // sh_audio->timer=-skip_audio_secs;
819 d_video->pts=priv->avi_video_pts; // OSD
824 void demux_close_avi(demuxer_t *demuxer) {
825 avi_priv_t* priv=demuxer->priv;
827 if(!priv)
828 return;
830 if(priv->idx_size > 0)
831 free(priv->idx);
832 free(priv);
836 static int demux_avi_control(demuxer_t *demuxer,int cmd, void *arg){
837 avi_priv_t *priv=demuxer->priv;
838 /* demux_stream_t *d_audio=demuxer->audio;*/
839 demux_stream_t *d_video=demuxer->video;
840 /* sh_audio_t *sh_audio=d_audio->sh;*/
841 sh_video_t *sh_video=d_video->sh;
845 switch(cmd) {
846 case DEMUXER_CTRL_GET_TIME_LENGTH:
847 if (!priv->numberofframes || !sh_video) return DEMUXER_CTRL_DONTKNOW;
848 *((double *)arg)=(double)priv->numberofframes/sh_video->fps;
849 if (sh_video->video.dwLength<=1) return DEMUXER_CTRL_GUESS;
850 return DEMUXER_CTRL_OK;
852 case DEMUXER_CTRL_GET_PERCENT_POS:
853 if (!priv->numberofframes || !sh_video) {
854 return DEMUXER_CTRL_DONTKNOW;
856 *((int *)arg)=(int)(priv->video_pack_no*100/priv->numberofframes);
857 if (sh_video->video.dwLength<=1) return DEMUXER_CTRL_GUESS;
858 return DEMUXER_CTRL_OK;
860 default:
861 return DEMUXER_CTRL_NOTIMPL;
866 static int avi_check_file(demuxer_t *demuxer)
868 int id=stream_read_dword_le(demuxer->stream); // "RIFF"
870 if((id==mmioFOURCC('R','I','F','F')) || (id==mmioFOURCC('O','N','2',' '))) {
871 stream_read_dword_le(demuxer->stream); //filesize
872 id=stream_read_dword_le(demuxer->stream); // "AVI "
873 if(id==formtypeAVI)
874 return DEMUXER_TYPE_AVI;
875 if(id==mmioFOURCC('O','N','2','f')){
876 mp_msg(MSGT_DEMUXER,MSGL_INFO,"ON2 AVI format");
877 return DEMUXER_TYPE_AVI;
881 return 0;
885 static demuxer_t* demux_open_hack_avi(demuxer_t *demuxer)
887 sh_audio_t* sh_a;
889 demuxer = (demuxer_t*) demux_open_avi(demuxer);
890 if(!demuxer) return NULL; // failed to open
891 sh_a = (sh_audio_t*)demuxer->audio->sh;
892 if(demuxer->audio->id != -2 && sh_a) {
893 #ifdef HAVE_OGGVORBIS
894 // support for Ogg-in-AVI:
895 if(sh_a->format == 0xFFFE)
896 demuxer = init_avi_with_ogg(demuxer);
897 else if(sh_a->format == 0x674F) {
898 stream_t* s;
899 demuxer_t *od;
900 s = new_ds_stream(demuxer->audio);
901 od = new_demuxer(s,DEMUXER_TYPE_OGG,-1,-2,-2,NULL);
902 if(!demux_ogg_open(od)) {
903 mp_msg( MSGT_DEMUXER,MSGL_ERR,MSGTR_ErrorOpeningOGGDemuxer);
904 free_stream(s);
905 demuxer->audio->id = -2;
906 } else
907 demuxer = new_demuxers_demuxer(demuxer,od,demuxer);
909 #endif
912 return demuxer;
916 demuxer_desc_t demuxer_desc_avi = {
917 "AVI demuxer",
918 "avi",
919 "AVI",
920 "Arpi?",
921 "AVI files, including non interleaved files",
922 DEMUXER_TYPE_AVI,
923 1, // safe autodetect
924 avi_check_file,
925 demux_avi_fill_buffer,
926 demux_open_hack_avi,
927 demux_close_avi,
928 demux_seek_avi,
929 demux_avi_control
932 demuxer_desc_t demuxer_desc_avi_ni = {
933 "AVI demuxer, non-interleaved",
934 "avini",
935 "AVI",
936 "Arpi?",
937 "AVI files, including non interleaved files",
938 DEMUXER_TYPE_AVI,
939 1, // safe autodetect
940 avi_check_file,
941 demux_avi_fill_buffer_ni,
942 demux_open_hack_avi,
943 demux_close_avi,
944 demux_seek_avi,
945 demux_avi_control
948 demuxer_desc_t demuxer_desc_avi_nini = {
949 "AVI demuxer, non-interleaved and no index",
950 "avinini",
951 "AVI",
952 "Arpi?",
953 "AVI files, including non interleaved files",
954 DEMUXER_TYPE_AVI,
955 1, // safe autodetect
956 avi_check_file,
957 demux_avi_fill_buffer_nini,
958 demux_open_hack_avi,
959 demux_close_avi,
960 demux_seek_avi,
961 demux_avi_control