Make various functions static
[mplayer.git] / libmpdemux / demux_avi.c
blobea0774c553167be3035ba1f8d9730f8fc7c167b1
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/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 const demuxer_desc_t demuxer_desc_avi_ni;
21 extern const 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 static demux_stream_t* demux_avi_select_stream(demuxer_t *demux,
28 unsigned int id)
30 int stream_id=avi_stream_id(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,MSGTR_WorkAroundBlockAlignHeaderBug);
60 priv->audio_block_size=1;
63 } else {
64 priv->audio_block_size=sh->audio.dwSampleSize;
67 return demux->audio;
69 if(stream_id==demux->video->id){
70 if(!demux->video->sh){
71 demux->video->sh=demux->v_streams[stream_id];
72 mp_msg(MSGT_DEMUX,MSGL_V,"Auto-selected AVI video ID = %d\n",demux->video->id);
74 return demux->video;
76 if(id!=mmioFOURCC('J','U','N','K')){
77 // unknown
78 mp_msg(MSGT_DEMUX,MSGL_DBG2,"Unknown chunk: %.4s (%X)\n",(char *) &id,id);
79 //abort();
81 return NULL;
84 static int valid_fourcc(unsigned int id){
85 static const char valid[] = "0123456789abcdefghijklmnopqrstuvwxyz"
86 "ABCDEFGHIJKLMNOPQRSTUVWXYZ_";
87 unsigned char* fcc=(unsigned char*)(&id);
88 return strchr(valid, fcc[0]) && strchr(valid, fcc[1]) &&
89 strchr(valid, fcc[2]) && strchr(valid, fcc[3]);
92 static int choose_chunk_len(unsigned int len1,unsigned int len2){
93 // len1 has a bit more priority than len2. len1!=len2
94 // Note: this is a first-idea-logic, may be wrong. comments welcomed.
96 // prefer small frames rather than 0
97 if(!len1) return (len2>0x80000) ? len1 : len2;
98 if(!len2) return (len1>0x100000) ? len2 : len1;
100 // choose the smaller value:
101 return (len1<len2)? len1 : len2;
104 static int demux_avi_read_packet(demuxer_t *demux,demux_stream_t *ds,unsigned int id,unsigned int len,int idxpos,int flags){
105 avi_priv_t *priv=demux->priv;
106 int skip;
107 float pts=0;
109 mp_dbg(MSGT_DEMUX,MSGL_DBG3,"demux_avi.read_packet: %X\n",id);
111 if(ds==demux->audio){
112 if(priv->pts_corrected==0){
113 if(priv->pts_has_video){
114 // we have video pts now
115 float delay=0;
116 if(((sh_audio_t*)(ds->sh))->wf->nAvgBytesPerSec)
117 delay=(float)priv->pts_corr_bytes/((sh_audio_t*)(ds->sh))->wf->nAvgBytesPerSec;
118 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);
119 //priv->pts_correction=-priv->avi_audio_pts+delay;
120 priv->pts_correction=delay-priv->avi_audio_pts;
121 priv->avi_audio_pts+=priv->pts_correction;
122 priv->pts_corrected=1;
123 } else
124 priv->pts_corr_bytes+=len;
126 if(pts_from_bps){
127 pts = priv->audio_block_no *
128 (float)((sh_audio_t*)demux->audio->sh)->audio.dwScale /
129 (float)((sh_audio_t*)demux->audio->sh)->audio.dwRate;
130 } else
131 pts=priv->avi_audio_pts; //+priv->pts_correction;
132 priv->avi_audio_pts=0;
133 // update blockcount:
134 priv->audio_block_no+=priv->audio_block_size ?
135 ((len+priv->audio_block_size-1)/priv->audio_block_size) : 1;
136 } else
137 if(ds==demux->video){
138 // video
139 if(priv->skip_video_frames>0){
140 // drop frame (seeking)
141 --priv->skip_video_frames;
142 ds=NULL;
145 pts = priv->avi_video_pts = priv->video_pack_no *
146 (float)((sh_video_t*)demux->video->sh)->video.dwScale /
147 (float)((sh_video_t*)demux->video->sh)->video.dwRate;
149 priv->avi_audio_pts=priv->avi_video_pts+priv->pts_correction;
150 priv->pts_has_video=1;
152 if(ds) ++priv->video_pack_no;
156 skip=(len+1)&(~1); // total bytes in this chunk
158 if(ds){
159 mp_dbg(MSGT_DEMUX,MSGL_DBG2,"DEMUX_AVI: Read %d data bytes from packet %04X\n",len,id);
160 ds_read_packet(ds,demux->stream,len,pts,idxpos,flags);
161 skip-=len;
163 if(skip){
164 mp_dbg(MSGT_DEMUX,MSGL_DBG2,"DEMUX_AVI: Skipping %d bytes from packet %04X\n",skip,id);
165 stream_skip(demux->stream,skip);
167 return ds?1:0;
170 // return value:
171 // 0 = EOF or no stream found
172 // 1 = successfully read a packet
173 static int demux_avi_fill_buffer(demuxer_t *demux, demux_stream_t *dsds){
174 avi_priv_t *priv=demux->priv;
175 unsigned int id=0;
176 unsigned int len;
177 int ret=0;
178 demux_stream_t *ds;
181 int flags=1;
182 AVIINDEXENTRY *idx=NULL;
183 if(priv->idx_size>0 && priv->idx_pos<priv->idx_size){
184 off_t pos;
186 idx=&((AVIINDEXENTRY *)priv->idx)[priv->idx_pos++];
188 if(idx->dwFlags&AVIIF_LIST){
189 // LIST
190 continue;
192 if(!demux_avi_select_stream(demux,idx->ckid)){
193 mp_dbg(MSGT_DEMUX,MSGL_DBG3,"Skip chunk %.4s (0x%X) \n",(char *)&idx->ckid,(unsigned int)idx->ckid);
194 continue; // skip this chunk
197 pos = (off_t)priv->idx_offset+AVI_IDX_OFFSET(idx);
198 if((pos<demux->movi_start || pos>=demux->movi_end) && (demux->movi_end>demux->movi_start) && (demux->stream->flags & STREAM_SEEK)){
199 mp_msg(MSGT_DEMUX,MSGL_V,"ChunkOffset out of range! idx=0x%"PRIX64" \n",(int64_t)pos);
200 continue;
202 stream_seek(demux->stream,pos);
203 demux->filepos=stream_tell(demux->stream);
204 id=stream_read_dword_le(demux->stream);
205 if(stream_eof(demux->stream)) return 0; // EOF!
207 if(id!=idx->ckid){
208 mp_msg(MSGT_DEMUX,MSGL_V,"ChunkID mismatch! raw=%.4s idx=%.4s \n",(char *)&id,(char *)&idx->ckid);
209 if(valid_fourcc(idx->ckid))
210 id=idx->ckid; // use index if valid
211 else
212 if(!valid_fourcc(id)) continue; // drop chunk if both id and idx bad
214 len=stream_read_dword_le(demux->stream);
215 if((len!=idx->dwChunkLength)&&((len+1)!=idx->dwChunkLength)){
216 mp_msg(MSGT_DEMUX,MSGL_V,"ChunkSize mismatch! raw=%d idx=%d \n",len,idx->dwChunkLength);
217 if(len>0x200000 && idx->dwChunkLength>0x200000) continue; // both values bad :(
218 len=choose_chunk_len(idx->dwChunkLength,len);
220 if(!(idx->dwFlags&AVIIF_KEYFRAME)) flags=0;
221 } else {
222 demux->filepos=stream_tell(demux->stream);
223 if(demux->filepos>=demux->movi_end && demux->movi_end>demux->movi_start && (demux->stream->flags & STREAM_SEEK)){
224 demux->stream->eof=1;
225 return 0;
227 id=stream_read_dword_le(demux->stream);
228 len=stream_read_dword_le(demux->stream);
229 if(stream_eof(demux->stream)) return 0; // EOF!
231 if(id==mmioFOURCC('L','I','S','T') || id==mmioFOURCC('R', 'I', 'F', 'F')){
232 id=stream_read_dword_le(demux->stream); // list or RIFF type
233 continue;
237 ds=demux_avi_select_stream(demux,id);
238 if(ds)
239 if(ds->packs+1>=MAX_PACKS || ds->bytes+len>=MAX_PACK_BYTES){
240 // this packet will cause a buffer overflow, switch to -ni mode!!!
241 mp_msg(MSGT_DEMUX,MSGL_WARN,MSGTR_SwitchToNi);
242 if(priv->idx_size>0){
243 // has index
244 demux->type=DEMUXER_TYPE_AVI_NI;
245 demux->desc=&demuxer_desc_avi_ni;
246 --priv->idx_pos; // hack
247 } else {
248 // no index
249 demux->type=DEMUXER_TYPE_AVI_NINI;
250 demux->desc=&demuxer_desc_avi_nini;
251 priv->idx_pos=demux->filepos; // hack
253 priv->idx_pos_v=priv->idx_pos_a=priv->idx_pos;
254 // quit now, we can't even (no enough buffer memory) read this packet :(
255 return -1;
258 ret=demux_avi_read_packet(demux,ds,id,len,priv->idx_pos-1,flags);
259 } while(ret!=1);
260 return 1;
264 // return value:
265 // 0 = EOF or no stream found
266 // 1 = successfully read a packet
267 static int demux_avi_fill_buffer_ni(demuxer_t *demux,demux_stream_t* ds){
268 avi_priv_t *priv=demux->priv;
269 unsigned int id=0;
270 unsigned int len;
271 int ret=0;
274 int flags=1;
275 AVIINDEXENTRY *idx=NULL;
276 int idx_pos=0;
277 demux->filepos=stream_tell(demux->stream);
279 if(ds==demux->video) idx_pos=priv->idx_pos_v++; else
280 if(ds==demux->audio) idx_pos=priv->idx_pos_a++; else
281 idx_pos=priv->idx_pos++;
283 if(priv->idx_size>0 && idx_pos<priv->idx_size){
284 off_t pos;
285 idx=&((AVIINDEXENTRY *)priv->idx)[idx_pos];
287 if(idx->dwFlags&AVIIF_LIST){
288 // LIST
289 continue;
291 if(ds && demux_avi_select_stream(demux,idx->ckid)!=ds){
292 mp_dbg(MSGT_DEMUX,MSGL_DBG3,"Skip chunk %.4s (0x%X) \n",(char *)&idx->ckid,(unsigned int)idx->ckid);
293 continue; // skip this chunk
296 pos = priv->idx_offset+AVI_IDX_OFFSET(idx);
297 if((pos<demux->movi_start || pos>=demux->movi_end) && (demux->movi_end>demux->movi_start)){
298 mp_msg(MSGT_DEMUX,MSGL_V,"ChunkOffset out of range! current=0x%"PRIX64" idx=0x%"PRIX64" \n",(int64_t)demux->filepos,(int64_t)pos);
299 continue;
301 stream_seek(demux->stream,pos);
303 id=stream_read_dword_le(demux->stream);
305 if(stream_eof(demux->stream)) return 0;
307 if(id!=idx->ckid){
308 mp_msg(MSGT_DEMUX,MSGL_V,"ChunkID mismatch! raw=%.4s idx=%.4s \n",(char *)&id,(char *)&idx->ckid);
309 if(valid_fourcc(idx->ckid))
310 id=idx->ckid; // use index if valid
311 else
312 if(!valid_fourcc(id)) continue; // drop chunk if both id and idx bad
314 len=stream_read_dword_le(demux->stream);
315 if((len!=idx->dwChunkLength)&&((len+1)!=idx->dwChunkLength)){
316 mp_msg(MSGT_DEMUX,MSGL_V,"ChunkSize mismatch! raw=%d idx=%d \n",len,idx->dwChunkLength);
317 if(len>0x200000 && idx->dwChunkLength>0x200000) continue; // both values bad :(
318 len=choose_chunk_len(idx->dwChunkLength,len);
320 if(!(idx->dwFlags&AVIIF_KEYFRAME)) flags=0;
321 } else return 0;
322 ret=demux_avi_read_packet(demux,demux_avi_select_stream(demux,id),id,len,idx_pos,flags);
323 } while(ret!=1);
324 return 1;
328 // return value:
329 // 0 = EOF or no stream found
330 // 1 = successfully read a packet
331 static int demux_avi_fill_buffer_nini(demuxer_t *demux,demux_stream_t* ds){
332 avi_priv_t *priv=demux->priv;
333 unsigned int id=0;
334 unsigned int len;
335 int ret=0;
336 off_t *fpos=NULL;
338 if(ds==demux->video) fpos=&priv->idx_pos_v; else
339 if(ds==demux->audio) fpos=&priv->idx_pos_a; else
340 return 0;
342 stream_seek(demux->stream,fpos[0]);
346 demux->filepos=stream_tell(demux->stream);
347 if(demux->filepos>=demux->movi_end && (demux->movi_end>demux->movi_start)){
348 ds->eof=1;
349 return 0;
352 id=stream_read_dword_le(demux->stream);
353 len=stream_read_dword_le(demux->stream);
355 if(stream_eof(demux->stream)) return 0;
357 if(id==mmioFOURCC('L','I','S','T')){
358 id=stream_read_dword_le(demux->stream); // list type
359 continue;
362 if(id==mmioFOURCC('R','I','F','F')){
363 mp_msg(MSGT_DEMUX,MSGL_V,"additional RIFF header...\n");
364 id=stream_read_dword_le(demux->stream); // "AVIX"
365 continue;
368 if(ds==demux_avi_select_stream(demux,id)){
369 // read it!
370 ret=demux_avi_read_packet(demux,ds,id,len,priv->idx_pos-1,0);
371 } else {
372 // skip it!
373 int skip=(len+1)&(~1); // total bytes in this chunk
374 stream_skip(demux->stream,skip);
377 } while(ret!=1);
378 fpos[0]=stream_tell(demux->stream);
379 return 1;
382 // AVI demuxer parameters:
383 int index_mode=-1; // -1=untouched 0=don't use index 1=use (generate) index
384 char *index_file_save = NULL, *index_file_load = NULL;
385 int force_ni=0; // force non-interleaved AVI parsing
387 void read_avi_header(demuxer_t *demuxer,int index_mode);
389 static demuxer_t* demux_open_avi(demuxer_t* demuxer){
390 demux_stream_t *d_audio=demuxer->audio;
391 demux_stream_t *d_video=demuxer->video;
392 sh_audio_t *sh_audio=NULL;
393 sh_video_t *sh_video=NULL;
394 avi_priv_t* priv=malloc(sizeof(avi_priv_t));
396 // priv struct:
397 priv->avi_audio_pts=priv->avi_video_pts=0.0f;
398 priv->pts_correction=0.0f;
399 priv->skip_video_frames=0;
400 priv->pts_corr_bytes=0;
401 priv->pts_has_video=priv->pts_corrected=0;
402 priv->video_pack_no=0;
403 priv->audio_block_no=0;
404 priv->audio_block_size=0;
405 priv->isodml = 0;
406 priv->suidx_size = 0;
407 priv->suidx = NULL;
409 demuxer->priv=(void*)priv;
411 //---- AVI header:
412 read_avi_header(demuxer,(demuxer->stream->flags & STREAM_SEEK_BW)?index_mode:-2);
414 if(demuxer->audio->id>=0 && !demuxer->a_streams[demuxer->audio->id]){
415 mp_msg(MSGT_DEMUX,MSGL_WARN,MSGTR_InvalidAudioStreamNosound,demuxer->audio->id);
416 demuxer->audio->id=-2; // disabled
418 if(demuxer->video->id>=0 && !demuxer->v_streams[demuxer->video->id]){
419 mp_msg(MSGT_DEMUX,MSGL_WARN,MSGTR_InvalidAudioStreamUsingDefault,demuxer->video->id);
420 demuxer->video->id=-1; // autodetect
423 stream_reset(demuxer->stream);
424 stream_seek(demuxer->stream,demuxer->movi_start);
425 priv->idx_pos=0;
426 priv->idx_pos_a=0;
427 priv->idx_pos_v=0;
428 if(priv->idx_size>1){
429 // decide index format:
430 #if 1
431 if((AVI_IDX_OFFSET(&((AVIINDEXENTRY *)priv->idx)[0])<demuxer->movi_start ||
432 AVI_IDX_OFFSET(&((AVIINDEXENTRY *)priv->idx)[1])<demuxer->movi_start )&& !priv->isodml)
433 priv->idx_offset=demuxer->movi_start-4;
434 else
435 priv->idx_offset=0;
436 #else
437 if(AVI_IDX_OFFSET(&((AVIINDEXENTRY *)priv->idx)[0])<demuxer->movi_start)
438 priv->idx_offset=demuxer->movi_start-4;
439 else
440 priv->idx_offset=0;
441 #endif
442 mp_msg(MSGT_DEMUX,MSGL_V,"AVI index offset: 0x%X (movi=0x%X idx0=0x%X idx1=0x%X)\n",
443 (int)priv->idx_offset,(int)demuxer->movi_start,
444 (int)((AVIINDEXENTRY *)priv->idx)[0].dwChunkOffset,
445 (int)((AVIINDEXENTRY *)priv->idx)[1].dwChunkOffset);
448 if(priv->idx_size>0){
449 // check that file is non-interleaved:
450 int i;
451 off_t a_pos=-1;
452 off_t v_pos=-1;
453 for(i=0;i<priv->idx_size;i++){
454 AVIINDEXENTRY* idx=&((AVIINDEXENTRY *)priv->idx)[i];
455 demux_stream_t* ds=demux_avi_select_stream(demuxer,idx->ckid);
456 off_t pos = priv->idx_offset + AVI_IDX_OFFSET(idx);
457 if(a_pos==-1 && ds==demuxer->audio){
458 a_pos=pos;
459 if(v_pos!=-1) break;
461 if(v_pos==-1 && ds==demuxer->video){
462 v_pos=pos;
463 if(a_pos!=-1) break;
466 if(v_pos==-1){
467 mp_msg(MSGT_DEMUX,MSGL_ERR,"AVI_NI: " MSGTR_MissingVideoStream);
468 return NULL;
470 if(a_pos==-1){
471 d_audio->sh=sh_audio=NULL;
472 } else {
473 if(force_ni || abs(a_pos-v_pos)>0x100000){ // distance > 1MB
474 mp_msg(MSGT_DEMUX,MSGL_INFO,MSGTR_NI_Message,force_ni?MSGTR_NI_Forced:MSGTR_NI_Detected);
475 demuxer->type=DEMUXER_TYPE_AVI_NI; // HACK!!!!
476 demuxer->desc=&demuxer_desc_avi_ni; // HACK!!!!
477 pts_from_bps=1; // force BPS sync!
480 } else {
481 // no index
482 if(force_ni){
483 mp_msg(MSGT_DEMUX,MSGL_INFO,MSGTR_UsingNINI);
484 demuxer->type=DEMUXER_TYPE_AVI_NINI; // HACK!!!!
485 demuxer->desc=&demuxer_desc_avi_nini; // HACK!!!!
486 priv->idx_pos_a=
487 priv->idx_pos_v=demuxer->movi_start;
488 pts_from_bps=1; // force BPS sync!
490 demuxer->seekable=0;
492 if(!ds_fill_buffer(d_video)){
493 mp_msg(MSGT_DEMUX,MSGL_ERR,"AVI: " MSGTR_MissingVideoStreamBug);
494 return NULL;
496 sh_video=d_video->sh;sh_video->ds=d_video;
497 if(d_audio->id!=-2){
498 mp_msg(MSGT_DEMUX,MSGL_V,"AVI: Searching for audio stream (id:%d)\n",d_audio->id);
499 if(!priv->audio_streams || !ds_fill_buffer(d_audio)){
500 mp_msg(MSGT_DEMUX,MSGL_INFO,"AVI: " MSGTR_MissingAudioStream);
501 d_audio->sh=sh_audio=NULL;
502 } else {
503 sh_audio=d_audio->sh;sh_audio->ds=d_audio;
507 // calculating audio/video bitrate:
508 if(priv->idx_size>0){
509 // we have index, let's count 'em!
510 int64_t vsize=0;
511 int64_t asize=0;
512 size_t vsamples=0;
513 size_t asamples=0;
514 int i;
515 for(i=0;i<priv->idx_size;i++){
516 int id=avi_stream_id(((AVIINDEXENTRY *)priv->idx)[i].ckid);
517 int len=((AVIINDEXENTRY *)priv->idx)[i].dwChunkLength;
518 if(sh_video->ds->id == id) {
519 vsize+=len;
520 ++vsamples;
522 else if(sh_audio && sh_audio->ds->id == id) {
523 asize+=len;
524 asamples+=(len+priv->audio_block_size-1)/priv->audio_block_size;
527 mp_msg(MSGT_DEMUX,MSGL_V,"AVI video size=%"PRId64" (%u) audio size=%"PRId64" (%u)\n",vsize,vsamples,asize,asamples);
528 priv->numberofframes=vsamples;
529 sh_video->i_bps=((float)vsize/(float)vsamples)*(float)sh_video->video.dwRate/(float)sh_video->video.dwScale;
530 if(sh_audio) sh_audio->i_bps=((float)asize/(float)asamples)*(float)sh_audio->audio.dwRate/(float)sh_audio->audio.dwScale;
531 } else {
532 // guessing, results may be inaccurate:
533 int64_t vsize;
534 int64_t asize=0;
536 if((priv->numberofframes=sh_video->video.dwLength)<=1)
537 // bad video header, try to get number of frames from audio
538 if(sh_audio && sh_audio->wf->nAvgBytesPerSec) priv->numberofframes=sh_video->fps*sh_audio->audio.dwLength/sh_audio->audio.dwRate*sh_audio->audio.dwScale;
539 if(priv->numberofframes<=1){
540 mp_msg(MSGT_SEEK,MSGL_WARN,MSGTR_CouldntDetFNo);
541 priv->numberofframes=0;
544 if(sh_audio){
545 if(sh_audio->wf->nAvgBytesPerSec && sh_audio->audio.dwSampleSize!=1){
546 asize=(float)sh_audio->wf->nAvgBytesPerSec*sh_audio->audio.dwLength*sh_audio->audio.dwScale/sh_audio->audio.dwRate;
547 } else {
548 asize=sh_audio->audio.dwLength;
549 sh_audio->i_bps=(float)asize/(sh_video->frametime*priv->numberofframes);
552 vsize=demuxer->movi_end-demuxer->movi_start-asize-8*priv->numberofframes;
553 mp_msg(MSGT_DEMUX,MSGL_V,"AVI video size=%"PRId64" (%u) audio size=%"PRId64"\n",vsize,priv->numberofframes,asize);
554 sh_video->i_bps=(float)vsize/(sh_video->frametime*priv->numberofframes);
557 return demuxer;
562 static void demux_seek_avi(demuxer_t *demuxer,float rel_seek_secs,float audio_delay,int flags){
563 avi_priv_t *priv=demuxer->priv;
564 demux_stream_t *d_audio=demuxer->audio;
565 demux_stream_t *d_video=demuxer->video;
566 sh_audio_t *sh_audio=d_audio->sh;
567 sh_video_t *sh_video=d_video->sh;
568 float skip_audio_secs=0;
570 //FIXME: OFF_T - Didn't check AVI case yet (avi files can't be >2G anyway?)
571 //================= seek in AVI ==========================
572 int rel_seek_frames=rel_seek_secs*sh_video->fps;
573 int video_chunk_pos=d_video->pos;
574 int i;
576 if(flags&SEEK_ABSOLUTE){
577 // seek absolute
578 video_chunk_pos=0;
581 if(flags&SEEK_FACTOR){
582 rel_seek_frames=rel_seek_secs*priv->numberofframes;
585 priv->skip_video_frames=0;
586 priv->avi_audio_pts=0;
588 // ------------ STEP 1: find nearest video keyframe chunk ------------
589 // find nearest video keyframe chunk pos:
590 if(rel_seek_frames>0){
591 // seek forward
592 while(video_chunk_pos<priv->idx_size-1){
593 int id=((AVIINDEXENTRY *)priv->idx)[video_chunk_pos].ckid;
594 if(avi_stream_id(id)==d_video->id){ // video frame
595 if((--rel_seek_frames)<0 && ((AVIINDEXENTRY *)priv->idx)[video_chunk_pos].dwFlags&AVIIF_KEYFRAME) break;
597 ++video_chunk_pos;
599 } else {
600 // seek backward
601 while(video_chunk_pos>0){
602 int id=((AVIINDEXENTRY *)priv->idx)[video_chunk_pos].ckid;
603 if(avi_stream_id(id)==d_video->id){ // video frame
604 if((++rel_seek_frames)>0 && ((AVIINDEXENTRY *)priv->idx)[video_chunk_pos].dwFlags&AVIIF_KEYFRAME) break;
606 --video_chunk_pos;
609 priv->idx_pos_a=priv->idx_pos_v=priv->idx_pos=video_chunk_pos;
611 // re-calc video pts:
612 d_video->pack_no=0;
613 for(i=0;i<video_chunk_pos;i++){
614 int id=((AVIINDEXENTRY *)priv->idx)[i].ckid;
615 if(avi_stream_id(id)==d_video->id) ++d_video->pack_no;
617 priv->video_pack_no=
618 sh_video->num_frames=sh_video->num_frames_decoded=d_video->pack_no;
619 priv->avi_video_pts=d_video->pack_no*(float)sh_video->video.dwScale/(float)sh_video->video.dwRate;
620 d_video->pos=video_chunk_pos;
622 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);
624 // ------------ STEP 2: seek audio, find the right chunk & pos ------------
626 d_audio->pack_no=0;
627 priv->audio_block_no=0;
628 d_audio->dpos=0;
630 if(sh_audio){
631 int i;
632 int len=0;
633 int skip_audio_bytes=0;
634 int curr_audio_pos=-1;
635 int audio_chunk_pos=-1;
636 int chunk_max=(demuxer->type==DEMUXER_TYPE_AVI)?video_chunk_pos:priv->idx_size;
638 if(sh_audio->audio.dwSampleSize){
639 // constant rate audio stream
640 /* immediate seeking to audio position, including when streams are delayed */
641 curr_audio_pos=(priv->avi_video_pts + audio_delay)*(float)sh_audio->audio.dwRate/(float)sh_audio->audio.dwScale;
642 curr_audio_pos*=sh_audio->audio.dwSampleSize;
644 // find audio chunk pos:
645 for(i=0;i<chunk_max;i++){
646 int id=((AVIINDEXENTRY *)priv->idx)[i].ckid;
647 if(avi_stream_id(id)==d_audio->id){
648 len=((AVIINDEXENTRY *)priv->idx)[i].dwChunkLength;
649 if(d_audio->dpos<=curr_audio_pos && curr_audio_pos<(d_audio->dpos+len)){
650 break;
652 ++d_audio->pack_no;
653 priv->audio_block_no+=priv->audio_block_size ?
654 ((len+priv->audio_block_size-1)/priv->audio_block_size) : 1;
655 d_audio->dpos+=len;
658 audio_chunk_pos=i;
659 skip_audio_bytes=curr_audio_pos-d_audio->dpos;
661 mp_msg(MSGT_SEEK,MSGL_V,"SEEK: i=%d (max:%d) dpos=%d (wanted:%d) \n",
662 i,chunk_max,(int)d_audio->dpos,curr_audio_pos);
664 } else {
665 // VBR audio
666 /* immediate seeking to audio position, including when streams are delayed */
667 int chunks=(priv->avi_video_pts + audio_delay)*(float)sh_audio->audio.dwRate/(float)sh_audio->audio.dwScale;
668 audio_chunk_pos=0;
670 // find audio chunk pos:
671 for(i=0;i<priv->idx_size && chunks>0;i++){
672 int id=((AVIINDEXENTRY *)priv->idx)[i].ckid;
673 if(avi_stream_id(id)==d_audio->id){
674 len=((AVIINDEXENTRY *)priv->idx)[i].dwChunkLength;
675 if(i>chunk_max){
676 skip_audio_bytes+=len;
677 } else {
678 ++d_audio->pack_no;
679 priv->audio_block_no+=priv->audio_block_size ?
680 ((len+priv->audio_block_size-1)/priv->audio_block_size) : 1;
681 d_audio->dpos+=len;
682 audio_chunk_pos=i;
684 if(priv->audio_block_size)
685 chunks-=(len+priv->audio_block_size-1)/priv->audio_block_size;
690 // Now we have:
691 // audio_chunk_pos = chunk no in index table (it's <=chunk_max)
692 // skip_audio_bytes = bytes to be skipped after chunk seek
693 // d-audio->pack_no = chunk_no in stream at audio_chunk_pos
694 // d_audio->dpos = bytepos in stream at audio_chunk_pos
695 // let's seek!
697 // update stream position:
698 d_audio->pos=audio_chunk_pos;
700 if(demuxer->type==DEMUXER_TYPE_AVI){
701 // interleaved stream:
702 if(audio_chunk_pos<video_chunk_pos){
703 // calc priv->skip_video_frames & adjust video pts counter:
704 for(i=audio_chunk_pos;i<video_chunk_pos;i++){
705 int id=((AVIINDEXENTRY *)priv->idx)[i].ckid;
706 if(avi_stream_id(id)==d_video->id) ++priv->skip_video_frames;
708 // requires for correct audio pts calculation (demuxer):
709 priv->avi_video_pts-=priv->skip_video_frames*(float)sh_video->video.dwScale/(float)sh_video->video.dwRate;
710 priv->avi_audio_pts=priv->avi_video_pts;
711 // set index position:
712 priv->idx_pos_a=priv->idx_pos_v=priv->idx_pos=audio_chunk_pos;
714 } else {
715 // non-interleaved stream:
716 priv->idx_pos_a=audio_chunk_pos;
717 priv->idx_pos_v=video_chunk_pos;
718 priv->idx_pos=(audio_chunk_pos<video_chunk_pos)?audio_chunk_pos:video_chunk_pos;
721 mp_msg(MSGT_SEEK,MSGL_V,"SEEK: idx=%d (a:%d v:%d) v.skip=%d a.skip=%d/%4.3f \n",
722 (int)priv->idx_pos,audio_chunk_pos,video_chunk_pos,
723 (int)priv->skip_video_frames,skip_audio_bytes,skip_audio_secs);
725 if(skip_audio_bytes){
726 demux_read_data(d_audio,NULL,skip_audio_bytes);
730 d_video->pts=priv->avi_video_pts; // OSD
735 static void demux_close_avi(demuxer_t *demuxer) {
736 avi_priv_t* priv=demuxer->priv;
738 if(!priv)
739 return;
741 if(priv->idx_size > 0)
742 free(priv->idx);
743 free(priv);
747 static int demux_avi_control(demuxer_t *demuxer,int cmd, void *arg){
748 avi_priv_t *priv=demuxer->priv;
749 demux_stream_t *d_video=demuxer->video;
750 sh_video_t *sh_video=d_video->sh;
752 switch(cmd) {
753 case DEMUXER_CTRL_GET_TIME_LENGTH:
754 if (!priv->numberofframes || !sh_video) return DEMUXER_CTRL_DONTKNOW;
755 *((double *)arg)=(double)priv->numberofframes/sh_video->fps;
756 if (sh_video->video.dwLength<=1) return DEMUXER_CTRL_GUESS;
757 return DEMUXER_CTRL_OK;
759 case DEMUXER_CTRL_GET_PERCENT_POS:
760 if (!priv->numberofframes || !sh_video) {
761 return DEMUXER_CTRL_DONTKNOW;
763 *((int *)arg)=(int)(priv->video_pack_no*100/priv->numberofframes);
764 if (sh_video->video.dwLength<=1) return DEMUXER_CTRL_GUESS;
765 return DEMUXER_CTRL_OK;
767 case DEMUXER_CTRL_SWITCH_AUDIO:
768 case DEMUXER_CTRL_SWITCH_VIDEO: {
769 int audio = (cmd == DEMUXER_CTRL_SWITCH_AUDIO);
770 demux_stream_t *ds = audio ? demuxer->audio : demuxer->video;
771 void **streams = audio ? demuxer->a_streams : demuxer->v_streams;
772 int maxid = FFMIN(100, audio ? MAX_A_STREAMS : MAX_V_STREAMS);
773 int chunkid;
774 if (ds->id < -1)
775 return DEMUXER_CTRL_NOTIMPL;
777 if (*(int *)arg >= 0)
778 ds->id = *(int *)arg;
779 else {
780 int i;
781 for (i = 0; i < maxid; i++) {
782 if (++ds->id >= maxid) ds->id = 0;
783 if (streams[ds->id]) break;
787 chunkid = (ds->id / 10 + '0') | (ds->id % 10 + '0') << 8;
788 ds->sh = NULL;
789 if (!streams[ds->id]) // stream not available
790 ds->id = -1;
791 else
792 demux_avi_select_stream(demuxer, chunkid);
793 *(int *)arg = ds->id;
794 return DEMUXER_CTRL_OK;
797 default:
798 return DEMUXER_CTRL_NOTIMPL;
803 static int avi_check_file(demuxer_t *demuxer)
805 int id=stream_read_dword_le(demuxer->stream); // "RIFF"
807 if((id==mmioFOURCC('R','I','F','F')) || (id==mmioFOURCC('O','N','2',' '))) {
808 stream_read_dword_le(demuxer->stream); //filesize
809 id=stream_read_dword_le(demuxer->stream); // "AVI "
810 if(id==formtypeAVI)
811 return DEMUXER_TYPE_AVI;
812 // "Samsung Digimax i6 PMP" crap according to bug 742
813 if(id==mmioFOURCC('A','V','I',0x19))
814 return DEMUXER_TYPE_AVI;
815 if(id==mmioFOURCC('O','N','2','f')){
816 mp_msg(MSGT_DEMUXER,MSGL_INFO,MSGTR_ON2AviFormat);
817 return DEMUXER_TYPE_AVI;
821 return 0;
825 static demuxer_t* demux_open_hack_avi(demuxer_t *demuxer)
827 struct MPOpts *opts = demuxer->opts;
828 sh_audio_t* sh_a;
830 demuxer = demux_open_avi(demuxer);
831 if(!demuxer) return NULL; // failed to open
832 sh_a = demuxer->audio->sh;
833 if(demuxer->audio->id != -2 && sh_a) {
834 #ifdef CONFIG_OGGVORBIS
835 // support for Ogg-in-AVI:
836 if(sh_a->format == 0xFFFE)
837 demuxer = init_avi_with_ogg(demuxer);
838 else if(sh_a->format == 0x674F) {
839 stream_t* s;
840 demuxer_t *od;
841 s = new_ds_stream(demuxer->audio);
842 od = new_demuxer(opts, s,DEMUXER_TYPE_OGG,-1,-2,-2,NULL);
843 if(!demux_ogg_open(od)) {
844 mp_msg( MSGT_DEMUXER,MSGL_ERR,MSGTR_ErrorOpeningOGGDemuxer);
845 free_stream(s);
846 demuxer->audio->id = -2;
847 } else
848 demuxer = new_demuxers_demuxer(demuxer,od,demuxer);
850 #endif
853 return demuxer;
857 const demuxer_desc_t demuxer_desc_avi = {
858 "AVI demuxer",
859 "avi",
860 "AVI",
861 "Arpi?",
862 "AVI files, including non interleaved files",
863 DEMUXER_TYPE_AVI,
864 1, // safe autodetect
865 avi_check_file,
866 demux_avi_fill_buffer,
867 demux_open_hack_avi,
868 demux_close_avi,
869 demux_seek_avi,
870 demux_avi_control
873 const demuxer_desc_t demuxer_desc_avi_ni = {
874 "AVI demuxer, non-interleaved",
875 "avini",
876 "AVI",
877 "Arpi?",
878 "AVI files, including non interleaved files",
879 DEMUXER_TYPE_AVI,
880 1, // safe autodetect
881 avi_check_file,
882 demux_avi_fill_buffer_ni,
883 demux_open_hack_avi,
884 demux_close_avi,
885 demux_seek_avi,
886 demux_avi_control
889 const demuxer_desc_t demuxer_desc_avi_nini = {
890 "AVI demuxer, non-interleaved and no index",
891 "avinini",
892 "AVI",
893 "Arpi?",
894 "AVI files, including non interleaved files",
895 DEMUXER_TYPE_AVI,
896 1, // safe autodetect
897 avi_check_file,
898 demux_avi_fill_buffer_nini,
899 demux_open_hack_avi,
900 demux_close_avi,
901 demux_seek_avi,
902 demux_avi_control