decklink in timing fix
This commit is contained in:
+24
-15
@@ -95,23 +95,29 @@ class DeckLinkInNode : public dmf::NodeBase {
|
||||
std::vector<float> audio_buf(static_cast<size_t>(max_audio_samples) * static_cast<size_t>(channels));
|
||||
|
||||
// --- Clock ---
|
||||
const mxlRational video_rate = {fps_num, fps_den};
|
||||
const mxlRational audio_rate = {receiver.audio_info.sample_rate, 1};
|
||||
uint64_t video_index = mxlGetCurrentIndex(&video_rate);
|
||||
uint64_t audio_index = has_audio ? mxlGetCurrentIndex(&audio_rate) : 0;
|
||||
log("start video_index=%llu", static_cast<unsigned long long>(video_index));
|
||||
// video_index is determined AFTER each hardware frame arrives so it reflects
|
||||
// the actual TAI slot the frame landed in. wait_for_frame() is the natural
|
||||
// pacer — no separate sleep needed.
|
||||
const mxlRational video_rate = {fps_num, fps_den};
|
||||
const mxlRational audio_rate = {receiver.audio_info.sample_rate, 1};
|
||||
uint64_t audio_index = has_audio ? mxlGetCurrentIndex(&audio_rate) : 0;
|
||||
uint64_t frame_count = 0, drop_count = 0;
|
||||
log("ready, waiting for first frame...");
|
||||
|
||||
// --- Capture loop ---
|
||||
while (dmf::g_running.load(std::memory_order_relaxed)) {
|
||||
int samples_written = 0;
|
||||
|
||||
// DeckLink delivers one video frame + accompanying audio per callback.
|
||||
// Blocks until DeckLink hardware delivers a frame — this IS the pacing.
|
||||
if (!receiver.wait_for_frame(
|
||||
frame_buf.data(), video_stride, width, height,
|
||||
(has_audio && audio_writer) ? audio_buf.data() : nullptr,
|
||||
max_audio_samples,
|
||||
(has_audio && audio_writer) ? &samples_written : nullptr)) break;
|
||||
|
||||
// Resolve TAI index now — after the frame arrived, not before.
|
||||
const uint64_t video_index = mxlGetCurrentIndex(&video_rate);
|
||||
|
||||
// Video grain
|
||||
mxlGrainInfo grain{};
|
||||
uint8_t* video_buf_ptr = nullptr;
|
||||
@@ -121,18 +127,26 @@ class DeckLinkInNode : public dmf::NodeBase {
|
||||
grain.flags = 0;
|
||||
grain.validSlices = grain.totalSlices;
|
||||
mxlFlowWriterCommitGrain(video_writer, &grain);
|
||||
frame_count++;
|
||||
if (frame_count % 25 == 0)
|
||||
log("heartbeat frames=%llu drops=%llu index=%llu",
|
||||
frame_count, drop_count, video_index);
|
||||
} else {
|
||||
drop_count++;
|
||||
log("OpenGrain failed (%s) at index=%llu drops=%llu",
|
||||
dmf::mxl_status_str(vst), video_index, drop_count);
|
||||
}
|
||||
|
||||
// Audio samples (same fragment-wrap pattern as videoin)
|
||||
// Audio samples
|
||||
if (has_audio && audio_writer && samples_written > 0) {
|
||||
mxlMutableWrappedMultiBufferSlice slice{};
|
||||
mxlStatus ast = mxlFlowWriterOpenSamples(
|
||||
audio_writer, audio_index, static_cast<size_t>(samples_written), &slice);
|
||||
if (ast == MXL_STATUS_OK) {
|
||||
for (int ch = 0; ch < channels; ++ch) {
|
||||
const uint8_t* src = reinterpret_cast<const uint8_t*>(
|
||||
const uint8_t* src = reinterpret_cast<const uint8_t*>(
|
||||
audio_buf.data() + ch * max_audio_samples);
|
||||
uint8_t* dst0 = static_cast<uint8_t*>(
|
||||
uint8_t* dst0 = static_cast<uint8_t*>(
|
||||
slice.base.fragments[0].pointer) + ch * slice.stride;
|
||||
const size_t frag0_bytes = slice.base.fragments[0].size;
|
||||
const size_t total_bytes = static_cast<size_t>(samples_written) * sizeof(float);
|
||||
@@ -153,14 +167,9 @@ class DeckLinkInNode : public dmf::NodeBase {
|
||||
}
|
||||
audio_index += static_cast<uint64_t>(samples_written);
|
||||
}
|
||||
|
||||
// Pace video to the MXL clock
|
||||
const uint64_t ns = mxlGetNsUntilIndex(video_index + 1, &video_rate);
|
||||
if (ns > 0 && ns < 2'000'000'000ULL) mxlSleepForNs(ns);
|
||||
video_index = mxlGetCurrentIndex(&video_rate);
|
||||
}
|
||||
|
||||
log("stopped at video_index=%llu", static_cast<unsigned long long>(video_index));
|
||||
log("stopped frames=%llu drops=%llu", frame_count, drop_count);
|
||||
mxlReleaseFlowWriter(instance(), video_writer);
|
||||
if (audio_writer) mxlReleaseFlowWriter(instance(), audio_writer);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user