From patchwork Wed Dec 13 10:59:23 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 6745 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.2.161.94 with SMTP id m30csp5265238jah; Wed, 13 Dec 2017 03:02:05 -0800 (PST) X-Google-Smtp-Source: ACJfBot71pR3QdkQXMWDkE6EYV+f60evLxRwJwEvBK8ZQZJOPRblJlMg6xFF7Obvd2GPy0BwfW2K X-Received: by 10.223.171.29 with SMTP id q29mr1922280wrc.141.1513162925825; Wed, 13 Dec 2017 03:02:05 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1513162925; cv=none; d=google.com; s=arc-20160816; b=u1o2Tk/64iHl0qlKUzz/3Uzibo5Hhl2c+mqKeodTiqBvRIoQiUrQA9AlHj2lYkWrQ2 gz/gBnk3pT/tzFUBSBd8kII2eHhdviNhYptSz/IuEx9zKfUK9K8vke/tL7R8Uh55H7ca +DijzKIsH57jfnLRon5lKQVoGG2Fw5YgfjD204laaid94/46LdhQZ834+8tRI7cpIe22 QfNBg9qRmXSp+l8n+Y0tGWK/+epUvBepX96EgcNxG4d7DjGsHxhhQhDCH5z+h/RQ6sX8 NB5/kBHe4DpOSq5Oz6TPpRdR71z3MSfmEUpGnprgbH+P68ZJk71knp2j2nYXWn7PvXIJ 6ZQQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:in-reply-to:message-id:date :to:from:dkim-signature:delivered-to:arc-authentication-results; bh=IEbKHy4WJGXE7IRoPRf08wQ3heaGgqR9Vxne/5LWh4I=; b=u6vOK/xyV8Ox0mYtbbepcTnFJFID3r6zmi/6eEFlJygVz6R3Hc9oKhZUm2YtTQ/BCW dufYOQf0tkitFUiphh66yuBKBJtJvmveD27Sfi34TBPz+Lp/FvRh5bJGBblo9e9eb7Fw RoH+swvM/AZnu113z5YewmN37fy2M/js6Ue1pYD4LZ0ewYp05EasxN8pk0ENBkmjY8Lh TYiuc3yXfFA4l+zG5mTyscSBy7R4zBBNZLvzn4MlvfN6bHe420sHF9yDMWnbMER7LaVO G2UuHLKsz1BaJDq3A3kPtCQhb1m2npmDybuQ2vmyqmzNZODZW+WKwj2lJIwyKah3zbV7 LO8Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20161025 header.b=rD7qoe6j; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=gmail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id 18si1127386wrz.329.2017.12.13.03.02.05; Wed, 13 Dec 2017 03:02:05 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20161025 header.b=rD7qoe6j; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 1A72268A417; Wed, 13 Dec 2017 13:00:33 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wm0-f68.google.com (mail-wm0-f68.google.com [74.125.82.68]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 5B28268A344 for ; Wed, 13 Dec 2017 13:00:24 +0200 (EET) Received: by mail-wm0-f68.google.com with SMTP id t8so4134864wmc.3 for ; Wed, 13 Dec 2017 03:00:32 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references; bh=ApHiRLkzKktpvHCvWg1ZumNSUWgtlHGf6ubaFFKVsJs=; b=rD7qoe6jH4TDlnSG0V9/43F74O4oHH/dCn6/HDJlXCmJojb32Lq8hBHZu1vDlxdcSX OvuO3nXRxV3ma1vTA1+V8Tig4e4fTurOe2QtckFHmQRWHYC46glUx5slb+DThld6L1PS Q+i/ovjOxg4F+0/n54vGwG6CotBEvq1sbbkbBZNU2EqLWosmdCgNaikybnjqMsxFl+LD xYGIVj5t9DX/5wJC3VgH8J13Btx1cARf1VB8dPOWdUirNjsc+EVAt3kmXjCMuop+rdVa rPErxbzGM+vrTQUBG7CXV6usnvh15g/0DD2gJmo+oULAnsOXZIRpf8d6bbBhMT1TgC1/ lrDA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=ApHiRLkzKktpvHCvWg1ZumNSUWgtlHGf6ubaFFKVsJs=; b=sKw1m1fxVRTvGiQAjh+ei0BQ1xKxceAJP5VVRfPGGRmNtsT4FQ94jmFkYhl9TLk3UE 0DSzQLcx12e8dlwyVEiwL3qXs0KJkuOe6jwFrFRy6anv2GlXXpOBW20UKCAU+5FMjdVP haZvPg4ATSuv0jWjGW/btIQyCsCWy243hK11hhB8BYEdzB12U8eAjtzLtgchyy7ZfMsc w1GJekPSSQn/GsvnlcjBjf7nU3/+omGGkpMi5yJ64o5ZhL+FQm7yqkm5h2uhq6Mh2Pl3 xGYxeK7QUKrlBMvaOD1G1pebKAk5vhBwqGBKqefuFDYRoplAiUn3Y0Osjlg97RpSNP1I D97Q== X-Gm-Message-State: AKGB3mKYvsRbo+eHQTg731Nwl6B7KBHm2AEM7BhVzZVbSsb+5wHINWA8 QC8PdIMObG9Z5plqEUvdRlZgpA== X-Received: by 10.28.110.26 with SMTP id j26mr1580789wmc.46.1513162831591; Wed, 13 Dec 2017 03:00:31 -0800 (PST) Received: from localhost.localdomain ([94.250.174.60]) by smtp.gmail.com with ESMTPSA id w21sm675961wra.30.2017.12.13.03.00.29 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 13 Dec 2017 03:00:30 -0800 (PST) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Wed, 13 Dec 2017 11:59:23 +0100 Message-Id: <20171213105940.32103-7-onemda@gmail.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20171213105940.32103-1-onemda@gmail.com> References: <20171213105940.32103-1-onemda@gmail.com> Subject: [FFmpeg-devel] [PATCH 07/24] avfilter: negotiate color_range between filters X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Signed-off-by: Paul B Mahol --- fftools/ffmpeg.c | 2 ++ fftools/ffmpeg_filter.c | 56 ++++++++++++++++++++++++++++++--- libavcodec/utils.c | 11 +++++++ libavfilter/avfilter.c | 9 ++++-- libavfilter/avfilter.h | 4 ++- libavfilter/avfiltergraph.c | 53 ++++++++++++++++++++++++++++++++ libavfilter/buffersink.c | 16 ++++++++++ libavfilter/buffersink.h | 1 + libavfilter/buffersrc.c | 4 +++ libavfilter/formats.c | 75 ++++++++++++++++++++++++++++++++++++++++++--- libavfilter/formats.h | 31 +++++++++++++++++++ libavfilter/internal.h | 11 +++++++ libavfilter/vf_format.c | 46 ++++++++++++++++++++++++++- libavfilter/vf_noise.c | 6 +++- libavfilter/vf_scale.c | 17 ++++++++-- libavfilter/vsrc_testsrc.c | 15 +++++++-- tests/fate/filter-video.mak | 2 +- tests/fate/pixlet.mak | 2 +- 18 files changed, 339 insertions(+), 22 deletions(-) diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c index 6aff3366c5..64de55d9a5 100644 --- a/fftools/ffmpeg.c +++ b/fftools/ffmpeg.c @@ -3390,6 +3390,8 @@ static int init_output_stream_encode(OutputStream *ost) enc_ctx->bits_per_raw_sample = FFMIN(dec_ctx->bits_per_raw_sample, av_pix_fmt_desc_get(enc_ctx->pix_fmt)->comp[0].depth); + enc_ctx->color_range = av_buffersink_get_color_range(ost->filter->filter); + enc_ctx->framerate = ost->frame_rate; ost->st->avg_frame_rate = ost->frame_rate; diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c index 877fd670e6..49b9ae232e 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -89,6 +89,28 @@ enum AVPixelFormat choose_pixel_fmt(AVStream *st, AVCodecContext *enc_ctx, AVCod return target; } +static enum AVColorRange choose_color_range(AVStream *st, AVCodecContext *enc_ctx, AVCodec *codec, enum AVColorRange target) +{ + if (codec && codec->color_ranges) { + const enum AVColorRange *p = codec->color_ranges; + + for (; *p != AVCOL_RANGE_UNSPECIFIED; p++) { + if (*p == target) + break; + } + if (*p == AVCOL_RANGE_UNSPECIFIED) { + if (target != AVCOL_RANGE_UNSPECIFIED) + av_log(NULL, AV_LOG_WARNING, + "Incompatible color range '%s' for codec '%s', auto-selecting color range '%s'\n", + av_color_range_name(target), + codec->name, + av_color_range_name(codec->color_ranges[0])); + return codec->color_ranges[0]; + } + } + return target; +} + void choose_sample_fmt(AVStream *st, AVCodec *codec) { if (codec && codec->sample_fmts) { @@ -127,7 +149,19 @@ static char *choose_pix_fmts(OutputFilter *ofilter) 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))); + AVIOContext *s = NULL; + uint8_t *ret; + int len; + + if (avio_open_dyn_buf(&s) < 0) + exit_program(1); + + avio_printf(s, "%s:%s", av_get_pix_fmt_name(choose_pixel_fmt(ost->st, ost->enc_ctx, ost->enc, ost->enc_ctx->pix_fmt)), + av_color_range_name(choose_color_range(ost->st, ost->enc_ctx, ost->enc, ost->enc_ctx->color_range))); + + len = avio_close_dyn_buf(s, &ret); + ret[len] = 0; + return ret; } else if (ost->enc && ost->enc->pix_fmts) { const enum AVPixelFormat *p; AVIOContext *s = NULL; @@ -144,7 +178,20 @@ static char *choose_pix_fmts(OutputFilter *ofilter) for (; *p != AV_PIX_FMT_NONE; p++) { const char *name = av_get_pix_fmt_name(*p); - avio_printf(s, "%s|", name); + avio_printf(s, "%s", name); + if (*(p + 1) != AV_PIX_FMT_NONE) + avio_printf(s, "|"); + else + avio_printf(s, ":"); + } + + if (ost->enc->color_ranges) { + const enum AVColorRange *c = ost->enc->color_ranges; + + for (; *c != AVCOL_RANGE_UNSPECIFIED; c++) { + const char *name = av_color_range_name(*c); + avio_printf(s, "%s|", name); + } } len = avio_close_dyn_buf(s, &ret); ret[len - 1] = 0; @@ -777,10 +824,11 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, av_bprint_init(&args, 0, 1); av_bprintf(&args, "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:" - "pixel_aspect=%d/%d:sws_param=flags=%d", + "pixel_aspect=%d/%d:sws_param=flags=%d:color_range=%s", ifilter->width, ifilter->height, ifilter->format, 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), + av_color_range_name(ist->dec_ctx->color_range)); if (fr.num && fr.den) av_bprintf(&args, ":frame_rate=%d/%d", fr.num, fr.den); snprintf(name, sizeof(name), "graph %d input from stream %d:%d", fg->index, diff --git a/libavcodec/utils.c b/libavcodec/utils.c index 873f39f9bd..3eca490a33 100644 --- a/libavcodec/utils.c +++ b/libavcodec/utils.c @@ -879,6 +879,17 @@ FF_ENABLE_DEPRECATION_WARNINGS avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ444P) avctx->color_range = AVCOL_RANGE_JPEG; } + if (avctx->codec->color_ranges) { + for (i = 0; avctx->codec->color_ranges[i] != AVCOL_RANGE_UNSPECIFIED; i++) + if (avctx->color_range == avctx->codec->color_ranges[i]) + break; + if (avctx->codec->color_ranges[i] == AVCOL_RANGE_UNSPECIFIED) { + av_log(avctx, AV_LOG_ERROR, "Specified color range %s is invalid or not supported\n", + (char *)av_x_if_null(av_color_range_name(avctx->color_range), "unknown")); + ret = AVERROR(EINVAL); + goto free_and_end; + } + } if (avctx->codec->supported_samplerates) { for (i = 0; avctx->codec->supported_samplerates[i] != 0; i++) if (avctx->sample_rate == avctx->codec->supported_samplerates[i]) diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c index 4a579bb49d..c3a19d100c 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -262,6 +262,9 @@ int avfilter_insert_filter(AVFilterLink *link, AVFilterContext *filt, if (link->out_formats) ff_formats_changeref(&link->out_formats, &filt->outputs[filt_dstpad_idx]->out_formats); + if (link->out_color_ranges) + ff_formats_changeref(&link->out_color_ranges, + &filt->outputs[filt_dstpad_idx]->out_color_ranges); if (link->out_samplerates) ff_formats_changeref(&link->out_samplerates, &filt->outputs[filt_dstpad_idx]->out_samplerates); @@ -785,6 +788,8 @@ static void free_link(AVFilterLink *link) ff_formats_unref(&link->in_formats); ff_formats_unref(&link->out_formats); + ff_formats_unref(&link->in_color_ranges); + ff_formats_unref(&link->out_color_ranges); ff_formats_unref(&link->in_samplerates); ff_formats_unref(&link->out_samplerates); ff_channel_layouts_unref(&link->in_channel_layouts); @@ -970,9 +975,7 @@ int avfilter_init_str(AVFilterContext *filter, const char *args) } #if FF_API_OLD_FILTER_OPTS_ERROR - if ( !strcmp(filter->filter->name, "format") || - !strcmp(filter->filter->name, "noformat") || - !strcmp(filter->filter->name, "frei0r") || + if ( !strcmp(filter->filter->name, "frei0r") || !strcmp(filter->filter->name, "frei0r_src") || !strcmp(filter->filter->name, "ocv") || !strcmp(filter->filter->name, "pan") || diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index 40ad28ffd8..138bdeb5c9 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -456,7 +456,7 @@ struct AVFilterLink { ***************************************************************** */ /** - * Lists of formats and channel layouts supported by the input and output + * Lists of formats, color_ranges and channel layouts supported by the input and output * filters respectively. These lists are used for negotiating the format * to actually be used, which will be loaded into the format and * channel_layout members, above, when chosen. @@ -464,6 +464,8 @@ struct AVFilterLink { */ AVFilterFormats *in_formats; AVFilterFormats *out_formats; + AVFilterFormats *in_color_ranges; + AVFilterFormats *out_color_ranges; /** * Lists of channel layouts and sample rates used for automatic diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c index 4cc6892404..0d023a5881 100644 --- a/libavfilter/avfiltergraph.c +++ b/libavfilter/avfiltergraph.c @@ -317,6 +317,7 @@ static int filter_query_formats(AVFilterContext *ctx) { int ret, i; AVFilterFormats *formats; + AVFilterFormats *color_ranges; AVFilterChannelLayouts *chlayouts; AVFilterFormats *samplerates; enum AVMediaType type = ctx->inputs && ctx->inputs [0] ? ctx->inputs [0]->type : @@ -338,6 +339,11 @@ static int filter_query_formats(AVFilterContext *ctx) formats = ff_all_formats(type); if ((ret = ff_set_common_formats(ctx, formats)) < 0) return ret; + if (type == AVMEDIA_TYPE_VIDEO) { + color_ranges = ff_all_color_ranges(); + if ((ret = ff_set_common_color_ranges(ctx, color_ranges)) < 0) + return ret; + } if (type == AVMEDIA_TYPE_AUDIO) { samplerates = ff_all_samplerates(); if ((ret = ff_set_common_samplerates(ctx, samplerates)) < 0) @@ -360,6 +366,9 @@ static int formats_declared(AVFilterContext *f) !(f->inputs[i]->out_samplerates && f->inputs[i]->out_channel_layouts)) return 0; + if (f->inputs[i]->type == AVMEDIA_TYPE_VIDEO && + !(f->inputs[i]->out_color_ranges)) + return 0; } for (i = 0; i < f->nb_outputs; i++) { if (!f->outputs[i]->in_formats) @@ -368,6 +377,9 @@ static int formats_declared(AVFilterContext *f) !(f->outputs[i]->in_samplerates && f->outputs[i]->in_channel_layouts)) return 0; + if (f->outputs[i]->type == AVMEDIA_TYPE_VIDEO && + !(f->outputs[i]->in_color_ranges)) + return 0; } return 1; } @@ -485,6 +497,15 @@ static int query_formats(AVFilterGraph *graph, AVClass *log_ctx) convert_needed = 1; } + if (link->type == AVMEDIA_TYPE_VIDEO) { + if (link->in_color_ranges != link->out_color_ranges + && link->in_color_ranges && link->out_color_ranges) + if (!can_merge_formats(link->in_color_ranges, + link->out_color_ranges, + 0, 1)) + convert_needed = 1; + } + #define MERGE_DISPATCH(field, statement) \ if (!(link->in_ ## field && link->out_ ## field)) { \ count_delayed++; \ @@ -507,6 +528,13 @@ static int query_formats(AVFilterGraph *graph, AVClass *log_ctx) convert_needed = 1; ) } + if (link->type == AVMEDIA_TYPE_VIDEO) { + MERGE_DISPATCH(color_ranges, + if (!ff_merge_samplerates(link->in_color_ranges, + link->out_color_ranges)) + convert_needed = 1; + ) + } MERGE_DISPATCH(formats, if (!ff_merge_formats(link->in_formats, link->out_formats, link->type)) @@ -575,6 +603,12 @@ static int query_formats(AVFilterGraph *graph, AVClass *log_ctx) av_assert0( inlink->out_formats->refcount > 0); av_assert0(outlink-> in_formats->refcount > 0); av_assert0(outlink->out_formats->refcount > 0); + if (outlink->type == AVMEDIA_TYPE_VIDEO) { + av_assert0( inlink-> in_color_ranges->refcount > 0); + av_assert0( inlink->out_color_ranges->refcount > 0); + av_assert0(outlink-> in_color_ranges->refcount > 0); + av_assert0(outlink->out_color_ranges->refcount > 0); + } if (outlink->type == AVMEDIA_TYPE_AUDIO) { av_assert0( inlink-> in_samplerates->refcount > 0); av_assert0( inlink->out_samplerates->refcount > 0); @@ -588,6 +622,12 @@ static int query_formats(AVFilterGraph *graph, AVClass *log_ctx) if (!ff_merge_formats( inlink->in_formats, inlink->out_formats, inlink->type) || !ff_merge_formats(outlink->in_formats, outlink->out_formats, outlink->type)) ret = AVERROR(ENOSYS); + if (inlink->type == AVMEDIA_TYPE_VIDEO && + (!ff_merge_samplerates(inlink->in_color_ranges, inlink->out_color_ranges))) + ret = AVERROR(ENOSYS); + if (outlink->type == AVMEDIA_TYPE_VIDEO && + (!ff_merge_samplerates(outlink->in_color_ranges, outlink->out_color_ranges))) + ret = AVERROR(ENOSYS); if (inlink->type == AVMEDIA_TYPE_AUDIO && (!ff_merge_samplerates(inlink->in_samplerates, inlink->out_samplerates) || @@ -709,6 +749,17 @@ static int pick_format(AVFilterLink *link, AVFilterLink *ref) link->in_formats->nb_formats = 1; link->format = link->in_formats->formats[0]; + if (link->type == AVMEDIA_TYPE_VIDEO) { + if (!link->in_color_ranges->nb_formats) { + av_log(link->src, AV_LOG_ERROR, "Cannot select color range for" + " the link between filters %s and %s.\n", link->src->name, + link->dst->name); + return AVERROR(EINVAL); + } + link->in_color_ranges->nb_formats = 1; + link->color_range = link->in_color_ranges->formats[0]; + } + if (link->type == AVMEDIA_TYPE_AUDIO) { if (!link->in_samplerates->nb_formats) { av_log(link->src, AV_LOG_ERROR, "Cannot select sample rate for" @@ -739,6 +790,8 @@ static int pick_format(AVFilterLink *link, AVFilterLink *ref) ff_formats_unref(&link->in_formats); ff_formats_unref(&link->out_formats); + ff_formats_unref(&link->in_color_ranges); + ff_formats_unref(&link->out_color_ranges); ff_formats_unref(&link->in_samplerates); ff_formats_unref(&link->out_samplerates); ff_channel_layouts_unref(&link->in_channel_layouts); diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c index 897396cac4..f3abb7de6e 100644 --- a/libavfilter/buffersink.c +++ b/libavfilter/buffersink.c @@ -45,6 +45,8 @@ typedef struct BufferSinkContext { /* only used for video */ enum AVPixelFormat *pixel_fmts; ///< list of accepted pixel formats, must be terminated with -1 int pixel_fmts_size; + enum AVColorRange *color_ranges; ///< list of accepted color ranges, must be terminated with 0 + int color_ranges_size; /* only used for audio */ enum AVSampleFormat *sample_fmts; ///< list of accepted sample formats, terminated by AV_SAMPLE_FMT_NONE @@ -130,11 +132,13 @@ int attribute_align_arg av_buffersink_get_samples(AVFilterContext *ctx, AVBufferSinkParams *av_buffersink_params_alloc(void) { static const int pixel_fmts[] = { AV_PIX_FMT_NONE }; + static const enum AVColorRange color_ranges[] = { AVCOL_RANGE_UNSPECIFIED }; AVBufferSinkParams *params = av_malloc(sizeof(AVBufferSinkParams)); if (!params) return NULL; params->pixel_fmts = pixel_fmts; + params->color_ranges = color_ranges; return params; } @@ -211,6 +215,8 @@ static av_cold int vsink_init(AVFilterContext *ctx, void *opaque) if (params) { if ((ret = av_opt_set_int_list(buf, "pix_fmts", params->pixel_fmts, AV_PIX_FMT_NONE, 0)) < 0) return ret; + if ((ret = av_opt_set_int_list(buf, "color_ranges", params->color_ranges, AVCOL_RANGE_UNSPECIFIED, 0)) < 0) + return ret; } return common_init(ctx); @@ -227,16 +233,25 @@ static int vsink_query_formats(AVFilterContext *ctx) { BufferSinkContext *buf = ctx->priv; AVFilterFormats *formats = NULL; + AVFilterFormats *color_ranges = NULL; unsigned i; int ret; CHECK_LIST_SIZE(pixel_fmts) + CHECK_LIST_SIZE(color_ranges) if (buf->pixel_fmts_size) { for (i = 0; i < NB_ITEMS(buf->pixel_fmts); i++) if ((ret = ff_add_format(&formats, buf->pixel_fmts[i])) < 0) return ret; if ((ret = ff_set_common_formats(ctx, formats)) < 0) return ret; + } + if (buf->color_ranges_size) { + for (i = 0; i < NB_ITEMS(buf->color_ranges); i++) + if ((ret = ff_add_format(&color_ranges, buf->color_ranges[i])) < 0) + return ret; + if ((ret = ff_set_common_color_ranges(ctx, color_ranges)) < 0) + return ret; } else { if ((ret = ff_default_query_formats(ctx)) < 0) return ret; @@ -318,6 +333,7 @@ static int asink_query_formats(AVFilterContext *ctx) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption buffersink_options[] = { { "pix_fmts", "set the supported pixel formats", OFFSET(pixel_fmts), AV_OPT_TYPE_BINARY, .flags = FLAGS }, + { "color_ranges", "set the supported color ranges", OFFSET(color_ranges), AV_OPT_TYPE_BINARY, .flags = FLAGS }, { NULL }, }; #undef FLAGS diff --git a/libavfilter/buffersink.h b/libavfilter/buffersink.h index e6d6504832..300a7bfb3a 100644 --- a/libavfilter/buffersink.h +++ b/libavfilter/buffersink.h @@ -64,6 +64,7 @@ int av_buffersink_get_frame_flags(AVFilterContext *ctx, AVFrame *frame, int flag */ typedef struct AVBufferSinkParams { const enum AVPixelFormat *pixel_fmts; ///< list of allowed pixel formats, terminated by AV_PIX_FMT_NONE + const enum AVColorRange *color_ranges; ///< list of allowed color ranges, terminated by AVCOL_RANGE_UNSPECIFIED } AVBufferSinkParams; /** diff --git a/libavfilter/buffersrc.c b/libavfilter/buffersrc.c index 51a1a9fb49..e450a2bca7 100644 --- a/libavfilter/buffersrc.c +++ b/libavfilter/buffersrc.c @@ -412,6 +412,7 @@ static int query_formats(AVFilterContext *ctx) BufferSourceContext *c = ctx->priv; AVFilterChannelLayouts *channel_layouts = NULL; AVFilterFormats *formats = NULL; + AVFilterFormats *color_ranges = NULL; AVFilterFormats *samplerates = NULL; int ret; @@ -420,6 +421,9 @@ static int query_formats(AVFilterContext *ctx) if ((ret = ff_add_format (&formats, c->pix_fmt)) < 0 || (ret = ff_set_common_formats (ctx , formats )) < 0) return ret; + if ((ret = ff_add_format (&color_ranges, c->color_range)) < 0 || + (ret = ff_set_common_color_ranges(ctx, color_ranges)) < 0) + return ret; break; case AVMEDIA_TYPE_AUDIO: if ((ret = ff_add_format (&formats , c->sample_fmt )) < 0 || diff --git a/libavfilter/formats.c b/libavfilter/formats.c index d4de862237..9fb226001d 100644 --- a/libavfilter/formats.c +++ b/libavfilter/formats.c @@ -262,11 +262,11 @@ int ff_fmt_is_in(int fmt, const int *fmts) return 0; } -#define MAKE_FORMAT_LIST(type, field, count_field) \ +#define MAKE_FORMAT_LIST(type, field, count_field, end) \ type *formats; \ int count = 0; \ if (fmts) \ - for (count = 0; fmts[count] != -1; count++) \ + for (count = 0; fmts[count] != end; count++) \ ; \ formats = av_mallocz(sizeof(*formats)); \ if (!formats) \ @@ -282,7 +282,16 @@ int ff_fmt_is_in(int fmt, const int *fmts) AVFilterFormats *ff_make_format_list(const int *fmts) { - MAKE_FORMAT_LIST(AVFilterFormats, formats, nb_formats); + MAKE_FORMAT_LIST(AVFilterFormats, formats, nb_formats, -1); + while (count--) + formats->formats[count] = fmts[count]; + + return formats; +} + +AVFilterFormats *ff_make_color_range_list(const int *fmts) +{ + MAKE_FORMAT_LIST(AVFilterFormats, formats, nb_formats, AVCOL_RANGE_UNSPECIFIED); while (count--) formats->formats[count] = fmts[count]; @@ -292,7 +301,7 @@ AVFilterFormats *ff_make_format_list(const int *fmts) AVFilterChannelLayouts *ff_make_formatu64_list(const uint64_t *fmts) { MAKE_FORMAT_LIST(AVFilterChannelLayouts, - channel_layouts, nb_channel_layouts); + channel_layouts, nb_channel_layouts, -1); if (count) memcpy(formats->channel_layouts, fmts, sizeof(*formats->channel_layouts) * count); @@ -303,7 +312,7 @@ AVFilterChannelLayouts *ff_make_formatu64_list(const uint64_t *fmts) AVFilterChannelLayouts *avfilter_make_format64_list(const int64_t *fmts) { MAKE_FORMAT_LIST(AVFilterChannelLayouts, - channel_layouts, nb_channel_layouts); + channel_layouts, nb_channel_layouts, -1); if (count) memcpy(formats->channel_layouts, fmts, sizeof(*formats->channel_layouts) * count); @@ -398,6 +407,39 @@ AVFilterFormats *ff_all_samplerates(void) return ret; } +AVFilterFormats *ff_jpeg_color_ranges(void) +{ + AVFilterFormats *ret = NULL; + + if (ff_add_format(&ret, AVCOL_RANGE_JPEG) < 0) + return NULL; + + return ret; +} + +AVFilterFormats *ff_mpeg_color_ranges(void) +{ + AVFilterFormats *ret = NULL; + + if (ff_add_format(&ret, AVCOL_RANGE_MPEG) < 0) + return NULL; + + return ret; +} + +AVFilterFormats *ff_all_color_ranges(void) +{ + AVFilterFormats *fmts = NULL; + + if (ff_add_format(&fmts, AVCOL_RANGE_MPEG) < 0) + return NULL; + if (ff_add_format(&fmts, AVCOL_RANGE_JPEG) < 0) + return NULL; + if (ff_add_format(&fmts, AVCOL_RANGE_UNSPECIFIED) < 0) + return NULL; + return fmts; +} + AVFilterChannelLayouts *ff_all_channel_layouts(void) { AVFilterChannelLayouts *ret = av_mallocz(sizeof(*ret)); @@ -560,6 +602,13 @@ int ff_set_common_samplerates(AVFilterContext *ctx, ff_formats_ref, ff_formats_unref, formats); } +int ff_set_common_color_ranges(AVFilterContext *ctx, + AVFilterFormats *color_ranges) +{ + SET_COMMON_FORMATS(ctx, color_ranges, in_color_ranges, out_color_ranges, + ff_formats_ref, ff_formats_unref, formats); +} + /** * A helper for query_formats() which sets all links to the same list of * formats. If there are no links hooked to this filter, the list of formats is @@ -590,6 +639,11 @@ static int default_query_formats_common(AVFilterContext *ctx, if (ret < 0) return ret; } + if (type == AVMEDIA_TYPE_VIDEO) { + ret = ff_set_common_color_ranges(ctx, ff_all_color_ranges()); + if (ret < 0) + return ret; + } return 0; } @@ -659,6 +713,17 @@ int ff_parse_sample_rate(int *ret, const char *arg, void *log_ctx) return 0; } +int ff_parse_color_range(enum AVColorRange *ret, const char *arg, void *log_ctx) +{ + int color_range = av_color_range_from_name(arg); + if (color_range < 0) { + av_log(log_ctx, AV_LOG_ERROR, "Invalid color range '%s'\n", arg); + return AVERROR(EINVAL); + } + *ret = color_range; + return 0; +} + int ff_parse_channel_layout(int64_t *ret, int *nret, const char *arg, void *log_ctx) { diff --git a/libavfilter/formats.h b/libavfilter/formats.h index 870809b5a0..6706f76dce 100644 --- a/libavfilter/formats.h +++ b/libavfilter/formats.h @@ -166,6 +166,9 @@ av_warn_unused_result int ff_set_common_formats(AVFilterContext *ctx, AVFilterFormats *formats); av_warn_unused_result +int ff_set_common_color_ranges(AVFilterContext *ctx, AVFilterFormats *color_ranges); + +av_warn_unused_result int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout); /** @@ -205,6 +208,16 @@ av_warn_unused_result AVFilterFormats *ff_make_format_list(const int *fmts); /** + * Create a list of supported color ranges. This is intended for use in + * AVFilter->query_formats(). + * + * @param fmts list of color ranges, terminated by -1 + * @return the format list, with no existing references + */ +av_warn_unused_result +AVFilterFormats *ff_make_color_range_list(const int *color_ranges); + +/** * Add fmt to the list of media formats contained in *avff. * If *avff is NULL the function allocates the filter formats struct * and puts its pointer in *avff. @@ -222,6 +235,24 @@ av_warn_unused_result AVFilterFormats *ff_all_formats(enum AVMediaType type); /** + * Return a list of all color ranges supported by FFmpeg. + */ +av_warn_unused_result +AVFilterFormats *ff_all_color_ranges(void); + +/** + * Return a list of all mpeg color ranges supported by FFmpeg. + */ +av_warn_unused_result +AVFilterFormats *ff_mpeg_color_ranges(void); + +/** + * Return a list of all jpeg color ranges supported by FFmpeg. + */ +av_warn_unused_result +AVFilterFormats *ff_jpeg_color_ranges(void); + +/** * Construct a formats list containing all planar sample formats. */ av_warn_unused_result diff --git a/libavfilter/internal.h b/libavfilter/internal.h index f9679ed1d7..15fd3ff99b 100644 --- a/libavfilter/internal.h +++ b/libavfilter/internal.h @@ -191,6 +191,17 @@ av_warn_unused_result int ff_parse_sample_rate(int *ret, const char *arg, void *log_ctx); /** + * Parse a color range. + * + * @param ret unsigned enum pointer to where the value should be written + * @param arg string to parse + * @param log_ctx log context + * @return >= 0 in case of success, a negative AVERROR code on error + */ +av_warn_unused_result +int ff_parse_color_range(enum AVColorRange *ret, const char *arg, void *log_ctx); + +/** * Parse a time base. * * @param ret unsigned AVRational pointer to where the value should be written diff --git a/libavfilter/vf_format.c b/libavfilter/vf_format.c index a57c99d797..420a01ec8a 100644 --- a/libavfilter/vf_format.c +++ b/libavfilter/vf_format.c @@ -38,18 +38,21 @@ typedef struct FormatContext { const AVClass *class; char *pix_fmts; + char *color_ranges_str; /** * pix_fmts parsed into AVPixelFormats and terminated with * AV_PIX_FMT_NONE */ enum AVPixelFormat *formats; + enum AVColorRange *color_ranges; } FormatContext; static av_cold void uninit(AVFilterContext *ctx) { FormatContext *s = ctx->priv; av_freep(&s->formats); + av_freep(&s->color_ranges); } static av_cold int init(AVFilterContext *ctx) @@ -57,6 +60,7 @@ static av_cold int init(AVFilterContext *ctx) FormatContext *s = ctx->priv; char *cur, *sep; int nb_formats = 1; + int nb_color_ranges = 1; int i; int ret; @@ -91,6 +95,37 @@ static av_cold int init(AVFilterContext *ctx) } s->formats[nb_formats] = AV_PIX_FMT_NONE; + if (!s->color_ranges_str) { + av_log(ctx, AV_LOG_ERROR, "Empty output color range string.\n"); + return AVERROR(EINVAL); + } + + /* count the color ranges */ + cur = s->color_ranges_str; + while ((cur = strchr(cur, '|'))) { + nb_color_ranges++; + if (*cur) + cur++; + } + + s->color_ranges = av_malloc_array(nb_color_ranges + 1, sizeof(*s->color_ranges)); + if (!s->color_ranges) + return AVERROR(ENOMEM); + + /* parse the list of formats */ + cur = s->color_ranges_str; + for (i = 0; i < nb_color_ranges; i++) { + sep = strchr(cur, '|'); + if (sep) + *sep++ = 0; + + if ((ret = ff_parse_color_range(&s->color_ranges[i], cur, ctx)) < 0) + return ret; + + cur = sep; + } + s->color_ranges[nb_color_ranges] = -1; + if (!strcmp(ctx->filter->name, "noformat")) { const AVPixFmtDescriptor *desc = NULL; enum AVPixelFormat *formats_allowed; @@ -130,17 +165,26 @@ static int query_formats(AVFilterContext *ctx) { FormatContext *s = ctx->priv; AVFilterFormats *formats = ff_make_format_list(s->formats); + AVFilterFormats *color_ranges = ff_make_color_range_list(s->color_ranges); + int ret; if (!formats) return AVERROR(ENOMEM); - return ff_set_common_formats(ctx, formats); + ret = ff_set_common_formats(ctx, formats); + if (ret < 0) + return ret; + + if (!color_ranges) + return AVERROR(ENOMEM); + return ff_set_common_color_ranges(ctx, color_ranges); } #define OFFSET(x) offsetof(FormatContext, x) static const AVOption options[] = { { "pix_fmts", "A '|'-separated list of pixel formats", OFFSET(pix_fmts), AV_OPT_TYPE_STRING, .flags = AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM }, + { "color_ranges", "A '|'-separated list of color ranges", OFFSET(color_ranges_str), AV_OPT_TYPE_STRING, {.str = "unknown"}, .flags = AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM }, { NULL } }; diff --git a/libavfilter/vf_noise.c b/libavfilter/vf_noise.c index abdf04708b..c7af77b669 100644 --- a/libavfilter/vf_noise.c +++ b/libavfilter/vf_noise.c @@ -142,7 +142,11 @@ static int query_formats(AVFilterContext *ctx) return ret; } - return ff_set_common_formats(ctx, formats); + ret = ff_set_common_formats(ctx, formats); + if (ret < 0) + return ret; + + return ff_set_common_color_ranges(ctx, ff_all_color_ranges()); } static int config_input(AVFilterLink *inlink) diff --git a/libavfilter/vf_scale.c b/libavfilter/vf_scale.c index 802f841cc3..af5edd5146 100644 --- a/libavfilter/vf_scale.c +++ b/libavfilter/vf_scale.c @@ -157,7 +157,9 @@ static av_cold void uninit(AVFilterContext *ctx) static int query_formats(AVFilterContext *ctx) { + ScaleContext *scale = ctx->priv; AVFilterFormats *formats; + AVFilterFormats *color_ranges; enum AVPixelFormat pix_fmt; int ret; @@ -174,10 +176,14 @@ static int query_formats(AVFilterContext *ctx) } if ((ret = ff_formats_ref(formats, &ctx->inputs[0]->out_formats)) < 0) return ret; + color_ranges = ff_all_color_ranges(); + if ((ret = ff_formats_ref(color_ranges, &ctx->inputs[0]->out_color_ranges)) < 0) + return ret; } if (ctx->outputs[0]) { const AVPixFmtDescriptor *desc = NULL; formats = NULL; + color_ranges = NULL; while ((desc = av_pix_fmt_desc_next(desc))) { pix_fmt = av_pix_fmt_desc_get_id(desc); if ((sws_isSupportedOutput(pix_fmt) || pix_fmt == AV_PIX_FMT_PAL8 || @@ -188,6 +194,10 @@ static int query_formats(AVFilterContext *ctx) } if ((ret = ff_formats_ref(formats, &ctx->outputs[0]->in_formats)) < 0) return ret; + + color_ranges = ff_all_color_ranges(); + if ((ret = ff_formats_ref(color_ranges, &ctx->outputs[0]->in_color_ranges)) < 0) + return ret; } return 0; @@ -276,6 +286,7 @@ static int config_props(AVFilterLink *outlink) scale->isws[0] = scale->isws[1] = scale->sws = NULL; if (inlink0->w == outlink->w && inlink0->h == outlink->h && + inlink0->color_range == outlink->color_range && !scale->out_color_matrix && scale->in_range == scale->out_range && inlink0->format == outlink->format) @@ -348,11 +359,11 @@ static int config_props(AVFilterLink *outlink) } else outlink->sample_aspect_ratio = inlink0->sample_aspect_ratio; - av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d fmt:%s sar:%d/%d -> w:%d h:%d fmt:%s sar:%d/%d flags:0x%0x\n", + av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d fmt:%s sar:%d/%d cr:%d -> w:%d h:%d fmt:%s sar:%d/%d cr:%d flags:0x%0x\n", inlink ->w, inlink ->h, av_get_pix_fmt_name( inlink->format), - inlink->sample_aspect_ratio.num, inlink->sample_aspect_ratio.den, + inlink->sample_aspect_ratio.num, inlink->sample_aspect_ratio.den, inlink->color_range, outlink->w, outlink->h, av_get_pix_fmt_name(outlink->format), - outlink->sample_aspect_ratio.num, outlink->sample_aspect_ratio.den, + outlink->sample_aspect_ratio.num, outlink->sample_aspect_ratio.den, outlink->color_range, scale->flags); return 0; diff --git a/libavfilter/vsrc_testsrc.c b/libavfilter/vsrc_testsrc.c index a790974d14..fddd7f55ee 100644 --- a/libavfilter/vsrc_testsrc.c +++ b/libavfilter/vsrc_testsrc.c @@ -207,7 +207,13 @@ static av_cold int color_init(AVFilterContext *ctx) static int color_query_formats(AVFilterContext *ctx) { - return ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0)); + int ret; + + ret = ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0)); + if (ret < 0) + return ret; + + return ff_set_common_color_ranges(ctx, ff_mpeg_color_ranges()); } static int color_config_props(AVFilterLink *inlink) @@ -653,11 +659,16 @@ static int test_query_formats(AVFilterContext *ctx) static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGB24, AV_PIX_FMT_NONE }; + int ret; AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts); if (!fmts_list) return AVERROR(ENOMEM); - return ff_set_common_formats(ctx, fmts_list); + ret = ff_set_common_formats(ctx, fmts_list); + if (ret < 0) + return ret; + + return ff_set_common_color_ranges(ctx, ff_all_color_ranges()); } static const AVFilterPad avfilter_vsrc_testsrc_outputs[] = { diff --git a/tests/fate/filter-video.mak b/tests/fate/filter-video.mak index c19f301ff8..24c96ffced 100644 --- a/tests/fate/filter-video.mak +++ b/tests/fate/filter-video.mak @@ -1,5 +1,5 @@ FATE_FILTER_SAMPLES-$(call ALLYES, SMJPEG_DEMUXER MJPEG_DECODER PERMS_FILTER OWDENOISE_FILTER) += fate-filter-owdenoise-sample -fate-filter-owdenoise-sample: CMD = ffmpeg -idct simple -i $(TARGET_SAMPLES)/smjpeg/scenwin.mjpg -vf "trim=duration=0.5,perms=random,owdenoise=10:20:20:enable=not(between(t\,0.2\,1.2))" -an -f rawvideo - +fate-filter-owdenoise-sample: CMD = ffmpeg -idct simple -i $(TARGET_SAMPLES)/smjpeg/scenwin.mjpg -vf "trim=duration=0.5,perms=random,owdenoise=10:20:20:enable=not(between(t\,0.2\,1.2)),format=yuv420p:tv" -an -f rawvideo - fate-filter-owdenoise-sample: REF = $(TARGET_SAMPLES)/filter-reference/owdenoise-scenwin.raw fate-filter-owdenoise-sample: CMP_TARGET = 1 fate-filter-owdenoise-sample: FUZZ = 3539 diff --git a/tests/fate/pixlet.mak b/tests/fate/pixlet.mak index c720f32616..e42c06d194 100644 --- a/tests/fate/pixlet.mak +++ b/tests/fate/pixlet.mak @@ -1,5 +1,5 @@ FATE_PIXLET += fate-pixlet-rgb -fate-pixlet-rgb: CMD = framecrc -i $(TARGET_SAMPLES)/pixlet/pixlet_rgb.mov -an -pix_fmt yuv420p16le +fate-pixlet-rgb: CMD = framecrc -i $(TARGET_SAMPLES)/pixlet/pixlet_rgb.mov -an -vf format=yuv420p16le:pc FATE_SAMPLES_AVCONV-$(call DEMDEC, MOV, PIXLET) += $(FATE_PIXLET) fate-pixlet: $(FATE_PIXLET)