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