]> git.sesse.net Git - nageru/commitdiff
Add a menu option to change x264 video bitrate while running.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Fri, 22 Jul 2016 17:54:37 +0000 (19:54 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Fri, 22 Jul 2016 17:54:37 +0000 (19:54 +0200)
mainwindow.cpp
mainwindow.h
mixer.h
ui_mainwindow.ui
video_encoder.cpp
video_encoder.h
x264_encoder.cpp
x264_encoder.h

index 627d558609910c6f5c69900076d7fc2640bec741..4b8eb51482543b01549a36d02a19c286f14abcc4 100644 (file)
@@ -7,6 +7,7 @@
 #include <string>
 #include <vector>
 #include <QBoxLayout>
+#include <QInputDialog>
 #include <QKeySequence>
 #include <QLabel>
 #include <QMetaType>
@@ -17,6 +18,7 @@
 #include <QString>
 
 #include "aboutdialog.h"
+#include "flags.h"
 #include "glwidget.h"
 #include "lrameter.h"
 #include "mixer.h"
@@ -61,7 +63,13 @@ MainWindow::MainWindow()
        // The menus.
        connect(ui->cut_action, &QAction::triggered, this, &MainWindow::cut_triggered);
        connect(ui->exit_action, &QAction::triggered, this, &MainWindow::exit_triggered);
-       connect(ui->about_action, &QAction::triggered, this, &MainWindow::about_triggered),
+       connect(ui->about_action, &QAction::triggered, this, &MainWindow::about_triggered);
+
+       if (global_flags.x264_video_to_http) {
+               connect(ui->x264_bitrate_action, &QAction::triggered, this, &MainWindow::x264_bitrate_triggered);
+       } else {
+               ui->x264_bitrate_action->setEnabled(false);
+       }
 
        // Hook up the transition buttons. (Keyboard shortcuts are set in set_transition_names().)
        // TODO: Make them dynamic.
@@ -187,6 +195,16 @@ void MainWindow::cut_triggered()
        global_mixer->schedule_cut();
 }
 
+void MainWindow::x264_bitrate_triggered()
+{
+       bool ok;
+       int new_bitrate = QInputDialog::getInt(this, "Change x264 bitrate", "Choose new bitrate for x264 HTTP output (from 100–100,000 kbit/sec):", global_flags.x264_bitrate, /*min=*/100, /*max=*/100000, /*step=*/100, &ok);
+       if (ok && new_bitrate >= 100 && new_bitrate <= 100000) {
+               global_flags.x264_bitrate = new_bitrate;
+               global_mixer->change_x264_bitrate(new_bitrate);
+       }
+}
+
 void MainWindow::exit_triggered()
 {
        close();
index e6d0880cc20f85017ea2d09cb611c35eb544c88f..8110176c20412b272afd57669b2a128ab294ed67 100644 (file)
@@ -33,6 +33,7 @@ public:
 
 public slots:
        void cut_triggered();
+       void x264_bitrate_triggered();
        void exit_triggered();
        void about_triggered();
        void transition_clicked(int transition_number);
diff --git a/mixer.h b/mixer.h
index 1d6d0308ca5570ee926b2bab0fa4b44f8b58835a..21b22552049d78c7ec9e7438648fccfcb7ed987e 100644 (file)
--- a/mixer.h
+++ b/mixer.h
@@ -387,6 +387,10 @@ public:
                cards[card_index].capture->set_audio_input(input);
        }
 
+       void change_x264_bitrate(unsigned rate_kbit) {
+               video_encoder->change_x264_bitrate(rate_kbit);
+       }
+
 private:
        void configure_card(unsigned card_index, CaptureInterface *capture, bool is_fake_capture);
        void bm_frame(unsigned card_index, uint16_t timecode,
index 2d7031c8ecad6403aaddc1f51b61079eafb134cf..8c923112150248600fefda3795f1c727860de74b 100644 (file)
      <x>0</x>
      <y>0</y>
      <width>1089</width>
-     <height>19</height>
+     <height>23</height>
     </rect>
    </property>
    <widget class="QMenu" name="menuWhat">
      <string>&amp;Video</string>
     </property>
     <addaction name="cut_action"/>
+    <addaction name="x264_bitrate_action"/>
     <addaction name="exit_action"/>
    </widget>
    <widget class="QMenu" name="menu_Help">
     <string>&amp;About Nageru…</string>
    </property>
   </action>
+  <action name="x264_bitrate_action">
+   <property name="text">
+    <string>Change &amp;x264 bitrate…</string>
+   </property>
+  </action>
  </widget>
  <layoutdefault spacing="6" margin="11"/>
  <customwidgets>
index 07bb1c95a1fb8aa72c35d3615f9c75676697bf2c..dbb2aa774502f67fb4469a8b7b462594aef5908a 100644 (file)
@@ -96,6 +96,11 @@ void VideoEncoder::do_cut(int frame)
        quicksync_encoder->set_stream_mux(stream_mux.get());
 }
 
