diff mbox series

[FFmpeg-devel,v2,05/15] avfilter/buffersrc: add color_space/range parameters

Message ID 20231213131536.10242-6-ffmpeg@haasn.xyz
State New
Headers show
Series YUV colorspace filter negotiation | expand

Checks

Context Check Description
yinshiyou/make_fate_loongarch64 success Make fate finished
yinshiyou/make_loongarch64 warning New warnings during build
andriy/make_fate_x86 success Make fate finished
andriy/make_x86 warning New warnings during build

Commit Message

Niklas Haas Dec. 13, 2023, 1:12 p.m. UTC
From: Niklas Haas <git@haasn.dev>

To allow adding proper negotiation, in particular, to fftools.

These values will simply be negotiated downstream for YUV formats, and
ignored otherwise.
---
 doc/filters.texi        | 10 ++++++
 libavfilter/buffersrc.c | 74 ++++++++++++++++++++++++++++++++++++-----
 libavfilter/buffersrc.h |  6 ++++
 3 files changed, 82 insertions(+), 8 deletions(-)
diff mbox series

Patch

diff --git a/doc/filters.texi b/doc/filters.texi
index 6d00ba2c3f..f182dc2ddb 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -28025,6 +28025,16 @@  Specify the timebase assumed by the timestamps of the buffered frames.
 @item frame_rate
 Specify the frame rate expected for the video stream.
 
+@item colorspace
+A string representing the color space of the buffered video frames.
+It may be a number corresponding to a color space, or a color space
+name.
+
+@item range
+A string representing the color range of the buffered video frames.
+It may be a number corresponding to a color range, or a color range
+name.
+
 @item pixel_aspect, sar
 The sample (pixel) aspect ratio of the input video.
 
diff --git a/libavfilter/buffersrc.c b/libavfilter/buffersrc.c
index b0a905d455..1c5db94696 100644
--- a/libavfilter/buffersrc.c
+++ b/libavfilter/buffersrc.c
@@ -28,6 +28,7 @@ 
 #include "libavutil/channel_layout.h"
 #include "libavutil/common.h"
 #include "libavutil/frame.h"
+#include "libavutil/hwcontext.h"
 #include "libavutil/imgutils.h"
 #include "libavutil/internal.h"
 #include "libavutil/opt.h"
