#include <string>
#include <vector>
#include <QBoxLayout>
+#include <QInputDialog>
#include <QKeySequence>
#include <QLabel>
#include <QMetaType>
#include <QString>
#include "aboutdialog.h"
+#include "flags.h"
#include "glwidget.h"
#include "lrameter.h"
#include "mixer.h"
// 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.
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();
public slots:
void cut_triggered();
+ void x264_bitrate_triggered();
void exit_triggered();
void about_triggered();
void transition_clicked(int transition_number);
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,
<x>0</x>
<y>0</y>
<width>1089</width>
- <height>19</height>
+ <height>23</height>
</rect>
</property>
<widget class="QMenu" name="menuWhat">
<string>&Video</string>
</property>
<addaction name="cut_action"/>
+ <addaction name="x264_bitrate_action"/>
<addaction name="exit_action"/>
</widget>
<widget class="QMenu" name="menu_Help">
<string>&About Nageru…</string>
</property>
</action>
+ <action name="x264_bitrate_action">
+ <property name="text">
+ <string>Change &x264 bitrate…</string>
+ </property>
+ </action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
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);
// 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);
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)
{
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(¶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
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, ¶m);
+ param.rc.i_bitrate = new_rate;
+ update_vbv_settings(¶m);
+ x264_encoder_reconfig(x264, ¶m);
+ 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);
}
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;
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;