diff mbox series

[FFmpeg-devel,4/4] ffmpeg: pass decoded or filtered AVFrame to output stream initialization

Message ID 20200913102622.168011-5-jeebjp@gmail.com
State Superseded
Headers show
Series ffmpeg: late A/V encoder init, AVFrame metadata usage | expand

Checks

Context Check Description
andriy/default pending
andriy/make success Make finished
andriy/make_fate success Make fate finished

Commit Message

Jan Ekström Sept. 13, 2020, 10:26 a.m. UTC
Additionally, reap the first rewards by being able to set the
color related encoding values based on the passed AVFrame.

The only tests that seem to have changed their results with this
change seem to be the MXF tests. There, the muxer writes the
limited/full range flag to the output container if the encoder
is not set to "unspecified".
---
 fftools/ffmpeg.c            | 42 +++++++++++++++++++++++++++----------
 tests/ref/lavf/mxf_d10      |  2 +-
 tests/ref/lavf/mxf_dv25     |  2 +-
 tests/ref/lavf/mxf_dvcpro50 |  2 +-
 tests/ref/lavf/mxf_opatom   |  2 +-
 5 files changed, 35 insertions(+), 15 deletions(-)

Comments

Michael Niedermayer Sept. 15, 2020, 7:54 a.m. UTC | #1
On Sun, Sep 13, 2020 at 01:26:22PM +0300, Jan Ekström wrote:
> Additionally, reap the first rewards by being able to set the
> color related encoding values based on the passed AVFrame.
> 
> The only tests that seem to have changed their results with this
> change seem to be the MXF tests. There, the muxer writes the
> limited/full range flag to the output container if the encoder
> is not set to "unspecified".
> ---
>  fftools/ffmpeg.c            | 42 +++++++++++++++++++++++++++----------
>  tests/ref/lavf/mxf_d10      |  2 +-
>  tests/ref/lavf/mxf_dv25     |  2 +-
>  tests/ref/lavf/mxf_dvcpro50 |  2 +-
>  tests/ref/lavf/mxf_opatom   |  2 +-
>  5 files changed, 35 insertions(+), 15 deletions(-)

This breaks:

./ffmpeg -i fate-suite//png1/lena-rgba.png -pix_fmt rgba -vcodec ljpeg -bitexact ljpeg.jpeg 

[...]
Jan Ekström Sept. 15, 2020, 10:06 a.m. UTC | #2
On Tue, Sep 15, 2020 at 10:54 AM Michael Niedermayer
<michael@niedermayer.cc> wrote:
>
> On Sun, Sep 13, 2020 at 01:26:22PM +0300, Jan Ekström wrote:
> > Additionally, reap the first rewards by being able to set the
> > color related encoding values based on the passed AVFrame.
> >
> > The only tests that seem to have changed their results with this
> > change seem to be the MXF tests. There, the muxer writes the
> > limited/full range flag to the output container if the encoder
> > is not set to "unspecified".
> > ---
> >  fftools/ffmpeg.c            | 42 +++++++++++++++++++++++++++----------
> >  tests/ref/lavf/mxf_d10      |  2 +-
> >  tests/ref/lavf/mxf_dv25     |  2 +-
> >  tests/ref/lavf/mxf_dvcpro50 |  2 +-
> >  tests/ref/lavf/mxf_opatom   |  2 +-
> >  5 files changed, 35 insertions(+), 15 deletions(-)
>
> This breaks:
>
> ./ffmpeg -i fate-suite//png1/lena-rgba.png -pix_fmt rgba -vcodec ljpeg -bitexact ljpeg.jpeg

By applying some logging (patch follows), it seems like the AVFrame
incorrectly identifies itself as limited range:
AVFrame: 128x128, pix_fmt: bgra, range: tv

Will have to see which filter does this, or if this is a general issue
when initializing AVFrames.

Jan

