1 #include "video/avi/codec.hpp"
2 #include "library/minmax.hpp"
3 #include "library/zlibstream.hpp"
4 #include "core/settings.hpp"
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
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
);
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
;
41 const uint8_t* pframe
;
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
);
65 size_t msrle_compressor::maxrle(const uint8_t* d
, size_t bound
)
68 while(r
< bound
&& d
[0] == d
[3 * r
+ 0] && d
[1] == d
[3 * r
+ 1] && d
[2] == d
[3 * r
+ 2])
73 size_t msrle_compressor::do_end_of_line(uint8_t* buffer
)
76 buffer
[1] = (cptr_y
== fheight
- 1) ? 1 : 0;
82 size_t msrle_compressor::do_end_of_picture(uint8_t* buffer
)
91 size_t msrle_compressor::do_null_frame(uint8_t* buffer
)
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);
104 buffer
[2] = newx
- cptr_x
;
105 buffer
[3] = newy
- cptr_y
;
111 void msrle_compressor::setframes(const uint8_t* thisframe
, const uint8_t* prevframe
, size_t width
,
124 size_t msrle_compressor::read(uint8_t* buffer
)
126 //If at end of picture, emit nothing.
127 if(cptr_y
== fheight
)
129 //If at end of line, emit end of line / end of picture.
131 return do_end_of_line(buffer
);
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
])
141 size_t next_x
= (cptr
% fstride
) / 3;
142 size_t next_y
= cptr
/ fstride
;
144 if(next_y
== fheight
)
145 return do_end_of_picture(buffer
);
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
);
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) {
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
);
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
);
183 avi_packet
getpacket();
185 void readrow(uint32_t* rptr
);
193 unsigned max_pframes
;
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");
208 avi_codec_tscc::avi_codec_tscc(unsigned compression
)
209 : z(getzlevel(compression
))
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
,
221 pframes
= std::numeric_limits
<unsigned>::max(); //Next frame has to be keyframe.
224 ewidth
= (iwidth
+ 3) >> 2 << 2;
225 eheight
= (iheight
+ 3) >> 2 << 2;
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);
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
)
244 bool keyframe
= false;
245 if(pframes
>= max_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;
273 //This codec is extended version of MSRLE followed by deflate.
274 c
.setframes(&_frame
[0], keyframe
? NULL
: &prevframe
[0], ewidth
, eheight
);
276 unsigned char buffer
[CBUFFER
];
277 size_t blockused
= 0;
279 while(!z
.get_flag()) {
280 block
= c
.read(buffer
+ blockused
);
284 if((blockused
+ 770 > CBUFFER
) || z
.get_flag()) {
285 z
.write(buffer
, blockused
);
290 memcpy(&prevframe
[0], &_frame
[0], 3 * ewidth
* eheight
);
291 out
.typecode
= 0x6264;
293 out
.indexflags
= keyframe
? 0x10 : 0x00;
297 bool avi_codec_tscc::ready()
302 avi_packet
avi_codec_tscc::getpacket()
308 avi_video_codec_type
rgb("tscc", "TSCC video codec",
309 []() -> avi_video_codec
* { return new avi_codec_tscc(clvl
);});