X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=cmdutils.c;h=9dbff9e846154fbeaf48692b4eb38be32cf058bd;hb=c010fce6c91f80245c6d38f6281ffb054b379f45;hp=336f50bfd8395a3d64bb9dd89a7791c8afe0c41a;hpb=4138cd29077de2fbca9c49e96f70d21a78e24e33;p=ffmpeg diff --git a/cmdutils.c b/cmdutils.c index 336f50bfd83..9dbff9e8461 100644 --- a/cmdutils.c +++ b/cmdutils.c @@ -32,9 +32,12 @@ #include "libavformat/avformat.h" #include "libavfilter/avfilter.h" #include "libavdevice/avdevice.h" +#include "libavresample/avresample.h" #include "libswscale/swscale.h" +#include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/mathematics.h" +#include "libavutil/imgutils.h" #include "libavutil/parseutils.h" #include "libavutil/pixdesc.h" #include "libavutil/eval.h" @@ -94,7 +97,7 @@ double parse_number_or_die(const char *context, const char *numstr, int type, else return d; av_log(NULL, AV_LOG_FATAL, error, context, numstr, min, max); - exit_program(1); + exit(1); return 0; } @@ -105,13 +108,13 @@ int64_t parse_time_or_die(const char *context, const char *timestr, if (av_parse_time(&us, timestr, is_duration) < 0) { av_log(NULL, AV_LOG_FATAL, "Invalid %s specification for %s: %s\n", is_duration ? "duration" : "date", context, timestr); - exit_program(1); + exit(1); } return us; } -void show_help_options(const OptionDef *options, const char *msg, int mask, - int value) +void show_help_options(const OptionDef *options, const char *msg, int req_flags, + int rej_flags, int alt_flags) { const OptionDef *po; int first; @@ -119,19 +122,24 @@ void show_help_options(const OptionDef *options, const char *msg, int mask, first = 1; for (po = options; po->name != NULL; po++) { char buf[64]; - if ((po->flags & mask) == value) { - if (first) { - printf("%s", msg); - first = 0; - } - av_strlcpy(buf, po->name, sizeof(buf)); - if (po->flags & HAS_ARG) { - av_strlcat(buf, " ", sizeof(buf)); - av_strlcat(buf, po->argname, sizeof(buf)); - } - printf("-%-17s %s\n", buf, po->help); + + if (((po->flags & req_flags) != req_flags) || + (alt_flags && !(po->flags & alt_flags)) || + (po->flags & rej_flags)) + continue; + + if (first) { + printf("%s\n", msg); + first = 0; + } + av_strlcpy(buf, po->name, sizeof(buf)); + if (po->argname) { + av_strlcat(buf, " ", sizeof(buf)); + av_strlcat(buf, po->argname, sizeof(buf)); } + printf("-%-17s %s\n", buf, po->help); } + printf("\n"); } void show_help_children(const AVClass *class, int flags) @@ -159,6 +167,7 @@ static const OptionDef *find_option(const OptionDef *po, const char *name) #if defined(_WIN32) && !defined(__MINGW32CE__) #include +#include /* Will be leaked on exit */ static char** win32_argv_utf8 = NULL; static int win32_argc = 0; @@ -262,6 +271,7 @@ int parse_option(void *optctx, const char *opt, const char *arg, if (po->flags & OPT_STRING) { char *str; str = av_strdup(arg); + av_freep(dst); *(char **)dst = str; } else if (po->flags & OPT_BOOL) { *(int *)dst = bool_val; @@ -276,8 +286,7 @@ int parse_option(void *optctx, const char *opt, const char *arg, } else if (po->flags & OPT_DOUBLE) { *(double *)dst = parse_number_or_die(opt, arg, OPT_DOUBLE, -INFINITY, INFINITY); } else if (po->u.func_arg) { - int ret = po->flags & OPT_FUNC2 ? po->u.func2_arg(optctx, opt, arg) - : po->u.func_arg(opt, arg); + int ret = po->u.func_arg(optctx, opt, arg); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Failed to set value '%s' for option '%s'\n", arg, opt); @@ -285,7 +294,7 @@ int parse_option(void *optctx, const char *opt, const char *arg, } } if (po->flags & OPT_EXIT) - exit_program(0); + exit(0); return !!(po->flags & HAS_ARG); } @@ -311,7 +320,7 @@ void parse_options(void *optctx, int argc, char **argv, const OptionDef *options opt++; if ((ret = parse_option(optctx, opt, argv[optindex], options)) < 0) - exit_program(1); + exit(1); optindex += ret; } else { if (parse_arg_function) @@ -352,11 +361,11 @@ void parse_loglevel(int argc, char **argv, const OptionDef *options) if (!idx) idx = locate_option(argc, argv, options, "v"); if (idx && argv[idx + 1]) - opt_loglevel("loglevel", argv[idx + 1]); + opt_loglevel(NULL, "loglevel", argv[idx + 1]); } #define FLAGS (o->type == AV_OPT_TYPE_FLAGS) ? AV_DICT_APPEND : 0 -int opt_default(const char *opt, const char *arg) +int opt_default(void *optctx, const char *opt, const char *arg) { const AVOption *o; char opt_stripped[128]; @@ -391,7 +400,7 @@ int opt_default(const char *opt, const char *arg) return AVERROR_OPTION_NOT_FOUND; } -int opt_loglevel(const char *opt, const char *arg) +int opt_loglevel(void *optctx, const char *opt, const char *arg) { const struct { const char *name; int level; } log_levels[] = { { "quiet" , AV_LOG_QUIET }, @@ -420,13 +429,13 @@ int opt_loglevel(const char *opt, const char *arg) "Possible levels are numbers or:\n", arg); for (i = 0; i < FF_ARRAY_ELEMS(log_levels); i++) av_log(NULL, AV_LOG_FATAL, "\"%s\"\n", log_levels[i].name); - exit_program(1); + exit(1); } av_log_set_level(level); return 0; } -int opt_timelimit(const char *opt, const char *arg) +int opt_timelimit(void *optctx, const char *opt, const char *arg) { #if HAVE_SETRLIMIT int lim = parse_number_or_die(opt, arg, OPT_INT64, 0, INT_MAX); @@ -460,7 +469,8 @@ static int warned_cfg = 0; const char *indent = flags & INDENT? " " : ""; \ if (flags & SHOW_VERSION) { \ unsigned int version = libname##_version(); \ - av_log(NULL, level, "%slib%-9s %2d.%3d.%2d / %2d.%3d.%2d\n",\ + av_log(NULL, level, \ + "%slib%-10s %2d.%3d.%2d / %2d.%3d.%2d\n", \ indent, #libname, \ LIB##LIBNAME##_VERSION_MAJOR, \ LIB##LIBNAME##_VERSION_MINOR, \ @@ -489,6 +499,7 @@ static void print_all_libs_info(int flags, int level) PRINT_LIB_INFO(avformat, AVFORMAT, flags, level); PRINT_LIB_INFO(avdevice, AVDEVICE, flags, level); PRINT_LIB_INFO(avfilter, AVFILTER, flags, level); + PRINT_LIB_INFO(avresample, AVRESAMPLE, flags, level); PRINT_LIB_INFO(swscale, SWSCALE, flags, level); } @@ -497,20 +508,23 @@ void show_banner(void) av_log(NULL, AV_LOG_INFO, "%s version " LIBAV_VERSION ", Copyright (c) %d-%d the Libav developers\n", program_name, program_birth_year, this_year); - av_log(NULL, AV_LOG_INFO, " built on %s %s with %s %s\n", - __DATE__, __TIME__, CC_TYPE, CC_VERSION); + av_log(NULL, AV_LOG_INFO, " built on %s %s with %s\n", + __DATE__, __TIME__, CC_IDENT); av_log(NULL, AV_LOG_VERBOSE, " configuration: " LIBAV_CONFIGURATION "\n"); print_all_libs_info(INDENT|SHOW_CONFIG, AV_LOG_VERBOSE); print_all_libs_info(INDENT|SHOW_VERSION, AV_LOG_VERBOSE); } -void show_version(void) { +int show_version(void *optctx, const char *opt, const char *arg) +{ av_log_set_callback(log_callback_help); printf("%s " LIBAV_VERSION "\n", program_name); print_all_libs_info(SHOW_VERSION, AV_LOG_INFO); + + return 0; } -void show_license(void) +int show_license(void *optctx, const char *opt, const char *arg) { printf( #if CONFIG_NONFREE @@ -577,9 +591,11 @@ void show_license(void) program_name, program_name, program_name #endif ); + + return 0; } -void show_formats(void) +int show_formats(void *optctx, const char *opt, const char *arg) { AVInputFormat *ifmt = NULL; AVOutputFormat *ofmt = NULL; @@ -624,86 +640,193 @@ void show_formats(void) name, long_name ? long_name:" "); } + return 0; } -void show_codecs(void) +#define PRINT_CODEC_SUPPORTED(codec, field, type, list_name, term, get_name) \ + if (codec->field) { \ + const type *p = c->field; \ + \ + printf(" Supported " list_name ":"); \ + while (*p != term) { \ + get_name(*p); \ + printf(" %s", name); \ + p++; \ + } \ + printf("\n"); \ + } \ + +static void print_codec(const AVCodec *c) { - AVCodec *p = NULL, *p2; - const char *last_name; + int encoder = av_codec_is_encoder(c); + + printf("%s %s [%s]:\n", encoder ? "Encoder" : "Decoder", c->name, + c->long_name ? c->long_name : ""); + + if (c->type == AVMEDIA_TYPE_VIDEO) { + printf(" Threading capabilities: "); + switch (c->capabilities & (CODEC_CAP_FRAME_THREADS | + CODEC_CAP_SLICE_THREADS)) { + case CODEC_CAP_FRAME_THREADS | + CODEC_CAP_SLICE_THREADS: printf("frame and slice"); break; + case CODEC_CAP_FRAME_THREADS: printf("frame"); break; + case CODEC_CAP_SLICE_THREADS: printf("slice"); break; + default: printf("no"); break; + } + printf("\n"); + } + + if (c->supported_framerates) { + const AVRational *fps = c->supported_framerates; + + printf(" Supported framerates:"); + while (fps->num) { + printf(" %d/%d", fps->num, fps->den); + fps++; + } + printf("\n"); + } + PRINT_CODEC_SUPPORTED(c, pix_fmts, enum PixelFormat, "pixel formats", + PIX_FMT_NONE, GET_PIX_FMT_NAME); + PRINT_CODEC_SUPPORTED(c, supported_samplerates, int, "sample rates", 0, + GET_SAMPLE_RATE_NAME); + PRINT_CODEC_SUPPORTED(c, sample_fmts, enum AVSampleFormat, "sample formats", + AV_SAMPLE_FMT_NONE, GET_SAMPLE_FMT_NAME); + PRINT_CODEC_SUPPORTED(c, channel_layouts, uint64_t, "channel layouts", + 0, GET_CH_LAYOUT_DESC); + + if (c->priv_class) { + show_help_children(c->priv_class, + AV_OPT_FLAG_ENCODING_PARAM | + AV_OPT_FLAG_DECODING_PARAM); + } +} + +static char get_media_type_char(enum AVMediaType type) +{ + switch (type) { + case AVMEDIA_TYPE_VIDEO: return 'V'; + case AVMEDIA_TYPE_AUDIO: return 'A'; + case AVMEDIA_TYPE_SUBTITLE: return 'S'; + default: return '?'; + } +} + +static const AVCodec *next_codec_for_id(enum AVCodecID id, const AVCodec *prev, + int encoder) +{ + while ((prev = av_codec_next(prev))) { + if (prev->id == id && + (encoder ? av_codec_is_encoder(prev) : av_codec_is_decoder(prev))) + return prev; + } + return NULL; +} + +static void print_codecs_for_id(enum AVCodecID id, int encoder) +{ + const AVCodec *codec = NULL; + + printf(" (%s: ", encoder ? "encoders" : "decoders"); + + while ((codec = next_codec_for_id(id, codec, encoder))) + printf("%s ", codec->name); + + printf(")"); +} + +int show_codecs(void *optctx, const char *opt, const char *arg) +{ + const AVCodecDescriptor *desc = NULL; + printf("Codecs:\n" " D..... = Decoding supported\n" " .E.... = Encoding supported\n" " ..V... = Video codec\n" " ..A... = Audio codec\n" " ..S... = Subtitle codec\n" - " ...S.. = Supports draw_horiz_band\n" - " ....D. = Supports direct rendering method 1\n" - " .....T = Supports weird frame truncation\n" - " ------\n"); - last_name= "000"; - for (;;) { - int decode = 0; - int encode = 0; - int cap = 0; - const char *type_str; - - p2 = NULL; - while ((p = av_codec_next(p))) { - if ((p2 == NULL || strcmp(p->name, p2->name) < 0) && - strcmp(p->name, last_name) > 0) { - p2 = p; - decode = encode = cap = 0; + " ...I.. = Intra frame-only codec\n" + " ....L. = Lossy compression\n" + " .....S = Lossless compression\n" + " -------\n"); + while ((desc = avcodec_descriptor_next(desc))) { + const AVCodec *codec = NULL; + + printf(avcodec_find_decoder(desc->id) ? "D" : "."); + printf(avcodec_find_encoder(desc->id) ? "E" : "."); + + printf("%c", get_media_type_char(desc->type)); + printf((desc->props & AV_CODEC_PROP_INTRA_ONLY) ? "I" : "."); + printf((desc->props & AV_CODEC_PROP_LOSSY) ? "L" : "."); + printf((desc->props & AV_CODEC_PROP_LOSSLESS) ? "S" : "."); + + printf(" %-20s %s", desc->name, desc->long_name ? desc->long_name : ""); + + /* print decoders/encoders when there's more than one or their + * names are different from codec name */ + while ((codec = next_codec_for_id(desc->id, codec, 0))) { + if (strcmp(codec->name, desc->name)) { + print_codecs_for_id(desc->id, 0); + break; } - if (p2 && strcmp(p->name, p2->name) == 0) { - if (av_codec_is_decoder(p)) - decode = 1; - if (av_codec_is_encoder(p)) - encode = 1; - cap |= p->capabilities; + } + codec = NULL; + while ((codec = next_codec_for_id(desc->id, codec, 1))) { + if (strcmp(codec->name, desc->name)) { + print_codecs_for_id(desc->id, 1); + break; } } - if (p2 == NULL) - break; - last_name = p2->name; - switch (p2->type) { - case AVMEDIA_TYPE_VIDEO: - type_str = "V"; - break; - case AVMEDIA_TYPE_AUDIO: - type_str = "A"; - break; - case AVMEDIA_TYPE_SUBTITLE: - type_str = "S"; - break; - default: - type_str = "?"; - break; - } - printf(" %s%s%s%s%s%s %-15s %s", - decode ? "D" : (/* p2->decoder ? "d" : */ " "), - encode ? "E" : " ", - type_str, - cap & CODEC_CAP_DRAW_HORIZ_BAND ? "S" : " ", - cap & CODEC_CAP_DR1 ? "D" : " ", - cap & CODEC_CAP_TRUNCATED ? "T" : " ", - p2->name, - p2->long_name ? p2->long_name : ""); -#if 0 - if (p2->decoder && decode == 0) - printf(" use %s for decoding", p2->decoder->name); -#endif printf("\n"); } - printf("\n"); - printf("Note, the names of encoders and decoders do not always match, so there are\n" - "several cases where the above table shows encoder only or decoder only entries\n" - "even though both encoding and decoding are supported. For example, the h263\n" - "decoder corresponds to the h263 and h263p encoders, for file formats it is even\n" - "worse.\n"); + return 0; } -void show_bsfs(void) +static void print_codecs(int encoder) +{ + const AVCodecDescriptor *desc = NULL; + + printf("%s:\n" + " V... = Video\n" + " A... = Audio\n" + " S... = Subtitle\n" + " .F.. = Frame-level multithreading\n" + " ..S. = Slice-level multithreading\n" + " ...X = Codec is experimental\n" + " ---\n", + encoder ? "Encoders" : "Decoders"); + while ((desc = avcodec_descriptor_next(desc))) { + const AVCodec *codec = NULL; + + while ((codec = next_codec_for_id(desc->id, codec, encoder))) { + printf("%c", get_media_type_char(desc->type)); + printf((codec->capabilities & CODEC_CAP_FRAME_THREADS) ? "F" : "."); + printf((codec->capabilities & CODEC_CAP_SLICE_THREADS) ? "S" : "."); + printf((codec->capabilities & CODEC_CAP_EXPERIMENTAL) ? "X" : "."); + + printf(" %-20s %s", codec->name, codec->long_name ? codec->long_name : ""); + if (strcmp(codec->name, desc->name)) + printf(" (codec %s)", desc->name); + + printf("\n"); + } + } +} + +int show_decoders(void *optctx, const char *opt, const char *arg) +{ + print_codecs(0); + return 0; +} + +int show_encoders(void *optctx, const char *opt, const char *arg) +{ + print_codecs(1); + return 0; +} + +int show_bsfs(void *optctx, const char *opt, const char *arg) { AVBitStreamFilter *bsf = NULL; @@ -711,9 +834,10 @@ void show_bsfs(void) while ((bsf = av_bitstream_filter_next(bsf))) printf("%s\n", bsf->name); printf("\n"); + return 0; } -void show_protocols(void) +int show_protocols(void *optctx, const char *opt, const char *arg) { void *opaque = NULL; const char *name; @@ -725,9 +849,10 @@ void show_protocols(void) printf("Output:\n"); while ((name = avio_enum_protocols(&opaque, 1))) printf("%s\n", name); + return 0; } -void show_filters(void) +int show_filters(void *optctx, const char *opt, const char *arg) { AVFilter av_unused(**filter) = NULL; @@ -736,9 +861,10 @@ void show_filters(void) while ((filter = av_filter_next(filter)) && *filter) printf("%-16s %s\n", (*filter)->name, (*filter)->description); #endif + return 0; } -void show_pix_fmts(void) +int show_pix_fmts(void *optctx, const char *opt, const char *arg) { enum PixelFormat pix_fmt; @@ -768,9 +894,10 @@ void show_pix_fmts(void) pix_desc->nb_components, av_get_bits_per_pixel(pix_desc)); } + return 0; } -int show_sample_fmts(const char *opt, const char *arg) +int show_sample_fmts(void *optctx, const char *opt, const char *arg) { int i; char fmt_str[128]; @@ -779,6 +906,120 @@ int show_sample_fmts(const char *opt, const char *arg) return 0; } +static void show_help_codec(const char *name, int encoder) +{ + const AVCodecDescriptor *desc; + const AVCodec *codec; + + if (!name) { + av_log(NULL, AV_LOG_ERROR, "No codec name specified.\n"); + return; + } + + codec = encoder ? avcodec_find_encoder_by_name(name) : + avcodec_find_decoder_by_name(name); + + if (codec) + print_codec(codec); + else if ((desc = avcodec_descriptor_get_by_name(name))) { + int printed = 0; + + while ((codec = next_codec_for_id(desc->id, codec, encoder))) { + printed = 1; + print_codec(codec); + } + + if (!printed) { + av_log(NULL, AV_LOG_ERROR, "Codec '%s' is known to Libav, " + "but no %s for it are available. Libav might need to be " + "recompiled with additional external libraries.\n", + name, encoder ? "encoders" : "decoders"); + } + } else { + av_log(NULL, AV_LOG_ERROR, "Codec '%s' is not recognized by Libav.\n", + name); + } +} + +static void show_help_demuxer(const char *name) +{ + const AVInputFormat *fmt = av_find_input_format(name); + + if (!fmt) { + av_log(NULL, AV_LOG_ERROR, "Unknown format '%s'.\n", name); + return; + } + + printf("Demuxer %s [%s]:\n", fmt->name, fmt->long_name); + + if (fmt->extensions) + printf(" Common extensions: %s.\n", fmt->extensions); + + if (fmt->priv_class) + show_help_children(fmt->priv_class, AV_OPT_FLAG_DECODING_PARAM); +} + +static void show_help_muxer(const char *name) +{ + const AVCodecDescriptor *desc; + const AVOutputFormat *fmt = av_guess_format(name, NULL, NULL); + + if (!fmt) { + av_log(NULL, AV_LOG_ERROR, "Unknown format '%s'.\n", name); + return; + } + + printf("Muxer %s [%s]:\n", fmt->name, fmt->long_name); + + if (fmt->extensions) + printf(" Common extensions: %s.\n", fmt->extensions); + if (fmt->mime_type) + printf(" Mime type: %s.\n", fmt->mime_type); + if (fmt->video_codec != AV_CODEC_ID_NONE && + (desc = avcodec_descriptor_get(fmt->video_codec))) { + printf(" Default video codec: %s.\n", desc->name); + } + if (fmt->audio_codec != AV_CODEC_ID_NONE && + (desc = avcodec_descriptor_get(fmt->audio_codec))) { + printf(" Default audio codec: %s.\n", desc->name); + } + if (fmt->subtitle_codec != AV_CODEC_ID_NONE && + (desc = avcodec_descriptor_get(fmt->subtitle_codec))) { + printf(" Default subtitle codec: %s.\n", desc->name); + } + + if (fmt->priv_class) + show_help_children(fmt->priv_class, AV_OPT_FLAG_ENCODING_PARAM); +} + +int show_help(void *optctx, const char *opt, const char *arg) +{ + char *topic, *par; + av_log_set_callback(log_callback_help); + + topic = av_strdup(arg ? arg : ""); + par = strchr(topic, '='); + if (par) + *par++ = 0; + + if (!*topic) { + show_help_default(topic, par); + } else if (!strcmp(topic, "decoder")) { + show_help_codec(par, 0); + } else if (!strcmp(topic, "encoder")) { + show_help_codec(par, 1); + } else if (!strcmp(topic, "demuxer")) { + show_help_demuxer(par); + } else if (!strcmp(topic, "muxer")) { + show_help_muxer(par); + } else { + show_help_default(topic, par); + } + + av_freep(&topic); + return 0; +} + int read_yesno(void) { int c = getchar(); @@ -872,12 +1113,12 @@ FILE *get_preset_file(char *filename, size_t filename_size, for (i = 0; i < 3 && !f; i++) { if (!base[i]) continue; - snprintf(filename, filename_size, "%s%s/%s.ffpreset", base[i], + snprintf(filename, filename_size, "%s%s/%s.avpreset", base[i], i != 1 ? "" : "/.avconv", preset_name); f = fopen(filename, "r"); if (!f && codec_name) { snprintf(filename, filename_size, - "%s%s/%s-%s.ffpreset", + "%s%s/%s-%s.avpreset", base[i], i != 1 ? "" : "/.avconv", codec_name, preset_name); f = fopen(filename, "r"); @@ -902,6 +1143,7 @@ int check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec) case 's': type = AVMEDIA_TYPE_SUBTITLE; break; case 'd': type = AVMEDIA_TYPE_DATA; break; case 't': type = AVMEDIA_TYPE_ATTACHMENT; break; + default: av_assert0(0); } if (type != st->codec->codec_type) return 0; @@ -941,18 +1183,19 @@ int check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec) return AVERROR(EINVAL); } -AVDictionary *filter_codec_opts(AVDictionary *opts, enum CodecID codec_id, - AVFormatContext *s, AVStream *st) +AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id, + AVFormatContext *s, AVStream *st, AVCodec *codec) { AVDictionary *ret = NULL; AVDictionaryEntry *t = NULL; - AVCodec *codec = s->oformat ? avcodec_find_encoder(codec_id) - : avcodec_find_decoder(codec_id); int flags = s->oformat ? AV_OPT_FLAG_ENCODING_PARAM : AV_OPT_FLAG_DECODING_PARAM; char prefix = 0; const AVClass *cc = avcodec_get_class(); + if (!codec) + codec = s->oformat ? avcodec_find_encoder(codec_id) + : avcodec_find_decoder(codec_id); if (!codec) return NULL; @@ -1014,91 +1257,154 @@ AVDictionary **setup_find_stream_info_opts(AVFormatContext *s, } for (i = 0; i < s->nb_streams; i++) opts[i] = filter_codec_opts(codec_opts, s->streams[i]->codec->codec_id, - s, s->streams[i]); + s, s->streams[i], NULL); return opts; } -#if CONFIG_AVFILTER +void *grow_array(void *array, int elem_size, int *size, int new_size) +{ + if (new_size >= INT_MAX / elem_size) { + av_log(NULL, AV_LOG_ERROR, "Array too big.\n"); + exit(1); + } + if (*size < new_size) { + uint8_t *tmp = av_realloc(array, new_size*elem_size); + if (!tmp) { + av_log(NULL, AV_LOG_ERROR, "Could not alloc buffer.\n"); + exit(1); + } + memset(tmp + *size*elem_size, 0, (new_size-*size) * elem_size); + *size = new_size; + return tmp; + } + return array; +} -static int sink_init(AVFilterContext *ctx, const char *args, void *opaque) +static int alloc_buffer(FrameBuffer **pool, AVCodecContext *s, FrameBuffer **pbuf) { - SinkContext *priv = ctx->priv; + FrameBuffer *buf = av_mallocz(sizeof(*buf)); + int i, ret; + const int pixel_size = av_pix_fmt_descriptors[s->pix_fmt].comp[0].step_minus1+1; + int h_chroma_shift, v_chroma_shift; + int edge = 32; // XXX should be avcodec_get_edge_width(), but that fails on svq1 + int w = s->width, h = s->height; + + if (!buf) + return AVERROR(ENOMEM); - if (!opaque) - return AVERROR(EINVAL); - *priv = *(SinkContext *)opaque; + if (!(s->flags & CODEC_FLAG_EMU_EDGE)) { + w += 2*edge; + h += 2*edge; + } + + avcodec_align_dimensions(s, &w, &h); + if ((ret = av_image_alloc(buf->base, buf->linesize, w, h, + s->pix_fmt, 32)) < 0) { + av_freep(&buf); + return ret; + } + /* XXX this shouldn't be needed, but some tests break without this line + * those decoders are buggy and need to be fixed. + * the following tests fail: + * cdgraphics, ansi, aasc, fraps-v1, qtrle-1bit + */ + memset(buf->base[0], 128, ret); + + avcodec_get_chroma_sub_sample(s->pix_fmt, &h_chroma_shift, &v_chroma_shift); + for (i = 0; i < FF_ARRAY_ELEMS(buf->data); i++) { + const int h_shift = i==0 ? 0 : h_chroma_shift; + const int v_shift = i==0 ? 0 : v_chroma_shift; + if (s->flags & CODEC_FLAG_EMU_EDGE) + buf->data[i] = buf->base[i]; + else if (buf->base[i]) + buf->data[i] = buf->base[i] + + FFALIGN((buf->linesize[i]*edge >> v_shift) + + (pixel_size*edge >> h_shift), 32); + } + buf->w = s->width; + buf->h = s->height; + buf->pix_fmt = s->pix_fmt; + buf->pool = pool; + *pbuf = buf; return 0; } -static void null_end_frame(AVFilterLink *inlink) { } - -static int sink_query_formats(AVFilterContext *ctx) +int codec_get_buffer(AVCodecContext *s, AVFrame *frame) { - SinkContext *priv = ctx->priv; - enum PixelFormat pix_fmts[] = { priv->pix_fmt, PIX_FMT_NONE }; + FrameBuffer **pool = s->opaque; + FrameBuffer *buf; + int ret, i; + + if (!*pool && (ret = alloc_buffer(pool, s, pool)) < 0) + return ret; + + buf = *pool; + *pool = buf->next; + buf->next = NULL; + if (buf->w != s->width || buf->h != s->height || buf->pix_fmt != s->pix_fmt) { + av_freep(&buf->base[0]); + av_free(buf); + if ((ret = alloc_buffer(pool, s, &buf)) < 0) + return ret; + } + buf->refcount++; + + frame->opaque = buf; + frame->type = FF_BUFFER_TYPE_USER; + frame->extended_data = frame->data; + frame->pkt_pts = s->pkt ? s->pkt->pts : AV_NOPTS_VALUE; + frame->width = buf->w; + frame->height = buf->h; + frame->format = buf->pix_fmt; + frame->sample_aspect_ratio = s->sample_aspect_ratio; + + for (i = 0; i < FF_ARRAY_ELEMS(buf->data); i++) { + frame->base[i] = buf->base[i]; // XXX h264.c uses base though it shouldn't + frame->data[i] = buf->data[i]; + frame->linesize[i] = buf->linesize[i]; + } - avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts)); return 0; } -AVFilter sink = { - .name = "sink", - .priv_size = sizeof(SinkContext), - .init = sink_init, - - .query_formats = sink_query_formats, +static void unref_buffer(FrameBuffer *buf) +{ + FrameBuffer **pool = buf->pool; - .inputs = (AVFilterPad[]) {{ .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .end_frame = null_end_frame, - .min_perms = AV_PERM_READ, }, - { .name = NULL }}, - .outputs = (AVFilterPad[]) {{ .name = NULL }}, -}; + av_assert0(buf->refcount); + buf->refcount--; + if (!buf->refcount) { + buf->next = *pool; + *pool = buf; + } +} -int get_filtered_video_frame(AVFilterContext *ctx, AVFrame *frame, - AVFilterBufferRef **picref_ptr, AVRational *tb) +void codec_release_buffer(AVCodecContext *s, AVFrame *frame) { - int ret; - AVFilterBufferRef *picref; + FrameBuffer *buf = frame->opaque; + int i; - if ((ret = avfilter_request_frame(ctx->inputs[0])) < 0) - return ret; - if (!(picref = ctx->inputs[0]->cur_buf)) - return AVERROR(ENOENT); - *picref_ptr = picref; - ctx->inputs[0]->cur_buf = NULL; - *tb = ctx->inputs[0]->time_base; - - memcpy(frame->data, picref->data, sizeof(frame->data)); - memcpy(frame->linesize, picref->linesize, sizeof(frame->linesize)); - frame->interlaced_frame = picref->video->interlaced; - frame->top_field_first = picref->video->top_field_first; - frame->key_frame = picref->video->key_frame; - frame->pict_type = picref->video->pict_type; - frame->sample_aspect_ratio = picref->video->pixel_aspect; - - return 1; + for (i = 0; i < FF_ARRAY_ELEMS(frame->data); i++) + frame->data[i] = NULL; + + unref_buffer(buf); } -#endif /* CONFIG_AVFILTER */ +void filter_release_buffer(AVFilterBuffer *fb) +{ + FrameBuffer *buf = fb->priv; + av_free(fb); + unref_buffer(buf); +} -void *grow_array(void *array, int elem_size, int *size, int new_size) +void free_buffer_pool(FrameBuffer **pool) { - if (new_size >= INT_MAX / elem_size) { - av_log(NULL, AV_LOG_ERROR, "Array too big.\n"); - exit_program(1); + FrameBuffer *buf = *pool; + while (buf) { + *pool = buf->next; + av_freep(&buf->base[0]); + av_free(buf); + buf = *pool; } - if (*size < new_size) { - uint8_t *tmp = av_realloc(array, new_size*elem_size); - if (!tmp) { - av_log(NULL, AV_LOG_ERROR, "Could not alloc buffer.\n"); - exit_program(1); - } - memset(tmp + *size*elem_size, 0, (new_size-*size) * elem_size); - *size = new_size; - return tmp; - } - return array; }