Mercurial > libavcodec.hg
annotate qtrleenc.c @ 8204:507854688c43 libavcodec
Some BMP files have file size declared in the header equal to headers size
without image data, so try to correct that value before conducting checks on
declared file size.
author | kostya |
---|---|
date | Mon, 24 Nov 2008 11:24:02 +0000 |
parents | 4525dcd81357 |
children | 7b81aa80ec46 |
rev | line source |
---|---|
5195 | 1 /* |
2 * Quicktime Animation (RLE) Video Encoder | |
3 * Copyright (C) 2007 Clemens Fruhwirth | |
4 * Copyright (C) 2007 Alexis Ballier | |
5 * | |
7804 | 6 * This file is based on flashsvenc.c. |
5195 | 7 * |
7804 | 8 * This file is part of FFmpeg. |
5195 | 9 * |
10 * FFmpeg is free software; you can redistribute it and/or | |
11 * modify it under the terms of the GNU Lesser General Public | |
7820
9905b13b3399
Relicense from LGPL 2.1 to LGPL 2.1 or later with the authors' permission.
diego
parents:
7804
diff
changeset
|
12 * License as published by the Free Software Foundation; either |
9905b13b3399
Relicense from LGPL 2.1 to LGPL 2.1 or later with the authors' permission.
diego
parents:
7804
diff
changeset
|
13 * version 2.1 of the License, or (at your option) any later version. |
5195 | 14 * |
15 * FFmpeg is distributed in the hope that it will be useful, | |
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
18 * Lesser General Public License for more details. | |
19 * | |
20 * You should have received a copy of the GNU Lesser General Public | |
21 * License along with FFmpeg; if not, write to the Free Software | |
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
23 */ | |
24 | |
25 #include "avcodec.h" | |
26 #include "bytestream.h" | |
27 | |
28 /** Maximum RLE code for bulk copy */ | |
29 #define MAX_RLE_BULK 127 | |
30 /** Maximum RLE code for repeat */ | |
31 #define MAX_RLE_REPEAT 128 | |
32 /** Maximum RLE code for skip */ | |
33 #define MAX_RLE_SKIP 254 | |
34 | |
35 typedef struct QtrleEncContext { | |
36 AVCodecContext *avctx; | |
37 AVFrame frame; | |
38 int pixel_size; | |
39 AVPicture previous_frame; | |
40 unsigned int max_buf_size; | |
41 /** | |
42 * This array will contain at ith position the value of the best RLE code | |
43 * if the line started at pixel i | |
44 * There can be 3 values : | |
45 * skip (0) : skip as much as possible pixels because they are equal to the | |
46 * previous frame ones | |
47 * repeat (<-1) : repeat that pixel -rle_code times, still as much as | |
48 * possible | |
49 * copy (>0) : copy the raw next rle_code pixels */ | |
50 signed char *rlecode_table; | |
51 /** | |
52 * This array will contain the length of the best rle encoding of the line | |
53 * starting at ith pixel */ | |
54 int *length_table; | |
55 /** | |
56 * Will contain at ith position the number of consecutive pixels equal to the previous | |
57 * frame starting from pixel i */ | |
58 uint8_t* skip_table; | |
59 } QtrleEncContext; | |
60 | |
6517
48759bfbd073
Apply 'cold' attribute to init/uninit functions in libavcodec
zuxy
parents:
6204
diff
changeset
|
61 static av_cold int qtrle_encode_init(AVCodecContext *avctx) |
5195 | 62 { |
63 QtrleEncContext *s = avctx->priv_data; | |
64 | |
65 if (avcodec_check_dimensions(avctx, avctx->width, avctx->height) < 0) { | |
66 return -1; | |
67 } | |
68 s->avctx=avctx; | |
69 | |
70 switch (avctx->pix_fmt) { | |
71 /* case PIX_FMT_RGB555: | |
72 s->pixel_size = 2; | |
73 break;*/ | |
74 case PIX_FMT_RGB24: | |
75 s->pixel_size = 3; | |
76 break; | |
77 default: | |
78 av_log(avctx, AV_LOG_ERROR, "Unsupported colorspace.\n"); | |
79 break; | |
80 } | |
7823
4525dcd81357
Bump Major version, this commit is almost just renaming bits_per_sample to
michael
parents:
7820
diff
changeset
|
81 avctx->bits_per_coded_sample = s->pixel_size*8; |
5195 | 82 |
83 s->rlecode_table = av_mallocz(s->avctx->width); | |
84 s->skip_table = av_mallocz(s->avctx->width); | |
85 s->length_table = av_mallocz((s->avctx->width + 1)*sizeof(int)); | |
86 if (!s->skip_table || !s->length_table || !s->rlecode_table) { | |
87 av_log(avctx, AV_LOG_ERROR, "Error allocating memory.\n"); | |
88 return -1; | |
89 } | |
90 if (avpicture_alloc(&s->previous_frame, avctx->pix_fmt, avctx->width, avctx->height) < 0) { | |
91 av_log(avctx, AV_LOG_ERROR, "Error allocating picture\n"); | |
92 return -1; | |
93 } | |
94 | |
95 s->max_buf_size = s->avctx->width*s->avctx->height*s->pixel_size /* image base material */ | |
96 + 15 /* header + footer */ | |
97 + s->avctx->height*2 /* skip code+rle end */ | |
98 + s->avctx->width/MAX_RLE_BULK + 1 /* rle codes */; | |
99 avctx->coded_frame = &s->frame; | |
100 return 0; | |
101 } | |
102 | |
103 /** | |
104 * Computes the best RLE sequence for a line | |
105 */ | |
106 static void qtrle_encode_line(QtrleEncContext *s, AVFrame *p, int line, uint8_t **buf) | |
107 { | |
108 int width=s->avctx->width; | |
109 int i; | |
110 signed char rlecode; | |
111 | |
112 /* We will use it to compute the best bulk copy sequence */ | |
113 unsigned int bulkcount; | |
114 /* This will be the number of pixels equal to the preivous frame one's | |
115 * starting from the ith pixel */ | |
116 unsigned int skipcount; | |
117 /* This will be the number of consecutive equal pixels in the current | |
118 * frame, starting from the ith one also */ | |
119 unsigned int repeatcount; | |
120 | |
121 /* The cost of the three different possibilities */ | |
122 int total_bulk_cost; | |
123 int total_skip_cost; | |
124 int total_repeat_cost; | |
125 | |
126 int temp_cost; | |
127 int j; | |
128 | |
129 uint8_t *this_line = p-> data[0] + line*p->linesize[0] + (width - 1)*s->pixel_size; | |
130 uint8_t *prev_line = s->previous_frame.data[0] + line*p->linesize[0] + (width - 1)*s->pixel_size; | |
131 | |
132 s->length_table[width] = 0; | |
133 skipcount = 0; | |
134 | |
135 for (i = width - 1; i >= 0; i--) { | |
136 | |
137 if (!s->frame.key_frame && !memcmp(this_line, prev_line, s->pixel_size)) | |
138 skipcount = FFMIN(skipcount + 1, MAX_RLE_SKIP); | |
139 else | |
140 skipcount = 0; | |
141 | |
142 total_skip_cost = s->length_table[i + skipcount] + 2; | |
143 s->skip_table[i] = skipcount; | |
144 | |
145 | |
146 if (i < width - 1 && !memcmp(this_line, this_line + s->pixel_size, s->pixel_size)) | |
147 repeatcount = FFMIN(repeatcount + 1, MAX_RLE_REPEAT); | |
148 else | |
149 repeatcount = 1; | |
150 | |
151 total_repeat_cost = s->length_table[i + repeatcount] + 1 + s->pixel_size; | |
152 | |
153 /* skip code is free for the first pixel, it costs one byte for repeat and bulk copy | |
154 * so let's make it aware */ | |
155 if (i == 0) { | |
156 total_skip_cost--; | |
157 total_repeat_cost++; | |
158 } | |
159 | |
160 if (repeatcount > 1 && (skipcount == 0 || total_repeat_cost < total_skip_cost)) { | |
161 /* repeat is the best */ | |
162 s->length_table[i] = total_repeat_cost; | |
163 s->rlecode_table[i] = -repeatcount; | |
164 } | |
165 else if (skipcount > 0) { | |
166 /* skip is the best choice here */ | |
167 s->length_table[i] = total_skip_cost; | |
168 s->rlecode_table[i] = 0; | |
169 } | |
170 else { | |
171 /* We cannot do neither skip nor repeat | |
172 * thus we search for the best bulk copy to do */ | |
173 | |
174 int limit = FFMIN(width - i, MAX_RLE_BULK); | |
175 | |
176 temp_cost = 1 + s->pixel_size + !i; | |
177 total_bulk_cost = INT_MAX; | |
178 | |
179 for (j = 1; j <= limit; j++) { | |
180 if (s->length_table[i + j] + temp_cost < total_bulk_cost) { | |
181 /* We have found a better bulk copy ... */ | |
182 total_bulk_cost = s->length_table[i + j] + temp_cost; | |
183 bulkcount = j; | |
184 } | |
185 temp_cost += s->pixel_size; | |
186 } | |
187 | |
188 s->length_table[i] = total_bulk_cost; | |
189 s->rlecode_table[i] = bulkcount; | |
190 } | |
191 | |
192 this_line -= s->pixel_size; | |
193 prev_line -= s->pixel_size; | |
194 } | |
195 | |
196 /* Good ! Now we have the best sequence for this line, let's ouput it */ | |
197 | |
198 /* We do a special case for the first pixel so that we avoid testing it in | |
199 * the whole loop */ | |
200 | |
201 i=0; | |
202 this_line = p-> data[0] + line*p->linesize[0]; | |
203 prev_line = s->previous_frame.data[0] + line*p->linesize[0]; | |
204 | |
205 if (s->rlecode_table[0] == 0) { | |
206 bytestream_put_byte(buf, s->skip_table[0] + 1); | |
207 i += s->skip_table[0]; | |
208 } | |
209 else bytestream_put_byte(buf, 1); | |
210 | |
211 | |
212 while (i < width) { | |
213 rlecode = s->rlecode_table[i]; | |
214 bytestream_put_byte(buf, rlecode); | |
215 if (rlecode == 0) { | |
216 /* Write a skip sequence */ | |
217 bytestream_put_byte(buf, s->skip_table[i] + 1); | |
218 i += s->skip_table[i]; | |
219 } | |
220 else if (rlecode > 0) { | |
221 /* bulk copy */ | |
222 bytestream_put_buffer(buf, this_line + i*s->pixel_size, rlecode*s->pixel_size); | |
223 i += rlecode; | |
224 } | |
225 else { | |
226 /* repeat the bits */ | |
227 bytestream_put_buffer(buf, this_line + i*s->pixel_size, s->pixel_size); | |
228 i -= rlecode; | |
229 } | |
230 } | |
231 bytestream_put_byte(buf, -1); // end RLE line | |
232 } | |
233 | |
234 /** Encodes frame including header */ | |
235 static int encode_frame(QtrleEncContext *s, AVFrame *p, uint8_t *buf) | |
236 { | |
237 int i; | |
238 int start_line = 0; | |
239 int end_line = s->avctx->height; | |
240 uint8_t *orig_buf = buf; | |
241 | |
242 if (!s->frame.key_frame) { | |
243 for (start_line = 0; start_line < s->avctx->height; start_line++) | |
244 if (memcmp(p->data[0] + start_line*p->linesize[0], | |
245 s->previous_frame.data[0] + start_line*p->linesize[0], | |
246 p->linesize[0])) | |
247 break; | |
248 | |
249 for (end_line=s->avctx->height; end_line > start_line; end_line--) | |
250 if (memcmp(p->data[0] + (end_line - 1)*p->linesize[0], | |
251 s->previous_frame.data[0] + (end_line - 1)*p->linesize[0], | |
252 p->linesize[0])) | |
253 break; | |
254 } | |
255 | |
256 bytestream_put_be32(&buf, 0); // CHUNK SIZE, patched later | |
257 | |
6204
2265a9c096a4
add parenthesis, fix warning: qtrleenc.c:257: warning: suggest parentheses around && within ||
bcoudurier
parents:
5215
diff
changeset
|
258 if ((start_line == 0 && end_line == s->avctx->height) || start_line == s->avctx->height) |
5195 | 259 bytestream_put_be16(&buf, 0); // header |
260 else { | |
261 bytestream_put_be16(&buf, 8); // header | |
262 bytestream_put_be16(&buf, start_line); // starting line | |
263 bytestream_put_be16(&buf, 0); // unknown | |
264 bytestream_put_be16(&buf, end_line - start_line); // lines to update | |
265 bytestream_put_be16(&buf, 0); // unknown | |
266 } | |
267 for (i = start_line; i < end_line; i++) | |
268 qtrle_encode_line(s, p, i, &buf); | |
269 | |
270 bytestream_put_byte(&buf, 0); // zero skip code = frame finished | |
271 AV_WB32(orig_buf, buf - orig_buf); // patch the chunk size | |
272 return buf - orig_buf; | |
273 } | |
274 | |
275 static int qtrle_encode_frame(AVCodecContext *avctx, uint8_t *buf, int buf_size, void *data) | |
276 { | |
277 QtrleEncContext * const s = avctx->priv_data; | |
278 AVFrame *pict = data; | |
279 AVFrame * const p = &s->frame; | |
280 int chunksize; | |
281 | |
282 *p = *pict; | |
283 | |
284 if (buf_size < s->max_buf_size) { | |
285 /* Upper bound check for compressed data */ | |
286 av_log(avctx, AV_LOG_ERROR, "buf_size %d < %d\n", buf_size, s->max_buf_size); | |
287 return -1; | |
288 } | |
289 | |
290 if (avctx->gop_size == 0 || (s->avctx->frame_number % avctx->gop_size) == 0) { | |
291 /* I-Frame */ | |
292 p->pict_type = FF_I_TYPE; | |
293 p->key_frame = 1; | |
294 } else { | |
295 /* P-Frame */ | |
296 p->pict_type = FF_P_TYPE; | |
297 p->key_frame = 0; | |
298 } | |
299 | |
300 chunksize = encode_frame(s, pict, buf); | |
301 | |
302 /* save the current frame */ | |
303 av_picture_copy(&s->previous_frame, (AVPicture *)p, avctx->pix_fmt, avctx->width, avctx->height); | |
304 return chunksize; | |
305 } | |
306 | |
6517
48759bfbd073
Apply 'cold' attribute to init/uninit functions in libavcodec
zuxy
parents:
6204
diff
changeset
|
307 static av_cold int qtrle_encode_end(AVCodecContext *avctx) |
5195 | 308 { |
309 QtrleEncContext *s = avctx->priv_data; | |
310 | |
311 avpicture_free(&s->previous_frame); | |
312 av_free(s->rlecode_table); | |
313 av_free(s->length_table); | |
314 av_free(s->skip_table); | |
315 return 0; | |
316 } | |
317 | |
318 AVCodec qtrle_encoder = { | |
319 "qtrle", | |
320 CODEC_TYPE_VIDEO, | |
321 CODEC_ID_QTRLE, | |
322 sizeof(QtrleEncContext), | |
323 qtrle_encode_init, | |
324 qtrle_encode_frame, | |
325 qtrle_encode_end, | |
6788 | 326 .pix_fmts = (enum PixelFormat[]){PIX_FMT_RGB24, PIX_FMT_NONE}, |
7040
e943e1409077
Make AVCodec long_names definition conditional depending on CONFIG_SMALL.
stefano
parents:
6788
diff
changeset
|
327 .long_name = NULL_IF_CONFIG_SMALL("QuickTime Animation (RLE) video"), |
5195 | 328 }; |