diff --git a/libavformat/avidec.c b/libavformat/avidec.c index 246648eb19..7ad9c2a0f4 100644 --- a/libavformat/avidec.c +++ b/libavformat/avidec.c @@ -27,8 +27,11 @@ //#define DEBUG_SEEK typedef struct AVIStream { - int frame_offset; /* current frame (video) or byte (audio) counter + int64_t frame_offset; /* current frame (video) or byte (audio) counter (used to compute the pts) */ + int remaining; + int packet_size; + int scale; int rate; int sample_size; /* audio only data */ @@ -46,6 +49,8 @@ typedef struct { offset_t movi_list; int index_loaded; int is_odml; + int non_interleaved; + int stream_index; DVDemuxContext* dv_demux; } AVIContext; @@ -92,6 +97,8 @@ static int avi_read_header(AVFormatContext *s, AVFormatParameters *ap) AVIStream *ast; int xan_video = 0; /* hack to support Xan A/V */ + avi->stream_index= -1; + if (get_riff(avi, pb) < 0) return -1; @@ -389,8 +396,124 @@ static int avi_read_packet(AVFormatContext *s, AVPacket *pkt) if (size >= 0) return size; } + + if(avi->non_interleaved){ + int best_stream_index; + AVStream *best_st= NULL; + AVIStream *best_ast; + int64_t best_ts= INT64_MAX; + int i; + + for(i=0; inb_streams; i++){ + AVStream *st = s->streams[i]; + AVIStream *ast = st->priv_data; + int64_t ts= ast->frame_offset; + if(ast->sample_size) + ts /= ast->sample_size; + ts= av_rescale(ts, AV_TIME_BASE * (int64_t)st->time_base.num, st->time_base.den); + +// av_log(NULL, AV_LOG_DEBUG, "%Ld %d/%d %Ld\n", ts, st->time_base.num, st->time_base.den, ast->frame_offset); + if(ts < best_ts){ + best_ts= ts; + best_st= st; + best_stream_index= i; + } + } + best_ast = best_st->priv_data; + best_ts= av_rescale(best_ts, best_st->time_base.den, AV_TIME_BASE * (int64_t)best_st->time_base.num); //FIXME a little ugly + if(best_ast->remaining) + i= av_index_search_timestamp(best_st, best_ts, AVSEEK_FLAG_ANY | AVSEEK_FLAG_BACKWARD); + else + i= av_index_search_timestamp(best_st, best_ts, AVSEEK_FLAG_ANY); + +// av_log(NULL, AV_LOG_DEBUG, "%d\n", i); + if(i>=0){ + int64_t pos= best_st->index_entries[i].pos; + pos += avi->movi_list + best_ast->packet_size - best_ast->remaining; + url_fseek(&s->pb, pos, SEEK_SET); +// av_log(NULL, AV_LOG_DEBUG, "pos=%Ld\n", pos); + + if(best_ast->remaining) + avi->stream_index= best_stream_index; + else + avi->stream_index= -1; + } + } + resync: + if(avi->stream_index >= 0){ + AVStream *st= s->streams[ avi->stream_index ]; + AVIStream *ast= st->priv_data; + int size; + + assert(ast->remaining); + + if(ast->sample_size == 0) + size= INT_MAX; + else if(ast->sample_size < 32) + size= 64*ast->sample_size; + else + size= ast->sample_size; + + if(size > ast->remaining) + size= ast->remaining; + av_new_packet(pkt, size); + get_buffer(pb, pkt->data, size); + + if (avi->dv_demux) { + dstr = pkt->destruct; + size = dv_produce_packet(avi->dv_demux, pkt, + pkt->data, pkt->size); + pkt->destruct = dstr; + pkt->flags |= PKT_FLAG_KEY; + } else { + /* XXX: how to handle B frames in avi ? */ + pkt->dts = ast->frame_offset; +// pkt->dts += ast->start; + if(ast->sample_size) + pkt->dts /= ast->sample_size; +//av_log(NULL, AV_LOG_DEBUG, "dts:%Ld offset:%d %d/%d smpl_siz:%d base:%d st:%d size:%d\n", pkt->dts, ast->frame_offset, ast->scale, ast->rate, ast->sample_size, AV_TIME_BASE, n, size); + pkt->stream_index = avi->stream_index; + + if (st->codec.codec_type == CODEC_TYPE_VIDEO) { + if(st->index_entries){ + AVIndexEntry *e; + int index; + + index= av_index_search_timestamp(st, pkt->dts, 0); + e= &st->index_entries[index]; + + if(e->timestamp == ast->frame_offset){ + if (e->flags & AVINDEX_KEYFRAME) + pkt->flags |= PKT_FLAG_KEY; + } + } else { + /* if no index, better to say that all frames + are key frames */ + pkt->flags |= PKT_FLAG_KEY; + } + } else { + pkt->flags |= PKT_FLAG_KEY; + } + if(ast->sample_size) + ast->frame_offset += pkt->size; + else + ast->frame_offset++; + } + ast->remaining -= size; + if(!ast->remaining){ + avi->stream_index= -1; + ast->packet_size= 0; + if (size & 1) { + get_byte(pb); + size++; + } + } + + return size; + } + memset(d, -1, sizeof(int)*8); for(i=sync=url_ftell(pb); !url_feof(pb); i++) { int j; @@ -436,7 +559,7 @@ resync: } //parse ##dc/##wb - if(n < s->nb_streams){ + if(n < s->nb_streams && size){ AVStream *st; AVIStream *ast; st = s->streams[n]; @@ -460,54 +583,11 @@ resync: ast->prefix_count= 0; } - av_new_packet(pkt, size); - get_buffer(pb, pkt->data, size); - if (size & 1) { - get_byte(pb); - size++; - } - - if (avi->dv_demux) { - dstr = pkt->destruct; - size = dv_produce_packet(avi->dv_demux, pkt, - pkt->data, pkt->size); - pkt->destruct = dstr; - pkt->flags |= PKT_FLAG_KEY; - } else { - /* XXX: how to handle B frames in avi ? */ - pkt->dts = ast->frame_offset; -// pkt->dts += ast->start; - if(ast->sample_size) - pkt->dts /= ast->sample_size; -//av_log(NULL, AV_LOG_DEBUG, "dts:%Ld offset:%d %d/%d smpl_siz:%d base:%d st:%d size:%d\n", pkt->dts, ast->frame_offset, ast->scale, ast->rate, ast->sample_size, AV_TIME_BASE, n, size); - pkt->stream_index = n; + avi->stream_index= n; + ast->packet_size= size + 8; + ast->remaining= size; + goto resync; - if (st->codec.codec_type == CODEC_TYPE_VIDEO) { - if(st->index_entries){ - AVIndexEntry *e; - int index; - - index= av_index_search_timestamp(st, pkt->dts, 0); - e= &st->index_entries[index]; - - if(e->timestamp == ast->frame_offset){ - if (e->flags & AVINDEX_KEYFRAME) - pkt->flags |= PKT_FLAG_KEY; - } - } else { - /* if no index, better to say that all frames - are key frames */ - pkt->flags |= PKT_FLAG_KEY; - } - } else { - pkt->flags |= PKT_FLAG_KEY; - } - if(ast->sample_size) - ast->frame_offset += pkt->size; - else - ast->frame_offset++; - } - return size; } } /* palette changed chunk */ @@ -548,11 +628,13 @@ resync: for each stream */ static int avi_read_idx1(AVFormatContext *s, int size) { + AVIContext *avi = s->priv_data; ByteIOContext *pb = &s->pb; int nb_index_entries, i; AVStream *st; AVIStream *ast; unsigned int index, tag, flags, pos, len; + unsigned last_pos= -1; nb_index_entries = size / 16; if (nb_index_entries <= 0) @@ -568,6 +650,9 @@ static int avi_read_idx1(AVFormatContext *s, int size) av_log(NULL, AV_LOG_DEBUG, "%d: tag=0x%x flags=0x%x pos=0x%x len=%d/", i, tag, flags, pos, len); #endif + if(i==0 && pos > avi->movi_list) + avi->movi_list= 0; //FIXME better check + index = ((tag & 0xff) - '0') * 10; index += ((tag >> 8) & 0xff) - '0'; if (index >= s->nb_streams) @@ -575,19 +660,44 @@ static int avi_read_idx1(AVFormatContext *s, int size) st = s->streams[index]; ast = st->priv_data; - if(ast->sample_size) - len /= ast->sample_size; - else - len = 1; #if defined(DEBUG_SEEK) av_log(NULL, AV_LOG_DEBUG, "%d cum_len=%d\n", len, ast->cum_len); #endif - av_add_index_entry(st, pos, ast->cum_len, 0, (flags&AVIIF_INDEX) ? AVINDEX_KEYFRAME : 0); - ast->cum_len += len; + if(len) + av_add_index_entry(st, pos, ast->cum_len, 0, (flags&AVIIF_INDEX) ? AVINDEX_KEYFRAME : 0); + if(ast->sample_size) + ast->cum_len += len / ast->sample_size; + else + ast->cum_len ++; + if(last_pos == pos) + avi->non_interleaved= 1; + last_pos= pos; } return 0; } +static int guess_ni_flag(AVFormatContext *s){ + AVIContext *avi = s->priv_data; + int i; + int64_t last_start=0; + int64_t first_end= INT64_MAX; + + for(i=0; inb_streams; i++){ + AVStream *st = s->streams[i]; + AVIStream *ast = st->priv_data; + int n= st->nb_index_entries; + + if(n <= 0) + continue; + + if(st->index_entries[0].pos > last_start) + last_start= st->index_entries[0].pos; + if(st->index_entries[n-1].pos < first_end) + first_end= st->index_entries[n-1].pos; + } + return last_start > first_end; +} + static int avi_load_index(AVFormatContext *s) { AVIContext *avi = s->priv_data; @@ -627,6 +737,7 @@ static int avi_load_index(AVFormatContext *s) } } the_end: + avi->non_interleaved |= guess_ni_flag(s); url_fseek(pb, pos, SEEK_SET); return 0; } @@ -659,6 +770,10 @@ static int avi_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp for(i = 0; i < s->nb_streams; i++) { AVStream *st2 = s->streams[i]; AVIStream *ast2 = st2->priv_data; + + ast2->packet_size= + ast2->remaining= 0; + if (st2->nb_index_entries <= 0) continue; @@ -671,12 +786,14 @@ static int avi_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp flags | AVSEEK_FLAG_BACKWARD); if(index<0) index=0; -#if 1 - while(index>0 && st2->index_entries[index].pos > pos) - index--; - while(index+1 < st2->nb_index_entries && st2->index_entries[index].pos < pos) - index++; -#endif + + if(!avi->non_interleaved){ + while(index>0 && st2->index_entries[index].pos > pos) + index--; + while(index+1 < st2->nb_index_entries && st2->index_entries[index].pos < pos) + index++; + } + // av_log(NULL, AV_LOG_DEBUG, "%Ld %d %Ld\n", timestamp, index, st2->index_entries[index].timestamp); /* extract the current frame number */ ast2->frame_offset = st2->index_entries[index].timestamp; @@ -689,6 +806,7 @@ static int avi_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp /* do the seek */ pos += avi->movi_list; url_fseek(&s->pb, pos, SEEK_SET); + avi->stream_index= -1; return 0; }