From 7dddf251a2e222488b7e611b04bb3b230b3db336 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer Date: Thu, 8 Apr 2004 00:22:22 +0000 Subject: [PATCH] fourcc & language code v->vb type some minor cleanups some bugfixes seeking to type 1 and 2 frames (no index needed) timestamp/position cache so seeking without an index is fast Originally committed as revision 2980 to svn://svn.ffmpeg.org/ffmpeg/trunk --- libavformat/nut.c | 370 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 332 insertions(+), 38 deletions(-) diff --git a/libavformat/nut.c b/libavformat/nut.c index a975c2c712..acb8db9f12 100644 --- a/libavformat/nut.c +++ b/libavformat/nut.c @@ -79,6 +79,7 @@ typedef struct { int lru_size[MAX_SIZE_LRU]; int initial_pts_predictor[MAX_PTS_LRU]; int initial_size_predictor[MAX_SIZE_LRU]; + int64_t last_sync_pos; ///8) + return UINT64_MAX; + + while(i--) + val = (val<<8) + get_byte(bc); + + return val; +} + static int get_packetheader(NUTContext *nut, ByteIOContext *bc, int prefix_length, int calculate_checksum) { int64_t start, size, last_size; start= url_ftell(bc) - prefix_length; - if(start != nut->packet_start + nut->written_packet_size){ + if(nut->written_packet_size >= 0 && start != nut->packet_start + nut->written_packet_size){ av_log(nut->avf, AV_LOG_ERROR, "get_packetheader called at weird position\n"); if(prefix_length<8) return -1; @@ -295,7 +309,7 @@ static int get_packetheader(NUTContext *nut, ByteIOContext *bc, int prefix_lengt size= get_v(bc); last_size= get_v(bc); - if(nut->written_packet_size != last_size){ + if(nut->written_packet_size >= 0 && nut->written_packet_size != last_size){ av_log(nut->avf, AV_LOG_ERROR, "packet size missmatch %d != %lld at %lld\n", nut->written_packet_size, last_size, start); if(prefix_length<8) return -1; @@ -319,11 +333,9 @@ static int check_checksum(ByteIOContext *bc){ static int get_length(uint64_t val){ int i; - for (i=7; ; i+=7) - if ((val>>i) == 0) - return i; + for (i=7; val>>i; i+=7); - return 7; //not reached + return i; } static uint64_t find_any_startcode(ByteIOContext *bc, int64_t pos){ @@ -345,15 +357,21 @@ static uint64_t find_any_startcode(ByteIOContext *bc, int64_t pos){ return state; } } - av_log(NULL, AV_LOG_DEBUG, "searched until %lld\n", url_ftell(bc)); + return 0; } -static int find_startcode(ByteIOContext *bc, uint64_t code, int64_t pos){ +/** + * find the given startcode. + * @param code the startcode + * @param pos the start position of the search, or -1 if the current position + * @returns the position of the startcode or -1 if not found + */ +static int64_t find_startcode(ByteIOContext *bc, uint64_t code, int64_t pos){ for(;;){ uint64_t startcode= find_any_startcode(bc, pos); if(startcode == code) - return 0; + return url_ftell(bc) - 8; else if(startcode == 0) return -1; pos=-1; @@ -361,7 +379,7 @@ static int find_startcode(ByteIOContext *bc, uint64_t code, int64_t pos){ } #ifdef CONFIG_ENCODERS -static int put_v(ByteIOContext *bc, uint64_t val) +static void put_v(ByteIOContext *bc, uint64_t val) { int i; @@ -373,17 +391,26 @@ static int put_v(ByteIOContext *bc, uint64_t val) } put_byte(bc, val&0x7f); - - return 0; } -static int put_str(ByteIOContext *bc, const char *string){ +/** + * stores a string as vb. + */ +static void put_str(ByteIOContext *bc, const char *string){ int len= strlen(string); put_v(bc, len); put_buffer(bc, string, len); +} + +static void put_vb(ByteIOContext *bc, uint64_t val){ + int i; - return 0; + for (i=8; val>>i; i+=8); + + put_v(bc, i>>3); + for(i-=8; i>=0; i-=8) + put_byte(bc, (val>>i)&0xFF); } static int put_packetheader(NUTContext *nut, ByteIOContext *bc, int max_size, int calculate_checksum) @@ -492,17 +519,17 @@ static int nut_write_header(AVFormatContext *s) put_v(bc, i /*s->streams[i]->index*/); put_v(bc, (codec->codec_type == CODEC_TYPE_AUDIO) ? 32 : 0); if (codec->codec_tag) - put_v(bc, codec->codec_tag); + put_vb(bc, codec->codec_tag); else if (codec->codec_type == CODEC_TYPE_VIDEO) { - put_v(bc, codec_get_bmp_tag(codec->codec_id)); + put_vb(bc, codec_get_bmp_tag(codec->codec_id)); } else if (codec->codec_type == CODEC_TYPE_AUDIO) { - put_v(bc, codec_get_wav_tag(codec->codec_id)); + put_vb(bc, codec_get_wav_tag(codec->codec_id)); } else - put_v(bc, 0); + put_vb(bc, 0); if (codec->codec_type == CODEC_TYPE_VIDEO) { @@ -524,7 +551,7 @@ static int nut_write_header(AVFormatContext *s) nut->stream[i].rate_den= denom; put_v(bc, codec->bit_rate); - put_v(bc, 0); /* no language code */ + put_vb(bc, 0); /* no language code */ put_v(bc, nom); put_v(bc, denom); if(nom / denom < 1000) @@ -875,7 +902,7 @@ static int decode_stream_header(NUTContext *nut){ if (!st) return AVERROR_NOMEM; class = get_v(bc); - tmp = get_v(bc); + tmp = get_vb(bc); switch(class) { case 0: @@ -895,7 +922,7 @@ static int decode_stream_header(NUTContext *nut){ return -1; } s->bit_rate += get_v(bc); - get_v(bc); /* language code */ + get_vb(bc); /* language code */ nom = get_v(bc); denom = get_v(bc); nut->stream[stream_id].msb_timestamp_shift = get_v(bc); @@ -1004,11 +1031,11 @@ static int nut_read_header(AVFormatContext *s, AVFormatParameters *ap) /* main header */ pos=0; for(;;){ - if (find_startcode(bc, MAIN_STARTCODE, pos)<0){ + pos= find_startcode(bc, MAIN_STARTCODE, pos)+1; + if (pos<0){ av_log(s, AV_LOG_ERROR, "no main startcode found\n"); return -1; } - pos= url_ftell(bc); if(decode_main_header(nut) >= 0) break; } @@ -1021,11 +1048,11 @@ static int nut_read_header(AVFormatContext *s, AVFormatParameters *ap) /* stream headers */ pos=0; for(inited_stream_count=0; inited_stream_count < nut->stream_count;){ - if (find_startcode(bc, STREAM_STARTCODE, pos)<0){ + pos= find_startcode(bc, STREAM_STARTCODE, pos)+1; + if (pos<0){ av_log(s, AV_LOG_ERROR, "not all stream headers found\n"); return -1; } - pos= url_ftell(bc); if(decode_stream_header(nut) >= 0) inited_stream_count++; } @@ -1060,7 +1087,7 @@ static int decode_frame(NUTContext *nut, AVPacket *pkt, int frame_code, int fram int key_frame = 0; int64_t pts = 0; const int prefix_len= frame_type == 2 ? 8+1 : 1; - const int64_t frame_start= url_ftell(bc) + prefix_len; + const int64_t frame_start= url_ftell(bc) - prefix_len; flags= nut->frame_code[frame_code].flags; size_mul= nut->frame_code[frame_code].size_mul; @@ -1097,6 +1124,16 @@ static int decode_frame(NUTContext *nut, AVPacket *pkt, int frame_code, int fram if(flags & FLAG_PTS){ if(flags & FLAG_FULL_PTS){ pts= get_v(bc); + if(frame_type && key_frame){ + av_add_index_entry( + s->streams[stream_id], + frame_start, + pts, + frame_start - nut->stream[stream_id].last_sync_pos, + AVINDEX_KEYFRAME); + nut->stream[stream_id].last_sync_pos= frame_start; + assert(nut->packet_start == frame_start); + } }else{ int64_t mask = (1<msb_timestamp_shift)-1; int64_t delta= stream->last_pts - mask/2; @@ -1138,11 +1175,11 @@ static int nut_read_packet(AVFormatContext *s, AVPacket *pkt) { NUTContext *nut = s->priv_data; ByteIOContext *bc = &s->pb; - int size, frame_code=0; - int frame_type= 0; + int size, i, frame_code=0; int64_t pos; for(;;){ + int frame_type= 0; uint64_t tmp= nut->next_startcode; nut->next_startcode=0; @@ -1153,15 +1190,11 @@ static int nut_read_packet(AVFormatContext *s, AVPacket *pkt) frame_code = get_byte(bc); if(frame_code == 'N'){ tmp= frame_code; - tmp<<=8 ; tmp |= get_byte(bc); - tmp<<=16; tmp |= get_be16(bc); - tmp<<=32; tmp |= get_be32(bc); + for(i=1; i<8; i++) + tmp = (tmp<<8) + get_byte(bc); } } switch(tmp){ - case KEYFRAME_STARTCODE: - frame_type = 2; - break; case MAIN_STARTCODE: case STREAM_STARTCODE: case INDEX_STARTCODE: @@ -1172,12 +1205,14 @@ static int nut_read_packet(AVFormatContext *s, AVPacket *pkt) if(decode_info_header(nut)<0) goto resync; break; + case KEYFRAME_STARTCODE: + frame_type = 2; + frame_code = get_byte(bc); case 0: if(decode_frame(nut, pkt, frame_code, frame_type)>=0) return 0; default: resync: - frame_type = 0; av_log(s, AV_LOG_DEBUG, "syncing from %lld\n", nut->packet_start+1); tmp= find_any_startcode(bc, nut->packet_start+1); if(tmp==0) @@ -1209,12 +1244,271 @@ av_log(s, AV_LOG_DEBUG, "steping back to %lld next %d\n", pos, size); } url_fseek(bc, pos, SEEK_SET); - nut->written_packet_size= size; - nut->packet_start= pos - size; + nut->written_packet_size= -1; } } } +static int64_t read_timestamp(AVFormatContext *s, int stream_index, int64_t *pos_arg, int64_t pos_limit){ + NUTContext *nut = s->priv_data; + ByteIOContext *bc = &s->pb; + int64_t pos, pts; + uint64_t code; + int frame_code,step, flags, stream_id, i; +av_log(s, AV_LOG_DEBUG, "read_timestamp(X,%d,%lld,%lld)\n", stream_index, *pos_arg, pos_limit); + + if(*pos_arg < 0) + return AV_NOPTS_VALUE; + + // find a previous startcode, FIXME use forward search and follow backward pointers if undamaged stream + pos= *pos_arg; + step= FFMIN(16*1024, pos); + do{ + pos-= step; + code= find_any_startcode(bc, pos); + + if(code && url_ftell(bc) - 8 < *pos_arg) + break; + step= FFMIN(2*step, pos); + }while(step); + + if(!code) //nothing found, not even after pos_arg + return AV_NOPTS_VALUE; + + url_fseek(bc, -8, SEEK_CUR); + for(i=0; inb_streams; i++) + nut->stream[i].last_sync_pos= url_ftell(bc); + + for(;;){ + int64_t pos= url_ftell(bc); + uint64_t tmp=0; + int prefix_len=1; + + if(pos > pos_limit) + return AV_NOPTS_VALUE; + + frame_code = get_byte(bc); + if(frame_code == 'N'){ + tmp= frame_code; + for(i=1; i<8; i++) + tmp = (tmp<<8) + get_byte(bc); + } +//av_log(s, AV_LOG_DEBUG, "before switch %llX at=%lld\n", tmp, pos); + + switch(tmp){ + case MAIN_STARTCODE: + case STREAM_STARTCODE: + case INDEX_STARTCODE: + case INFO_STARTCODE: + nut->written_packet_size= -1; + get_packetheader(nut, bc, 8, 0); + url_fseek(bc, nut->written_packet_size + nut->packet_start, SEEK_SET); + break; + case KEYFRAME_STARTCODE: + nut->written_packet_size= -1; + prefix_len+=8; + frame_code = get_byte(bc); + case 0: + flags= nut->frame_code[frame_code].flags; + stream_id= nut->frame_code[frame_code].stream_id_plus1 - 1; + + if(get_packetheader(nut, bc, prefix_len, 0) < 0) + goto resync; + + if(!(flags & FLAG_FRAME_TYPE) || !(flags & FLAG_PTS) || !(flags & FLAG_FULL_PTS)) + goto resync; + + if(stream_id==-1) + stream_id= get_v(bc); + if(stream_id >= s->nb_streams) + goto resync; + + pts= get_v(bc); + + if(flags & FLAG_KEY_FRAME){ + av_add_index_entry( + s->streams[stream_id], + pos, + pts, + pos - nut->stream[stream_id].last_sync_pos, + AVINDEX_KEYFRAME); + nut->stream[stream_id].last_sync_pos= pos; + } + if(stream_id != stream_index || !(flags & FLAG_KEY_FRAME) || nut->packet_start < *pos_arg){ + url_fseek(bc, nut->written_packet_size + nut->packet_start, SEEK_SET); + break; + } + + *pos_arg= nut->packet_start; + assert(nut->packet_start == pos); + return pts; + default: +resync: +av_log(s, AV_LOG_DEBUG, "syncing from %lld\n", nut->packet_start+1); + if(!find_any_startcode(bc, nut->packet_start+1)) + return AV_NOPTS_VALUE; + + url_fseek(bc, -8, SEEK_CUR); + } + } + return AV_NOPTS_VALUE; +} + +#define DEBUG_SEEK +static int nut_read_seek(AVFormatContext *s, int stream_index, int64_t target_ts){ + NUTContext *nut = s->priv_data; + StreamContext *stream; + int64_t pos_min, pos_max, pos, pos_limit; + int64_t ts_min, ts_max, ts; + int index, no_change,i; + AVStream *st; + + if (stream_index < 0) { + stream_index = av_find_default_stream_index(s); + if (stream_index < 0) + return -1; + } + stream= &nut->stream[stream_index]; + target_ts= (av_rescale(target_ts, stream->rate_num, stream->rate_den) + AV_TIME_BASE/2) / AV_TIME_BASE; + +#ifdef DEBUG_SEEK + av_log(s, AV_LOG_DEBUG, "read_seek: %d %lld\n", stream_index, target_ts); +#endif + + ts_max= + ts_min= AV_NOPTS_VALUE; + pos_limit= -1; //gcc falsely says it may be uninitalized + + st= s->streams[stream_index]; + if(st->index_entries){ + AVIndexEntry *e; + + index= av_index_search_timestamp(st, target_ts); + e= &st->index_entries[index]; + + if(e->timestamp <= target_ts || e->pos == e->min_distance){ + pos_min= e->pos; + ts_min= e->timestamp; +#ifdef DEBUG_SEEK + av_log(s, AV_LOG_DEBUG, "unsing cached pos_min=0x%llx dts_min=%lld\n", + pos_min,ts_min); +#endif + }else{ + assert(index==0); + } + index++; + if(index < st->nb_index_entries){ + e= &st->index_entries[index]; + assert(e->timestamp >= target_ts); + pos_max= e->pos; + ts_max= e->timestamp; + pos_limit= pos_max - e->min_distance; +#ifdef DEBUG_SEEK + av_log(s, AV_LOG_DEBUG, "unsing cached pos_max=0x%llx pos_limit=0x%llx dts_max=%lld\n", + pos_max,pos_limit, ts_max); +#endif + } + } + + if(ts_min == AV_NOPTS_VALUE){ + pos_min = 0; + ts_min = read_timestamp(s, stream_index, &pos_min, INT64_MAX); + if (ts_min == AV_NOPTS_VALUE) + return -1; + } + + if(ts_max == AV_NOPTS_VALUE){ + int step= 1024; + pos_max = url_filesize(url_fileno(&s->pb)) - 1; + do{ + pos_max -= step; + ts_max = read_timestamp(s, stream_index, &pos_max, pos_max + step); + step += step; + }while(ts_max == AV_NOPTS_VALUE && pos_max >= step); + if (ts_max == AV_NOPTS_VALUE) + return -1; + + for(;;){ + int64_t tmp_pos= pos_max + 1; + int64_t tmp_ts= read_timestamp(s, stream_index, &tmp_pos, INT64_MAX); + if(tmp_ts == AV_NOPTS_VALUE) + break; + ts_max= tmp_ts; + pos_max= tmp_pos; + } + pos_limit= pos_max; + } + + no_change=0; + while (pos_min < pos_limit) { +#ifdef DEBUG_SEEK + av_log(s, AV_LOG_DEBUG, "pos_min=0x%llx pos_max=0x%llx dts_min=%lld dts_max=%lld\n", + pos_min, pos_max, + ts_min, ts_max); +#endif + int64_t start_pos; + assert(pos_limit <= pos_max); + + if(no_change==0){ + int64_t approximate_keyframe_distance= pos_max - pos_limit; + // interpolate position (better than dichotomy) + pos = (int64_t)((double)(pos_max - pos_min) * + (double)(target_ts - ts_min) / + (double)(ts_max - ts_min)) + pos_min - approximate_keyframe_distance; + }else if(no_change==1){ + // bisection, if interpolation failed to change min or max pos last time + pos = (pos_min + pos_limit)>>1; + }else{ + // linear search if bisection failed, can only happen if there are very few or no keframes between min/max + pos=pos_min; + } + if(pos <= pos_min) + pos= pos_min + 1; + else if(pos > pos_limit) + pos= pos_limit; + start_pos= pos; + + ts = read_timestamp(s, stream_index, &pos, INT64_MAX); //may pass pos_limit instead of -1 + if(pos == pos_max) + no_change++; + else + no_change=0; +#ifdef DEBUG_SEEK +av_log(s, AV_LOG_DEBUG, "%Ld %Ld %Ld / %Ld %Ld %Ld target:%Ld limit:%Ld start:%Ld noc:%d\n", pos_min, pos, pos_max, ts_min, ts, ts_max, target_ts, pos_limit, start_pos, no_change); +#endif + assert(ts != AV_NOPTS_VALUE); + if (target_ts < ts) { + pos_limit = start_pos - 1; + pos_max = pos; + ts_max = ts; + } else { + pos_min = pos; + ts_min = ts; + /* check if we are lucky */ + if (target_ts == ts) + break; + } + } + + pos = pos_min; +#ifdef DEBUG_SEEK + pos_min = pos; + ts_min = read_timestamp(s, stream_index, &pos_min, INT64_MAX); + pos_min++; + ts_max = read_timestamp(s, stream_index, &pos_min, INT64_MAX); + av_log(s, AV_LOG_DEBUG, "pos=0x%llx %lld<=%lld<=%lld\n", + pos, ts_min, target_ts, ts_max); +#endif + /* do the seek */ + url_fseek(&s->pb, pos, SEEK_SET); + + nut->written_packet_size= -1; + for(i=0; inb_streams; i++) + nut->stream[i].last_sync_pos= pos; + + return 0; +} + static int nut_read_close(AVFormatContext *s) { NUTContext *nut = s->priv_data; @@ -1236,7 +1530,7 @@ static AVInputFormat nut_iformat = { nut_read_header, nut_read_packet, nut_read_close, -// nut_read_seek, + nut_read_seek, .extensions = "nut", };