OBJS += chroma_subsampler.o v210_converter.o mixer.o pbo_frame_allocator.o context.o ref_counted_frame.o theme.o httpd.o flags.o image_input.o alsa_output.o disk_space_estimator.o print_latency.o timecode_renderer.o $(AUDIO_MIXER_OBJS)
# Streaming and encoding objects
-OBJS += quicksync_encoder.o x264_encoder.o x264_speed_control.o video_encoder.o metacube2.o mux.o audio_encoder.o ffmpeg_raii.o
+OBJS += quicksync_encoder.o x264_encoder.o x264_dynamic.o x264_speed_control.o video_encoder.o metacube2.o mux.o audio_encoder.o ffmpeg_raii.o
# DeckLink
OBJS += decklink_capture.o decklink_util.o decklink_output.o decklink/DeckLinkAPIDispatch.o
OPTION_X264_BITRATE,
OPTION_X264_VBV_BUFSIZE,
OPTION_X264_VBV_MAX_BITRATE,
+ OPTION_X264_10_BIT,
OPTION_X264_PARAM,
OPTION_HTTP_MUX,
OPTION_HTTP_COARSE_TIMEBASE,
fprintf(stderr, " default: same as --x264-bitrate, that is, one-second VBV)\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-10-bit enable 10-bit x264 encoding\n");
fprintf(stderr, " --x264-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");
{ "x264-bitrate", required_argument, 0, OPTION_X264_BITRATE },
{ "x264-vbv-bufsize", required_argument, 0, OPTION_X264_VBV_BUFSIZE },
{ "x264-vbv-max-bitrate", required_argument, 0, OPTION_X264_VBV_MAX_BITRATE },
+ { "x264-10-bit", no_argument, 0, OPTION_X264_10_BIT },
{ "x264-param", required_argument, 0, OPTION_X264_PARAM },
{ "http-mux", required_argument, 0, OPTION_HTTP_MUX },
{ "http-coarse-timebase", no_argument, 0, OPTION_HTTP_COARSE_TIMEBASE },
case OPTION_X264_VBV_BUFSIZE:
global_flags.x264_vbv_buffer_size = atoi(optarg);
break;
+ case OPTION_X264_10_BIT:
+ global_flags.x264_bit_depth = 10;
+ break;
case OPTION_X264_VBV_MAX_BITRATE:
global_flags.x264_vbv_max_bitrate = atoi(optarg);
break;
int x264_bitrate = DEFAULT_X264_OUTPUT_BIT_RATE; // In kilobit/sec.
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).
+ int x264_bit_depth = 8;
std::vector<std::string> x264_extra_param; // In “key[,value]” format.
bool enable_alsa_output = true;
std::map<int, int> default_stream_mapping;
--- /dev/null
+#include "x264_dynamic.h"
+
+#include <assert.h>
+#include <dlfcn.h>
+#include <link.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+
+using namespace std;
+
+X264Dynamic load_x264_for_bit_depth(unsigned depth)
+{
+ X264Dynamic dyn;
+ if (unsigned(x264_bit_depth) >= depth) {
+ // Just use the one we are linked to.
+ dyn.handle = nullptr;
+ dyn.x264_encoder_close = x264_encoder_close;
+ dyn.x264_encoder_delayed_frames = x264_encoder_delayed_frames;
+ dyn.x264_encoder_encode = x264_encoder_encode;
+ dyn.x264_encoder_headers = x264_encoder_headers;
+ dyn.x264_encoder_open = x264_encoder_open;
+ dyn.x264_encoder_parameters = x264_encoder_parameters;
+ dyn.x264_encoder_reconfig = x264_encoder_reconfig;
+ dyn.x264_param_apply_profile = x264_param_apply_profile;
+ dyn.x264_param_default_preset = x264_param_default_preset;
+ dyn.x264_param_parse = x264_param_parse;
+ dyn.x264_picture_init = x264_picture_init;
+ return dyn;
+ }
+
+ // Uh-oh, our currently loaded library doesn't have the required support.
+ // Let's try to dynamically load a 10-bit version; in particular, Debian
+ // has a version in /usr/lib/x86_64-linux-gnu/x264-10bit/libx264.so.<soname>,
+ // so we try to figure out where our libx264 comes from, and modify that path.
+ string x264_dir, x264_suffix;
+ void *handle = dlopen(nullptr, RTLD_NOW);
+ link_map *m;
+ int err = dlinfo(handle, RTLD_DI_LINKMAP, &m);
+ assert(err != -1);
+ for ( ; m != nullptr; m = m->l_next) {
+ if (m->l_name == nullptr) {
+ continue;
+ }
+ const char *ptr = strstr(m->l_name, "/libx264.so.");
+ if (ptr != nullptr) {
+ x264_dir = string(m->l_name, ptr - m->l_name);
+ x264_suffix = string(ptr, (m->l_name + strlen(m->l_name)) - ptr);
+ break;
+ }
+ }
+ dlclose(handle);
+
+ if (x264_dir.empty()) {
+ fprintf(stderr, "ERROR: Requested %d-bit x264, but is not linked to such an x264, and could not find one.\n",
+ depth);
+ exit(1);
+ }
+
+ string x264_10b_string = x264_dir + "/x264-10bit" + x264_suffix;
+ void *x264_dlhandle = dlopen(x264_10b_string.c_str(), RTLD_NOW);
+ if (x264_dlhandle == nullptr) {
+ fprintf(stderr, "ERROR: Requested %d-bit x264, but is not linked to such an x264, and %s would not load.\n",
+ depth, x264_10b_string.c_str());
+ exit(1);
+ }
+
+ dyn.handle = x264_dlhandle;
+ dyn.x264_encoder_close = (decltype(::x264_encoder_close) *)dlsym(x264_dlhandle, "x264_encoder_close");
+ dyn.x264_encoder_delayed_frames = (decltype(::x264_encoder_delayed_frames) *)dlsym(x264_dlhandle, "x264_encoder_delayed_frames");
+ dyn.x264_encoder_encode = (decltype(::x264_encoder_encode) *)dlsym(x264_dlhandle, "x264_encoder_encode");
+ dyn.x264_encoder_headers = (decltype(::x264_encoder_headers) *)dlsym(x264_dlhandle, "x264_encoder_headers");
+ char x264_encoder_open_symname[256];
+ snprintf(x264_encoder_open_symname, sizeof(x264_encoder_open_symname), "x264_encoder_open_%d", X264_BUILD);
+ dyn.x264_encoder_open = (decltype(::x264_encoder_open) *)dlsym(x264_dlhandle, x264_encoder_open_symname);
+ dyn.x264_encoder_parameters = (decltype(::x264_encoder_parameters) *)dlsym(x264_dlhandle, "x264_encoder_parameters");
+ dyn.x264_encoder_reconfig = (decltype(::x264_encoder_reconfig) *)dlsym(x264_dlhandle, "x264_encoder_reconfig");
+ dyn.x264_param_apply_profile = (decltype(::x264_param_apply_profile) *)dlsym(x264_dlhandle, "x264_param_apply_profile");
+ dyn.x264_param_default_preset = (decltype(::x264_param_default_preset) *)dlsym(x264_dlhandle, "x264_param_default_preset");
+ dyn.x264_param_parse = (decltype(::x264_param_parse) *)dlsym(x264_dlhandle, "x264_param_parse");
+ dyn.x264_picture_init = (decltype(::x264_picture_init) *)dlsym(x264_dlhandle, "x264_picture_init");
+ return dyn;
+}
--- /dev/null
+#ifndef _X264_DYNAMIC_H
+#define _X264_DYNAMIC_H 1
+
+// A helper to load 10-bit x264 if needed.
+
+#include <stdint.h>
+
+extern "C" {
+#include <x264.h>
+}
+
+struct X264Dynamic {
+ void *handle; // If not nullptr, needs to be dlclose()d.
+ decltype(::x264_encoder_close) *x264_encoder_close;
+ decltype(::x264_encoder_delayed_frames) *x264_encoder_delayed_frames;
+ decltype(::x264_encoder_encode) *x264_encoder_encode;
+ decltype(::x264_encoder_headers) *x264_encoder_headers;
+ decltype(::x264_encoder_open) *x264_encoder_open;
+ decltype(::x264_encoder_parameters) *x264_encoder_parameters;
+ decltype(::x264_encoder_reconfig) *x264_encoder_reconfig;
+ decltype(::x264_param_apply_profile) *x264_param_apply_profile;
+ decltype(::x264_param_default_preset) *x264_param_default_preset;
+ decltype(::x264_param_parse) *x264_param_parse;
+ decltype(::x264_picture_init) *x264_picture_init;
+};
+X264Dynamic load_x264_for_bit_depth(unsigned depth);
+
+#endif // !defined(_X264_DYNAMIC_H)
#include "x264_encoder.h"
#include <assert.h>
+#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mux.h"
#include "print_latency.h"
#include "timebase.h"
+#include "x264_dynamic.h"
#include "x264_speed_control.h"
extern "C" {
} // namespace
X264Encoder::X264Encoder(AVOutputFormat *oformat)
- : wants_global_headers(oformat->flags & AVFMT_GLOBALHEADER)
+ : wants_global_headers(oformat->flags & AVFMT_GLOBALHEADER),
+ dyn(load_x264_for_bit_depth(global_flags.x264_bit_depth))
{
frame_pool.reset(new uint8_t[global_flags.width * global_flags.height * 2 * X264_QUEUE_LENGTH]);
for (unsigned i = 0; i < X264_QUEUE_LENGTH; ++i) {
should_quit = true;
queued_frames_nonempty.notify_all();
encoder_thread.join();
+ if (dyn.handle) {
+ dlclose(dyn.handle);
+ }
}
void X264Encoder::add_frame(int64_t pts, int64_t duration, YCbCrLumaCoefficients ycbcr_coefficients, const uint8_t *data, const ReceivedTimestamps &received_ts)
void X264Encoder::init_x264()
{
x264_param_t param;
- x264_param_default_preset(¶m, global_flags.x264_preset.c_str(), global_flags.x264_tune.c_str());
+ 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;
for (const string &str : global_flags.x264_extra_param) {
const size_t pos = str.find(',');
if (pos == string::npos) {
- if (x264_param_parse(¶m, str.c_str(), nullptr) != 0) {
+ if (dyn.x264_param_parse(¶m, str.c_str(), nullptr) != 0) {
fprintf(stderr, "ERROR: x264 rejected parameter '%s'\n", str.c_str());
}
} else {
const string key = str.substr(0, pos);
const string value = str.substr(pos + 1);
- if (x264_param_parse(¶m, key.c_str(), value.c_str()) != 0) {
+ if (dyn.x264_param_parse(¶m, key.c_str(), value.c_str()) != 0) {
fprintf(stderr, "ERROR: x264 rejected parameter '%s' set to '%s'\n",
key.c_str(), value.c_str());
}
}
}
- x264_param_apply_profile(¶m, "high");
+ if (global_flags.x264_bit_depth > 8) {
+ dyn.x264_param_apply_profile(¶m, "high10");
+ } else {
+ dyn.x264_param_apply_profile(¶m, "high");
+ }
param.b_repeat_headers = !wants_global_headers;
- x264 = x264_encoder_open(¶m);
+ x264 = dyn.x264_encoder_open(¶m);
if (x264 == nullptr) {
fprintf(stderr, "ERROR: x264 initialization failed.\n");
exit(1);
x264_nal_t *nal;
int num_nal;
- x264_encoder_headers(x264, &nal, &num_nal);
+ dyn.x264_encoder_headers(x264, &nal, &num_nal);
for (int i = 0; i < num_nal; ++i) {
if (nal[i].i_type == NAL_SEI) {
// We should quit only if the should_quit flag is set _and_ we have nothing
// in either queue.
- } while (!should_quit || frames_left || x264_encoder_delayed_frames(x264) > 0);
+ } while (!should_quit || frames_left || dyn.x264_encoder_delayed_frames(x264) > 0);
- x264_encoder_close(x264);
+ dyn.x264_encoder_close(x264);
}
void X264Encoder::encode_frame(X264Encoder::QueuedFrame qf)
x264_picture_t *input_pic = nullptr;
if (qf.data) {
- x264_picture_init(&pic);
+ dyn.x264_picture_init(&pic);
pic.i_pts = qf.pts;
pic.img.i_csp = X264_CSP_NV12;
});
} else {
x264_param_t param;
- x264_encoder_parameters(x264, ¶m);
+ dyn.x264_encoder_parameters(x264, ¶m);
if (bitrate_override_func) {
bitrate_override_func(¶m);
}
ycbcr_coefficients_override_func(¶m);
- x264_encoder_reconfig(x264, ¶m);
+ dyn.x264_encoder_reconfig(x264, ¶m);
}
if (speed_control) {
speed_control->before_frame(float(free_frames.size()) / X264_QUEUE_LENGTH, X264_QUEUE_LENGTH, 1e6 * qf.duration / TIMEBASE);
}
- x264_encoder_encode(x264, &nal, &num_nal, input_pic, &pic);
+ dyn.x264_encoder_encode(x264, &nal, &num_nal, input_pic, &pic);
if (speed_control) {
speed_control->after_frame();
}
#include <movit/image_format.h>
#include "print_latency.h"
+#include "x264_dynamic.h"
class Mux;
class X264SpeedControl;
std::thread encoder_thread;
std::atomic<bool> x264_init_done{false};
std::atomic<bool> should_quit{false};
+ X264Dynamic dyn;
x264_t *x264;
std::unique_ptr<X264SpeedControl> speed_control;
#include "x264_speed_control.h"
+#include <dlfcn.h>
#include <math.h>
#include <stdio.h>
#include <x264.h>
using namespace std::chrono;
X264SpeedControl::X264SpeedControl(x264_t *x264, float f_speed, int i_buffer_size, float f_buffer_init)
- : x264(x264), f_speed(f_speed)
+ : dyn(load_x264_for_bit_depth(global_flags.x264_bit_depth)),
+ x264(x264), f_speed(f_speed)
{
x264_param_t param;
- x264_encoder_parameters(x264, ¶m);
+ dyn.x264_encoder_parameters(x264, ¶m);
float fps = (float)param.i_fps_num / param.i_fps_den;
uspf = 1e6 / fps;
(float)stat.min_buffer / buffer_size,
(float)stat.max_buffer / buffer_size );
// x264_log( x264, X264_LOG_INFO, "speedcontrol: avg cplx=%.5f\n", cplx_num / cplx_den );
+ if (dyn.handle) {
+ dlclose(dyn.handle);
+ }
}
typedef struct
const sc_preset_t *s = &presets[new_preset];
x264_param_t p;
- x264_encoder_parameters(x264, &p);
+ dyn.x264_encoder_parameters(x264, &p);
p.i_frame_reference = s->refs;
p.i_bframe_adaptive = s->badapt;
if (override_func) {
override_func(&p);
}
- x264_encoder_reconfig(x264, &p);
+ dyn.x264_encoder_reconfig(x264, &p);
preset = new_preset;
}
#include <functional>
extern "C" {
-#include "x264.h"
+#include <x264.h>
}
+#include "x264_dynamic.h"
+
class X264SpeedControl {
public:
// x264: Encoding object we are using; must be opened. Assumed to be
int dither_preset(float f);
void apply_preset(int new_preset);
+ X264Dynamic dyn;
+
// Not owned by us.
x264_t *x264;