fac29acc2c664a9ffe1f8f92e65763f5e375d29e
[ffmpeg.git] / libavcodec / dvdsubenc.c
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
23 #undef NDEBUG
24 #include <assert.h>
25
26 // ncnt is the nibble counter
27 #define PUTNIBBLE(val)\
28 do {\
29     if (ncnt++ & 1)\
30         *q++ = bitbuf | ((val) & 0x0f);\
31     else\
32         bitbuf = (val) << 4;\
33 } while(0)
34
35 static void dvd_encode_rle(uint8_t **pq,
36                            const uint8_t *bitmap, int linesize,
37                            int w, int h,
38                            const int cmap[256])
39 {
40     uint8_t *q;
41     unsigned int bitbuf = 0;
42     int ncnt;
43     int x, y, len, color;
44
45     q = *pq;
46
47     for (y = 0; y < h; ++y) {
48         ncnt = 0;
49         for(x = 0; x < w; x += len) {
50             color = bitmap[x];
51             for (len=1; x+len < w; ++len)
52                 if (bitmap[x+len] != color)
53                     break;
54             color = cmap[color];
55             assert(color < 4);
56             if (len < 0x04) {
57                 PUTNIBBLE((len << 2)|color);
58             } else if (len < 0x10) {
59                 PUTNIBBLE(len >> 2);
60                 PUTNIBBLE((len << 2)|color);
61             } else if (len < 0x40) {
62                 PUTNIBBLE(0);
63                 PUTNIBBLE(len >> 2);
64                 PUTNIBBLE((len << 2)|color);
65             } else if (x+len == w) {
66                 PUTNIBBLE(0);
67                 PUTNIBBLE(0);
68                 PUTNIBBLE(0);
69                 PUTNIBBLE(color);
70             } else {
71                 if (len > 0xff)
72                     len = 0xff;
73                 PUTNIBBLE(0);
74                 PUTNIBBLE(len >> 6);
75                 PUTNIBBLE(len >> 2);
76                 PUTNIBBLE((len << 2)|color);
77             }
78         }
79         /* end of line */
80         if (ncnt & 1)
81             PUTNIBBLE(0);
82         bitmap += linesize;
83     }
84
85     *pq = q;
86 }
87
88 static inline void putbe16(uint8_t **pq, uint16_t v)
89 {
90     uint8_t *q = *pq;
91     *q++ = v >> 8;
92     *q++ = v;
93     *pq = q;
94 }
95
96 static int encode_dvd_subtitles(uint8_t *outbuf, int outbuf_size,
97                                 const AVSubtitle *h)
98 {
99     uint8_t *q, *qq;
100     int object_id;
101     int offset1[20], offset2[20];
102     int i, imax, color, alpha, rects = h->num_rects;
103     unsigned long hmax;
104     unsigned long hist[256];
105     int           cmap[256];
106
107     if (rects == 0 || h->rects == NULL)
108         return -1;
109     if (rects > 20)
110         rects = 20;
111
112     // analyze bitmaps, compress to 4 colors
113     for (i=0; i<256; ++i) {
114         hist[i] = 0;
115         cmap[i] = 0;
116     }
117     for (object_id = 0; object_id < rects; object_id++)
118         for (i=0; i<h->rects[object_id].w*h->rects[object_id].h; ++i) {
119             color = h->rects[object_id].bitmap[i];
120             // only count non-transparent pixels
121             alpha = h->rects[object_id].rgba_palette[color] >> 24;
122             hist[color] += alpha;
123         }
124     for (color=3;; --color) {
125         hmax = 0;
126         imax = 0;
127         for (i=0; i<256; ++i)
128             if (hist[i] > hmax) {
129                 imax = i;
130                 hmax = hist[i];
131             }
132         if (hmax == 0)
133             break;
134         if (color == 0)
135             color = 3;
136         av_log(NULL, AV_LOG_DEBUG, "dvd_subtitle hist[%d]=%ld -> col %d\n",
137                imax, hist[imax], color);
138         cmap[imax] = color;
139         hist[imax] = 0;
140     }
141
142
143     // encode data block
144     q = outbuf + 4;
145     for (object_id = 0; object_id < rects; object_id++) {
146         offset1[object_id] = q - outbuf;
147         // worst case memory requirement: 1 nibble per pixel..
148         if ((q - outbuf) + h->rects[object_id].w*h->rects[object_id].h/2
149             + 17*rects + 21 > outbuf_size) {
150             av_log(NULL, AV_LOG_ERROR, "dvd_subtitle too big\n");
151             return -1;
152         }
153         dvd_encode_rle(&q, h->rects[object_id].bitmap,
154                        h->rects[object_id].w*2,
155                        h->rects[object_id].w, h->rects[object_id].h >> 1,
156                        cmap);
157         offset2[object_id] = q - outbuf;
158         dvd_encode_rle(&q, h->rects[object_id].bitmap + h->rects[object_id].w,
159                        h->rects[object_id].w*2,
160                        h->rects[object_id].w, h->rects[object_id].h >> 1,
161                        cmap);
162     }
163
164     // set data packet size
165     qq = outbuf + 2;
166     putbe16(&qq, q - outbuf);
167
168     // send start display command
169     putbe16(&q, (h->start_display_time*90) >> 10);
170     putbe16(&q, (q - outbuf) /*- 2 */ + 8 + 12*rects + 2);
171     *q++ = 0x03; // palette - 4 nibbles
172     *q++ = 0x03; *q++ = 0x7f;
173     *q++ = 0x04; // alpha - 4 nibbles
174     *q++ = 0xf0; *q++ = 0x00;
175     //*q++ = 0x0f; *q++ = 0xff;
176
177     // XXX not sure if more than one rect can really be encoded..
178     // 12 bytes per rect
179     for (object_id = 0; object_id < rects; object_id++) {
180         int x2 = h->rects[object_id].x + h->rects[object_id].w - 1;
181         int y2 = h->rects[object_id].y + h->rects[object_id].h - 1;
182
183         *q++ = 0x05;
184         // x1 x2 -> 6 nibbles
185         *q++ = h->rects[object_id].x >> 4;
186         *q++ = (h->rects[object_id].x << 4) | ((x2 >> 8) & 0xf);
187         *q++ = x2;
188         // y1 y2 -> 6 nibbles
189         *q++ = h->rects[object_id].y >> 4;
190         *q++ = (h->rects[object_id].y << 4) | ((y2 >> 8) & 0xf);
191         *q++ = y2;
192
193         *q++ = 0x06;
194         // offset1, offset2
195         putbe16(&q, offset1[object_id]);
196         putbe16(&q, offset2[object_id]);
197     }
198     *q++ = 0x01; // start command
199     *q++ = 0xff; // terminating command
200
201     // send stop display command last
202     putbe16(&q, (h->end_display_time*90) >> 10);
203     putbe16(&q, (q - outbuf) - 2 /*+ 4*/);
204     *q++ = 0x02; // set end
205     *q++ = 0xff; // terminating command
206
207     qq = outbuf;
208     putbe16(&qq, q - outbuf);
209
210     av_log(NULL, AV_LOG_DEBUG, "subtitle_packet size=%td\n", q - outbuf);
211     return q - outbuf;
212 }
213
214 static int dvdsub_init_encoder(AVCodecContext *avctx)
215 {
216     return 0;
217 }
218
219 static int dvdsub_close_encoder(AVCodecContext *avctx)
220 {
221     return 0;
222 }
223
224 static int dvdsub_encode(AVCodecContext *avctx,
225                          unsigned char *buf, int buf_size, void *data)
226 {
227     //DVDSubtitleContext *s = avctx->priv_data;
228     AVSubtitle *sub = data;
229     int ret;
230
231     ret = encode_dvd_subtitles(buf, buf_size, sub);
232     return ret;
233 }
234
235 AVCodec dvdsub_encoder = {
236     "dvdsub",
237     CODEC_TYPE_SUBTITLE,
238     CODEC_ID_DVD_SUBTITLE,
239     0,
240     dvdsub_init_encoder,
241     dvdsub_encode,
242     dvdsub_close_encoder,
243 };
244
245 /* Local Variables: */
246 /* c-basic-offset:4 */
247 /* End: */