1/* 2 * DVD subtitle encoding for ffmpeg 3 * Copyright (c) 2005 Wolfram Gloger 4 * 5 * This file is part of FFmpeg. 6 * 7 * FFmpeg is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * FFmpeg is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with FFmpeg; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21#include "avcodec.h" 22#include "bytestream.h" 23 24#undef NDEBUG 25#include <assert.h> 26 27// ncnt is the nibble counter 28#define PUTNIBBLE(val)\ 29do {\ 30 if (ncnt++ & 1)\ 31 *q++ = bitbuf | ((val) & 0x0f);\ 32 else\ 33 bitbuf = (val) << 4;\ 34} while(0) 35 36static void dvd_encode_rle(uint8_t **pq, 37 const uint8_t *bitmap, int linesize, 38 int w, int h, 39 const int cmap[256]) 40{ 41 uint8_t *q; 42 unsigned int bitbuf = 0; 43 int ncnt; 44 int x, y, len, color; 45 46 q = *pq; 47 48 for (y = 0; y < h; ++y) { 49 ncnt = 0; 50 for(x = 0; x < w; x += len) { 51 color = bitmap[x]; 52 for (len=1; x+len < w; ++len) 53 if (bitmap[x+len] != color) 54 break; 55 color = cmap[color]; 56 assert(color < 4); 57 if (len < 0x04) { 58 PUTNIBBLE((len << 2)|color); 59 } else if (len < 0x10) { 60 PUTNIBBLE(len >> 2); 61 PUTNIBBLE((len << 2)|color); 62 } else if (len < 0x40) { 63 PUTNIBBLE(0); 64 PUTNIBBLE(len >> 2); 65 PUTNIBBLE((len << 2)|color); 66 } else if (x+len == w) { 67 PUTNIBBLE(0); 68 PUTNIBBLE(0); 69 PUTNIBBLE(0); 70 PUTNIBBLE(color); 71 } else { 72 if (len > 0xff) 73 len = 0xff; 74 PUTNIBBLE(0); 75 PUTNIBBLE(len >> 6); 76 PUTNIBBLE(len >> 2); 77 PUTNIBBLE((len << 2)|color); 78 } 79 } 80 /* end of line */ 81 if (ncnt & 1) 82 PUTNIBBLE(0); 83 bitmap += linesize; 84 } 85 86 *pq = q; 87} 88 89static int encode_dvd_subtitles(uint8_t *outbuf, int outbuf_size, 90 const AVSubtitle *h) 91{ 92 uint8_t *q, *qq; 93 int object_id; 94 int offset1[20], offset2[20]; 95 int i, imax, color, alpha, rects = h->num_rects; 96 unsigned long hmax; 97 unsigned long hist[256]; 98 int cmap[256]; 99 100 if (rects == 0 || h->rects == NULL) 101 return -1; 102 if (rects > 20) 103 rects = 20; 104 105 // analyze bitmaps, compress to 4 colors 106 for (i=0; i<256; ++i) { 107 hist[i] = 0; 108 cmap[i] = 0; 109 } 110 for (object_id = 0; object_id < rects; object_id++) 111 for (i=0; i<h->rects[object_id]->w*h->rects[object_id]->h; ++i) { 112 color = h->rects[object_id]->pict.data[0][i]; 113 // only count non-transparent pixels 114 alpha = ((uint32_t*)h->rects[object_id]->pict.data[1])[color] >> 24; 115 hist[color] += alpha; 116 } 117 for (color=3;; --color) { 118 hmax = 0; 119 imax = 0; 120 for (i=0; i<256; ++i) 121 if (hist[i] > hmax) { 122 imax = i; 123 hmax = hist[i]; 124 } 125 if (hmax == 0) 126 break; 127 if (color == 0) 128 color = 3; 129 av_log(NULL, AV_LOG_DEBUG, "dvd_subtitle hist[%d]=%ld -> col %d\n", 130 imax, hist[imax], color); 131 cmap[imax] = color; 132 hist[imax] = 0; 133 } 134 135 136 // encode data block 137 q = outbuf + 4; 138 for (object_id = 0; object_id < rects; object_id++) { 139 offset1[object_id] = q - outbuf; 140 // worst case memory requirement: 1 nibble per pixel.. 141 if ((q - outbuf) + h->rects[object_id]->w*h->rects[object_id]->h/2 142 + 17*rects + 21 > outbuf_size) { 143 av_log(NULL, AV_LOG_ERROR, "dvd_subtitle too big\n"); 144 return -1; 145 } 146 dvd_encode_rle(&q, h->rects[object_id]->pict.data[0], 147 h->rects[object_id]->w*2, 148 h->rects[object_id]->w, h->rects[object_id]->h >> 1, 149 cmap); 150 offset2[object_id] = q - outbuf; 151 dvd_encode_rle(&q, h->rects[object_id]->pict.data[0] + h->rects[object_id]->w, 152 h->rects[object_id]->w*2, 153 h->rects[object_id]->w, h->rects[object_id]->h >> 1, 154 cmap); 155 } 156 157 // set data packet size 158 qq = outbuf + 2; 159 bytestream_put_be16(&qq, q - outbuf); 160 161 // send start display command 162 bytestream_put_be16(&q, (h->start_display_time*90) >> 10); 163 bytestream_put_be16(&q, (q - outbuf) /*- 2 */ + 8 + 12*rects + 2); 164 *q++ = 0x03; // palette - 4 nibbles 165 *q++ = 0x03; *q++ = 0x7f; 166 *q++ = 0x04; // alpha - 4 nibbles 167 *q++ = 0xf0; *q++ = 0x00; 168 //*q++ = 0x0f; *q++ = 0xff; 169 170 // XXX not sure if more than one rect can really be encoded.. 171 // 12 bytes per rect 172 for (object_id = 0; object_id < rects; object_id++) { 173 int x2 = h->rects[object_id]->x + h->rects[object_id]->w - 1; 174 int y2 = h->rects[object_id]->y + h->rects[object_id]->h - 1; 175 176 *q++ = 0x05; 177 // x1 x2 -> 6 nibbles 178 *q++ = h->rects[object_id]->x >> 4; 179 *q++ = (h->rects[object_id]->x << 4) | ((x2 >> 8) & 0xf); 180 *q++ = x2; 181 // y1 y2 -> 6 nibbles 182 *q++ = h->rects[object_id]->y >> 4; 183 *q++ = (h->rects[object_id]->y << 4) | ((y2 >> 8) & 0xf); 184 *q++ = y2; 185 186 *q++ = 0x06; 187 // offset1, offset2 188 bytestream_put_be16(&q, offset1[object_id]); 189 bytestream_put_be16(&q, offset2[object_id]); 190 } 191 *q++ = 0x01; // start command 192 *q++ = 0xff; // terminating command 193 194 // send stop display command last 195 bytestream_put_be16(&q, (h->end_display_time*90) >> 10); 196 bytestream_put_be16(&q, (q - outbuf) - 2 /*+ 4*/); 197 *q++ = 0x02; // set end 198 *q++ = 0xff; // terminating command 199 200 qq = outbuf; 201 bytestream_put_be16(&qq, q - outbuf); 202 203 av_log(NULL, AV_LOG_DEBUG, "subtitle_packet size=%td\n", q - outbuf); 204 return q - outbuf; 205} 206 207static int dvdsub_encode(AVCodecContext *avctx, 208 unsigned char *buf, int buf_size, void *data) 209{ 210 //DVDSubtitleContext *s = avctx->priv_data; 211 AVSubtitle *sub = data; 212 int ret; 213 214 ret = encode_dvd_subtitles(buf, buf_size, sub); 215 return ret; 216} 217 218AVCodec dvdsub_encoder = { 219 "dvdsub", 220 AVMEDIA_TYPE_SUBTITLE, 221 CODEC_ID_DVD_SUBTITLE, 222 0, 223 NULL, 224 dvdsub_encode, 225 .long_name = NULL_IF_CONFIG_SMALL("DVD subtitles"), 226}; 227