mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-11-21 10:55:51 +02:00
initial seek support
Originally committed as revision 2502 to svn://svn.ffmpeg.org/ffmpeg/trunk
This commit is contained in:
parent
27f388aa24
commit
155e9ee909
@ -13,6 +13,9 @@
|
||||
#define AVI_MAX_RIFF_SIZE 0x40000000LL
|
||||
#define AVI_MASTER_INDEX_SIZE 256
|
||||
|
||||
/* index flags */
|
||||
#define AVIIF_INDEX 0x10
|
||||
|
||||
offset_t start_tag(ByteIOContext *pb, const char *tag);
|
||||
void end_tag(ByteIOContext *pb, offset_t start);
|
||||
|
||||
|
@ -21,18 +21,33 @@
|
||||
#include "dv.h"
|
||||
|
||||
//#define DEBUG
|
||||
//#define DEBUG_SEEK
|
||||
|
||||
typedef struct AVIIndex {
|
||||
unsigned char tag[4];
|
||||
unsigned int flags, pos, len;
|
||||
struct AVIIndex *next;
|
||||
} AVIIndex;
|
||||
typedef struct AVIIndexEntry {
|
||||
unsigned int flags;
|
||||
unsigned int pos;
|
||||
unsigned int cum_len; /* sum of all lengths before this packet */
|
||||
} AVIIndexEntry;
|
||||
|
||||
typedef struct AVIStream {
|
||||
AVIIndexEntry *index_entries;
|
||||
int nb_index_entries;
|
||||
int index_entries_allocated_size;
|
||||
int frame_offset; /* current frame (video) or byte (audio) counter
|
||||
(used to compute the pts) */
|
||||
int scale;
|
||||
int rate;
|
||||
int sample_size; /* audio only data */
|
||||
|
||||
int new_frame_offset; /* temporary storage (used during seek) */
|
||||
int cum_len; /* temporary storage (used during seek) */
|
||||
} AVIStream;
|
||||
|
||||
typedef struct {
|
||||
int64_t riff_end;
|
||||
int64_t movi_end;
|
||||
offset_t movi_list;
|
||||
AVIIndex *first, *last;
|
||||
int index_loaded;
|
||||
DVDemuxContext* dv_demux;
|
||||
} AVIContext;
|
||||
|
||||
@ -74,8 +89,11 @@ static int avi_read_header(AVFormatContext *s, AVFormatParameters *ap)
|
||||
unsigned int size, nb_frames;
|
||||
int i, n;
|
||||
AVStream *st;
|
||||
AVIStream *ast;
|
||||
int xan_video = 0; /* hack to support Xan A/V */
|
||||
|
||||
av_set_pts_info(s, 64, 1, AV_TIME_BASE);
|
||||
|
||||
if (get_riff(avi, pb) < 0)
|
||||
return -1;
|
||||
|
||||
@ -100,7 +118,8 @@ static int avi_read_header(AVFormatContext *s, AVFormatParameters *ap)
|
||||
print_tag("list", tag1, 0);
|
||||
#endif
|
||||
if (tag1 == MKTAG('m', 'o', 'v', 'i')) {
|
||||
avi->movi_end = url_ftell(pb) + size - 4;
|
||||
avi->movi_list = url_ftell(pb) - 4;
|
||||
avi->movi_end = avi->movi_list + size;
|
||||
#ifdef DEBUG
|
||||
printf("movi end=%Lx\n", avi->movi_end);
|
||||
#endif
|
||||
@ -115,9 +134,14 @@ static int avi_read_header(AVFormatContext *s, AVFormatParameters *ap)
|
||||
url_fskip(pb, 4 * 4);
|
||||
n = get_le32(pb);
|
||||
for(i=0;i<n;i++) {
|
||||
AVIStream *ast;
|
||||
st = av_new_stream(s, i);
|
||||
if (!st)
|
||||
goto fail;
|
||||
ast = av_mallocz(sizeof(AVIStream));
|
||||
if (!ast)
|
||||
goto fail;
|
||||
st->priv_data = ast;
|
||||
}
|
||||
url_fskip(pb, size - 7 * 4);
|
||||
break;
|
||||
@ -159,7 +183,8 @@ static int avi_read_header(AVFormatContext *s, AVFormatParameters *ap)
|
||||
}
|
||||
|
||||
st = s->streams[stream_index];
|
||||
|
||||
ast = st->priv_data;
|
||||
|
||||
get_le32(pb); /* flags */
|
||||
get_le16(pb); /* priority */
|
||||
get_le16(pb); /* language */
|
||||
@ -168,27 +193,28 @@ static int avi_read_header(AVFormatContext *s, AVFormatParameters *ap)
|
||||
rate = get_le32(pb); /* rate */
|
||||
|
||||
if(scale && rate){
|
||||
st->codec.frame_rate = rate;
|
||||
st->codec.frame_rate_base = scale;
|
||||
}else if(frame_period){
|
||||
st->codec.frame_rate = 1000000;
|
||||
st->codec.frame_rate_base = frame_period;
|
||||
rate = 1000000;
|
||||
scale = frame_period;
|
||||
}else{
|
||||
st->codec.frame_rate = 25;
|
||||
st->codec.frame_rate_base = 1;
|
||||
rate = 25;
|
||||
scale = 1;
|
||||
}
|
||||
ast->rate = rate;
|
||||
ast->scale = scale;
|
||||
st->codec.frame_rate = rate;
|
||||
st->codec.frame_rate_base = scale;
|
||||
get_le32(pb); /* start */
|
||||
nb_frames = get_le32(pb);
|
||||
st->start_time = 0;
|
||||
st->duration = (double)nb_frames *
|
||||
st->codec.frame_rate_base * AV_TIME_BASE /
|
||||
st->codec.frame_rate;
|
||||
|
||||
url_fskip(pb, size - 9 * 4);
|
||||
break;
|
||||
case MKTAG('a', 'u', 'd', 's'):
|
||||
{
|
||||
unsigned int length, rate;
|
||||
unsigned int length;
|
||||
|
||||
codec_type = CODEC_TYPE_AUDIO;
|
||||
|
||||
@ -197,19 +223,23 @@ static int avi_read_header(AVFormatContext *s, AVFormatParameters *ap)
|
||||
break;
|
||||
}
|
||||
st = s->streams[stream_index];
|
||||
|
||||
ast = st->priv_data;
|
||||
|
||||
get_le32(pb); /* flags */
|
||||
get_le16(pb); /* priority */
|
||||
get_le16(pb); /* language */
|
||||
get_le32(pb); /* initial frame */
|
||||
get_le32(pb); /* scale */
|
||||
rate = get_le32(pb);
|
||||
ast->scale = get_le32(pb); /* scale */
|
||||
ast->rate = get_le32(pb);
|
||||
get_le32(pb); /* start */
|
||||
length = get_le32(pb); /* length, in samples or bytes */
|
||||
get_le32(pb); /* buffer size */
|
||||
get_le32(pb); /* quality */
|
||||
ast->sample_size = get_le32(pb); /* sample ssize */
|
||||
st->start_time = 0;
|
||||
if (rate != 0)
|
||||
st->duration = (int64_t)length * AV_TIME_BASE / rate;
|
||||
url_fskip(pb, size - 9 * 4);
|
||||
if (ast->rate != 0)
|
||||
st->duration = (int64_t)length * AV_TIME_BASE / ast->rate;
|
||||
url_fskip(pb, size - 12 * 4);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -274,6 +304,9 @@ static int avi_read_header(AVFormatContext *s, AVFormatParameters *ap)
|
||||
url_fskip(pb, 1);
|
||||
/* special case time: To support Xan DPCM, hardcode
|
||||
* the format if Xxan is the video codec */
|
||||
st->need_parsing = 1;
|
||||
/* force parsing as several audio frames can be in
|
||||
one packet */
|
||||
if (xan_video)
|
||||
st->codec.codec_id = CODEC_ID_XAN_DPCM;
|
||||
break;
|
||||
@ -377,10 +410,31 @@ static int avi_read_packet(AVFormatContext *s, AVPacket *pkt)
|
||||
size = dv_produce_packet(avi->dv_demux, pkt,
|
||||
pkt->data, pkt->size);
|
||||
pkt->destruct = dstr;
|
||||
pkt->flags |= PKT_FLAG_KEY;
|
||||
} else {
|
||||
AVStream *st;
|
||||
AVIStream *ast;
|
||||
st = s->streams[n];
|
||||
ast = st->priv_data;
|
||||
|
||||
/* XXX: how to handle B frames in avi ? */
|
||||
pkt->pts = ((int64_t)ast->frame_offset * ast->scale* AV_TIME_BASE) / ast->rate;
|
||||
pkt->stream_index = n;
|
||||
pkt->flags |= PKT_FLAG_KEY; // FIXME: We really should read
|
||||
// index for that
|
||||
/* FIXME: We really should read index for that */
|
||||
if (st->codec.codec_type == CODEC_TYPE_VIDEO) {
|
||||
if (ast->frame_offset < ast->nb_index_entries) {
|
||||
if (ast->index_entries[ast->frame_offset].flags & AVIIF_INDEX)
|
||||
pkt->flags |= PKT_FLAG_KEY;
|
||||
} else {
|
||||
/* if no index, better to say that all frames
|
||||
are key frames */
|
||||
pkt->flags |= PKT_FLAG_KEY;
|
||||
}
|
||||
ast->frame_offset++;
|
||||
} else {
|
||||
ast->frame_offset += pkt->size;
|
||||
pkt->flags |= PKT_FLAG_KEY;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
@ -388,6 +442,214 @@ static int avi_read_packet(AVFormatContext *s, AVPacket *pkt)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* XXX: we make the implicit supposition that the position are sorted
|
||||
for each stream */
|
||||
static int avi_read_idx1(AVFormatContext *s, int size)
|
||||
{
|
||||
ByteIOContext *pb = &s->pb;
|
||||
int nb_index_entries, i;
|
||||
AVStream *st;
|
||||
AVIStream *ast;
|
||||
AVIIndexEntry *ie, *entries;
|
||||
unsigned int index, tag, flags, pos, len;
|
||||
|
||||
nb_index_entries = size / 16;
|
||||
if (nb_index_entries <= 0)
|
||||
return -1;
|
||||
|
||||
/* read the entries and sort them in each stream component */
|
||||
for(i = 0; i < nb_index_entries; i++) {
|
||||
tag = get_le32(pb);
|
||||
flags = get_le32(pb);
|
||||
pos = get_le32(pb);
|
||||
len = get_le32(pb);
|
||||
#if defined(DEBUG_SEEK) && 0
|
||||
printf("%d: tag=0x%x flags=0x%x pos=0x%x len=%d\n",
|
||||
i, tag, flags, pos, len);
|
||||
#endif
|
||||
index = ((tag & 0xff) - '0') * 10;
|
||||
index += ((tag >> 8) & 0xff) - '0';
|
||||
if (index >= s->nb_streams)
|
||||
continue;
|
||||
st = s->streams[index];
|
||||
ast = st->priv_data;
|
||||
|
||||
entries = av_fast_realloc(ast->index_entries,
|
||||
&ast->index_entries_allocated_size,
|
||||
(ast->nb_index_entries + 1) *
|
||||
sizeof(AVIIndexEntry));
|
||||
if (entries) {
|
||||
ast->index_entries = entries;
|
||||
ie = &entries[ast->nb_index_entries++];
|
||||
ie->flags = flags;
|
||||
ie->pos = pos;
|
||||
ie->cum_len = ast->cum_len;
|
||||
ast->cum_len += len;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avi_load_index(AVFormatContext *s)
|
||||
{
|
||||
AVIContext *avi = s->priv_data;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
uint32_t tag, size;
|
||||
|
||||
url_fseek(pb, avi->movi_end, SEEK_SET);
|
||||
#ifdef DEBUG_SEEK
|
||||
printf("movi_end=0x%llx\n", avi->movi_end);
|
||||
#endif
|
||||
for(;;) {
|
||||
if (url_feof(pb))
|
||||
break;
|
||||
tag = get_le32(pb);
|
||||
size = get_le32(pb);
|
||||
#ifdef DEBUG_SEEK
|
||||
printf("tag=%c%c%c%c size=0x%x\n",
|
||||
tag & 0xff,
|
||||
(tag >> 8) & 0xff,
|
||||
(tag >> 16) & 0xff,
|
||||
(tag >> 24) & 0xff,
|
||||
size);
|
||||
#endif
|
||||
switch(tag) {
|
||||
case MKTAG('i', 'd', 'x', '1'):
|
||||
if (avi_read_idx1(s, size) < 0)
|
||||
goto skip;
|
||||
else
|
||||
goto the_end;
|
||||
break;
|
||||
default:
|
||||
skip:
|
||||
size += (size & 1);
|
||||
url_fskip(pb, size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
the_end:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return the index entry whose position is immediately >= 'wanted_pos' */
|
||||
static int locate_frame_in_index(AVIIndexEntry *entries,
|
||||
int nb_entries, int wanted_pos)
|
||||
{
|
||||
int a, b, m, pos;
|
||||
|
||||
a = 0;
|
||||
b = nb_entries - 1;
|
||||
while (a <= b) {
|
||||
m = (a + b) >> 1;
|
||||
pos = entries[m].pos;
|
||||
if (pos == wanted_pos)
|
||||
goto found;
|
||||
else if (pos > wanted_pos) {
|
||||
b = m - 1;
|
||||
} else {
|
||||
a = m + 1;
|
||||
}
|
||||
}
|
||||
m = a;
|
||||
if (m > 0)
|
||||
m--;
|
||||
found:
|
||||
return m;
|
||||
}
|
||||
|
||||
static int avi_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp)
|
||||
{
|
||||
AVIContext *avi = s->priv_data;
|
||||
AVStream *st;
|
||||
AVIStream *ast;
|
||||
int frame_number, i;
|
||||
int64_t pos;
|
||||
|
||||
if (!avi->index_loaded) {
|
||||
/* we only load the index on demand */
|
||||
avi_load_index(s);
|
||||
avi->index_loaded = 1;
|
||||
}
|
||||
if (stream_index < 0) {
|
||||
for(i = 0; i < s->nb_streams; i++) {
|
||||
st = s->streams[i];
|
||||
if (st->codec.codec_type == CODEC_TYPE_VIDEO)
|
||||
goto found;
|
||||
}
|
||||
return -1;
|
||||
found:
|
||||
stream_index = i;
|
||||
}
|
||||
|
||||
st = s->streams[stream_index];
|
||||
if (st->codec.codec_type != CODEC_TYPE_VIDEO)
|
||||
return -1;
|
||||
ast = st->priv_data;
|
||||
/* compute the frame number */
|
||||
frame_number = (timestamp * ast->rate) /
|
||||
(ast->scale * (int64_t)AV_TIME_BASE);
|
||||
#ifdef DEBUG_SEEK
|
||||
printf("timestamp=%0.3f nb_indexes=%d frame_number=%d\n",
|
||||
(double)timestamp / AV_TIME_BASE,
|
||||
ast->nb_index_entries, frame_number);
|
||||
#endif
|
||||
/* find a closest key frame before */
|
||||
if (frame_number >= ast->nb_index_entries)
|
||||
return -1;
|
||||
while (frame_number >= 0 &&
|
||||
!(ast->index_entries[frame_number].flags & AVIIF_INDEX))
|
||||
frame_number--;
|
||||
if (frame_number < 0)
|
||||
return -1;
|
||||
ast->new_frame_offset = frame_number;
|
||||
|
||||
/* find the position */
|
||||
pos = ast->index_entries[frame_number].pos;
|
||||
|
||||
#ifdef DEBUG_SEEK
|
||||
printf("key_frame_number=%d pos=0x%llx\n",
|
||||
frame_number, pos);
|
||||
#endif
|
||||
|
||||
/* update the frame counters for all the other stream by looking
|
||||
at the positions just after the one found */
|
||||
for(i = 0; i < s->nb_streams; i++) {
|
||||
int j;
|
||||
if (i != stream_index) {
|
||||
st = s->streams[i];
|
||||
ast = st->priv_data;
|
||||
if (ast->nb_index_entries <= 0)
|
||||
return -1;
|
||||
j = locate_frame_in_index(ast->index_entries,
|
||||
ast->nb_index_entries,
|
||||
pos);
|
||||
/* get next frame */
|
||||
if ((j + 1) < ast->nb_index_entries)
|
||||
j++;
|
||||
/* extract the current frame number */
|
||||
if (st->codec.codec_type == CODEC_TYPE_VIDEO)
|
||||
ast->new_frame_offset = j;
|
||||
else
|
||||
ast->new_frame_offset = ast->index_entries[j].cum_len;
|
||||
}
|
||||
}
|
||||
|
||||
/* everything is OK now. We can update the frame offsets */
|
||||
for(i = 0; i < s->nb_streams; i++) {
|
||||
st = s->streams[i];
|
||||
ast = st->priv_data;
|
||||
ast->frame_offset = ast->new_frame_offset;
|
||||
#ifdef DEBUG_SEEK
|
||||
printf("%d: frame_offset=%d\n", i,
|
||||
ast->frame_offset);
|
||||
#endif
|
||||
}
|
||||
/* do the seek */
|
||||
pos += avi->movi_list;
|
||||
url_fseek(&s->pb, pos, SEEK_SET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avi_read_close(AVFormatContext *s)
|
||||
{
|
||||
int i;
|
||||
@ -395,7 +657,9 @@ static int avi_read_close(AVFormatContext *s)
|
||||
|
||||
for(i=0;i<s->nb_streams;i++) {
|
||||
AVStream *st = s->streams[i];
|
||||
// av_free(st->priv_data);
|
||||
AVIStream *ast = st->priv_data;
|
||||
av_free(ast->index_entries);
|
||||
av_free(ast);
|
||||
av_free(st->codec.extradata);
|
||||
av_free(st->codec.palctrl);
|
||||
}
|
||||
@ -428,6 +692,7 @@ static AVInputFormat avi_iformat = {
|
||||
avi_read_header,
|
||||
avi_read_packet,
|
||||
avi_read_close,
|
||||
avi_read_seek,
|
||||
};
|
||||
|
||||
int avidec_init(void)
|
||||
|
Loading…
Reference in New Issue
Block a user