fixes + cmake fixes for macos
This commit is contained in:
@@ -7,5 +7,10 @@ set(NDI_INCLUDE "${NDI_SDK_DIR}/include")
|
|||||||
target_include_directories(dmf-node-ndiin PRIVATE
|
target_include_directories(dmf-node-ndiin PRIVATE
|
||||||
"${NDI_INCLUDE}"
|
"${NDI_INCLUDE}"
|
||||||
)
|
)
|
||||||
find_library(NDI_LIB NAMES ndi PATHS "${NDI_SDK_DIR}/lib/${CMAKE_SYSTEM_PROCESSOR}-linux-gnu" "${NDI_SDK_DIR}/lib/x64" "${NDI_SDK_DIR}/lib" NO_DEFAULT_PATH)
|
find_library(NDI_LIB NAMES ndi PATHS
|
||||||
|
"${NDI_SDK_DIR}/lib/macOS"
|
||||||
|
"${NDI_SDK_DIR}/lib/${CMAKE_SYSTEM_PROCESSOR}-linux-gnu"
|
||||||
|
"${NDI_SDK_DIR}/lib/x64"
|
||||||
|
"${NDI_SDK_DIR}/lib"
|
||||||
|
NO_DEFAULT_PATH)
|
||||||
target_link_libraries(dmf-node-ndiin PRIVATE ${NDI_LIB})
|
target_link_libraries(dmf-node-ndiin PRIVATE ${NDI_LIB})
|
||||||
|
|||||||
+37
-28
@@ -96,40 +96,49 @@ class NDIInNode : public dmf::NodeBase {
|
|||||||
dmf::NDIReceiver::AudioInfo audio_info;
|
dmf::NDIReceiver::AudioInfo audio_info;
|
||||||
|
|
||||||
while (dmf::g_running.load(std::memory_order_relaxed)) {
|
while (dmf::g_running.load(std::memory_order_relaxed)) {
|
||||||
dmf::NDIReceiver::FrameKind kind;
|
// Drain all queued NDI frames before writing one MXL grain.
|
||||||
|
// With audio enabled, many audio packets may be queued ahead of a video frame.
|
||||||
|
// Calling capture() once per grain window would starve video reception.
|
||||||
|
// Strategy: blocking first call (5ms), then non-blocking drain of remainder.
|
||||||
try {
|
try {
|
||||||
kind = ndi.capture(latest_video.data(), video_stride, audio_buf, audio_info, has_audio);
|
bool first = true;
|
||||||
|
while (dmf::g_running.load(std::memory_order_relaxed)) {
|
||||||
|
auto kind = ndi.capture(latest_video.data(), video_stride,
|
||||||
|
audio_buf, audio_info, has_audio,
|
||||||
|
first ? 5 : 0);
|
||||||
|
first = false;
|
||||||
|
if (kind == dmf::NDIReceiver::FrameKind::Video) {
|
||||||
|
have_video = true;
|
||||||
|
} else if (kind == dmf::NDIReceiver::FrameKind::Audio && has_audio) {
|
||||||
|
mxlMutableWrappedMultiBufferSlice slice{};
|
||||||
|
if (mxlFlowWriterOpenSamples(audio_writer, audio_index,
|
||||||
|
static_cast<size_t>(audio_info.samples),
|
||||||
|
&slice) == MXL_STATUS_OK) {
|
||||||
|
const size_t frag0 = slice.base.fragments[0].size / sizeof(float);
|
||||||
|
const size_t frag1 = slice.base.fragments[1].size / sizeof(float);
|
||||||
|
for (int c = 0; c < audio_info.channels; ++c) {
|
||||||
|
const float* src = &audio_buf[c * audio_info.channel_stride];
|
||||||
|
auto* dst0 = reinterpret_cast<float*>(
|
||||||
|
static_cast<uint8_t*>(slice.base.fragments[0].pointer) + c * slice.stride);
|
||||||
|
std::memcpy(dst0, src, frag0 * sizeof(float));
|
||||||
|
if (frag1 > 0) {
|
||||||
|
auto* dst1 = reinterpret_cast<float*>(
|
||||||
|
static_cast<uint8_t*>(slice.base.fragments[1].pointer) + c * slice.stride);
|
||||||
|
std::memcpy(dst1, src + frag0, frag1 * sizeof(float));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mxlFlowWriterCommitSamples(audio_writer);
|
||||||
|
}
|
||||||
|
audio_index += audio_info.samples;
|
||||||
|
} else {
|
||||||
|
break; // None — queue drained
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (const std::runtime_error& e) {
|
} catch (const std::runtime_error& e) {
|
||||||
log("NDI error: %s — stopping", e.what());
|
log("NDI error: %s — stopping", e.what());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kind == dmf::NDIReceiver::FrameKind::Video) {
|
|
||||||
have_video = true;
|
|
||||||
} else if (kind == dmf::NDIReceiver::FrameKind::Audio && has_audio) {
|
|
||||||
mxlMutableWrappedMultiBufferSlice slice{};
|
|
||||||
if (mxlFlowWriterOpenSamples(audio_writer, audio_index,
|
|
||||||
static_cast<size_t>(audio_info.samples), &slice) == MXL_STATUS_OK) {
|
|
||||||
// MXL audio is float32 planar: each channel occupies its own ring buffer
|
|
||||||
// region, accessed at base + c * stride. Fragments handle ring wraparound.
|
|
||||||
const size_t frag0 = slice.base.fragments[0].size / sizeof(float);
|
|
||||||
const size_t frag1 = slice.base.fragments[1].size / sizeof(float);
|
|
||||||
for (int c = 0; c < audio_info.channels; ++c) {
|
|
||||||
const float* src = &audio_buf[c * audio_info.channel_stride];
|
|
||||||
auto* dst0 = reinterpret_cast<float*>(
|
|
||||||
static_cast<uint8_t*>(slice.base.fragments[0].pointer) + c * slice.stride);
|
|
||||||
std::memcpy(dst0, src, frag0 * sizeof(float));
|
|
||||||
if (frag1 > 0) {
|
|
||||||
auto* dst1 = reinterpret_cast<float*>(
|
|
||||||
static_cast<uint8_t*>(slice.base.fragments[1].pointer) + c * slice.stride);
|
|
||||||
std::memcpy(dst1, src + frag0, frag1 * sizeof(float));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mxlFlowWriterCommitSamples(audio_writer);
|
|
||||||
}
|
|
||||||
audio_index += audio_info.samples;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write video grain whenever the MXL clock has reached video_index
|
// Write video grain whenever the MXL clock has reached video_index
|
||||||
const uint64_t current = mxlGetCurrentIndex(&video_rate);
|
const uint64_t current = mxlGetCurrentIndex(&video_rate);
|
||||||
if (current >= video_index) {
|
if (current >= video_index) {
|
||||||
|
|||||||
@@ -7,5 +7,10 @@ set(NDI_INCLUDE "${NDI_SDK_DIR}/include")
|
|||||||
target_include_directories(dmf-node-ndiout PRIVATE
|
target_include_directories(dmf-node-ndiout PRIVATE
|
||||||
"${NDI_INCLUDE}"
|
"${NDI_INCLUDE}"
|
||||||
)
|
)
|
||||||
find_library(NDI_LIB NAMES ndi PATHS "${NDI_SDK_DIR}/lib/${CMAKE_SYSTEM_PROCESSOR}-linux-gnu" "${NDI_SDK_DIR}/lib/x64" "${NDI_SDK_DIR}/lib" NO_DEFAULT_PATH)
|
find_library(NDI_LIB NAMES ndi PATHS
|
||||||
|
"${NDI_SDK_DIR}/lib/macOS"
|
||||||
|
"${NDI_SDK_DIR}/lib/${CMAKE_SYSTEM_PROCESSOR}-linux-gnu"
|
||||||
|
"${NDI_SDK_DIR}/lib/x64"
|
||||||
|
"${NDI_SDK_DIR}/lib"
|
||||||
|
NO_DEFAULT_PATH)
|
||||||
target_link_libraries(dmf-node-ndiout PRIVATE ${NDI_LIB})
|
target_link_libraries(dmf-node-ndiout PRIVATE ${NDI_LIB})
|
||||||
|
|||||||
@@ -124,15 +124,16 @@ public:
|
|||||||
// Receives one NDI frame. On video: converts to V210 in frame_buffer/frame_stride.
|
// Receives one NDI frame. On video: converts to V210 in frame_buffer/frame_stride.
|
||||||
// On audio: copies float32 planar samples into audio_out and fills audio_info.
|
// On audio: copies float32 planar samples into audio_out and fills audio_info.
|
||||||
// Returns FrameKind::None on timeout or non-A/V frames.
|
// Returns FrameKind::None on timeout or non-A/V frames.
|
||||||
// Throws on source lost or video format change.
|
// Throws on source lost or unsupported video format.
|
||||||
|
// timeout_ms=0 is non-blocking (useful for draining queued frames after an initial wait).
|
||||||
FrameKind capture(uint8_t* frame_buffer, uint32_t frame_stride,
|
FrameKind capture(uint8_t* frame_buffer, uint32_t frame_stride,
|
||||||
std::vector<float>& audio_out, AudioInfo& audio_info,
|
std::vector<float>& audio_out, AudioInfo& audio_info,
|
||||||
bool want_audio = false) {
|
bool want_audio = false, int timeout_ms = 5) {
|
||||||
NDIlib_video_frame_v2_t video_frame{};
|
NDIlib_video_frame_v2_t video_frame{};
|
||||||
NDIlib_audio_frame_v3_t audio_frame{};
|
NDIlib_audio_frame_v3_t audio_frame{};
|
||||||
auto type = NDIlib_recv_capture_v3(recv_, &video_frame,
|
auto type = NDIlib_recv_capture_v3(recv_, &video_frame,
|
||||||
want_audio ? &audio_frame : nullptr,
|
want_audio ? &audio_frame : nullptr,
|
||||||
nullptr, 5);
|
nullptr, timeout_ms);
|
||||||
|
|
||||||
if (type == NDIlib_frame_type_error)
|
if (type == NDIlib_frame_type_error)
|
||||||
throw std::runtime_error("NDI source lost");
|
throw std::runtime_error("NDI source lost");
|
||||||
|
|||||||
Reference in New Issue
Block a user