]> git.sesse.net Git - nageru/blobdiff - nageru/flags.cpp
Make number of cards flexible at runtime.
[nageru] / nageru / flags.cpp
index b025076c0639361fd74307a99af986582e089999..402e33d7eca7a0be7d05d6ae87a4025f78ad9d0f 100644 (file)
@@ -15,10 +15,12 @@ Flags global_flags;
 enum LongOption {
        OPTION_HELP = 1000,
        OPTION_FULLSCREEN,
+       OPTION_MAX_NUM_CARDS,
        OPTION_MULTICHANNEL,
        OPTION_MIDI_MAPPING,
        OPTION_DEFAULT_HDMI_INPUT,
        OPTION_FAKE_CARDS_AUDIO,
+       OPTION_V4L_OUTPUT,
        OPTION_HTTP_UNCOMPRESSED_VIDEO,
        OPTION_HTTP_X264_VIDEO,
        OPTION_RECORD_X264_VIDEO,
@@ -36,7 +38,10 @@ enum LongOption {
        OPTION_HTTP_AUDIO_CODEC,
        OPTION_HTTP_AUDIO_BITRATE,
        OPTION_HTTP_PORT,
+       OPTION_SRT_PORT,
+       OPTION_NO_SRT,
        OPTION_NO_TRANSCODE_AUDIO,
+       OPTION_DISABLE_AUDIO,
        OPTION_FLAT_AUDIO,
        OPTION_GAIN_STAGING,
        OPTION_DISABLE_LOCUT,
@@ -87,21 +92,21 @@ map<unsigned, unsigned> parse_mjpeg_export_cards(char *optarg)
                }
                if (range_end < range_begin) {
                        fprintf(stderr, "ERROR: Invalid range %u-%u in --mjpeg-export-cards=\n", range_begin, range_end);
-                       abort();
+                       exit(1);
                }
-               if (range_end >= unsigned(global_flags.num_cards)) {
+               if (range_end >= unsigned(global_flags.max_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);
-                       abort();
+                               range_end, global_flags.max_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);
-                               abort();
+                               exit(1);
                        }
                        ret[card_idx] = stream_idx++;
                }
@@ -129,7 +134,8 @@ void usage(Program program)
        fprintf(stderr, "  -w, --width                     output width in pixels (default 1280)\n");
        fprintf(stderr, "  -h, --height                    output height in pixels (default 720)\n");
        if (program == PROGRAM_NAGERU) {
-               fprintf(stderr, "  -c, --num-cards                 set number of input cards (default 2)\n");
+               fprintf(stderr, "  -c, --num-cards                 set minimum number of input cards (default 2)\n");
+               fprintf(stderr, "      --max-num-cards             set maximum number of input cards (default %d)\n", MAX_VIDEO_CARDS);
                fprintf(stderr, "  -o, --output-card=CARD          also output signal to the given card (default none)\n");
                fprintf(stderr, "  -t, --theme=FILE                choose theme (default theme.lua)\n");
                fprintf(stderr, "  -I, --theme-dir=DIR             search for theme in this directory (can be given multiple times)\n");
@@ -142,6 +148,7 @@ void usage(Program program)
                fprintf(stderr, "      --midi-mapping=FILE         start with the given MIDI controller mapping (implies --multichannel)\n");
                fprintf(stderr, "      --default-hdmi-input        default to HDMI over SDI inputs for cards that have both\n");
                fprintf(stderr, "      --fake-cards-audio          make fake (disconnected) cards output a simple tone\n");
+               fprintf(stderr, "      --v4l-output DEVICE         send video (no audio) to V4L2 output/loopback\n");
                fprintf(stderr, "      --http-uncompressed-video   send uncompressed NV12 video to HTTP clients\n");
                fprintf(stderr, "      --http-x264-video           send x264-compressed video to HTTP clients\n");
                fprintf(stderr, "      --record-x264-video         store x264-compressed video to disk (implies --http-x264-video,\n");
@@ -167,9 +174,13 @@ void usage(Program program)
                DEFAULT_AUDIO_OUTPUT_BIT_RATE / 1000);
        fprintf(stderr, "      --http-port=PORT            which port to use for the built-in HTTP server\n");
        fprintf(stderr, "                                  (default is %d)\n", DEFAULT_HTTPD_PORT);
