}
check_error();
break;
+ default:
+ assert(false);
}
userdata->last_width[field] = width;
userdata->last_height[field] = height;
}
card->capture.reset(capture);
card->is_fake_capture = (card_type == CardType::FAKE_CAPTURE);
+ card->is_cef_capture = (card_type == CardType::CEF_INPUT);
+ card->may_have_dropped_last_frame = false;
card->type = card_type;
if (card->output.get() != output) {
card->output.reset(output);
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;
if (num_samples > OUTPUT_FREQUENCY / 10) {
- printf("Card %d: Dropping frame with implausible audio length (len=%d, offset=%d) [timecode=0x%04x video_len=%d video_offset=%d video_format=%x)\n",
- card_index, int(audio_frame.len), int(audio_offset),
+ printf("%s: Dropping frame with implausible audio length (len=%d, offset=%d) [timecode=0x%04x video_len=%d video_offset=%d video_format=%x)\n",
+ spec_to_string(device).c_str(), int(audio_frame.len), int(audio_offset),
timecode, int(video_frame.len), int(video_offset), video_format.id);
if (video_frame.owner) {
video_frame.owner->release_frame(video_frame);
const int silence_samples = OUTPUT_FREQUENCY * video_format.frame_rate_den / video_format.frame_rate_nom;
if (dropped_frames > MAX_FPS * 2) {
- fprintf(stderr, "Card %d lost more than two seconds (or time code jumping around; from 0x%04x to 0x%04x), resetting resampler\n",
- card_index, card->last_timecode, timecode);
+ fprintf(stderr, "%s lost more than two seconds (or time code jumping around; from 0x%04x to 0x%04x), resetting resampler\n",
+ spec_to_string(device).c_str(), card->last_timecode, timecode);
audio_mixer.reset_resampler(device);
dropped_frames = 0;
++card->metric_input_resets;
} else if (dropped_frames > 0) {
// Insert silence as needed.
- fprintf(stderr, "Card %d dropped %d frame(s) (before timecode 0x%04x), inserting silence.\n",
- card_index, dropped_frames, timecode);
+ fprintf(stderr, "%s dropped %d frame(s) (before timecode 0x%04x), inserting silence.\n",
+ spec_to_string(device).c_str(), dropped_frames, timecode);
card->metric_input_dropped_frames_error += dropped_frames;
bool success;
if (video_frame.len - video_offset == 0 ||
video_frame.len - video_offset != expected_length) {
if (video_frame.len != 0) {
- printf("Card %d: Dropping video frame with wrong length (%ld; expected %ld)\n",
- card_index, video_frame.len - video_offset, expected_length);
+ printf("%s: Dropping video frame with wrong length (%ld; expected %ld)\n",
+ spec_to_string(device).c_str(), video_frame.len - video_offset, expected_length);
}
if (video_frame.owner) {
video_frame.owner->release_frame(video_frame);
new_frame.received_timestamp = video_frame.received_timestamp; // Ignore the audio timestamp.
card->new_frames.push_back(move(new_frame));
card->jitter_history.frame_arrived(video_frame.received_timestamp, frame_length, dropped_frames);
+ card->may_have_dropped_last_frame = false;
}
card->new_frames_changed.notify_all();
}
card->new_frames_changed.notify_all();
--queue_length;
++dropped_frames;
+
+ if (queue_length == 0 && card->is_cef_capture) {
+ card->may_have_dropped_last_frame = true;
+ }
}
card->metric_input_dropped_frames_jitter += dropped_frames;
CaptureCard *card = &cards[card_index];
if (card->new_frames.empty()) { // Starvation.
++card->metric_input_duped_frames;
+#ifdef HAVE_CEF
+ if (card->is_cef_capture && card->may_have_dropped_last_frame) {
+ // Unlike other sources, CEF is not guaranteed to send us a steady
+ // stream of frames, so we'll have to ask it to repaint the frame
+ // we dropped. (may_have_dropped_last_frame is set whenever we
+ // trim the queue completely away, and cleared when we actually
+ // get a new frame.)
+ ((CEFCapture *)card->capture.get())->request_new_frame();
+ }
+#endif
} else {
new_frames[card_index] = move(card->new_frames.front());
has_new_frame[card_index] = true;
theme_main_chain.setup_chain();
//theme_main_chain.chain->enable_phase_timing(true);
- // The theme can't (or at least shouldn't!) call connect_signal() on
- // each FFmpeg or CEF input, so we'll do it here.
- for (const pair<LiveInputWrapper *, FFmpegCapture *> &conn : theme->get_video_signal_connections()) {
- conn.first->connect_signal_raw(conn.second->get_card_index(), input_state);
- }
-#ifdef HAVE_CEF
- for (const pair<LiveInputWrapper *, CEFCapture *> &conn : theme->get_html_signal_connections()) {
- conn.first->connect_signal_raw(conn.second->get_card_index(), input_state);
- }
-#endif
-
// If HDMI/SDI output is active and the user has requested auto mode,
// its mode overrides the existing Y'CbCr setting for the chain.
YCbCrLumaCoefficients ycbcr_output_coefficients;