fftools/ffmpeg: parse forced keyframes in of_open()
Allows to remove the ugly of_get_chapters() wrapper.
This commit is contained in:
parent
efe4423627
commit
334e52e094
@ -113,15 +113,6 @@ const int program_birth_year = 2000;
|
|||||||
|
|
||||||
static FILE *vstats_file;
|
static FILE *vstats_file;
|
||||||
|
|
||||||
const char *const forced_keyframes_const_names[] = {
|
|
||||||
"n",
|
|
||||||
"n_forced",
|
|
||||||
"prev_forced_n",
|
|
||||||
"prev_forced_t",
|
|
||||||
"t",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct BenchmarkTimeStamps {
|
typedef struct BenchmarkTimeStamps {
|
||||||
int64_t real_usec;
|
int64_t real_usec;
|
||||||
int64_t user_usec;
|
int64_t user_usec;
|
||||||
@ -2590,11 +2581,6 @@ static int init_input_stream(InputStream *ist, char *error, int error_len)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int compare_int64(const void *a, const void *b)
|
|
||||||
{
|
|
||||||
return FFDIFFSIGN(*(const int64_t *)a, *(const int64_t *)b);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int init_output_stream_streamcopy(OutputStream *ost)
|
static int init_output_stream_streamcopy(OutputStream *ost)
|
||||||
{
|
{
|
||||||
OutputFile *of = output_files[ost->file_index];
|
OutputFile *of = output_files[ost->file_index];
|
||||||
@ -2748,61 +2734,6 @@ static void set_encoder_id(OutputFile *of, OutputStream *ost)
|
|||||||
AV_DICT_DONT_STRDUP_VAL | AV_DICT_DONT_OVERWRITE);
|
AV_DICT_DONT_STRDUP_VAL | AV_DICT_DONT_OVERWRITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_forced_key_frames(KeyframeForceCtx *kf, OutputFile *of)
|
|
||||||
{
|
|
||||||
const char *p;
|
|
||||||
int n = 1, i, size, index = 0;
|
|
||||||
int64_t t, *pts;
|
|
||||||
|
|
||||||
for (p = kf->forced_keyframes; *p; p++)
|
|
||||||
if (*p == ',')
|
|
||||||
n++;
|
|
||||||
size = n;
|
|
||||||
pts = av_malloc_array(size, sizeof(*pts));
|
|
||||||
if (!pts)
|
|
||||||
report_and_exit(AVERROR(ENOMEM));
|
|
||||||
|
|
||||||
p = kf->forced_keyframes;
|
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
char *next = strchr(p, ',');
|
|
||||||
|
|
||||||
if (next)
|
|
||||||
*next++ = 0;
|
|
||||||
|
|
||||||
if (!memcmp(p, "chapters", 8)) {
|
|
||||||
AVChapter * const *ch;
|
|
||||||
unsigned int nb_ch;
|
|
||||||
int j;
|
|
||||||
|
|
||||||
ch = of_get_chapters(of, &nb_ch);
|
|
||||||
|
|
||||||
if (nb_ch > INT_MAX - size ||
|
|
||||||
!(pts = av_realloc_f(pts, size += nb_ch - 1,
|
|
||||||
sizeof(*pts))))
|
|
||||||
report_and_exit(AVERROR(ENOMEM));
|
|
||||||
t = p[8] ? parse_time_or_die("force_key_frames", p + 8, 1) : 0;
|
|
||||||
|
|
||||||
for (j = 0; j < nb_ch; j++) {
|
|
||||||
const AVChapter *c = ch[j];
|
|
||||||
av_assert1(index < size);
|
|
||||||
pts[index++] = av_rescale_q(c->start, c->time_base,
|
|
||||||
AV_TIME_BASE_Q) + t;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
av_assert1(index < size);
|
|
||||||
pts[index++] = parse_time_or_die("force_key_frames", p, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
p = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
av_assert0(index == size);
|
|
||||||
qsort(pts, size, sizeof(*pts), compare_int64);
|
|
||||||
kf->nb_pts = size;
|
|
||||||
kf->pts = pts;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void init_encoder_time_base(OutputStream *ost, AVRational default_time_base)
|
static void init_encoder_time_base(OutputStream *ost, AVRational default_time_base)
|
||||||
{
|
{
|
||||||
InputStream *ist = ost->ist;
|
InputStream *ist = ost->ist;
|
||||||
@ -2949,26 +2880,6 @@ static int init_output_stream_encode(OutputStream *ost, AVFrame *frame)
|
|||||||
enc_ctx->field_order = AV_FIELD_TT;
|
enc_ctx->field_order = AV_FIELD_TT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ost->kf.forced_keyframes) {
|
|
||||||
if (!strncmp(ost->kf.forced_keyframes, "expr:", 5)) {
|
|
||||||
ret = av_expr_parse(&ost->kf.pexpr, ost->kf.forced_keyframes+5,
|
|
||||||
forced_keyframes_const_names, NULL, NULL, NULL, NULL, 0, NULL);
|
|
||||||
if (ret < 0) {
|
|
||||||
av_log(NULL, AV_LOG_ERROR,
|
|
||||||
"Invalid force_key_frames expression '%s'\n", ost->kf.forced_keyframes+5);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
ost->kf.expr_const_values[FKF_N] = 0;
|
|
||||||
ost->kf.expr_const_values[FKF_N_FORCED] = 0;
|
|
||||||
ost->kf.expr_const_values[FKF_PREV_FORCED_N] = NAN;
|
|
||||||
ost->kf.expr_const_values[FKF_PREV_FORCED_T] = NAN;
|
|
||||||
|
|
||||||
// Don't parse the 'forced_keyframes' in case of 'keep-source-keyframes',
|
|
||||||
// parse it only for static kf timings
|
|
||||||
} else if(strncmp(ost->kf.forced_keyframes, "source", 6)) {
|
|
||||||
parse_forced_key_frames(&ost->kf, of);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case AVMEDIA_TYPE_SUBTITLE:
|
case AVMEDIA_TYPE_SUBTITLE:
|
||||||
enc_ctx->time_base = AV_TIME_BASE_Q;
|
enc_ctx->time_base = AV_TIME_BASE_Q;
|
||||||
|
@ -757,8 +757,6 @@ void of_close(OutputFile **pof);
|
|||||||
*/
|
*/
|
||||||
void of_output_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int eof);
|
void of_output_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int eof);
|
||||||
int64_t of_filesize(OutputFile *of);
|
int64_t of_filesize(OutputFile *of);
|
||||||
AVChapter * const *
|
|
||||||
of_get_chapters(OutputFile *of, unsigned int *nb_chapters);
|
|
||||||
|
|
||||||
int ifile_open(const OptionsContext *o, const char *filename);
|
int ifile_open(const OptionsContext *o, const char *filename);
|
||||||
void ifile_close(InputFile **f);
|
void ifile_close(InputFile **f);
|
||||||
|
@ -736,11 +736,3 @@ int64_t of_filesize(OutputFile *of)
|
|||||||
Muxer *mux = mux_from_of(of);
|
Muxer *mux = mux_from_of(of);
|
||||||
return atomic_load(&mux->last_filesize);
|
return atomic_load(&mux->last_filesize);
|
||||||
}
|
}
|
||||||
|
|
||||||
AVChapter * const *
|
|
||||||
of_get_chapters(OutputFile *of, unsigned int *nb_chapters)
|
|
||||||
{
|
|
||||||
Muxer *mux = mux_from_of(of);
|
|
||||||
*nb_chapters = mux->fc->nb_chapters;
|
|
||||||
return mux->fc->chapters;
|
|
||||||
}
|
|
||||||
|
@ -1745,6 +1745,104 @@ finish:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *const forced_keyframes_const_names[] = {
|
||||||
|
"n",
|
||||||
|
"n_forced",
|
||||||
|
"prev_forced_n",
|
||||||
|
"prev_forced_t",
|
||||||
|
"t",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static int compare_int64(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
return FFDIFFSIGN(*(const int64_t *)a, *(const int64_t *)b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_forced_key_frames(KeyframeForceCtx *kf, const Muxer *mux)
|
||||||
|
{
|
||||||
|
const char *p;
|
||||||
|
int n = 1, i, size, index = 0;
|
||||||
|
int64_t t, *pts;
|
||||||
|
|
||||||
|
for (p = kf->forced_keyframes; *p; p++)
|
||||||
|
if (*p == ',')
|
||||||
|
n++;
|
||||||
|
size = n;
|
||||||
|
pts = av_malloc_array(size, sizeof(*pts));
|
||||||
|
if (!pts)
|
||||||
|
report_and_exit(AVERROR(ENOMEM));
|
||||||
|
|
||||||
|
p = kf->forced_keyframes;
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
char *next = strchr(p, ',');
|
||||||
|
|
||||||
|
if (next)
|
||||||
|
*next++ = 0;
|
||||||
|
|
||||||
|
if (!memcmp(p, "chapters", 8)) {
|
||||||
|
AVChapter * const *ch = mux->fc->chapters;
|
||||||
|
unsigned int nb_ch = mux->fc->nb_chapters;
|
||||||
|
int j;
|
||||||
|
|
||||||
|
if (nb_ch > INT_MAX - size ||
|
||||||
|
!(pts = av_realloc_f(pts, size += nb_ch - 1,
|
||||||
|
sizeof(*pts))))
|
||||||
|
report_and_exit(AVERROR(ENOMEM));
|
||||||
|
t = p[8] ? parse_time_or_die("force_key_frames", p + 8, 1) : 0;
|
||||||
|
|
||||||
|
for (j = 0; j < nb_ch; j++) {
|
||||||
|
const AVChapter *c = ch[j];
|
||||||
|
av_assert1(index < size);
|
||||||
|
pts[index++] = av_rescale_q(c->start, c->time_base,
|
||||||
|
AV_TIME_BASE_Q) + t;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
av_assert1(index < size);
|
||||||
|
pts[index++] = parse_time_or_die("force_key_frames", p, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
p = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
av_assert0(index == size);
|
||||||
|
qsort(pts, size, sizeof(*pts), compare_int64);
|
||||||
|
kf->nb_pts = size;
|
||||||
|
kf->pts = pts;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int process_forced_keyframes(Muxer *mux)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < mux->of.nb_streams; i++) {
|
||||||
|
OutputStream *ost = mux->of.streams[i];
|
||||||
|
|
||||||
|
if (!ost->kf.forced_keyframes)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!strncmp(ost->kf.forced_keyframes, "expr:", 5)) {
|
||||||
|
int ret = av_expr_parse(&ost->kf.pexpr, ost->kf.forced_keyframes+5,
|
||||||
|
forced_keyframes_const_names, NULL, NULL, NULL, NULL, 0, NULL);
|
||||||
|
if (ret < 0) {
|
||||||
|
av_log(NULL, AV_LOG_ERROR,
|
||||||
|
"Invalid force_key_frames expression '%s'\n", ost->kf.forced_keyframes+5);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
ost->kf.expr_const_values[FKF_N] = 0;
|
||||||
|
ost->kf.expr_const_values[FKF_N_FORCED] = 0;
|
||||||
|
ost->kf.expr_const_values[FKF_PREV_FORCED_N] = NAN;
|
||||||
|
ost->kf.expr_const_values[FKF_PREV_FORCED_T] = NAN;
|
||||||
|
|
||||||
|
// Don't parse the 'forced_keyframes' in case of 'keep-source-keyframes',
|
||||||
|
// parse it only for static kf timings
|
||||||
|
} else if (strncmp(ost->kf.forced_keyframes, "source", 6)) {
|
||||||
|
parse_forced_key_frames(&ost->kf, mux);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void validate_enc_avopt(const Muxer *mux, const AVDictionary *codec_avopt)
|
static void validate_enc_avopt(const Muxer *mux, const AVDictionary *codec_avopt)
|
||||||
{
|
{
|
||||||
const AVClass *class = avcodec_get_class();
|
const AVClass *class = avcodec_get_class();
|
||||||
@ -1961,6 +2059,14 @@ int of_open(const OptionsContext *o, const char *filename)
|
|||||||
exit_program(1);
|
exit_program(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parse forced keyframe specifications;
|
||||||
|
// must be done after chapters are created
|
||||||
|
err = process_forced_keyframes(mux);
|
||||||
|
if (err < 0) {
|
||||||
|
av_log(NULL, AV_LOG_FATAL, "Error processing forced keyframes\n");
|
||||||
|
exit_program(1);
|
||||||
|
}
|
||||||
|
|
||||||
err = setup_sync_queues(mux, oc, o->shortest_buf_duration * AV_TIME_BASE);
|
err = setup_sync_queues(mux, oc, o->shortest_buf_duration * AV_TIME_BASE);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
av_log(NULL, AV_LOG_FATAL, "Error setting up output sync queues\n");
|
av_log(NULL, AV_LOG_FATAL, "Error setting up output sync queues\n");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user