+       fprintf(stderr, "      --srt-port=PORT             which port to use for receiving SRT streams\n");
+       fprintf(stderr, "                                  (default is %d)\n", DEFAULT_SRT_PORT);
+       fprintf(stderr, "      --no-srt                    disable receiving SRT streams\n");
        if (program == PROGRAM_KAERU) {
                fprintf(stderr, "      --no-transcode-audio        copy encoded audio raw from the source stream\n");
                fprintf(stderr, "                                    (requires --http-audio-codec= to be set)\n");
+               fprintf(stderr, "      --disable-audio             do not include any audio in the stream\n");
        }
        if (program == PROGRAM_NAGERU) {
                fprintf(stderr, "      --flat-audio                start with most audio processing turned off\n");
@@ -224,6 +235,7 @@ void parse_flags(Program program, int argc, char * const argv[])
                { "width", required_argument, 0, 'w' },
                { "height", required_argument, 0, 'h' },
                { "num-cards", required_argument, 0, 'c' },
+               { "max-num-cards", required_argument, 0, OPTION_MAX_NUM_CARDS },
                { "output-card", required_argument, 0, 'o' },
                { "theme", required_argument, 0, 't' },
                { "theme-dir", required_argument, 0, 'I' },
@@ -235,6 +247,7 @@ void parse_flags(Program program, int argc, char * const argv[])
                { "midi-mapping", required_argument, 0, OPTION_MIDI_MAPPING },
                { "default-hdmi-input", no_argument, 0, OPTION_DEFAULT_HDMI_INPUT },
                { "fake-cards-audio", no_argument, 0, OPTION_FAKE_CARDS_AUDIO },
+               { "v4l-output", required_argument, 0, OPTION_V4L_OUTPUT },
                { "http-uncompressed-video", no_argument, 0, OPTION_HTTP_UNCOMPRESSED_VIDEO },
                { "http-x264-video", no_argument, 0, OPTION_HTTP_X264_VIDEO },
                { "record-x264-video", no_argument, 0, OPTION_RECORD_X264_VIDEO },
@@ -251,7 +264,10 @@ void parse_flags(Program program, int argc, char * const argv[])
                { "http-audio-codec", required_argument, 0, OPTION_HTTP_AUDIO_CODEC },
                { "http-audio-bitrate", required_argument, 0, OPTION_HTTP_AUDIO_BITRATE },
                { "http-port", required_argument, 0, OPTION_HTTP_PORT },
+               { "srt-port", required_argument, 0, OPTION_SRT_PORT },
+               { "no-srt", no_argument, 0, OPTION_NO_SRT },
                { "no-transcode-audio", no_argument, 0, OPTION_NO_TRANSCODE_AUDIO },
+               { "disable-audio", no_argument, 0, OPTION_DISABLE_AUDIO },
                { "flat-audio", no_argument, 0, OPTION_FLAT_AUDIO },
                { "gain-staging", required_argument, 0, OPTION_GAIN_STAGING },
                { "disable-locut", no_argument, 0, OPTION_DISABLE_LOCUT },
@@ -286,7 +302,7 @@ void parse_flags(Program program, int argc, char * const argv[])
        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);
