]> git.sesse.net Git - nageru/blobdiff - video_encoder.cpp
Add functionality for MJPEG export.
[nageru] / video_encoder.cpp
index 6a4ebf7219b04ec7b7bd5c85cdcb0bff4ca60680..2b8fcd5edec9e0a9ea3a79aed79d39d02494375d 100644 (file)
@@ -34,13 +34,14 @@ string generate_local_dump_filename(int frame)
        tm now_tm;
        localtime_r(&now, &now_tm);
 
-       char timestamp[256];
+       char timestamp[64];
        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",
+       snprintf(filename, sizeof(filename), "%s/%s%s-f%02d%s",
+               global_flags.recording_dir.c_str(),
                LOCAL_DUMP_PREFIX, timestamp, frame % 100, LOCAL_DUMP_SUFFIX);
        return filename;
 }
@@ -95,7 +96,8 @@ void VideoEncoder::do_cut(int frame)
        // the same time, it means pts could come out of order to the stream mux,
        // and we need to plug it until the shutdown is complete.
        stream_mux->plug();
-       lock_guard<mutex> lock(qs_mu);
+       lock(qs_mu, qs_audio_mu);
+       lock_guard<mutex> lock1(qs_mu, adopt_lock), lock2(qs_audio_mu, adopt_lock);
        QuickSyncEncoder *old_encoder = quicksync_encoder.release();  // When we go C++14, we can use move capture instead.
        X264Encoder *old_x264_encoder = nullptr;
        if (global_flags.x264_video_to_disk) {
@@ -135,14 +137,22 @@ void VideoEncoder::change_x264_bitrate(unsigned rate_kbit)
 
 void VideoEncoder::add_audio(int64_t pts, std::vector<float> audio)
 {
-       lock_guard<mutex> lock(qs_mu);
-       quicksync_encoder->add_audio(pts, audio);
+       // Take only qs_audio_mu, since add_audio() is thread safe
+       // (we can only conflict with do_cut(), which takes qs_audio_mu)
+       // and we don't want to contend with begin_frame().
+       {
+               lock_guard<mutex> lock(qs_audio_mu);
+               quicksync_encoder->add_audio(pts, audio);
+       }
        stream_audio_encoder->encode_audio(audio, pts + quicksync_encoder->global_delay());
 }
 
 bool VideoEncoder::is_zerocopy() const
 {
-       lock_guard<mutex> lock(qs_mu);
+       // Explicitly do _not_ take qs_mu; this is called from the mixer,
+       // and qs_mu might be contended. is_zerocopy() is thread safe
+       // and never called in parallel with do_cut() (both happen only
+       // from the mixer thread).
        return quicksync_encoder->is_zerocopy();
 }
 
@@ -183,9 +193,9 @@ void VideoEncoder::open_output_stream()
                video_extradata = x264_encoder->get_global_headers();
        }
 
-       int time_base = global_flags.stream_coarse_timebase ? COARSE_TIMEBASE : TIMEBASE;
-       stream_mux.reset(new Mux(avctx, width, height, video_codec, video_extradata, stream_audio_encoder->get_codec_parameters().get(), time_base,
-               /*write_callback=*/nullptr));
+       stream_mux.reset(new Mux(avctx, width, height, video_codec, video_extradata, stream_audio_encoder->get_codec_parameters().get(), COARSE_TIMEBASE,
+               /*write_callback=*/nullptr, Mux::WRITE_FOREGROUND, { &stream_mux_metrics }));
+       stream_mux_metrics.init({{ "destination", "http" }});
 }
 
 int VideoEncoder::write_packet2_thunk(void *opaque, uint8_t *buf, int buf_size, AVIODataMarkerType type, int64_t time)
@@ -206,9 +216,9 @@ int VideoEncoder::write_packet2(uint8_t *buf, int buf_size, AVIODataMarkerType t
 
        if (type == AVIO_DATA_MARKER_HEADER) {
                stream_mux_header.append((char *)buf, buf_size);
-               httpd->set_header(stream_mux_header);
+               httpd->set_header(HTTPD::MAIN_STREAM, stream_mux_header);
        } else {
-               httpd->add_data((char *)buf, buf_size, type == AVIO_DATA_MARKER_SYNC_POINT);
+               httpd->add_data(HTTPD::MAIN_STREAM, (char *)buf, buf_size, type == AVIO_DATA_MARKER_SYNC_POINT, time, AVRational{ AV_TIME_BASE, 1 });
        }
        return buf_size;
 }