diff mbox series

[FFmpeg-devel] liavcodec: add bit-rate support to RoQ video encoder

Message ID f0767f5a-fd79-4002-9a3c-d1da8895d5f7@gmail.com
State New
Headers show
Series [FFmpeg-devel] liavcodec: add bit-rate support to RoQ video encoder | expand

Checks

Context Check Description
yinshiyou/configure_loongarch64 warning Failed to apply patch
andriy/configure_x86 warning Failed to apply patch

Commit Message

Victor Luchitz Jan. 21, 2024, 9:08 p.m. UTC
One can now use the bitrate option (-b) to specify bit rate of the video
stream in the RoQ encoder. The option only becomes effective for values
above 800kbit/s, which is roughly equivalent to bandwidth of a 1x-speed
CD-ROM drive, minus the bandwidth taken up by stereo DPCM stream. Values
below this threshold produce visually inadequate results.

Original patch by Joseph Fenton aka Chilly Willy

Signed-off-by: Victor Luchits <vluchits@gmail.com>
---
  Changelog                |   1 +
  libavcodec/roqvideo.h    |   1 +
  libavcodec/roqvideodec.c |  15 +++++
  libavcodec/roqvideoenc.c | 118 ++++++++++++++++++++++++++++++++++-----
  libavcodec/version.h     |   2 +-
  5 files changed, 123 insertions(+), 14 deletions(-)

Comments

Tomas Härdin Jan. 22, 2024, 1:05 p.m. UTC | #1
mån 2024-01-22 klockan 00:08 +0300 skrev Victor Luchits:
> One can now use the bitrate option (-b) to specify bit rate of the
> video
> stream in the RoQ encoder. The option only becomes effective for
> values
> above 800kbit/s, which is roughly equivalent to bandwidth of a 1x-
> speed
> CD-ROM drive, minus the bandwidth taken up by stereo DPCM stream.
> Values
> below this threshold produce visually inadequate results.
> 
> Original patch by Joseph Fenton aka Chilly Willy
> 
> Signed-off-by: Victor Luchits <vluchits@gmail.com>
> ---
>   Changelog                |   1 +
>   libavcodec/roqvideo.h    |   1 +
>   libavcodec/roqvideodec.c |  15 +++++
>   libavcodec/roqvideoenc.c | 118 ++++++++++++++++++++++++++++++++++--
> ---
>   libavcodec/version.h     |   2 +-
>   5 files changed, 123 insertions(+), 14 deletions(-)

Fails to apply with git am on current master

> warning: Patch sent with format=flowed; space at the end of lines
> might be lost.
> Applying: liavcodec: add bit-rate support to RoQ video encoder
> error: corrupt patch at line 20
> Patch failed at 0001 liavcodec: add bit-rate support to RoQ video
> encoder

Typo: liavcodec

