Make SwScaler recognize RGB48 BE/LE colourspaces (not support though).
[mplayer/glamo.git] / libmpdemux / demux_avi.c
blob21529fb588fa34881b326b025fd2dcd371e82fea
1 /*
2 * AVI file parser for DEMUXER v2.9
3 * Copyright (c) 2001 A'rpi/ESP-team
5 * This file is part of MPlayer.
7 * MPlayer is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * MPlayer is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
26 #include "config.h"
27 #include "mp_msg.h"
28 #include "help_mp.h"
30 #include "stream/stream.h"
31 #include "demuxer.h"
32 #include "stheader.h"
34 #include "aviheader.h"
36 demuxer_t* init_avi_with_ogg(demuxer_t* demuxer);
37 int demux_ogg_open(demuxer_t* demuxer);
39 extern const demuxer_desc_t demuxer_desc_avi_ni;
40 extern const demuxer_desc_t demuxer_desc_avi_nini;
42 // PTS: 0=interleaved 1=BPS-based
43 int pts_from_bps=1;
45 // Select ds from ID
46 demux_stream_t* demux_avi_select_stream(demuxer_t *demux,unsigned int id){
47 int stream_id=avi_stream_id(id);
50 if(demux->video->id==-1)
51 if(demux->v_streams[stream_id])
52 demux->video->id=stream_id;
54 if(demux->audio->id==-1)
55 if(demux->a_streams[stream_id])
56 demux->audio->id=stream_id;
58 if(stream_id==demux->audio->id){
59 if(!demux->audio->sh){
60 sh_audio_t* sh;
61 avi_priv_t *priv=demux->priv;
62 sh=demux->audio->sh=demux->a_streams[stream_id];
63 mp_msg(MSGT_DEMUX,MSGL_V,"Auto-selected AVI audio ID = %d\n",demux->audio->id);
64 if(sh->wf){
65 priv->audio_block_size=sh->wf->nBlockAlign;
66 if(!priv->audio_block_size){
67 // for PCM audio we can calculate the blocksize:
68 if(sh->format==1)
69 priv->audio_block_size=sh->wf->nChannels*(sh->wf->wBitsPerSample/8);
70 else
71 priv->audio_block_size=1; // hope the best...
72 } else {
73 // workaround old mencoder's bug:
74 if(sh->audio.dwSampleSize==1 && sh->audio.dwScale==1 &&
75 (sh->wf->nBlockAlign==1152 || sh->wf->nBlockAlign==576)){
76 mp_msg(MSGT_DEMUX,MSGL_WARN,MSGTR_WorkAroundBlockAlignHeaderBug);
77 priv->audio_block_size=1;
80 } else {
81 priv->audio_block_size=sh->audio.dwSampleSize;
84 return demux->audio;
86 if(stream_id==demux->video->id){
87 if(!demux->video->sh){
88 demux->video->sh=demux->v_streams[stream_id];
89 mp_msg(MSGT_DEMUX,MSGL_V,"Auto-selected AVI video ID = %d\n",demux->video->id);
91 return demux->video;
93 if(id!=mmioFOURCC('J','U','N','K')){
94 // unknown
95 mp_msg(MSGT_DEMUX,MSGL_DBG2,"Unknown chunk: %.4s (%X)\n",(char *) &id,id);
96 //abort();
98 return NULL;
101 static int valid_fourcc(unsigned int id){
102 static const char valid[] = "0123456789abcdefghijklmnopqrstuvwxyz"
103 "ABCDEFGHIJKLMNOPQRSTUVWXYZ_";
104 unsigned char* fcc=(unsigned char*)(&id);
105 return strchr(valid, fcc[0]) && strchr(valid, fcc[1]) &&
106 strchr(valid, fcc[2]) && strchr(valid, fcc[3]);
109 static int choose_chunk_len(unsigned int len1,unsigned int len2){
110 // len1 has a bit more priority than len2. len1!=len2
111 // Note: this is a first-idea-logic, may be wrong. comments welcomed.
113 // prefer small frames rather than 0
114 if(!len1) return (len2>0x80000) ? len1 : len2;
115 if(!len2) return (len1>0x100000) ? len2 : len1;
117 // choose the smaller value:
118 return (len1<len2)? len1 : len2;
121 static int demux_avi_read_packet(demuxer_t *demux,demux_stream_t *ds,unsigned int id,unsigned int len,int idxpos,int flags){
122 avi_priv_t *priv=demux->priv;
123 int skip;
124 float pts=0;
126 mp_dbg(MSGT_DEMUX,MSGL_DBG3,"demux_avi.read_packet: %X\n",id);
128 if(ds==demux->audio){
129 if(priv->pts_corrected==0){
130 if(priv->pts_has_video){
131 // we have video pts now
132 float delay=0;
133 if(((sh_audio_t*)(ds->sh))->wf->nAvgBytesPerSec)
134 delay=(float)priv->pts_corr_bytes/((sh_audio_t*)(ds->sh))->wf->nAvgBytesPerSec;
135 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);
136 //priv->pts_correction=-priv->avi_audio_pts+delay;
137 priv->pts_correction=delay-priv->avi_audio_pts;
138 priv->avi_audio_pts+=priv->pts_correction;
139 priv->pts_corrected=1;
140 } else
141 priv->pts_corr_bytes+=len;
143 if(pts_from_bps){
144 pts = priv->audio_block_no *
145 (float)((sh_audio_t*)demux->audio->sh)->audio.dwScale /
146 (float)((sh_audio_t*)demux->audio->sh)->audio.dwRate;
147 } else
148 pts=priv->avi_audio_pts; //+priv->pts_correction;
149 priv->avi_audio_pts=0;
150 // update blockcount:
151 priv->audio_block_no+=priv->audio_block_size ?
152 ((len+priv->audio_block_size-1)/priv->audio_block_size) : 1;
153 } else
154 if(ds==demux->video){
155 // video
156 if(priv->skip_video_frames>0){
157 // drop frame (seeking)
158 --priv->skip_video_frames;
159 ds=NULL;
162 pts = priv->avi_video_pts = priv->video_pack_no *
163 (float)((sh_video_t*)demux->video->sh)->video.dwScale /
164 (float)((sh_video_t*)demux->video->sh)->video.dwRate;
166 priv->avi_audio_pts=priv->avi_video_pts+priv->pts_correction;
167 priv->pts_has_video=1;
169 if(ds) ++priv->video_pack_no;
173 skip=(len+1)&(~1); // total bytes in this chunk
175 if(ds){
176 mp_dbg(MSGT_DEMUX,MSGL_DBG2,"DEMUX_AVI: Read %d data bytes from packet %04X\n",len,id);
177 ds_read_packet(ds,demux->stream,len,pts,idxpos,flags);
178 skip-=len;
180 skip = FFMAX(skip, 0);
181 if (avi_stream_id(id) > 99 && id != mmioFOURCC('J','U','N','K'))
182 skip = FFMIN(skip, 65536);
183 if(skip){
184 mp_dbg(MSGT_DEMUX,MSGL_DBG2,"DEMUX_AVI: Skipping %d bytes from packet %04X\n",skip,id);
185 stream_skip(demux->stream,skip);
187 return ds?1:0;
190 static uint32_t avi_find_id(stream_t *stream) {
191 uint32_t id = stream_read_dword_le(stream);
192 if (!id) {
193 mp_msg(MSGT_DEMUX, MSGL_WARN, "Incomplete stream? Trying resync.\n");
194 do {
195 id = stream_read_dword_le(stream);
196 if (stream_eof(stream)) return 0;
197 } while (avi_stream_id(id) > 99);
199 return id;
202 // return value:
203 // 0 = EOF or no stream found
204 // 1 = successfully read a packet
205 static int demux_avi_fill_buffer(demuxer_t *demux, demux_stream_t *dsds){
206 avi_priv_t *priv=demux->priv;
207 unsigned int id=0;
208 unsigned int len;
209 int ret=0;
210 demux_stream_t *ds;
213 int flags=1;
214 AVIINDEXENTRY *idx=NULL;
215 if(priv->idx_size>0 && priv->idx_pos<priv->idx_size){
216 off_t pos;
218 idx=&((AVIINDEXENTRY *)priv->idx)[priv->idx_pos++];
220 if(idx->dwFlags&AVIIF_LIST){
221 // LIST
222 continue;
224 if(!demux_avi_select_stream(demux,idx->ckid)){
225 mp_dbg(MSGT_DEMUX,MSGL_DBG3,"Skip chunk %.4s (0x%X) \n",(char *)&idx->ckid,(unsigned int)idx->ckid);
226 continue; // skip this chunk
229 pos = (off_t)priv->idx_offset+AVI_IDX_OFFSET(idx);
230 if((pos<demux->movi_start || pos>=demux->movi_end) && (demux->movi_end>demux->movi_start) && (demux->stream->flags & STREAM_SEEK)){
231 mp_msg(MSGT_DEMUX,MSGL_V,"ChunkOffset out of range! idx=0x%"PRIX64" \n",(int64_t)pos);
232 continue;
234 stream_seek(demux->stream,pos);
235 demux->filepos=stream_tell(demux->stream);
236 id=stream_read_dword_le(demux->stream);
237 if(stream_eof(demux->stream)) return 0; // EOF!
239 if(id!=idx->ckid){
240 mp_msg(MSGT_DEMUX,MSGL_V,"ChunkID mismatch! raw=%.4s idx=%.4s \n",(char *)&id,(char *)&idx->ckid);
241 if(valid_fourcc(idx->ckid))
242 id=idx->ckid; // use index if valid
243 else
244 if(!valid_fourcc(id)) continue; // drop chunk if both id and idx bad
246 len=stream_read_dword_le(demux->stream);
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=avi_find_id(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 } while(ret!=1);
292 return 1;
296 // return value:
297 // 0 = EOF or no stream found
298 // 1 = successfully read a packet
299 int demux_avi_fill_buffer_ni(demuxer_t *demux,demux_stream_t* ds){
300 avi_priv_t *priv=demux->priv;
301 unsigned int id=0;
302 unsigned int len;
303 int ret=0;
306 int flags=1;
307 AVIINDEXENTRY *idx=NULL;
308 int idx_pos=0;
309 demux->filepos=stream_tell(demux->stream);
311 if(ds==demux->video) idx_pos=priv->idx_pos_v++; else
312 if(ds==demux->audio) idx_pos=priv->idx_pos_a++; else
313 idx_pos=priv->idx_pos++;
315 if(priv->idx_size>0 && idx_pos<priv->idx_size){
316 off_t pos;
317 idx=&((AVIINDEXENTRY *)priv->idx)[idx_pos];
319 if(idx->dwFlags&AVIIF_LIST){
320 // LIST
321 continue;
323 if(ds && demux_avi_select_stream(demux,idx->ckid)!=ds){
324 mp_dbg(MSGT_DEMUX,MSGL_DBG3,"Skip chunk %.4s (0x%X) \n",(char *)&idx->ckid,(unsigned int)idx->ckid);
325 continue; // skip this chunk
328 pos = priv->idx_offset+AVI_IDX_OFFSET(idx);
329 if((pos<demux->movi_start || pos>=demux->movi_end) && (demux->movi_end>demux->movi_start)){
330 mp_msg(MSGT_DEMUX,MSGL_V,"ChunkOffset out of range! current=0x%"PRIX64" idx=0x%"PRIX64" \n",(int64_t)demux->filepos,(int64_t)pos);
331 continue;
333 stream_seek(demux->stream,pos);
335 id=stream_read_dword_le(demux->stream);
337 if(stream_eof(demux->stream)) return 0;
339 if(id!=idx->ckid){
340 mp_msg(MSGT_DEMUX,MSGL_V,"ChunkID mismatch! raw=%.4s idx=%.4s \n",(char *)&id,(char *)&idx->ckid);
341 if(valid_fourcc(idx->ckid))
342 id=idx->ckid; // use index if valid
343 else
344 if(!valid_fourcc(id)) continue; // drop chunk if both id and idx bad
346 len=stream_read_dword_le(demux->stream);
347 if((len!=idx->dwChunkLength)&&((len+1)!=idx->dwChunkLength)){
348 mp_msg(MSGT_DEMUX,MSGL_V,"ChunkSize mismatch! raw=%d idx=%d \n",len,idx->dwChunkLength);
349 if(len>0x200000 && idx->dwChunkLength>0x200000) continue; // both values bad :(
350 len=choose_chunk_len(idx->dwChunkLength,len);
352 if(!(idx->dwFlags&AVIIF_KEYFRAME)) flags=0;
353 } else return 0;
354 ret=demux_avi_read_packet(demux,demux_avi_select_stream(demux,id),id,len,idx_pos,flags);
355 } while(ret!=1);
356 return 1;
360 // return value:
361 // 0 = EOF or no stream found
362 // 1 = successfully read a packet
363 int demux_avi_fill_buffer_nini(demuxer_t *demux,demux_stream_t* ds){
364 avi_priv_t *priv=demux->priv;
365 unsigned int id=0;
366 unsigned int len;
367 int ret=0;
368 off_t *fpos=NULL;
370 if(ds==demux->video) fpos=&priv->idx_pos_v; else
371 if(ds==demux->audio) fpos=&priv->idx_pos_a; else
372 return 0;
374 stream_seek(demux->stream,fpos[0]);
378 demux->filepos=stream_tell(demux->stream);
379 if(demux->filepos>=demux->movi_end && (demux->movi_end>demux->movi_start)){
380 ds->eof=1;
381 return 0;
384 id=avi_find_id(demux->stream);
385 len=stream_read_dword_le(demux->stream);
387 if(stream_eof(demux->stream)) return 0;
389 if(id==mmioFOURCC('L','I','S','T')){
390 id=stream_read_dword_le(demux->stream); // list type
391 continue;
394 if(id==mmioFOURCC('R','I','F','F')){
395 mp_msg(MSGT_DEMUX,MSGL_V,"additional RIFF header...\n");
396 id=stream_read_dword_le(demux->stream); // "AVIX"
397 continue;
400 if(ds==demux_avi_select_stream(demux,id)){
401 // read it!
402 ret=demux_avi_read_packet(demux,ds,id,len,priv->idx_pos-1,0);
403 } else {
404 // skip it!
405 int skip=(len+1)&(~1); // total bytes in this chunk
406 stream_skip(demux->stream,skip);
409 } while(ret!=1);
410 fpos[0]=stream_tell(demux->stream);
411 return 1;
414 // AVI demuxer parameters:
415 int index_mode=-1; // -1=untouched 0=don't use index 1=use (generate) index
416 char *index_file_save = NULL, *index_file_load = NULL;
417 int force_ni=0; // force non-interleaved AVI parsing
419 void read_avi_header(demuxer_t *demuxer,int index_mode);
421 static demuxer_t* demux_open_avi(demuxer_t* demuxer){
422 demux_stream_t *d_audio=demuxer->audio;
423 demux_stream_t *d_video=demuxer->video;
424 sh_audio_t *sh_audio=NULL;
425 sh_video_t *sh_video=NULL;
426 avi_priv_t* priv=malloc(sizeof(avi_priv_t));
428 // priv struct:
429 priv->avi_audio_pts=priv->avi_video_pts=0.0f;
430 priv->pts_correction=0.0f;
431 priv->skip_video_frames=0;
432 priv->pts_corr_bytes=0;
433 priv->pts_has_video=priv->pts_corrected=0;
434 priv->video_pack_no=0;
435 priv->audio_block_no=0;
436 priv->audio_block_size=0;
437 priv->isodml = 0;
438 priv->suidx_size = 0;
439 priv->suidx = NULL;
441 demuxer->priv=(void*)priv;
443 //---- AVI header:
444 read_avi_header(demuxer,(demuxer->stream->flags & STREAM_SEEK_BW)?index_mode:-2);
446 if(demuxer->audio->id>=0 && !demuxer->a_streams[demuxer->audio->id]){
447 mp_msg(MSGT_DEMUX,MSGL_WARN,MSGTR_InvalidAudioStreamNosound,demuxer->audio->id);
448 demuxer->audio->id=-2; // disabled
450 if(demuxer->video->id>=0 && !demuxer->v_streams[demuxer->video->id]){
451 mp_msg(MSGT_DEMUX,MSGL_WARN,MSGTR_InvalidAudioStreamUsingDefault,demuxer->video->id);
452 demuxer->video->id=-1; // autodetect
455 stream_reset(demuxer->stream);
456 stream_seek(demuxer->stream,demuxer->movi_start);
457 priv->idx_pos=0;
458 priv->idx_pos_a=0;
459 priv->idx_pos_v=0;
460 if(priv->idx_size>1){
461 // decide index format:
462 #if 1
463 if((AVI_IDX_OFFSET(&((AVIINDEXENTRY *)priv->idx)[0])<demuxer->movi_start ||
464 AVI_IDX_OFFSET(&((AVIINDEXENTRY *)priv->idx)[1])<demuxer->movi_start )&& !priv->isodml)
465 priv->idx_offset=demuxer->movi_start-4;
466 else
467 priv->idx_offset=0;
468 #else
469 if(AVI_IDX_OFFSET(&((AVIINDEXENTRY *)priv->idx)[0])<demuxer->movi_start)
470 priv->idx_offset=demuxer->movi_start-4;
471 else
472 priv->idx_offset=0;
473 #endif
474 mp_msg(MSGT_DEMUX,MSGL_V,"AVI index offset: 0x%X (movi=0x%X idx0=0x%X idx1=0x%X)\n",
475 (int)priv->idx_offset,(int)demuxer->movi_start,
476 (int)((AVIINDEXENTRY *)priv->idx)[0].dwChunkOffset,
477 (int)((AVIINDEXENTRY *)priv->idx)[1].dwChunkOffset);
480 if(priv->idx_size>0){
481 // check that file is non-interleaved:
482 int i;
483 off_t a_pos=-1;
484 off_t v_pos=-1;
485 for(i=0;i<priv->idx_size;i++){
486 AVIINDEXENTRY* idx=&((AVIINDEXENTRY *)priv->idx)[i];
487 demux_stream_t* ds=demux_avi_select_stream(demuxer,idx->ckid);
488 off_t pos = priv->idx_offset + AVI_IDX_OFFSET(idx);
489 if(a_pos==-1 && ds==demuxer->audio){
490 a_pos=pos;
491 if(v_pos!=-1) break;
493 if(v_pos==-1 && ds==demuxer->video){
494 v_pos=pos;
495 if(a_pos!=-1) break;
498 if(v_pos==-1){
499 mp_msg(MSGT_DEMUX,MSGL_ERR,"AVI_NI: " MSGTR_MissingVideoStream);
500 return NULL;
502 if(a_pos==-1){
503 d_audio->sh=sh_audio=NULL;
504 } else {
505 if(force_ni || abs(a_pos-v_pos)>0x100000){ // distance > 1MB
506 mp_msg(MSGT_DEMUX,MSGL_INFO,MSGTR_NI_Message,force_ni?MSGTR_NI_Forced:MSGTR_NI_Detected);
507 demuxer->type=DEMUXER_TYPE_AVI_NI; // HACK!!!!
508 demuxer->desc=&demuxer_desc_avi_ni; // HACK!!!!
509 pts_from_bps=1; // force BPS sync!
512 } else {
513 // no index
514 if(force_ni){
515 mp_msg(MSGT_DEMUX,MSGL_INFO,MSGTR_UsingNINI);
516 demuxer->type=DEMUXER_TYPE_AVI_NINI; // HACK!!!!
517 demuxer->desc=&demuxer_desc_avi_nini; // HACK!!!!
518 priv->idx_pos_a=
519 priv->idx_pos_v=demuxer->movi_start;
520 pts_from_bps=1; // force BPS sync!
522 demuxer->seekable=0;
524 if(!ds_fill_buffer(d_video)){
525 mp_msg(MSGT_DEMUX,MSGL_ERR,"AVI: " MSGTR_MissingVideoStreamBug);
526 return NULL;
528 sh_video=d_video->sh;sh_video->ds=d_video;
529 if(d_audio->id!=-2){
530 mp_msg(MSGT_DEMUX,MSGL_V,"AVI: Searching for audio stream (id:%d)\n",d_audio->id);
531 if(!priv->audio_streams || !ds_fill_buffer(d_audio)){
532 mp_msg(MSGT_DEMUX,MSGL_INFO,"AVI: " MSGTR_MissingAudioStream);
533 d_audio->sh=sh_audio=NULL;
534 } else {
535 sh_audio=d_audio->sh;sh_audio->ds=d_audio;
539 // calculating audio/video bitrate:
540 if(priv->idx_size>0){
541 // we have index, let's count 'em!
542 int64_t vsize=0;
543 int64_t asize=0;
544 size_t vsamples=0;
545 size_t asamples=0;
546 int i;
547 for(i=0;i<priv->idx_size;i++){
548 int id=avi_stream_id(((AVIINDEXENTRY *)priv->idx)[i].ckid);
549 int len=((AVIINDEXENTRY *)priv->idx)[i].dwChunkLength;
550 if(sh_video->ds->id == id) {
551 vsize+=len;
552 ++vsamples;
554 else if(sh_audio && sh_audio->ds->id == id) {
555 asize+=len;
556 asamples+=(len+priv->audio_block_size-1)/priv->audio_block_size;
559 mp_msg(MSGT_DEMUX,MSGL_V,"AVI video size=%"PRId64" (%u) audio size=%"PRId64" (%u)\n",vsize,vsamples,asize,asamples);
560 priv->numberofframes=vsamples;
561 sh_video->i_bps=((float)vsize/(float)vsamples)*(float)sh_video->video.dwRate/(float)sh_video->video.dwScale;
562 if(sh_audio) sh_audio->i_bps=((float)asize/(float)asamples)*(float)sh_audio->audio.dwRate/(float)sh_audio->audio.dwScale;
563 } else {
564 // guessing, results may be inaccurate:
565 int64_t vsize;
566 int64_t asize=0;
568 if((priv->numberofframes=sh_video->video.dwLength)<=1)
569 // bad video header, try to get number of frames from audio
570 if(sh_audio && sh_audio->wf->nAvgBytesPerSec) priv->numberofframes=sh_video->fps*sh_audio->audio.dwLength/sh_audio->audio.dwRate*sh_audio->audio.dwScale;
571 if(priv->numberofframes<=1){
572 mp_msg(MSGT_SEEK,MSGL_WARN,MSGTR_CouldntDetFNo);
573 priv->numberofframes=0;
576 if(sh_audio){
577 if(sh_audio->wf->nAvgBytesPerSec && sh_audio->audio.dwSampleSize!=1){
578 asize=(float)sh_audio->wf->nAvgBytesPerSec*sh_audio->audio.dwLength*sh_audio->audio.dwScale/sh_audio->audio.dwRate;
579 } else {
580 asize=sh_audio->audio.dwLength;
581 sh_audio->i_bps=(float)asize/(sh_video->frametime*priv->numberofframes);
584 vsize=demuxer->movi_end-demuxer->movi_start-asize-8*priv->numberofframes;
585 mp_msg(MSGT_DEMUX,MSGL_V,"AVI video size=%"PRId64" (%u) audio size=%"PRId64"\n",vsize,priv->numberofframes,asize);
586 sh_video->i_bps=(float)vsize/(sh_video->frametime*priv->numberofframes);
589 return demuxer;
594 void demux_seek_avi(demuxer_t *demuxer,float rel_seek_secs,float audio_delay,int flags){
595 avi_priv_t *priv=demuxer->priv;
596 demux_stream_t *d_audio=demuxer->audio;
597 demux_stream_t *d_video=demuxer->video;
598 sh_audio_t *sh_audio=d_audio->sh;
599 sh_video_t *sh_video=d_video->sh;
600 float skip_audio_secs=0;
602 //FIXME: OFF_T - Didn't check AVI case yet (avi files can't be >2G anyway?)
603 //================= seek in AVI ==========================
604 int rel_seek_frames=rel_seek_secs*sh_video->fps;
605 int video_chunk_pos=d_video->pos;
606 int i;
608 if(flags&SEEK_ABSOLUTE){
609 // seek absolute
610 video_chunk_pos=0;
613 if(flags&SEEK_FACTOR){
614 rel_seek_frames=rel_seek_secs*priv->numberofframes;
617 priv->skip_video_frames=0;
618 priv->avi_audio_pts=0;
620 // ------------ STEP 1: find nearest video keyframe chunk ------------
621 // find nearest video keyframe chunk pos:
622 if(rel_seek_frames>0){
623 // seek forward
624 while(video_chunk_pos<priv->idx_size-1){
625 int id=((AVIINDEXENTRY *)priv->idx)[video_chunk_pos].ckid;
626 if(avi_stream_id(id)==d_video->id){ // video frame
627 if((--rel_seek_frames)<0 && ((AVIINDEXENTRY *)priv->idx)[video_chunk_pos].dwFlags&AVIIF_KEYFRAME) break;
629 ++video_chunk_pos;
631 } else {
632 // seek backward
633 while(video_chunk_pos>0){
634 int id=((AVIINDEXENTRY *)priv->idx)[video_chunk_pos].ckid;
635 if(avi_stream_id(id)==d_video->id){ // video frame
636 if((++rel_seek_frames)>0 && ((AVIINDEXENTRY *)priv->idx)[video_chunk_pos].dwFlags&AVIIF_KEYFRAME) break;
638 --video_chunk_pos;
641 priv->idx_pos_a=priv->idx_pos_v=priv->idx_pos=video_chunk_pos;
643 // re-calc video pts:
644 d_video->pack_no=0;
645 for(i=0;i<video_chunk_pos;i++){
646 int id=((AVIINDEXENTRY *)priv->idx)[i].ckid;
647 if(avi_stream_id(id)==d_video->id) ++d_video->pack_no;
649 priv->video_pack_no=
650 sh_video->num_frames=sh_video->num_frames_decoded=d_video->pack_no;
651 priv->avi_video_pts=d_video->pack_no*(float)sh_video->video.dwScale/(float)sh_video->video.dwRate;
652 d_video->pos=video_chunk_pos;
654 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);
656 // ------------ STEP 2: seek audio, find the right chunk & pos ------------
658 d_audio->pack_no=0;
659 priv->audio_block_no=0;
660 d_audio->dpos=0;
662 if(sh_audio){
663 int i;
664 int len=0;
665 int skip_audio_bytes=0;
666 int curr_audio_pos=-1;
667 int audio_chunk_pos=-1;
668 int chunk_max=(demuxer->type==DEMUXER_TYPE_AVI)?video_chunk_pos:priv->idx_size;
670 if(sh_audio->audio.dwSampleSize){
671 // constant rate audio stream
672 /* immediate seeking to audio position, including when streams are delayed */
673 curr_audio_pos=(priv->avi_video_pts + audio_delay)*(float)sh_audio->audio.dwRate/(float)sh_audio->audio.dwScale;
674 curr_audio_pos*=sh_audio->audio.dwSampleSize;
676 // find audio chunk pos:
677 for(i=0;i<chunk_max;i++){
678 int id=((AVIINDEXENTRY *)priv->idx)[i].ckid;
679 if(avi_stream_id(id)==d_audio->id){
680 len=((AVIINDEXENTRY *)priv->idx)[i].dwChunkLength;
681 if(d_audio->dpos<=curr_audio_pos && curr_audio_pos<(d_audio->dpos+len)){
682 break;
684 ++d_audio->pack_no;
685 priv->audio_block_no+=priv->audio_block_size ?
686 ((len+priv->audio_block_size-1)/priv->audio_block_size) : 1;
687 d_audio->dpos+=len;
690 audio_chunk_pos=i;
691 skip_audio_bytes=curr_audio_pos-d_audio->dpos;
693 mp_msg(MSGT_SEEK,MSGL_V,"SEEK: i=%d (max:%d) dpos=%d (wanted:%d) \n",
694 i,chunk_max,(int)d_audio->dpos,curr_audio_pos);
696 } else {
697 // VBR audio
698 /* immediate seeking to audio position, including when streams are delayed */
699 int chunks=(priv->avi_video_pts + audio_delay)*(float)sh_audio->audio.dwRate/(float)sh_audio->audio.dwScale;
700 audio_chunk_pos=0;
702 // find audio chunk pos:
703 for(i=0;i<priv->idx_size && chunks>0;i++){
704 int id=((AVIINDEXENTRY *)priv->idx)[i].ckid;
705 if(avi_stream_id(id)==d_audio->id){
706 len=((AVIINDEXENTRY *)priv->idx)[i].dwChunkLength;
707 if(i>chunk_max){
708 skip_audio_bytes+=len;
709 } else {
710 ++d_audio->pack_no;
711 priv->audio_block_no+=priv->audio_block_size ?
712 ((len+priv->audio_block_size-1)/priv->audio_block_size) : 1;
713 d_audio->dpos+=len;
714 audio_chunk_pos=i;
716 if(priv->audio_block_size)
717 chunks-=(len+priv->audio_block_size-1)/priv->audio_block_size;
722 // Now we have:
723 // audio_chunk_pos = chunk no in index table (it's <=chunk_max)
724 // skip_audio_bytes = bytes to be skipped after chunk seek
725 // d-audio->pack_no = chunk_no in stream at audio_chunk_pos
726 // d_audio->dpos = bytepos in stream at audio_chunk_pos
727 // let's seek!
729 // update stream position:
730 d_audio->pos=audio_chunk_pos;
732 if(demuxer->type==DEMUXER_TYPE_AVI){
733 // interleaved stream:
734 if(audio_chunk_pos<video_chunk_pos){
735 // calc priv->skip_video_frames & adjust video pts counter:
736 for(i=audio_chunk_pos;i<video_chunk_pos;i++){
737 int id=((AVIINDEXENTRY *)priv->idx)[i].ckid;
738 if(avi_stream_id(id)==d_video->id) ++priv->skip_video_frames;
740 // requires for correct audio pts calculation (demuxer):
741 priv->avi_video_pts-=priv->skip_video_frames*(float)sh_video->video.dwScale/(float)sh_video->video.dwRate;
742 priv->avi_audio_pts=priv->avi_video_pts;
743 // set index position:
744 priv->idx_pos_a=priv->idx_pos_v=priv->idx_pos=audio_chunk_pos;
746 } else {
747 // non-interleaved stream:
748 priv->idx_pos_a=audio_chunk_pos;
749 priv->idx_pos_v=video_chunk_pos;
750 priv->idx_pos=(audio_chunk_pos<video_chunk_pos)?audio_chunk_pos:video_chunk_pos;
753 mp_msg(MSGT_SEEK,MSGL_V,"SEEK: idx=%d (a:%d v:%d) v.skip=%d a.skip=%d/%4.3f \n",
754 (int)priv->idx_pos,audio_chunk_pos,video_chunk_pos,
755 (int)priv->skip_video_frames,skip_audio_bytes,skip_audio_secs);
757 if(skip_audio_bytes){
758 demux_read_data(d_audio,NULL,skip_audio_bytes);
762 d_video->pts=priv->avi_video_pts; // OSD
767 void demux_close_avi(demuxer_t *demuxer) {
768 avi_priv_t* priv=demuxer->priv;
770 if(!priv)
771 return;
773 if(priv->idx_size > 0)
774 free(priv->idx);
775 free(priv);
779 static int demux_avi_control(demuxer_t *demuxer,int cmd, void *arg){
780 avi_priv_t *priv=demuxer->priv;
781 demux_stream_t *d_video=demuxer->video;
782 sh_video_t *sh_video=d_video->sh;
784 switch(cmd) {
785 case DEMUXER_CTRL_GET_TIME_LENGTH:
786 if (!priv->numberofframes || !sh_video) return DEMUXER_CTRL_DONTKNOW;
787 *((double *)arg)=(double)priv->numberofframes/sh_video->fps;
788 if (sh_video->video.dwLength<=1) return DEMUXER_CTRL_GUESS;
789 return DEMUXER_CTRL_OK;
791 case DEMUXER_CTRL_GET_PERCENT_POS:
792 if (!priv->numberofframes || !sh_video) {
793 return DEMUXER_CTRL_DONTKNOW;
795 *((int *)arg)=(int)(priv->video_pack_no*100/priv->numberofframes);
796 if (sh_video->video.dwLength<=1) return DEMUXER_CTRL_GUESS;
797 return DEMUXER_CTRL_OK;
799 case DEMUXER_CTRL_SWITCH_AUDIO:
800 case DEMUXER_CTRL_SWITCH_VIDEO: {
801 int audio = (cmd == DEMUXER_CTRL_SWITCH_AUDIO);
802 demux_stream_t *ds = audio ? demuxer->audio : demuxer->video;
803 void **streams = audio ? demuxer->a_streams : demuxer->v_streams;
804 int maxid = FFMIN(100, audio ? MAX_A_STREAMS : MAX_V_STREAMS);
805 int chunkid;
806 if (ds->id < -1)
807 return DEMUXER_CTRL_NOTIMPL;
809 if (*(int *)arg >= 0)
810 ds->id = *(int *)arg;
811 else {
812 int i;
813 for (i = 0; i < maxid; i++) {
814 if (++ds->id >= maxid) ds->id = 0;
815 if (streams[ds->id]) break;
819 chunkid = (ds->id / 10 + '0') | (ds->id % 10 + '0') << 8;
820 ds->sh = NULL;
821 if (!streams[ds->id]) // stream not available
822 ds->id = -1;
823 else
824 demux_avi_select_stream(demuxer, chunkid);
825 *(int *)arg = ds->id;
826 return DEMUXER_CTRL_OK;
829 default:
830 return DEMUXER_CTRL_NOTIMPL;
835 static int avi_check_file(demuxer_t *demuxer)
837 int id=stream_read_dword_le(demuxer->stream); // "RIFF"
839 if((id==mmioFOURCC('R','I','F','F')) || (id==mmioFOURCC('O','N','2',' '))) {
840 stream_read_dword_le(demuxer->stream); //filesize
841 id=stream_read_dword_le(demuxer->stream); // "AVI "
842 if(id==formtypeAVI)
843 return DEMUXER_TYPE_AVI;
844 // "Samsung Digimax i6 PMP" crap according to bug 742
845 if(id==mmioFOURCC('A','V','I',0x19))
846 return DEMUXER_TYPE_AVI;
847 if(id==mmioFOURCC('O','N','2','f')){
848 mp_msg(MSGT_DEMUXER,MSGL_INFO,MSGTR_ON2AviFormat);
849 return DEMUXER_TYPE_AVI;
853 return 0;
857 static demuxer_t* demux_open_hack_avi(demuxer_t *demuxer)
859 sh_audio_t* sh_a;
861 demuxer = demux_open_avi(demuxer);
862 if(!demuxer) return NULL; // failed to open
863 sh_a = demuxer->audio->sh;
864 if(demuxer->audio->id != -2 && sh_a) {
865 #ifdef CONFIG_OGGVORBIS
866 // support for Ogg-in-AVI:
867 if(sh_a->format == 0xFFFE)
868 demuxer = init_avi_with_ogg(demuxer);
869 else if(sh_a->format == 0x674F) {
870 stream_t* s;
871 demuxer_t *od;
872 s = new_ds_stream(demuxer->audio);
873 od = new_demuxer(s,DEMUXER_TYPE_OGG,-1,-2,-2,NULL);
874 if(!demux_ogg_open(od)) {
875 mp_msg( MSGT_DEMUXER,MSGL_ERR,MSGTR_ErrorOpeningOGGDemuxer);
876 free_stream(s);
877 demuxer->audio->id = -2;
878 } else
879 demuxer = new_demuxers_demuxer(demuxer,od,demuxer);
881 #endif
884 return demuxer;
888 const demuxer_desc_t demuxer_desc_avi = {
889 "AVI demuxer",
890 "avi",
891 "AVI",
892 "Arpi?",
893 "AVI files, including non interleaved files",
894 DEMUXER_TYPE_AVI,
895 1, // safe autodetect
896 avi_check_file,
897 demux_avi_fill_buffer,
898 demux_open_hack_avi,
899 demux_close_avi,
900 demux_seek_avi,
901 demux_avi_control
904 const demuxer_desc_t demuxer_desc_avi_ni = {
905 "AVI demuxer, non-interleaved",
906 "avini",
907 "AVI",
908 "Arpi?",
909 "AVI files, including non interleaved files",
910 DEMUXER_TYPE_AVI,
911 1, // safe autodetect
912 avi_check_file,
913 demux_avi_fill_buffer_ni,
914 demux_open_hack_avi,
915 demux_close_avi,
916 demux_seek_avi,
917 demux_avi_control
920 const demuxer_desc_t demuxer_desc_avi_nini = {
921 "AVI demuxer, non-interleaved and no index",
922 "avinini",
923 "AVI",
924 "Arpi?",
925 "AVI files, including non interleaved files",
926 DEMUXER_TYPE_AVI,
927 1, // safe autodetect
928 avi_check_file,
929 demux_avi_fill_buffer_nini,
930 demux_open_hack_avi,
931 demux_close_avi,
932 demux_seek_avi,
933 demux_avi_control