@@ -50,6 +51,8 @@  typedef struct BufferSourceContext {
     /* video only */
     int               w, h;
     enum AVPixelFormat  pix_fmt;
+    enum AVColorSpace color_space;
+    enum AVColorRange color_range;
     AVRational        pixel_aspect;
 
     AVBufferRef *hw_frames_ctx;
@@ -65,10 +68,13 @@  typedef struct BufferSourceContext {
     int64_t last_pts;
 } BufferSourceContext;
 
-#define CHECK_VIDEO_PARAM_CHANGE(s, c, width, height, format, pts)\
-    if (c->w != width || c->h != height || c->pix_fmt != format) {\
-        av_log(s, AV_LOG_INFO, "filter context - w: %d h: %d fmt: %d, incoming frame - w: %d h: %d fmt: %d pts_time: %s\n",\
-               c->w, c->h, c->pix_fmt, width, height, format, av_ts2timestr(pts, &s->outputs[0]->time_base));\
+#define CHECK_VIDEO_PARAM_CHANGE(s, c, width, height, format, csp, range, pts)\
+    if (c->w != width || c->h != height || c->pix_fmt != format ||\
+        c->color_space != csp || c->color_range != range) {\
+        av_log(s, AV_LOG_INFO, "filter context - w: %d h: %d fmt: %d csp: %s range: %s, incoming frame - w: %d h: %d fmt: %d csp: %s range: %s pts_time: %s\n",\
+               c->w, c->h, c->pix_fmt, av_color_space_name(c->color_space), av_color_range_name(c->color_range),\
+               width, height, format, av_color_space_name(csp), av_color_range_name(range),\
+               av_ts2timestr(pts, &s->outputs[0]->time_base));\
         av_log(s, AV_LOG_WARNING, "Changing video frame properties on the fly is not supported by all filters.\n");\
     }
 
@@ -89,6 +95,8 @@  AVBufferSrcParameters *av_buffersrc_parameters_alloc(void)
         return NULL;
 
     par->format = -1;
+    par->color_range = AVCOL_RANGE_UNSPECIFIED;
+    par->color_space = AVCOL_SPC_UNSPECIFIED;
 
     return par;
 }
@@ -119,6 +127,10 @@  int av_buffersrc_parameters_set(AVFilterContext *ctx, AVBufferSrcParameters *par
             if (!s->hw_frames_ctx)
                 return AVERROR(ENOMEM);
         }
+        if (param->color_space != AVCOL_SPC_UNSPECIFIED)
+            s->color_space = param->color_space;
+        if (param->color_range != AVCOL_RANGE_UNSPECIFIED)
+            s->color_range = param->color_range;
         break;
     case AVMEDIA_TYPE_AUDIO:
         if (param->format != AV_SAMPLE_FMT_NONE) {
@@ -206,7 +218,8 @@  FF_ENABLE_DEPRECATION_WARNINGS
         switch (ctx->outputs[0]->type) {
         case AVMEDIA_TYPE_VIDEO:
             CHECK_VIDEO_PARAM_CHANGE(ctx, s, frame->width, frame->height,
-                                     frame->format, frame->pts);
+                                     frame->format, frame->colorspace,
+                                     frame->color_range, frame->pts);
             break;
         case AVMEDIA_TYPE_AUDIO:
             /* For layouts unknown on input but known on link after negotiation. */
@@ -303,10 +316,11 @@  static av_cold int init_video(AVFilterContext *ctx)
         return AVERROR(EINVAL);
     }
 
-    av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d pixfmt:%s tb:%d/%d fr:%d/%d sar:%d/%d\n",
+    av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d pixfmt:%s tb:%d/%d fr:%d/%d sar:%d/%d csp:%s range:%s\n",
            c->w, c->h, av_get_pix_fmt_name(c->pix_fmt),
            c->time_base.num, c->time_base.den, c->frame_rate.num, c->frame_rate.den,
-           c->pixel_aspect.num, c->pixel_aspect.den);
+           c->pixel_aspect.num, c->pixel_aspect.den,
+           av_color_space_name(c->color_space), av_color_range_name(c->color_range));
 
     return 0;
 }
