]> git.sesse.net Git - nageru/commitdiff
Add an option to control the mapping of streams to export to MJPEG (or turn it off... mjpeg
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 12 Nov 2018 00:02:42 +0000 (01:02 +0100)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 12 Nov 2018 00:02:42 +0000 (01:02 +0100)
flags.cpp
flags.h
mixer.cpp
mjpeg_encoder.cpp

index 9b3a9dac7f302de31f315154f85f0236b301545b..bdb9821d5aafed89dbe453be4865600f888beb68 100644 (file)
--- a/flags.cpp
+++ b/flags.cpp
@@ -63,8 +63,57 @@ enum LongOption {
        OPTION_10_BIT_INPUT,
        OPTION_10_BIT_OUTPUT,
        OPTION_INPUT_YCBCR_INTERPRETATION,
+       OPTION_MJPEG_EXPORT_CARDS,
 };
 
+map<unsigned, unsigned> parse_mjpeg_export_cards(char *optarg)
+{
+       map<unsigned, unsigned> ret;
+       if (optarg[0] == '\0') {
+               return ret;
+       }
+
+       unsigned stream_idx = 0;
+       char *start = optarg;
+       for ( ;; ) {
+               char *end = strchr(start, ',');
+               if (end != nullptr) {
+                       *end = '\0';
+               }
+
+               unsigned range_begin, range_end;
+               if (sscanf(start, "%u-%u", &range_begin, &range_end) != 2) {
+                       range_begin = range_end = atoi(start);
+               }
+               if (range_end < range_begin) {
+                       fprintf(stderr, "ERROR: Invalid range %u-%u in --mjpeg-export-cards=\n", range_begin, range_end);
+                       exit(1);
+               }
+               if (range_end >= unsigned(global_flags.num_cards)) {
+                       // There are situations where we could possibly want to
+                       // include FFmpeg inputs (CEF inputs are unlikely),
+                       // but they're not necessarily in 4:2:2 Y'CbCr, so it would
+                       // require more functionality the the JPEG encoder.
+                       fprintf(stderr, "ERROR: Asked for (zero-indexed) card %u in --mjpeg-export-cards=, but there are only %u cards\n",
+                               range_end, global_flags.num_cards);
+                       exit(1);
+               }
+               for (unsigned card_idx = range_begin; card_idx <= range_end; ++card_idx) {
+                       if (ret.count(card_idx)) {
+                               fprintf(stderr, "ERROR: Card %u was given twice in --mjpeg-export-cards=\n", card_idx);
+                               exit(1);
+                       }
+                       ret[card_idx] = stream_idx++;
+               }
+               if (end == nullptr) {
+                       break;
+               } else {
+                       start = end + 1;
+               }
+       }
+       return ret;
+}
+
 void usage(Program program)
 {
        if (program == PROGRAM_KAERU) {
@@ -160,6 +209,10 @@ void usage(Program program)
                fprintf(stderr, "                                  Y'CbCr coefficient standard of card CARD (default auto)\n");
                fprintf(stderr, "                                    auto is rec601 for SD, rec709 for HD, always limited\n");
                fprintf(stderr, "                                    limited means standard 0-240/0-235 input range (for 8-bit)\n");
+               fprintf(stderr, "      --mjpeg-export-cards=RANGE[,RANGE...]\n");
+               fprintf(stderr, "                                  export the given cards in MJPEG format to /multicam.mp4,\n");
+               fprintf(stderr, "                                    in the given order (ranges can be either single card indexes\n");
+               fprintf(stderr, "                                    or pairs like 1-3 for camera 1,2,3; default is all cards)\n");
        }
 }
 
@@ -225,10 +278,12 @@ void parse_flags(Program program, int argc, char * const argv[])
                { "10-bit-input", no_argument, 0, OPTION_10_BIT_INPUT },
                { "10-bit-output", no_argument, 0, OPTION_10_BIT_OUTPUT },
                { "input-ycbcr-interpretation", required_argument, 0, OPTION_INPUT_YCBCR_INTERPRETATION },
+               { "mjpeg-export-cards", required_argument, 0, OPTION_MJPEG_EXPORT_CARDS },
                { 0, 0, 0, 0 }
        };
        vector<string> theme_dirs;
        string output_ycbcr_coefficients = "auto";
+       bool card_to_mjpeg_stream_export_set = false;
        for ( ;; ) {
                int option_index = 0;
                int c = getopt_long(argc, argv, "c:t:I:r:v:m:M:w:h:", long_options, &option_index);
@@ -481,6 +536,15 @@ void parse_flags(Program program, int argc, char * const argv[])
                case OPTION_FULLSCREEN:
                        global_flags.fullscreen = true;
                        break;
+               case OPTION_MJPEG_EXPORT_CARDS: {
+                       if (card_to_mjpeg_stream_export_set) {
+                               fprintf(stderr, "ERROR: --mjpeg-export-cards given twice\n");
+                               exit(1);
+                       }
+                       global_flags.card_to_mjpeg_stream_export = parse_mjpeg_export_cards(optarg);    
+                       card_to_mjpeg_stream_export_set = true;
+                       break;
+               }
                case OPTION_HELP:
                        usage(program);
                        exit(0);
@@ -601,4 +665,11 @@ void parse_flags(Program program, int argc, char * const argv[])
        } else if (global_flags.x264_bitrate == -1) {
                global_flags.x264_bitrate = DEFAULT_X264_OUTPUT_BIT_RATE;
        }
