}
}
+string generate_local_dump_filename(int frame)
+{
+ time_t now = time(NULL);
+ tm now_tm;
+ localtime_r(&now, &now_tm);
+
+ char timestamp[256];
+ strftime(timestamp, sizeof(timestamp), "%F-%T%z", &now_tm);
+
+ // Use the frame number to disambiguate between two cuts starting
+ // on the same second.
+ char filename[256];
+ snprintf(filename, sizeof(filename), "%s%s-f%02d%s",
+ LOCAL_DUMP_PREFIX, timestamp, frame % 100, LOCAL_DUMP_SUFFIX);
+ return filename;
+}
} // namespace
Mixer::Mixer(const QSurfaceFormat &format, unsigned num_cards)
- : httpd(LOCAL_DUMP_FILE_NAME, WIDTH, HEIGHT),
+ : httpd(WIDTH, HEIGHT),
num_cards(num_cards),
mixer_surface(create_surface(format)),
h264_encoder_surface(create_surface(format)),
limiter(OUTPUT_FREQUENCY),
compressor(OUTPUT_FREQUENCY)
{
+ httpd.open_output_file(generate_local_dump_filename(/*frame=*/0).c_str());
httpd.start(9095);
CHECK(init_movit(MOVIT_SHADER_DIR, MOVIT_DEBUG_OFF));
cards[card_index].usb->start_bm_capture();
}
- //chain->enable_phase_timing(true);
-
// Set up stuff for NV12 conversion.
// Cb/Cr shader.
{
float m = fabs(samples[0]);
for (size_t i = 1; i < num_samples; ++i) {
- m = std::max(m, fabs(samples[i]));
+ m = max(m, fabs(samples[i]));
}
return m;
}
if (card->should_quit) return;
}
+ size_t expected_length = width * (height + extra_lines_top + extra_lines_bottom) * 2;
if (video_frame.len - video_offset == 0 ||
- video_frame.len - video_offset != size_t(width * (height + extra_lines_top + extra_lines_bottom) * 2)) {
+ video_frame.len - video_offset != expected_length) {
if (video_frame.len != 0) {
- printf("Card %d: Dropping video frame with wrong length (%ld)\n",
- card_index, video_frame.len - video_offset);
+ printf("Card %d: Dropping video frame with wrong length (%ld; expected %ld)\n",
+ card_index, video_frame.len - video_offset, expected_length);
}
if (video_frame.owner) {
video_frame.owner->release_frame(video_frame);
unsigned num_fields = interlaced ? 2 : 1;
timespec frame_upload_start;
if (interlaced) {
- // NOTE: This isn't deinterlacing. This is just sending the two fields along
- // as separate frames without considering anything like the half-field offset.
- // We'll need to add a proper deinterlacer on the receiving side to get this right.
+ // Send the two fields along as separate frames; the other side will need to add
+ // a deinterlacer to actually get this right.
assert(height % 2 == 0);
height /= 2;
assert(frame_length % 2 == 0);
num_fields = 2;
clock_gettime(CLOCK_MONOTONIC, &frame_upload_start);
}
+ userdata->last_interlaced = interlaced;
+ userdata->last_frame_rate_nom = frame_rate_nom;
+ userdata->last_frame_rate_den = frame_rate_den;
RefCountedFrame new_frame(video_frame);
// Upload the textures.
Theme::Chain theme_main_chain = theme->get_chain(0, pts(), WIDTH, HEIGHT, input_state);
EffectChain *chain = theme_main_chain.chain;
theme_main_chain.setup_chain();
+ //theme_main_chain.chain->enable_phase_timing(true);
GLuint y_tex, cbcr_tex;
bool got_frame = h264_encoder->begin_frame(&y_tex, &cbcr_tex);
// chain->print_phase_timing();
}
+ if (should_cut.exchange(false)) { // Test and clear.
+ string filename = generate_local_dump_filename(frame);
+ printf("Starting new recording: %s\n", filename.c_str());
+ h264_encoder->shutdown();
+ httpd.close_output_file();
+ httpd.open_output_file(filename.c_str());
+ h264_encoder.reset(new H264Encoder(h264_encoder_surface, WIDTH, HEIGHT, &httpd));
+ }
+
#if 0
// Reset every 100 frames, so that local variations in frame times
// (especially for the first few frames, when the shaders are