mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-07 11:13:41 +02:00
4243da4ff4
This is possible, because every given FFCodec has to implement exactly one of these. Doing so decreases sizeof(FFCodec) and therefore decreases the size of the binary. Notice that in case of position-independent code the decrease is in .data.rel.ro, so that this translates to decreased memory consumption. Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
517 lines
16 KiB
C
517 lines
16 KiB
C
/*
|
|
* DVB subtitle encoding
|
|
* Copyright (c) 2005 Fabrice Bellard
|
|
*
|
|
* This file is part of FFmpeg.
|
|
*
|
|
* FFmpeg is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* FFmpeg is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with FFmpeg; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
#include "avcodec.h"
|
|
#include "bytestream.h"
|
|
#include "codec_internal.h"
|
|
#include "libavutil/colorspace.h"
|
|
|
|
typedef struct DVBSubtitleContext {
|
|
int object_version;
|
|
} DVBSubtitleContext;
|
|
|
|
#define PUTBITS2(val)\
|
|
{\
|
|
bitbuf |= (val) << bitcnt;\
|
|
bitcnt -= 2;\
|
|
if (bitcnt < 0) {\
|
|
bitcnt = 6;\
|
|
*q++ = bitbuf;\
|
|
bitbuf = 0;\
|
|
}\
|
|
}
|
|
|
|
static int dvb_encode_rle2(uint8_t **pq, int buf_size,
|
|
const uint8_t *bitmap, int linesize,
|
|
int w, int h)
|
|
{
|
|
uint8_t *q, *line_begin;
|
|
unsigned int bitbuf;
|
|
int bitcnt;
|
|
int x, y, len, x1, v, color;
|
|
|
|
q = *pq;
|
|
|
|
for(y = 0; y < h; y++) {
|
|
// Worst case line is 3 bits per value + 4 bytes overhead
|
|
if (buf_size * 8 < w * 3 + 32)
|
|
return AVERROR_BUFFER_TOO_SMALL;
|
|
line_begin = q;
|
|
*q++ = 0x10;
|
|
bitbuf = 0;
|
|
bitcnt = 6;
|
|
|
|
x = 0;
|
|
while (x < w) {
|
|
x1 = x;
|
|
color = bitmap[x1++];
|
|
while (x1 < w && bitmap[x1] == color)
|
|
x1++;
|
|
len = x1 - x;
|
|
if (color == 0 && len == 2) {
|
|
PUTBITS2(0);
|
|
PUTBITS2(0);
|
|
PUTBITS2(1);
|
|
} else if (len >= 3 && len <= 10) {
|
|
v = len - 3;
|
|
PUTBITS2(0);
|
|
PUTBITS2((v >> 2) | 2);
|
|
PUTBITS2(v & 3);
|
|
PUTBITS2(color);
|
|
} else if (len >= 12 && len <= 27) {
|
|
v = len - 12;
|
|
PUTBITS2(0);
|
|
PUTBITS2(0);
|
|
PUTBITS2(2);
|
|
PUTBITS2(v >> 2);
|
|
PUTBITS2(v & 3);
|
|
PUTBITS2(color);
|
|
} else if (len >= 29) {
|
|
/* length = 29 ... 284 */
|
|
if (len > 284)
|
|
len = 284;
|
|
v = len - 29;
|
|
PUTBITS2(0);
|
|
PUTBITS2(0);
|
|
PUTBITS2(3);
|
|
PUTBITS2((v >> 6));
|
|
PUTBITS2((v >> 4) & 3);
|
|
PUTBITS2((v >> 2) & 3);
|
|
PUTBITS2(v & 3);
|
|
PUTBITS2(color);
|
|
} else {
|
|
PUTBITS2(color);
|
|
if (color == 0) {
|
|
PUTBITS2(1);
|
|
}
|
|
len = 1;
|
|
}
|
|
x += len;
|
|
}
|
|
/* end of line */
|
|
PUTBITS2(0);
|
|
PUTBITS2(0);
|
|
PUTBITS2(0);
|
|
if (bitcnt != 6) {
|
|
*q++ = bitbuf;
|
|
}
|
|
*q++ = 0xf0;
|
|
bitmap += linesize;
|
|
buf_size -= q - line_begin;
|
|
}
|
|
len = q - *pq;
|
|
*pq = q;
|
|
return len;
|
|
}
|
|
|
|
#define PUTBITS4(val)\
|
|
{\
|
|
bitbuf |= (val) << bitcnt;\
|
|
bitcnt -= 4;\
|
|
if (bitcnt < 0) {\
|
|
bitcnt = 4;\
|
|
*q++ = bitbuf;\
|
|
bitbuf = 0;\
|
|
}\
|
|
}
|
|
|
|
/* some DVB decoders only implement 4 bits/pixel */
|
|
static int dvb_encode_rle4(uint8_t **pq, int buf_size,
|
|
const uint8_t *bitmap, int linesize,
|
|
int w, int h)
|
|
{
|
|
uint8_t *q, *line_begin;
|
|
unsigned int bitbuf;
|
|
int bitcnt;
|
|
int x, y, len, x1, v, color;
|
|
|
|
q = *pq;
|
|
|
|
for(y = 0; y < h; y++) {
|
|
// Worst case line is 6 bits per value, + 4 bytes overhead
|
|
if (buf_size * 8 < w * 6 + 32)
|
|
return AVERROR_BUFFER_TOO_SMALL;
|
|
line_begin = q;
|
|
*q++ = 0x11;
|
|
bitbuf = 0;
|
|
bitcnt = 4;
|
|
|
|
x = 0;
|
|
while (x < w) {
|
|
x1 = x;
|
|
color = bitmap[x1++];
|
|
while (x1 < w && bitmap[x1] == color)
|
|
x1++;
|
|
len = x1 - x;
|
|
if (color == 0 && len == 2) {
|
|
PUTBITS4(0);
|
|
PUTBITS4(0xd);
|
|
} else if (color == 0 && (len >= 3 && len <= 9)) {
|
|
PUTBITS4(0);
|
|
PUTBITS4(len - 2);
|
|
} else if (len >= 4 && len <= 7) {
|
|
PUTBITS4(0);
|
|
PUTBITS4(8 + len - 4);
|
|
PUTBITS4(color);
|
|
} else if (len >= 9 && len <= 24) {
|
|
PUTBITS4(0);
|
|
PUTBITS4(0xe);
|
|
PUTBITS4(len - 9);
|
|
PUTBITS4(color);
|
|
} else if (len >= 25) {
|
|
if (len > 280)
|
|
len = 280;
|
|
v = len - 25;
|
|
PUTBITS4(0);
|
|
PUTBITS4(0xf);
|
|
PUTBITS4(v >> 4);
|
|
PUTBITS4(v & 0xf);
|
|
PUTBITS4(color);
|
|
} else {
|
|
PUTBITS4(color);
|
|
if (color == 0) {
|
|
PUTBITS4(0xc);
|
|
}
|
|
len = 1;
|
|
}
|
|
x += len;
|
|
}
|
|
/* end of line */
|
|
PUTBITS4(0);
|
|
PUTBITS4(0);
|
|
if (bitcnt != 4) {
|
|
*q++ = bitbuf;
|
|
}
|
|
*q++ = 0xf0;
|
|
bitmap += linesize;
|
|
buf_size -= q - line_begin;
|
|
}
|
|
len = q - *pq;
|
|
*pq = q;
|
|
return len;
|
|
}
|
|
|
|
static int dvb_encode_rle8(uint8_t **pq, int buf_size,
|
|
const uint8_t *bitmap, int linesize,
|
|
int w, int h)
|
|
{
|
|
uint8_t *q, *line_begin;
|
|
int x, y, len, x1, color;
|
|
|
|
q = *pq;
|
|
|
|
for (y = 0; y < h; y++) {
|
|
// Worst case line is 12 bits per value, + 3 bytes overhead
|
|
if (buf_size * 8 < w * 12 + 24)
|
|
return AVERROR_BUFFER_TOO_SMALL;
|
|
line_begin = q;
|
|
*q++ = 0x12;
|
|
|
|
x = 0;
|
|
while (x < w) {
|
|
x1 = x;
|
|
color = bitmap[x1++];
|
|
while (x1 < w && bitmap[x1] == color)
|
|
x1++;
|
|
len = x1 - x;
|
|
if (len == 1 && color) {
|
|
// 00000001 to 11111111 1 pixel in colour x
|
|
*q++ = color;
|
|
} else {
|
|
if (color == 0x00) {
|
|
// 00000000 0LLLLLLL L pixels (1-127) in colour 0 (L > 0)
|
|
len = FFMIN(len, 127);
|
|
*q++ = 0x00;
|
|
*q++ = len;
|
|
} else if (len > 2) {
|
|
// 00000000 1LLLLLLL CCCCCCCC L pixels (3-127) in colour C (L > 2)
|
|
len = FFMIN(len, 127);
|
|
*q++ = 0x00;
|
|
*q++ = 0x80+len;
|
|
*q++ = color;
|
|
}
|
|
else if (len == 2) {
|
|
*q++ = color;
|
|
*q++ = color;
|
|
} else {
|
|
*q++ = color;
|
|
len = 1;
|
|
}
|
|
}
|
|
x += len;
|
|
}
|
|
/* end of line */
|
|
// 00000000 end of 8-bit/pixel_code_string
|
|
*q++ = 0x00;
|
|
*q++ = 0xf0;
|
|
bitmap += linesize;
|
|
buf_size -= q - line_begin;
|
|
}
|
|
len = q - *pq;
|
|
*pq = q;
|
|
return len;
|
|
}
|
|
|
|
static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
|
|
const AVSubtitle *h)
|
|
{
|
|
DVBSubtitleContext *s = avctx->priv_data;
|
|
uint8_t *q, *pseg_len;
|
|
int page_id, region_id, clut_id, object_id, i, bpp_index, page_state;
|
|
|
|
|
|
q = outbuf;
|
|
|
|
page_id = 1;
|
|
|
|
if (h->num_rects && !h->rects)
|
|
return AVERROR(EINVAL);
|
|
|
|
if (avctx->width > 0 && avctx->height > 0) {
|
|
if (buf_size < 11)
|
|
return AVERROR_BUFFER_TOO_SMALL;
|
|
/* display definition segment */
|
|
*q++ = 0x0f; /* sync_byte */
|
|
*q++ = 0x14; /* segment_type */
|
|
bytestream_put_be16(&q, page_id);
|
|
pseg_len = q;
|
|
q += 2; /* segment length */
|
|
*q++ = 0x00; /* dds version number & display window flag */
|
|
bytestream_put_be16(&q, avctx->width - 1); /* display width */
|
|
bytestream_put_be16(&q, avctx->height - 1); /* display height */
|
|
bytestream_put_be16(&pseg_len, q - pseg_len - 2);
|
|
buf_size -= 11;
|
|
}
|
|
|
|
/* page composition segment */
|
|
|
|
if (buf_size < 8 + h->num_rects * 6)
|
|
return AVERROR_BUFFER_TOO_SMALL;
|
|
*q++ = 0x0f; /* sync_byte */
|
|
*q++ = 0x10; /* segment_type */
|
|
bytestream_put_be16(&q, page_id);
|
|
pseg_len = q;
|
|
q += 2; /* segment length */
|
|
*q++ = 30; /* page_timeout (seconds) */
|
|
page_state = 2; /* mode change */
|
|
/* page_version = 0 + page_state */
|
|
*q++ = (s->object_version << 4) | (page_state << 2) | 3;
|
|
|
|
for (region_id = 0; region_id < h->num_rects; region_id++) {
|
|
*q++ = region_id;
|
|
*q++ = 0xff; /* reserved */
|
|
bytestream_put_be16(&q, h->rects[region_id]->x); /* left pos */
|
|
bytestream_put_be16(&q, h->rects[region_id]->y); /* top pos */
|
|
}
|
|
|
|
bytestream_put_be16(&pseg_len, q - pseg_len - 2);
|
|
buf_size -= 8 + h->num_rects * 6;
|
|
|
|
if (h->num_rects) {
|
|
for (clut_id = 0; clut_id < h->num_rects; clut_id++) {
|
|
if (buf_size < 6 + h->rects[clut_id]->nb_colors * 6)
|
|
return AVERROR_BUFFER_TOO_SMALL;
|
|
|
|
/* CLUT segment */
|
|
|
|
if (h->rects[clut_id]->nb_colors <= 4) {
|
|
/* 2 bpp, some decoders do not support it correctly */
|
|
bpp_index = 0;
|
|
} else if (h->rects[clut_id]->nb_colors <= 16) {
|
|
/* 4 bpp, standard encoding */
|
|
bpp_index = 1;
|
|
} else if (h->rects[clut_id]->nb_colors <= 256) {
|
|
/* 8 bpp, standard encoding */
|
|
bpp_index = 2;
|
|
} else {
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
|
|
/* CLUT segment */
|
|
*q++ = 0x0f; /* sync byte */
|
|
*q++ = 0x12; /* CLUT definition segment */
|
|
bytestream_put_be16(&q, page_id);
|
|
pseg_len = q;
|
|
q += 2; /* segment length */
|
|
*q++ = clut_id;
|
|
*q++ = (0 << 4) | 0xf; /* version = 0 */
|
|
|
|
for(i = 0; i < h->rects[clut_id]->nb_colors; i++) {
|
|
*q++ = i; /* clut_entry_id */
|
|
*q++ = (1 << (7 - bpp_index)) | (0xf << 1) | 1; /* 2 bits/pixel full range */
|
|
{
|
|
int a, r, g, b;
|
|
uint32_t x= ((uint32_t*)h->rects[clut_id]->data[1])[i];
|
|
a = (x >> 24) & 0xff;
|
|
r = (x >> 16) & 0xff;
|
|
g = (x >> 8) & 0xff;
|
|
b = (x >> 0) & 0xff;
|
|
|
|
*q++ = RGB_TO_Y_CCIR(r, g, b);
|
|
*q++ = RGB_TO_V_CCIR(r, g, b, 0);
|
|
*q++ = RGB_TO_U_CCIR(r, g, b, 0);
|
|
*q++ = 255 - a;
|
|
}
|
|
}
|
|
|
|
bytestream_put_be16(&pseg_len, q - pseg_len - 2);
|
|
buf_size -= 6 + h->rects[clut_id]->nb_colors * 6;
|
|
}
|
|
|
|
if (buf_size < h->num_rects * 22)
|
|
return AVERROR_BUFFER_TOO_SMALL;
|
|
for (region_id = 0; region_id < h->num_rects; region_id++) {
|
|
|
|
/* region composition segment */
|
|
|
|
if (h->rects[region_id]->nb_colors <= 4) {
|
|
/* 2 bpp, some decoders do not support it correctly */
|
|
bpp_index = 0;
|
|
} else if (h->rects[region_id]->nb_colors <= 16) {
|
|
/* 4 bpp, standard encoding */
|
|
bpp_index = 1;
|
|
} else if (h->rects[region_id]->nb_colors <= 256) {
|
|
/* 8 bpp, standard encoding */
|
|
bpp_index = 2;
|
|
} else {
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
*q++ = 0x0f; /* sync_byte */
|
|
*q++ = 0x11; /* segment_type */
|
|
bytestream_put_be16(&q, page_id);
|
|
pseg_len = q;
|
|
q += 2; /* segment length */
|
|
*q++ = region_id;
|
|
*q++ = (s->object_version << 4) | (0 << 3) | 0x07; /* version , no fill */
|
|
bytestream_put_be16(&q, h->rects[region_id]->w); /* region width */
|
|
bytestream_put_be16(&q, h->rects[region_id]->h); /* region height */
|
|
*q++ = ((1 + bpp_index) << 5) | ((1 + bpp_index) << 2) | 0x03;
|
|
*q++ = region_id; /* clut_id == region_id */
|
|
*q++ = 0; /* 8 bit fill colors */
|
|
*q++ = 0x03; /* 4 bit and 2 bit fill colors */
|
|
|
|
bytestream_put_be16(&q, region_id); /* object_id == region_id */
|
|
*q++ = (0 << 6) | (0 << 4);
|
|
*q++ = 0;
|
|
*q++ = 0xf0;
|
|
*q++ = 0;
|
|
|
|
bytestream_put_be16(&pseg_len, q - pseg_len - 2);
|
|
}
|
|
buf_size -= h->num_rects * 22;
|
|
|
|
for (object_id = 0; object_id < h->num_rects; object_id++) {
|
|
int (*dvb_encode_rle)(uint8_t **pq, int buf_size,
|
|
const uint8_t *bitmap, int linesize,
|
|
int w, int h);
|
|
|
|
if (buf_size < 13)
|
|
return AVERROR_BUFFER_TOO_SMALL;
|
|
|
|
/* bpp_index maths */
|
|
if (h->rects[object_id]->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) {
|
|
/* 4 bpp, standard encoding */
|
|
dvb_encode_rle = dvb_encode_rle4;
|
|
} else if (h->rects[object_id]->nb_colors <= 256) {
|
|
/* 8 bpp, standard encoding */
|
|
dvb_encode_rle = dvb_encode_rle8;
|
|
} else {
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
/* Object Data segment */
|
|
*q++ = 0x0f; /* sync byte */
|
|
*q++ = 0x13;
|
|
bytestream_put_be16(&q, page_id);
|
|
pseg_len = q;
|
|
q += 2; /* segment length */
|
|
|
|
bytestream_put_be16(&q, object_id);
|
|
*q++ = (s->object_version << 4) | (0 << 2) | (0 << 1) | 1; /* version = 0,
|
|
object_coding_method,
|
|
non_modifying_color_flag */
|
|
{
|
|
uint8_t *ptop_field_len, *pbottom_field_len, *top_ptr, *bottom_ptr;
|
|
int ret;
|
|
|
|
ptop_field_len = q;
|
|
q += 2;
|
|
pbottom_field_len = q;
|
|
q += 2;
|
|
buf_size -= 13;
|
|
|
|
top_ptr = q;
|
|
ret = dvb_encode_rle(&q, buf_size,
|
|
h->rects[object_id]->data[0],
|
|
h->rects[object_id]->w * 2,
|
|
h->rects[object_id]->w,
|
|
h->rects[object_id]->h >> 1);
|
|
if (ret < 0)
|
|
return ret;
|
|
buf_size -= ret;
|
|
bottom_ptr = q;
|
|
ret = dvb_encode_rle(&q, buf_size,
|
|
h->rects[object_id]->data[0] + h->rects[object_id]->w,
|
|
h->rects[object_id]->w * 2,
|
|
h->rects[object_id]->w,
|
|
h->rects[object_id]->h >> 1);
|
|
if (ret < 0)
|
|
return ret;
|
|
buf_size -= ret;
|
|
|
|
bytestream_put_be16(&ptop_field_len, bottom_ptr - top_ptr);
|
|
bytestream_put_be16(&pbottom_field_len, q - bottom_ptr);
|
|
}
|
|
|
|
bytestream_put_be16(&pseg_len, q - pseg_len - 2);
|
|
}
|
|
}
|
|
|
|
/* end of display set segment */
|
|
|
|
if (buf_size < 6)
|
|
return AVERROR_BUFFER_TOO_SMALL;
|
|
*q++ = 0x0f; /* sync_byte */
|
|
*q++ = 0x80; /* segment_type */
|
|
bytestream_put_be16(&q, page_id);
|
|
pseg_len = q;
|
|
q += 2; /* segment length */
|
|
|
|
bytestream_put_be16(&pseg_len, q - pseg_len - 2);
|
|
buf_size -= 6;
|
|
|
|
s->object_version = (s->object_version + 1) & 0xf;
|
|
return q - outbuf;
|
|
}
|
|
|
|
const FFCodec ff_dvbsub_encoder = {
|
|
.p.name = "dvbsub",
|
|
.p.long_name = NULL_IF_CONFIG_SMALL("DVB subtitles"),
|
|
.p.type = AVMEDIA_TYPE_SUBTITLE,
|
|
.p.id = AV_CODEC_ID_DVB_SUBTITLE,
|
|
.priv_data_size = sizeof(DVBSubtitleContext),
|
|
FF_CODEC_ENCODE_SUB_CB(dvbsub_encode),
|
|
};
|