|
|
@ -20,10 +20,11 @@ |
|
|
|
|
|
|
|
|
|
|
|
#include <stdint.h> |
|
|
|
#include <stdint.h> |
|
|
|
|
|
|
|
|
|
|
|
#include "ffmpeg/ffmpeg.h" |
|
|
|
#include "ffmpeg.h" |
|
|
|
|
|
|
|
|
|
|
|
#include "libavfilter/avfilter.h" |
|
|
|
#include "libavfilter/avfilter.h" |
|
|
|
#include "libavfilter/buffersink.h" |
|
|
|
#include "libavfilter/buffersink.h" |
|
|
|
|
|
|
|
#include "libavfilter/buffersrc.h" |
|
|
|
|
|
|
|
|
|
|
|
#include "libavutil/avassert.h" |
|
|
|
#include "libavutil/avassert.h" |
|
|
|
#include "libavutil/avstring.h" |
|
|
|
#include "libavutil/avstring.h" |
|
|
@ -36,7 +37,6 @@ |
|
|
|
#include "libavutil/imgutils.h" |
|
|
|
#include "libavutil/imgutils.h" |
|
|
|
#include "libavutil/samplefmt.h" |
|
|
|
#include "libavutil/samplefmt.h" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const enum AVPixelFormat *get_compliance_unofficial_pix_fmts(enum AVCodecID codec_id, const enum AVPixelFormat default_formats[]) |
|
|
|
static const enum AVPixelFormat *get_compliance_unofficial_pix_fmts(enum AVCodecID codec_id, const enum AVPixelFormat default_formats[]) |
|
|
|
{ |
|
|
|
{ |
|
|
|
static const enum AVPixelFormat mjpeg_formats[] = |
|
|
|
static const enum AVPixelFormat mjpeg_formats[] = |
|
|
@ -63,6 +63,7 @@ enum AVPixelFormat choose_pixel_fmt(AVStream *st, AVCodecContext *enc_ctx, AVCod |
|
|
|
if (codec && codec->pix_fmts) { |
|
|
|
if (codec && codec->pix_fmts) { |
|
|
|
const enum AVPixelFormat *p = codec->pix_fmts; |
|
|
|
const enum AVPixelFormat *p = codec->pix_fmts; |
|
|
|
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(target); |
|
|
|
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(target); |
|
|
|
|
|
|
|
//FIXME: This should check for AV_PIX_FMT_FLAG_ALPHA after PAL8 pixel format without alpha is implemented
|
|
|
|
int has_alpha = desc ? desc->nb_components % 2 == 0 : 0; |
|
|
|
int has_alpha = desc ? desc->nb_components % 2 == 0 : 0; |
|
|
|
enum AVPixelFormat best= AV_PIX_FMT_NONE; |
|
|
|
enum AVPixelFormat best= AV_PIX_FMT_NONE; |
|
|
|
|
|
|
|
|
|
|
@ -92,33 +93,33 @@ void choose_sample_fmt(AVStream *st, AVCodec *codec) |
|
|
|
if (codec && codec->sample_fmts) { |
|
|
|
if (codec && codec->sample_fmts) { |
|
|
|
const enum AVSampleFormat *p = codec->sample_fmts; |
|
|
|
const enum AVSampleFormat *p = codec->sample_fmts; |
|
|
|
for (; *p != -1; p++) { |
|
|
|
for (; *p != -1; p++) { |
|
|
|
if (*p == st->codec->sample_fmt) |
|
|
|
if (*p == st->codecpar->format) |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
if (*p == -1) { |
|
|
|
if (*p == -1) { |
|
|
|
if((codec->capabilities & AV_CODEC_CAP_LOSSLESS) && av_get_sample_fmt_name(st->codec->sample_fmt) > av_get_sample_fmt_name(codec->sample_fmts[0])) |
|
|
|
if((codec->capabilities & AV_CODEC_CAP_LOSSLESS) && av_get_sample_fmt_name(st->codecpar->format) > av_get_sample_fmt_name(codec->sample_fmts[0])) |
|
|
|
av_log(NULL, AV_LOG_ERROR, "Conversion will not be lossless.\n"); |
|
|
|
av_log(NULL, AV_LOG_ERROR, "Conversion will not be lossless.\n"); |
|
|
|
if(av_get_sample_fmt_name(st->codec->sample_fmt)) |
|
|
|
if(av_get_sample_fmt_name(st->codecpar->format)) |
|
|
|
av_log(NULL, AV_LOG_WARNING, |
|
|
|
av_log(NULL, AV_LOG_WARNING, |
|
|
|
"Incompatible sample format '%s' for codec '%s', auto-selecting format '%s'\n", |
|
|
|
"Incompatible sample format '%s' for codec '%s', auto-selecting format '%s'\n", |
|
|
|
av_get_sample_fmt_name(st->codec->sample_fmt), |
|
|
|
av_get_sample_fmt_name(st->codecpar->format), |
|
|
|
codec->name, |
|
|
|
codec->name, |
|
|
|
av_get_sample_fmt_name(codec->sample_fmts[0])); |
|
|
|
av_get_sample_fmt_name(codec->sample_fmts[0])); |
|
|
|
st->codec->sample_fmt = codec->sample_fmts[0]; |
|
|
|
st->codecpar->format = codec->sample_fmts[0]; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static char *choose_pix_fmts(OutputStream *ost) |
|
|
|
static char *choose_pix_fmts(OutputFilter *ofilter) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
OutputStream *ost = ofilter->ost; |
|
|
|
AVDictionaryEntry *strict_dict = av_dict_get(ost->encoder_opts, "strict", NULL, 0); |
|
|
|
AVDictionaryEntry *strict_dict = av_dict_get(ost->encoder_opts, "strict", NULL, 0); |
|
|
|
if (strict_dict) |
|
|
|
if (strict_dict) |
|
|
|
// used by choose_pixel_fmt() and below
|
|
|
|
// used by choose_pixel_fmt() and below
|
|
|
|
av_opt_set(ost->enc_ctx, "strict", strict_dict->value, 0); |
|
|
|
av_opt_set(ost->enc_ctx, "strict", strict_dict->value, 0); |
|
|
|
|
|
|
|
|
|
|
|
if (ost->keep_pix_fmt) { |
|
|
|
if (ost->keep_pix_fmt) { |
|
|
|
if (ost->filter) |
|
|
|
avfilter_graph_set_auto_convert(ofilter->graph->graph, |
|
|
|
avfilter_graph_set_auto_convert(ost->filter->graph->graph, |
|
|
|
|
|
|
|
AVFILTER_AUTO_CONVERT_NONE); |
|
|
|
AVFILTER_AUTO_CONVERT_NONE); |
|
|
|
if (ost->enc_ctx->pix_fmt == AV_PIX_FMT_NONE) |
|
|
|
if (ost->enc_ctx->pix_fmt == AV_PIX_FMT_NONE) |
|
|
|
return NULL; |
|
|
|
return NULL; |
|
|
@ -153,13 +154,13 @@ static char *choose_pix_fmts(OutputStream *ost) |
|
|
|
|
|
|
|
|
|
|
|
/* Define a function for building a string containing a list of
|
|
|
|
/* Define a function for building a string containing a list of
|
|
|
|
* allowed formats. */ |
|
|
|
* allowed formats. */ |
|
|
|
#define DEF_CHOOSE_FORMAT(type, var, supported_list, none, get_name) \ |
|
|
|
#define DEF_CHOOSE_FORMAT(suffix, type, var, supported_list, none, get_name) \ |
|
|
|
static char *choose_ ## var ## s(OutputStream *ost) \
|
|
|
|
static char *choose_ ## suffix (OutputFilter *ofilter) \
|
|
|
|
{ \
|
|
|
|
{ \
|
|
|
|
if (ost->enc_ctx->var != none) { \
|
|
|
|
if (ofilter->var != none) { \
|
|
|
|
get_name(ost->enc_ctx->var); \
|
|
|
|
get_name(ofilter->var); \
|
|
|
|
return av_strdup(name); \
|
|
|
|
return av_strdup(name); \
|
|
|
|
} else if (ost->enc && ost->enc->supported_list) { \
|
|
|
|
} else if (ofilter->supported_list) { \
|
|
|
|
const type *p; \
|
|
|
|
const type *p; \
|
|
|
|
AVIOContext *s = NULL; \
|
|
|
|
AVIOContext *s = NULL; \
|
|
|
|
uint8_t *ret; \
|
|
|
|
uint8_t *ret; \
|
|
|
@ -168,7 +169,7 @@ static char *choose_ ## var ## s(OutputStream *ost) \ |
|
|
|
if (avio_open_dyn_buf(&s) < 0) \
|
|
|
|
if (avio_open_dyn_buf(&s) < 0) \
|
|
|
|
exit_program(1); \
|
|
|
|
exit_program(1); \
|
|
|
|
\
|
|
|
|
\
|
|
|
|
for (p = ost->enc->supported_list; *p != none; p++) { \
|
|
|
|
for (p = ofilter->supported_list; *p != none; p++) { \
|
|
|
|
get_name(*p); \
|
|
|
|
get_name(*p); \
|
|
|
|
avio_printf(s, "%s|", name); \
|
|
|
|
avio_printf(s, "%s|", name); \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
@ -179,19 +180,19 @@ static char *choose_ ## var ## s(OutputStream *ost) \ |
|
|
|
return NULL; \
|
|
|
|
return NULL; \
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// DEF_CHOOSE_FORMAT(enum AVPixelFormat, pix_fmt, pix_fmts, AV_PIX_FMT_NONE,
|
|
|
|
//DEF_CHOOSE_FORMAT(pix_fmts, enum AVPixelFormat, format, formats, AV_PIX_FMT_NONE,
|
|
|
|
// GET_PIX_FMT_NAME)
|
|
|
|
// GET_PIX_FMT_NAME)
|
|
|
|
|
|
|
|
|
|
|
|
DEF_CHOOSE_FORMAT(enum AVSampleFormat, sample_fmt, sample_fmts, |
|
|
|
DEF_CHOOSE_FORMAT(sample_fmts, enum AVSampleFormat, format, formats, |
|
|
|
AV_SAMPLE_FMT_NONE, GET_SAMPLE_FMT_NAME) |
|
|
|
AV_SAMPLE_FMT_NONE, GET_SAMPLE_FMT_NAME) |
|
|
|
|
|
|
|
|
|
|
|
DEF_CHOOSE_FORMAT(int, sample_rate, supported_samplerates, 0, |
|
|
|
DEF_CHOOSE_FORMAT(sample_rates, int, sample_rate, sample_rates, 0, |
|
|
|
GET_SAMPLE_RATE_NAME) |
|
|
|
GET_SAMPLE_RATE_NAME) |
|
|
|
|
|
|
|
|
|
|
|
DEF_CHOOSE_FORMAT(uint64_t, channel_layout, channel_layouts, 0, |
|
|
|
DEF_CHOOSE_FORMAT(channel_layouts, uint64_t, channel_layout, channel_layouts, 0, |
|
|
|
GET_CH_LAYOUT_NAME) |
|
|
|
GET_CH_LAYOUT_NAME) |
|
|
|
|
|
|
|
|
|
|
|
FilterGraph *init_simple_filtergraph(InputStream *ist, OutputStream *ost) |
|
|
|
int init_simple_filtergraph(InputStream *ist, OutputStream *ost) |
|
|
|
{ |
|
|
|
{ |
|
|
|
FilterGraph *fg = av_mallocz(sizeof(*fg)); |
|
|
|
FilterGraph *fg = av_mallocz(sizeof(*fg)); |
|
|
|
|
|
|
|
|
|
|
@ -204,6 +205,7 @@ FilterGraph *init_simple_filtergraph(InputStream *ist, OutputStream *ost) |
|
|
|
exit_program(1); |
|
|
|
exit_program(1); |
|
|
|
fg->outputs[0]->ost = ost; |
|
|
|
fg->outputs[0]->ost = ost; |
|
|
|
fg->outputs[0]->graph = fg; |
|
|
|
fg->outputs[0]->graph = fg; |
|
|
|
|
|
|
|
fg->outputs[0]->format = -1; |
|
|
|
|
|
|
|
|
|
|
|
ost->filter = fg->outputs[0]; |
|
|
|
ost->filter = fg->outputs[0]; |
|
|
|
|
|
|
|
|
|
|
@ -212,6 +214,11 @@ FilterGraph *init_simple_filtergraph(InputStream *ist, OutputStream *ost) |
|
|
|
exit_program(1); |
|
|
|
exit_program(1); |
|
|
|
fg->inputs[0]->ist = ist; |
|
|
|
fg->inputs[0]->ist = ist; |
|
|
|
fg->inputs[0]->graph = fg; |
|
|
|
fg->inputs[0]->graph = fg; |
|
|
|
|
|
|
|
fg->inputs[0]->format = -1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fg->inputs[0]->frame_queue = av_fifo_alloc(8 * sizeof(AVFrame*)); |
|
|
|
|
|
|
|
if (!fg->inputs[0]->frame_queue) |
|
|
|
|
|
|
|
exit_program(1); |
|
|
|
|
|
|
|
|
|
|
|
GROW_ARRAY(ist->filters, ist->nb_filters); |
|
|
|
GROW_ARRAY(ist->filters, ist->nb_filters); |
|
|
|
ist->filters[ist->nb_filters - 1] = fg->inputs[0]; |
|
|
|
ist->filters[ist->nb_filters - 1] = fg->inputs[0]; |
|
|
@ -219,7 +226,26 @@ FilterGraph *init_simple_filtergraph(InputStream *ist, OutputStream *ost) |
|
|
|
GROW_ARRAY(filtergraphs, nb_filtergraphs); |
|
|
|
GROW_ARRAY(filtergraphs, nb_filtergraphs); |
|
|
|
filtergraphs[nb_filtergraphs - 1] = fg; |
|
|
|
filtergraphs[nb_filtergraphs - 1] = fg; |
|
|
|
|
|
|
|
|
|
|
|
return fg; |
|
|
|
return 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static char *describe_filter_link(FilterGraph *fg, AVFilterInOut *inout, int in) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
AVFilterContext *ctx = inout->filter_ctx; |
|
|
|
|
|
|
|
AVFilterPad *pads = in ? ctx->input_pads : ctx->output_pads; |
|
|
|
|
|
|
|
int nb_pads = in ? ctx->nb_inputs : ctx->nb_outputs; |
|
|
|
|
|
|
|
AVIOContext *pb; |
|
|
|
|
|
|
|
uint8_t *res = NULL; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (avio_open_dyn_buf(&pb) < 0) |
|
|
|
|
|
|
|
exit_program(1); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
avio_printf(pb, "%s", ctx->filter->name); |
|
|
|
|
|
|
|
if (nb_pads > 1) |
|
|
|
|
|
|
|
avio_printf(pb, ":%s", avfilter_pad_get_name(pads, inout->pad_idx)); |
|
|
|
|
|
|
|
avio_w8(pb, 0); |
|
|
|
|
|
|
|
avio_close_dyn_buf(pb, &res); |
|
|
|
|
|
|
|
return res; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) |
|
|
|
static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) |
|
|
@ -249,7 +275,7 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) |
|
|
|
s = input_files[file_idx]->ctx; |
|
|
|
s = input_files[file_idx]->ctx; |
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < s->nb_streams; i++) { |
|
|
|
for (i = 0; i < s->nb_streams; i++) { |
|
|
|
enum AVMediaType stream_type = s->streams[i]->codec->codec_type; |
|
|
|
enum AVMediaType stream_type = s->streams[i]->codecpar->codec_type; |
|
|
|
if (stream_type != type && |
|
|
|
if (stream_type != type && |
|
|
|
!(stream_type == AVMEDIA_TYPE_SUBTITLE && |
|
|
|
!(stream_type == AVMEDIA_TYPE_SUBTITLE && |
|
|
|
type == AVMEDIA_TYPE_VIDEO /* sub2video hack */)) |
|
|
|
type == AVMEDIA_TYPE_VIDEO /* sub2video hack */)) |
|
|
@ -265,10 +291,17 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) |
|
|
|
exit_program(1); |
|
|
|
exit_program(1); |
|
|
|
} |
|
|
|
} |
|
|
|
ist = input_streams[input_files[file_idx]->ist_index + st->index]; |
|
|
|
ist = input_streams[input_files[file_idx]->ist_index + st->index]; |
|
|
|
|
|
|
|
if (ist->user_set_discard == AVDISCARD_ALL) { |
|
|
|
|
|
|
|
av_log(NULL, AV_LOG_FATAL, "Stream specifier '%s' in filtergraph description %s " |
|
|
|
|
|
|
|
"matches a disabled input stream.\n", p, fg->graph_desc); |
|
|
|
|
|
|
|
exit_program(1); |
|
|
|
|
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
/* find the first unused stream of corresponding type */ |
|
|
|
/* find the first unused stream of corresponding type */ |
|
|
|
for (i = 0; i < nb_input_streams; i++) { |
|
|
|
for (i = 0; i < nb_input_streams; i++) { |
|
|
|
ist = input_streams[i]; |
|
|
|
ist = input_streams[i]; |
|
|
|
|
|
|
|
if (ist->user_set_discard == AVDISCARD_ALL) |
|
|
|
|
|
|
|
continue; |
|
|
|
if (ist->dec_ctx->codec_type == type && ist->discard) |
|
|
|
if (ist->dec_ctx->codec_type == type && ist->discard) |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
@ -290,6 +323,13 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) |
|
|
|
exit_program(1); |
|
|
|
exit_program(1); |
|
|
|
fg->inputs[fg->nb_inputs - 1]->ist = ist; |
|
|
|
fg->inputs[fg->nb_inputs - 1]->ist = ist; |
|
|
|
fg->inputs[fg->nb_inputs - 1]->graph = fg; |
|
|
|
fg->inputs[fg->nb_inputs - 1]->graph = fg; |
|
|
|
|
|
|
|
fg->inputs[fg->nb_inputs - 1]->format = -1; |
|
|
|
|
|
|
|
fg->inputs[fg->nb_inputs - 1]->type = ist->st->codecpar->codec_type; |
|
|
|
|
|
|
|
fg->inputs[fg->nb_inputs - 1]->name = describe_filter_link(fg, in, 1); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fg->inputs[fg->nb_inputs - 1]->frame_queue = av_fifo_alloc(8 * sizeof(AVFrame*)); |
|
|
|
|
|
|
|
if (!fg->inputs[fg->nb_inputs - 1]->frame_queue) |
|
|
|
|
|
|
|
exit_program(1); |
|
|
|
|
|
|
|
|
|
|
|
GROW_ARRAY(ist->filters, ist->nb_filters); |
|
|
|
GROW_ARRAY(ist->filters, ist->nb_filters); |
|
|
|
ist->filters[ist->nb_filters - 1] = fg->inputs[fg->nb_inputs - 1]; |
|
|
|
ist->filters[ist->nb_filters - 1] = fg->inputs[fg->nb_inputs - 1]; |
|
|
@ -306,6 +346,7 @@ int init_complex_filtergraph(FilterGraph *fg) |
|
|
|
graph = avfilter_graph_alloc(); |
|
|
|
graph = avfilter_graph_alloc(); |
|
|
|
if (!graph) |
|
|
|
if (!graph) |
|
|
|
return AVERROR(ENOMEM); |
|
|
|
return AVERROR(ENOMEM); |
|
|
|
|
|
|
|
graph->nb_threads = 1; |
|
|
|
|
|
|
|
|
|
|
|
ret = avfilter_graph_parse2(graph, fg->graph_desc, &inputs, &outputs); |
|
|
|
ret = avfilter_graph_parse2(graph, fg->graph_desc, &inputs, &outputs); |
|
|
|
if (ret < 0) |
|
|
|
if (ret < 0) |
|
|
@ -324,6 +365,7 @@ int init_complex_filtergraph(FilterGraph *fg) |
|
|
|
fg->outputs[fg->nb_outputs - 1]->out_tmp = cur; |
|
|
|
fg->outputs[fg->nb_outputs - 1]->out_tmp = cur; |
|
|
|
fg->outputs[fg->nb_outputs - 1]->type = avfilter_pad_get_type(cur->filter_ctx->output_pads, |
|
|
|
fg->outputs[fg->nb_outputs - 1]->type = avfilter_pad_get_type(cur->filter_ctx->output_pads, |
|
|
|
cur->pad_idx); |
|
|
|
cur->pad_idx); |
|
|
|
|
|
|
|
fg->outputs[fg->nb_outputs - 1]->name = describe_filter_link(fg, cur, 0); |
|
|
|
cur = cur->next; |
|
|
|
cur = cur->next; |
|
|
|
fg->outputs[fg->nb_outputs - 1]->out_tmp->next = NULL; |
|
|
|
fg->outputs[fg->nb_outputs - 1]->out_tmp->next = NULL; |
|
|
|
} |
|
|
|
} |
|
|
@ -412,13 +454,12 @@ static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, |
|
|
|
char *pix_fmts; |
|
|
|
char *pix_fmts; |
|
|
|
OutputStream *ost = ofilter->ost; |
|
|
|
OutputStream *ost = ofilter->ost; |
|
|
|
OutputFile *of = output_files[ost->file_index]; |
|
|
|
OutputFile *of = output_files[ost->file_index]; |
|
|
|
AVCodecContext *codec = ost->enc_ctx; |
|
|
|
|
|
|
|
AVFilterContext *last_filter = out->filter_ctx; |
|
|
|
AVFilterContext *last_filter = out->filter_ctx; |
|
|
|
int pad_idx = out->pad_idx; |
|
|
|
int pad_idx = out->pad_idx; |
|
|
|
int ret; |
|
|
|
int ret; |
|
|
|
char name[255]; |
|
|
|
char name[255]; |
|
|
|
|
|
|
|
|
|
|
|
snprintf(name, sizeof(name), "output stream %d:%d", ost->file_index, ost->index); |
|
|
|
snprintf(name, sizeof(name), "out_%d_%d", ost->file_index, ost->index); |
|
|
|
ret = avfilter_graph_create_filter(&ofilter->filter, |
|
|
|
ret = avfilter_graph_create_filter(&ofilter->filter, |
|
|
|
avfilter_get_by_name("buffersink"), |
|
|
|
avfilter_get_by_name("buffersink"), |
|
|
|
name, NULL, NULL, fg->graph); |
|
|
|
name, NULL, NULL, fg->graph); |
|
|
@ -426,21 +467,20 @@ static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, |
|
|
|
if (ret < 0) |
|
|
|
if (ret < 0) |
|
|
|
return ret; |
|
|
|
return ret; |
|
|
|
|
|
|
|
|
|
|
|
if (codec->width || codec->height) { |
|
|
|
if (ofilter->width || ofilter->height) { |
|
|
|
char args[255]; |
|
|
|
char args[255]; |
|
|
|
AVFilterContext *filter; |
|
|
|
AVFilterContext *filter; |
|
|
|
AVDictionaryEntry *e = NULL; |
|
|
|
AVDictionaryEntry *e = NULL; |
|
|
|
|
|
|
|
|
|
|
|
snprintf(args, sizeof(args), "%d:%d", |
|
|
|
snprintf(args, sizeof(args), "%d:%d", |
|
|
|
codec->width, |
|
|
|
ofilter->width, ofilter->height); |
|
|
|
codec->height); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while ((e = av_dict_get(ost->sws_dict, "", e, |
|
|
|
while ((e = av_dict_get(ost->sws_dict, "", e, |
|
|
|
AV_DICT_IGNORE_SUFFIX))) { |
|
|
|
AV_DICT_IGNORE_SUFFIX))) { |
|
|
|
av_strlcatf(args, sizeof(args), ":%s=%s", e->key, e->value); |
|
|
|
av_strlcatf(args, sizeof(args), ":%s=%s", e->key, e->value); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
snprintf(name, sizeof(name), "scaler for output stream %d:%d", |
|
|
|
snprintf(name, sizeof(name), "scaler_out_%d_%d", |
|
|
|
ost->file_index, ost->index); |
|
|
|
ost->file_index, ost->index); |
|
|
|
if ((ret = avfilter_graph_create_filter(&filter, avfilter_get_by_name("scale"), |
|
|
|
if ((ret = avfilter_graph_create_filter(&filter, avfilter_get_by_name("scale"), |
|
|
|
name, args, NULL, fg->graph)) < 0) |
|
|
|
name, args, NULL, fg->graph)) < 0) |
|
|
@ -452,9 +492,9 @@ static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, |
|
|
|
pad_idx = 0; |
|
|
|
pad_idx = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if ((pix_fmts = choose_pix_fmts(ost))) { |
|
|
|
if ((pix_fmts = choose_pix_fmts(ofilter))) { |
|
|
|
AVFilterContext *filter; |
|
|
|
AVFilterContext *filter; |
|
|
|
snprintf(name, sizeof(name), "pixel format for output stream %d:%d", |
|
|
|
snprintf(name, sizeof(name), "format_out_%d_%d", |
|
|
|
ost->file_index, ost->index); |
|
|
|
ost->file_index, ost->index); |
|
|
|
ret = avfilter_graph_create_filter(&filter, |
|
|
|
ret = avfilter_graph_create_filter(&filter, |
|
|
|
avfilter_get_by_name("format"), |
|
|
|
avfilter_get_by_name("format"), |
|
|
@ -475,7 +515,7 @@ static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, |
|
|
|
|
|
|
|
|
|
|
|
snprintf(args, sizeof(args), "fps=%d/%d", ost->frame_rate.num, |
|
|
|
snprintf(args, sizeof(args), "fps=%d/%d", ost->frame_rate.num, |
|
|
|
ost->frame_rate.den); |
|
|
|
ost->frame_rate.den); |
|
|
|
snprintf(name, sizeof(name), "fps for output stream %d:%d", |
|
|
|
snprintf(name, sizeof(name), "fps_out_%d_%d", |
|
|
|
ost->file_index, ost->index); |
|
|
|
ost->file_index, ost->index); |
|
|
|
ret = avfilter_graph_create_filter(&fps, avfilter_get_by_name("fps"), |
|
|
|
ret = avfilter_graph_create_filter(&fps, avfilter_get_by_name("fps"), |
|
|
|
name, args, NULL, fg->graph); |
|
|
|
name, args, NULL, fg->graph); |
|
|
@ -489,7 +529,7 @@ static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, |
|
|
|
pad_idx = 0; |
|
|
|
pad_idx = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
snprintf(name, sizeof(name), "trim for output stream %d:%d", |
|
|
|
snprintf(name, sizeof(name), "trim_out_%d_%d", |
|
|
|
ost->file_index, ost->index); |
|
|
|
ost->file_index, ost->index); |
|
|
|
ret = insert_trim(of->start_time, of->recording_time, |
|
|
|
ret = insert_trim(of->start_time, of->recording_time, |
|
|
|
&last_filter, &pad_idx, name); |
|
|
|
&last_filter, &pad_idx, name); |
|
|
@ -514,7 +554,7 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter, |
|
|
|
char name[255]; |
|
|
|
char name[255]; |
|
|
|
int ret; |
|
|
|
int ret; |
|
|
|
|
|
|
|
|
|
|
|
snprintf(name, sizeof(name), "output stream %d:%d", ost->file_index, ost->index); |
|
|
|
snprintf(name, sizeof(name), "out_%d_%d", ost->file_index, ost->index); |
|
|
|
ret = avfilter_graph_create_filter(&ofilter->filter, |
|
|
|
ret = avfilter_graph_create_filter(&ofilter->filter, |
|
|
|
avfilter_get_by_name("abuffersink"), |
|
|
|
avfilter_get_by_name("abuffersink"), |
|
|
|
name, NULL, NULL, fg->graph); |
|
|
|
name, NULL, NULL, fg->graph); |
|
|
@ -559,9 +599,9 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter, |
|
|
|
if (codec->channels && !codec->channel_layout) |
|
|
|
if (codec->channels && !codec->channel_layout) |
|
|
|
codec->channel_layout = av_get_default_channel_layout(codec->channels); |
|
|
|
codec->channel_layout = av_get_default_channel_layout(codec->channels); |
|
|
|
|
|
|
|
|
|
|
|
sample_fmts = choose_sample_fmts(ost); |
|
|
|
sample_fmts = choose_sample_fmts(ofilter); |
|
|
|
sample_rates = choose_sample_rates(ost); |
|
|
|
sample_rates = choose_sample_rates(ofilter); |
|
|
|
channel_layouts = choose_channel_layouts(ost); |
|
|
|
channel_layouts = choose_channel_layouts(ofilter); |
|
|
|
if (sample_fmts || sample_rates || channel_layouts) { |
|
|
|
if (sample_fmts || sample_rates || channel_layouts) { |
|
|
|
AVFilterContext *format; |
|
|
|
AVFilterContext *format; |
|
|
|
char args[256]; |
|
|
|
char args[256]; |
|
|
@ -581,7 +621,7 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter, |
|
|
|
av_freep(&sample_rates); |
|
|
|
av_freep(&sample_rates); |
|
|
|
av_freep(&channel_layouts); |
|
|
|
av_freep(&channel_layouts); |
|
|
|
|
|
|
|
|
|
|
|
snprintf(name, sizeof(name), "audio format for output stream %d:%d", |
|
|
|
snprintf(name, sizeof(name), "format_out_%d_%d", |
|
|
|
ost->file_index, ost->index); |
|
|
|
ost->file_index, ost->index); |
|
|
|
ret = avfilter_graph_create_filter(&format, |
|
|
|
ret = avfilter_graph_create_filter(&format, |
|
|
|
avfilter_get_by_name("aformat"), |
|
|
|
avfilter_get_by_name("aformat"), |
|
|
@ -609,7 +649,7 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter, |
|
|
|
int i; |
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
|
|
for (i=0; i<of->ctx->nb_streams; i++) |
|
|
|
for (i=0; i<of->ctx->nb_streams; i++) |
|
|
|
if (of->ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) |
|
|
|
if (of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) |
|
|
|
break; |
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
if (i<of->ctx->nb_streams) { |
|
|
|
if (i<of->ctx->nb_streams) { |
|
|
@ -631,30 +671,10 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter, |
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#define DESCRIBE_FILTER_LINK(f, inout, in) \ |
|
|
|
|
|
|
|
{ \
|
|
|
|
|
|
|
|
AVFilterContext *ctx = inout->filter_ctx; \
|
|
|
|
|
|
|
|
AVFilterPad *pads = in ? ctx->input_pads : ctx->output_pads; \
|
|
|
|
|
|
|
|
int nb_pads = in ? ctx->nb_inputs : ctx->nb_outputs; \
|
|
|
|
|
|
|
|
AVIOContext *pb; \
|
|
|
|
|
|
|
|
\
|
|
|
|
|
|
|
|
if (avio_open_dyn_buf(&pb) < 0) \
|
|
|
|
|
|
|
|
exit_program(1); \
|
|
|
|
|
|
|
|
\
|
|
|
|
|
|
|
|
avio_printf(pb, "%s", ctx->filter->name); \
|
|
|
|
|
|
|
|
if (nb_pads > 1) \
|
|
|
|
|
|
|
|
avio_printf(pb, ":%s", avfilter_pad_get_name(pads, inout->pad_idx));\
|
|
|
|
|
|
|
|
avio_w8(pb, 0); \
|
|
|
|
|
|
|
|
avio_close_dyn_buf(pb, &f->name); \
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) |
|
|
|
int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) |
|
|
|
{ |
|
|
|
{ |
|
|
|
av_freep(&ofilter->name); |
|
|
|
|
|
|
|
DESCRIBE_FILTER_LINK(ofilter, out, 0); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!ofilter->ost) { |
|
|
|
if (!ofilter->ost) { |
|
|
|
av_log(NULL, AV_LOG_FATAL, "Filter %s has a unconnected output\n", ofilter->name); |
|
|
|
av_log(NULL, AV_LOG_FATAL, "Filter %s has an unconnected output\n", ofilter->name); |
|
|
|
exit_program(1); |
|
|
|
exit_program(1); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -665,21 +685,36 @@ int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOu |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static int sub2video_prepare(InputStream *ist) |
|
|
|
void check_filter_outputs(void) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
int i; |
|
|
|
|
|
|
|
for (i = 0; i < nb_filtergraphs; i++) { |
|
|
|
|
|
|
|
int n; |
|
|
|
|
|
|
|
for (n = 0; n < filtergraphs[i]->nb_outputs; n++) { |
|
|
|
|
|
|
|
OutputFilter *output = filtergraphs[i]->outputs[n]; |
|
|
|
|
|
|
|
if (!output->ost) { |
|
|
|
|
|
|
|
av_log(NULL, AV_LOG_FATAL, "Filter %s has an unconnected output\n", output->name); |
|
|
|
|
|
|
|
exit_program(1); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int sub2video_prepare(InputStream *ist, InputFilter *ifilter) |
|
|
|
{ |
|
|
|
{ |
|
|
|
AVFormatContext *avf = input_files[ist->file_index]->ctx; |
|
|
|
AVFormatContext *avf = input_files[ist->file_index]->ctx; |
|
|
|
int i, w, h; |
|
|
|
int i, w, h; |
|
|
|
|
|
|
|
|
|
|
|
/* Compute the size of the canvas for the subtitles stream.
|
|
|
|
/* Compute the size of the canvas for the subtitles stream.
|
|
|
|
If the subtitles codec has set a size, use it. Otherwise use the |
|
|
|
If the subtitles codecpar has set a size, use it. Otherwise use the |
|
|
|
maximum dimensions of the video streams in the same file. */ |
|
|
|
maximum dimensions of the video streams in the same file. */ |
|
|
|
w = ist->dec_ctx->width; |
|
|
|
w = ifilter->width; |
|
|
|
h = ist->dec_ctx->height; |
|
|
|
h = ifilter->height; |
|
|
|
if (!(w && h)) { |
|
|
|
if (!(w && h)) { |
|
|
|
for (i = 0; i < avf->nb_streams; i++) { |
|
|
|
for (i = 0; i < avf->nb_streams; i++) { |
|
|
|
if (avf->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { |
|
|
|
if (avf->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { |
|
|
|
w = FFMAX(w, avf->streams[i]->codec->width); |
|
|
|
w = FFMAX(w, avf->streams[i]->codecpar->width); |
|
|
|
h = FFMAX(h, avf->streams[i]->codec->height); |
|
|
|
h = FFMAX(h, avf->streams[i]->codecpar->height); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (!(w && h)) { |
|
|
|
if (!(w && h)) { |
|
|
@ -688,17 +723,21 @@ static int sub2video_prepare(InputStream *ist) |
|
|
|
} |
|
|
|
} |
|
|
|
av_log(avf, AV_LOG_INFO, "sub2video: using %dx%d canvas\n", w, h); |
|
|
|
av_log(avf, AV_LOG_INFO, "sub2video: using %dx%d canvas\n", w, h); |
|
|
|
} |
|
|
|
} |
|
|
|
ist->sub2video.w = ist->resample_width = w; |
|
|
|
ist->sub2video.w = ifilter->width = w; |
|
|
|
ist->sub2video.h = ist->resample_height = h; |
|
|
|
ist->sub2video.h = ifilter->height = h; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ifilter->width = ist->dec_ctx->width ? ist->dec_ctx->width : ist->sub2video.w; |
|
|
|
|
|
|
|
ifilter->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h; |
|
|
|
|
|
|
|
|
|
|
|
/* rectangles are AV_PIX_FMT_PAL8, but we have no guarantee that the
|
|
|
|
/* rectangles are AV_PIX_FMT_PAL8, but we have no guarantee that the
|
|
|
|
palettes for all rectangles are identical or compatible */ |
|
|
|
palettes for all rectangles are identical or compatible */ |
|
|
|
ist->resample_pix_fmt = ist->dec_ctx->pix_fmt = AV_PIX_FMT_RGB32; |
|
|
|
ifilter->format = AV_PIX_FMT_RGB32; |
|
|
|
|
|
|
|
|
|
|
|
ist->sub2video.frame = av_frame_alloc(); |
|
|
|
ist->sub2video.frame = av_frame_alloc(); |
|
|
|
if (!ist->sub2video.frame) |
|
|
|
if (!ist->sub2video.frame) |
|
|
|
return AVERROR(ENOMEM); |
|
|
|
return AVERROR(ENOMEM); |
|
|
|
ist->sub2video.last_pts = INT64_MIN; |
|
|
|
ist->sub2video.last_pts = INT64_MIN; |
|
|
|
|
|
|
|
ist->sub2video.end_pts = INT64_MIN; |
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -717,32 +756,36 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, |
|
|
|
char name[255]; |
|
|
|
char name[255]; |
|
|
|
int ret, pad_idx = 0; |
|
|
|
int ret, pad_idx = 0; |
|
|
|
int64_t tsoffset = 0; |
|
|
|
int64_t tsoffset = 0; |
|
|
|
|
|
|
|
AVBufferSrcParameters *par = av_buffersrc_parameters_alloc(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!par) |
|
|
|
|
|
|
|
return AVERROR(ENOMEM); |
|
|
|
|
|
|
|
memset(par, 0, sizeof(*par)); |
|
|
|
|
|
|
|
par->format = AV_PIX_FMT_NONE; |
|
|
|
|
|
|
|
|
|
|
|
if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { |
|
|
|
if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { |
|
|
|
av_log(NULL, AV_LOG_ERROR, "Cannot connect video filter to audio input\n"); |
|
|
|
av_log(NULL, AV_LOG_ERROR, "Cannot connect video filter to audio input\n"); |
|
|
|
return AVERROR(EINVAL); |
|
|
|
ret = AVERROR(EINVAL); |
|
|
|
|
|
|
|
goto fail; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!fr.num) |
|
|
|
if (!fr.num) |
|
|
|
fr = av_guess_frame_rate(input_files[ist->file_index]->ctx, ist->st, NULL); |
|
|
|
fr = av_guess_frame_rate(input_files[ist->file_index]->ctx, ist->st, NULL); |
|
|
|
|
|
|
|
|
|
|
|
if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) { |
|
|
|
if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) { |
|
|
|
ret = sub2video_prepare(ist); |
|
|
|
ret = sub2video_prepare(ist, ifilter); |
|
|
|
if (ret < 0) |
|
|
|
if (ret < 0) |
|
|
|
return ret; |
|
|
|
goto fail; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
sar = ist->st->sample_aspect_ratio.num ? |
|
|
|
sar = ifilter->sample_aspect_ratio; |
|
|
|
ist->st->sample_aspect_ratio : |
|
|
|
|
|
|
|
ist->dec_ctx->sample_aspect_ratio; |
|
|
|
|
|
|
|
if(!sar.den) |
|
|
|
if(!sar.den) |
|
|
|
sar = (AVRational){0,1}; |
|
|
|
sar = (AVRational){0,1}; |
|
|
|
av_bprint_init(&args, 0, 1); |
|
|
|
av_bprint_init(&args, 0, AV_BPRINT_SIZE_AUTOMATIC); |
|
|
|
av_bprintf(&args, |
|
|
|
av_bprintf(&args, |
|
|
|
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:" |
|
|
|
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:" |
|
|
|
"pixel_aspect=%d/%d:sws_param=flags=%d", ist->resample_width, |
|
|
|
"pixel_aspect=%d/%d:sws_param=flags=%d", |
|
|
|
ist->resample_height, |
|
|
|
ifilter->width, ifilter->height, ifilter->format, |
|
|
|
ist->hwaccel_retrieve_data ? ist->hwaccel_retrieved_pix_fmt : ist->resample_pix_fmt, |
|
|
|
|
|
|
|
tb.num, tb.den, sar.num, sar.den, |
|
|
|
tb.num, tb.den, sar.num, sar.den, |
|
|
|
SWS_BILINEAR + ((ist->dec_ctx->flags&AV_CODEC_FLAG_BITEXACT) ? SWS_BITEXACT:0)); |
|
|
|
SWS_BILINEAR + ((ist->dec_ctx->flags&AV_CODEC_FLAG_BITEXACT) ? SWS_BITEXACT:0)); |
|
|
|
if (fr.num && fr.den) |
|
|
|
if (fr.num && fr.den) |
|
|
@ -750,9 +793,15 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, |
|
|
|
snprintf(name, sizeof(name), "graph %d input from stream %d:%d", fg->index, |
|
|
|
snprintf(name, sizeof(name), "graph %d input from stream %d:%d", fg->index, |
|
|
|
ist->file_index, ist->st->index); |
|
|
|
ist->file_index, ist->st->index); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ((ret = avfilter_graph_create_filter(&ifilter->filter, buffer_filt, name, |
|
|
|
if ((ret = avfilter_graph_create_filter(&ifilter->filter, buffer_filt, name, |
|
|
|
args.str, NULL, fg->graph)) < 0) |
|
|
|
args.str, NULL, fg->graph)) < 0) |
|
|
|
return ret; |
|
|
|
goto fail; |
|
|
|
|
|
|
|
par->hw_frames_ctx = ifilter->hw_frames_ctx; |
|
|
|
|
|
|
|
ret = av_buffersrc_parameters_set(ifilter->filter, par); |
|
|
|
|
|
|
|
if (ret < 0) |
|
|
|
|
|
|
|
goto fail; |
|
|
|
|
|
|
|
av_freep(&par); |
|
|
|
last_filter = ifilter->filter; |
|
|
|
last_filter = ifilter->filter; |
|
|
|
|
|
|
|
|
|
|
|
if (ist->autorotate) { |
|
|
|
if (ist->autorotate) { |
|
|
@ -776,27 +825,10 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, |
|
|
|
return ret; |
|
|
|
return ret; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (ist->framerate.num) { |
|
|
|
|
|
|
|
AVFilterContext *setpts; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
snprintf(name, sizeof(name), "force CFR for input from stream %d:%d", |
|
|
|
|
|
|
|
ist->file_index, ist->st->index); |
|
|
|
|
|
|
|
if ((ret = avfilter_graph_create_filter(&setpts, |
|
|
|
|
|
|
|
avfilter_get_by_name("setpts"), |
|
|
|
|
|
|
|
name, "N", NULL, |
|
|
|
|
|
|
|
fg->graph)) < 0) |
|
|
|
|
|
|
|
return ret; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ((ret = avfilter_link(last_filter, 0, setpts, 0)) < 0) |
|
|
|
|
|
|
|
return ret; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
last_filter = setpts; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (do_deinterlace) { |
|
|
|
if (do_deinterlace) { |
|
|
|
AVFilterContext *yadif; |
|
|
|
AVFilterContext *yadif; |
|
|
|
|
|
|
|
|
|
|
|
snprintf(name, sizeof(name), "deinterlace input from stream %d:%d", |
|
|
|
snprintf(name, sizeof(name), "deinterlace_in_%d_%d", |
|
|
|
ist->file_index, ist->st->index); |
|
|
|
ist->file_index, ist->st->index); |
|
|
|
if ((ret = avfilter_graph_create_filter(&yadif, |
|
|
|
if ((ret = avfilter_graph_create_filter(&yadif, |
|
|
|
avfilter_get_by_name("yadif"), |
|
|
|
avfilter_get_by_name("yadif"), |
|
|
@ -810,7 +842,7 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, |
|
|
|
last_filter = yadif; |
|
|
|
last_filter = yadif; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
snprintf(name, sizeof(name), "trim for input stream %d:%d", |
|
|
|
snprintf(name, sizeof(name), "trim_in_%d_%d", |
|
|
|
ist->file_index, ist->st->index); |
|
|
|
ist->file_index, ist->st->index); |
|
|
|
if (copy_ts) { |
|
|
|
if (copy_ts) { |
|
|
|
tsoffset = f->start_time == AV_NOPTS_VALUE ? 0 : f->start_time; |
|
|
|
tsoffset = f->start_time == AV_NOPTS_VALUE ? 0 : f->start_time; |
|
|
@ -826,6 +858,10 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, |
|
|
|
if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0) |
|
|
|
if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0) |
|
|
|
return ret; |
|
|
|
return ret; |
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
|
|
|
|
fail: |
|
|
|
|
|
|
|
av_freep(&par); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return ret; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static int configure_input_audio_filter(FilterGraph *fg, InputFilter *ifilter, |
|
|
|
static int configure_input_audio_filter(FilterGraph *fg, InputFilter *ifilter, |
|
|
@ -847,15 +883,15 @@ static int configure_input_audio_filter(FilterGraph *fg, InputFilter *ifilter, |
|
|
|
|
|
|
|
|
|
|
|
av_bprint_init(&args, 0, AV_BPRINT_SIZE_AUTOMATIC); |
|
|
|
av_bprint_init(&args, 0, AV_BPRINT_SIZE_AUTOMATIC); |
|
|
|
av_bprintf(&args, "time_base=%d/%d:sample_rate=%d:sample_fmt=%s", |
|
|
|
av_bprintf(&args, "time_base=%d/%d:sample_rate=%d:sample_fmt=%s", |
|
|
|
1, ist->dec_ctx->sample_rate, |
|
|
|
1, ifilter->sample_rate, |
|
|
|
ist->dec_ctx->sample_rate, |
|
|
|
ifilter->sample_rate, |
|
|
|
av_get_sample_fmt_name(ist->dec_ctx->sample_fmt)); |
|
|
|
av_get_sample_fmt_name(ifilter->format)); |
|
|
|
if (ist->dec_ctx->channel_layout) |
|
|
|
if (ifilter->channel_layout) |
|
|
|
av_bprintf(&args, ":channel_layout=0x%"PRIx64, |
|
|
|
av_bprintf(&args, ":channel_layout=0x%"PRIx64, |
|
|
|
ist->dec_ctx->channel_layout); |
|
|
|
ifilter->channel_layout); |
|
|
|
else |
|
|
|
else |
|
|
|
av_bprintf(&args, ":channels=%d", ist->dec_ctx->channels); |
|
|
|
av_bprintf(&args, ":channels=%d", ifilter->channels); |
|
|
|
snprintf(name, sizeof(name), "graph %d input from stream %d:%d", fg->index, |
|
|
|
snprintf(name, sizeof(name), "graph_%d_in_%d_%d", fg->index, |
|
|
|
ist->file_index, ist->st->index); |
|
|
|
ist->file_index, ist->st->index); |
|
|
|
|
|
|
|
|
|
|
|
if ((ret = avfilter_graph_create_filter(&ifilter->filter, abuffer_filt, |
|
|
|
if ((ret = avfilter_graph_create_filter(&ifilter->filter, abuffer_filt, |
|
|
@ -870,7 +906,7 @@ static int configure_input_audio_filter(FilterGraph *fg, InputFilter *ifilter, |
|
|
|
av_log(NULL, AV_LOG_INFO, opt_name " is forwarded to lavfi " \
|
|
|
|
av_log(NULL, AV_LOG_INFO, opt_name " is forwarded to lavfi " \
|
|
|
|
"similarly to -af " filter_name "=%s.\n", arg); \
|
|
|
|
"similarly to -af " filter_name "=%s.\n", arg); \
|
|
|
|
\
|
|
|
|
\
|
|
|
|
snprintf(name, sizeof(name), "graph %d %s for input stream %d:%d", \
|
|
|
|
snprintf(name, sizeof(name), "graph_%d_%s_in_%d_%d", \
|
|
|
|
fg->index, filter_name, ist->file_index, ist->st->index); \
|
|
|
|
fg->index, filter_name, ist->file_index, ist->st->index); \
|
|
|
|
ret = avfilter_graph_create_filter(&filt_ctx, \
|
|
|
|
ret = avfilter_graph_create_filter(&filt_ctx, \
|
|
|
|
avfilter_get_by_name(filter_name), \
|
|
|
|
avfilter_get_by_name(filter_name), \
|
|
|
@ -941,9 +977,6 @@ static int configure_input_audio_filter(FilterGraph *fg, InputFilter *ifilter, |
|
|
|
static int configure_input_filter(FilterGraph *fg, InputFilter *ifilter, |
|
|
|
static int configure_input_filter(FilterGraph *fg, InputFilter *ifilter, |
|
|
|
AVFilterInOut *in) |
|
|
|
AVFilterInOut *in) |
|
|
|
{ |
|
|
|
{ |
|
|
|
av_freep(&ifilter->name); |
|
|
|
|
|
|
|
DESCRIBE_FILTER_LINK(ifilter, in, 1); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!ifilter->ist->dec) { |
|
|
|
if (!ifilter->ist->dec) { |
|
|
|
av_log(NULL, AV_LOG_ERROR, |
|
|
|
av_log(NULL, AV_LOG_ERROR, |
|
|
|
"No decoder for stream #%d:%d, filtering impossible\n", |
|
|
|
"No decoder for stream #%d:%d, filtering impossible\n", |
|
|
@ -957,14 +990,24 @@ static int configure_input_filter(FilterGraph *fg, InputFilter *ifilter, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void cleanup_filtergraph(FilterGraph *fg) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
int i; |
|
|
|
|
|
|
|
for (i = 0; i < fg->nb_outputs; i++) |
|
|
|
|
|
|
|
fg->outputs[i]->filter = (AVFilterContext *)NULL; |
|
|
|
|
|
|
|
for (i = 0; i < fg->nb_inputs; i++) |
|
|
|
|
|
|
|
fg->inputs[i]->filter = (AVFilterContext *)NULL; |
|
|
|
|
|
|
|
avfilter_graph_free(&fg->graph); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int configure_filtergraph(FilterGraph *fg) |
|
|
|
int configure_filtergraph(FilterGraph *fg) |
|
|
|
{ |
|
|
|
{ |
|
|
|
AVFilterInOut *inputs, *outputs, *cur; |
|
|
|
AVFilterInOut *inputs, *outputs, *cur; |
|
|
|
int ret, i, simple = !fg->graph_desc; |
|
|
|
int ret, i, simple = filtergraph_is_simple(fg); |
|
|
|
const char *graph_desc = simple ? fg->outputs[0]->ost->avfilter : |
|
|
|
const char *graph_desc = simple ? fg->outputs[0]->ost->avfilter : |
|
|
|
fg->graph_desc; |
|
|
|
fg->graph_desc; |
|
|
|
|
|
|
|
|
|
|
|
avfilter_graph_free(&fg->graph); |
|
|
|
cleanup_filtergraph(fg); |
|
|
|
if (!(fg->graph = avfilter_graph_alloc())) |
|
|
|
if (!(fg->graph = avfilter_graph_alloc())) |
|
|
|
return AVERROR(ENOMEM); |
|
|
|
return AVERROR(ENOMEM); |
|
|
|
|
|
|
|
|
|
|
@ -973,6 +1016,8 @@ int configure_filtergraph(FilterGraph *fg) |
|
|
|
char args[512]; |
|
|
|
char args[512]; |
|
|
|
AVDictionaryEntry *e = NULL; |
|
|
|
AVDictionaryEntry *e = NULL; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fg->graph->nb_threads = filter_nbthreads; |
|
|
|
|
|
|
|
|
|
|
|
args[0] = 0; |
|
|
|
args[0] = 0; |
|
|
|
while ((e = av_dict_get(ost->sws_dict, "", e, |
|
|
|
while ((e = av_dict_get(ost->sws_dict, "", e, |
|
|
|
AV_DICT_IGNORE_SUFFIX))) { |
|
|
|
AV_DICT_IGNORE_SUFFIX))) { |
|
|
@ -998,15 +1043,28 @@ int configure_filtergraph(FilterGraph *fg) |
|
|
|
} |
|
|
|
} |
|
|
|
if (strlen(args)) |
|
|
|
if (strlen(args)) |
|
|
|
args[strlen(args) - 1] = '\0'; |
|
|
|
args[strlen(args) - 1] = '\0'; |
|
|
|
fg->graph->resample_lavr_opts = av_strdup(args); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
e = av_dict_get(ost->encoder_opts, "threads", NULL, 0); |
|
|
|
e = av_dict_get(ost->encoder_opts, "threads", NULL, 0); |
|
|
|
if (e) |
|
|
|
if (e) |
|
|
|
av_opt_set(fg->graph, "threads", e->value, 0); |
|
|
|
av_opt_set(fg->graph, "threads", e->value, 0); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
fg->graph->nb_threads = filter_complex_nbthreads; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if ((ret = avfilter_graph_parse2(fg->graph, graph_desc, &inputs, &outputs)) < 0) |
|
|
|
if ((ret = avfilter_graph_parse2(fg->graph, graph_desc, &inputs, &outputs)) < 0) |
|
|
|
return ret; |
|
|
|
goto fail; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (filter_hw_device || hw_device_ctx) { |
|
|
|
|
|
|
|
AVBufferRef *device = filter_hw_device ? filter_hw_device->device_ref |
|
|
|
|
|
|
|
: hw_device_ctx; |
|
|
|
|
|
|
|
for (i = 0; i < fg->graph->nb_filters; i++) { |
|
|
|
|
|
|
|
fg->graph->filters[i]->hw_device_ctx = av_buffer_ref(device); |
|
|
|
|
|
|
|
if (!fg->graph->filters[i]->hw_device_ctx) { |
|
|
|
|
|
|
|
ret = AVERROR(ENOMEM); |
|
|
|
|
|
|
|
goto fail; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (simple && (!inputs || inputs->next || !outputs || outputs->next)) { |
|
|
|
if (simple && (!inputs || inputs->next || !outputs || outputs->next)) { |
|
|
|
const char *num_inputs; |
|
|
|
const char *num_inputs; |
|
|
@ -1030,14 +1088,15 @@ int configure_filtergraph(FilterGraph *fg) |
|
|
|
" However, it had %s input(s) and %s output(s)." |
|
|
|
" However, it had %s input(s) and %s output(s)." |
|
|
|
" Please adjust, or use a complex filtergraph (-filter_complex) instead.\n", |
|
|
|
" Please adjust, or use a complex filtergraph (-filter_complex) instead.\n", |
|
|
|
graph_desc, num_inputs, num_outputs); |
|
|
|
graph_desc, num_inputs, num_outputs); |
|
|
|
return AVERROR(EINVAL); |
|
|
|
ret = AVERROR(EINVAL); |
|
|
|
|
|
|
|
goto fail; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for (cur = inputs, i = 0; cur; cur = cur->next, i++) |
|
|
|
for (cur = inputs, i = 0; cur; cur = cur->next, i++) |
|
|
|
if ((ret = configure_input_filter(fg, fg->inputs[i], cur)) < 0) { |
|
|
|
if ((ret = configure_input_filter(fg, fg->inputs[i], cur)) < 0) { |
|
|
|
avfilter_inout_free(&inputs); |
|
|
|
avfilter_inout_free(&inputs); |
|
|
|
avfilter_inout_free(&outputs); |
|
|
|
avfilter_inout_free(&outputs); |
|
|
|
return ret; |
|
|
|
goto fail; |
|
|
|
} |
|
|
|
} |
|
|
|
avfilter_inout_free(&inputs); |
|
|
|
avfilter_inout_free(&inputs); |
|
|
|
|
|
|
|
|
|
|
@ -1046,7 +1105,22 @@ int configure_filtergraph(FilterGraph *fg) |
|
|
|
avfilter_inout_free(&outputs); |
|
|
|
avfilter_inout_free(&outputs); |
|
|
|
|
|
|
|
|
|
|
|
if ((ret = avfilter_graph_config(fg->graph, NULL)) < 0) |
|
|
|
if ((ret = avfilter_graph_config(fg->graph, NULL)) < 0) |
|
|
|
return ret; |
|
|
|
goto fail; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* limit the lists of allowed formats to the ones selected, to
|
|
|
|
|
|
|
|
* make sure they stay the same if the filtergraph is reconfigured later */ |
|
|
|
|
|
|
|
for (i = 0; i < fg->nb_outputs; i++) { |
|
|
|
|
|
|
|
OutputFilter *ofilter = fg->outputs[i]; |
|
|
|
|
|
|
|
AVFilterContext *sink = ofilter->filter; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ofilter->format = av_buffersink_get_format(sink); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ofilter->width = av_buffersink_get_w(sink); |
|
|
|
|
|
|
|
ofilter->height = av_buffersink_get_h(sink); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ofilter->sample_rate = av_buffersink_get_sample_rate(sink); |
|
|
|
|
|
|
|
ofilter->channel_layout = av_buffersink_get_channel_layout(sink); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fg->reconfiguration = 1; |
|
|
|
fg->reconfiguration = 1; |
|
|
|
|
|
|
|
|
|
|
@ -1056,8 +1130,9 @@ int configure_filtergraph(FilterGraph *fg) |
|
|
|
/* identical to the same check in ffmpeg.c, needed because
|
|
|
|
/* identical to the same check in ffmpeg.c, needed because
|
|
|
|
complex filter graphs are initialized earlier */ |
|
|
|
complex filter graphs are initialized earlier */ |
|
|
|
av_log(NULL, AV_LOG_ERROR, "Encoder (codec %s) not found for output stream #%d:%d\n", |
|
|
|
av_log(NULL, AV_LOG_ERROR, "Encoder (codec %s) not found for output stream #%d:%d\n", |
|
|
|
avcodec_get_name(ost->st->codec->codec_id), ost->file_index, ost->index); |
|
|
|
avcodec_get_name(ost->st->codecpar->codec_id), ost->file_index, ost->index); |
|
|
|
return AVERROR(EINVAL); |
|
|
|
ret = AVERROR(EINVAL); |
|
|
|
|
|
|
|
goto fail; |
|
|
|
} |
|
|
|
} |
|
|
|
if (ost->enc->type == AVMEDIA_TYPE_AUDIO && |
|
|
|
if (ost->enc->type == AVMEDIA_TYPE_AUDIO && |
|
|
|
!(ost->enc->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)) |
|
|
|
!(ost->enc->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)) |
|
|
@ -1065,6 +1140,66 @@ int configure_filtergraph(FilterGraph *fg) |
|
|
|
ost->enc_ctx->frame_size); |
|
|
|
ost->enc_ctx->frame_size); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < fg->nb_inputs; i++) { |
|
|
|
|
|
|
|
while (av_fifo_size(fg->inputs[i]->frame_queue)) { |
|
|
|
|
|
|
|
AVFrame *tmp; |
|
|
|
|
|
|
|
av_fifo_generic_read(fg->inputs[i]->frame_queue, &tmp, sizeof(tmp), NULL); |
|
|
|
|
|
|
|
ret = av_buffersrc_add_frame(fg->inputs[i]->filter, tmp); |
|
|
|
|
|
|
|
av_frame_free(&tmp); |
|
|
|
|
|
|
|
if (ret < 0) |
|
|
|
|
|
|
|
goto fail; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* send the EOFs for the finished inputs */ |
|
|
|
|
|
|
|
for (i = 0; i < fg->nb_inputs; i++) { |
|
|
|
|
|
|
|
if (fg->inputs[i]->eof) { |
|
|
|
|
|
|
|
ret = av_buffersrc_add_frame(fg->inputs[i]->filter, NULL); |
|
|
|
|
|
|
|
if (ret < 0) |
|
|
|
|
|
|
|
goto fail; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* process queued up subtitle packets */ |
|
|
|
|
|
|
|
for (i = 0; i < fg->nb_inputs; i++) { |
|
|
|
|
|
|
|
InputStream *ist = fg->inputs[i]->ist; |
|
|
|
|
|
|
|
if (ist->sub2video.sub_queue && ist->sub2video.frame) { |
|
|
|
|
|
|
|
while (av_fifo_size(ist->sub2video.sub_queue)) { |
|
|
|
|
|
|
|
AVSubtitle tmp; |
|
|
|
|
|
|
|
av_fifo_generic_read(ist->sub2video.sub_queue, &tmp, sizeof(tmp), NULL); |
|
|
|
|
|
|
|
sub2video_update(ist, &tmp); |
|
|
|
|
|
|
|
avsubtitle_free(&tmp); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fail: |
|
|
|
|
|
|
|
cleanup_filtergraph(fg); |
|
|
|
|
|
|
|
return ret; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
av_buffer_unref(&ifilter->hw_frames_ctx); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ifilter->format = frame->format; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ifilter->width = frame->width; |
|
|
|
|
|
|
|
ifilter->height = frame->height; |
|
|
|
|
|
|
|
ifilter->sample_aspect_ratio = frame->sample_aspect_ratio; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ifilter->sample_rate = frame->sample_rate; |
|
|
|
|
|
|
|
ifilter->channels = frame->channels; |
|
|
|
|
|
|
|
ifilter->channel_layout = frame->channel_layout; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (frame->hw_frames_ctx) { |
|
|
|
|
|
|
|
ifilter->hw_frames_ctx = av_buffer_ref(frame->hw_frames_ctx); |
|
|
|
|
|
|
|
if (!ifilter->hw_frames_ctx) |
|
|
|
|
|
|
|
return AVERROR(ENOMEM); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -1077,3 +1212,7 @@ int ist_in_filtergraph(FilterGraph *fg, InputStream *ist) |
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int filtergraph_is_simple(FilterGraph *fg) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return !fg->graph_desc; |
|
|
|
|
|
|
|
} |
|
|
|