+
+       if (!card_to_mjpeg_stream_export_set) {
+               // Fill in the default mapping (export all cards, in order).
+               for (unsigned card_idx = 0; card_idx < unsigned(global_flags.num_cards); ++card_idx) {
+                       global_flags.card_to_mjpeg_stream_export[card_idx] = card_idx;
+               }
+       }
 }
diff --git a/flags.h b/flags.h
index 09337d13b9e12513d307e395dd9cd1bf8c4f42ae..4d990f8511d7a2fb7567fb2195a0e31c5c2a2e16 100644 (file)
--- a/flags.h
+++ b/flags.h
@@ -67,6 +67,7 @@ struct Flags {
        bool use_zerocopy = false;  // Not user-settable.
        bool can_disable_srgb_decoder = false;  // Not user-settable.
        bool fullscreen = false;
+       std::map<unsigned, unsigned> card_to_mjpeg_stream_export;  // If a card is not in the map, it is not exported.
 };
 extern Flags global_flags;
 
index 95cdda98ecd5a42e97cb12cfdb4fb796e771a0e0..2f156fa8a1f11ee9b8f22ade874a3ddfd114acaa 100644 (file)
--- a/mixer.cpp
+++ b/mixer.cpp
@@ -358,7 +358,9 @@ Mixer::Mixer(const QSurfaceFormat &format, unsigned num_cards)
        display_chain->finalize();
 
        video_encoder.reset(new VideoEncoder(resource_pool.get(), h264_encoder_surface, global_flags.va_display, global_flags.width, global_flags.height, &httpd, global_disk_space_estimator));
-       mjpeg_encoder.reset(new MJPEGEncoder(&httpd, global_flags.va_display));
+       if (!global_flags.card_to_mjpeg_stream_export.empty()) {
+               mjpeg_encoder.reset(new MJPEGEncoder(&httpd, global_flags.va_display));
+       }
 
        // Must be instantiated after VideoEncoder has initialized global_flags.use_zerocopy.
        theme.reset(new Theme(global_flags.theme_filename, global_flags.theme_dirs, resource_pool.get(), num_cards));
@@ -502,7 +504,9 @@ Mixer::Mixer(const QSurfaceFormat &format, unsigned num_cards)
 
 Mixer::~Mixer()
 {
-       mjpeg_encoder->stop();
+       if (mjpeg_encoder != nullptr) {
+               mjpeg_encoder->stop();
+       }
        httpd.stop();
        BMUSBCapture::stop_bm_thread();
 
@@ -1074,12 +1078,9 @@ void Mixer::thread_func()
                        // Only bother doing MJPEG encoding if there are any connected clients
                        // that want the stream.
                        if (httpd.get_num_connected_multicam_clients() > 0) {
-                               // There are situations where we could possibly want to
-                               // include FFmpeg inputs (CEF inputs are unlikely),
-                               // but they're not necessarily in 4:2:2 Y'CbCr, so it would
-                               // require more functionality the the JPEG encoder.
-                               if (card_index < num_cards) {
-                                       mjpeg_encoder->upload_frame(pts_int, card_index, new_frame->frame, new_frame->video_format, new_frame->y_offset, new_frame->cbcr_offset);
+                               auto stream_it = global_flags.card_to_mjpeg_stream_export.find(card_index);
+                               if (stream_it != global_flags.card_to_mjpeg_stream_export.end()) {
+                                       mjpeg_encoder->upload_frame(pts_int, stream_it->second, new_frame->frame, new_frame->video_format, new_frame->y_offset, new_frame->cbcr_offset);
                                }
                        }
                }
index 6c0d17043074e5042e83f2c2d73f6760e8af1d9b..fd48d70eb64454a4b9e40524fd0fe464ffc5ec51 100644 (file)
@@ -130,7 +130,7 @@ MJPEGEncoder::MJPEGEncoder(HTTPD *httpd, const string &va_display)
        avctx->pb->write_data_type = &MJPEGEncoder::write_packet2_thunk;
        avctx->flags = AVFMT_FLAG_CUSTOM_IO;
 
-       for (int card_idx = 0; card_idx < global_flags.num_cards; ++card_idx) {
+       for (unsigned card_idx = 0; card_idx < global_flags.card_to_mjpeg_stream_export.size(); ++card_idx) {
                AVStream *stream = avformat_new_stream(avctx.get(), nullptr);
                if (stream == nullptr) {
                        fprintf(stderr, "avformat_new_stream() failed\n");