]> git.sesse.net Git - nageru/commitdiff
Make it possible to set input Y'CbCr interpretation on the command line.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 15 May 2017 21:33:24 +0000 (23:33 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 15 May 2017 21:33:24 +0000 (23:33 +0200)
flags.cpp
flags.h
glwidget.cpp
mixer.cpp
mixer.h
ycbcr_interpretation.h [new file with mode: 0644]

index f7a29f7330eb34f218d1146c97ddf438b7441352..fc1bc886dfba392a214caec0838ff6a73e4988bb 100644 (file)
--- a/flags.cpp
+++ b/flags.cpp
@@ -57,6 +57,7 @@ enum LongOption {
        OPTION_TIMECODE_STDOUT,
        OPTION_10_BIT_INPUT,
        OPTION_10_BIT_OUTPUT,
+       OPTION_INPUT_YCBCR_INTERPRETATION,
 };
 
 void usage()
@@ -134,6 +135,10 @@ void usage()
        fprintf(stderr, "      --10-bit-input              use 10-bit video input (requires compute shaders)\n");
        fprintf(stderr, "      --10-bit-output             use 10-bit video output (requires compute shaders,\n");
        fprintf(stderr, "                                    implies --record-x264-video)\n");
+       fprintf(stderr, "      --input-ycbcr-interpretation=CARD,{rec601,rec709,auto}[,{limited,full}]\n");
+       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");
 }
 
 void parse_flags(int argc, char * const argv[])
@@ -193,6 +198,7 @@ void parse_flags(int argc, char * const argv[])
                { "timecode-stdout", no_argument, 0, OPTION_TIMECODE_STDOUT },
                { "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 },
                { 0, 0, 0, 0 }
        };
        vector<string> theme_dirs;
@@ -389,6 +395,54 @@ void parse_flags(int argc, char * const argv[])
                        global_flags.x264_video_to_http = true;
                        global_flags.x264_bit_depth = 10;
                        break;
+               case OPTION_INPUT_YCBCR_INTERPRETATION: {
+                       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);
+                               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);
+                               exit(1);
+                       }
+
+                       YCbCrInterpretation interpretation;
+                       char *interpretation_str = ptr + 1;
+                       ptr = strchr(interpretation_str, ',');
+                       if (ptr != nullptr) {
+                               *ptr = '\0';
+                               const char *range = ptr + 1;
+                               if (strcmp(range, "full") == 0) {
+                                       interpretation.full_range = true;
+                               } else if (strcmp(range, "limited") == 0) {
+                                       interpretation.full_range = false;
+                               } else {
+                                       fprintf(stderr, "ERROR: Invalid Y'CbCr range '%s' (must be “full” or “limited”)\n", range);
+                                       exit(1);
+                               }
+                       }
+
+                       if (strcmp(interpretation_str, "rec601") == 0) {
+                               interpretation.ycbcr_coefficients_auto = false;
+                               interpretation.ycbcr_coefficients = movit::YCBCR_REC_601;
+                       } else if (strcmp(interpretation_str, "rec709") == 0) {
+                               interpretation.ycbcr_coefficients_auto = false;
+                               interpretation.ycbcr_coefficients = movit::YCBCR_REC_709;
+                       } else if (strcmp(interpretation_str, "auto") == 0) {
+                               interpretation.ycbcr_coefficients_auto = true;
+                               if (interpretation.full_range) {
+                                       fprintf(stderr, "ERROR: Cannot use “auto” Y'CbCr coefficients with full range\n");
+                                       exit(1);
+                               }
+                       } else {
+                               fprintf(stderr, "ERROR: Invalid Y'CbCr coefficients '%s' (must be “rec601”, “rec709” or “auto”)\n", interpretation_str);
+                               exit(1);
+                       }
+                       global_flags.ycbcr_interpretation[card_num] = interpretation;
+                       break;
+               }
                case OPTION_HELP:
                        usage();
                        exit(0);
