[FFmpeg-devel] [PATCH 4/4] ffmpeg: pass decoded or filtered AVFrame to output stream initialization
Jan Ekström
jeebjp at gmail.com
Tue Sep 15 22:23:28 EEST 2020
On Tue, Sep 15, 2020 at 9:12 PM Jan Ekström <jeebjp at gmail.com> wrote:
>
> On Tue, Sep 15, 2020 at 7:39 PM James Almer <jamrial at 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 at 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;
}
More information about the ffmpeg-devel
mailing list