]> git.sesse.net Git - nageru/commitdiff
Support loading 10-bit x264 dynamically.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 13 Mar 2017 20:55:02 +0000 (21:55 +0100)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 13 Mar 2017 21:08:42 +0000 (22:08 +0100)
Makefile
flags.cpp
flags.h
x264_dynamic.cpp [new file with mode: 0644]
x264_dynamic.h [new file with mode: 0644]
x264_encoder.cpp
x264_encoder.h
x264_speed_control.cpp
x264_speed_control.h

index 6d46bc38619f8803db2ddf93dbff9d5d5cb1d573..5733c473790b2c1d244df1a10363ace5ecf07541 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -25,7 +25,7 @@ AUDIO_MIXER_OBJS = audio_mixer.o alsa_input.o alsa_pool.o ebu_r128_proc.o stereo
 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
index 62028dfb83df184969dc6988c4fe5ccfd89601ee..e6c306be52d23b38614aa8361aac6331404c5cca 100644 (file)
--- a/flags.cpp
+++ b/flags.cpp
@@ -27,6 +27,7 @@ enum LongOption {
        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,
@@ -89,6 +90,7 @@ void usage()
        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");
@@ -156,6 +158,7 @@ void parse_flags(int argc, char * const argv[])
                { "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 },
@@ -287,6 +290,9 @@ void parse_flags(int argc, char * const argv[])
                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;
diff --git a/flags.h b/flags.h
index 97d2fe66b2b534275c462fecf8303716d06a4512..b840c15de32084bc674aeee4d08e9bbe44e0cf3a 100644 (file)
--- a/flags.h
+++ b/flags.h
@@ -35,6 +35,7 @@ struct Flags {
        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;
diff --git a/x264_dynamic.cpp b/x264_dynamic.cpp
new file mode 100644 (file)
index 0000000..6aa649e
--- /dev/null
@@ -0,0 +1,85 @@
+#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;
+}
diff --git a/x264_dynamic.h b/x264_dynamic.h
new file mode 100644 (file)
index 0000000..27e5202
--- /dev/null
@@ -0,0 +1,28 @@
+#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)
index 8e3b567abe1684801315cd86dec9721aade3be9a..c8f7b81279f2cf9e5c4a57dc3fe969a978cf4b0f 100644 (file)
@@ -1,6 +1,7 @@
 #include "x264_encoder.h"
 
 #include <assert.h>
+#include <dlfcn.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -13,6 +14,7 @@
 #include "mux.h"
 #include "print_latency.h"
 #include "timebase.h"
+#include "x264_dynamic.h"
 #include "x264_speed_control.h"
 
 extern "C" {
@@ -43,7 +45,8 @@ void update_vbv_settings(x264_param_t *param)
 }  // 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) {
@@ -57,6 +60,9 @@ X264Encoder::~X264Encoder()
        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)
@@ -92,7 +98,7 @@ void X264Encoder::add_frame(int64_t pts, int64_t duration, YCbCrLumaCoefficients
 void X264Encoder::init_x264()
 {
        x264_param_t param;
-       x264_param_default_preset(&param, global_flags.x264_preset.c_str(), global_flags.x264_tune.c_str());
+       dyn.x264_param_default_preset(&param, global_flags.x264_preset.c_str(), global_flags.x264_tune.c_str());
 
        param.i_width = global_flags.width;
        param.i_height = global_flags.height;
@@ -154,24 +160,28 @@ void X264Encoder::init_x264()
        for (const string &str : global_flags.x264_extra_param) {
                const size_t pos = str.find(',');
                if (pos == string::npos) {
-                       if (x264_param_parse(&param, str.c_str(), nullptr) != 0) {
+                       if (dyn.x264_param_parse(&param, 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(&param, key.c_str(), value.c_str()) != 0) {
+                       if (dyn.x264_param_parse(&param, 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(&param, "high");
+       if (global_flags.x264_bit_depth > 8) {
+               dyn.x264_param_apply_profile(&param, "high10");
+       } else {
+               dyn.x264_param_apply_profile(&param, "high");
+       }
 
        param.b_repeat_headers = !wants_global_headers;
 
-       x264 = x264_encoder_open(&param);
+       x264 = dyn.x264_encoder_open(&param);
        if (x264 == nullptr) {
                fprintf(stderr, "ERROR: x264 initialization failed.\n");
                exit(1);
@@ -185,7 +195,7 @@ void X264Encoder::init_x264()
                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) {
@@ -237,9 +247,9 @@ void X264Encoder::encoder_thread_func()
 
                // 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)
@@ -250,7 +260,7 @@ 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;
@@ -293,18 +303,18 @@ void X264Encoder::encode_frame(X264Encoder::QueuedFrame qf)
                });
        } else {
                x264_param_t param;
-               x264_encoder_parameters(x264, &param);
+               dyn.x264_encoder_parameters(x264, &param);
                if (bitrate_override_func) {
                        bitrate_override_func(&param);
                }
                ycbcr_coefficients_override_func(&param);
-               x264_encoder_reconfig(x264, &param);
+               dyn.x264_encoder_reconfig(x264, &param);
        }
 
        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();
        }
index 34cf70242b75dbb60cf806386bfcca74700c0e06..455bb1e96e822282c3dfd41162f6fe0304b7ceda 100644 (file)
@@ -33,6 +33,7 @@ extern "C" {
 #include <movit/image_format.h>
 
 #include "print_latency.h"
+#include "x264_dynamic.h"
 
 class Mux;
 class X264SpeedControl;
@@ -88,6 +89,7 @@ private:
        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;
 
index 141813203c61e4ae1e6d2ff0b5cb585ab2b8a5a7..47086829dbf0a239a85a96bf4adeede319fab752 100644 (file)
@@ -1,5 +1,6 @@
 #include "x264_speed_control.h"
 
+#include <dlfcn.h>
 #include <math.h>
 #include <stdio.h>
 #include <x264.h>
@@ -15,10 +16,11 @@ using namespace std;
 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, &param);
+       dyn.x264_encoder_parameters(x264, &param);
 
        float fps = (float)param.i_fps_num / param.i_fps_den;
        uspf = 1e6 / fps;
@@ -43,6 +45,9 @@ X264SpeedControl::~X264SpeedControl()
                (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
@@ -302,7 +307,7 @@ void X264SpeedControl::apply_preset(int new_preset)
 
        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;
@@ -317,6 +322,6 @@ void X264SpeedControl::apply_preset(int new_preset)
        if (override_func) {
                override_func(&p);
        }
-       x264_encoder_reconfig(x264, &p);
+       dyn.x264_encoder_reconfig(x264, &p);
        preset = new_preset;
 }
index b45e6c6de285c90e866293f0aa95e52258601b2a..de88f66705a7f15a068c91cb84f6e3e815852c34 100644 (file)
 #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
@@ -95,6 +97,8 @@ private:
        int dither_preset(float f);
        void apply_preset(int new_preset);
 
+       X264Dynamic dyn;
+
        // Not owned by us.
        x264_t *x264;