QActionGroup mode_group(&mode_submenu);
std::map<uint32_t, VideoMode> video_modes = global_mixer->get_available_video_modes(current_card);
uint32_t current_video_mode = global_mixer->get_current_video_mode(current_card);
+ bool has_auto_mode = false;
for (const auto &mode : video_modes) {
QString description(QString::fromStdString(mode.second.name));
QAction *action = new QAction(description, &mode_group);
}
action->setData(QList<QVariant>{"video_mode", mode.first});
mode_submenu.addAction(action);
+
+ // TODO: Relying on the 0 value here (from bmusb.h) is ugly, it should be a named constant.
+ if (mode.first == 0) {
+ has_auto_mode = true;
+ }
+ }
+
+ // Add a “scan” menu if there's no “auto” mode.
+ if (!has_auto_mode) {
+ QAction *action = new QAction("Scan", &mode_group);
+ action->setData(QList<QVariant>{"video_mode", 0});
+ mode_submenu.addSeparator();
+ mode_submenu.addAction(action);
}
mode_submenu.setTitle("Input mode");
QList<QVariant> selected = selected_item->data().toList();
if (selected[0].toString() == "video_mode") {
uint32_t mode = selected[1].toUInt(nullptr);
- global_mixer->set_video_mode(current_card, mode);
+ if (mode == 0 && !has_auto_mode) {
+ global_mixer->start_mode_scanning(current_card);
+ } else {
+ global_mixer->set_video_mode(current_card, mode);
+ }
} else if (selected[0].toString() == "card") {
unsigned card_index = selected[1].toUInt(nullptr);
global_mixer->set_signal_mapping(signal_num, card_index);
{
CaptureCard *card = &cards[card_index];
+ if (is_mode_scanning[card_index]) {
+ if (video_format.has_signal) {
+ // Found a stable signal, so stop scanning.
+ is_mode_scanning[card_index] = false;
+ } else {
+ static constexpr double switch_time_s = 0.5; // Should be enough time for the signal to stabilize.
+ timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ double sec_since_last_switch = (now.tv_sec - last_mode_scan_change[card_index].tv_sec) +
+ 1e-9 * (now.tv_nsec - last_mode_scan_change[card_index].tv_nsec);
+ if (sec_since_last_switch > switch_time_s) {
+ // It isn't this mode; try the next one.
+ mode_scanlist_index[card_index]++;
+ mode_scanlist_index[card_index] %= mode_scanlist[card_index].size();
+ cards[card_index].capture->set_video_mode(mode_scanlist[card_index][mode_scanlist_index[card_index]]);
+ last_mode_scan_change[card_index] = now;
+ }
+ }
+ }
+
int64_t frame_length = int64_t(TIMEBASE * video_format.frame_rate_den) / video_format.frame_rate_nom;
size_t num_samples = (audio_frame.len >= audio_offset) ? (audio_frame.len - audio_offset) / audio_format.num_channels / (audio_format.bits_per_sample / 8) : 0;
correlation.reset();
}
+void Mixer::start_mode_scanning(unsigned card_index)
+{
+ assert(card_index < num_cards);
+ if (is_mode_scanning[card_index]) {
+ return;
+ }
+ is_mode_scanning[card_index] = true;
+ mode_scanlist[card_index].clear();
+ for (const auto &mode : cards[card_index].capture->get_available_video_modes()) {
+ mode_scanlist[card_index].push_back(mode.first);
+ }
+ assert(!mode_scanlist[card_index].empty());
+ mode_scanlist_index[card_index] = 0;
+ cards[card_index].capture->set_video_mode(mode_scanlist[card_index][0]);
+ clock_gettime(CLOCK_MONOTONIC, &last_mode_scan_change[card_index]);
+}
+
Mixer::OutputChannel::~OutputChannel()
{
if (has_current_frame) {
cards[card_index].capture->set_video_mode(mode);
}
+ void start_mode_scanning(unsigned card_index);
+
private:
void configure_card(unsigned card_index, const QSurfaceFormat &format, CaptureInterface *capture);
void bm_frame(unsigned card_index, uint16_t timecode,
std::mutex audio_mutex;
std::condition_variable audio_task_queue_changed;
std::queue<AudioTask> audio_task_queue; // Under audio_mutex.
+
+ // For mode scanning.
+ bool is_mode_scanning[MAX_CARDS]{ false };
+ std::vector<uint32_t> mode_scanlist[MAX_CARDS];
+ unsigned mode_scanlist_index[MAX_CARDS]{ 0 };
+ timespec last_mode_scan_change[MAX_CARDS];
};
extern Mixer *global_mixer;