9 #define AVI_CUTOFF 2000000000
11 avi_frame::avi_frame(uint32_t _flags
, uint32_t _type
, uint32_t _offset
, uint32_t _size
)
19 void avi_frame::write(uint8_t* buf
)
21 //Yes, this is written big-endian!
34 buf
[10] = offset
>> 16;
35 buf
[11] = offset
>> 24;
45 struct dumper_thread_obj
47 int operator()(avidumper
* d
)
50 return d
->encode_thread();
51 } catch(std::exception
& e
) {
52 std::cerr
<< "Encode thread threw: " << e
.what() << std::endl
;
53 d
->set_capture_error(e
.what());
60 int avidumper::encode_thread()
62 umutex_class
_frame_mutex(frame_mutex
);
65 _frame_mutex
.unlock();
66 on_frame_threaded(mt_data
, mt_width
, mt_height
, mt_fps_n
, mt_fps_d
);
70 frame_cond
.notify_all();
71 frame_cond
.wait(_frame_mutex
);
76 avidumper::avidumper(const std::string
& _prefix
, struct avi_info parameters
)
78 compression_level
= parameters
.compression_level
;
79 audio_sampling_rate
= parameters
.audio_sampling_rate
;
80 keyframe_interval
= parameters
.keyframe_interval
;
81 maxframes
= parameters
.max_frames_per_segment
;
84 capture_error
= false;
104 std::cerr
<< "Creating thread..." << std::endl
;
105 dumper_thread_obj dto
;
106 frame_thread
= new thread_class(dto
, this);
107 std::cerr
<< "Created thread..." << std::endl
;
110 void avidumper::set_capture_error(const char* err
) throw()
113 capture_error
= true;
114 capture_error_str
= err
;
115 } catch(std::bad_alloc
& e
) {
119 void avidumper::on_sample(short left
, short right
) throw(std::bad_alloc
, std::runtime_error
)
122 throw std::runtime_error("Video capture thread crashed: " + capture_error_str
);
123 audio_buffer
[audio_put_ptr
++] = left
;
124 audio_buffer
[audio_put_ptr
++] = right
;
125 if(audio_put_ptr
== AVIDUMPER_AUDIO_BUFFER
)
129 void avidumper::on_frame(const uint32_t* data
, uint16_t width
, uint16_t height
, uint32_t fps_n
, uint32_t fps_d
)
130 throw(std::bad_alloc
, std::runtime_error
)
133 throw std::runtime_error("Video capture thread crashed: " + capture_error_str
);
136 audio_commit_ptr
= audio_put_ptr
;
142 frame_cond
.notify_all();
143 frame_mutex
.unlock();
145 on_frame_threaded(mt_data
, mt_width
, mt_height
, mt_fps_n
, mt_fps_d
);
152 std::string
fmtint(uint64_t val
, unsigned prec
)
154 std::ostringstream s2
;
155 s2
<< std::setw(prec
) << std::setfill(' ') << val
;
159 std::string
fmtdbl(double val
, unsigned prec
)
161 std::ostringstream s2
;
162 s2
<< std::setw(prec
) << std::setfill(' ') << val
;
163 std::string x
= s2
.str();
164 if(x
.length() == prec
)
166 size_t p
= x
.find_first_of("e");
168 return x
.substr(0, prec
);
169 return x
.substr(0, p
- (x
.length() - prec
)) + x
.substr(p
);
173 void avidumper::print_summary(std::ostream
& str
)
175 uint64_t local_segno
= current_segment
;
176 uint64_t local_vframes
= segment_frames
;
177 double local_vlength
= segment_frames
* static_cast<double>(pfps_d
) / pfps_n
;
178 uint64_t global_vframes
= total_frames
;
179 double global_vlength
= total_frames
* static_cast<double>(pfps_d
) / pfps_n
;
180 uint64_t local_aframes
= segment_samples
;
181 double local_alength
= static_cast<double>(segment_samples
) / audio_sampling_rate
;
182 uint64_t global_aframes
= total_samples
;
183 double global_alength
= static_cast<double>(total_samples
) / audio_sampling_rate
;
184 uint64_t local_size
= segment_movi_ptr
+ 352 + 16 * segment_chunks
.size();
185 uint64_t global_size
= total_data
+ 8 + 16 * segment_chunks
.size();
187 std::ostringstream s2
;
189 s2
<< "Quantity |This segment |All segments |" << std::endl
;
190 s2
<< "----------------+---------------------+---------------------+" << std::endl
;
191 s2
<< "Segment number | " << fmtint(local_segno
, 10) << "| N/A|" << std::endl
;
192 s2
<< "Video stream |" << fmtint(local_vframes
, 10) << "/" << fmtdbl(local_vlength
, 10) << "|"
193 << fmtint(global_vframes
, 10) << "/" << fmtdbl(global_vlength
, 10) << "|" << std::endl
;
194 s2
<< "Audio stream |" << fmtint(local_aframes
, 10) << "/" << fmtdbl(local_alength
, 10) << "|"
195 << fmtint(global_aframes
, 10) << "/" << fmtdbl(global_alength
, 10) << "|" << std::endl
;
196 s2
<< "A/V desync | " << fmtdbl(local_alength
- local_vlength
, 10) << "| "
197 << fmtdbl(global_alength
- global_vlength
, 10) << "|" << std::endl
;
198 s2
<< "Size | " << fmtint(local_size
, 10) << "| "
199 << fmtint(global_size
, 10) << "|" << std::endl
;
200 s2
<< "----------------+---------------------+---------------------+" << std::endl
;
205 void avidumper::on_frame_threaded(const uint32_t* data
, uint16_t width
, uint16_t height
, uint32_t fps_n
, uint32_t fps_d
)
206 throw(std::bad_alloc
, std::runtime_error
)
208 //The AVI part of sound to write is [audio_get, audio_commit). We don't write part [audio_commit,audio_put)
209 //yet, as it is being concurrently written. Also grab lock to read the commit value. Also, if global frame
210 //counter is 0, don't write audio to avoid A/V desync.
212 unsigned commit_to
= audio_commit_ptr
;
213 frame_mutex
.unlock();
215 flush_audio_to(commit_to
);
217 audio_get_ptr
= commit_to
;
219 if(segment_movi_ptr
> AVI_CUTOFF
- 16 * segment_chunks
.size() || (maxframes
&& segment_frames
> maxframes
))
220 fixup_avi_header_and_close();
222 uint16_t rheight
= (height
+ 3) / 4 * 4;
223 bool this_is_keyframe
;
224 if(width
!= pwidth
|| height
!= pheight
|| fps_n
!= pfps_n
|| fps_d
!= pfps_d
|| !avi_open
) {
225 std::cerr
<< "Starting segment # " << current_segment
<< ": " << width
<< "x" << height
<< "."
227 fixup_avi_header_and_close();
232 pframe
.resize(4 * static_cast<size_t>(width
) * height
);
233 tframe
.resize(4 * static_cast<size_t>(width
) * rheight
);
234 cframe
.resize(compressBound(4 * static_cast<size_t>(width
) * rheight
) + 13);
235 memset(&tframe
[0], 0, 4 * static_cast<size_t>(width
) * rheight
);
236 open_and_write_avi_header(width
, rheight
, fps_n
, fps_d
);
239 this_is_keyframe
= (segment_frames
== 0 || segment_frames
- segment_last_keyframe
>= keyframe_interval
);
241 if(this_is_keyframe
) {
242 memcpy(&tframe
[0], data
, 4 * static_cast<size_t>(width
) * height
);
243 segment_last_keyframe
= segment_frames
;
245 memcpy(&tframe
[0], data
, 4 * static_cast<size_t>(width
) * height
);
246 for(size_t i
= 0; i
< 4 * static_cast<size_t>(width
) * height
; i
++)
247 tframe
[i
] -= pframe
[i
];
249 uLongf l
= cframe
.size() - 10;
250 if(compress2(&cframe
[10], &l
, &tframe
[0], tframe
.size(), compression_level
) != Z_OK
)
251 throw std::runtime_error("Error compressing frame");
258 cframe
[3] = 'b'; //strictly speaking, this is wrong, but FCEUX does this when dumping.
260 cframe
[5] = (l
+ 2) >> 8;
261 cframe
[6] = (l
+ 2) >> 16;
262 cframe
[7] = (l
+ 2) >> 24;
263 cframe
[8] = (this_is_keyframe
? 0x3 : 0x2) | (compression_level
<< 4);
265 avi_stream
.write(reinterpret_cast<char*>(&cframe
[0]), l
+ 10);
267 throw std::runtime_error("Error writing video frame");
268 //Flags is 0x10 for keyframes, because those frames are always keyframes, chunks, independent and take time.
269 //For non-keyframes, flags are 0x00 (chunk, not a keyframe).
270 segment_chunks
.push_back(avi_frame(this_is_keyframe
? 0x10 : 0x00, 0x30306462, segment_movi_ptr
+ 4, l
+ 2));
271 segment_movi_ptr
+= (l
+ 10);
272 total_data
+= (l
+ 10);
276 if((segment_frames
% 1200) == 0)
277 print_summary(std::cerr
);
279 memcpy(&pframe
[0], data
, 4 * static_cast<size_t>(width
) * height
);
282 void avidumper::on_end() throw(std::bad_alloc
, std::runtime_error
)
285 throw std::runtime_error("Video capture thread crashed: " + capture_error_str
);
288 frame_cond
.notify_all();
289 frame_mutex
.unlock();
290 frame_thread
->join();
292 flush_audio_to(audio_put_ptr
);
293 fixup_avi_header_and_close();
327 ptr(uint32_t& _p
) : p(_p
)
333 void append(std::vector
<uint8_t>& v
)
337 template<typename
... rest
>
338 void append(std::vector
<uint8_t>& v
, struct str s
, rest
... _rest
)
340 const char* str
= s
.s
;
342 v
.push_back(*(str
++));
346 template<typename
... rest
>
347 void append(std::vector
<uint8_t>& v
, struct u16 u
, rest
... _rest
)
351 v
.push_back(val
>> 8);
355 template<typename
... rest
>
356 void append(std::vector
<uint8_t>& v
, struct u32 u
, rest
... _rest
)
360 v
.push_back(val
>> 8);
361 v
.push_back(val
>> 16);
362 v
.push_back(val
>> 24);
366 template<typename
... rest
>
367 void append(std::vector
<uint8_t>& v
, ptr u
, rest
... _rest
)
373 void fix_write(std::ostream
& str
, uint32_t off
, uint32_t val
)
375 str
.seekp(off
, std::ios::beg
);
383 throw std::runtime_error("Can't fixup AVI header");
387 void avidumper::flush_audio_to(unsigned commit_to
)
392 //Count the number of samples to actually write.
393 unsigned samples_to_write
= 0;
394 unsigned aptr
= audio_get_ptr
;
396 while(aptr
!= commit_to
) {
398 if((aptr
+= 2) == AVIDUMPER_AUDIO_BUFFER
)
402 std::vector
<uint8_t> buf
;
403 buf
.resize(8 + 4 * samples_to_write
);
408 buf
[4] = (4 * samples_to_write
);
409 buf
[5] = (4 * samples_to_write
) >> 8;
410 buf
[6] = (4 * samples_to_write
) >> 16;
411 buf
[7] = (4 * samples_to_write
) >> 24;
413 while(audio_get_ptr
!= commit_to
) {
415 buf
[idx
] = static_cast<unsigned short>(audio_buffer
[audio_get_ptr
]);
416 buf
[idx
+ 1] = static_cast<unsigned short>(audio_buffer
[audio_get_ptr
]) >> 8;
417 buf
[idx
+ 2] = static_cast<unsigned short>(audio_buffer
[audio_get_ptr
+ 1]);
418 buf
[idx
+ 3] = static_cast<unsigned short>(audio_buffer
[audio_get_ptr
+ 1]) >> 8;
422 if((audio_get_ptr
+= 2) == AVIDUMPER_AUDIO_BUFFER
)
425 assert(idx
== 8 + 4 * samples_to_write
);
426 avi_stream
.write(reinterpret_cast<char*>(&buf
[0]), idx
);
428 throw std::runtime_error("Error writing audio frame");
429 //Flags is 0x10, because sound frames are always keyframes, chunks, independent and take time.
430 segment_chunks
.push_back(avi_frame(0x10, 0x30317762, segment_movi_ptr
+ 4, 4 * samples_to_write
));
431 segment_movi_ptr
+= idx
;
435 void avidumper::open_and_write_avi_header(uint16_t width
, uint16_t height
, uint32_t fps_n
, uint32_t fps_d
)
439 std::ostringstream str
;
440 str
<< prefix
<< "_" << std::setw(9) << std::setfill('0') << (current_segment
++) << ".avi";
444 avi_stream
.open(fstr
.c_str(), std::ios::out
| std::ios::binary
);
446 throw std::runtime_error("Can't open output AVI file");
447 std::vector
<uint8_t> aviheader
;
448 uint32_t usecs_per_frame
= static_cast<uint32_t>(1000000ULL * fps_d
/ fps_n
);
450 /* AVI main chunk header. */
451 /* The tentative AVI header size of 336 doesn't include the video data, so we need to fix it up later. */
452 append(aviheader
, str("RIFF"), ptr(fixup_avi_size
), u32(336), str("AVI "));
453 /* Header list header. Has 312 bytes of data. */
454 append(aviheader
, str("LIST"), u32(312), str("hdrl"));
455 /* Main AVI header. */
456 append(aviheader
, str("avih"), u32(56), /* 56 byte header of type avih. */
457 u32(usecs_per_frame
), /* usecs per frame. */
458 u32(1000000), /* Max transfer rate... Give some random value. */
459 u32(0), /* Padding granularity (no padding). */
460 u32(2064), /* Flags... Has index, trust chunk types */
461 ptr(fixup_avi_frames
), u32(0), /* Frame count... To be fixed later. */
462 u32(0), /* Initial frames... We don't have any. */
463 u32(2), /* 2 streams (video + audio). */
464 u32(1000000), /* Suggested buffer size... Give some random value. */
465 u32(width
), u32(height
), /* Size of image. */
466 u32(0), u32(0), u32(0), u32(0)); /* Reserved. */
467 /* Stream list header For stream #1, 124 bytes of data. */
468 append(aviheader
, str("LIST"), u32(124), str("strl"));
469 /* Stream header for stream #1 (video). */
470 append(aviheader
, str("strh"), u32(64), /* 64 byte header of type strh */
471 str("vids"), u32(0), /* Video data??? */
472 u32(0), /* Some flags that are all clear. */
473 u16(0), u16(0), /* Priority and language... Doesn't matter. */
474 u32(0), /* Initial frames... We don't have any. */
475 u32(fps_d
), u32(fps_n
), /* Frame rate is fps_n / fps_d. */
476 u32(0), /* Starting time... It starts at t=0. */
477 ptr(fixup_avi_length
), u32(0), /* Video length (to be fixed later). */
478 u32(1000000), /* Suggested buffer size... Just give some random value. */
479 u32(9999), /* Quality... Doesn't matter. */
480 u32(4), /* Video sample size... 32bpp. */
481 u32(0), u32(0), /* Bounding box upper left. */
482 u32(width
), u32(height
)); /* Bounding box lower right. */
483 /* BITMAPINFO header for the video stream. */
484 append(aviheader
, str("strf"), u32(40), /* 40 byte header of type strf. */
485 u32(40), /* BITMAPINFOHEADER is 40 bytes. */
486 u32(width
), u32(height
), /* Image size. */
487 u16(1), u16(32), /* 1 plane, 32 bits (RGB32). */
488 str("CSCD"), /* Compressed with Camstudio codec. */
489 u32(4 * width
* height
), /* Image size. */
490 u32(4000), u32(4000), /* Resolution... Give some random values. */
491 u32(0), u32(0)); /* Colors used values (0 => All colors used). */
492 /* Stream list header For stream #2, 104 bytes of data. */
493 append(aviheader
, str("LIST"), u32(104), str("strl"));
494 /* Stream header for stream #2. */
495 append(aviheader
, str("strh"), u32(64), /* 64 byte header of type strh */
496 str("auds"), u32(0), /* audio data??? */
497 u32(0), /* Flags... None set. */
498 u16(0), u16(0), /* Priority and language... Doesn't matter. */
499 u32(0), /* Initial frames... None. */
500 u32(1), u32(audio_sampling_rate
), /* Audio sampling rate. */
501 u32(0), /* Starts at t=0s. */
502 ptr(fixup_avi_a_length
), u32(0), /* Audio length (to be fixed later). */
503 u32(4096), /* Suggested buffer size... Some random value. */
504 u32(5), /* Audio quality... Some random value. */
505 u32(4), /* Sample size (16bit Stereo PCM). */
506 u32(0), u32(0), u32(0), u32(0)); /* Bounding box, not sane for audio data. */
507 /* WAVEFORMAT header for the audio stream. */
508 append(aviheader
, str("strf"), u32(20), /* 20 byte header of type strf. */
510 u16(2), /* Stereo. */
511 u32(audio_sampling_rate
), /* Audio Sampling rate. */
512 u32(4 * audio_sampling_rate
), /* Audio transfer rate (4 times sampling rate). */
513 u16(4), /* Sample size. */
514 u16(16), /* Bits per sample. */
515 u16(0), /* Extension size... We don't have extension. */
516 u16(0)); /* Dummy. */
517 /* MOVI list header. 4 bytes without movie data. */
518 append(aviheader
, str("LIST"), ptr(fixup_movi_size
), u32(4), str("movi"));
519 avi_stream
.write(reinterpret_cast<char*>(&aviheader
[0]), aviheader
.size());
521 throw std::runtime_error("Can't write AVI header");
522 total_data
+= aviheader
.size();
524 segment_movi_ptr
= 0;
527 segment_last_keyframe
= 0;
528 segment_chunks
.clear();
531 void avidumper::fixup_avi_header_and_close()
535 print_summary(std::cerr
);
541 buf
[4] = (16 * segment_chunks
.size());
542 buf
[5] = (16 * segment_chunks
.size()) >> 8;
543 buf
[6] = (16 * segment_chunks
.size()) >> 16;
544 buf
[7] = (16 * segment_chunks
.size()) >> 24;
545 avi_stream
.write(reinterpret_cast<char*>(buf
), 8);
547 throw std::runtime_error("Error writing index header");
548 for(auto i
= segment_chunks
.begin(); i
!= segment_chunks
.end(); ++i
) {
550 avi_stream
.write(reinterpret_cast<char*>(buf
), 16);
552 throw std::runtime_error("Error writing index entry");
554 total_data
+= (8 + 16 * segment_chunks
.size());
556 fix_write(avi_stream
, fixup_avi_size
, 344 + segment_movi_ptr
+ 16 * segment_chunks
.size());
557 fix_write(avi_stream
, fixup_avi_frames
, segment_frames
);
558 fix_write(avi_stream
, fixup_avi_length
, segment_frames
);
559 fix_write(avi_stream
, fixup_avi_a_length
, segment_samples
);
560 fix_write(avi_stream
, fixup_movi_size
, 4 + segment_movi_ptr
);
561 segment_chunks
.clear();
566 avidumper::~avidumper() throw()
571 void avidumper::wait_idle() throw()
573 umutex_class
_frame_mutex(frame_mutex
);
577 frame_cond
.wait(_frame_mutex
);