> +    /* Keyframe when no MOT or FCC codes in frame */
> +    if (s->key_frame) {
> +        av_log(avctx, AV_LOG_VERBOSE, "\nFound keyframe!\n");
> +        rframe->pict_type = AV_PICTURE_TYPE_I;
> +        avpkt->flags |= AV_PKT_FLAG_KEY;

Consider resetting framesSinceKeyframe here

>  -    if (avctx->width > 65535 || avctx->height > 65535) {

The leading space is probably what makes the patch not apply

> -        av_log(avctx, AV_LOG_ERROR, "Dimensions are max %d\n", enc-
> >quake3_compat ? 32768 : 65535);
> +    if (enc->quake3_compat && ((avctx->width > 32767 || avctx-
> >height > 32767))) {
> +        av_log(avctx, AV_LOG_ERROR, "Dimensions are max %d\n",
> 32767);
> +        return AVERROR(EINVAL);
> +    }
> +    else if (avctx->width > 65535 || avctx->height > 65535) {
> +        av_log(avctx, AV_LOG_ERROR, "Dimensions are max %d\n",
> 65535);
>          return AVERROR(EINVAL);
>      }
>  -    if (((avctx->width)&(avctx->width-1))||((avctx->height)&(avctx-
> >height-1)))
> +    if (enc->quake3_compat && ((avctx->width)&(avctx->width-
> 1))||((avctx->height)&(avctx->height-1)))
>          av_log(avctx, AV_LOG_ERROR, "Warning: dimensions not power
> of two, this is not supported by quake\n");

These changes appear to be unrelated to bitrate. Consider separating
them into a separate patch.

>  -    if (frame->quality)
> -        enc->lambda = frame->quality - 1;
> -    else
> -        enc->lambda = 2*ROQ_LAMBDA_SCALE;
> +    if (avctx->bit_rate <= ROQ_DEFAULT_MIN_BIT_RATE) {
> +        /* no specific bit rate desired, use frame quality */
> +        if (frame->quality)
> +            enc->lambda = frame->quality - 1;
> +        else
> +            enc->lambda = 2*ROQ_LAMBDA_SCALE;
> +    }

This looks like a bit of a janky way to switch between qscale and
bitrate. Isn't there a way to detect whether an option has been set
explicitly? At the very least this behavior should be documented in
doc/encoders.texi

/Tomas
Victor Luchitz Jan. 22, 2024, 1:38 p.m. UTC | #2
On Mon, Jan 22, 2024 at 4:06 PM Tomas Härdin <git@haerdin.se> wrote:

> mån 2024-01-22 klockan 00:08 +0300 skrev Victor Luchits:
> > One can now use the bitrate option (-b) to specify bit rate of the
> > video
> > stream in the RoQ encoder. The option only becomes effective for
> > values
> > above 800kbit/s, which is roughly equivalent to bandwidth of a 1x-
> > speed
> > CD-ROM drive, minus the bandwidth taken up by stereo DPCM stream.
> > Values
> > below this threshold produce visually inadequate results.
> >
> > Original patch by Joseph Fenton aka Chilly Willy
> >
> > Signed-off-by: Victor Luchits <vluchits@gmail.com>
> > ---
> >   Changelog                |   1 +
> >   libavcodec/roqvideo.h    |   1 +
> >   libavcodec/roqvideodec.c |  15 +++++
> >   libavcodec/roqvideoenc.c | 118 ++++++++++++++++++++++++++++++++++--
> > ---
> >   libavcodec/version.h     |   2 +-
> >   5 files changed, 123 insertions(+), 14 deletions(-)
>
> Fails to apply with git am on current master
>
> > warning: Patch sent with format=flowed; space at the end of lines
> > might be lost.
> > Applying: liavcodec: add bit-rate support to RoQ video encoder
> > error: corrupt patch at line 20
> > Patch failed at 0001 liavcodec: add bit-rate support to RoQ video
> > encoder
>
> Typo: liavcodec
>
> > +    /* Keyframe when no MOT or FCC codes in frame */
> > +    if (s->key_frame) {
> > +        av_log(avctx, AV_LOG_VERBOSE, "\nFound keyframe!\n");
> > +        rframe->pict_type = AV_PICTURE_TYPE_I;
> > +        avpkt->flags |= AV_PKT_FLAG_KEY;
>
> Consider resetting framesSinceKeyframe here
>

Thanks, good catch, gonna look into that.


>
> >  -    if (avctx->width > 65535 || avctx->height > 65535) {
>
> The leading space is probably what makes the patch not apply
>

Found it, thanks.


>
> > -        av_log(avctx, AV_LOG_ERROR, "Dimensions are max %d\n", enc-
> > >quake3_compat ? 32768 : 65535);
> > +    if (enc->quake3_compat && ((avctx->width > 32767 || avctx-
> > >height > 32767))) {
> > +        av_log(avctx, AV_LOG_ERROR, "Dimensions are max %d\n",
> > 32767);
> > +        return AVERROR(EINVAL);
> > +    }
> > +    else if (avctx->width > 65535 || avctx->height > 65535) {
> > +        av_log(avctx, AV_LOG_ERROR, "Dimensions are max %d\n",
> > 65535);
> >          return AVERROR(EINVAL);
> >      }
> >  -    if (((avctx->width)&(avctx->width-1))||((avctx->height)&(avctx-
> > >height-1)))
> > +    if (enc->quake3_compat && ((avctx->width)&(avctx->width-
> > 1))||((avctx->height)&(avctx->height-1)))
> >          av_log(avctx, AV_LOG_ERROR, "Warning: dimensions not power
> > of two, this is not supported by quake\n");
>
> These changes appear to be unrelated to bitrate. Consider separating
> them into a separate patch.
>

Noted.


>
> >  -    if (frame->quality)
> > -        enc->lambda = frame->quality - 1;
> > -    else
> > -        enc->lambda = 2*ROQ_LAMBDA_SCALE;
> > +    if (avctx->bit_rate <= ROQ_DEFAULT_MIN_BIT_RATE) {
> > +        /* no specific bit rate desired, use frame quality */
> > +        if (frame->quality)
> > +            enc->lambda = frame->quality - 1;
> > +        else
> > +            enc->lambda = 2*ROQ_LAMBDA_SCALE;
> > +    }
>
> This looks like a bit of a janky way to switch between qscale and
> bitrate. Isn't there a way to detect whether an option has been set
> explicitly? At the very least this behavior should be documented in
> doc/encoders.texi
>

Originally, the code just checked for bit_rate != AV_CODEC_DEFAULT_BITRATE,
which required including options_table.h, which in turn produced a bunch
of compilation warnings about certain fields being deprecated. None of the
other codecs include that file + many simply check the bit_rate field
against
magic constants. That made me assume that including options_table.h
goes against the common practice.


>
> /Tomas
> _______________________________________________
> 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".
>
Tomas Härdin Jan. 22, 2024, 1:57 p.m. UTC | #3
> > 
> > >  -    if (frame->quality)
> > > -        enc->lambda = frame->quality - 1;
> > > -    else
> > > -        enc->lambda = 2*ROQ_LAMBDA_SCALE;
> > > +    if (avctx->bit_rate <= ROQ_DEFAULT_MIN_BIT_RATE) {
> > > +        /* no specific bit rate desired, use frame quality */
> > > +        if (frame->quality)
> > > +            enc->lambda = frame->quality - 1;
> > > +        else
> > > +            enc->lambda = 2*ROQ_LAMBDA_SCALE;
> > > +    }
> > 
> > This looks like a bit of a janky way to switch between qscale and
> > bitrate. Isn't there a way to detect whether an option has been set
> > explicitly? At the very least this behavior should be documented in
> > doc/encoders.texi
> > 
> 
> Originally, the code just checked for bit_rate !=
> AV_CODEC_DEFAULT_BITRATE,
> which required including options_table.h, which in turn produced a
> bunch
> of compilation warnings about certain fields being deprecated. None
> of the
> other codecs include that file + many simply check the bit_rate field
> against
> magic constants.

grepping for 200000 didn't reveal anything like that. Do you have a
specific example of an encoder that does this?

Perhaps we could move AV_CODEC_DEFAULT_BITRATE somewhere else, to avoid
pulling in a bunch of unrelated stuff. Maybe that doesn't need to hold
up this patch though. Tbh the way bitrate is defaulted to a value,
which makes it impossible to differentiate between a user-supplied -b
200k an no -b at all, is even more janky. The default is also
ridiculously low..

I know some encoders like libvpx allow specifying both quality (-crf)
and bitrate at the same time

/Tomas
Martin Storsjö Jan. 22, 2024, 1:59 p.m. UTC | #4
On Mon, 22 Jan 2024, Tomas Härdin wrote:

>>> >>>  -    if (frame->quality)
>> > > -        enc->lambda = frame->quality - 1;
>> > > -    else
>> > > -        enc->lambda = 2*ROQ_LAMBDA_SCALE;
>> > > +    if (avctx->bit_rate <= ROQ_DEFAULT_MIN_BIT_RATE) {
>> > > +        /* no specific bit rate desired, use frame quality */
>> > > +        if (frame->quality)
>> > > +            enc->lambda = frame->quality - 1;
>> > > +        else
>> > > +            enc->lambda = 2*ROQ_LAMBDA_SCALE;
>> > > +    }
>> > 
>> > This looks like a bit of a janky way to switch between qscale and
>> > bitrate. Isn't there a way to detect whether an option has been set
>> > explicitly? At the very least this behavior should be documented in
>> > doc/encoders.texi
>> > 
>> 
>> Originally, the code just checked for bit_rate !=
>> AV_CODEC_DEFAULT_BITRATE,
>> which required including options_table.h, which in turn produced a
>> bunch
>> of compilation warnings about certain fields being deprecated. None
>> of the
>> other codecs include that file + many simply check the bit_rate field
>> against
>> magic constants.
>
> grepping for 200000 didn't reveal anything like that. Do you have a
> specific example of an encoder that does this?
>
> Perhaps we could move AV_CODEC_DEFAULT_BITRATE somewhere else, to avoid
> pulling in a bunch of unrelated stuff. Maybe that doesn't need to hold
> up this patch though. Tbh the way bitrate is defaulted to a value,
> which makes it impossible to differentiate between a user-supplied -b
> 200k an no -b at all, is even more janky. The default is also
> ridiculously low..
>
> I know some encoders like libvpx allow specifying both quality (-crf)
> and bitrate at the same time

FWIW, it's possible for an encoder to individually override the defaults 
for fields like these. See e.g. x264_defaults in libx264.c, where it 
overrides the default bitrate to zero.

// Martin
Tomas Härdin Jan. 22, 2024, 2:19 p.m. UTC | #5
mån 2024-01-22 klockan 15:59 +0200 skrev Martin Storsjö:
> On Mon, 22 Jan 2024, Tomas Härdin wrote:
> 
> > > > > > >  -    if (frame->quality)
> > > > > -        enc->lambda = frame->quality - 1;
> > > > > -    else
> > > > > -        enc->lambda = 2*ROQ_LAMBDA_SCALE;
> > > > > +    if (avctx->bit_rate <= ROQ_DEFAULT_MIN_BIT_RATE) {
> > > > > +        /* no specific bit rate desired, use frame quality
> > > > > */
> > > > > +        if (frame->quality)
> > > > > +            enc->lambda = frame->quality - 1;
> > > > > +        else
> > > > > +            enc->lambda = 2*ROQ_LAMBDA_SCALE;
> > > > > +    }
> > > > 
> > > > This looks like a bit of a janky way to switch between qscale
> > > > and
> > > > bitrate. Isn't there a way to detect whether an option has been
> > > > set
> > > > explicitly? At the very least this behavior should be
> > > > documented in
> > > > doc/encoders.texi
> > > > 
> > > 
> > > Originally, the code just checked for bit_rate !=
> > > AV_CODEC_DEFAULT_BITRATE,
> > > which required including options_table.h, which in turn produced
> > > a
> > > bunch
> > > of compilation warnings about certain fields being deprecated.
> > > None
> > > of the
> > > other codecs include that file + many simply check the bit_rate
> > > field
> > > against
> > > magic constants.
> > 
> > grepping for 200000 didn't reveal anything like that. Do you have a
> > specific example of an encoder that does this?
> > 
> > Perhaps we could move AV_CODEC_DEFAULT_BITRATE somewhere else, to
> > avoid
> > pulling in a bunch of unrelated stuff. Maybe that doesn't need to
> > hold
> > up this patch though. Tbh the way bitrate is defaulted to a value,
> > which makes it impossible to differentiate between a user-supplied
> > -b
> > 200k an no -b at all, is even more janky. The default is also
> > ridiculously low..
> > 
> > I know some encoders like libvpx allow specifying both quality (-
> > crf)
> > and bitrate at the same time
> 
> FWIW, it's possible for an encoder to individually override the
> defaults 
> for fields like these. See e.g. x264_defaults in libx264.c, where it 
> overrides the default bitrate to zero.

Ooh, didn't know that. That sounds like a decent solution here. Can RoQ
really not do < 800 kbps at all or is it just that it looks bad with
say 256x256 but perhaps more decent with 128x128 or so?

/Tomas
Victor Luchitz Jan. 22, 2024, 4:21 p.m. UTC | #6
Thanks, I will look into it

On Mon, Jan 22, 2024 at 4:59 PM Martin Storsjö <martin@martin.st> wrote:

> On Mon, 22 Jan 2024, Tomas Härdin wrote:
>
> >>> >>>  -    if (frame->quality)
> >> > > -        enc->lambda = frame->quality - 1;
> >> > > -    else
> >> > > -        enc->lambda = 2*ROQ_LAMBDA_SCALE;
> >> > > +    if (avctx->bit_rate <= ROQ_DEFAULT_MIN_BIT_RATE) {
> >> > > +        /* no specific bit rate desired, use frame quality */
> >> > > +        if (frame->quality)
> >> > > +            enc->lambda = frame->quality - 1;
> >> > > +        else
> >> > > +            enc->lambda = 2*ROQ_LAMBDA_SCALE;
> >> > > +    }
> >> >
> >> > This looks like a bit of a janky way to switch between qscale and
> >> > bitrate. Isn't there a way to detect whether an option has been set
> >> > explicitly? At the very least this behavior should be documented in
> >> > doc/encoders.texi
> >> >
> >>
> >> Originally, the code just checked for bit_rate !=
> >> AV_CODEC_DEFAULT_BITRATE,
> >> which required including options_table.h, which in turn produced a
> >> bunch
> >> of compilation warnings about certain fields being deprecated. None
> >> of the
> >> other codecs include that file + many simply check the bit_rate field
> >> against
> >> magic constants.
> >
> > grepping for 200000 didn't reveal anything like that. Do you have a
> > specific example of an encoder that does this?
> >
> > Perhaps we could move AV_CODEC_DEFAULT_BITRATE somewhere else, to avoid
> > pulling in a bunch of unrelated stuff. Maybe that doesn't need to hold
> > up this patch though. Tbh the way bitrate is defaulted to a value,
> > which makes it impossible to differentiate between a user-supplied -b
> > 200k an no -b at all, is even more janky. The default is also
> > ridiculously low..
> >
> > I know some encoders like libvpx allow specifying both quality (-crf)
> > and bitrate at the same time
>
> FWIW, it's possible for an encoder to individually override the defaults
> for fields like these. See e.g. x264_defaults in libx264.c, where it
> overrides the default bitrate to zero.
>
> // Martin
> _______________________________________________
> 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".
>
Victor Luchitz Jan. 22, 2024, 4:36 p.m. UTC | #7
The rationale behind the bit_rate check is that RoQ was originally
designed with early CD-ROM games in mind (The 7th Guest, The
11th Hour), thus the lowest bit_rate that would make any sense in
that context should be based around that. You really want to get
the best picture quality without exceeding the 150KB/s limit of the
drive.

I also did try various lower bitrates, including the default one, on a
variety of files, and they resulted in either extremely poor visuals,
or the encoder spending a lot of extra time trying to downgrade the
picture quality before giving up. Or both. I didn't try extremely low
resolutions such as 128x128, as I don't see why anyone would
want to specify a bitrate that is less than that of a 1x CD-ROM
drive, while also using a viewport the size of a post stamp.

Either way, I'd really appreciate your input on how to deal with this
situation.

On Mon, Jan 22, 2024 at 5:19 PM Tomas Härdin <git@haerdin.se> wrote:

> mån 2024-01-22 klockan 15:59 +0200 skrev Martin Storsjö:
> > On Mon, 22 Jan 2024, Tomas Härdin wrote:
> >
> > > > > > > >  -    if (frame->quality)
> > > > > > -        enc->lambda = frame->quality - 1;
> > > > > > -    else
> > > > > > -        enc->lambda = 2*ROQ_LAMBDA_SCALE;
> > > > > > +    if (avctx->bit_rate <= ROQ_DEFAULT_MIN_BIT_RATE) {
> > > > > > +        /* no specific bit rate desired, use frame quality
> > > > > > */
> > > > > > +        if (frame->quality)
> > > > > > +            enc->lambda = frame->quality - 1;
> > > > > > +        else
> > > > > > +            enc->lambda = 2*ROQ_LAMBDA_SCALE;
> > > > > > +    }
> > > > >
> > > > > This looks like a bit of a janky way to switch between qscale
> > > > > and
> > > > > bitrate. Isn't there a way to detect whether an option has been
> > > > > set
> > > > > explicitly? At the very least this behavior should be
> > > > > documented in
> > > > > doc/encoders.texi
> > > > >
> > > >
> > > > Originally, the code just checked for bit_rate !=
> > > > AV_CODEC_DEFAULT_BITRATE,
> > > > which required including options_table.h, which in turn produced
> > > > a
> > > > bunch
> > > > of compilation warnings about certain fields being deprecated.
> > > > None
> > > > of the
> > > > other codecs include that file + many simply check the bit_rate
> > > > field
> > > > against
> > > > magic constants.
> > >
> > > grepping for 200000 didn't reveal anything like that. Do you have a
> > > specific example of an encoder that does this?
> > >
> > > Perhaps we could move AV_CODEC_DEFAULT_BITRATE somewhere else, to
> > > avoid
> > > pulling in a bunch of unrelated stuff. Maybe that doesn't need to
> > > hold
> > > up this patch though. Tbh the way bitrate is defaulted to a value,
> > > which makes it impossible to differentiate between a user-supplied
> > > -b
> > > 200k an no -b at all, is even more janky. The default is also
> > > ridiculously low..
> > >
> > > I know some encoders like libvpx allow specifying both quality (-
> > > crf)
> > > and bitrate at the same time
> >
> > FWIW, it's possible for an encoder to individually override the
> > defaults
> > for fields like these. See e.g. x264_defaults in libx264.c, where it
> > overrides the default bitrate to zero.
>
> Ooh, didn't know that. That sounds like a decent solution here. Can RoQ
> really not do < 800 kbps at all or is it just that it looks bad with
> say 256x256 but perhaps more decent with 128x128 or so?
>
> /Tomas
> _______________________________________________
> 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".
>
Victor Luchitz Jan. 22, 2024, 6:32 p.m. UTC | #8
On Mon, Jan 22, 2024 at 4:57 PM Tomas Härdin <git@haerdin.se> wrote:

> > >
> > > >  -    if (frame->quality)
> > > > -        enc->lambda = frame->quality - 1;
> > > > -    else
> > > > -        enc->lambda = 2*ROQ_LAMBDA_SCALE;
> > > > +    if (avctx->bit_rate <= ROQ_DEFAULT_MIN_BIT_RATE) {
> > > > +        /* no specific bit rate desired, use frame quality */
> > > > +        if (frame->quality)
> > > > +            enc->lambda = frame->quality - 1;
> > > > +        else
> > > > +            enc->lambda = 2*ROQ_LAMBDA_SCALE;
> > > > +    }
> > >
> > > This looks like a bit of a janky way to switch between qscale and
> > > bitrate. Isn't there a way to detect whether an option has been set
> > > explicitly? At the very least this behavior should be documented in
> > > doc/encoders.texi
> > >
> >
> > Originally, the code just checked for bit_rate !=
> > AV_CODEC_DEFAULT_BITRATE,
> > which required including options_table.h, which in turn produced a
> > bunch
> > of compilation warnings about certain fields being deprecated. None
> > of the
> > other codecs include that file + many simply check the bit_rate field
> > against
> > magic constants.
>
> grepping for 200000 didn't reveal anything like that. Do you have a
> specific example of an encoder that does this?
>

~/ffmpeg/libavcodec# grep -RI bit_rate . | grep 000 | grep -v 1000 | wc -l
38

Most of the cases here are comparisons against magic constants without
any comments about how they were chosen or what they do, none whatsoever.


> Perhaps we could move AV_CODEC_DEFAULT_BITRATE somewhere else, to avoid
> pulling in a bunch of unrelated stuff. Maybe that doesn't need to hold
> up this patch though. Tbh the way bitrate is defaulted to a value,
> which makes it impossible to differentiate between a user-supplied -b
> 200k an no -b at all, is even more janky. The default is also
> ridiculously low..
>
> I know some encoders like libvpx allow specifying both quality (-crf)
> and bitrate at the same time
>
> /Tomas
> _______________________________________________
> 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".
>
Tomas Härdin Jan. 22, 2024, 7:10 p.m. UTC | #9
mån 2024-01-22 klockan 21:32 +0300 skrev Victor Luchitz:
> On Mon, Jan 22, 2024 at 4:57 PM Tomas Härdin <git@haerdin.se> wrote:
> 
> > > > 
> > > > >  -    if (frame->quality)
> > > > > -        enc->lambda = frame->quality - 1;
> > > > > -    else
> > > > > -        enc->lambda = 2*ROQ_LAMBDA_SCALE;
> > > > > +    if (avctx->bit_rate <= ROQ_DEFAULT_MIN_BIT_RATE) {
> > > > > +        /* no specific bit rate desired, use frame quality
> > > > > */
> > > > > +        if (frame->quality)
> > > > > +            enc->lambda = frame->quality - 1;
> > > > > +        else
> > > > > +            enc->lambda = 2*ROQ_LAMBDA_SCALE;
> > > > > +    }
> > > > 
> > > > This looks like a bit of a janky way to switch between qscale
> > > > and
> > > > bitrate. Isn't there a way to detect whether an option has been
> > > > set
> > > > explicitly? At the very least this behavior should be
> > > > documented in
> > > > doc/encoders.texi
> > > > 
> > > 
> > > Originally, the code just checked for bit_rate !=
> > > AV_CODEC_DEFAULT_BITRATE,
> > > which required including options_table.h, which in turn produced
> > > a
> > > bunch
> > > of compilation warnings about certain fields being deprecated.
> > > None
> > > of the
> > > other codecs include that file + many simply check the bit_rate
> > > field
> > > against
> > > magic constants.
> > 
> > grepping for 200000 didn't reveal anything like that. Do you have a
> > specific example of an encoder that does this?
> > 
> 
> ~/ffmpeg/libavcodec# grep -RI bit_rate . | grep 000 | grep -v 1000 |
> wc -l
> 38

Most of these seem to revolve around sane defaults, picking various
submodes or rejecting too high or too low bitrates


> Most of the cases here are comparisons against magic constants
> without
> any comments about how they were chosen or what they do, none
> whatsoever.

I would suggest adding a different default as Martin suggests. If users
really want shitty encodes I see no reason to deny them. I could see it
being useful for modders.

You might want an option for the tolerance stuff too

/Tomas
Victor Luchitz Jan. 22, 2024, 7:16 p.m. UTC | #10
I've just posted a new version of the patch that addresses most (if not
all) of the comments from previous reviews.

On Mon, Jan 22, 2024 at 10:10 PM Tomas Härdin <git@haerdin.se> wrote:

> mån 2024-01-22 klockan 21:32 +0300 skrev Victor Luchitz:
> > On Mon, Jan 22, 2024 at 4:57 PM Tomas Härdin <git@haerdin.se> wrote:
> >
> > > > >
> > > > > >  -    if (frame->quality)
> > > > > > -        enc->lambda = frame->quality - 1;
> > > > > > -    else
> > > > > > -        enc->lambda = 2*ROQ_LAMBDA_SCALE;
> > > > > > +    if (avctx->bit_rate <= ROQ_DEFAULT_MIN_BIT_RATE) {
> > > > > > +        /* no specific bit rate desired, use frame quality
> > > > > > */
> > > > > > +        if (frame->quality)
> > > > > > +            enc->lambda = frame->quality - 1;
> > > > > > +        else
> > > > > > +            enc->lambda = 2*ROQ_LAMBDA_SCALE;
> > > > > > +    }
> > > > >
> > > > > This looks like a bit of a janky way to switch between qscale
> > > > > and
> > > > > bitrate. Isn't there a way to detect whether an option has been
> > > > > set
> > > > > explicitly? At the very least this behavior should be
> > > > > documented in
> > > > > doc/encoders.texi
> > > > >
> > > >
> > > > Originally, the code just checked for bit_rate !=
> > > > AV_CODEC_DEFAULT_BITRATE,
> > > > which required including options_table.h, which in turn produced
> > > > a
> > > > bunch
> > > > of compilation warnings about certain fields being deprecated.
> > > > None
> > > > of the
> > > > other codecs include that file + many simply check the bit_rate
> > > > field
> > > > against
> > > > magic constants.
> > >
> > > grepping for 200000 didn't reveal anything like that. Do you have a
> > > specific example of an encoder that does this?
> > >
> >
> > ~/ffmpeg/libavcodec# grep -RI bit_rate . | grep 000 | grep -v 1000 |
> > wc -l
> > 38
>
> Most of these seem to revolve around sane defaults, picking various
> submodes or rejecting too high or too low bitrates
>
>
> > Most of the cases here are comparisons against magic constants
> > without
> > any comments about how they were chosen or what they do, none
> > whatsoever.
>
> I would suggest adding a different default as Martin suggests. If users
> really want shitty encodes I see no reason to deny them. I could see it
> being useful for modders.
>
> You might want an option for the tolerance stuff too
>
> /Tomas
> _______________________________________________
> 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".
>
diff mbox series

Patch

diff --git a/Changelog b/Changelog
index c40b6d08fd..6974312f9d 100644
--- a/Changelog
+++ b/Changelog
@@ -22,6 +22,7 @@  version <next>:
  - ffmpeg CLI -bsf option may now be used for input as well as output
  - ffmpeg CLI options may now be used as -/opt <path>, which is equivalent
    to -opt <contents of file <path>>
+- RoQ video bit rate option support
  
  version 6.1:
  - libaribcaption decoder
diff --git a/libavcodec/roqvideo.h b/libavcodec/roqvideo.h
index 2c2e42884d..6d30bcaada 100644
--- a/libavcodec/roqvideo.h
+++ b/libavcodec/roqvideo.h
@@ -43,6 +43,7 @@  typedef struct RoqContext {
      AVFrame *last_frame;
      AVFrame *current_frame;
      int width, height;
+    int key_frame;
  
      roq_cell cb2x2[256];
      roq_qcell cb4x4[256];
diff --git a/libavcodec/roqvideodec.c b/libavcodec/roqvideodec.c
index bfc69a65c9..07d6b8bb8f 100644
--- a/libavcodec/roqvideodec.c
+++ b/libavcodec/roqvideodec.c
@@ -70,6 +70,7 @@  static void roqvideo_decode_frame(RoqContext *ri, GetByteContext *gb)
  
      chunk_start = bytestream2_tell(gb);
      xpos = ypos = 0;
+    ri->key_frame = 1;
  
      if (chunk_size > bytestream2_get_bytes_left(gb)) {
          av_log(ri->logctx, AV_LOG_ERROR, "Chunk does not fit in input buffer\n");
@@ -92,12 +93,14 @@  static void roqvideo_decode_frame(RoqContext *ri, GetByteContext *gb)
  
                  switch(vqid) {
                  case RoQ_ID_MOT:
+                    ri->key_frame = 0;
                      break;
                  case RoQ_ID_FCC: {
                      int byte = bytestream2_get_byte(gb);
                      mx = 8 - (byte >> 4) - ((signed char) (chunk_arg >> 8));
                      my = 8 - (byte & 0xf) - ((signed char) chunk_arg);
                      ff_apply_motion_8x8(ri, xp, yp, mx, my);
+                    ri->key_frame = 0;
                      break;
                  }
                  case RoQ_ID_SLD:
@@ -125,12 +128,14 @@  static void roqvideo_decode_frame(RoqContext *ri, GetByteContext *gb)
                          vqflg_pos--;
                          switch(vqid) {
                          case RoQ_ID_MOT:
+                            ri->key_frame = 0;
                              break;
                          case RoQ_ID_FCC: {
                              int byte = bytestream2_get_byte(gb);
                              mx = 8 - (byte >> 4) - ((signed char) (chunk_arg >> 8));
                              my = 8 - (byte & 0xf) - ((signed char) chunk_arg);
                              ff_apply_motion_4x4(ri, x, y, mx, my);
+                            ri->key_frame = 0;
                              break;
                          }
                          case RoQ_ID_SLD:
@@ -214,6 +219,16 @@  static int roq_decode_frame(AVCodecContext *avctx, AVFrame *rframe,
  
      if ((ret = av_frame_ref(rframe, s->current_frame)) < 0)
          return ret;
+
+    /* Keyframe when no MOT or FCC codes in frame */
+    if (s->key_frame) {
+        av_log(avctx, AV_LOG_VERBOSE, "\nFound keyframe!\n");
+        rframe->pict_type = AV_PICTURE_TYPE_I;
+        avpkt->flags |= AV_PKT_FLAG_KEY;
+    } else {
+        rframe->pict_type = AV_PICTURE_TYPE_P;
+    }
+
      *got_frame      = 1;
  
      /* shuffle frames */
diff --git a/libavcodec/roqvideoenc.c b/libavcodec/roqvideoenc.c
index 0933abf4f9..bcead80bbd 100644
--- a/libavcodec/roqvideoenc.c
+++ b/libavcodec/roqvideoenc.c
@@ -79,6 +79,9 @@ 
  /* The cast is useful when multiplying it by INT_MAX */
  #define ROQ_LAMBDA_SCALE ((uint64_t) FF_LAMBDA_SCALE)
  
+/* The default minimum bitrate, set around the value of a 1x speed CD-ROM drive */
+#define ROQ_DEFAULT_MIN_BIT_RATE 800*1024
+
  typedef struct RoqCodebooks {
      int numCB4;
      int numCB2;
@@ -136,6 +139,8 @@  typedef struct RoqEncContext {
      struct ELBGContext *elbg;
      AVLFG randctx;
      uint64_t lambda;
+    uint64_t last_lambda;
+    int lambda_delta;
  
      motion_vect *this_motion4;
      motion_vect *last_motion4;
@@ -887,8 +892,9 @@  static int generate_new_codebooks(RoqEncContext *enc)
      return 0;
  }
  
-static int roq_encode_video(RoqEncContext *enc)
+static int roq_encode_video(AVCodecContext *avctx)
  {
+    RoqEncContext *const enc = avctx->priv_data;
      RoqTempData *const tempData = &enc->tmp_data;
      RoqContext *const roq = &enc->common;
      int ret;
@@ -910,14 +916,14 @@  static int roq_encode_video(RoqEncContext *enc)
  
      /* Quake 3 can't handle chunks bigger than 65535 bytes */
      if (tempData->mainChunkSize/8 > 65535 && enc->quake3_compat) {
-        if (enc->lambda > 100000) {
+        if (enc->lambda > 100000000) {
              av_log(roq->logctx, AV_LOG_ERROR, "Cannot encode video in Quake compatible form\n");
              return AVERROR(EINVAL);
          }
          av_log(roq->logctx, AV_LOG_ERROR,
                 "Warning, generated a frame too big for Quake (%d > 65535), "
-               "now switching to a bigger qscale value.\n",
-               tempData->mainChunkSize/8);
+               "now switching to a bigger qscale value (%d).\n",
+               tempData->mainChunkSize/8, (int)enc->lambda);
          enc->lambda *= 1.5;
          tempData->mainChunkSize = 0;
          memset(tempData->used_option, 0, sizeof(tempData->used_option));
@@ -931,6 +937,80 @@  static int roq_encode_video(RoqEncContext *enc)
  
      remap_codebooks(enc);
  
+    /* bit rate control code - could make encoding very slow */
+    if (avctx->bit_rate > ROQ_DEFAULT_MIN_BIT_RATE) {
+        /* a bit rate has been specified - try to match it */
+        int ftotal = (tempData->mainChunkSize / 8 + tempData->numCB2*6 + tempData->numCB4*4) * avctx->time_base.den * 8;
+        int64_t tol = avctx->bit_rate_tolerance;
+
+        /* tolerance > bit rate, set to 5% of the bit rate */
+        if (tol > avctx->bit_rate)
+            tol = avctx->bit_rate / 20;
+
+        av_log(roq->logctx, AV_LOG_VERBOSE,
+               "\nDesired bit rate (%d kbps), "
+               "Bit rate tolerance (%d), "
+               "Frame rate (%d)\n",
+               (int)avctx->bit_rate, (int)tol, avctx->time_base.den);
+
+        if (ftotal > (avctx->bit_rate + tol)) {
+            /* frame is too big - increase qscale */
+            if (enc->lambda > 100000000) {
+                av_log(roq->logctx, AV_LOG_ERROR, "\nCannot encode video at desired bitrate\n");
+                return AVERROR(EINVAL);
+            }
+            enc->lambda_delta = enc->lambda_delta <= 0 ? 1 : enc->lambda_delta < 65536 ? enc->lambda_delta*2 : 65536;
+            enc->last_lambda = enc->lambda;
+            enc->lambda += enc->lambda_delta;
+            av_log(roq->logctx, AV_LOG_INFO,
+                   "\nGenerated a frame too big for desired bit rate (%d kbps), "
+                   "now switching to a bigger qscale value (%d).\n",
+                   ftotal / 1000, (int)enc->lambda);
+            tempData->mainChunkSize = 0;
+            memset(tempData->used_option, 0, sizeof(tempData->used_option));
+            memset(tempData->codebooks.usedCB4, 0,
+                   sizeof(tempData->codebooks.usedCB4));
+            memset(tempData->codebooks.usedCB2, 0,
+                   sizeof(tempData->codebooks.usedCB2));
+
+            goto retry_encode;
+        } else if (ftotal < (avctx->bit_rate - tol)) {
+            /* frame is too small - decrease qscale */
+            if (enc->lambda <= 1) {
+                av_log(roq->logctx, AV_LOG_WARNING,
+                       "\nGenerated a frame too small for desired bit rate (%d kbps), "
+                       "qscale value cannot be lowered any further (%d).\n",
+                       ftotal / 1000, (int)enc->lambda);
+            } else if ((enc->lambda - enc->last_lambda) == 1) {
+                av_log(roq->logctx, AV_LOG_WARNING,
+                       "\nCannot find qscale that gives desired bit rate within desired tolerance, "
+                       "using lower bitrate (%d kbps) with higher qscale value (%d).\n",
+                       ftotal / 1000, (int)enc->lambda);
+            } else {
+                enc->lambda_delta = 0;
+                if (enc->lambda == enc->last_lambda) {
+                    enc->lambda >>= 1;
+                    enc->last_lambda = enc->lambda;
+                } else {
+                    enc->lambda = enc->last_lambda;
+                    //enc->lambda *= (float)(tempData->mainChunkSize * avctx->time_base.den) / avctx->bit_rate;
+                    av_log(roq->logctx, AV_LOG_INFO,
+                           "\nGenerated a frame too small for desired bit rate (%d kbps), "
+                           "reverting lambda and using smaller inc on qscale (%d).\n",
+                           ftotal / 1000, (int)enc->lambda);
+                }
+                tempData->mainChunkSize = 0;
+                memset(tempData->used_option, 0, sizeof(tempData->used_option));
+                memset(tempData->codebooks.usedCB4, 0,
+                       sizeof(tempData->codebooks.usedCB4));
+                memset(tempData->codebooks.usedCB2, 0,
+                       sizeof(tempData->codebooks.usedCB2));
+
+                goto retry_encode;
+            }
+        }
+    }
+
      write_codebooks(enc);
  
      reconstruct_and_encode_image(enc, roq->width, roq->height,
@@ -980,19 +1060,26 @@  static av_cold int roq_encode_init(AVCodecContext *avctx)
          return AVERROR(EINVAL);
      }
  
-    if (avctx->width > 65535 || avctx->height > 65535) {
-        av_log(avctx, AV_LOG_ERROR, "Dimensions are max %d\n", enc->quake3_compat ? 32768 : 65535);
+    if (enc->quake3_compat && ((avctx->width > 32767 || avctx->height > 32767))) {
+        av_log(avctx, AV_LOG_ERROR, "Dimensions are max %d\n", 32767);
+        return AVERROR(EINVAL);
+    }
+    else if (avctx->width > 65535 || avctx->height > 65535) {
+        av_log(avctx, AV_LOG_ERROR, "Dimensions are max %d\n", 65535);
          return AVERROR(EINVAL);
      }
  
-    if (((avctx->width)&(avctx->width-1))||((avctx->height)&(avctx->height-1)))
+    if (enc->quake3_compat && ((avctx->width)&(avctx->width-1))||((avctx->height)&(avctx->height-1)))
          av_log(avctx, AV_LOG_ERROR, "Warning: dimensions not power of two, this is not supported by quake\n");
  
      roq->width  = avctx->width;
      roq->height = avctx->height;
  
+    enc->lambda = 2*ROQ_LAMBDA_SCALE;
      enc->framesSinceKeyframe = 0;
      enc->first_frame = 1;
+    enc->last_lambda = 1;
+    enc->lambda_delta = 0;
  
      roq->last_frame    = av_frame_alloc();
      roq->current_frame = av_frame_alloc();
@@ -1043,9 +1130,11 @@  static void roq_write_video_info_chunk(RoqEncContext *enc)
      /* Height */
      bytestream_put_le16(&enc->out_buf, enc->common.height);
  
-    /* Unused in Quake 3, mimics the output of the real encoder */
+    /* Quake 3: starting size for cell subdivision */
      bytestream_put_byte(&enc->out_buf, 0x08);
      bytestream_put_byte(&enc->out_buf, 0x00);
+
+    /* Quake 3: ending size for cell subdivision */
      bytestream_put_byte(&enc->out_buf, 0x04);
      bytestream_put_byte(&enc->out_buf, 0x00);
  }
@@ -1059,10 +1148,13 @@  static int roq_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
  
      enc->frame_to_enc = frame;
  
-    if (frame->quality)
-        enc->lambda = frame->quality - 1;
-    else
-        enc->lambda = 2*ROQ_LAMBDA_SCALE;
+    if (avctx->bit_rate <= ROQ_DEFAULT_MIN_BIT_RATE) {
+        /* no specific bit rate desired, use frame quality */
+        if (frame->quality)
+            enc->lambda = frame->quality - 1;
+        else
+            enc->lambda = 2*ROQ_LAMBDA_SCALE;
+    }
  
      /* 138 bits max per 8x8 block +
       *     256 codebooks*(6 bytes 2x2 + 4 bytes 4x4) + 8 bytes frame header */
@@ -1089,7 +1181,7 @@  static int roq_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
      }
  
      /* Encode the actual frame */
-    ret = roq_encode_video(enc);
+    ret = roq_encode_video(avctx);
      if (ret < 0)
          return ret;
  
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 376388c5bb..581151cda7 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -30,7 +30,7 @@ 
  #include "version_major.h"
  
  #define LIBAVCODEC_VERSION_MINOR  37
-#define LIBAVCODEC_VERSION_MICRO 100
+#define LIBAVCODEC_VERSION_MICRO 101
  
  #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
                                                 LIBAVCODEC_VERSION_MINOR, \