]> git.sesse.net Git - ffmpeg/blob - libavcodec/dvdsubenc.c
1d423af5b37738c59dd6cfd49f01ee364cfaea78
[ffmpeg] / libavcodec / dvdsubenc.c
1 /*
2  * DVD subtitle encoding for ffmpeg
3  * Copyright (c) 2005 Wolfram Gloger.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 #include "avcodec.h"
20
21 #undef NDEBUG
22 #include <assert.h>
23
24 typedef struct DVDSubtitleContext {
25 } DVDSubtitleContext;
26
27 // ncnt is the nibble counter
28 #define PUTNIBBLE(val)\
29 do {\
30     if (ncnt++ & 1)\
31         *q++ = bitbuf | ((val) & 0x0f);\
32     else\
33         bitbuf = (val) << 4;\
34 } while(0)
35
36 static 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
89 static inline void putbe16(uint8_t **pq, uint16_t v)
90 {
91     uint8_t *q = *pq;
92     *q++ = v >> 8;
93     *q++ = v;
94     *pq = q;
95 }
96
97 static int encode_dvd_subtitles(uint8_t *outbuf, int outbuf_size,
98                                 const AVSubtitle *h)
99 {
100     uint8_t *q, *qq;
101     int object_id;
102     int offset1[20], offset2[20];
103     int i, imax, color, alpha, rects = h->num_rects;
104     unsigned long hmax;
105     unsigned long hist[256];
106     int           cmap[256];
107
108     if (rects == 0 || h->rects == NULL)
109         return -1;
110     if (rects > 20)
111         rects = 20;
112
113     // analyze bitmaps, compress to 4 colors
114     for (i=0; i<256; ++i) {
115         hist[i] = 0;
116         cmap[i] = 0;
117     }
118     for (object_id = 0; object_id < rects; object_id++)
119         for (i=0; i<h->rects[object_id].w*h->rects[object_id].h; ++i) {
120             color = h->rects[object_id].bitmap[i];
121             // only count non-transparent pixels
122             alpha = h->rects[object_id].rgba_palette[color] >> 24;
123             hist[color] += alpha;
124         }
125     for (color=3;; --color) {
126         hmax = 0;
127         imax = 0;
128         for (i=0; i<256; ++i)
129             if (hist[i] > hmax) {
130                 imax = i;
131                 hmax = hist[i];
132             }
133         if (hmax == 0)
134             break;
135         if (color == 0)
136             color = 3;
137         av_log(NULL, AV_LOG_DEBUG, "dvd_subtitle hist[%d]=%ld -> col %d\n",
138                imax, hist[imax], color);
139         cmap[imax] = color;
140         hist[imax] = 0;
141     }
142
143
144     // encode data block
145     q = outbuf + 4;
146     for (object_id = 0; object_id < rects; object_id++) {
147         offset1[object_id] = q - outbuf;
148         // worst case memory requirement: 1 nibble per pixel..
149         if ((q - outbuf) + h->rects[object_id].w*h->rects[object_id].h/2
150             + 17*rects + 21 > outbuf_size) {
151             av_log(NULL, AV_LOG_ERROR, "dvd_subtitle too big\n");
152             return -1;
153         }
154         dvd_encode_rle(&q, h->rects[object_id].bitmap,
155                        h->rects[object_id].w*2,
156                        h->rects[object_id].w, h->rects[object_id].h >> 1,
157                        cmap);
158         offset2[object_id] = q - outbuf;
159         dvd_encode_rle(&q, h->rects[object_id].bitmap + h->rects[object_id].w,
160                        h->rects[object_id].w*2,
161                        h->rects[object_id].w, h->rects[object_id].h >> 1,
162                        cmap);
163     }
164
165     // set data packet size
166     qq = outbuf + 2;
167     putbe16(&qq, q - outbuf);
168
169     // send start display command
170     putbe16(&q, (h->start_display_time*90) >> 10);
171     putbe16(&q, (q - outbuf) /*- 2 */ + 8 + 12*rects + 2);
172     *q++ = 0x03; // palette - 4 nibbles
173     *q++ = 0x03; *q++ = 0x7f;
174     *q++ = 0x04; // alpha - 4 nibbles
175     *q++ = 0xf0; *q++ = 0x00;
176     //*q++ = 0x0f; *q++ = 0xff;
177
178     // XXX not sure if more than one rect can really be encoded..
179     // 12 bytes per rect
180     for (object_id = 0; object_id < rects; object_id++) {
181         int x2 = h->rects[object_id].x + h->rects[object_id].w - 1;
182         int y2 = h->rects[object_id].y + h->rects[object_id].h - 1;
183
184         *q++ = 0x05;
185         // x1 x2 -> 6 nibbles
186         *q++ = h->rects[object_id].x >> 4;
187         *q++ = (h->rects[object_id].x << 4) | ((x2 >> 8) & 0xf);
188         *q++ = x2;
189         // y1 y2 -> 6 nibbles
190         *q++ = h->rects[object_id].y >> 4;
191         *q++ = (h->rects[object_id].y << 4) | ((y2 >> 8) & 0xf);
192         *q++ = y2;
193
194         *q++ = 0x06;
195         // offset1, offset2
196         putbe16(&q, offset1[object_id]);
197         putbe16(&q, offset2[object_id]);
198     }
199     *q++ = 0x01; // start command
200     *q++ = 0xff; // terminating command
201
202     // send stop display command last
203     putbe16(&q, (h->end_display_time*90) >> 10);
204     putbe16(&q, (q - outbuf) - 2 /*+ 4*/);
205     *q++ = 0x02; // set end
206     *q++ = 0xff; // terminating command
207
208     qq = outbuf;
209     putbe16(&qq, q - outbuf);
210
211     av_log(NULL, AV_LOG_DEBUG, "subtitle_packet size=%td\n", q - outbuf);
212     return q - outbuf;
213 }
214
215 static int dvdsub_init_encoder(AVCodecContext *avctx)
216 {
217     return 0;
218 }
219
220 static int dvdsub_close_encoder(AVCodecContext *avctx)
221 {
222     return 0;
223 }
224
225 static int dvdsub_encode(AVCodecContext *avctx,
226                          unsigned char *buf, int buf_size, void *data)
227 {
228     //DVDSubtitleContext *s = avctx->priv_data;
229     AVSubtitle *sub = data;
230     int ret;
231
232     ret = encode_dvd_subtitles(buf, buf_size, sub);
233     return ret;
234 }
235
236 AVCodec dvdsub_encoder = {
237     "dvdsub",
238     CODEC_TYPE_SUBTITLE,
239     CODEC_ID_DVD_SUBTITLE,
240     sizeof(DVDSubtitleContext),
241     dvdsub_init_encoder,
242     dvdsub_encode,
243     dvdsub_close_encoder,
244 };
245
246 /* Local Variables: */
247 /* c-basic-offset:4 */
248 /* End: */