blob: f7f98efde346aab85e9c59ccde71008dc4077b7b
1 | /* |
2 | * Flash Screen Video encoder |
3 | * Copyright (C) 2004 Alex Beregszaszi |
4 | * Copyright (C) 2006 Benjamin Larsson |
5 | * |
6 | * This file is part of FFmpeg. |
7 | * |
8 | * FFmpeg is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU Lesser General Public |
10 | * License as published by the Free Software Foundation; either |
11 | * version 2.1 of the License, or (at your option) any later version. |
12 | * |
13 | * FFmpeg is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * Lesser General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU Lesser General Public |
19 | * License along with FFmpeg; if not, write to the Free Software |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
21 | */ |
22 | |
23 | /* Encoding development sponsored by http://fh-campuswien.ac.at */ |
24 | |
25 | /** |
26 | * @file |
27 | * Flash Screen Video encoder |
28 | * @author Alex Beregszaszi |
29 | * @author Benjamin Larsson |
30 | * |
31 | * A description of the bitstream format for Flash Screen Video version 1/2 |
32 | * is part of the SWF File Format Specification (version 10), which can be |
33 | * downloaded from http://www.adobe.com/devnet/swf.html. |
34 | */ |
35 | |
36 | /* |
37 | * Encoding ideas: A basic encoder would just use a fixed block size. |
38 | * Block sizes can be multiples of 16, from 16 to 256. The blocks don't |
39 | * have to be quadratic. A brute force search with a set of different |
40 | * block sizes should give a better result than to just use a fixed size. |
41 | * |
42 | * TODO: |
43 | * Don't reencode the frame in brute force mode if the frame is a dupe. |
44 | * Speed up. Make the difference check faster. |
45 | */ |
46 | |
47 | #include <stdio.h> |
48 | #include <stdlib.h> |
49 | #include <zlib.h> |
50 | |
51 | #include "avcodec.h" |
52 | #include "internal.h" |
53 | #include "put_bits.h" |
54 | #include "bytestream.h" |
55 | |
56 | |
57 | typedef struct FlashSVContext { |
58 | AVCodecContext *avctx; |
59 | uint8_t *previous_frame; |
60 | int image_width, image_height; |
61 | int block_width, block_height; |
62 | uint8_t *tmpblock; |
63 | uint8_t *encbuffer; |
64 | int block_size; |
65 | z_stream zstream; |
66 | int last_key_frame; |
67 | } FlashSVContext; |
68 | |
69 | static int copy_region_enc(uint8_t *sptr, uint8_t *dptr, int dx, int dy, |
70 | int h, int w, int stride, uint8_t *pfptr) |
71 | { |
72 | int i, j; |
73 | uint8_t *nsptr; |
74 | uint8_t *npfptr; |
75 | int diff = 0; |
76 | |
77 | for (i = dx + h; i > dx; i--) { |
78 | nsptr = sptr + i * stride + dy * 3; |
79 | npfptr = pfptr + i * stride + dy * 3; |
80 | for (j = 0; j < w * 3; j++) { |
81 | diff |= npfptr[j] ^ nsptr[j]; |
82 | dptr[j] = nsptr[j]; |
83 | } |
84 | dptr += w * 3; |
85 | } |
86 | if (diff) |
87 | return 1; |
88 | return 0; |
89 | } |
90 | |
91 | static av_cold int flashsv_encode_end(AVCodecContext *avctx) |
92 | { |
93 | FlashSVContext *s = avctx->priv_data; |
94 | |
95 | deflateEnd(&s->zstream); |
96 | |
97 | av_freep(&s->encbuffer); |
98 | av_freep(&s->previous_frame); |
99 | av_freep(&s->tmpblock); |
100 | |
101 | return 0; |
102 | } |
103 | |
104 | static av_cold int flashsv_encode_init(AVCodecContext *avctx) |
105 | { |
106 | FlashSVContext *s = avctx->priv_data; |
107 | |
108 | s->avctx = avctx; |
109 | |
110 | if (avctx->width > 4095 || avctx->height > 4095) { |
111 | av_log(avctx, AV_LOG_ERROR, |
112 | "Input dimensions too large, input must be max 4095x4095 !\n"); |
113 | return AVERROR_INVALIDDATA; |
114 | } |
115 | |
116 | // Needed if zlib unused or init aborted before deflateInit |
117 | memset(&s->zstream, 0, sizeof(z_stream)); |
118 | |
119 | s->last_key_frame = 0; |
120 | |
121 | s->image_width = avctx->width; |
122 | s->image_height = avctx->height; |
123 | |
124 | s->tmpblock = av_mallocz(3 * 256 * 256); |
125 | s->encbuffer = av_mallocz(s->image_width * s->image_height * 3); |
126 | |
127 | if (!s->tmpblock || !s->encbuffer) { |
128 | av_log(avctx, AV_LOG_ERROR, "Memory allocation failed.\n"); |
129 | return AVERROR(ENOMEM); |
130 | } |
131 | |
132 | return 0; |
133 | } |
134 | |
135 | |
136 | static int encode_bitstream(FlashSVContext *s, const AVFrame *p, uint8_t *buf, |
137 | int buf_size, int block_width, int block_height, |
138 | uint8_t *previous_frame, int *I_frame) |
139 | { |
140 | |
141 | PutBitContext pb; |
142 | int h_blocks, v_blocks, h_part, v_part, i, j; |
143 | int buf_pos, res; |
144 | int pred_blocks = 0; |
145 | |
146 | init_put_bits(&pb, buf, buf_size); |
147 | |
148 | put_bits(&pb, 4, block_width / 16 - 1); |
149 | put_bits(&pb, 12, s->image_width); |
150 | put_bits(&pb, 4, block_height / 16 - 1); |
151 | put_bits(&pb, 12, s->image_height); |
152 | flush_put_bits(&pb); |
153 | buf_pos = 4; |
154 | |
155 | h_blocks = s->image_width / block_width; |
156 | h_part = s->image_width % block_width; |
157 | v_blocks = s->image_height / block_height; |
158 | v_part = s->image_height % block_height; |
159 | |
160 | /* loop over all block columns */ |
161 | for (j = 0; j < v_blocks + (v_part ? 1 : 0); j++) { |
162 | |
163 | int y_pos = j * block_height; // vertical position in frame |
164 | int cur_blk_height = (j < v_blocks) ? block_height : v_part; |
165 | |
166 | /* loop over all block rows */ |
167 | for (i = 0; i < h_blocks + (h_part ? 1 : 0); i++) { |
168 | int x_pos = i * block_width; // horizontal position in frame |
169 | int cur_blk_width = (i < h_blocks) ? block_width : h_part; |
170 | int ret = Z_OK; |
171 | uint8_t *ptr = buf + buf_pos; |
172 | |
173 | /* copy the block to the temp buffer before compression |
174 | * (if it differs from the previous frame's block) */ |
175 | res = copy_region_enc(p->data[0], s->tmpblock, |
176 | s->image_height - (y_pos + cur_blk_height + 1), |
177 | x_pos, cur_blk_height, cur_blk_width, |
178 | p->linesize[0], previous_frame); |
179 | |
180 | if (res || *I_frame) { |
181 | unsigned long zsize = 3 * block_width * block_height; |
182 | ret = compress2(ptr + 2, &zsize, s->tmpblock, |
183 | 3 * cur_blk_width * cur_blk_height, 9); |
184 | |
185 | //ret = deflateReset(&s->zstream); |
186 | if (ret != Z_OK) |
187 | av_log(s->avctx, AV_LOG_ERROR, |
188 | "error while compressing block %dx%d\n", i, j); |
189 | |
190 | bytestream_put_be16(&ptr, zsize); |
191 | buf_pos += zsize + 2; |
192 | ff_dlog(s->avctx, "buf_pos = %d\n", buf_pos); |
193 | } else { |
194 | pred_blocks++; |
195 | bytestream_put_be16(&ptr, 0); |
196 | buf_pos += 2; |
197 | } |
198 | } |
199 | } |
200 | |
201 | if (pred_blocks) |
202 | *I_frame = 0; |
203 | else |
204 | *I_frame = 1; |
205 | |
206 | return buf_pos; |
207 | } |
208 | |
209 | |
210 | static int flashsv_encode_frame(AVCodecContext *avctx, AVPacket *pkt, |
211 | const AVFrame *pict, int *got_packet) |
212 | { |
213 | FlashSVContext * const s = avctx->priv_data; |
214 | const AVFrame * const p = pict; |
215 | uint8_t *pfptr; |
216 | int res; |
217 | int I_frame = 0; |
218 | int opt_w = 4, opt_h = 4; |
219 | |
220 | /* First frame needs to be a keyframe */ |
221 | if (avctx->frame_number == 0) { |
222 | s->previous_frame = av_mallocz(FFABS(p->linesize[0]) * s->image_height); |
223 | if (!s->previous_frame) { |
224 | av_log(avctx, AV_LOG_ERROR, "Memory allocation failed.\n"); |
225 | return AVERROR(ENOMEM); |
226 | } |
227 | I_frame = 1; |
228 | } |
229 | |
230 | if (p->linesize[0] < 0) |
231 | pfptr = s->previous_frame - (s->image_height - 1) * p->linesize[0]; |
232 | else |
233 | pfptr = s->previous_frame; |
234 | |
235 | /* Check the placement of keyframes */ |
236 | if (avctx->gop_size > 0 && |
237 | avctx->frame_number >= s->last_key_frame + avctx->gop_size) { |
238 | I_frame = 1; |
239 | } |
240 | |
241 | if ((res = ff_alloc_packet2(avctx, pkt, s->image_width * s->image_height * 3, 0)) < 0) |
242 | return res; |
243 | |
244 | pkt->size = encode_bitstream(s, p, pkt->data, pkt->size, opt_w * 16, opt_h * 16, |
245 | pfptr, &I_frame); |
246 | |
247 | //save the current frame |
248 | if (p->linesize[0] > 0) |
249 | memcpy(s->previous_frame, p->data[0], s->image_height * p->linesize[0]); |
250 | else |
251 | memcpy(s->previous_frame, |
252 | p->data[0] + p->linesize[0] * (s->image_height - 1), |
253 | s->image_height * FFABS(p->linesize[0])); |
254 | |
255 | //mark the frame type so the muxer can mux it correctly |
256 | if (I_frame) { |
257 | #if FF_API_CODED_FRAME |
258 | FF_DISABLE_DEPRECATION_WARNINGS |
259 | avctx->coded_frame->pict_type = AV_PICTURE_TYPE_I; |
260 | avctx->coded_frame->key_frame = 1; |
261 | FF_ENABLE_DEPRECATION_WARNINGS |
262 | #endif |
263 | s->last_key_frame = avctx->frame_number; |
264 | ff_dlog(avctx, "Inserting keyframe at frame %d\n", avctx->frame_number); |
265 | } else { |
266 | #if FF_API_CODED_FRAME |
267 | FF_DISABLE_DEPRECATION_WARNINGS |
268 | avctx->coded_frame->pict_type = AV_PICTURE_TYPE_P; |
269 | avctx->coded_frame->key_frame = 0; |
270 | FF_ENABLE_DEPRECATION_WARNINGS |
271 | #endif |
272 | } |
273 | |
274 | if (I_frame) |
275 | pkt->flags |= AV_PKT_FLAG_KEY; |
276 | *got_packet = 1; |
277 | |
278 | return 0; |
279 | } |
280 | |
281 | AVCodec ff_flashsv_encoder = { |
282 | .name = "flashsv", |
283 | .long_name = NULL_IF_CONFIG_SMALL("Flash Screen Video"), |
284 | .type = AVMEDIA_TYPE_VIDEO, |
285 | .id = AV_CODEC_ID_FLASHSV, |
286 | .priv_data_size = sizeof(FlashSVContext), |
287 | .init = flashsv_encode_init, |
288 | .encode2 = flashsv_encode_frame, |
289 | .close = flashsv_encode_end, |
290 | .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_BGR24, AV_PIX_FMT_NONE }, |
291 | }; |
292 |