#include "decklink_capture.h"
#include "defs.h"
#include "flags.h"
-#include "h264encode.h"
+#include "video_encoder.h"
#include "pbo_frame_allocator.h"
#include "ref_counted_gl_sync.h"
#include "timebase.h"
}
}
-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
void QueueLengthPolicy::update_policy(int queue_length)
if (queue_length >= int(safe_queue_length)) {
been_at_safe_point_since_last_starvation = true;
}
- if (++frames_with_at_least_one >= 50 && safe_queue_length > 0) {
+ if (++frames_with_at_least_one >= 1000 && safe_queue_length > 0) {
--safe_queue_length;
- fprintf(stderr, "Card %u: Spare frames for more than 50 frames, reducing safe limit to %u frames\n",
+ fprintf(stderr, "Card %u: Spare frames for more than 1000 frames, reducing safe limit to %u frames\n",
card_index, safe_queue_length);
frames_with_at_least_one = 0;
}
}
Mixer::Mixer(const QSurfaceFormat &format, unsigned num_cards)
- : httpd(WIDTH, HEIGHT),
+ : httpd(),
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));
check_error();
display_chain->set_dither_bits(0); // Don't bother.
display_chain->finalize();
- h264_encoder.reset(new H264Encoder(h264_encoder_surface, global_flags.va_display, WIDTH, HEIGHT, &httpd));
+ video_encoder.reset(new VideoEncoder(resource_pool.get(), h264_encoder_surface, global_flags.va_display, WIDTH, HEIGHT, &httpd));
+
+ // Start listening for clients only once VideoEncoder has written its header, if any.
+ httpd.start(9095);
// First try initializing the PCI devices, then USB, until we have the desired number of cards.
unsigned num_pci_devices = 0, num_usb_devices = 0;
cards[card_index].capture->stop_dequeue_thread();
}
- h264_encoder.reset(nullptr);
+ video_encoder.reset(nullptr);
}
void Mixer::configure_card(unsigned card_index, const QSurfaceFormat &format, CaptureInterface *capture)
check_error();
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
check_error();
- glFlushMappedBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, video_frame.size);
- check_error();
+
+ size_t field_y_start = y_offset + video_format.width * field_start_line;
+ size_t field_cbcr_start = cbcr_offset + cbcr_width * field_start_line * sizeof(uint16_t);
+
+ if (global_flags.flush_pbos) {
+ glFlushMappedBufferRange(GL_PIXEL_UNPACK_BUFFER, field_y_start, video_format.width * video_format.height);
+ check_error();
+ glFlushMappedBufferRange(GL_PIXEL_UNPACK_BUFFER, field_cbcr_start, cbcr_width * video_format.height * sizeof(uint16_t));
+ check_error();
+ }
glBindTexture(GL_TEXTURE_2D, userdata->tex_cbcr[field]);
check_error();
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, cbcr_width, video_format.height, GL_RG, GL_UNSIGNED_BYTE, BUFFER_OFFSET(cbcr_offset + cbcr_width * field_start_line * sizeof(uint16_t)));
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, cbcr_width, video_format.height, GL_RG, GL_UNSIGNED_BYTE, BUFFER_OFFSET(field_cbcr_start));
check_error();
glBindTexture(GL_TEXTURE_2D, userdata->tex_y[field]);
check_error();
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, video_format.width, video_format.height, GL_RED, GL_UNSIGNED_BYTE, BUFFER_OFFSET(y_offset + video_format.width * field_start_line));
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, video_format.width, video_format.height, GL_RED, GL_UNSIGNED_BYTE, BUFFER_OFFSET(field_y_start));
check_error();
glBindTexture(GL_TEXTURE_2D, 0);
check_error();
RefCountedGLsync fence(GL_SYNC_GPU_COMMANDS_COMPLETE, /*flags=*/0);
check_error();
assert(fence.get() != nullptr);
+ glFlush(); // Make sure the main thread doesn't have to wait until we push out enough frames to make a new command buffer.
+ check_error();
if (field == 1) {
// Don't upload the second field as fast as we can; wait until
}
}
- render_one_frame();
+ int64_t duration = new_frames[master_card_index].length;
+ render_one_frame(duration);
++frame;
- pts_int += new_frames[master_card_index].length;
+ pts_int += duration;
clock_gettime(CLOCK_MONOTONIC, &now);
double elapsed = now.tv_sec - start.tv_sec +
}
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, global_flags.va_display, WIDTH, HEIGHT, &httpd));
+ video_encoder->do_cut(frame);
}
#if 0
}
}
-void Mixer::render_one_frame()
+void Mixer::render_one_frame(int64_t duration)
{
// Get the main chain from the theme, and set its state immediately.
Theme::Chain theme_main_chain = theme->get_chain(0, pts(), WIDTH, HEIGHT, input_state);
//theme_main_chain.chain->enable_phase_timing(true);
GLuint y_tex, cbcr_tex;
- bool got_frame = h264_encoder->begin_frame(&y_tex, &cbcr_tex);
+ bool got_frame = video_encoder->begin_frame(&y_tex, &cbcr_tex);
assert(got_frame);
// Render main chain.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- RefCountedGLsync fence(GL_SYNC_GPU_COMMANDS_COMPLETE, /*flags=*/0);
- check_error();
-
const int64_t av_delay = TIMEBASE / 10; // Corresponds to the fixed delay in resampling_queue.h. TODO: Make less hard-coded.
- h264_encoder->end_frame(fence, pts_int + av_delay, theme_main_chain.input_frames);
+ RefCountedGLsync fence = video_encoder->end_frame(pts_int + av_delay, duration, theme_main_chain.input_frames);
// The live frame just shows the RGBA texture we just rendered.
// It owns rgba_tex now.
}
// And finally add them to the output.
- h264_encoder->add_audio(frame_pts_int, move(samples_out));
+ video_encoder->add_audio(frame_pts_int, move(samples_out));
}
void Mixer::subsample_chroma(GLuint src_tex, GLuint dst_tex)
new_frame_ready_callback = callback;
has_new_frame_ready_callback = true;
}
+
+mutex RefCountedGLsync::fence_lock;