+               int c = getopt_long(argc, argv, "c:o:t:I:r:v:m:M:w:h:", long_options, &option_index);
 
                if (c == -1) {
                        break;
@@ -299,7 +315,10 @@ void parse_flags(Program program, int argc, char * const argv[])
                        global_flags.height = atoi(optarg);
                        break;
                case 'c':
-                       global_flags.num_cards = atoi(optarg);
+                       global_flags.min_num_cards = atoi(optarg);
+                       break;
+               case OPTION_MAX_NUM_CARDS:
+                       global_flags.max_num_cards = atoi(optarg);
                        break;
                case 'o':
                        global_flags.output_card = atoi(optarg);
@@ -317,7 +336,7 @@ void parse_flags(Program program, int argc, char * const argv[])
                        char *ptr = strchr(optarg, ',');
                        if (ptr == nullptr) {
                                fprintf(stderr, "ERROR: Invalid argument '%s' to --map-signal (needs a signal and a card number, separated by comma)\n", optarg);
-                               abort();
+                               exit(1);
                        }
                        *ptr = '\0';
                        const int signal_num = atoi(optarg);
@@ -325,7 +344,7 @@ void parse_flags(Program program, int argc, char * const argv[])
                        if (global_flags.default_stream_mapping.count(signal_num)) {
                                fprintf(stderr, "ERROR: Signal %d already mapped to card %d\n",
                                        signal_num, global_flags.default_stream_mapping[signal_num]);
-                               abort();
+                               exit(1);
                        }
                        global_flags.default_stream_mapping[signal_num] = card_num;
                        break;
@@ -349,6 +368,9 @@ void parse_flags(Program program, int argc, char * const argv[])
                case OPTION_FAKE_CARDS_AUDIO:
                        global_flags.fake_cards_audio = true;
                        break;
+               case OPTION_V4L_OUTPUT:
+                       global_flags.v4l_output_device = optarg;
+                       break;
                case OPTION_HTTP_UNCOMPRESSED_VIDEO:
                        global_flags.uncompressed_video_to_http = true;
                        break;
@@ -364,9 +386,19 @@ void parse_flags(Program program, int argc, char * const argv[])
                case OPTION_HTTP_PORT:
                        global_flags.http_port = atoi(optarg);
                        break;
+               case OPTION_SRT_PORT:
+                       global_flags.srt_port = atoi(optarg);
+                       break;
+               case OPTION_NO_SRT:
+                       global_flags.srt_port = -1;
+                       break;
                case OPTION_NO_TRANSCODE_AUDIO:
                        global_flags.transcode_audio = false;
                        break;
+               case OPTION_DISABLE_AUDIO:
+                       global_flags.transcode_audio = false;
+                       global_flags.enable_audio = false;
+                       break;
                case OPTION_HTTP_X264_VIDEO:
                        global_flags.x264_video_to_http = true;
                        break;
@@ -489,13 +521,13 @@ void parse_flags(Program program, int argc, char * const argv[])
                        char *ptr = strchr(optarg, ',');
                        if (ptr == nullptr) {
                                fprintf(stderr, "ERROR: Invalid argument '%s' to --input-ycbcr-interpretation (needs a card and an interpretation, separated by comma)\n", optarg);
-                               abort();
+                               exit(1);
                        }
                        *ptr = '\0';
                        const int card_num = atoi(optarg);
                        if (card_num < 0 || card_num >= MAX_VIDEO_CARDS) {
                                fprintf(stderr, "ERROR: Invalid card number %d\n", card_num);
-                               abort();
+                               exit(1);
                        }
 
                        YCbCrInterpretation interpretation;
@@ -510,7 +542,7 @@ void parse_flags(Program program, int argc, char * const argv[])
                                        interpretation.full_range = false;
                                } else {
                                        fprintf(stderr, "ERROR: Invalid Y'CbCr range '%s' (must be “full” or “limited”)\n", range);
-                                       abort();
+                                       exit(1);
                                }
                        }
 
@@ -524,11 +556,11 @@ void parse_flags(Program program, int argc, char * const argv[])
                                interpretation.ycbcr_coefficients_auto = true;
                                if (interpretation.full_range) {
                                        fprintf(stderr, "ERROR: Cannot use “auto” Y'CbCr coefficients with full range\n");
-                                       abort();
+                                       exit(1);
                                }
                        } else {
                                fprintf(stderr, "ERROR: Invalid Y'CbCr coefficients '%s' (must be “rec601”, “rec709” or “auto”)\n", interpretation_str);
-                               abort();
+                               exit(1);
                        }
                        global_flags.ycbcr_interpretation[card_num] = interpretation;
                        break;
@@ -539,7 +571,7 @@ void parse_flags(Program program, int argc, char * const argv[])
                case OPTION_MJPEG_EXPORT_CARDS: {
                        if (card_to_mjpeg_stream_export_set) {
                                fprintf(stderr, "ERROR: --mjpeg-export-cards given twice\n");
-                               abort();
+                               exit(1);
                        }
                        global_flags.card_to_mjpeg_stream_export = parse_mjpeg_export_cards(optarg);    
                        card_to_mjpeg_stream_export_set = true;
@@ -552,28 +584,36 @@ void parse_flags(Program program, int argc, char * const argv[])
                        fprintf(stderr, "Unknown option '%s'\n", argv[option_index]);
                        fprintf(stderr, "\n");
                        usage(program);
-                       abort();
+                       exit(1);
                }
        }
 
        if (global_flags.uncompressed_video_to_http &&
            global_flags.x264_video_to_http) {
                fprintf(stderr, "ERROR: --http-uncompressed-video and --http-x264-video are mutually incompatible\n");
-               abort();
+               exit(1);
        }