+void VideoEncoder::change_x264_bitrate(unsigned rate_kbit)
+{
+       x264_encoder->change_bitrate(rate_kbit);
+}
+
 void VideoEncoder::add_audio(int64_t pts, std::vector<float> audio)
 {
        lock_guard<mutex> lock(qs_mu);
index fc3a570a736c4867e002ec27987505d7099c6de0..fd16ac82f66584cc1f5a76eb783bf954b3a056d9 100644 (file)
@@ -37,6 +37,8 @@ public:
        // Does a cut of the disk stream immediately ("frame" is used for the filename only).
        void do_cut(int frame);
 
+       void change_x264_bitrate(unsigned rate_kbit);
+
 private:
        void open_output_stream();
        static int write_packet2_thunk(void *opaque, uint8_t *buf, int buf_size, AVIODataMarkerType type, int64_t time);
index 7be454c75c8e0bc33394f4a1ddd610af05b07e40..756d673bfb40b2adc6588992e3b78c2fd3b14d3f 100644 (file)
@@ -14,6 +14,24 @@ extern "C" {
 
 using namespace std;
 
+namespace {
+
+void update_vbv_settings(x264_param_t *param)
+{
+       if (global_flags.x264_vbv_buffer_size < 0) {
+               param->rc.i_vbv_buffer_size = param->rc.i_bitrate;  // One-second VBV.
+       } else {
+               param->rc.i_vbv_buffer_size = global_flags.x264_vbv_buffer_size;
+       }
+       if (global_flags.x264_vbv_max_bitrate < 0) {
+               param->rc.i_vbv_max_bitrate = param->rc.i_bitrate;  // CBR.
+       } else {
+               param->rc.i_vbv_max_bitrate = global_flags.x264_vbv_max_bitrate;
+       }
+}
+
+}  // namespace
+
 X264Encoder::X264Encoder(AVOutputFormat *oformat)
        : wants_global_headers(oformat->flags & AVFMT_GLOBALHEADER)
 {
@@ -83,16 +101,7 @@ void X264Encoder::init_x264()
 
        param.rc.i_rc_method = X264_RC_ABR;
        param.rc.i_bitrate = global_flags.x264_bitrate;
-       if (global_flags.x264_vbv_buffer_size < 0) {
-               param.rc.i_vbv_buffer_size = param.rc.i_bitrate;  // One-second VBV.
-       } else {
-               param.rc.i_vbv_buffer_size = global_flags.x264_vbv_buffer_size;
-       }
-       if (global_flags.x264_vbv_max_bitrate < 0) {
-               param.rc.i_vbv_max_bitrate = param.rc.i_bitrate;  // CBR.
-       } else {
-               param.rc.i_vbv_max_bitrate = global_flags.x264_vbv_max_bitrate;
-       }
+       update_vbv_settings(&param);
        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
@@ -234,6 +243,17 @@ void X264Encoder::encode_frame(X264Encoder::QueuedFrame qf)
                input_pic = &pic;
        }
 
+       // See if we have a new bitrate to change to.
+       unsigned new_rate = new_bitrate_kbit.exchange(0);  // Read and clear.
+       if (new_rate != 0) {
+               x264_param_t param;
+               x264_encoder_parameters(x264, &param);
+               param.rc.i_bitrate = new_rate;
+               update_vbv_settings(&param);
+               x264_encoder_reconfig(x264, &param);
+               printf("changing rate to %u\n", new_rate);
+       }
+
        if (speed_control) {
                speed_control->before_frame(float(free_frames.size()) / X264_QUEUE_LENGTH, X264_QUEUE_LENGTH, 1e6 * qf.duration / TIMEBASE);
        }
index ef118c6f4410d7e3554aad2a4e8354d6875e1c0b..bb9f1dc066dd43b0b544b744452a1c93251a56da 100644 (file)
@@ -50,6 +50,10 @@ public:
 
        std::string get_global_headers() const { return global_headers; }
 
+       void change_bitrate(unsigned rate_kbit) {
+               new_bitrate_kbit = rate_kbit;
+       }
+
 private:
        struct QueuedFrame {
                int64_t pts, duration;
@@ -75,6 +79,8 @@ private:
        x264_t *x264;
        std::unique_ptr<X264SpeedControl> speed_control;
 
+       std::atomic<unsigned> new_bitrate_kbit{0};  // 0 for no change.
+
        // Protects everything below it.
        std::mutex mu;