lavf/concatdec: support seeking.
This commit is contained in:
parent
710cd0fddf
commit
de1a71595d
@ -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,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user