diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 2d3cbe6083..2426391082 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -17,8 +17,8 @@ OBJS= bitstream.o utils.o mem.o allcodecs.o \ ratecontrol.o adpcm.o eval.o error_resilience.o \ fft.o mdct.o raw.o golomb.o cabac.o\ dpcm.o adx.o faandct.o parser.o g726.o \ - vp3dsp.o h264idct.o rangecoder.o pnm.o h263.o msmpeg4.o h263dec.o dvdsub.o dvbsub.o dvbsubdec.o\ - opt.o + vp3dsp.o h264idct.o rangecoder.o pnm.o h263.o msmpeg4.o h263dec.o \ + dvdsub.o dvbsub.o dvbsubdec.o dvdsubenc.o opt.o ifeq ($(CONFIG_AASC_DECODER),yes) OBJS+= aasc.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 1dcb011bca..ff075f26ad 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -570,6 +570,7 @@ PCM_CODEC(CODEC_ID_ADPCM_YAMAHA, adpcm_yamaha); /* subtitles */ #ifdef CONFIG_ENCODERS register_avcodec(&dvbsub_encoder); + register_avcodec(&dvdsub_encoder); #endif #ifdef CONFIG_DECODERS diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index e7d604608d..7777f6da3b 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -2124,6 +2124,7 @@ extern AVCodec ac3_decoder; extern AVCodec dts_decoder; /* subtitles */ +extern AVCodec dvdsub_encoder; extern AVCodec dvdsub_decoder; extern AVCodec dvbsub_encoder; extern AVCodec dvbsub_decoder; diff --git a/libavcodec/dvbsub.c b/libavcodec/dvbsub.c index 8efe8b2f98..d85836b8b6 100644 --- a/libavcodec/dvbsub.c +++ b/libavcodec/dvbsub.c @@ -343,10 +343,10 @@ static int encode_dvb_subtitles(DVBSubtitleContext *s, for (object_id = 0; object_id < h->num_rects; object_id++) { /* Object Data segment */ - if (h->rects[region_id].nb_colors <= 4) { + if (h->rects[object_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) { + } else if (h->rects[object_id].nb_colors <= 16) { /* 4 bpp, standard encoding */ bpp_index = 1; } else { diff --git a/libavcodec/dvdsubenc.c b/libavcodec/dvdsubenc.c new file mode 100644 index 0000000000..632586c6d0 --- /dev/null +++ b/libavcodec/dvdsubenc.c @@ -0,0 +1,248 @@ +/* + * DVD subtitle encoding for ffmpeg + * Copyright (c) 2005 Wolfram Gloger. + * + * 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" + +#undef NDEBUG +#include + +typedef struct DVDSubtitleContext { +} DVDSubtitleContext; + +// ncnt is the nibble counter +#define PUTNIBBLE(val)\ +do {\ + if (ncnt++ & 1)\ + *q++ = bitbuf | ((val) & 0x0f);\ + else\ + bitbuf = (val) << 4;\ +} while(0) + +static void dvd_encode_rle(uint8_t **pq, + const uint8_t *bitmap, int linesize, + int w, int h, + const int cmap[256]) +{ + uint8_t *q; + unsigned int bitbuf = 0; + int ncnt; + int x, y, len, color; + + q = *pq; + + for (y = 0; y < h; ++y) { + ncnt = 0; + for(x = 0; x < w; x += len) { + color = bitmap[x]; + for (len=1; x+len < w; ++len) + if (bitmap[x+len] != color) + break; + color = cmap[color]; + assert(color < 4); + if (len < 0x04) { + PUTNIBBLE((len << 2)|color); + } else if (len < 0x10) { + PUTNIBBLE(len >> 2); + PUTNIBBLE((len << 2)|color); + } else if (len < 0x40) { + PUTNIBBLE(0); + PUTNIBBLE(len >> 2); + PUTNIBBLE((len << 2)|color); + } else if (x+len == w) { + PUTNIBBLE(0); + PUTNIBBLE(0); + PUTNIBBLE(0); + PUTNIBBLE(color); + } else { + if (len > 0xff) + len = 0xff; + PUTNIBBLE(0); + PUTNIBBLE(len >> 6); + PUTNIBBLE(len >> 2); + PUTNIBBLE((len << 2)|color); + } + } + /* end of line */ + if (ncnt & 1) + PUTNIBBLE(0); + bitmap += linesize; + } + + *pq = q; +} + +static inline void putbe16(uint8_t **pq, uint16_t v) +{ + uint8_t *q = *pq; + *q++ = v >> 8; + *q++ = v; + *pq = q; +} + +static int encode_dvd_subtitles(uint8_t *outbuf, int outbuf_size, + const AVSubtitle *h) +{ + uint8_t *q, *qq; + int object_id; + int offset1[20], offset2[20]; + int i, imax, color, alpha, rects = h->num_rects; + unsigned long hmax; + unsigned long hist[256]; + int cmap[256]; + + if (rects == 0 || h->rects == NULL) + return -1; + if (rects > 20) + rects = 20; + + // analyze bitmaps, compress to 4 colors + for (i=0; i<256; ++i) { + hist[i] = 0; + cmap[i] = 0; + } + for (object_id = 0; object_id < rects; object_id++) + for (i=0; irects[object_id].w*h->rects[object_id].h; ++i) { + color = h->rects[object_id].bitmap[i]; + // only count non-transparent pixels + alpha = h->rects[object_id].rgba_palette[color] >> 24; + hist[color] += alpha; + } + for (color=3;; --color) { + hmax = 0; + imax = 0; + for (i=0; i<256; ++i) + if (hist[i] > hmax) { + imax = i; + hmax = hist[i]; + } + if (hmax == 0) + break; + if (color == 0) + color = 3; + av_log(NULL, AV_LOG_DEBUG, "dvd_subtitle hist[%d]=%ld -> col %d\n", + imax, hist[imax], color); + cmap[imax] = color; + hist[imax] = 0; + } + + + // encode data block + q = outbuf + 4; + for (object_id = 0; object_id < rects; object_id++) { + offset1[object_id] = q - outbuf; + // worst case memory requirement: 1 nibble per pixel.. + if ((q - outbuf) + h->rects[object_id].w*h->rects[object_id].h/2 + + 17*rects + 21 > outbuf_size) { + av_log(NULL, AV_LOG_ERROR, "dvd_subtitle too big\n"); + return -1; + } + dvd_encode_rle(&q, h->rects[object_id].bitmap, + h->rects[object_id].w*2, + h->rects[object_id].w, h->rects[object_id].h >> 1, + cmap); + offset2[object_id] = q - outbuf; + dvd_encode_rle(&q, h->rects[object_id].bitmap + h->rects[object_id].w, + h->rects[object_id].w*2, + h->rects[object_id].w, h->rects[object_id].h >> 1, + cmap); + } + + // set data packet size + qq = outbuf + 2; + putbe16(&qq, q - outbuf); + + // send start display command + putbe16(&q, (h->start_display_time*90) >> 10); + putbe16(&q, (q - outbuf) /*- 2 */ + 8 + 12*rects + 2); + *q++ = 0x03; // palette - 4 nibbles + *q++ = 0x03; *q++ = 0x7f; + *q++ = 0x04; // alpha - 4 nibbles + *q++ = 0xf0; *q++ = 0x00; + //*q++ = 0x0f; *q++ = 0xff; + + // XXX not sure if more than one rect can really be encoded.. + // 12 bytes per rect + for (object_id = 0; object_id < rects; object_id++) { + int x2 = h->rects[object_id].x + h->rects[object_id].w - 1; + int y2 = h->rects[object_id].y + h->rects[object_id].h - 1; + + *q++ = 0x05; + // x1 x2 -> 6 nibbles + *q++ = h->rects[object_id].x >> 4; + *q++ = (h->rects[object_id].x << 4) | ((x2 >> 8) & 0xf); + *q++ = x2; + // y1 y2 -> 6 nibbles + *q++ = h->rects[object_id].y >> 4; + *q++ = (h->rects[object_id].y << 4) | ((y2 >> 8) & 0xf); + *q++ = y2; + + *q++ = 0x06; + // offset1, offset2 + putbe16(&q, offset1[object_id]); + putbe16(&q, offset2[object_id]); + } + *q++ = 0x01; // start command + *q++ = 0xff; // terminating command + + // send stop display command last + putbe16(&q, (h->end_display_time*90) >> 10); + putbe16(&q, (q - outbuf) - 2 /*+ 4*/); + *q++ = 0x02; // set end + *q++ = 0xff; // terminating command + + qq = outbuf; + putbe16(&qq, q - outbuf); + + av_log(NULL, AV_LOG_DEBUG, "subtitle_packet size=%d\n", q - outbuf); + return q - outbuf; +} + +static int dvdsub_init_encoder(AVCodecContext *avctx) +{ + return 0; +} + +static int dvdsub_close_encoder(AVCodecContext *avctx) +{ + return 0; +} + +static int dvdsub_encode(AVCodecContext *avctx, + unsigned char *buf, int buf_size, void *data) +{ + //DVDSubtitleContext *s = avctx->priv_data; + AVSubtitle *sub = data; + int ret; + + ret = encode_dvd_subtitles(buf, buf_size, sub); + return ret; +} + +AVCodec dvdsub_encoder = { + "dvdsub", + CODEC_TYPE_SUBTITLE, + CODEC_ID_DVD_SUBTITLE, + sizeof(DVDSubtitleContext), + dvdsub_init_encoder, + dvdsub_encode, + dvdsub_close_encoder, +}; + +/* Local Variables: */ +/* c-basic-offset:4 */ +/* End: */