-       if (global_flags.num_cards <= 0) {
+       if (global_flags.min_num_cards <= 0) {
                fprintf(stderr, "ERROR: --num-cards must be at least 1\n");
-               abort();
+               exit(1);
+       }
+       if (global_flags.max_num_cards <= 0) {
+               fprintf(stderr, "ERROR: --max-num-cards must be at least 1\n");
+               exit(1);
+       }
+       if (global_flags.max_num_cards < global_flags.min_num_cards) {
+               fprintf(stderr, "ERROR: --max-num-cards can not be lower than --num-cards\n");
+               exit(1);
        }
        if (global_flags.output_card < -1 ||
-           global_flags.output_card >= global_flags.num_cards) {
+           global_flags.output_card >= global_flags.max_num_cards) {
                fprintf(stderr, "ERROR: --output-card points to a nonexistant card\n");
-               abort();
+               exit(1);
        }
-       if (!global_flags.transcode_audio && global_flags.stream_audio_codec_name.empty()) {
+       if (global_flags.enable_audio && !global_flags.transcode_audio && global_flags.stream_audio_codec_name.empty()) {
                fprintf(stderr, "ERROR: If not transcoding audio, you must specify ahead-of-time what audio codec is in use\n");
                fprintf(stderr, "       (using --http-audio-codec).\n");
-               abort();
+               exit(1);
        }
        if (global_flags.x264_speedcontrol) {
                if (!global_flags.x264_preset.empty() && global_flags.x264_preset != "faster") {
@@ -594,14 +634,14 @@ void parse_flags(Program program, int argc, char * const argv[])
        if (global_flags.width <= 0 || (global_flags.width % 8) != 0 ||
            global_flags.height <= 0 || (global_flags.height % 8) != 0) {
                fprintf(stderr, "ERROR: --width and --height must be positive integers divisible by 8\n");
-               abort();
+               exit(1);
        }
 
        for (pair<int, int> mapping : global_flags.default_stream_mapping) {
-               if (mapping.second >= global_flags.num_cards) {
-                       fprintf(stderr, "ERROR: Signal %d mapped to card %d, which doesn't exist (try adjusting --num-cards)\n",
+               if (mapping.second >= global_flags.max_num_cards) {
+                       fprintf(stderr, "ERROR: Signal %d mapped to card %d, which doesn't exist (try adjusting --max-num-cards)\n",
                                mapping.first, mapping.second);
-                       abort();
+                       exit(1);
                }
        }
 
@@ -633,22 +673,22 @@ void parse_flags(Program program, int argc, char * const argv[])
                global_flags.ycbcr_auto_coefficients = false;
        } else {
                fprintf(stderr, "ERROR: --output-ycbcr-coefficients must be “rec601”, “rec709” or “auto”\n");
-               abort();
+               exit(1);
        }
 
        if (global_flags.output_buffer_frames < 0.0f) {
                // Actually, even zero probably won't make sense; there is some internal
                // delay to the card.
                fprintf(stderr, "ERROR: --output-buffer-frames can't be negative.\n");
-               abort();
+               exit(1);
        }
        if (global_flags.output_slop_frames < 0.0f) {
                fprintf(stderr, "ERROR: --output-slop-frames can't be negative.\n");
-               abort();
+               exit(1);
        }
        if (global_flags.max_input_queue_frames < 1) {
                fprintf(stderr, "ERROR: --max-input-queue-frames must be at least 1.\n");
-               abort();
+               exit(1);
        }
        if (global_flags.max_input_queue_frames > 10) {
                fprintf(stderr, "WARNING: --max-input-queue-frames has little effect over 10.\n");
@@ -657,7 +697,7 @@ void parse_flags(Program program, int argc, char * const argv[])
        if (!isinf(global_flags.x264_crf)) {  // CRF mode is selected.
                if (global_flags.x264_bitrate != -1) {
                        fprintf(stderr, "ERROR: --x264-bitrate and --x264-crf are mutually incompatible.\n");
-                       abort();
+                       exit(1);
                }
                if (global_flags.x264_vbv_max_bitrate != -1 && global_flags.x264_vbv_buffer_size != -1) {
                        fprintf(stderr, "WARNING: VBV settings are ignored with --x264-crf.\n");
@@ -668,7 +708,7 @@ void parse_flags(Program program, int argc, char * const argv[])
 
        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) {
+               for (unsigned card_idx = 0; card_idx < unsigned(global_flags.max_num_cards); ++card_idx) {
                        global_flags.card_to_mjpeg_stream_export[card_idx] = card_idx;
                }
        }