diff --git a/flags.h b/flags.h
index 66912defd00423dbb7289c21e928ea7781aad2bc..f26d4dbab79b0fb82905de4c61504677d965b467 100644 (file)
--- a/flags.h
+++ b/flags.h
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "defs.h"
+#include "ycbcr_interpretation.h"
 
 struct Flags {
        int width = 1280, height = 720;
@@ -57,6 +58,7 @@ struct Flags {
        bool display_timecode_on_stdout = false;
        bool ten_bit_input = false;
        bool ten_bit_output = false;  // Implies x264_video_to_disk == true and x264_bit_depth == 10.
+       YCbCrInterpretation ycbcr_interpretation[MAX_VIDEO_CARDS];
        int x264_bit_depth = 8;  // Not user-settable.
        bool use_zerocopy = false;  // Not user-settable.
        bool can_disable_srgb_decoder = false;  // Not user-settable.
index 73d8b7fd3bcb72a19e6cd10313f3482eb6a5f85e..31018d6b95d5436cddc840ab01738b1b30ded18b 100644 (file)
@@ -193,15 +193,11 @@ void GLWidget::show_preview_context_menu(unsigned signal_num, const QPoint &pos)
        QMenu interpretation_submenu;
        QActionGroup interpretation_group(&interpretation_submenu);
 
-       bool current_ycbcr_coefficients_auto, current_full_range;
-       YCbCrLumaCoefficients current_ycbcr_coefficients;
-       global_mixer->get_input_ycbcr_interpretation(
-               current_card, &current_ycbcr_coefficients_auto,
-               &current_ycbcr_coefficients, &current_full_range);
+       YCbCrInterpretation current_interpretation = global_mixer->get_input_ycbcr_interpretation(current_card);
        {
                QAction *action = new QAction("Auto", &interpretation_group);
                action->setCheckable(true);
-               if (current_ycbcr_coefficients_auto) {
+               if (current_interpretation.ycbcr_coefficients_auto) {
                        action->setChecked(true);
                }
                action->setData(QList<QVariant>{"interpretation", true, YCBCR_REC_709, false});
@@ -220,9 +216,9 @@ void GLWidget::show_preview_context_menu(unsigned signal_num, const QPoint &pos)
                        }
                        QAction *action = new QAction(QString::fromStdString(description), &interpretation_group);
                        action->setCheckable(true);
-                       if (!current_ycbcr_coefficients_auto &&
-                           ycbcr_coefficients == current_ycbcr_coefficients &&
-                           full_range == current_full_range) {
+                       if (!current_interpretation.ycbcr_coefficients_auto &&
+                           ycbcr_coefficients == current_interpretation.ycbcr_coefficients &&
+                           full_range == current_interpretation.full_range) {
                                action->setChecked(true);
                        }
                        action->setData(QList<QVariant>{"interpretation", false, ycbcr_coefficients, full_range});
@@ -357,10 +353,11 @@ void GLWidget::show_preview_context_menu(unsigned signal_num, const QPoint &pos)
                        unsigned card_index = selected[1].toUInt(nullptr);
                        global_mixer->set_signal_mapping(signal_num, card_index);
                } else if (selected[0].toString() == "interpretation") {
-                       bool ycbcr_coefficients_auto = selected[1].toBool();
-                       YCbCrLumaCoefficients ycbcr_coefficients = YCbCrLumaCoefficients(selected[2].toUInt(nullptr));
-                       bool full_range = selected[3].toBool();
-                       global_mixer->set_input_ycbcr_interpretation(current_card, ycbcr_coefficients_auto, ycbcr_coefficients, full_range);
+                       YCbCrInterpretation interpretation;
+                       interpretation.ycbcr_coefficients_auto = selected[1].toBool();
+                       interpretation.ycbcr_coefficients = YCbCrLumaCoefficients(selected[2].toUInt(nullptr));
+                       interpretation.full_range = selected[3].toBool();
+                       global_mixer->set_input_ycbcr_interpretation(current_card, interpretation);
                } else {
                        assert(false);
                }
index 6439e58cfaf845614357314c7f7f82fdfcd9b206..1a76ac39c5c4226bab61b22b7af0f0ae0f841c18 100644 (file)
--- a/mixer.cpp
+++ b/mixer.cpp
@@ -206,6 +206,7 @@ Mixer::Mixer(const QSurfaceFormat &format, unsigned num_cards)
          mixer_surface(create_surface(format)),
          h264_encoder_surface(create_surface(format)),
          decklink_output_surface(create_surface(format)),
+         ycbcr_interpretation(global_flags.ycbcr_interpretation),
          audio_mixer(num_cards)
 {
        CHECK(init_movit(MOVIT_SHADER_DIR, MOVIT_DEBUG_OFF));
@@ -1085,10 +1086,10 @@ void Mixer::render_one_frame(int64_t duration)
        {
                unique_lock<mutex> lock(card_mutex);
                for (unsigned card_index = 0; card_index < num_cards; ++card_index) {
-                       CaptureCard *card = &cards[card_index];
-                       input_state.ycbcr_coefficients_auto[card_index] = card->ycbcr_coefficients_auto;
-                       input_state.ycbcr_coefficients[card_index] = card->ycbcr_coefficients;
-                       input_state.full_range[card_index] = card->full_range;
+                       YCbCrInterpretation *interpretation = &ycbcr_interpretation[card_index];
+                       input_state.ycbcr_coefficients_auto[card_index] = interpretation->ycbcr_coefficients_auto;
+                       input_state.ycbcr_coefficients[card_index] = interpretation->ycbcr_coefficients;
+                       input_state.full_range[card_index] = interpretation->full_range;
                }
        }
 
@@ -1293,24 +1294,16 @@ void Mixer::channel_clicked(int preview_num)
        theme->channel_clicked(preview_num);
 }
 
-void Mixer::get_input_ycbcr_interpretation(unsigned card_index, bool *ycbcr_coefficients_auto,
-                                           movit::YCbCrLumaCoefficients *ycbcr_coefficients, bool *full_range)
+YCbCrInterpretation Mixer::get_input_ycbcr_interpretation(unsigned card_index) const
 {
        unique_lock<mutex> lock(card_mutex);
-       CaptureCard *card = &cards[card_index];
-       *ycbcr_coefficients_auto = card->ycbcr_coefficients_auto;
-       *ycbcr_coefficients = card->ycbcr_coefficients;
-       *full_range = card->full_range;
+       return ycbcr_interpretation[card_index];
 }
 
-void Mixer::set_input_ycbcr_interpretation(unsigned card_index, bool ycbcr_coefficients_auto,
-                                           movit::YCbCrLumaCoefficients ycbcr_coefficients, bool full_range)
+void Mixer::set_input_ycbcr_interpretation(unsigned card_index, const YCbCrInterpretation &interpretation)
 {
        unique_lock<mutex> lock(card_mutex);
-       CaptureCard *card = &cards[card_index];
-       card->ycbcr_coefficients_auto = ycbcr_coefficients_auto;
-       card->ycbcr_coefficients = ycbcr_coefficients;
-       card->full_range = full_range;
+       ycbcr_interpretation[card_index] = interpretation;
 }
 
 void Mixer::start_mode_scanning(unsigned card_index)
diff --git a/mixer.h b/mixer.h
index 84ef32db0efac732975c1a5e7d86e94c0db13d4e..c63db6406da5f9ecaedaacee3f8e57e9e3da8291 100644 (file)
--- a/mixer.h
+++ b/mixer.h
@@ -37,6 +37,7 @@
 #include "theme.h"
 #include "timebase.h"
 #include "video_encoder.h"
+#include "ycbcr_interpretation.h"
 
 class ALSAOutput;
 class ChromaSubsampler;
@@ -218,10 +219,8 @@ public:
                return theme->set_signal_mapping(signal, card);
        }
 
-       void get_input_ycbcr_interpretation(unsigned card_index, bool *ycbcr_coefficients_auto,
-                                           movit::YCbCrLumaCoefficients *ycbcr_coefficients, bool *full_range);
-       void set_input_ycbcr_interpretation(unsigned card_index, bool ycbcr_coefficients_auto,
-                                           movit::YCbCrLumaCoefficients ycbcr_coefficients, bool full_range);
+       YCbCrInterpretation get_input_ycbcr_interpretation(unsigned card_index) const;
+       void set_input_ycbcr_interpretation(unsigned card_index, const YCbCrInterpretation &interpretation);
 
        bool get_supports_set_wb(unsigned channel) const
        {
@@ -446,12 +445,9 @@ private:
                QueueLengthPolicy queue_length_policy;  // Refers to the "new_frames" queue.
 
                int last_timecode = -1;  // Unwrapped.
-
-               bool ycbcr_coefficients_auto = true;
-               movit::YCbCrLumaCoefficients ycbcr_coefficients = movit::YCBCR_REC_709;
-               bool full_range = false;
        };
        CaptureCard cards[MAX_VIDEO_CARDS];  // Protected by <card_mutex>.
+       YCbCrInterpretation ycbcr_interpretation[MAX_VIDEO_CARDS];  // Protected by <card_mutex>.
        AudioMixer audio_mixer;  // Same as global_audio_mixer (see audio_mixer.h).
        bool input_card_is_master_clock(unsigned card_index, unsigned master_card_index) const;
        struct OutputFrameInfo {
diff --git a/ycbcr_interpretation.h b/ycbcr_interpretation.h
new file mode 100644 (file)
index 0000000..51bad76
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _YCBCR_INTERPRETATION_H
+#define _YCBCR_INTERPRETATION_H 1
+
+#include <movit/image_format.h>
+
+struct YCbCrInterpretation {
+       bool ycbcr_coefficients_auto = true;
+       movit::YCbCrLumaCoefficients ycbcr_coefficients = movit::YCBCR_REC_709;
+       bool full_range = false;
+};
+
+#endif  // !defined(_YCBCR_INTERPRETATION_H)