Mercurial > libavcodec.hg
comparison dvdsubenc.c @ 2946:ac94d509884e libavcodec
dvbsub encoder, patch by Wolfram Gloger < wmglo AH dent POIS med POIS uni-muenchen POIS de >
Original thread:
Date: 8 Oct 2005 09:35:38 -0000
Subject: [Ffmpeg-devel] [PATCH] dvdsub encoder -- 2nd version
author | gpoirier |
---|---|
date | Mon, 14 Nov 2005 22:17:29 +0000 |
parents | |
children | fde28cb7e3d5 |
comparison
equal
deleted
inserted
replaced
2945:394ea2703387 | 2946:ac94d509884e |
---|---|
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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=%d\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: */ |