void QueueLengthPolicy::update_policy(int queue_length)
{
if (queue_length < 0) { // Starvation.
- if (safe_queue_length < 5) {
+ if (been_at_safe_point_since_last_starvation && safe_queue_length < 5) {
++safe_queue_length;
fprintf(stderr, "Card %u: Starvation, increasing safe limit to %u frames\n",
card_index, safe_queue_length);
}
frames_with_at_least_one = 0;
+ been_at_safe_point_since_last_starvation = false;
return;
}
if (queue_length > 0) {
- if (++frames_with_at_least_one >= 50) {
+ 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) {
--safe_queue_length;
fprintf(stderr, "Card %u: Spare frames for more than 50 frames, reducing safe limit to %u frames\n",
card_index, safe_queue_length);
movit_texel_subpixel_precision /= 2.0;
resource_pool.reset(new ResourcePool);
- theme.reset(new Theme("theme.lua", resource_pool.get(), num_cards));
+ theme.reset(new Theme(global_flags.theme_filename.c_str(), resource_pool.get(), num_cards));
for (unsigned i = 0; i < NUM_OUTPUTS; ++i) {
output_channel[i].parent = this;
}
locut.init(FILTER_HPF, 2);
+ // If --flat-audio is given, turn off everything that messes with the sound,
+ // except the final makeup gain.
+ if (global_flags.flat_audio) {
+ set_locut_enabled(false);
+ set_limiter_enabled(false);
+ set_compressor_enabled(false);
+ }
+
// hlen=16 is pretty low quality, but we use quite a bit of CPU otherwise,
// and there's a limit to how important the peak meter is.
peak_resampler.setup(OUTPUT_FREQUENCY, OUTPUT_FREQUENCY * 4, /*num_channels=*/2, /*hlen=*/16, /*frel=*/1.0);
new_frame.frame = RefCountedFrame(FrameAllocator::Frame());
new_frame.length = frame_length;
new_frame.interlaced = false;
- new_frame.ready_fence = nullptr;
new_frame.dropped_frames = dropped_frames;
card->new_frames.push(move(new_frame));
card->new_frames_changed.notify_all();
GLuint pbo = userdata->pbo;
check_error();
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pbo);
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
check_error();
- glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT);
+ glFlushMappedBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, video_frame.size);
check_error();
glBindTexture(GL_TEXTURE_2D, userdata->tex_cbcr[field]);
check_error();
glBindTexture(GL_TEXTURE_2D, 0);
check_error();
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
check_error();
- GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, /*flags=*/0);
+ RefCountedGLsync fence(GL_SYNC_GPU_COMMANDS_COMPLETE, /*flags=*/0);
check_error();
- assert(fence != nullptr);
+ assert(fence.get() != nullptr);
if (field == 1) {
// Don't upload the second field as fast as we can; wait until
bool has_new_frame[MAX_CARDS] = { false };
int num_samples[MAX_CARDS] = { 0 };
- // TODO: Make configurable, and with a timeout.
- unsigned master_card_index = 0;
+ // TODO: Add a timeout.
+ unsigned master_card_index = theme->map_signal(master_clock_channel);
+ assert(master_card_index < num_cards);
get_one_frame_from_each_card(master_card_index, new_frames, has_new_frame, num_samples);
-
- // Resample the audio as needed, including from previously dropped frames.
- assert(num_cards > 0);
- for (unsigned frame_num = 0; frame_num < new_frames[master_card_index].dropped_frames + 1; ++frame_num) {
- {
- // Signal to the audio thread to process this frame.
- unique_lock<mutex> lock(audio_mutex);
- audio_task_queue.push(AudioTask{pts_int, num_samples[master_card_index]});
- audio_task_queue_changed.notify_one();
- }
- if (frame_num != new_frames[master_card_index].dropped_frames) {
- // For dropped frames, increase the pts. Note that if the format changed
- // in the meantime, we have no way of detecting that; we just have to
- // assume the frame length is always the same.
- ++stats_dropped_frames;
- pts_int += new_frames[master_card_index].length;
- }
- }
-
+ schedule_audio_resampling_tasks(new_frames[master_card_index].dropped_frames, num_samples[master_card_index], new_frames[master_card_index].length);
+ stats_dropped_frames += new_frames[master_card_index].dropped_frames;
send_audio_level_callback();
for (unsigned card_index = 0; card_index < num_cards; ++card_index) {
// The new texture might still be uploaded,
// tell the GPU to wait until it's there.
if (new_frame->ready_fence) {
- glWaitSync(new_frame->ready_fence, /*flags=*/0, GL_TIMEOUT_IGNORED);
+ glWaitSync(new_frame->ready_fence.get(), /*flags=*/0, GL_TIMEOUT_IGNORED);
check_error();
- glDeleteSync(new_frame->ready_fence);
+ new_frame->ready_fence.reset();
check_error();
}
}
card->fractional_samples = num_samples_times_timebase % TIMEBASE;
assert(num_samples[card_index] >= 0);
- if (card_index != master_card_index) {
+ if (card_index == master_card_index) {
+ // We don't use the queue length policy for the master card,
+ // but we will if it stops being the master. Thus, clear out
+ // the policy in case we switch in the future.
+ card->queue_length_policy.reset(card_index);
+ } else {
// If we have excess frames compared to the policy for this card,
// drop frames from the head.
card->queue_length_policy.update_policy(card->new_frames.size());
}
}
+void Mixer::schedule_audio_resampling_tasks(unsigned dropped_frames, int num_samples_per_frame, int length_per_frame)
+{
+ // Resample the audio as needed, including from previously dropped frames.
+ assert(num_cards > 0);
+ for (unsigned frame_num = 0; frame_num < dropped_frames + 1; ++frame_num) {
+ {
+ // Signal to the audio thread to process this frame.
+ unique_lock<mutex> lock(audio_mutex);
+ audio_task_queue.push(AudioTask{pts_int, num_samples_per_frame});
+ audio_task_queue_changed.notify_one();
+ }
+ if (frame_num != dropped_frames) {
+ // For dropped frames, increase the pts. Note that if the format changed
+ // in the meantime, we have no way of detecting that; we just have to
+ // assume the frame length is always the same.
+ pts_int += length_per_frame;
+ }
+ }
+}
+
void Mixer::render_one_frame()
{
// Get the main chain from the theme, and set its state immediately.