From 0fff2c95c89541e5b23611962a00886c64c00daa Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Sun, 10 Jan 2016 20:57:04 +0100 Subject: [PATCH] Add a menu item to start a new video segment (cut/cue, except that would be a confusing name). --- defs.h | 3 ++- httpd.cpp | 39 ++++++++++++++++++++++++++------------- httpd.h | 6 +++++- mainwindow.cpp | 8 ++++++++ mainwindow.h | 1 + mixer.cpp | 29 ++++++++++++++++++++++++++++- mixer.h | 6 ++++++ ui_mainwindow.ui | 6 ++++++ 8 files changed, 82 insertions(+), 16 deletions(-) diff --git a/defs.h b/defs.h index ef5aefd..9a08bc3 100644 --- a/defs.h +++ b/defs.h @@ -14,7 +14,8 @@ #define AUDIO_OUTPUT_SAMPLE_FMT AV_SAMPLE_FMT_S32 #define AUDIO_OUTPUT_BIT_RATE 0 -#define LOCAL_DUMP_FILE_NAME "test.mov" +#define LOCAL_DUMP_PREFIX "record-" +#define LOCAL_DUMP_SUFFIX ".mov" #define STREAM_MUX_NAME "mov" #define MUX_OPTS {{ "movflags", "empty_moov+frag_keyframe+default_base_moof" }} diff --git a/httpd.cpp b/httpd.cpp index f148fa1..29923f9 100644 --- a/httpd.cpp +++ b/httpd.cpp @@ -24,20 +24,9 @@ struct MHD_Response; using namespace std; -HTTPD::HTTPD(const char *output_filename, int width, int height) +HTTPD::HTTPD(int width, int height) : width(width), height(height) { - AVFormatContext *avctx = avformat_alloc_context(); - avctx->oformat = av_guess_format(NULL, output_filename, NULL); - strcpy(avctx->filename, output_filename); - int ret = avio_open2(&avctx->pb, output_filename, AVIO_FLAG_WRITE, &avctx->interrupt_callback, NULL); - if (ret < 0) { - char tmp[AV_ERROR_MAX_STRING_SIZE]; - fprintf(stderr, "%s: avio_open2() failed: %s\n", output_filename, av_make_error_string(tmp, sizeof(tmp), ret)); - exit(1); - } - - file_mux.reset(new Mux(avctx, width, height)); } void HTTPD::start(int port) @@ -53,7 +42,31 @@ void HTTPD::add_packet(const AVPacket &pkt, int64_t pts, int64_t dts) for (Stream *stream : streams) { stream->add_packet(pkt, pts, dts); } - file_mux->add_packet(pkt, pts, dts); + if (file_mux) { + file_mux->add_packet(pkt, pts, dts); + } +} + +void HTTPD::open_output_file(const string &filename) +{ + AVFormatContext *avctx = avformat_alloc_context(); + avctx->oformat = av_guess_format(NULL, filename.c_str(), NULL); + strcpy(avctx->filename, filename.c_str()); + + string url = "file:" + filename; + int ret = avio_open2(&avctx->pb, url.c_str(), AVIO_FLAG_WRITE, &avctx->interrupt_callback, NULL); + if (ret < 0) { + char tmp[AV_ERROR_MAX_STRING_SIZE]; + fprintf(stderr, "%s: avio_open2() failed: %s\n", filename.c_str(), av_make_error_string(tmp, sizeof(tmp), ret)); + exit(1); + } + + file_mux.reset(new Mux(avctx, width, height)); +} + +void HTTPD::close_output_file() +{ + file_mux.reset(); } int HTTPD::answer_to_connection_thunk(void *cls, MHD_Connection *connection, diff --git a/httpd.h b/httpd.h index d546fee..e8efc03 100644 --- a/httpd.h +++ b/httpd.h @@ -29,10 +29,14 @@ extern "C" { class HTTPD { public: - HTTPD(const char *output_filename, int width, int height); + HTTPD(int width, int height); void start(int port); void add_packet(const AVPacket &pkt, int64_t pts, int64_t dts); + // You can only have one going at the same time. + void open_output_file(const std::string &filename); + void close_output_file(); + private: static int answer_to_connection_thunk(void *cls, MHD_Connection *connection, const char *url, const char *method, diff --git a/mainwindow.cpp b/mainwindow.cpp index f15e56a..e23d191 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -41,6 +41,9 @@ MainWindow::MainWindow() ui->me_live->set_output(Mixer::OUTPUT_LIVE); ui->me_preview->set_output(Mixer::OUTPUT_PREVIEW); + // The menu. + connect(ui->cut_action, &QAction::triggered, this, &MainWindow::cut_triggered); + // Hook up the transition buttons. // TODO: Make them dynamic. connect(ui->transition_btn1, &QPushButton::clicked, bind(&MainWindow::transition_clicked, this, 0)); @@ -126,6 +129,11 @@ void MainWindow::mixer_shutting_down() } } +void MainWindow::cut_triggered() +{ + global_mixer->schedule_cut(); +} + void MainWindow::cutoff_knob_changed(int value) { float octaves = value * 0.1f; diff --git a/mainwindow.h b/mainwindow.h index f768aaa..bb5a870 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -32,6 +32,7 @@ public: void mixer_shutting_down(); public slots: + void cut_triggered(); void transition_clicked(int transition_number); void channel_clicked(int channel_number); void wb_button_clicked(int channel_number); diff --git a/mixer.cpp b/mixer.cpp index 1a4a3f9..6e1e45d 100644 --- a/mixer.cpp +++ b/mixer.cpp @@ -74,10 +74,27 @@ void insert_new_frame(RefCountedFrame frame, unsigned field_num, bool interlaced } } +string generate_local_dump_filename(int frame) +{ + time_t now = time(NULL); + tm now_tm; + localtime_r(&now, &now_tm); + + char timestamp[256]; + strftime(timestamp, sizeof(timestamp), "%F-%T%z", &now_tm); + + // Use the frame number to disambiguate between two cuts starting + // on the same second. + char filename[256]; + snprintf(filename, sizeof(filename), "%s%s-f%02d%s", + LOCAL_DUMP_PREFIX, timestamp, frame % 100, LOCAL_DUMP_SUFFIX); + return filename; +} + } // namespace Mixer::Mixer(const QSurfaceFormat &format, unsigned num_cards) - : httpd(LOCAL_DUMP_FILE_NAME, WIDTH, HEIGHT), + : httpd(WIDTH, HEIGHT), num_cards(num_cards), mixer_surface(create_surface(format)), h264_encoder_surface(create_surface(format)), @@ -85,6 +102,7 @@ Mixer::Mixer(const QSurfaceFormat &format, unsigned num_cards) limiter(OUTPUT_FREQUENCY), compressor(OUTPUT_FREQUENCY) { + httpd.open_output_file(generate_local_dump_filename(/*frame=*/0).c_str()); httpd.start(9095); CHECK(init_movit(MOVIT_SHADER_DIR, MOVIT_DEBUG_OFF)); @@ -626,6 +644,15 @@ void Mixer::thread_func() // chain->print_phase_timing(); } + if (should_cut.exchange(false)) { // Test and clear. + string filename = generate_local_dump_filename(frame); + printf("Starting new recording: %s\n", filename.c_str()); + h264_encoder->shutdown(); + httpd.close_output_file(); + httpd.open_output_file(filename.c_str()); + h264_encoder.reset(new H264Encoder(h264_encoder_surface, WIDTH, HEIGHT, &httpd)); + } + #if 0 // Reset every 100 frames, so that local variations in frame times // (especially for the first few frames, when the shaders are diff --git a/mixer.h b/mixer.h index 6d0cbce..db6db15 100644 --- a/mixer.h +++ b/mixer.h @@ -169,6 +169,11 @@ public: compressor_enabled = enabled; } + void schedule_cut() + { + should_cut = true; + } + void reset_meters(); private: @@ -252,6 +257,7 @@ private: std::thread mixer_thread; std::thread audio_thread; std::atomic should_quit{false}; + std::atomic should_cut{false}; audio_level_callback_t audio_level_callback = nullptr; std::mutex r128_mutex; diff --git a/ui_mainwindow.ui b/ui_mainwindow.ui index 62a35f6..1912efb 100644 --- a/ui_mainwindow.ui +++ b/ui_mainwindow.ui @@ -721,6 +721,7 @@ Video + @@ -730,6 +731,11 @@ Exit + + + Begin new video segment + + -- 2.39.2