------>8--------
diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
index 8874da9268..27185441ff 100644
--- a/fftools/ffmpeg.c
+++ b/fftools/ffmpeg.c
@@ -1124,6 +1124,13 @@ static void do_video_out(OutputFile *of,
     InputStream *ist = NULL;
     AVFilterContext *filter = ost->filter->filter;

+    if (next_picture) {
+        const AVPixFmtDescriptor *desc =
av_pix_fmt_desc_get(next_picture->format);
+        av_log(NULL, AV_LOG_VERBOSE, "AVFrame: %dx%d, pix_fmt: %s,
range: %s\n",
+               next_picture->width, next_picture->height,
+               desc->name, av_color_range_name(next_picture->color_range));
+    }
+
     init_output_stream_wrapper(ost, next_picture, 1);
     sync_ipts = adjust_frame_pts_to_encoder_tb(of, ost, next_picture);
Jan Ekström Sept. 15, 2020, 2:28 p.m. UTC | #3
On Tue, Sep 15, 2020 at 1:06 PM Jan Ekström <jeebjp@gmail.com> wrote:
>
> On Tue, Sep 15, 2020 at 10:54 AM Michael Niedermayer
> <michael@niedermayer.cc> wrote:
> >
> > On Sun, Sep 13, 2020 at 01:26:22PM +0300, Jan Ekström wrote:
> > > Additionally, reap the first rewards by being able to set the
> > > color related encoding values based on the passed AVFrame.
> > >
> > > The only tests that seem to have changed their results with this
> > > change seem to be the MXF tests. There, the muxer writes the
> > > limited/full range flag to the output container if the encoder
> > > is not set to "unspecified".
> > > ---
> > >  fftools/ffmpeg.c            | 42 +++++++++++++++++++++++++++----------
> > >  tests/ref/lavf/mxf_d10      |  2 +-
> > >  tests/ref/lavf/mxf_dv25     |  2 +-
> > >  tests/ref/lavf/mxf_dvcpro50 |  2 +-
> > >  tests/ref/lavf/mxf_opatom   |  2 +-
> > >  5 files changed, 35 insertions(+), 15 deletions(-)
> >
> > This breaks:
> >
> > ./ffmpeg -i fate-suite//png1/lena-rgba.png -pix_fmt rgba -vcodec ljpeg -bitexact ljpeg.jpeg
>
> By applying some logging (patch follows), it seems like the AVFrame
> incorrectly identifies itself as limited range:
> AVFrame: 128x128, pix_fmt: bgra, range: tv
>
> Will have to see which filter does this, or if this is a general issue
> when initializing AVFrames.
>
> Jan
>
> ------>8--------
> diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
> index 8874da9268..27185441ff 100644
> --- a/fftools/ffmpeg.c
> +++ b/fftools/ffmpeg.c
> @@ -1124,6 +1124,13 @@ static void do_video_out(OutputFile *of,
>      InputStream *ist = NULL;
>      AVFilterContext *filter = ost->filter->filter;
>
> +    if (next_picture) {
> +        const AVPixFmtDescriptor *desc =
> av_pix_fmt_desc_get(next_picture->format);
> +        av_log(NULL, AV_LOG_VERBOSE, "AVFrame: %dx%d, pix_fmt: %s,
> range: %s\n",
> +               next_picture->width, next_picture->height,
> +               desc->name, av_color_range_name(next_picture->color_range));
> +    }
> +
>      init_output_stream_wrapper(ost, next_picture, 1);
>      sync_ipts = adjust_frame_pts_to_encoder_tb(of, ost, next_picture);

Further looking into this, it seems to come from filtering (?) (diff attached)

decoded AVFrame: 128x128, pix_fmt: rgba, range: pc
while...
AVFrame to be encoded: 128x128, pix_fmt: bgra, range: tv

Jan

------>8--------
diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
index 8874da9268..f4af6cc71f 100644
--- a/fftools/ffmpeg.c
+++ b/fftools/ffmpeg.c
@@ -1124,6 +1124,13 @@ static void do_video_out(OutputFile *of,
     InputStream *ist = NULL;
     AVFilterContext *filter = ost->filter->filter;

+    if (next_picture) {
+        const AVPixFmtDescriptor *desc =
av_pix_fmt_desc_get(next_picture->format);
+        av_log(NULL, AV_LOG_VERBOSE, "AVFrame to be encoded: %dx%d,
pix_fmt: %s, range: %s\n",
+               next_picture->width, next_picture->height,
+               desc->name, av_color_range_name(next_picture->color_range));
+    }
+
     init_output_stream_wrapper(ost, next_picture, 1);
     sync_ipts = adjust_frame_pts_to_encoder_tb(of, ost, next_picture);

@@ -2266,6 +2273,13 @@ static int decode(AVCodecContext *avctx,
AVFrame *frame, int *got_frame, AVPacke
     if (ret >= 0)
         *got_frame = 1;

+    if (frame) {
+        const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
+        av_log(NULL, AV_LOG_VERBOSE, "decoded AVFrame: %dx%d,
pix_fmt: %s, range: %s\n",
+               frame->width, frame->height,
+               desc ? desc->name : "unknown",
av_color_range_name(frame->color_range));
+    }
+
     return 0;
 }
Jan Ekström Sept. 15, 2020, 4:21 p.m. UTC | #4
On Tue, Sep 15, 2020 at 5:28 PM Jan Ekström <jeebjp@gmail.com> wrote:
>
>
> Further looking into this, it seems to come from filtering (?) (diff attached)
>
> decoded AVFrame: 128x128, pix_fmt: rgba, range: pc
> while...
> AVFrame to be encoded: 128x128, pix_fmt: bgra, range: tv
>
> Jan

For the record, the culprit seems to be this piece of logic within
libavfilter/vf_scale.c:
http://git.videolan.org/?p=ffmpeg.git;a=blob;f=libavfilter/vf_scale.c;h=58eee967440657798f84383ec6f79e8a05c3ece0;hb=HEAD#l747

Only input has range defined, out_full never gets set to nonzero.
Thus,
out->color_range = out_full ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG
leads to AVCOL_RANGE_MPEG.

Jan
James Almer Sept. 15, 2020, 4:38 p.m. UTC | #5
On 9/15/2020 1:21 PM, Jan Ekström wrote:
> On Tue, Sep 15, 2020 at 5:28 PM Jan Ekström <jeebjp@gmail.com> wrote:
>>
>>
>> Further looking into this, it seems to come from filtering (?) (diff attached)
>>
>> decoded AVFrame: 128x128, pix_fmt: rgba, range: pc
>> while...
>> AVFrame to be encoded: 128x128, pix_fmt: bgra, range: tv
>>
>> Jan
> 
> For the record, the culprit seems to be this piece of logic within
> libavfilter/vf_scale.c:
> http://git.videolan.org/?p=ffmpeg.git;a=blob;f=libavfilter/vf_scale.c;h=58eee967440657798f84383ec6f79e8a05c3ece0;hb=HEAD#l747
> 
> Only input has range defined, out_full never gets set to nonzero.
> Thus,
> out->color_range = out_full ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG
> leads to AVCOL_RANGE_MPEG.

Would changing that to out->color_range = scale->out_range help, or
break things too much?

> 
> Jan
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
Jan Ekström Sept. 15, 2020, 6:12 p.m. UTC | #6
On Tue, Sep 15, 2020 at 7:39 PM James Almer <jamrial@gmail.com> wrote:
>
> On 9/15/2020 1:21 PM, Jan Ekström wrote:
> > On Tue, Sep 15, 2020 at 5:28 PM Jan Ekström <jeebjp@gmail.com> wrote:
> >>
> >>
> >> Further looking into this, it seems to come from filtering (?) (diff attached)
> >>
> >> decoded AVFrame: 128x128, pix_fmt: rgba, range: pc
> >> while...
> >> AVFrame to be encoded: 128x128, pix_fmt: bgra, range: tv
> >>
> >> Jan
> >
> > For the record, the culprit seems to be this piece of logic within
> > libavfilter/vf_scale.c:
> > http://git.videolan.org/?p=ffmpeg.git;a=blob;f=libavfilter/vf_scale.c;h=58eee967440657798f84383ec6f79e8a05c3ece0;hb=HEAD#l747
> >
> > Only input has range defined, out_full never gets set to nonzero.
> > Thus,
> > out->color_range = out_full ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG
> > leads to AVCOL_RANGE_MPEG.
>
> Would changing that to out->color_range = scale->out_range help, or
> break things too much?
>

I was informed that swscale doesn't say that RGB is full range, so I
moved on to fix that since that is utilized as out_full here. Patch
follows.

After doing that, FATE still passed but...

[swscaler @ 0x62f000000400] sws_init_context: srcRange: 1, dstRange: 1
[auto_scaler_0 @ 0x611000007980] w:128 h:128 fmt:rgba sar:2835/2835 ->
w:128 h:128 fmt:bgra sar:1/1 flags:0x4
scale_frame: out_full: 0
vf_scale AVFrame: 128x128, pix_fmt: bgra, range: tv

I am very confused at this point :P

Jan

------>8--------
diff --git a/libswscale/utils.c b/libswscale/utils.c
index 9ca378bd3b..59d4d5e873 100644
--- a/libswscale/utils.c
+++ b/libswscale/utils.c
@@ -1200,8 +1200,11 @@ av_cold int sws_init_context(SwsContext *c,
SwsFilter *srcFilter,

     unscaled = (srcW == dstW && srcH == dstH);

-    c->srcRange |= handle_jpeg(&c->srcFormat);
-    c->dstRange |= handle_jpeg(&c->dstFormat);
+    c->srcRange |= isAnyRGB(c->srcFormat) ? 1 : handle_jpeg(&c->srcFormat);
+    c->dstRange |= isAnyRGB(c->dstFormat) ? 1 : handle_jpeg(&c->dstFormat);
+
+    av_log(c, AV_LOG_VERBOSE, "%s: srcRange: %d, dstRange: %d\n",
+           __FUNCTION__, c->srcRange, c->dstRange);

     if(srcFormat!=c->srcFormat || dstFormat!=c->dstFormat)
         av_log(c, AV_LOG_WARNING, "deprecated pixel format used, make
sure you did set range correctly\n");
Jan Ekström Sept. 15, 2020, 7:23 p.m. UTC | #7
On Tue, Sep 15, 2020 at 9:12 PM Jan Ekström <jeebjp@gmail.com> wrote:
>
> On Tue, Sep 15, 2020 at 7:39 PM James Almer <jamrial@gmail.com> wrote:
> >
> > On 9/15/2020 1:21 PM, Jan Ekström wrote:
> > > On Tue, Sep 15, 2020 at 5:28 PM Jan Ekström <jeebjp@gmail.com> wrote:
> > >>
> > >>
> > >> Further looking into this, it seems to come from filtering (?) (diff attached)
> > >>
> > >> decoded AVFrame: 128x128, pix_fmt: rgba, range: pc
> > >> while...
> > >> AVFrame to be encoded: 128x128, pix_fmt: bgra, range: tv
> > >>
> > >> Jan
> > >
> > > For the record, the culprit seems to be this piece of logic within
> > > libavfilter/vf_scale.c:
> > > http://git.videolan.org/?p=ffmpeg.git;a=blob;f=libavfilter/vf_scale.c;h=58eee967440657798f84383ec6f79e8a05c3ece0;hb=HEAD#l747
> > >
> > > Only input has range defined, out_full never gets set to nonzero.
> > > Thus,
> > > out->color_range = out_full ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG
> > > leads to AVCOL_RANGE_MPEG.
> >
> > Would changing that to out->color_range = scale->out_range help, or
> > break things too much?
> >
>
> I was informed that swscale doesn't say that RGB is full range, so I
> moved on to fix that since that is utilized as out_full here. Patch
> follows.
>
> After doing that, FATE still passed but...
>
> [swscaler @ 0x62f000000400] sws_init_context: srcRange: 1, dstRange: 1
> [auto_scaler_0 @ 0x611000007980] w:128 h:128 fmt:rgba sar:2835/2835 ->
> w:128 h:128 fmt:bgra sar:1/1 flags:0x4
> scale_frame: out_full: 0
> vf_scale AVFrame: 128x128, pix_fmt: bgra, range: tv
>
> I am very confused at this point :P
>
> Jan
>
> ------>8--------
> diff --git a/libswscale/utils.c b/libswscale/utils.c
> index 9ca378bd3b..59d4d5e873 100644
> --- a/libswscale/utils.c
> +++ b/libswscale/utils.c
> @@ -1200,8 +1200,11 @@ av_cold int sws_init_context(SwsContext *c,
> SwsFilter *srcFilter,
>
>      unscaled = (srcW == dstW && srcH == dstH);
>
> -    c->srcRange |= handle_jpeg(&c->srcFormat);
> -    c->dstRange |= handle_jpeg(&c->dstFormat);
> +    c->srcRange |= isAnyRGB(c->srcFormat) ? 1 : handle_jpeg(&c->srcFormat);
> +    c->dstRange |= isAnyRGB(c->dstFormat) ? 1 : handle_jpeg(&c->dstFormat);
> +
> +    av_log(c, AV_LOG_VERBOSE, "%s: srcRange: %d, dstRange: %d\n",
> +           __FUNCTION__, c->srcRange, c->dstRange);
>
>      if(srcFormat!=c->srcFormat || dstFormat!=c->dstFormat)
>          av_log(c, AV_LOG_WARNING, "deprecated pixel format used, make
> sure you did set range correctly\n");

OK, I found the silent override in sws_setColorspaceDetails. If this
is indeed how swscale notes what the input/output range is, maybe it
should be something a la the following? (maybe just without the
duplication by making it a function).

@@ -876,10 +914,23 @@ int sws_setColorspaceDetails(struct SwsContext
*c, const int inv_table[4],
     desc_dst = av_pix_fmt_desc_get(c->dstFormat);
     desc_src = av_pix_fmt_desc_get(c->srcFormat);

-    if(!isYUV(c->dstFormat) && !isGray(c->dstFormat))
-        dstRange = 0;
-    if(!isYUV(c->srcFormat) && !isGray(c->srcFormat))
-        srcRange = 0;
+    if ((isAnyRGB(c->dstFormat) || handle_jpeg(&c->dstFormat)) && !dstRange) {
+        av_log(c, AV_LOG_WARNING,
+               "Destination pixel format '%s' is considered full range, "
+               "but an attempt was made to set the conversion to limited "
+               "range. Enforcing full range.\n",
+               (desc_dst && desc_dst->name) ? desc_dst->name : "unknown");
+        dstRange = 1;
+    }
+
+    if ((isAnyRGB(c->srcFormat) || handle_jpeg(&c->srcFormat)) && !srcRange) {
+        av_log(c, AV_LOG_WARNING,
+               "Source pixel format '%s' is considered full range, "
+               "but an attempt was made to set the conversion to limited "
+               "range. Enforcing full range.\n",
+               (desc_dst && desc_dst->name) ? desc_dst->name : "unknown");
+        srcRange = 1;
+    }

     if (c->srcRange != srcRange ||
         c->dstRange != dstRange ||

Alternatively, if the swscale value is not the input/output range
exactly, maybe we should switch to something a la the following?

static int decide_color_range(ScaleContext *scale, AVFrame *in_frame,
                              AVFrame *out_frame)
{
    const AVPixFmtDescriptor *in_desc  = av_pix_fmt_desc_get(in_frame->format);
    const AVPixFmtDescriptor *out_desc = av_pix_fmt_desc_get(out_frame->format);
    if (!in_desc || !out_desc) {
        return AVERROR_INVALIDDATA;
    }

    int output_is_yuvj = \
        (out_desc->name && !strncmp(out_desc->name, "yuvj", 4));

    // first decide on the range. start by setting it to the
    // scale context's value.
    out_frame->color_range = scale->out_range;
    if (out_frame->color_range != AVCOL_RANGE_UNSPECIFIED)
        // we have a range specified for output, off ye go
        goto early_exit;

    // output range is unspecified, time to do some logic
    if (!!(in_desc->flags & AV_PIX_FMT_FLAG_RGB) ==
        !!(out_desc->flags & AV_PIX_FMT_FLAG_RGB)) {
        // if both input and output match in type, utilize the input range
        // (prefer parameter, although if that override is not available,
        // utilize the AVFrame's range)
        out_frame->color_range = (scale->in_range == AVCOL_RANGE_UNSPECIFIED) ?
            in_frame->color_range : scale->in_range;

        // if we got a value, run with it.
        if (out_frame->color_range != AVCOL_RANGE_UNSPECIFIED)
            goto early_exit;
    }

    // alright, if we are here neither the input or output knows what it
    // wants, but we know what will happen.
    // - If it's YCbCr, it is going to be full range for J formats and
    //   limited range for non-J.
    // - If it's RGB, it's going to be full range.
    out_frame->color_range = (out_desc->flags & AV_PIX_FMT_FLAG_RGB) ?
        AVCOL_RANGE_JPEG :
        (output_is_yuvj ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG);

early_exit:

    return 0;
}
diff mbox series

Patch

diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
index 66d7da695a..5425ba245d 100644
--- a/fftools/ffmpeg.c
+++ b/fftools/ffmpeg.c
@@ -946,9 +946,11 @@  static double adjust_frame_pts_to_encoder_tb(OutputFile *of, OutputStream *ost,
     return float_pts;
 }
 
-static int init_output_stream(OutputStream *ost, char *error, int error_len);
+static int init_output_stream(OutputStream *ost, AVFrame *frame,
+                              char *error, int error_len);
 
-static int init_output_stream_wrapper(OutputStream *ost, unsigned int fatal)
+static int init_output_stream_wrapper(OutputStream *ost, AVFrame *frame,
+                                      unsigned int fatal)
 {
     int ret = AVERROR_BUG;
     char error[1024] = {0};
@@ -956,7 +958,7 @@  static int init_output_stream_wrapper(OutputStream *ost, unsigned int fatal)
     if (ost->initialized)
         return 0;
 
-    ret = init_output_stream(ost, error, sizeof(error));
+    ret = init_output_stream(ost, frame, error, sizeof(error));
     if (ret < 0) {
         av_log(NULL, AV_LOG_ERROR, "Error initializing output stream %d:%d -- %s\n",
                ost->file_index, ost->index, error);
@@ -1130,7 +1132,7 @@  static void do_video_out(OutputFile *of,
     InputStream *ist = NULL;
     AVFilterContext *filter = ost->filter->filter;
 
-    init_output_stream_wrapper(ost, 1);
+    init_output_stream_wrapper(ost, next_picture, 1);
     sync_ipts = adjust_frame_pts_to_encoder_tb(of, ost, next_picture);
 
     if (ost->source_index >= 0)
@@ -1512,7 +1514,7 @@  static int reap_filters(int flush)
          * the encoder earlier than receiving the first AVFrame.
          */
         if (av_buffersink_get_type(filter) == AVMEDIA_TYPE_AUDIO)
-            init_output_stream_wrapper(ost, 1);
+            init_output_stream_wrapper(ost, NULL, 1);
 
         if (!ost->filtered_frame && !(ost->filtered_frame = av_frame_alloc())) {
             return AVERROR(ENOMEM);
@@ -1935,7 +1937,7 @@  static void flush_encoders(void)
                 finish_output_stream(ost);
             }
 
-            init_output_stream_wrapper(ost, 1);
+            init_output_stream_wrapper(ost, NULL, 1);
         }
 
         if (enc->codec_type != AVMEDIA_TYPE_VIDEO && enc->codec_type != AVMEDIA_TYPE_AUDIO)
@@ -3307,7 +3309,7 @@  static void init_encoder_time_base(OutputStream *ost, AVRational default_time_ba
     enc_ctx->time_base = default_time_base;
 }
 
-static int init_output_stream_encode(OutputStream *ost)
+static int init_output_stream_encode(OutputStream *ost, AVFrame *frame)
 {
     InputStream *ist = get_input_stream(ost);
     AVCodecContext *enc_ctx = ost->enc_ctx;
@@ -3404,6 +3406,23 @@  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);
 
+        if (frame) {
+            if (!av_dict_get(ost->encoder_opts, "color_range", NULL, 0))
+                enc_ctx->color_range = frame->color_range;
+
+            if (!av_dict_get(ost->encoder_opts, "color_primaries", NULL, 0))
+                enc_ctx->color_primaries = frame->color_primaries;
+
+            if (!av_dict_get(ost->encoder_opts, "color_trc", NULL, 0))
+                enc_ctx->color_trc = frame->color_trc;
+
+            if (!av_dict_get(ost->encoder_opts, "colorspace", NULL, 0))
+                enc_ctx->colorspace = frame->colorspace;
+
+            if (!av_dict_get(ost->encoder_opts, "chroma_sample_location", NULL, 0))
+                enc_ctx->chroma_sample_location = frame->chroma_location;
+        }
+
         enc_ctx->framerate = ost->frame_rate;
 
         ost->st->avg_frame_rate = ost->frame_rate;
@@ -3461,7 +3480,8 @@  static int init_output_stream_encode(OutputStream *ost)
     return 0;
 }
 
-static int init_output_stream(OutputStream *ost, char *error, int error_len)
+static int init_output_stream(OutputStream *ost, AVFrame *frame,
+                              char *error, int error_len)
 {
     int ret = 0;
 
@@ -3470,7 +3490,7 @@  static int init_output_stream(OutputStream *ost, char *error, int error_len)
         AVCodecContext *dec = NULL;
         InputStream *ist;
 
-        ret = init_output_stream_encode(ost);
+        ret = init_output_stream_encode(ost, frame);
         if (ret < 0)
             return ret;
 
@@ -3722,7 +3742,7 @@  static int transcode_init(void)
              output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO))
             continue;
 
-        ret = init_output_stream_wrapper(output_streams[i], 0);
+        ret = init_output_stream_wrapper(output_streams[i], NULL, 0);
         if (ret < 0)
             goto dump_format;
     }
@@ -4654,7 +4674,7 @@  static int transcode_step(void)
          * these early exits can be gotten rid of.
          */
         if (av_buffersink_get_type(ost->filter->filter) == AVMEDIA_TYPE_AUDIO)
-            init_output_stream_wrapper(ost, 1);
+            init_output_stream_wrapper(ost, NULL, 1);
 
         if ((ret = transcode_from_filter(ost->filter->graph, &ist)) < 0)
             return ret;
diff --git a/tests/ref/lavf/mxf_d10 b/tests/ref/lavf/mxf_d10
index aea469bb58..85e337d157 100644
--- a/tests/ref/lavf/mxf_d10
+++ b/tests/ref/lavf/mxf_d10
@@ -1,3 +1,3 @@ 
-e597f73ef9c9819710d2f815813eb91f *tests/data/lavf/lavf.mxf_d10
+36fc2a640368f6d33987d2b2d39df966 *tests/data/lavf/lavf.mxf_d10
 5332013 tests/data/lavf/lavf.mxf_d10
 tests/data/lavf/lavf.mxf_d10 CRC=0x6c74d488
diff --git a/tests/ref/lavf/mxf_dv25 b/tests/ref/lavf/mxf_dv25
index db6b76c6f8..d4559df862 100644
--- a/tests/ref/lavf/mxf_dv25
+++ b/tests/ref/lavf/mxf_dv25
@@ -1,3 +1,3 @@ 
-0fc964fa22bc8b3a389b81b9a2efccb3 *tests/data/lavf/lavf.mxf_dv25
+57623b3b968c0bb0d8a0bbaeef6fe657 *tests/data/lavf/lavf.mxf_dv25
 3834413 tests/data/lavf/lavf.mxf_dv25
 tests/data/lavf/lavf.mxf_dv25 CRC=0xbdaf7f52
diff --git a/tests/ref/lavf/mxf_dvcpro50 b/tests/ref/lavf/mxf_dvcpro50
index 09999914bf..8bcf67d17f 100644
--- a/tests/ref/lavf/mxf_dvcpro50
+++ b/tests/ref/lavf/mxf_dvcpro50
@@ -1,3 +1,3 @@ 
-aa81ea83af44a69e73849e327cc4bd12 *tests/data/lavf/lavf.mxf_dvcpro50
+6e82b4cc962199e2593e30851ff7ccfb *tests/data/lavf/lavf.mxf_dvcpro50
 7431213 tests/data/lavf/lavf.mxf_dvcpro50
 tests/data/lavf/lavf.mxf_dvcpro50 CRC=0xe3bbe4b4
diff --git a/tests/ref/lavf/mxf_opatom b/tests/ref/lavf/mxf_opatom
index 05794a4e5e..1aa843a22a 100644
--- a/tests/ref/lavf/mxf_opatom
+++ b/tests/ref/lavf/mxf_opatom
@@ -1,3 +1,3 @@ 
-06a1816aa91c733e1ef7e45d82e4f1d3 *tests/data/lavf/lavf.mxf_opatom
+d5f56215c2b16dee204f03bfa653dd1b *tests/data/lavf/lavf.mxf_opatom
 4717625 tests/data/lavf/lavf.mxf_opatom
 tests/data/lavf/lavf.mxf_opatom CRC=0xf55aa22a