New settings framework
[lsnes.git] / src / video / avi / codec / video / tscc.cpp
blob1c57a84e9d12d4c801b267e9a83632d3fd31ac49
1 #include "video/avi/codec.hpp"
2 #include "library/minmax.hpp"
3 #include "library/zlibstream.hpp"
4 #include "core/settings.hpp"
5 #include <limits>
6 #include <cassert>
7 #include <cstring>
8 #include <cerrno>
9 #include <stdexcept>
11 #define CBUFFER 65536
13 const void* check;
15 namespace
17 setting_var<setting_var_model_int<0,9>> clvl(lsnes_vset, "avi-tscc-compression", "AVI‣TSCC‣Compression", 7);
18 setting_var<setting_var_model_int<0,999999999>> kint(lsnes_vset, "avi-tscc-keyint",
19 "AVI‣TSCC‣Keyframe interval", 299);
21 struct msrle_compressor
23 /**
24 * Set the frames involved with compression.
26 * Parameter thisframe: The frame to compress.
27 * Parameter prevframe: The previous frame. May be NULL to signal no previous frame.
28 * Parameter width: The width of frame.
29 * Parameter height: The height of frame.
31 void setframes(const uint8_t* thisframe, const uint8_t* prevframe, size_t width, size_t height);
32 /**
33 * Read compressed data.
35 * Parameter buffer: The buffer to fill with compressed data. Must be at least 770 bytes.
36 * Returns: Amount of data filled. Calls after image is compressed always return 0.
38 size_t read(uint8_t* buffer);
39 const uint8_t* tframe;
40 private:
41 const uint8_t* pframe;
42 size_t fwidth;
43 size_t fheight;
44 size_t fstride;
45 size_t cptr_x; //Compression pointer x. If fwidth, next thing will be end of line.
46 size_t cptr_y; //Compression pointer y.
47 size_t do_end_of_line(uint8_t* buffer);
48 size_t do_end_of_picture(uint8_t* buffer);
49 size_t do_null_frame(uint8_t* buffer);
50 size_t do_xy_delta(uint8_t* buffer, size_t newx, size_t newy);
51 size_t do_rle(uint8_t* buffer, size_t count, uint8_t d1, uint8_t d2, uint8_t d3);
52 size_t maxrle(const uint8_t* data, size_t bound);
55 size_t msrle_compressor::do_rle(uint8_t* buffer, size_t count, uint8_t d1, uint8_t d2, uint8_t d3)
57 buffer[0] = min(count, fwidth - cptr_x);
58 buffer[1] = d1;
59 buffer[2] = d2;
60 buffer[3] = d3;
61 cptr_x += buffer[0];
62 return 4;
65 size_t msrle_compressor::maxrle(const uint8_t* d, size_t bound)
67 size_t r = 0;
68 while(r < bound && d[0] == d[3 * r + 0] && d[1] == d[3 * r + 1] && d[2] == d[3 * r + 2])
69 r++;
70 return r;
73 size_t msrle_compressor::do_end_of_line(uint8_t* buffer)
75 buffer[0] = 0;
76 buffer[1] = (cptr_y == fheight - 1) ? 1 : 0;
77 cptr_x = 0;
78 cptr_y++;
79 return 2;
82 size_t msrle_compressor::do_end_of_picture(uint8_t* buffer)
84 buffer[0] = 0;
85 buffer[1] = 1;
86 cptr_x = 0;
87 cptr_y = fheight;
88 return 2;
91 size_t msrle_compressor::do_null_frame(uint8_t* buffer)
93 cptr_x = 0;
94 cptr_y = fheight;
95 return 0;
98 size_t msrle_compressor::do_xy_delta(uint8_t* buffer, size_t newx, size_t newy)
100 newx = min(newx, cptr_x + 255);
101 newy = min(newy, cptr_y + 255);
102 buffer[0] = 0;
103 buffer[1] = 2;
104 buffer[2] = newx - cptr_x;
105 buffer[3] = newy - cptr_y;
106 cptr_x = newx;
107 cptr_y = newy;
108 return 4;
111 void msrle_compressor::setframes(const uint8_t* thisframe, const uint8_t* prevframe, size_t width,
112 size_t height)
114 tframe = thisframe;
115 check = tframe;
116 pframe = prevframe;
117 fwidth = width;
118 fheight = height;
119 fstride = 3 * width;
120 cptr_x = 0;
121 cptr_y = 0;
124 size_t msrle_compressor::read(uint8_t* buffer)
126 //If at end of picture, emit nothing.
127 if(cptr_y == fheight)
128 return 0;
129 //If at end of line, emit end of line / end of picture.
130 if(cptr_x == fwidth)
131 return do_end_of_line(buffer);
132 if(pframe) {
133 //See if we can reuse content from previous image.
134 size_t cptr = cptr_y * fstride + 3 * cptr_x;
135 size_t maxcptr = fheight * fstride;
136 while(cptr < maxcptr)
137 if(tframe[cptr] != pframe[cptr])
138 break;
139 else
140 cptr++;
141 size_t next_x = (cptr % fstride) / 3;
142 size_t next_y = cptr / fstride;
143 //1) End of picture.
144 if(next_y == fheight)
145 return do_end_of_picture(buffer);
146 //2) emit xy-delta.
147 if(next_x >= cptr_x && next_y > cptr_y)
148 return do_xy_delta(buffer, next_x, next_y);
149 //3) Emit y delta followed by end of line.
150 if(next_y > cptr_y + 1) {
151 size_t p = do_xy_delta(buffer, cptr_x, next_y - 1);
152 return p + do_end_of_line(buffer + p);
154 //4) Emit end of line.
155 if(next_y == cptr_y + 1)
156 return do_end_of_line(buffer);
157 //5) Emit X delta.
158 if(next_x > cptr_x)
159 return do_xy_delta(buffer, next_x, cptr_y);
161 //If RLE is possible, do that.
162 const uint8_t* d = &tframe[cptr_y * fstride + 3 * cptr_x];
163 size_t maxreps = maxrle(d, min(static_cast<size_t>(255), fwidth - cptr_x));
164 if(maxreps > 1 || fwidth < cptr_x + 3) //Also do this if near end of line.
165 return do_rle(buffer, maxreps, d[0], d[1], d[2]);
166 //If RLE is not possible within 3 positions, emit literial insert.
167 size_t maxreps1 = maxrle(d + 3, min(static_cast<size_t>(255), fwidth - cptr_x - 1));
168 size_t maxreps2 = maxrle(d + 6, min(static_cast<size_t>(255), fwidth - cptr_x - 2));
169 if(maxreps1 == 1 && maxreps2 == 1) {
170 //TODO.
172 //Fallback.
173 return do_rle(buffer, 1, d[0], d[1], d[2]);
176 struct avi_codec_tscc : public avi_video_codec
178 avi_codec_tscc(unsigned level);
179 ~avi_codec_tscc();
180 avi_video_codec::format reset(uint32_t width, uint32_t height, uint32_t fps_n, uint32_t fps_d);
181 void frame(uint32_t* data);
182 bool ready();
183 avi_packet getpacket();
184 private:
185 void readrow(uint32_t* rptr);
186 avi_packet out;
187 bool ready_flag;
188 unsigned iwidth;
189 unsigned iheight;
190 unsigned ewidth;
191 unsigned eheight;
192 unsigned pframes;
193 unsigned max_pframes;
194 unsigned level;
195 uint64_t frameno;
196 zlibstream z;
197 std::vector<uint8_t> _frame;
198 std::vector<uint8_t> prevframe;
201 unsigned getzlevel(uint32_t _level)
203 if(_level < 0 || _level > 9)
204 throw std::runtime_error("Invalid compression level");
205 return _level;
208 avi_codec_tscc::avi_codec_tscc(unsigned compression)
209 : z(getzlevel(compression))
211 frameno = 0;
214 avi_codec_tscc::~avi_codec_tscc()
218 avi_video_codec::format avi_codec_tscc::reset(uint32_t width, uint32_t height, uint32_t fps_n,
219 uint32_t fps_d)
221 pframes = std::numeric_limits<unsigned>::max(); //Next frame has to be keyframe.
222 iwidth = width;
223 iheight = height;
224 ewidth = (iwidth + 3) >> 2 << 2;
225 eheight = (iheight + 3) >> 2 << 2;
226 ready_flag = true;
227 _frame.resize(3 * ewidth * eheight);
228 prevframe.resize(3 * ewidth * eheight);
229 memset(&_frame[0], 0, 3 * ewidth * eheight);
230 avi_video_codec::format fmt(ewidth, eheight, 0x43435354, 24);
231 return fmt;
234 //24-bit MSRLE variant:
235 //00 00: End of line.
236 //00 01: End of bitmap.
237 //00 02 xx yy: Move x pixels right, y pixels down.
238 //00 03-FF <pixels>: 3-255 literal pixels (determined by the second byte).
239 //01-FF <pixel>: 1-255 repetions of pixel (determined by the first byte).
241 void avi_codec_tscc::frame(uint32_t* data)
243 msrle_compressor c;
244 bool keyframe = false;
245 if(pframes >= max_pframes) {
246 keyframe = true;
247 pframes = 0;
248 } else
249 pframes++;
251 //Reduce the frame to rgb24.
252 for(uint32_t y = eheight - iheight; y < eheight; y++) {
253 const uint32_t* rptr = data + (eheight - y - 1) * iwidth;
254 for(uint32_t i = 0; i < iwidth; i++) {
255 _frame[3 * y * ewidth + 3 * i + 0] = rptr[i] >> 16;
256 _frame[3 * y * ewidth + 3 * i + 1] = rptr[i] >> 8;
257 _frame[3 * y * ewidth + 3 * i + 2] = rptr[i] >> 0;
261 //NULL frames are disabled for now. Even without these, no-change frames are very small,
262 //as those are just end of frame code.
263 //if(!keyframe && !memcmp(&_frame[0], &prevframe[0], 3 * ewidth * eheight)) {
264 // //Special: NULL frame.
265 // out.payload.resize(0);
266 // out.typecode = 0x6264;
267 // out.hidden = false;
268 // out.indexflags = 0x00;
269 // ready_flag = false;
270 // return;
273 //This codec is extended version of MSRLE followed by deflate.
274 c.setframes(&_frame[0], keyframe ? NULL : &prevframe[0], ewidth, eheight);
275 size_t block;
276 unsigned char buffer[CBUFFER];
277 size_t blockused = 0;
278 z.reset(NULL, 0);
279 while(!z.get_flag()) {
280 block = c.read(buffer + blockused);
281 if(!block)
282 z.set_flag(true);
283 blockused += block;
284 if((blockused + 770 > CBUFFER) || z.get_flag()) {
285 z.write(buffer, blockused);
286 blockused = 0;
289 z.read(out.payload);
290 memcpy(&prevframe[0], &_frame[0], 3 * ewidth * eheight);
291 out.typecode = 0x6264;
292 out.hidden = false;
293 out.indexflags = keyframe ? 0x10 : 0x00;
294 ready_flag = false;
297 bool avi_codec_tscc::ready()
299 return ready_flag;
302 avi_packet avi_codec_tscc::getpacket()
304 ready_flag = true;
305 return out;
308 avi_video_codec_type rgb("tscc", "TSCC video codec",
309 []() -> avi_video_codec* { return new avi_codec_tscc(clvl);});