// These need to survive several QuickSyncEncoderImpl instances,
// so they are outside.
-bool mux_metrics_inited = false;
+once_flag quick_sync_metrics_inited;
LatencyHistogram mixer_latency_histogram, qs_latency_histogram;
MuxMetrics current_file_mux_metrics, total_mux_metrics;
std::atomic<double> metric_current_file_start_time_seconds{0.0 / 0.0};
+std::atomic<int64_t> metric_quick_sync_stalled_frames{0};
} // namespace
static constexpr unsigned int MaxPicOrderCntLsb = (2<<8);
static constexpr unsigned int Log2MaxFrameNum = 16;
static constexpr unsigned int Log2MaxPicOrderCntLsb = 8;
-static constexpr int rc_default_modes[] = { // Priority list of modes.
- VA_RC_VBR,
- VA_RC_CQP,
- VA_RC_VBR_CONSTRAINED,
- VA_RC_CBR,
- VA_RC_VCM,
- VA_RC_NONE,
-};
using namespace std;
}
-static const char *rc_to_string(int rc_mode)
-{
- switch (rc_mode) {
- case VA_RC_NONE:
- return "NONE";
- case VA_RC_CBR:
- return "CBR";
- case VA_RC_VBR:
- return "VBR";
- case VA_RC_VCM:
- return "VCM";
- case VA_RC_CQP:
- return "CQP";
- case VA_RC_VBR_CONSTRAINED:
- return "VBR_CONSTRAINED";
- default:
- return "Unknown";
- }
-}
-
void QuickSyncEncoderImpl::enable_zerocopy_if_possible()
{
if (global_flags.x264_video_to_disk) {
}
if (attrib[VAConfigAttribRateControl].value != VA_ATTRIB_NOT_SUPPORTED) {
- int tmp = attrib[VAConfigAttribRateControl].value;
-
- if (rc_mode == -1 || !(rc_mode & tmp)) {
- if (rc_mode != -1) {
- printf("Warning: Don't support the specified RateControl mode: %s!!!, switch to ", rc_to_string(rc_mode));
- }
-
- for (i = 0; i < sizeof(rc_default_modes) / sizeof(rc_default_modes[0]); i++) {
- if (rc_default_modes[i] & tmp) {
- rc_mode = rc_default_modes[i];
- break;
- }
- }
+ if (!(attrib[VAConfigAttribRateControl].value & VA_RC_CQP)) {
+ fprintf(stderr, "ERROR: VA-API encoder does not support CQP mode.\n");
+ exit(1);
}
config_attrib[config_attrib_num].type = VAConfigAttribRateControl;
- config_attrib[config_attrib_num].value = rc_mode;
+ config_attrib[config_attrib_num].value = VA_RC_CQP;
config_attrib_num++;
}
memset(&slice_param, 0, sizeof(slice_param));
}
- if (!mux_metrics_inited) {
+ call_once(quick_sync_metrics_inited, [](){
mixer_latency_histogram.init("mixer");
qs_latency_histogram.init("quick_sync");
current_file_mux_metrics.init({{ "destination", "current_file" }});
total_mux_metrics.init({{ "destination", "files_total" }});
global_metrics.add("current_file_start_time_seconds", &metric_current_file_start_time_seconds, Metrics::TYPE_GAUGE);
- mux_metrics_inited = true;
- }
+ global_metrics.add("quick_sync_stalled_frames", &metric_quick_sync_stalled_frames);
+ });
storage_thread = thread(&QuickSyncEncoderImpl::storage_task_thread, this);
if (surf == nullptr) {
fprintf(stderr, "Warning: No free slots for frame %d, rendering has to wait for H.264 encoder\n",
current_storage_frame);
+ ++metric_quick_sync_stalled_frames;
storage_task_queue_changed.wait(lock, [this, &surf]{
if (storage_thread_should_quit)
return true;
void QuickSyncEncoderImpl::add_audio(int64_t pts, vector<float> audio)
{
+ lock_guard<mutex> lock(file_audio_encoder_mutex);
assert(!is_shutdown);
file_audio_encoder->encode_audio(audio, pts + global_delay());
}
storage_thread.join();
// Encode any leftover audio in the queues, and also any delayed frames.
- file_audio_encoder->encode_last_audio();
+ {
+ lock_guard<mutex> lock(file_audio_encoder_mutex);
+ file_audio_encoder->encode_last_audio();
+ }
if (!global_flags.x264_video_to_disk) {
release_encode();
current_file_mux_metrics.reset();
- AVCodecParametersWithDeleter audio_codecpar = file_audio_encoder->get_codec_parameters();
- file_mux.reset(new Mux(avctx, frame_width, frame_height, Mux::CODEC_H264, video_extradata, audio_codecpar.get(), TIMEBASE,
- std::bind(&DiskSpaceEstimator::report_write, disk_space_estimator, filename, _1),
- { ¤t_file_mux_metrics, &total_mux_metrics }));
+ {
+ lock_guard<mutex> lock(file_audio_encoder_mutex);
+ AVCodecParametersWithDeleter audio_codecpar = file_audio_encoder->get_codec_parameters();
+ file_mux.reset(new Mux(avctx, frame_width, frame_height, Mux::CODEC_H264, video_extradata, audio_codecpar.get(), TIMEBASE,
+ std::bind(&DiskSpaceEstimator::report_write, disk_space_estimator, filename, _1),
+ Mux::WRITE_BACKGROUND,
+ { ¤t_file_mux_metrics, &total_mux_metrics }));
+ }
metric_current_file_start_time_seconds = get_timestamp_for_metrics();
if (global_flags.x264_video_to_disk) {
// Wait for the GPU to be done with the frame.
GLenum sync_status;
do {
- sync_status = glClientWaitSync(frame.fence.get(), 0, 1000000000);
+ sync_status = glClientWaitSync(frame.fence.get(), 0, 0);
check_error();
+ if (sync_status == GL_TIMEOUT_EXPIRED) {
+ // NVIDIA likes to busy-wait; yield instead.
+ this_thread::sleep_for(milliseconds(1));
+ }
} while (sync_status == GL_TIMEOUT_EXPIRED);
assert(sync_status != GL_WAIT_FAILED);