lavf/concatdec: support seeking.

This commit is contained in:
Nicolas George 2013-02-14 16:00:06 +01:00
parent 710cd0fddf
commit de1a71595d
2 changed files with 99 additions and 2 deletions

View File

@ -78,6 +78,9 @@ Duration of the file. This information can be specified from the file;
specifying it here may be more efficient or help if the information from the
file is not available or accurate.
If the duration is set for all files, then it is possible to seek in the
whole concatenated video.
@end table
@subsection Options

View File

@ -37,6 +37,7 @@ typedef struct {
unsigned nb_files;
AVFormatContext *avf;
int safe;
int seekable;
} ConcatContext;
static int concat_probe(AVProbeData *probe)
@ -128,6 +129,8 @@ static int open_file(AVFormatContext *avf, unsigned fileno)
ConcatFile *file = &cat->files[fileno];
int ret;
if (cat->avf)
avformat_close_input(&cat->avf);
if ((ret = avformat_open_input(&cat->avf, file->url, NULL, NULL)) < 0 ||
(ret = avformat_find_stream_info(cat->avf, NULL)) < 0) {
av_log(avf, AV_LOG_ERROR, "Impossible to open '%s'\n", file->url);
@ -223,8 +226,10 @@ static int concat_read_header(AVFormatContext *avf)
break;
time += cat->files[i].duration;
}
if (i == cat->nb_files)
if (i == cat->nb_files) {
avf->duration = time;
cat->seekable = 1;
}
if ((ret = open_file(avf, 0)) < 0)
FAIL(ret);
@ -257,7 +262,6 @@ static int open_next_file(AVFormatContext *avf)
if (++fileno >= cat->nb_files)
return AVERROR_EOF;
avformat_close_input(&cat->avf);
return open_file(avf, fileno);
}
@ -282,6 +286,95 @@ static int concat_read_packet(AVFormatContext *avf, AVPacket *pkt)
return ret;
}
static void rescale_interval(AVRational tb_in, AVRational tb_out,
int64_t *min_ts, int64_t *ts, int64_t *max_ts)
{
*ts = av_rescale_q (* ts, tb_in, tb_out);
*min_ts = av_rescale_q_rnd(*min_ts, tb_in, tb_out,
AV_ROUND_UP | AV_ROUND_PASS_MINMAX);
*max_ts = av_rescale_q_rnd(*max_ts, tb_in, tb_out,
AV_ROUND_DOWN | AV_ROUND_PASS_MINMAX);
}
static int try_seek(AVFormatContext *avf, int stream,
int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
{
ConcatContext *cat = avf->priv_data;
int64_t t0 = cat->cur_file->start_time - cat->avf->start_time;
ts -= t0;
min_ts = min_ts == INT64_MIN ? INT64_MIN : min_ts - t0;
max_ts = max_ts == INT64_MAX ? INT64_MAX : max_ts - t0;
if (stream >= 0) {
if (stream >= cat->avf->nb_streams)
return AVERROR(EIO);
rescale_interval(AV_TIME_BASE_Q, cat->avf->streams[stream]->time_base,
&min_ts, &ts, &max_ts);
}
return avformat_seek_file(cat->avf, stream, min_ts, ts, max_ts, flags);
}
static int real_seek(AVFormatContext *avf, int stream,
int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
{
ConcatContext *cat = avf->priv_data;
int ret, left, right;
if (stream >= 0) {
if (stream >= avf->nb_streams)
return AVERROR(EINVAL);
rescale_interval(avf->streams[stream]->time_base, AV_TIME_BASE_Q,
&min_ts, &ts, &max_ts);
}
left = 0;
right = cat->nb_files;
while (right - left > 1) {
int mid = (left + right) / 2;
if (ts < cat->files[mid].start_time)
right = mid;
else
left = mid;
}
if ((ret = open_file(avf, left)) < 0)
return ret;
ret = try_seek(avf, stream, min_ts, ts, max_ts, flags);
if (ret < 0 && !(flags & AVSEEK_FLAG_BACKWARD) &&
left < cat->nb_files - 1 &&
cat->files[left + 1].start_time < max_ts) {
if ((ret = open_file(avf, left + 1)) < 0)
return ret;
ret = try_seek(avf, stream, min_ts, ts, max_ts, flags);
}
return ret;
}
static int concat_seek(AVFormatContext *avf, int stream,
int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
{
ConcatContext *cat = avf->priv_data;
ConcatFile *cur_file_saved = cat->cur_file;
AVFormatContext *cur_avf_saved = cat->avf;
int ret;
if (!cat->seekable)
return AVERROR(ESPIPE); /* XXX: can we use it? */
if (flags & (AVSEEK_FLAG_BYTE | AVSEEK_FLAG_FRAME))
return AVERROR(ENOSYS);
cat->avf = NULL;
if ((ret = real_seek(avf, stream, min_ts, ts, max_ts, flags)) < 0) {
if (cat->avf)
avformat_close_input(&cat->avf);
cat->avf = cur_avf_saved;
cat->cur_file = cur_file_saved;
} else {
avformat_close_input(&cur_avf_saved);
}
return ret;
}
#define OFFSET(x) offsetof(ConcatContext, x)
#define DEC AV_OPT_FLAG_DECODING_PARAM
@ -307,5 +400,6 @@ AVInputFormat ff_concat_demuxer = {
.read_header = concat_read_header,
.read_packet = concat_read_packet,
.read_close = concat_read_close,
.read_seek2 = concat_seek,
.priv_class = &concat_class,
};