@@ -329,6 +343,30 @@  static const AVOption buffer_options[] = {
     { "pixel_aspect",  "sample aspect ratio",    OFFSET(pixel_aspect),     AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, DBL_MAX, V },
     { "time_base",     NULL,                     OFFSET(time_base),        AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, DBL_MAX, V },
     { "frame_rate",    NULL,                     OFFSET(frame_rate),       AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, DBL_MAX, V },
+    { "colorspace", "select colorspace", OFFSET(color_space), AV_OPT_TYPE_INT, {.i64=AVCOL_SPC_UNSPECIFIED}, 0, AVCOL_SPC_NB-1, V, "colorspace"},
+    {   "gbr",         NULL,  0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_RGB},               INT_MIN, INT_MAX, V, "colorspace"},
+    {   "bt709",       NULL,  0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT709},             INT_MIN, INT_MAX, V, "colorspace"},
+    {   "unknown",     NULL,  0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_UNSPECIFIED},       INT_MIN, INT_MAX, V, "colorspace"},
+    {   "fcc",         NULL,  0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_FCC},               INT_MIN, INT_MAX, V, "colorspace"},
+    {   "bt470bg",     NULL,  0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT470BG},           INT_MIN, INT_MAX, V, "colorspace"},
+    {   "smpte170m",   NULL,  0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_SMPTE170M},         INT_MIN, INT_MAX, V, "colorspace"},
+    {   "smpte240m",   NULL,  0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_SMPTE240M},         INT_MIN, INT_MAX, V, "colorspace"},
+    {   "ycgco",       NULL,  0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_YCGCO},             INT_MIN, INT_MAX, V, "colorspace"},
+    {   "bt2020nc",    NULL,  0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT2020_NCL},        INT_MIN, INT_MAX, V, "colorspace"},
+    {   "bt2020c",     NULL,  0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT2020_CL},         INT_MIN, INT_MAX, V, "colorspace"},
+    {   "smpte2085",   NULL,  0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_SMPTE2085},         INT_MIN, INT_MAX, V, "colorspace"},
+    {   "chroma-derived-nc",  NULL,  0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_CHROMA_DERIVED_NCL},INT_MIN, INT_MAX, V, "colorspace"},
+    {   "chroma-derived-c",   NULL,  0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_CHROMA_DERIVED_CL}, INT_MIN, INT_MAX, V, "colorspace"},
+    {   "ictcp",       NULL,  0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_ICTCP},             INT_MIN, INT_MAX, V, "colorspace"},
+    { "range", "select color range", OFFSET(color_range), AV_OPT_TYPE_INT, {.i64=AVCOL_RANGE_UNSPECIFIED}, 0, AVCOL_RANGE_NB-1, V, "range"},
+    {   "unspecified", NULL,   0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_UNSPECIFIED},  0, 0, V, "range"},
+    {   "unknown",     NULL,   0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_UNSPECIFIED},  0, 0, V, "range"},
+    {   "limited",     NULL,   0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG},         0, 0, V, "range"},
+    {   "tv",          NULL,   0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG},         0, 0, V, "range"},
+    {   "mpeg",        NULL,   0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG},         0, 0, V, "range"},
+    {   "full",        NULL,   0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG},         0, 0, V, "range"},
+    {   "pc",          NULL,   0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG},         0, 0, V, "range"},
+    {   "jpeg",        NULL,   0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG},         0, 0, V, "range"},
     { NULL },
 };
 
@@ -426,13 +464,33 @@  static int query_formats(AVFilterContext *ctx)
     AVFilterChannelLayouts *channel_layouts = NULL;
     AVFilterFormats *formats = NULL;
     AVFilterFormats *samplerates = NULL;
+    AVFilterFormats *color_spaces = NULL;
+    AVFilterFormats *color_ranges = NULL;
     int ret;
 
     switch (ctx->outputs[0]->type) {
-    case AVMEDIA_TYPE_VIDEO:
+    case AVMEDIA_TYPE_VIDEO:;
+        enum AVPixelFormat swfmt = c->pix_fmt;
+        if (av_pix_fmt_desc_get(swfmt)->flags & AV_PIX_FMT_FLAG_HWACCEL) {
+            if (!c->hw_frames_ctx) {
+                av_log(ctx, AV_LOG_ERROR, "Setting BufferSourceContext.pix_fmt "
+                       "to a HW format requires hw_frames_ctx to be non-NULL!\n");
+                return AVERROR(EINVAL);
+            }
+            swfmt = ((AVHWFramesContext *) c->hw_frames_ctx->data)->sw_format;
+        }
         if ((ret = ff_add_format         (&formats, c->pix_fmt)) < 0 ||
             (ret = ff_set_common_formats (ctx     , formats   )) < 0)
             return ret;
+        /* force specific colorspace/range downstream only for ordinary YUV */
+        if (ff_fmt_is_regular_yuv(swfmt)) {
+            if ((ret = ff_add_format(&color_spaces, c->color_space)) < 0 ||
+                (ret = ff_set_common_color_spaces(ctx, color_spaces)) < 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/buffersrc.h b/libavfilter/buffersrc.h
index 3b248b37cd..1e29f0340e 100644
--- a/libavfilter/buffersrc.h
+++ b/libavfilter/buffersrc.h
@@ -123,6 +123,12 @@  typedef struct AVBufferSrcParameters {
      * Audio only, the audio channel layout
      */
     AVChannelLayout ch_layout;
+
+    /**
+     * Video only, the YUV colorspace and range.
+     */
+    enum AVColorSpace color_space;
+    enum AVColorRange color_range;
 } AVBufferSrcParameters;
 
 /**