]> git.sesse.net Git - nageru/blobdiff - nageru/mjpeg_encoder.cpp
Fix an unneeded copy when muxing MJPEGs.
[nageru] / nageru / mjpeg_encoder.cpp
index 9ddb9ff15af20598ead26b5b9a6c282f16861e6c..07e302c4e93148c2b9a83edfc1034e5465f1e8bc 100644 (file)
@@ -16,6 +16,7 @@ extern "C" {
 #include "flags.h"
 #include "shared/httpd.h"
 #include "shared/memcpy_interleaved.h"
+#include "shared/metrics.h"
 #include "pbo_frame_allocator.h"
 #include "shared/timebase.h"
 #include "va_display_with_cleanup.h"
@@ -177,12 +178,26 @@ MJPEGEncoder::MJPEGEncoder(HTTPD *httpd, const string &va_display)
                va_receiver_thread = thread(&MJPEGEncoder::va_receiver_thread_func, this);
        }
 
+       global_metrics.add("mjpeg_frames", {{ "status", "dropped" }, { "reason", "zero_size" }}, &metric_mjpeg_frames_zero_size_dropped);
+       global_metrics.add("mjpeg_frames", {{ "status", "dropped" }, { "reason", "interlaced" }}, &metric_mjpeg_frames_interlaced_dropped);
+       global_metrics.add("mjpeg_frames", {{ "status", "dropped" }, { "reason", "unsupported_pixel_format" }}, &metric_mjpeg_frames_unsupported_pixel_format_dropped);
+       global_metrics.add("mjpeg_frames", {{ "status", "dropped" }, { "reason", "oversized" }}, &metric_mjpeg_frames_oversized_dropped);
+       global_metrics.add("mjpeg_frames", {{ "status", "dropped" }, { "reason", "overrun" }}, &metric_mjpeg_overrun_dropped);
+       global_metrics.add("mjpeg_frames", {{ "status", "submitted" }}, &metric_mjpeg_overrun_submitted);
+
        running = true;
 }
 
 MJPEGEncoder::~MJPEGEncoder()
 {
        av_free(avctx->pb->buffer);
+
+       global_metrics.remove("mjpeg_frames", {{ "status", "dropped" }, { "reason", "zero_size" }});
+       global_metrics.remove("mjpeg_frames", {{ "status", "dropped" }, { "reason", "interlaced" }});
+       global_metrics.remove("mjpeg_frames", {{ "status", "dropped" }, { "reason", "unsupported_pixel_format" }});
+       global_metrics.remove("mjpeg_frames", {{ "status", "dropped" }, { "reason", "oversized" }});
+       global_metrics.remove("mjpeg_frames", {{ "status", "dropped" }, { "reason", "overrun" }});
+       global_metrics.remove("mjpeg_frames", {{ "status", "submitted" }});
 }
 
 void MJPEGEncoder::stop()
@@ -193,6 +208,7 @@ void MJPEGEncoder::stop()
        running = false;
        should_quit = true;
        any_frames_to_be_encoded.notify_all();
+       any_frames_encoding.notify_all();
        encoder_thread.join();
        if (va_dpy != nullptr) {
                va_receiver_thread.join();
@@ -247,27 +263,33 @@ void MJPEGEncoder::upload_frame(int64_t pts, unsigned card_index, RefCountedFram
 {
        PBOFrameAllocator::Userdata *userdata = (PBOFrameAllocator::Userdata *)frame->userdata;
        if (video_format.width == 0 || video_format.height == 0) {
+               ++metric_mjpeg_frames_zero_size_dropped;
                return;
        }
        if (video_format.interlaced) {
                fprintf(stderr, "Card %u: Ignoring JPEG encoding for interlaced frame\n", card_index);
+               ++metric_mjpeg_frames_interlaced_dropped;
                return;
        }
        if (userdata->pixel_format != PixelFormat_8BitYCbCr ||
            !frame->interleaved) {
                fprintf(stderr, "Card %u: Ignoring JPEG encoding for unsupported pixel format\n", card_index);
+               ++metric_mjpeg_frames_unsupported_pixel_format_dropped;
                return;
        }
        if (video_format.width > 4096 || video_format.height > 4096) {
                fprintf(stderr, "Card %u: Ignoring JPEG encoding for oversized frame\n", card_index);
+               ++metric_mjpeg_frames_oversized_dropped;
                return;
        }
 
        lock_guard<mutex> lock(mu);
-       if (frames_to_be_encoded.size() + frames_encoding.size() > 10) {
+       if (frames_to_be_encoded.size() + frames_encoding.size() > 50) {
                fprintf(stderr, "WARNING: MJPEG encoding doesn't keep up, discarding frame.\n");
+               ++metric_mjpeg_overrun_dropped;
                return;
        }
+       ++metric_mjpeg_overrun_submitted;
        frames_to_be_encoded.push(QueuedFrame{ pts, card_index, frame, video_format, y_offset, cbcr_offset });
        any_frames_to_be_encoded.notify_all();
 }
@@ -296,7 +318,7 @@ void MJPEGEncoder::encoder_thread_func()
                } else {
                        // Encode synchronously, in the same thread.
                        vector<uint8_t> jpeg = encode_jpeg_libjpeg(qf);
-                       write_mjpeg_packet(qf.pts, qf.card_index, jpeg);
+                       write_mjpeg_packet(qf.pts, qf.card_index, jpeg.data(), jpeg.size());
                }
        }
 
@@ -306,16 +328,17 @@ void MJPEGEncoder::encoder_thread_func()
        free(tmp_cr);
 }
 
-void MJPEGEncoder::write_mjpeg_packet(int64_t pts, unsigned card_index, const vector<uint8_t> &jpeg)
+void MJPEGEncoder::write_mjpeg_packet(int64_t pts, unsigned card_index, const uint8_t *jpeg, size_t jpeg_size)
 {
        AVPacket pkt;
        memset(&pkt, 0, sizeof(pkt));
        pkt.buf = nullptr;
-       pkt.data = const_cast<uint8_t *>(&jpeg[0]);
-       pkt.size = jpeg.size();
+       pkt.data = const_cast<uint8_t *>(jpeg);
+       pkt.size = jpeg_size;
        pkt.stream_index = card_index;
        pkt.flags = AV_PKT_FLAG_KEY;
-       pkt.pts = pkt.dts = pts;
+       AVRational time_base = avctx->streams[pkt.stream_index]->time_base;
+       pkt.pts = pkt.dts = av_rescale_q(pts, AVRational{ 1, TIMEBASE }, time_base);
 
        if (av_write_frame(avctx.get(), &pkt) < 0) {
                fprintf(stderr, "av_write_frame() failed\n");
@@ -685,13 +708,11 @@ void MJPEGEncoder::va_receiver_thread_func()
                va_status = vaMapBuffer(va_dpy->va_dpy, qf.resources.data_buffer, (void **)&segment);
                CHECK_VASTATUS(va_status, "vaMapBuffer");
 
-               const char *coded_buf = reinterpret_cast<char *>(segment->buf);
-               vector<uint8_t> jpeg(coded_buf, coded_buf + segment->size);
+               const uint8_t *coded_buf = reinterpret_cast<uint8_t *>(segment->buf);
+               write_mjpeg_packet(qf.pts, qf.card_index, coded_buf, segment->size);
 
                va_status = vaUnmapBuffer(va_dpy->va_dpy, qf.resources.data_buffer);
                CHECK_VASTATUS(va_status, "vaUnmapBuffer");
-
-               write_mjpeg_packet(qf.pts, qf.card_index, jpeg);
        }
 }