1178 lines · 43.5 KB · C
RawDownload
/* * ffmpeg filter configuration * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <stdint.h> #include "ffmpeg.h" #include "libavfilter/avfilter.h"#include "libavfilter/buffersink.h"#include "libavfilter/buffersrc.h" #include "libavresample/avresample.h" #include "libavutil/avassert.h"#include "libavutil/avstring.h"#include "libavutil/bprint.h"#include "libavutil/channel_layout.h"#include "libavutil/display.h"#include "libavutil/opt.h"#include "libavutil/pixdesc.h"#include "libavutil/pixfmt.h"#include "libavutil/imgutils.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 mjpeg_formats[] =        { AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P,          AV_PIX_FMT_YUV420P,  AV_PIX_FMT_YUV422P,  AV_PIX_FMT_YUV444P,          AV_PIX_FMT_NONE };    static const enum AVPixelFormat ljpeg_formats[] =        { AV_PIX_FMT_BGR24   , AV_PIX_FMT_BGRA    , AV_PIX_FMT_BGR0,          AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ422P,          AV_PIX_FMT_YUV420P , AV_PIX_FMT_YUV444P , AV_PIX_FMT_YUV422P,          AV_PIX_FMT_NONE};     if (codec_id == AV_CODEC_ID_MJPEG) {        return mjpeg_formats;    } else if (codec_id == AV_CODEC_ID_LJPEG) {        return ljpeg_formats;    } else {        return default_formats;    }} enum AVPixelFormat choose_pixel_fmt(AVStream *st, AVCodecContext *enc_ctx, AVCodec *codec, enum AVPixelFormat target){    if (codec && codec->pix_fmts) {        const enum AVPixelFormat *p = codec->pix_fmts;        const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(target);        int has_alpha = desc ? desc->nb_components % 2 == 0 : 0;        enum AVPixelFormat best= AV_PIX_FMT_NONE;         if (enc_ctx->strict_std_compliance <= FF_COMPLIANCE_UNOFFICIAL) {            p = get_compliance_unofficial_pix_fmts(enc_ctx->codec_id, p);        }        for (; *p != AV_PIX_FMT_NONE; p++) {            best= avcodec_find_best_pix_fmt_of_2(best, *p, target, has_alpha, NULL);            if (*p == target)                break;        }        if (*p == AV_PIX_FMT_NONE) {            if (target != AV_PIX_FMT_NONE)                av_log(NULL, AV_LOG_WARNING,                       "Incompatible pixel format '%s' for codec '%s', auto-selecting format '%s'\n",                       av_get_pix_fmt_name(target),                       codec->name,                       av_get_pix_fmt_name(best));            return best;        }    }    return target;} void choose_sample_fmt(AVStream *st, AVCodec *codec){    if (codec && codec->sample_fmts) {        const enum AVSampleFormat *p = codec->sample_fmts;        for (; *p != -1; p++) {            if (*p == st->codecpar->format)                break;        }        if (*p == -1) {            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");            if(av_get_sample_fmt_name(st->codecpar->format))            av_log(NULL, AV_LOG_WARNING,                   "Incompatible sample format '%s' for codec '%s', auto-selecting format '%s'\n",                   av_get_sample_fmt_name(st->codecpar->format),                   codec->name,                   av_get_sample_fmt_name(codec->sample_fmts[0]));            st->codecpar->format = codec->sample_fmts[0];        }    }} static char *choose_pix_fmts(OutputFilter *ofilter){    OutputStream *ost = ofilter->ost;    AVDictionaryEntry *strict_dict = av_dict_get(ost->encoder_opts, "strict", NULL, 0);    if (strict_dict)        // used by choose_pixel_fmt() and below        av_opt_set(ost->enc_ctx, "strict", strict_dict->value, 0);      if (ost->keep_pix_fmt) {        avfilter_graph_set_auto_convert(ofilter->graph->graph,                                            AVFILTER_AUTO_CONVERT_NONE);        if (ost->enc_ctx->pix_fmt == AV_PIX_FMT_NONE)            return NULL;        return av_strdup(av_get_pix_fmt_name(ost->enc_ctx->pix_fmt));    }    if (ost->enc_ctx->pix_fmt != AV_PIX_FMT_NONE) {        return av_strdup(av_get_pix_fmt_name(choose_pixel_fmt(ost->st, ost->enc_ctx, ost->enc, ost->enc_ctx->pix_fmt)));    } else if (ost->enc && ost->enc->pix_fmts) {        const enum AVPixelFormat *p;        AVIOContext *s = NULL;        uint8_t *ret;        int len;         if (avio_open_dyn_buf(&s) < 0)            exit_program(1);         p = ost->enc->pix_fmts;        if (ost->enc_ctx->strict_std_compliance <= FF_COMPLIANCE_UNOFFICIAL) {            p = get_compliance_unofficial_pix_fmts(ost->enc_ctx->codec_id, p);        }         for (; *p != AV_PIX_FMT_NONE; p++) {            const char *name = av_get_pix_fmt_name(*p);            avio_printf(s, "%s|", name);        }        len = avio_close_dyn_buf(s, &ret);        ret[len - 1] = 0;        return ret;    } else        return NULL;} /* Define a function for building a string containing a list of * allowed formats. */#define DEF_CHOOSE_FORMAT(suffix, type, var, supported_list, none, get_name)   \static char *choose_ ## suffix (OutputFilter *ofilter)                         \{                                                                              \    if (ofilter->var != none) {                                                \        get_name(ofilter->var);                                                \        return av_strdup(name);                                                \    } else if (ofilter->supported_list) {                                      \        const type *p;                                                         \        AVIOContext *s = NULL;                                                 \        uint8_t *ret;                                                          \        int len;                                                               \                                                                               \        if (avio_open_dyn_buf(&s) < 0)                                         \            exit_program(1);                                                           \                                                                               \        for (p = ofilter->supported_list; *p != none; p++) {                   \            get_name(*p);                                                      \            avio_printf(s, "%s|", name);                                       \        }                                                                      \        len = avio_close_dyn_buf(s, &ret);                                     \        ret[len - 1] = 0;                                                      \        return ret;                                                            \    } else                                                                     \        return NULL;                                                           \} //DEF_CHOOSE_FORMAT(pix_fmts, enum AVPixelFormat, format, formats, AV_PIX_FMT_NONE,//                  GET_PIX_FMT_NAME) DEF_CHOOSE_FORMAT(sample_fmts, enum AVSampleFormat, format, formats,                  AV_SAMPLE_FMT_NONE, GET_SAMPLE_FMT_NAME) DEF_CHOOSE_FORMAT(sample_rates, int, sample_rate, sample_rates, 0,                  GET_SAMPLE_RATE_NAME) DEF_CHOOSE_FORMAT(channel_layouts, uint64_t, channel_layout, channel_layouts, 0,                  GET_CH_LAYOUT_NAME) int init_simple_filtergraph(InputStream *ist, OutputStream *ost){    FilterGraph *fg = av_mallocz(sizeof(*fg));     if (!fg)        exit_program(1);    fg->index = nb_filtergraphs;     GROW_ARRAY(fg->outputs, fg->nb_outputs);    if (!(fg->outputs[0] = av_mallocz(sizeof(*fg->outputs[0]))))        exit_program(1);    fg->outputs[0]->ost   = ost;    fg->outputs[0]->graph = fg;    fg->outputs[0]->format = -1;     ost->filter = fg->outputs[0];     GROW_ARRAY(fg->inputs, fg->nb_inputs);    if (!(fg->inputs[0] = av_mallocz(sizeof(*fg->inputs[0]))))        exit_program(1);    fg->inputs[0]->ist   = ist;    fg->inputs[0]->graph = fg;    fg->inputs[0]->format = -1;     GROW_ARRAY(ist->filters, ist->nb_filters);    ist->filters[ist->nb_filters - 1] = fg->inputs[0];     GROW_ARRAY(filtergraphs, nb_filtergraphs);    filtergraphs[nb_filtergraphs - 1] = fg;     return 0;} static void init_input_filter(FilterGraph *fg, AVFilterInOut *in){    InputStream *ist = NULL;    enum AVMediaType type = avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx);    int i;     // TODO: support other filter types    if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO) {        av_log(NULL, AV_LOG_FATAL, "Only video and audio filters supported "               "currently.\n");        exit_program(1);    }     if (in->name) {        AVFormatContext *s;        AVStream       *st = NULL;        char *p;        int file_idx = strtol(in->name, &p, 0);         if (file_idx < 0 || file_idx >= nb_input_files) {            av_log(NULL, AV_LOG_FATAL, "Invalid file index %d in filtergraph description %s.\n",                   file_idx, fg->graph_desc);            exit_program(1);        }        s = input_files[file_idx]->ctx;         for (i = 0; i < s->nb_streams; i++) {            enum AVMediaType stream_type = s->streams[i]->codecpar->codec_type;            if (stream_type != type &&                !(stream_type == AVMEDIA_TYPE_SUBTITLE &&                  type == AVMEDIA_TYPE_VIDEO /* sub2video hack */))                continue;            if (check_stream_specifier(s, s->streams[i], *p == ':' ? p + 1 : p) == 1) {                st = s->streams[i];                break;            }        }        if (!st) {            av_log(NULL, AV_LOG_FATAL, "Stream specifier '%s' in filtergraph description %s "                   "matches no streams.\n", p, fg->graph_desc);            exit_program(1);        }        ist = input_streams[input_files[file_idx]->ist_index + st->index];    } else {        /* find the first unused stream of corresponding type */        for (i = 0; i < nb_input_streams; i++) {            ist = input_streams[i];            if (ist->dec_ctx->codec_type == type && ist->discard)                break;        }        if (i == nb_input_streams) {            av_log(NULL, AV_LOG_FATAL, "Cannot find a matching stream for "                   "unlabeled input pad %d on filter %s\n", in->pad_idx,                   in->filter_ctx->name);            exit_program(1);        }    }    av_assert0(ist);     ist->discard         = 0;    ist->decoding_needed |= DECODING_FOR_FILTER;    ist->st->discard = AVDISCARD_NONE;     GROW_ARRAY(fg->inputs, fg->nb_inputs);    if (!(fg->inputs[fg->nb_inputs - 1] = av_mallocz(sizeof(*fg->inputs[0]))))        exit_program(1);    fg->inputs[fg->nb_inputs - 1]->ist   = ist;    fg->inputs[fg->nb_inputs - 1]->graph = fg;    fg->inputs[fg->nb_inputs - 1]->format = -1;     GROW_ARRAY(ist->filters, ist->nb_filters);    ist->filters[ist->nb_filters - 1] = fg->inputs[fg->nb_inputs - 1];} int init_complex_filtergraph(FilterGraph *fg){    AVFilterInOut *inputs, *outputs, *cur;    AVFilterGraph *graph;    int ret = 0;     /* this graph is only used for determining the kinds of inputs     * and outputs we have, and is discarded on exit from this function */    graph = avfilter_graph_alloc();    if (!graph)        return AVERROR(ENOMEM);     ret = avfilter_graph_parse2(graph, fg->graph_desc, &inputs, &outputs);    if (ret < 0)        goto fail;     for (cur = inputs; cur; cur = cur->next)        init_input_filter(fg, cur);     for (cur = outputs; cur;) {        GROW_ARRAY(fg->outputs, fg->nb_outputs);        fg->outputs[fg->nb_outputs - 1] = av_mallocz(sizeof(*fg->outputs[0]));        if (!fg->outputs[fg->nb_outputs - 1])            exit_program(1);         fg->outputs[fg->nb_outputs - 1]->graph   = fg;        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,                                                                         cur->pad_idx);        cur = cur->next;        fg->outputs[fg->nb_outputs - 1]->out_tmp->next = NULL;    } fail:    avfilter_inout_free(&inputs);    avfilter_graph_free(&graph);    return ret;} static int insert_trim(int64_t start_time, int64_t duration,                       AVFilterContext **last_filter, int *pad_idx,                       const char *filter_name){    AVFilterGraph *graph = (*last_filter)->graph;    AVFilterContext *ctx;    const AVFilter *trim;    enum AVMediaType type = avfilter_pad_get_type((*last_filter)->output_pads, *pad_idx);    const char *name = (type == AVMEDIA_TYPE_VIDEO) ? "trim" : "atrim";    int ret = 0;     if (duration == INT64_MAX && start_time == AV_NOPTS_VALUE)        return 0;     trim = avfilter_get_by_name(name);    if (!trim) {        av_log(NULL, AV_LOG_ERROR, "%s filter not present, cannot limit "               "recording time.\n", name);        return AVERROR_FILTER_NOT_FOUND;    }     ctx = avfilter_graph_alloc_filter(graph, trim, filter_name);    if (!ctx)        return AVERROR(ENOMEM);     if (duration != INT64_MAX) {        ret = av_opt_set_int(ctx, "durationi", duration,                                AV_OPT_SEARCH_CHILDREN);    }    if (ret >= 0 && start_time != AV_NOPTS_VALUE) {        ret = av_opt_set_int(ctx, "starti", start_time,                                AV_OPT_SEARCH_CHILDREN);    }    if (ret < 0) {        av_log(ctx, AV_LOG_ERROR, "Error configuring the %s filter", name);        return ret;    }     ret = avfilter_init_str(ctx, NULL);    if (ret < 0)        return ret;     ret = avfilter_link(*last_filter, *pad_idx, ctx, 0);    if (ret < 0)        return ret;     *last_filter = ctx;    *pad_idx     = 0;    return 0;} static int insert_filter(AVFilterContext **last_filter, int *pad_idx,                         const char *filter_name, const char *args){    AVFilterGraph *graph = (*last_filter)->graph;    AVFilterContext *ctx;    int ret;     ret = avfilter_graph_create_filter(&ctx,                                       avfilter_get_by_name(filter_name),                                       filter_name, args, NULL, graph);    if (ret < 0)        return ret;     ret = avfilter_link(*last_filter, *pad_idx, ctx, 0);    if (ret < 0)        return ret;     *last_filter = ctx;    *pad_idx     = 0;    return 0;} static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out){    char *pix_fmts;    OutputStream *ost = ofilter->ost;    OutputFile    *of = output_files[ost->file_index];    AVFilterContext *last_filter = out->filter_ctx;    int pad_idx = out->pad_idx;    int ret;    char name[255];     snprintf(name, sizeof(name), "out_%d_%d", ost->file_index, ost->index);    ret = avfilter_graph_create_filter(&ofilter->filter,                                       avfilter_get_by_name("buffersink"),                                       name, NULL, NULL, fg->graph);     if (ret < 0)        return ret;     if (!hw_device_ctx && (ofilter->width || ofilter->height)) {        char args[255];        AVFilterContext *filter;        AVDictionaryEntry *e = NULL;         snprintf(args, sizeof(args), "%d:%d",                 ofilter->width, ofilter->height);         while ((e = av_dict_get(ost->sws_dict, "", e,                                AV_DICT_IGNORE_SUFFIX))) {            av_strlcatf(args, sizeof(args), ":%s=%s", e->key, e->value);        }         snprintf(name, sizeof(name), "scaler_out_%d_%d",                 ost->file_index, ost->index);        if ((ret = avfilter_graph_create_filter(&filter, avfilter_get_by_name("scale"),                                                name, args, NULL, fg->graph)) < 0)            return ret;        if ((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0)            return ret;         last_filter = filter;        pad_idx = 0;    }     if ((pix_fmts = choose_pix_fmts(ofilter))) {        AVFilterContext *filter;        snprintf(name, sizeof(name), "format_out_%d_%d",                 ost->file_index, ost->index);        ret = avfilter_graph_create_filter(&filter,                                           avfilter_get_by_name("format"),                                           "format", pix_fmts, NULL, fg->graph);        av_freep(&pix_fmts);        if (ret < 0)            return ret;        if ((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0)            return ret;         last_filter = filter;        pad_idx     = 0;    }     if (ost->frame_rate.num && 0) {        AVFilterContext *fps;        char args[255];         snprintf(args, sizeof(args), "fps=%d/%d", ost->frame_rate.num,                 ost->frame_rate.den);        snprintf(name, sizeof(name), "fps_out_%d_%d",                 ost->file_index, ost->index);        ret = avfilter_graph_create_filter(&fps, avfilter_get_by_name("fps"),                                           name, args, NULL, fg->graph);        if (ret < 0)            return ret;         ret = avfilter_link(last_filter, pad_idx, fps, 0);        if (ret < 0)            return ret;        last_filter = fps;        pad_idx = 0;    }     snprintf(name, sizeof(name), "trim_out_%d_%d",             ost->file_index, ost->index);    ret = insert_trim(of->start_time, of->recording_time,                      &last_filter, &pad_idx, name);    if (ret < 0)        return ret;

Lines 1–500 of 1178.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t