OPTION_HTTP_UNCOMPRESSED_VIDEO,
OPTION_HTTP_X264_VIDEO,
OPTION_RECORD_X264_VIDEO,
+ OPTION_SEPARATE_X264_DISK_ENCODE,
OPTION_X264_PRESET,
OPTION_X264_TUNE,
OPTION_X264_SPEEDCONTROL,
OPTION_X264_VBV_BUFSIZE,
OPTION_X264_VBV_MAX_BITRATE,
OPTION_X264_PARAM,
+ OPTION_X264_SEPARATE_DISK_PRESET,
+ OPTION_X264_SEPARATE_DISK_TUNE,
+ OPTION_X264_SEPARATE_DISK_BITRATE,
+ OPTION_X264_SEPARATE_DISK_CRF,
+ OPTION_X264_SEPARATE_DISK_PARAM,
OPTION_HTTP_MUX,
OPTION_HTTP_COARSE_TIMEBASE,
OPTION_HTTP_AUDIO_CODEC,
fprintf(stderr, " --http-x264-video send x264-compressed video to HTTP clients\n");
fprintf(stderr, " --record-x264-video store x264-compressed video to disk (implies --http-x264-video,\n");
fprintf(stderr, " removes the need for working VA-API encoding)\n");
+ fprintf(stderr, " --separate-x264-disk-encode run a different x264 encoder for disk recording\n");
+ fprintf(stderr, " (implies --record-x264-video)\n");
}
fprintf(stderr, " --x264-preset x264 quality preset (default " X264_DEFAULT_PRESET ")\n");
fprintf(stderr, " --x264-tune x264 tuning (default " X264_DEFAULT_TUNE ", can be blank)\n");
fprintf(stderr, " --x264-vbv-max-bitrate x264 local max bitrate (in kilobit/sec per --vbv-bufsize,\n");
fprintf(stderr, " 0 = no limit, default: same as --x264-bitrate, i.e., CBR)\n");
fprintf(stderr, " --x264-param=NAME[,VALUE] set any x264 parameter, for fine tuning\n");
+ if (program == PROGRAM_NAGERU) {
+ fprintf(stderr, " --x264-separate-disk-preset x264 quality preset (default " X264_DEFAULT_PRESET ")\n");
+ fprintf(stderr, " --x264-separate-disk-tune x264 tuning (default " X264_DEFAULT_TUNE ", can be blank)\n");
+ fprintf(stderr, " --x264-separate-disk-bitrate x264 bitrate (in kilobit/sec, default %d)\n",
+ DEFAULT_X264_OUTPUT_BIT_RATE);
+ fprintf(stderr, " --x264-separate-disk-crf=VALUE quality-based VBR (-12 to 51), \n");
+ fprintf(stderr, " incompatible with --x264-separate-disk-bitrate\n");
+ fprintf(stderr, " --x264-separate-disk-param=NAME[,VALUE] set any x264 parameter, for fine tuning\n");
+ }
fprintf(stderr, " --http-mux=NAME mux to use for HTTP streams (default " DEFAULT_STREAM_MUX_NAME ")\n");
fprintf(stderr, " --http-audio-codec=NAME audio codec to use for HTTP streams\n");
fprintf(stderr, " (default is to use the same as for the recording)\n");
{ "http-uncompressed-video", no_argument, 0, OPTION_HTTP_UNCOMPRESSED_VIDEO },
{ "http-x264-video", no_argument, 0, OPTION_HTTP_X264_VIDEO },
{ "record-x264-video", no_argument, 0, OPTION_RECORD_X264_VIDEO },
+ { "separate-x264-disk-encode", no_argument, 0, OPTION_SEPARATE_X264_DISK_ENCODE },
{ "x264-preset", required_argument, 0, OPTION_X264_PRESET },
{ "x264-tune", required_argument, 0, OPTION_X264_TUNE },
{ "x264-speedcontrol", no_argument, 0, OPTION_X264_SPEEDCONTROL },
{ "x264-vbv-bufsize", required_argument, 0, OPTION_X264_VBV_BUFSIZE },
{ "x264-vbv-max-bitrate", required_argument, 0, OPTION_X264_VBV_MAX_BITRATE },
{ "x264-param", required_argument, 0, OPTION_X264_PARAM },
+ { "x264-separate-disk-preset", required_argument, 0, OPTION_X264_SEPARATE_DISK_PRESET },
+ { "x264-separate-disk-tune", required_argument, 0, OPTION_X264_SEPARATE_DISK_TUNE },
+ { "x264-separate-disk-bitrate", required_argument, 0, OPTION_X264_SEPARATE_DISK_BITRATE },
+ { "x264-separate-disk-crf", required_argument, 0, OPTION_X264_SEPARATE_DISK_CRF },
+ { "x264-separate-disk-param", required_argument, 0, OPTION_X264_SEPARATE_DISK_PARAM },
{ "http-mux", required_argument, 0, OPTION_HTTP_MUX },
{ "http-audio-codec", required_argument, 0, OPTION_HTTP_AUDIO_CODEC },
{ "http-audio-bitrate", required_argument, 0, OPTION_HTTP_AUDIO_BITRATE },
global_flags.x264_video_to_disk = true;
global_flags.x264_video_to_http = true;
break;
+ case OPTION_SEPARATE_X264_DISK_ENCODE:
+ global_flags.x264_video_to_disk = true;
+ global_flags.x264_video_to_http = true;
+ global_flags.x264_separate_disk_encode = true;
+ break;
case OPTION_X264_PRESET:
global_flags.x264_preset = optarg;
break;
case OPTION_X264_PARAM:
global_flags.x264_extra_param.push_back(optarg);
break;
+ case OPTION_X264_SEPARATE_DISK_PRESET:
+ global_flags.x264_separate_disk_preset = optarg;
+ break;
+ case OPTION_X264_SEPARATE_DISK_TUNE:
+ global_flags.x264_separate_disk_tune = optarg;
+ break;
+ case OPTION_X264_SEPARATE_DISK_BITRATE:
+ global_flags.x264_separate_disk_bitrate = atoi(optarg);
+ break;
+ case OPTION_X264_SEPARATE_DISK_CRF:
+ global_flags.x264_separate_disk_crf = atof(optarg);
+ break;
+ case OPTION_X264_SEPARATE_DISK_PARAM:
+ global_flags.x264_separate_disk_extra_param.push_back(optarg);
+ break;
case OPTION_FLAT_AUDIO:
// If --flat-audio is given, turn off everything that messes with the sound,
// except the final makeup gain.
} else if (global_flags.x264_preset.empty()) {
global_flags.x264_preset = X264_DEFAULT_PRESET;
}
+ if (global_flags.x264_separate_disk_preset.empty()) {
+ global_flags.x264_separate_disk_preset = X264_DEFAULT_PRESET;
+ }
if (!theme_dirs.empty()) {
global_flags.theme_dirs = theme_dirs;
}
global_flags.x264_bitrate = DEFAULT_X264_OUTPUT_BIT_RATE;
}
+ if (!isinf(global_flags.x264_separate_disk_crf)) { // CRF mode is selected.
+ if (global_flags.x264_separate_disk_bitrate != -1) {
+ fprintf(stderr, "ERROR: --x264-separate-disk-bitrate and --x264-separate-disk-crf are mutually incompatible.\n");
+ exit(1);
+ }
+ } else if (global_flags.x264_separate_disk_bitrate == -1) {
+ global_flags.x264_separate_disk_bitrate = DEFAULT_X264_OUTPUT_BIT_RATE;
+ }
+
if (!card_to_mjpeg_stream_export_set) {
// Fill in the default mapping (export all cards, in order).
for (unsigned card_idx = 0; card_idx < unsigned(global_flags.max_num_cards); ++card_idx) {
bool uncompressed_video_to_http = false;
bool x264_video_to_http = false;
bool x264_video_to_disk = false; // Disables Quick Sync entirely. Implies x264_video_to_http == true.
+ bool x264_separate_disk_encode = false; // Disables Quick Sync entirely. Implies x264_video_to_disk == true.
std::vector<std::string> theme_dirs { ".", PREFIX "/share/nageru" };
std::string recording_dir = ".";
std::string theme_filename = "theme.lua";
int x264_vbv_max_bitrate = -1; // In kilobits. 0 = no limit, -1 = same as <x264_bitrate> (CBR).
int x264_vbv_buffer_size = -1; // In kilobits. 0 = one-frame VBV, -1 = same as <x264_bitrate> (one-second VBV).
std::vector<std::string> x264_extra_param; // In “key[,value]” format.
+
+ std::string x264_separate_disk_preset; // Empty will be overridden by X264_DEFAULT_PRESET, unless speedcontrol is set.
+ std::string x264_separate_disk_tune = X264_DEFAULT_TUNE;
+ int x264_separate_disk_bitrate = -1;
+ float x264_separate_disk_crf = HUGE_VAL;
+ std::vector<std::string> x264_separate_disk_extra_param; // In “key[,value]” format.
+
std::string v4l_output_device; // Empty if none.
bool enable_alsa_output = true;
std::map<int, int> default_stream_mapping;
audio_encoder.reset(new AudioEncoder(global_flags.stream_audio_codec_name, global_flags.stream_audio_codec_bitrate, oformat));
}
- unique_ptr<X264Encoder> x264_encoder(new X264Encoder(oformat));
+ unique_ptr<X264Encoder> x264_encoder(new X264Encoder(oformat, /*use_separate_disk_params=*/false));
unique_ptr<Mux> http_mux = create_mux(&httpd, oformat, x264_encoder.get(), audio_encoder.get());
if (global_flags.transcode_audio) {
audio_encoder->add_mux(http_mux.get());
has_released_gl_resources = true;
}
-QuickSyncEncoderImpl::QuickSyncEncoderImpl(const std::string &filename, ResourcePool *resource_pool, QSurface *surface, const string &va_display, int width, int height, AVOutputFormat *oformat, X264Encoder *x264_encoder, DiskSpaceEstimator *disk_space_estimator)
- : current_storage_frame(0), resource_pool(resource_pool), surface(surface), x264_encoder(x264_encoder), frame_width(width), frame_height(height), disk_space_estimator(disk_space_estimator)
+QuickSyncEncoderImpl::QuickSyncEncoderImpl(const std::string &filename, ResourcePool *resource_pool, QSurface *surface, const string &va_display, int width, int height, AVOutputFormat *oformat, X264Encoder *http_encoder, X264Encoder *disk_encoder, DiskSpaceEstimator *disk_space_estimator)
+ : current_storage_frame(0), resource_pool(resource_pool), surface(surface), x264_http_encoder(http_encoder), x264_disk_encoder(disk_encoder), frame_width(width), frame_height(height), disk_space_estimator(disk_space_estimator)
{
file_audio_encoder.reset(new AudioEncoder(AUDIO_OUTPUT_CODEC_NAME, DEFAULT_AUDIO_OUTPUT_BIT_RATE, oformat));
open_output_file(filename);
//print_input();
if (global_flags.x264_video_to_http || global_flags.x264_video_to_disk) {
- assert(x264_encoder != nullptr);
+ assert(x264_http_encoder != nullptr);
} else {
- assert(x264_encoder == nullptr);
+ assert(x264_http_encoder == nullptr);
+ }
+ if (global_flags.x264_separate_disk_encode) {
+ assert(x264_disk_encoder != nullptr);
+ } else {
+ assert(x264_disk_encoder == nullptr);
}
enable_zerocopy_if_possible();
string video_extradata; // FIXME: See other comment about global headers.
if (global_flags.x264_video_to_disk) {
- video_extradata = x264_encoder->get_global_headers();
+ video_extradata = x264_disk_encoder->get_global_headers();
}
current_file_mux_metrics.reset();
metric_current_file_start_time_seconds = get_timestamp_for_metrics();
if (global_flags.x264_video_to_disk) {
- x264_encoder->add_mux(file_mux.get());
+ x264_disk_encoder->add_mux(file_mux.get());
}
}
if (global_flags.uncompressed_video_to_http) {
add_packet_for_uncompressed_frame(pts, duration, data);
} else if (global_flags.x264_video_to_http || global_flags.x264_video_to_disk) {
- x264_encoder->add_frame(pts, duration, frame.ycbcr_coefficients, data, received_ts);
+ x264_http_encoder->add_frame(pts, duration, frame.ycbcr_coefficients, data, received_ts);
+ }
+ if (global_flags.x264_separate_disk_encode) {
+ x264_disk_encoder->add_frame(pts, duration, frame.ycbcr_coefficients, data, received_ts);
}
if (v4l_output != nullptr) {
}
// Proxy object.
-QuickSyncEncoder::QuickSyncEncoder(const std::string &filename, ResourcePool *resource_pool, QSurface *surface, const string &va_display, int width, int height, AVOutputFormat *oformat, X264Encoder *x264_encoder, DiskSpaceEstimator *disk_space_estimator)
- : impl(new QuickSyncEncoderImpl(filename, resource_pool, surface, va_display, width, height, oformat, x264_encoder, disk_space_estimator)) {}
+QuickSyncEncoder::QuickSyncEncoder(const std::string &filename, ResourcePool *resource_pool, QSurface *surface, const string &va_display, int width, int height, AVOutputFormat *oformat, X264Encoder *http_encoder, X264Encoder *disk_encoder, DiskSpaceEstimator *disk_space_estimator)
+ : impl(new QuickSyncEncoderImpl(filename, resource_pool, surface, va_display, width, height, oformat, http_encoder, disk_encoder, disk_space_estimator)) {}
// Must be defined here because unique_ptr<> destructor needs to know the impl.
QuickSyncEncoder::~QuickSyncEncoder() {}
// This class is _not_ thread-safe, except where mentioned.
class QuickSyncEncoder {
public:
- QuickSyncEncoder(const std::string &filename, movit::ResourcePool *resource_pool, QSurface *surface, const std::string &va_display, int width, int height, AVOutputFormat *oformat, X264Encoder *x264_encoder, DiskSpaceEstimator *disk_space_estimator);
+ QuickSyncEncoder(const std::string &filename, movit::ResourcePool *resource_pool, QSurface *surface, const std::string &va_display, int width, int height, AVOutputFormat *oformat, X264Encoder *http_encoder, X264Encoder *disk_encoder, DiskSpaceEstimator *disk_space_estimator);
~QuickSyncEncoder();
void set_stream_mux(Mux *mux); // Does not take ownership. Must be called unless x264 is used for the stream.
class QuickSyncEncoderImpl {
public:
- QuickSyncEncoderImpl(const std::string &filename, movit::ResourcePool *resource_pool, QSurface *surface, const std::string &va_display, int width, int height, AVOutputFormat *oformat, X264Encoder *x264_encoder, DiskSpaceEstimator *disk_space_estimator);
+ QuickSyncEncoderImpl(const std::string &filename, movit::ResourcePool *resource_pool, QSurface *surface, const std::string &va_display, int width, int height, AVOutputFormat *oformat, X264Encoder *http_encoder, X264Encoder *disk_encoder, DiskSpaceEstimator *disk_space_estimator);
~QuickSyncEncoderImpl();
void add_audio(int64_t pts, std::vector<float> audio);
bool is_zerocopy() const;
std::mutex file_audio_encoder_mutex;
std::unique_ptr<AudioEncoder> file_audio_encoder;
- X264Encoder *x264_encoder; // nullptr if not using x264.
+ X264Encoder *x264_http_encoder; // nullptr if not using x264.
+ X264Encoder *x264_disk_encoder;
std::unique_ptr<V4LOutput> v4l_output; // nullptr if not using V4L2 output.
Mux* stream_mux = nullptr; // To HTTP.
stream_audio_encoder.reset(new AudioEncoder(global_flags.stream_audio_codec_name, global_flags.stream_audio_codec_bitrate, oformat));
}
if (global_flags.x264_video_to_http || global_flags.x264_video_to_disk) {
- x264_encoder.reset(new X264Encoder(oformat));
+ x264_encoder.reset(new X264Encoder(oformat, /*use_separate_disk_params=*/false));
+ }
+ X264Encoder *http_encoder = x264_encoder.get();
+ X264Encoder *disk_encoder = x264_encoder.get();
+ if (global_flags.x264_separate_disk_encode) {
+ x264_disk_encoder.reset(new X264Encoder(oformat, /*use_separate_disk_params=*/true));
+ disk_encoder = x264_disk_encoder.get();
}
string filename = generate_local_dump_filename(/*frame=*/0);
- quicksync_encoder.reset(new QuickSyncEncoder(filename, resource_pool, surface, va_display, width, height, oformat, x264_encoder.get(), disk_space_estimator));
+ quicksync_encoder.reset(new QuickSyncEncoder(filename, resource_pool, surface, va_display, width, height, oformat, http_encoder, disk_encoder, disk_space_estimator));
open_output_stream();
stream_audio_encoder->add_mux(stream_mux.get());
{
quicksync_encoder->shutdown();
x264_encoder.reset(nullptr);
+ x264_disk_encoder.reset(nullptr);
quicksync_encoder->close_file();
quicksync_encoder.reset(nullptr);
while (quicksync_encoders_in_shutdown.load() > 0) {
lock_guard<mutex> lock1(qs_mu, adopt_lock), lock2(qs_audio_mu, adopt_lock);
QuickSyncEncoder *old_encoder = quicksync_encoder.release(); // When we go C++14, we can use move capture instead.
X264Encoder *old_x264_encoder = nullptr;
+ X264Encoder *old_x264_disk_encoder = nullptr;
if (global_flags.x264_video_to_disk) {
old_x264_encoder = x264_encoder.release();
}
- thread([old_encoder, old_x264_encoder, this]{
+ if (global_flags.x264_separate_disk_encode) {
+ old_x264_disk_encoder = x264_disk_encoder.release();
+ }
+ thread([old_encoder, old_x264_encoder, old_x264_disk_encoder, this]{
old_encoder->shutdown();
delete old_x264_encoder;
+ delete old_x264_disk_encoder;
old_encoder->close_file();
stream_mux->unplug();
}).detach();
if (global_flags.x264_video_to_disk) {
- x264_encoder.reset(new X264Encoder(oformat));
+ x264_encoder.reset(new X264Encoder(oformat, /*use_separate_disk_params=*/false));
+ assert(global_flags.x264_video_to_http);
if (global_flags.x264_video_to_http) {
x264_encoder->add_mux(stream_mux.get());
}
x264_encoder->change_bitrate(overriding_bitrate);
}
}
+ X264Encoder *http_encoder = x264_encoder.get();
+ X264Encoder *disk_encoder = x264_encoder.get();
+ if (global_flags.x264_separate_disk_encode) {
+ x264_disk_encoder.reset(new X264Encoder(oformat, /*use_separate_disk_params=*/true));
+ disk_encoder = x264_disk_encoder.get();
+ }
- quicksync_encoder.reset(new QuickSyncEncoder(filename, resource_pool, surface, va_display, width, height, oformat, x264_encoder.get(), disk_space_estimator));
+ quicksync_encoder.reset(new QuickSyncEncoder(filename, resource_pool, surface, va_display, width, height, oformat, http_encoder, disk_encoder, disk_space_estimator));
quicksync_encoder->set_stream_mux(stream_mux.get());
}
std::unique_ptr<Mux> stream_mux; // To HTTP.
std::unique_ptr<AudioEncoder> stream_audio_encoder;
std::unique_ptr<X264Encoder> x264_encoder; // nullptr if not using x264.
+ std::unique_ptr<X264Encoder> x264_disk_encoder; // nullptr if not using x264, or if not having separate disk encodes.
std::string stream_mux_header;
MuxMetrics stream_mux_metrics;
atomic<int64_t> metric_x264_output_frames_b{0};
Histogram metric_x264_crf;
LatencyHistogram x264_latency_histogram;
-once_flag x264_metrics_inited;
+
+atomic<int64_t> metric_x264_disk_queued_frames{0};
+atomic<int64_t> metric_x264_disk_max_queued_frames{X264_QUEUE_LENGTH};
+atomic<int64_t> metric_x264_disk_dropped_frames{0};
+atomic<int64_t> metric_x264_disk_output_frames_i{0};
+atomic<int64_t> metric_x264_disk_output_frames_p{0};
+atomic<int64_t> metric_x264_disk_output_frames_b{0};
+Histogram metric_x264_disk_crf;
+LatencyHistogram x264_disk_latency_histogram;
+
+once_flag x264_metrics_inited, x264_disk_metrics_inited;
void update_vbv_settings(x264_param_t *param)
{
} // namespace
-X264Encoder::X264Encoder(AVOutputFormat *oformat)
+X264Encoder::X264Encoder(AVOutputFormat *oformat, bool use_separate_disk_params)
: wants_global_headers(oformat->flags & AVFMT_GLOBALHEADER),
+ use_separate_disk_params(use_separate_disk_params),
dyn(load_x264_for_bit_depth(global_flags.x264_bit_depth))
{
- call_once(x264_metrics_inited, [](){
- global_metrics.add("x264_queued_frames", &metric_x264_queued_frames, Metrics::TYPE_GAUGE);
- global_metrics.add("x264_max_queued_frames", &metric_x264_max_queued_frames, Metrics::TYPE_GAUGE);
- global_metrics.add("x264_dropped_frames", &metric_x264_dropped_frames);
- global_metrics.add("x264_output_frames", {{ "type", "i" }}, &metric_x264_output_frames_i);
- global_metrics.add("x264_output_frames", {{ "type", "p" }}, &metric_x264_output_frames_p);
- global_metrics.add("x264_output_frames", {{ "type", "b" }}, &metric_x264_output_frames_b);
-
- metric_x264_crf.init_uniform(50);
- global_metrics.add("x264_crf", &metric_x264_crf);
- x264_latency_histogram.init("x264");
- });
+ if (use_separate_disk_params) {
+ call_once(x264_disk_metrics_inited, []{
+ global_metrics.add("x264_queued_frames", {{ "encode", "separate_disk" }}, &metric_x264_disk_queued_frames, Metrics::TYPE_GAUGE);
+ global_metrics.add("x264_max_queued_frames", {{ "encode", "separate_disk" }}, &metric_x264_disk_max_queued_frames, Metrics::TYPE_GAUGE);
+ global_metrics.add("x264_dropped_frames", {{ "encode", "separate_disk" }}, &metric_x264_disk_dropped_frames);
+ global_metrics.add("x264_output_frames", {{ "encode", "separate_disk" }, { "type", "i" }}, &metric_x264_disk_output_frames_i);
+ global_metrics.add("x264_output_frames", {{ "encode", "separate_disk" }, { "type", "p" }}, &metric_x264_disk_output_frames_p);
+ global_metrics.add("x264_output_frames", {{ "encode", "separate_disk" }, { "type", "b" }}, &metric_x264_disk_output_frames_b);
+
+ metric_x264_disk_crf.init_uniform(50);
+ global_metrics.add("x264_crf", {{ "encode", "separate_disk" }}, &metric_x264_disk_crf);
+ x264_disk_latency_histogram.init("x264_disk");
+ });
+ } else {
+ call_once(use_separate_disk_params ? x264_disk_metrics_inited : x264_metrics_inited, []{
+ global_metrics.add("x264_queued_frames", {{ "encode", "regular" }}, &metric_x264_queued_frames, Metrics::TYPE_GAUGE);
+ global_metrics.add("x264_max_queued_frames", {{ "encode", "regular" }}, &metric_x264_max_queued_frames, Metrics::TYPE_GAUGE);
+ global_metrics.add("x264_dropped_frames", {{ "encode", "regular" }}, &metric_x264_dropped_frames);
+ global_metrics.add("x264_output_frames", {{ "encode", "regular" }, { "type", "i" }}, &metric_x264_output_frames_i);
+ global_metrics.add("x264_output_frames", {{ "encode", "regular" }, { "type", "p" }}, &metric_x264_output_frames_p);
+ global_metrics.add("x264_output_frames", {{ "encode", "regular" }, { "type", "b" }}, &metric_x264_output_frames_b);
+
+ metric_x264_crf.init_uniform(50);
+ global_metrics.add("x264_crf", {{ "encode", "regular" }}, &metric_x264_crf);
+ x264_latency_histogram.init("x264");
+ });
+ }
size_t bytes_per_pixel = global_flags.x264_bit_depth > 8 ? 2 : 1;
frame_pool.reset(new uint8_t[global_flags.width * global_flags.height * 2 * bytes_per_pixel * X264_QUEUE_LENGTH]);
{
lock_guard<mutex> lock(mu);
if (free_frames.empty()) {
- fprintf(stderr, "WARNING: x264 queue full, dropping frame with pts %" PRId64 "\n", pts);
- ++metric_x264_dropped_frames;
+ if (use_separate_disk_params) {
+ fprintf(stderr, "WARNING: x264 queue full (disk encoder), dropping frame with pts %" PRId64 "\n", pts);
+ ++metric_x264_disk_dropped_frames;
+ } else {
+ fprintf(stderr, "WARNING: x264 queue full, dropping frame with pts %" PRId64 "\n", pts);
+ ++metric_x264_dropped_frames;
+ }
return;
}
lock_guard<mutex> lock(mu);
queued_frames.push(qf);
queued_frames_nonempty.notify_all();
- metric_x264_queued_frames = queued_frames.size();
+ if (use_separate_disk_params) {
+ metric_x264_disk_queued_frames = queued_frames.size();
+ } else {
+ metric_x264_queued_frames = queued_frames.size();
+ }
}
}
void X264Encoder::init_x264()
{
x264_param_t param;
- dyn.x264_param_default_preset(¶m, global_flags.x264_preset.c_str(), global_flags.x264_tune.c_str());
+ if (use_separate_disk_params) {
+ dyn.x264_param_default_preset(¶m, global_flags.x264_separate_disk_preset.c_str(), global_flags.x264_separate_disk_tune.c_str());
+ } else {
+ dyn.x264_param_default_preset(¶m, global_flags.x264_preset.c_str(), global_flags.x264_tune.c_str());
+ }
param.i_width = global_flags.width;
param.i_height = global_flags.height;
param.i_timebase_num = 1;
param.i_timebase_den = TIMEBASE;
param.i_keyint_max = 50; // About one second.
- if (global_flags.x264_speedcontrol) {
+ if (!use_separate_disk_params && global_flags.x264_speedcontrol) {
param.i_frame_reference = 16; // Because speedcontrol is never allowed to change this above what we set at start.
}
#if X264_BUILD >= 153
param.vui.i_colmatrix = 6; // BT.601/SMPTE 170M.
}
- if (!isinf(global_flags.x264_crf)) {
+ const double crf = use_separate_disk_params ? global_flags.x264_separate_disk_crf : global_flags.x264_crf;
+ const int bitrate = use_separate_disk_params ? global_flags.x264_separate_disk_bitrate : global_flags.x264_bitrate;
+ if (!isinf(crf)) {
param.rc.i_rc_method = X264_RC_CRF;
- param.rc.f_rf_constant = global_flags.x264_crf;
+ param.rc.f_rf_constant = crf;
} else {
param.rc.i_rc_method = X264_RC_ABR;
- param.rc.i_bitrate = global_flags.x264_bitrate;
+ param.rc.i_bitrate = bitrate;
+ }
+ if (!use_separate_disk_params) {
+ update_vbv_settings(¶m);
}
- update_vbv_settings(¶m);
if (param.rc.i_vbv_max_bitrate > 0) {
// If the user wants VBV control to cap the max rate, it is
// also reasonable to assume that they are fine with the stream
// be on the safe side. Shouldn't affect quality in any meaningful way.
param.rc.i_qp_min = 5;
- for (const string &str : global_flags.x264_extra_param) {
+ const vector<string> &extra_param = use_separate_disk_params ? global_flags.x264_separate_disk_extra_param : global_flags.x264_extra_param;
+ for (const string &str : extra_param) {
const size_t pos = str.find(',');
if (pos == string::npos) {
if (dyn.x264_param_parse(¶m, str.c_str(), nullptr) != 0) {
abort();
}
- if (global_flags.x264_speedcontrol) {
+ if (!use_separate_disk_params && global_flags.x264_speedcontrol) {
speed_control.reset(new X264SpeedControl(x264, /*f_speed=*/1.0f, X264_QUEUE_LENGTH, /*f_buffer_init=*/1.0f));
}
qf.data = nullptr;
}
- metric_x264_queued_frames = queued_frames.size();
+ if (use_separate_disk_params) {
+ metric_x264_disk_queued_frames = queued_frames.size();
+ } else {
+ metric_x264_queued_frames = queued_frames.size();
+ }
frames_left = !queued_frames.empty();
}
if (num_nal == 0) return;
- if (IS_X264_TYPE_I(pic.i_type)) {
- ++metric_x264_output_frames_i;
- } else if (IS_X264_TYPE_B(pic.i_type)) {
- ++metric_x264_output_frames_b;
+ if (use_separate_disk_params) {
+ if (IS_X264_TYPE_I(pic.i_type)) {
+ ++metric_x264_disk_output_frames_i;
+ } else if (IS_X264_TYPE_B(pic.i_type)) {
+ ++metric_x264_disk_output_frames_b;
+ } else {
+ ++metric_x264_disk_output_frames_p;
+ }
+
+ metric_x264_disk_crf.count_event(pic.prop.f_crf_avg);
} else {
- ++metric_x264_output_frames_p;
- }
+ if (IS_X264_TYPE_I(pic.i_type)) {
+ ++metric_x264_output_frames_i;
+ } else if (IS_X264_TYPE_B(pic.i_type)) {
+ ++metric_x264_output_frames_b;
+ } else {
+ ++metric_x264_output_frames_p;
+ }
- metric_x264_crf.count_event(pic.prop.f_crf_avg);
+ metric_x264_crf.count_event(pic.prop.f_crf_avg);
+ }
if (frames_being_encoded.count(pic.i_pts)) {
ReceivedTimestamps received_ts = frames_being_encoded[pic.i_pts];
class X264Encoder {
public:
- X264Encoder(AVOutputFormat *oformat); // Does not take ownership.
+ X264Encoder(AVOutputFormat *oformat, bool use_separate_disk_params); // Does not take ownership.
// Called after the last frame. Will block; once this returns,
// the last data is flushed.
std::unique_ptr<uint8_t[]> frame_pool;
std::vector<Mux *> muxes;
- bool wants_global_headers;
+ const bool wants_global_headers;
+ const bool use_separate_disk_params;
std::string global_headers;
std::string buffered_sei; // Will be output before first frame, if any.