diff options
author | Fabrice Bellard <fabrice@bellard.org> | 2005-06-03 13:59:38 +0000 |
---|---|---|
committer | Fabrice Bellard <fabrice@bellard.org> | 2005-06-03 13:59:38 +0000 |
commit | 240c1657dcd45adc0e63ef947b920919071ec1f7 (patch) | |
tree | 385f5f33871332a0c78cc3bc00aa5d74026536f3 /libavcodec/dvbsub.c | |
parent | c62112ff06f859c8c3ef096f859a0f5a941a1ce8 (diff) | |
download | ffmpeg-240c1657dcd45adc0e63ef947b920919071ec1f7.tar.gz |
subtitle codec type support
Originally committed as revision 4346 to svn://svn.ffmpeg.org/ffmpeg/trunk
Diffstat (limited to 'libavcodec/dvbsub.c')
-rw-r--r-- | libavcodec/dvbsub.c | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/libavcodec/dvbsub.c b/libavcodec/dvbsub.c new file mode 100644 index 0000000000..9b52848d98 --- /dev/null +++ b/libavcodec/dvbsub.c @@ -0,0 +1,407 @@ +/* + * DVB subtitle encoding for ffmpeg + * Copyright (c) 2005 Fabrice Bellard. + * + * This library 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 of the License, or (at your option) any later version. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "avcodec.h" + +typedef struct DVBSubtitleContext { + int hide_state; + int object_version; +} DVBSubtitleContext; + +#define PUTBITS2(val)\ +{\ + bitbuf |= (val) << bitcnt;\ + bitcnt -= 2;\ + if (bitcnt < 0) {\ + bitcnt = 6;\ + *q++ = bitbuf;\ + bitbuf = 0;\ + }\ +} + +static void dvb_encode_rle2(uint8_t **pq, + const uint8_t *bitmap, int linesize, + int w, int h) +{ + uint8_t *q; + unsigned int bitbuf; + int bitcnt; + int x, y, len, x1, v, color; + + q = *pq; + + for(y = 0; y < h; y++) { + *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; + } + *pq = q; +} + +#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 void dvb_encode_rle4(uint8_t **pq, + const uint8_t *bitmap, int linesize, + int w, int h) +{ + uint8_t *q; + unsigned int bitbuf; + int bitcnt; + int x, y, len, x1, v, color; + + q = *pq; + + for(y = 0; y < h; y++) { + *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; + } + *pq = q; +} + +#define SCALEBITS 10 +#define ONE_HALF (1 << (SCALEBITS - 1)) +#define FIX(x) ((int) ((x) * (1<<SCALEBITS) + 0.5)) + +#define RGB_TO_Y_CCIR(r, g, b) \ +((FIX(0.29900*219.0/255.0) * (r) + FIX(0.58700*219.0/255.0) * (g) + \ + FIX(0.11400*219.0/255.0) * (b) + (ONE_HALF + (16 << SCALEBITS))) >> SCALEBITS) + +#define RGB_TO_U_CCIR(r1, g1, b1, shift)\ +(((- FIX(0.16874*224.0/255.0) * r1 - FIX(0.33126*224.0/255.0) * g1 + \ + FIX(0.50000*224.0/255.0) * b1 + (ONE_HALF << shift) - 1) >> (SCALEBITS + shift)) + 128) + +#define RGB_TO_V_CCIR(r1, g1, b1, shift)\ +(((FIX(0.50000*224.0/255.0) * r1 - FIX(0.41869*224.0/255.0) * g1 - \ + FIX(0.08131*224.0/255.0) * b1 + (ONE_HALF << shift) - 1) >> (SCALEBITS + shift)) + 128) + +static inline void putbe16(uint8_t **pq, uint16_t v) +{ + uint8_t *q; + q = *pq; + *q++ = v >> 8; + *q++ = v; + *pq = q; +} + +static int encode_dvb_subtitles(DVBSubtitleContext *s, + uint8_t *outbuf, AVSubtitle *h) +{ + uint8_t *q, *pseg_len; + int page_id, region_id, clut_id, object_id, i, bpp_index, page_state; + + + q = outbuf; + + page_id = 1; + region_id = 0; + clut_id = 0; + object_id = 0; + if (h->nb_colors <= 4) { + /* 2 bpp, some decoders do not support it correctly */ + bpp_index = 0; + } else if (h->nb_colors <= 16) { + /* 4 bpp, standard encoding */ + bpp_index = 1; + } else { + return -1; + } + + *q++ = 0x00; /* subtitle_stream_id */ + + /* page composition segment */ + + *q++ = 0x0f; /* sync_byte */ + *q++ = 0x10; /* segment_type */ + putbe16(&q, page_id); + pseg_len = q; + q += 2; /* segment length */ + *q++ = 30; /* page_timeout (seconds) */ + if (s->hide_state) + page_state = 0; /* normal case */ + else + page_state = 2; /* mode change */ + /* page_version = 0 + page_state */ + *q++ = s->object_version | (page_state << 2) | 3; + *q++ = region_id; + *q++ = 0xff; /* reserved */ + putbe16(&q, 0); /* left pos */ + putbe16(&q, 0); /* top pos */ + + putbe16(&pseg_len, q - pseg_len - 2); + + if (!s->hide_state) { + /* CLUT segment */ + + *q++ = 0x0f; /* sync byte */ + *q++ = 0x12; /* CLUT definition segment */ + putbe16(&q, page_id); + pseg_len = q; + q += 2; /* segment length */ + *q++ = clut_id; + *q++ = (0 << 4) | 0xf; /* version = 0 */ + + for(i = 0; i < h->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; + a = (h->rgba_palette[i] >> 24) & 0xff; + r = (h->rgba_palette[i] >> 16) & 0xff; + g = (h->rgba_palette[i] >> 8) & 0xff; + b = (h->rgba_palette[i] >> 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; + } + } + + putbe16(&pseg_len, q - pseg_len - 2); + } + /* region composition segment */ + + *q++ = 0x0f; /* sync_byte */ + *q++ = 0x11; /* segment_type */ + putbe16(&q, page_id); + pseg_len = q; + q += 2; /* segment length */ + *q++ = region_id; + *q++ = (s->object_version << 4) | (0 << 3) | 0x07; /* version , no fill */ + putbe16(&q, 720); /* region width */ + putbe16(&q, 576); /* region height */ + *q++ = ((1 + bpp_index) << 5) | ((1 + bpp_index) << 2) | 0x03; + *q++ = clut_id; + *q++ = 0; /* 8 bit fill colors */ + *q++ = 0x03; /* 4 bit and 2 bit fill colors */ + + if (!s->hide_state) { + putbe16(&q, object_id); + *q++ = (0 << 6) | (0 << 4) | ((h->x >> 8) & 0xf); + *q++ = h->x; + *q++ = 0xf0 | ((h->y >> 8) & 0xf); + *q++ = h->y; + } + + putbe16(&pseg_len, q - pseg_len - 2); + + if (!s->hide_state) { + + /* Object Data segment */ + + *q++ = 0x0f; /* sync byte */ + *q++ = 0x13; + putbe16(&q, page_id); + pseg_len = q; + q += 2; /* segment length */ + + putbe16(&q, object_id); + *q++ = (s->object_version << 4) | (0 << 2) | (0 << 1) | 1; /* version = 0, + onject_coding_method, + non_modifying_color_flag */ + { + uint8_t *ptop_field_len, *pbottom_field_len, *top_ptr, *bottom_ptr; + void (*dvb_encode_rle)(uint8_t **pq, + const uint8_t *bitmap, int linesize, + int w, int h); + ptop_field_len = q; + q += 2; + pbottom_field_len = q; + q += 2; + + if (bpp_index == 0) + dvb_encode_rle = dvb_encode_rle2; + else + dvb_encode_rle = dvb_encode_rle4; + + top_ptr = q; + dvb_encode_rle(&q, h->bitmap, h->w * 2, h->w, h->h >> 1); + bottom_ptr = q; + dvb_encode_rle(&q, h->bitmap + h->w, h->w * 2, h->w, h->h >> 1); + + putbe16(&ptop_field_len, bottom_ptr - top_ptr); + putbe16(&pbottom_field_len, q - bottom_ptr); + } + + putbe16(&pseg_len, q - pseg_len - 2); + } + + /* end of display set segment */ + + *q++ = 0x0f; /* sync_byte */ + *q++ = 0x80; /* segment_type */ + putbe16(&q, page_id); + pseg_len = q; + q += 2; /* segment length */ + + putbe16(&pseg_len, q - pseg_len - 2); + + *q++ = 0xff; /* end of PES data */ + + s->object_version = (s->object_version + 1) & 0xf; + s->hide_state = !s->hide_state; + return q - outbuf; +} + +static int dvbsub_init_decoder(AVCodecContext *avctx) +{ + return 0; +} + +static int dvbsub_close_decoder(AVCodecContext *avctx) +{ + return 0; +} + +static int dvbsub_encode(AVCodecContext *avctx, + unsigned char *buf, int buf_size, void *data) +{ + DVBSubtitleContext *s = avctx->priv_data; + AVSubtitle *sub = data; + int ret; + + ret = encode_dvb_subtitles(s, buf, sub); + return ret; +} + +AVCodec dvbsub_encoder = { + "dvbsub", + CODEC_TYPE_SUBTITLE, + CODEC_ID_DVB_SUBTITLE, + sizeof(DVBSubtitleContext), + dvbsub_init_decoder, + dvbsub_encode, + dvbsub_close_decoder, +}; |