X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=mixer.cpp;h=f8ad584e9ac6996e83e7595fd6891d03705478e1;hb=312ed2563ea113dc56960cbfedffd2ca32011a7d;hp=05b7c51dcb78110f5bb94dcf910dbd3feeecef81;hpb=bdd88ec1fc406944e1d05d2fecfdd61c4d82f29e;p=nageru diff --git a/mixer.cpp b/mixer.cpp index 05b7c51..f8ad584 100644 --- a/mixer.cpp +++ b/mixer.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +47,7 @@ class QOpenGLContext; using namespace movit; using namespace std; +using namespace std::chrono; using namespace std::placeholders; using namespace bmusb; @@ -380,10 +382,8 @@ void Mixer::bm_frame(unsigned card_index, uint16_t timecode, is_mode_scanning[card_index] = false; } else { static constexpr double switch_time_s = 0.5; // Should be enough time for the signal to stabilize. - timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - double sec_since_last_switch = (now.tv_sec - last_mode_scan_change[card_index].tv_sec) + - 1e-9 * (now.tv_nsec - last_mode_scan_change[card_index].tv_nsec); + steady_clock::time_point now = steady_clock::now(); + double sec_since_last_switch = duration(steady_clock::now() - last_mode_scan_change[card_index]).count(); if (sec_since_last_switch > switch_time_s) { // It isn't this mode; try the next one. mode_scanlist_index[card_index]++; @@ -505,7 +505,7 @@ void Mixer::bm_frame(unsigned card_index, uint16_t timecode, PBOFrameAllocator::Userdata *userdata = (PBOFrameAllocator::Userdata *)video_frame.userdata; unsigned num_fields = video_format.interlaced ? 2 : 1; - timespec frame_upload_start; + steady_clock::time_point frame_upload_start; if (video_format.interlaced) { // Send the two fields along as separate frames; the other side will need to add // a deinterlacer to actually get this right. @@ -514,7 +514,7 @@ void Mixer::bm_frame(unsigned card_index, uint16_t timecode, assert(frame_length % 2 == 0); frame_length /= 2; num_fields = 2; - clock_gettime(CLOCK_MONOTONIC, &frame_upload_start); + frame_upload_start = steady_clock::now(); } userdata->last_interlaced = video_format.interlaced; userdata->last_has_signal = video_format.has_signal; @@ -595,16 +595,9 @@ void Mixer::bm_frame(unsigned card_index, uint16_t timecode, // against the video display, although the latter is not as critical.) // This requires our system clock to be reasonably close to the // video clock, but that's not an unreasonable assumption. - timespec second_field_start; - second_field_start.tv_nsec = frame_upload_start.tv_nsec + - frame_length * 1000000000 / TIMEBASE; - second_field_start.tv_sec = frame_upload_start.tv_sec + - second_field_start.tv_nsec / 1000000000; - second_field_start.tv_nsec %= 1000000000; - - while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, - &second_field_start, nullptr) == -1 && - errno == EINTR) ; + steady_clock::time_point second_field_start = frame_upload_start + + nanoseconds(frame_length * 1000000000 / TIMEBASE); + this_thread::sleep_until(second_field_start); } { @@ -642,8 +635,8 @@ void Mixer::thread_func() exit(1); } - struct timespec start, now; - clock_gettime(CLOCK_MONOTONIC, &start); + steady_clock::time_point start, now; + start = steady_clock::now(); int frame = 0; int stats_dropped_frames = 0; @@ -700,14 +693,13 @@ void Mixer::thread_func() } } - int64_t duration = new_frames[master_card_index].length; - render_one_frame(duration); + int64_t frame_duration = new_frames[master_card_index].length; + render_one_frame(frame_duration); ++frame; - pts_int += duration; + pts_int += frame_duration; - clock_gettime(CLOCK_MONOTONIC, &now); - double elapsed = now.tv_sec - start.tv_sec + - 1e-9 * (now.tv_nsec - start.tv_nsec); + now = steady_clock::now(); + double elapsed = duration(now - start).count(); if (frame % 100 == 0) { printf("%d frames (%d dropped) in %.3f seconds = %.1f fps (%.1f ms/frame)", frame, stats_dropped_frames, elapsed, frame / elapsed, @@ -863,13 +855,27 @@ void Mixer::schedule_audio_resampling_tasks(unsigned dropped_frames, int num_sam // 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) { + const bool dropped_frame = (frame_num != dropped_frames); { // Signal to the audio thread to process this frame. + // Note that if the frame is a dropped frame, we signal that + // we don't want to use this frame as base for adjusting + // the resampler rate. The reason for this is that the timing + // of these frames is often way too late; they typically don't + // “arrive” before we synthesize them. Thus, we could end up + // in a situation where we have inserted e.g. five audio frames + // into the queue before we then start pulling five of them + // back out. This makes ResamplingQueue overestimate the delay, + // causing undue resampler changes. (We _do_ use the last, + // non-dropped frame; perhaps we should just discard that as well, + // since dropped frames are expected to be rare, and it might be + // better to just wait until we have a slightly more normal situation). unique_lock lock(audio_mutex); - audio_task_queue.push(AudioTask{pts_int, num_samples_per_frame}); + bool adjust_rate = !dropped_frame; + audio_task_queue.push(AudioTask{pts_int, num_samples_per_frame, adjust_rate}); audio_task_queue_changed.notify_one(); } - if (frame_num != dropped_frames) { + if (dropped_frame) { // 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. @@ -969,11 +975,11 @@ void Mixer::audio_thread_func() audio_task_queue.pop(); } - process_audio_one_frame(task.pts_int, task.num_samples); + process_audio_one_frame(task.pts_int, task.num_samples, task.adjust_rate); } } -void Mixer::process_audio_one_frame(int64_t frame_pts_int, int num_samples) +void Mixer::process_audio_one_frame(int64_t frame_pts_int, int num_samples, bool adjust_rate) { vector samples_card; vector samples_out; @@ -986,7 +992,13 @@ void Mixer::process_audio_one_frame(int64_t frame_pts_int, int num_samples) samples_card.resize(num_samples * 2); { unique_lock lock(cards[card_index].audio_mutex); - cards[card_index].resampling_queue->get_output_samples(double(frame_pts_int) / TIMEBASE, &samples_card[0], num_samples); + ResamplingQueue::RateAdjustmentPolicy rate_adjustment_policy = + adjust_rate ? ResamplingQueue::ADJUST_RATE : ResamplingQueue::DO_NOT_ADJUST_RATE; + cards[card_index].resampling_queue->get_output_samples( + double(frame_pts_int) / TIMEBASE, + &samples_card[0], + num_samples, + rate_adjustment_policy); } if (card_index == selected_audio_card) { samples_out = move(samples_card); @@ -1250,7 +1262,7 @@ void Mixer::start_mode_scanning(unsigned card_index) assert(!mode_scanlist[card_index].empty()); mode_scanlist_index[card_index] = 0; cards[card_index].capture->set_video_mode(mode_scanlist[card_index][0]); - clock_gettime(CLOCK_MONOTONIC, &last_mode_scan_change[card_index]); + last_mode_scan_change[card_index] = steady_clock::now(); } Mixer::OutputChannel::~OutputChannel()