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