|
|
|
@@ -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; ///<pos of last 1/2 type frame |
|
|
|
} StreamContext; |
|
|
|
|
|
|
|
typedef struct { |
|
|
|
@@ -280,12 +281,25 @@ static int get_str(ByteIOContext *bc, char *string, int maxlen){ |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static uint64_t get_vb(ByteIOContext *bc){ |
|
|
|
uint64_t val=0; |
|
|
|
int i= get_v(bc); |
|
|
|
|
|
|
|
if(i>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<<stream->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,10 +1244,269 @@ 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; i<s->nb_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; i<s->nb_streams; i++) |
|
|
|
nut->stream[i].last_sync_pos= pos; |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static int nut_read_close(AVFormatContext *s) |
|
|
|
@@ -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", |
|
|
|
}; |
|
|
|
|
|
|
|
|