Commit de1a7159 authored by Nicolas George's avatar Nicolas George

lavf/concatdec: support seeking.

parent 710cd0fd
......@@ -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
......
......@@ -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,
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment