1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2025-08-04 22:03:09 +02:00

avcodec/dvbsubenc: add a min_bpp option to work around some decoders

As noted in the code in several places, some DVB subtitle decoders
don't handle 2bpp color. This patch adds a min_bpp option which
sets a lower bound on the number of bits-per-pixel in the color
tables. It defaults to 4 to avoid the problematic 2bpp decoders.

Signed-off-by: Ronan Waide <waider@waider.ie>
This commit is contained in:
Ronan Waide
2025-08-04 19:45:45 +01:00
parent a84d060aa1
commit 09828e7fe2
3 changed files with 63 additions and 11 deletions

View File

@ -4602,6 +4602,25 @@ Reduces detail but attempts to preserve color at extremely low bitrates.
@chapter Subtitles Encoders
@c man begin SUBTITLES ENCODERS
@section dvbsub
This codec encodes the bitmap subtitle format that is used in DVB
broadcasts and recordings. The bitmaps are typically embedded in a
container such as MPEG-TS as a separate stream.
@subsection Options
@table @option
@item min_bpp @var{integer (2, 4, or 8)}
Set a minimum bits-per-pixel value for the subtitle color lookup tables.
DVB supports 2, 4, and 8 bits-per-pixel color lookup tables. This
option enables forcing a particular bits-per-pixel value regardless of
the number of colors. Since not all players support or properly
support 2 bits-per-pixel, this value defaults to 4.
@end table
@section dvdsub
This codec encodes the bitmap subtitle format that is used in DVDs.

View File

@ -22,9 +22,12 @@
#include "bytestream.h"
#include "codec_internal.h"
#include "libavutil/colorspace.h"
#include "libavutil/opt.h"
typedef struct DVBSubtitleContext {
AVClass * class;
int object_version;
int min_bpp;
} DVBSubtitleContext;
#define PUTBITS2(val)\
@ -275,13 +278,24 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
{
DVBSubtitleContext *s = avctx->priv_data;
uint8_t *q, *pseg_len;
int page_id, region_id, clut_id, object_id, i, bpp_index, page_state;
int page_id, region_id, clut_id, object_id, i, bpp_index, page_state, min_colors;
q = outbuf;
page_id = 1;
switch(s->min_bpp) {
case 2:
case 4:
case 8:
min_colors = 1 << s->min_bpp;
break;
default:
av_log(avctx, AV_LOG_ERROR, "Invalid min_bpp value %d.\n", s->min_bpp);
return AVERROR(EINVAL);
}
if (h->num_rects && !h->rects)
return AVERROR(EINVAL);
@ -331,14 +345,15 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
if (h->num_rects) {
for (clut_id = 0; clut_id < h->num_rects; clut_id++) {
/* CLUT segment */
int nb_colors = FFMAX(min_colors, h->rects[clut_id]->nb_colors);
if (h->rects[clut_id]->nb_colors <= 4U) {
if (nb_colors <= 4U) {
/* 2 bpp, some decoders do not support it correctly */
bpp_index = 0;
} else if (h->rects[clut_id]->nb_colors <= 16U) {
} else if (nb_colors <= 16U) {
/* 4 bpp, standard encoding */
bpp_index = 1;
} else if (h->rects[clut_id]->nb_colors <= 256U) {
} else if (nb_colors <= 256U) {
/* 8 bpp, standard encoding */
bpp_index = 2;
} else {
@ -384,14 +399,15 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
for (region_id = 0; region_id < h->num_rects; region_id++) {
/* region composition segment */
int nb_colors = FFMAX(min_colors, h->rects[region_id]->nb_colors);
if (h->rects[region_id]->nb_colors <= 4) {
if (nb_colors <= 4) {
/* 2 bpp, some decoders do not support it correctly */
bpp_index = 0;
} else if (h->rects[region_id]->nb_colors <= 16) {
} else if (nb_colors <= 16) {
/* 4 bpp, standard encoding */
bpp_index = 1;
} else if (h->rects[region_id]->nb_colors <= 256) {
} else if (nb_colors <= 256) {
/* 8 bpp, standard encoding */
bpp_index = 2;
} else {
@ -427,17 +443,19 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
const uint8_t *bitmap, int linesize,
int w, int h);
int nb_colors = FFMAX(min_colors, h->rects[object_id]->nb_colors);
if (buf_size < 13)
return AVERROR_BUFFER_TOO_SMALL;
/* bpp_index maths */
if (h->rects[object_id]->nb_colors <= 4) {
if (nb_colors <= 4) {
/* 2 bpp, some decoders do not support it correctly */
dvb_encode_rle = dvb_encode_rle2;
} else if (h->rects[object_id]->nb_colors <= 16) {
} else if (nb_colors <= 16) {
/* 4 bpp, standard encoding */
dvb_encode_rle = dvb_encode_rle4;
} else if (h->rects[object_id]->nb_colors <= 256) {
} else if (nb_colors <= 256) {
/* 8 bpp, standard encoding */
dvb_encode_rle = dvb_encode_rle8;
} else {
@ -509,6 +527,20 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
return q - outbuf;
}
#define OFFSET(x) offsetof(DVBSubtitleContext, x)
#define SE AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
{"min_bpp", "minimum bits-per-pixel for subtitle colors (2, 4 or 8)", OFFSET(min_bpp), AV_OPT_TYPE_INT, {.i64 = 4}, 2, 8, SE},
{ NULL },
};
static const AVClass dvbsubenc_class = {
.class_name = "DVBSUB subtitle encoder",
.item_name = av_default_item_name,
.option = options,
.version = LIBAVUTIL_VERSION_INT,
};
const FFCodec ff_dvbsub_encoder = {
.p.name = "dvbsub",
CODEC_LONG_NAME("DVB subtitles"),
@ -516,4 +548,5 @@ const FFCodec ff_dvbsub_encoder = {
.p.id = AV_CODEC_ID_DVB_SUBTITLE,
.priv_data_size = sizeof(DVBSubtitleContext),
FF_CODEC_ENCODE_SUB_CB(dvbsub_encode),
.p.priv_class = &dvbsubenc_class,
};

View File

@ -30,7 +30,7 @@
#include "version_major.h"
#define LIBAVCODEC_VERSION_MINOR 8
#define LIBAVCODEC_VERSION_MICRO 100
#define LIBAVCODEC_VERSION_MICRO 101
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
LIBAVCODEC_VERSION_MINOR, \