#include "shared/context.h"
#include "decklink_capture.h"
#include "decklink_output.h"
+#include "decklink_util.h"
#include "defs.h"
#include "shared/disk_space_estimator.h"
#include "ffmpeg_capture.h"
break;
}
+ if (!decklink_card_is_active(decklink, card_index)) {
+ fprintf(stderr, "DeckLink card %u is inactive in current profile, skipping (try changing it in Desktop Video Setup)\n", card_index);
+ decklink->Release();
+ continue;
+ }
+
DeckLinkCapture *capture = new DeckLinkCapture(decklink, card_index);
DeckLinkOutput *output = new DeckLinkOutput(resource_pool.get(), decklink_output_surface, global_flags.width, global_flags.height, card_index);
if (!output->set_device(decklink)) {
}
#endif
- for (unsigned card_index = 0; card_index < MAX_VIDEO_CARDS; ++card_index) {
- cards[card_index].queue_length_policy.reset(card_index);
- }
-
chroma_subsampler.reset(new ChromaSubsampler(resource_pool.get()));
if (global_flags.ten_bit_input) {
card->parked_capture = move(card->capture);
CaptureInterface *fake_capture = new FakeCapture(global_flags.width, global_flags.height, FAKE_FPS, OUTPUT_FREQUENCY, card_index, global_flags.fake_cards_audio);
configure_card(card_index, fake_capture, CardType::FAKE_CAPTURE, card->output.release(), /*is_srt_card=*/false);
- card->queue_length_policy.reset(card_index);
+ card->jitter_history.clear();
card->capture->start_bm_capture();
desired_output_video_mode = output_video_mode = card->output->pick_video_mode(desired_output_video_mode);
card->output->start_output(desired_output_video_mode, pts_int);
output->start_output(desired_output_video_mode, pts_int);
}
+ {
+ lock_guard<mutex> lock(card_mutex);
+ handle_hotplugged_cards();
+ }
+
CaptureCard::NewFrame new_frames[MAX_VIDEO_CARDS];
bool has_new_frame[MAX_VIDEO_CARDS] = { false };
assert(master_card_index < MAX_VIDEO_CARDS);
}
- {
- lock_guard<mutex> lock(card_mutex);
- handle_hotplugged_cards();
- }
-
vector<int32_t> raw_audio[MAX_VIDEO_CARDS]; // For MJPEG encoding.
OutputFrameInfo output_frame_info = get_one_frame_from_each_card(master_card_index, master_card_is_output, new_frames, has_new_frame, raw_audio);
schedule_audio_resampling_tasks(output_frame_info.dropped_frames, output_frame_info.num_samples, output_frame_info.frame_duration, output_frame_info.is_preroll, output_frame_info.frame_timestamp);
Mixer::OutputFrameInfo Mixer::get_one_frame_from_each_card(unsigned master_card_index, bool master_card_is_output, CaptureCard::NewFrame new_frames[MAX_VIDEO_CARDS], bool has_new_frame[MAX_VIDEO_CARDS], vector<int32_t> raw_audio[MAX_VIDEO_CARDS])
{
OutputFrameInfo output_frame_info;
- constexpr steady_clock::duration master_card_timeout = milliseconds(100);
+ constexpr steady_clock::duration master_card_timeout = milliseconds(200);
start:
unique_lock<mutex> lock(card_mutex, defer_lock);
bool timed_out = false;
lock.lock();
timed_out = !cards[master_card_index].new_frames_changed.wait_for(lock,
master_card_timeout,
- [this, master_card_index]{
+ [this, master_card_index] {
return !cards[master_card_index].new_frames.empty() ||
+ cards[master_card_index].capture == nullptr ||
cards[master_card_index].capture->get_disconnected();
});
if (timed_out) {
- fprintf(stderr, "WARNING: Master card (%s) did not deliver a frame for 100 ms, creating a fake one.\n",
- description_for_card(master_card_index).c_str());
+ fprintf(stderr, "WARNING: Master card (%s) did not deliver a frame for %u ms, creating a fake one.\n",
+ description_for_card(master_card_index).c_str(),
+ unsigned(duration_cast<milliseconds>(master_card_timeout).count()));
}
}
if (timed_out) {
- // The master card stalled for 100 ms (possible when it's e.g.
+ // The master card stalled for 200 ms (possible when it's e.g.
// an SRT card). Send a frame no matter what; this also makes sure
// any other cards get to empty their queues, and in general,
// that we make _some_ sort of forward progress.
} else if (cards[master_card_index].new_frames.empty()) {
// We were woken up, but not due to a new frame. Deal with it
// and then restart.
- assert(cards[master_card_index].capture->get_disconnected());
+ assert(cards[master_card_index].capture == nullptr ||
+ cards[master_card_index].capture->get_disconnected());
handle_hotplugged_cards();
lock.unlock();
goto start;
fractional_samples = num_samples_times_timebase % TIMEBASE;
assert(output_frame_info.num_samples >= 0);
+ if (timed_out) {
+ DeviceSpec device{InputSourceType::CAPTURE_CARD, master_card_index};
+ bool success;
+ do {
+ success = audio_mixer->add_silence(device, output_frame_info.num_samples, /*dropped_frames=*/0);
+ } while (!success);
+ }
+
return output_frame_info;
}
for (unsigned card_index = 0; card_index < MAX_VIDEO_CARDS; ++card_index) {
CaptureCard *card = &cards[card_index];
if (card->capture != nullptr && card->capture->get_disconnected()) {
- fprintf(stderr, "Card %u went away, replacing with a fake card.\n", card_index);
- FakeCapture *capture = new FakeCapture(global_flags.width, global_flags.height, FAKE_FPS, OUTPUT_FREQUENCY, card_index, global_flags.fake_cards_audio);
- configure_card(card_index, capture, CardType::FAKE_CAPTURE, /*output=*/nullptr, /*is_srt_card=*/false);
- card->queue_length_policy.reset(card_index);
- card->capture->start_bm_capture();
+ bool is_active = card_index < unsigned(global_flags.min_num_cards) || cards[card_index].force_active;
+ if (is_active) {
+ fprintf(stderr, "Card %u went away, replacing with a fake card.\n", card_index);
+ FakeCapture *capture = new FakeCapture(global_flags.width, global_flags.height, FAKE_FPS, OUTPUT_FREQUENCY, card_index, global_flags.fake_cards_audio);
+ configure_card(card_index, capture, CardType::FAKE_CAPTURE, /*output=*/nullptr, /*is_srt_card=*/false);
+ card->jitter_history.clear();
+ card->capture->start_bm_capture();
+ } else {
+ // NOTE: The theme might end up forcing the card back at some later point
+ // (ie., force_active is false now, but might immediately be true again on
+ // e.g. the next frame). That should be rare, though, so we don't bother
+ // adjusting the message.
+ fprintf(stderr, "Card %u went away, removing. (To keep a fake card, increase --num-cards.)\n", card_index);
+ theme->remove_card(card_index);
+ configure_card(card_index, /*capture=*/nullptr, CardType::FAKE_CAPTURE, /*output=*/nullptr, /*is_srt_card=*/false);
+ card->jitter_history.clear();
+ }
}
}
CaptureCard *card = &cards[free_card_index];
BMUSBCapture *capture = new BMUSBCapture(free_card_index, new_dev);
configure_card(free_card_index, capture, CardType::LIVE_CARD, /*output=*/nullptr, /*is_srt_card=*/false);
- card->queue_length_policy.reset(free_card_index);
+ card->jitter_history.clear();
capture->set_card_disconnected_callback(bind(&Mixer::bm_hotplug_remove, this, free_card_index));
capture->start_bm_capture();
}
configure_card(free_card_index, capture, CardType::FFMPEG_INPUT, /*output=*/nullptr, /*is_srt_card=*/true);
update_srt_stats(sock, card); // Initial zero stats.
card->last_srt_stream_id = stream_id;
- card->queue_length_policy.reset(free_card_index);
+ card->jitter_history.clear();
capture->set_card_disconnected_callback(bind(&Mixer::bm_hotplug_remove, this, free_card_index));
capture->start_bm_capture();
}
if (card->capture == nullptr && card->force_active) {
FakeCapture *capture = new FakeCapture(global_flags.width, global_flags.height, FAKE_FPS, OUTPUT_FREQUENCY, card_index, global_flags.fake_cards_audio);
configure_card(card_index, capture, CardType::FAKE_CAPTURE, /*output=*/nullptr, /*is_srt_card=*/false);
- card->queue_length_policy.reset(card_index);
+ card->jitter_history.clear();
card->capture->start_bm_capture();
}
}
if (global_flags.srt_port >= 0) {
// There's seemingly no other reasonable way to wake up the thread
// (libsrt's epoll equivalent is busy-waiting).
- int sock = srt_socket(AF_INET6, 0, 0);
+ int sock = srt_create_socket();
if (sock != -1) {
sockaddr_in6 addr;
memset(&addr, 0, sizeof(addr));
#ifdef HAVE_SRT
void Mixer::start_srt()
{
- SRTSOCKET sock = srt_socket(AF_INET6, 0, 0);
+ SRTSOCKET sock = srt_create_socket();
sockaddr_in6 addr;
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;