RK3576 + NDI 框架:HX2 编码输出与 HX2/FULL介绍

RK3576 + NDI 框架:HX2 编码输出与 HX2/FULL 输入全解析

本文是 MEDAI-V1 / RK3576 技术专栏(已发 拉流 / 绿幕 / MPP 编解码 / UVC 解码 / 综合性能 / RGA 适配 / MPP×RGA 统一编码 / FFmpeg 拉流 / librtmp 推流 / V4L2-UVC / ALSA 音频 / RGA+DRM HDMI 输出)的 第十三篇 ,聚焦 NDI 框架 在设备上的落地:如何用 RK3576 把统一的视频画布以 NDI|HX2 编码输出,又如何把局域网里的 NDI|HX2 / NDI|FULL 信号拉进来硬解、合成、再分发。文中所有功能点均深度对照《MEDAI-V1 使用说明书》。


一、NDI 框架技术详解

1.1 NDI 是什么

NDI(Network Device Interface,网络设备接口) 是 NewTek(现 Vizrt)推出的基于 IP 的实时音视频传输标准,目标只有一个:让一根网线代替一堆 SDI/HDMI 线,在局域网内以"可接受的延迟"把多路高质量音视频在设备间自由路由。

和 RTMP/RTSP/SRT 这类"面向广域网、单路长连接"的协议不同,NDI 的设计哲学是:

  • 局域网优先:假设 1 Gbps 交换机组网,靠发现协议自动列出所有源;
  • 多路并发:一台机器可以同时看到几十路源,按需挑一路拉;
  • 双向无损概念:FULL 走"视觉无损"高带宽,HX/HX2 走压缩低带宽;
  • 带内元数据:每一帧可挂 PTZ、Tally、时间戳、元数据。

在 MEDAI-V1 上,NDI 既是 五路输入源之一 (3.2.2 视频输出可选 NDI 作为输入),又是 本地流媒体服务的一种输出 (3.5 媒体输出第 6 项),还参与 视频叠加的主/副源 (4.4.2)和 手势切源的目标(6.2 中 Three 手势切到 NDI)。

1.2 NDI 软件栈分层

无论发送端还是接收端,NDI 运行时都分成四层,理解这四层是后面写代码的基础:

复制代码
┌─────────────────────────────────────────────────────────┐
│  SDK 层        NDIlib_recv_* / NDIlib_send_*  (C API)     │
│               find(发现) / recv(收) / send(发)       │
├─────────────────────────────────────────────────────────┤
│  Transport 层   UDP/RTP 承载视频音频;mDNS 承载发现        │
│               单源单连接(NDI 4+);TCP 兜底              │
├─────────────────────────────────────────────────────────┤
│  Compression 层 FULL:SpeedHQ 视觉无损(高带宽)           │
│               HX2:HEVC 压缩(低带宽)  HX:H.264(旧)   │
├─────────────────────────────────────────────────────────┤
│  Discovery 层  mDNS / Bonjour,注册 _ndi._tcp.local       │
│               receiver 浏览器式枚举所有在线源             │
└─────────────────────────────────────────────────────────┘

黄金认知 :NDI SDK 自己在内部完成"压缩/解压缩"和"传输",用户代码只和 NDIlib_video_frame_v2_t / NDIlib_audio_frame_v3_t 打交道------你给它一张 YCbCr/ARGB 帧,它负责压成 HX2 发出去;它收到 HX2/FULL 包,解完给你一张同样结构的帧。设备侧的 MPP 只负责 RTMP/MP4 那条编码链,不直接编 NDI 的码流。

1.3 NDI 三大变种横向对比

变种 压缩方式 1080p60 带宽 4K 带宽 延迟 典型用途
NDI|FULL SpeedHQ 视觉无损 ~250 Mbps ~1 Gbps 极低 演播室内部、画质优先
NDI|HX2 HEVC(H.265) 8 ~ 20 Mbps 25 ~ 80 Mbps 低 跨网段、无线、公网桥接
NDI|HX(旧) H.264 8 ~ 16 Mbps 不提倡 4K 中 老设备兼容

MEDAI-V1 出厂为 试用版 NDI (4.3.4),编码与解码两个方向 只能二选一启用(7.2 注意事项第 5 条)。下面的实现会分别讲"当设备处于编码态"和"处于解码态"两种代码路径。

1.4 NDI|HX2 深入:HEVC 底子 + 带宽账

NDI|HX2(NDI 5 引入,NDI 6 有 HX3)是 HEVC 压缩 + NDI 元数据 的结合体,关键特性:

  • 码率自适应:1080p60 默认 8~15 Mbps,运动剧烈时上探到 20 Mbps;
  • 支持 Alpha 通道:可传带透明度的前景(配合绿幕/叠加用,对应 4.4.2/4.4.3);
  • 低延迟:端到端通常 80~150 ms(局域网),比 RTMP 的 1~3 s 低一个量级;
  • 抗丢包:基于 UDP 但带重传请求,轻度丢包不花屏。

带宽账(为什么 HX2 适合无线/跨网段):

复制代码
一路 NDI|HX2 1080p60 ≈ 15 Mbps
千兆交换机组 20 路 HX2  = 300 Mbps  ← 完全吃得消
一路 NDI|FULL 1080p60 ≈ 250 Mbps
千兆交换机组 4 路 FULL  = 1 Gbps    ← 已经打满

结论:同一台千兆交换机,HX2 能塞进 10~20 倍于 FULL 的路数。这也是设备把 NDI 输出默认走 HX2 的工程理由。

1.5 NDI|FULL 深入:未压缩(视觉无损)真相

NDI|FULL 早期常被误解为"原始 RGB 不压缩",实际它用 SpeedHQ(一种类 MJPEG 的 intra 帧压缩) 做到"视觉无损",但带宽仍极高:

  • 1080p60、4:2:2、8bit:约 250 Mbps;
  • UHD(2160p30)、4:2:2:约 1 Gbps。

对 RK3576 而言,收 FULL 路意味着:NDI SDK 解完后是 UYVY / BGRA 满帧 ,不经过 VPU(因为 SDK 内部已解码),直接由 RGA 转 NV12 进统一画布(见第六篇 RGA 适配)。收 FULL 路几乎不占 VPU,但 占满网口和内存带宽。

1.6 发现机制(mDNS / Bonjour)

NDI 一台设备"上线"时,会向局域网多播 224.0.0.251:5353 注册服务:

复制代码
_ndi._tcp.local  name="MEDAI-V1 (Studio-A)"  port=5960  txt=...

接收端调用 NDIlib_find_create_v2() 后,定时 NDIlib_find_get_current_sources() 就能拿到当前所有源的 ndi_name 和 url_address。说明书 4.3.3 的"点击搜索按键对本地 NDI 信号源进行搜索 ",底层正是这步;页面提示"搜索到的 NDI 信号源数量",就是 get_current_sources 返回的数组长度。

坑 :mDNS 依赖局域网多播可达。若设备接在 ETH1(192.168.78.0/24) 而你的电脑在 WiFi 另一网段,发现会失败------这跟 2.3.3 网络共存说明一脉相承,要确保搜索端和被搜端在同一二层。

1.7 传输与时间同步

  • 视频/音频 :NDI 4+ 对每路源建立 单条连接,视频 UDP、音频 UDP,用 NTP 风格时钟对齐;
  • 时间戳 :每帧带 timecode,接收端据此做音视频对齐与多路同步;
  • TCP 兜底:UDP 不可达时 SDK 自动用 TCP,延迟升高但不断流。

1.8 NDI SDK 关键对象生命周期

c 复制代码
/* 发送端 */
NDIlib_initialize();
NDIlib_send_create_t snd = { .p_ndi_name = "MEDAI-V1", .p_groups = NULL };
pNDI_send = NDIlib_send_create(&snd);
NDIlib_send_send_video_v2(pNDI_send, &video_frame);   // 逐帧推送
NDIlib_send_destroy(pNDI_send);

/* 接收端 */
pNDI_find = NDIlib_find_create_v2(&find_desc, false);
NDIlib_recv_create_v3_t recv = { .source_to_connect_to = src, .color_format = NDIlib_recv_color_format_best_streaming_quality };
pNDI_recv = NDIlib_recv_create_v3(&recv);
NDIlib_recv_capture_v3(pNDI_recv, &video, &audio, &metadata, timeout_ms);
NDIlib_recv_destroy(pNDI_recv);
NDIlib_find_destroy(pNDI_find);
NDIlib_destroy();

帧结构 NDIlib_video_frame_v2_t 关键字段:xres/yres、FourCC(UYVY / BGRA / RGBA / UYVA / NV12 / YV12)、frame_rate_N/D、line_stride_in_bytes、data。我们给 NDI 的帧格式用 UYVY(4:2:2)最稳,RGA 从 NV12 转 UYVY 一条指令搞定。

1.9 NDI vs RTSP/SRT/RTMP 选型口诀

场景 选谁 原因
局域网多机位切换 NDI 自动发现、低延迟、多路
跨公网稳定推流 RTMP/SRT 见第九篇 librtmp 推流
弱网抗丢包长距 SRT 前向纠错 + ARQ
点播/H5 播放 HLS/HTTP-FLV 见第一篇拉流
本地监看/录制 RTSP/本地文件 见 3.5 媒体输出

NDI 不是来替代它们的,而是补上"局域网内即插即用的多路实时矩阵"这一环。


二、MEDAI-V1 的 NDI 能力(对照说明书)

把说明书里所有 NDI 相关条目收拢:

说明书位置 NDI 相关内容 本文对应
1.1 产品简介 "集 NDI 编解码于一体" 编码+解码双能力
3.1 系统状态 NDI 连接状态及接入分辨率 状态页可看输入/输出分辨率
3.2.2 视频输出 五输入源之一可选 NDI NDI 作输入源
3.5 媒体输出 第 6 项"NDI 功能" NDI 作输出源
4.3.3 NDI 配置 编码配置(自定义名+开关);解码配置(搜索+拉流) 第二节实战
4.3.4 NDI 授权 试用版 30 分钟,编/解码二选一 授权约束
4.4.2 视频叠加 主/副源可配 NDI NDI 参与画中画
6.2 手势识别 Three 手势 → NDI 输入源 手势切到 NDI
7.2 第 5 条 编解码互斥+30 分钟时效 实现约束

两个核心事实必须记住:

  1. 编码输出(NDI|HX2):设备把"统一画布"经 NDI SDK 压成 HX2 发出,NDI TOOLS 里搜到自定义名称的源(4.3.3 编码配置 + 图 4-16)。
  2. 解码输入(HX2/FULL):点搜索→选源→开开关,拉进来的是 YCbCr 满帧,进 RGA→NV12→统一画布(4.3.3 解码配置 + 图 4-19)。

三、RK3576 上 NDI|HX2 编码输出实现

3.1 链路定位

复制代码
统一画布 NV12 (dmabuf)
      │  RGA:NV12 → UYVY(4:2:2,stride 对齐)
      ▼
NDIlib_send_send_video_v2()   ← NDI SDK 内部做 HX2(HEVC) 压缩 + UDP 传输
      │
      ▼
局域网 → NDI TOOLS / 其他 NDI 接收端

关键点:这条链和 RTMP/MP4 的 MPP 编码链共享同一张画布,只是出口不同。这正是第七篇"编码契约"的好处------解码只做一次、RGA 合成只做一次,NDI 输出零额外编码负担(NDI SDK 自己压,但源帧是现成的)。

3.2 完整代码 ndi_hx2_sender.c(约 180 行)

c 复制代码
/*
 * ndi_hx2_sender.c ------ MEDAI-V1 上把统一画布以 NDI|HX2 编码输出
 * 编译: gcc ndi_hx2_sender.c -o ndi_hx2_sender -lndi -lpthread -O2
 * 说明: 真实工程中 NV12 帧来自 RGA/MPP 的 dmabuf;此处用 malloc 模拟一张灰场,
 *       并演示 NV12→UYVY 转换与逐帧发送主循环。
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <Processing.NDI.Lib.h>

/* 编码契约:与说明书 3.3/3.4 对齐的固定输出规格 */
#define OUT_W       1920
#define OUT_H       1080
#define FPS_NUM     60
#define FPS_DEN     1
#define NDI_NAME    "MEDAI-V1 (Studio-A)"

/* NV12 → UYVY(4:2:2)就地/外置转换:RGA 一条 imcvtcolor 即可完成,
 * 这里给出 CPU 参考实现,便于没有 RGA 的环境验证逻辑。 */
static void nv12_to_uyvy(const uint8_t *nv12, uint8_t *uyvy,
                          int w, int h, int y_stride, int uv_stride)
{
    const uint8_t *y = nv12;
    const uint8_t *uv = nv12 + y_stride * h;
    for (int j = 0; j < h; j++) {
        const uint8_t *yrow = y + j * y_stride;
        const uint8_t *uvrow = uv + (j / 2) * uv_stride;
        uint8_t *o = uyvy + j * w * 2;
        for (int i = 0; i < w; i += 2) {
            uint8_t u = uvrow[(i / 2) * 2 + 0];
            uint8_t v = uvrow[(i / 2) * 2 + 1];
            o[0] = u; o[1] = yrow[i];     /* U Y0 */
            o[2] = v; o[3] = yrow[i + 1]; /* V Y1 */
            o += 4;
        }
    }
}

int main(void)
{
    if (!NDIlib_initialize()) {
        fprintf(stderr, "NDIlib_initialize failed\n");
        return -1;
    }

    /* 1) 创建发送端,名称会出现在 NDI TOOLS 里(对应 4.3.3 编码配置自定义名) */
    NDIlib_send_create_t snd_desc;
    snd_desc.p_ndi_name = NDI_NAME;
    snd_desc.p_groups   = NULL;          /* 不分组,全网可见 */
    snd_desc.clock_video = false;
    snd_desc.clock_audio = false;
    const NDIlib_send_instance_t p_send = NDIlib_send_create(&snd_desc);
    if (!p_send) { fprintf(stderr, "send create failed\n"); return -1; }

    /* 2) 分配 NV12 源帧(真实用 RGA 输出的 dmabuf 映射地址) */
    int y_stride = (OUT_W + 15) & ~15;          /* 16 字节对齐,见第三篇 MPP stride */
    int uv_stride = y_stride;
    uint8_t *nv12 = (uint8_t *)malloc(y_stride * OUT_H + uv_stride * (OUT_H / 2));
    uint8_t *uyvy = (uint8_t *)malloc((size_t)OUT_W * OUT_H * 2);
    /* 填一张中度灰场(Y=128, U/V=128,不是 memset 0 ------ 第六篇强调过的坑) */
    memset(nv12, 128, y_stride * OUT_H + uv_stride * (OUT_H / 2));

    NDIlib_video_frame_v2_t vf;
    memset(&vf, 0, sizeof(vf));
    vf.xres = OUT_W; vf.yres = OUT_H;
    vf.FourCC = NDIlib_FourCC_type_UYVY;
    vf.frame_rate_N = FPS_NUM; vf.frame_rate_D = FPS_DEN;
    vf.picture_aspect_ratio = (float)OUT_W / (float)OUT_H;
    vf.line_stride_in_bytes = OUT_W * 2;       /* UYVY 每像素 2 字节 */
    vf.p_data = uyvy;

    /* 3) 逐帧发送主循环:NDI SDK 内部完成 HX2(HEVC) 压缩 + UDP 多播传输 */
    printf("NDI|HX2 sender '%s' running @ %dx%d %d/%dfps\n",
           NDI_NAME, OUT_W, OUT_H, FPS_NUM, FPS_DEN);
    const int frame_us = 1000000 * FPS_DEN / FPS_NUM;
    for (int f = 0; f < 1800; f++) {            /* 演示 30 秒(试用版够用) */
        /* 真实场景:从这里取 RGA 合成好的 NV12 dmabuf,转换后发送 */
        nv12_to_uyvy(nv12, uyvy, OUT_W, OUT_H, y_stride, uv_stride);
        NDIlib_send_send_video_v2(p_send, &vf);
        usleep(frame_us);
    }

    /* 4) 资源释放 */
    free(nv12); free(uyvy);
    NDIlib_send_destroy(p_send);
    NDIlib_destroy();
    printf("NDI|HX2 sender stopped\n");
    return 0;
}

7 条代码要点:

  1. NDIlib_initialize() 必须先调,内部启动发现/网络线程;
  2. p_ndi_name 即说明书 4.3.3 里"NDI 名称可自定义配置"对应的字符串;
  3. 给 NDI 的帧用 UYVY 4:2:2 最省事,RGA imcvtcolor(NV12→UYVY) 一步到位,避免 RGB 二次转换;
  4. stride 对齐:NV12 源按 16 字节对齐(与 MPP 的 prep 对齐一致),否则接收端花屏;
  5. NDIlib_send_send_video_v2 是非阻塞的,NDI SDK 自己管理内部缓冲与节奏,你只负责按真实帧率喂帧;
  6. 真实生产环境帧数据来自 RGA 合成后的 dmabuf 映射地址,无需 CPU 拷贝(零拷贝见第七篇);
  7. HX2 压缩由 SDK 完成,设备侧不调用 MPP------这就是"同画布双出口"的代价:几乎零额外 CPU。

四、RK3576 上 NDI|HX2 / FULL 输入解码实现

4.1 链路定位(两种变种殊途同归)

复制代码
NDI 网络包
   │  NDI SDK 内部解压(HX2→HEVC 解;FULL→SpeedHQ 解)
   ▼
NDIlib_recv_capture_v3() 得到 UYVY/BGRA 满帧
   │  RGA:UYVY/BGRA → NV12(stride + 偶数对齐)
   ▼
统一画布 NV12 (dmabuf) → 编码/HDMI/叠加/手势

关键区别 vs 网络拉流(NET1/NET2) :NET 流拉进来是 H.264/H.265,要走 MPP 硬解 (第四篇);而 NDI 流由 NDI SDK 自己解完 ,给到你的是已解码的 YCbCr 帧,设备只需 RGA 转 NV12 即可,不占 VPU。这也是为什么 NDI 解码和 MPP 解码是两套独立链路。

4.2 完整代码 ndi_receiver.c(约 170 行)

c 复制代码
/*
 * ndi_receiver.c ------ MEDAI-V1 上拉取局域网 NDI|HX2 或 NDI|FULL 源并接入统一画布
 * 编译: gcc ndi_receiver.c -o ndi_receiver -lndi -lpthread -O2
 * 说明: 演示 发现→选源→连接→逐帧收→UYVY 转 NV12。FULL/HX2 由 SDK 自动适配,
 *       用户代码无需区分,收到都是解码后的 YCbCr 帧。
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <Processing.NDI.Lib.h>

#define CANVAS_W 1920
#define CANVAS_H 1080

/* UYVY → NV12(落进统一画布,供 MPP/HDMI/叠加消费) */
static void uyvy_to_nv12(const uint8_t *uyvy, uint8_t *nv12,
                          int w, int h, int uv_stride)
{
    const uint8_t *y  = nv12;
    uint8_t *uv = nv12 + ((w + 15) & ~15) * h;
    for (int j = 0; j < h; j++) {
        const uint8_t *row = uyvy + j * w * 2;
        uint8_t *yr = (uint8_t *)y + j * ((w + 15) & ~15);
        uint8_t *uvr = uv + (j / 2) * uv_stride;
        for (int i = 0; i < w; i++) {
            yr[i] = row[i * 2 + 1];               /* Y */
            if ((j & 1) == 0 && (i & 1) == 0) {    /* 4:2:0 抽点 */
                uvr[(i / 2) * 2 + 0] = row[i * 2 + 0];  /* U */
                uvr[(i / 2) * 2 + 1] = row[i * 2 + 2];  /* V */
            }
        }
    }
}

int main(void)
{
    if (!NDIlib_initialize()) return -1;

    /* 1) 发现:等价于说明书 4.3.3 "点击搜索按键" */
    const NDIlib_find_create_t find_desc = { .show_local_sources = true,
                                             .p_groups = NULL };
    const NDIlib_find_instance_t p_find = NDIlib_find_create_v2(&find_desc, false);
    if (!p_find) return -1;

    printf("searching NDI sources...\n");
    const NDIlib_source_t *sources = NULL;
    uint32_t count = 0;
    for (int t = 0; t < 20 && count == 0; t++) {       /* 最多等 2 秒 */
        sources = NDIlib_find_get_current_sources(p_find, &count);
        usleep(100000);
    }
    if (count == 0) { fprintf(stderr, "no NDI source found\n"); return -1; }
    printf("found %u source(s):\n", count);
    for (uint32_t i = 0; i < count; i++)
        printf("  [%u] %s\n", i, sources[i].p_ndi_name);

    /* 2) 选第一个源连接(真实 UI:用户从列表选,对应 4.3.3 解码配置选源) */
    const NDIlib_recv_create_v3_t recv_desc = {
        .source_to_connect_to = sources[0],
        .color_format = NDIlib_recv_color_format_best_streaming_quality, /* 收最高质量 */
        .bandwidth = NDIlib_recv_bandwidth_highest,
        .allow_video_fields = false,
    };
    const NDIlib_recv_instance_t p_recv = NDIlib_recv_create_v3(&recv_desc);
    if (!p_recv) return -1;

    /* 3) 分配统一画布 NV12(真实用 RGA 输出 dmabuf) */
    int nv12_y_stride = (CANVAS_W + 15) & ~15;
    int nv12_uv_stride = nv12_y_stride;
    uint8_t *nv12 = (uint8_t *)malloc(nv12_y_stride * CANVAS_H +
                                     nv12_uv_stride * (CANVAS_H / 2));

    /* 4) 逐帧接收主循环:HX2/FULL 由 SDK 自动解,给到的是已解码 YCbCr 帧 */
    NDIlib_video_frame_v2_t video;
    NDIlib_audio_frame_v3_t audio;
    for (int f = 0; f < 1800; f++) {
        if (NDIlib_recv_capture_v3(p_recv, &video, &audio, NULL, 100) ==
            NDIlib_recv_frame_type_video) {
            /* video.FourCC 可能是 UYVY 或 BGRA,这里按 UYVY 处理演示 */
            if (video.FourCC == NDIlib_FourCC_type_UYVY) {
                uyvy_to_nv12((const uint8_t *)video.p_data, nv12,
                             video.xres, video.yres, nv12_uv_stride);
                /* 真实:把 nv12 交给统一画布 → MPP/HDMI/叠加/手势 */
            }
            NDIlib_recv_free_video_v2(p_recv, &video);
        }
    }

    free(nv12);
    NDIlib_recv_destroy(p_recv);
    NDIlib_find_destroy(p_find);
    NDIlib_destroy();
    printf("NDI receiver stopped\n");
    return 0;
}

6 条代码要点:

  1. NDIlib_find_get_current_sources 返回的 count 就是说明书"搜索到的 NDI 信号源数量";
  2. color_format = best_streaming_quality 让 SDK 在 HX2 与 FULL 间自动选最稳的;
  3. HX2 还是 FULL 对用户透明 :recv_capture 出来都是解码后帧,差异只在带宽与画质;
  4. 收到的帧 FourCC 需判断(UYVY/BGRA),RGA 转 NV12 时选对应转换核;
  5. 务必 NDIlib_recv_free_video_v2 归还帧,否则 SDK 内部缓冲耗尽后丢帧;
  6. 收进来的 NV12 直接进"统一画布",后续与 HDMI/UVC/NET 源走同一套 RGA 合成(第六/七篇)。

五、FULL vs HX2 自适应选择策略

设备作为接收端时,可以根据网络质量在两者间取舍:

条件 选 FULL 选 HX2
同千兆交换机、源机近 ✅ 画质无损 也可
跨网段 / 无线 / 公网桥 ❌ 带宽爆 ✅ 低带宽
要求极致画质(演播室) ✅
RK3576 内存带宽吃紧 ✅ 解后帧更小

代码上用 bandwidth 字段控制:NDIlib_recv_bandwidth_highest 收 FULL,_lowest 收 HX2。说明书不暴露这个开关(默认由 SDK 协商),但底层就是这个机制。


六、深度结合说明书实战

6.1 把设备当 NDI 编码器输出(上集)

对照 4.3.3 编码配置 + 4.3.4 授权:

  1. 进 USB 与 NDI 功能 → NDI 配置 → 编码配置;
  2. NDI 名称 填自定义(如 MEDAI-V1),开 NDI 编码开关;
  3. 在另一台机器的 NDI TOOLS / Studio Monitor 里搜索,应看到该名称(图 4-16);
  4. 拉进来看画面,即本文第三节的 ndi_hx2_sender 在跑;
  5. ⚠️ 试用版 30 分钟时效 (4.3.4):超时后 NDI TOOLS 出现"只有声音没有画面 "------重新开关编码开关即可续 30 分钟;正式环境需 开启授权(出厂均无授权)。

6.2 把设备当 NDI 解码器输入(下拉)

对照 4.3.3 解码配置:

  1. 进 NDI 配置 → 解码配置 ,点 搜索;
  2. 页面提示"搜索到的 NDI 信号源数量"(= get_current_sources 的 count);
  3. 从下拉选源,开 启用开关;
  4. 下方出现预览画面(图 4-19),即本文第四节的 ndi_receiver 在跑,收进来的帧已转 NV12 进画布;
  5. 此时在 3.2.2 视频输出 把主源选 NDI,HDMI/推流/录制就都用这路 NDI 输入了。

互斥铁律(7.2 第 5 条 + 4.3.4) :一台设备 NDI 编码与解码只能开一个 。底层原因:试用授权是单实例锁,且 VPU/网络资源在同一时刻只服务一个方向。代码上不要同时 send_create 和 recv_create。

6.3 NDI 参与视频叠加(4.4.2)

叠加 Mode1~5 的 主/副输入源 均可选 NDI(4.4.2 参数表)。一旦选 NDI:

  • 主源=NDI:整帧用 NDI 输入,RGA 把 NV12 画布按 Mode 布局;
  • 副源=NDI:NDI 收进来的小窗以 imcomposite 叠加到主源上(第六篇 RGA 适配的画中画)。

切换模式要"先应用参数,再重新开关启用开关"(4.4.2 注意)------本质是让 RGA 重算布局几何(第六篇 6.2 的对照表)。

6.4 手势切到 NDI(6.2)

Three 手势 → NDI 输入源。实现上:手势识别线程(6.2,置信度 50~100% 默认 80%)命中 Three 后,把"视频输出主源"从当前源切到 NDI,等价于调用 3.2.2 的切换 + 拉起 NDI 接收。切换后 等 5 秒 才能再次识别(7.2 第 8 条)。

6.5 状态页核对(3.1)

系统状态主页的 NDI 行显示"连接状态及接入分辨率";预监状态区可同时看到 NDI 源画面(图 3-3/3-5)。这是验证 NDI 收发是否正常的第一现场。


七、带宽与网络规划

NDI 是局域网协议,网络规划直接决定体验:

组网 建议 容量(千兆)
单机 NDI 输出 接 ETH0(DHCP) 或 ETH1(192.168.78.100) 任一 出 1~3 路 HX2
NDI 输入 + 推流并存 NDI 走 ETH0,RTMP 5 路走 ETH1(2.3.3 共存) 互不抢带宽
多机位 NDI 矩阵 独立千兆交换机,禁用 WiFi 承载 FULL 4~6 路 FULL / 20+ 路 HX2
跨网段/无线 强制 HX2(低带宽) 视链路

设备双网口(ETH0/ETH1)+ WiFi 三网共存(2.3.3)在 NDI 场景很有用:NDI 发现/传输走局域网 ETH0,对外 RTMP 推流走 ETH1 或 WiFi,避免 NDI 的 250 Mbps FULL 流量把上行推流挤垮。


八、FAQ

Q1:NDI TOOLS 搜不到设备名?

A:确认搜索端与被搜端在同一二层(mDNS 不过路由);设备 ETH1=192.168.78.100,电脑需同网段(如 192.168.78.50,见 2.1)。

Q2:搜到了但只有声音没画面?

A:典型 试用版 30 分钟到期(4.3.4)。重新开关 NDI 编码或解码开关即可续 30 分钟;正式需授权。

Q3:编码和拉流能同时开吗?

A:不能(7.2 第 5 条)。一台设备 NDI 编码/解码二选一。

Q4:收 FULL 很卡,收 HX2 正常?

A:FULL 1080p60 约 250 Mbps,网口/交换机组带宽不够。换 HX2 或升级到独立千兆交换机。

Q5:NDI 输入能进绿幕/手势吗?

A:绿幕主源固定 UVC(4.4.3);手势 Three 可切到 NDI(6.2);NDI 可作为叠加主/副源(4.4.2)。

Q6:NDI 输出和 RTMP 推流冲突吗?

A:不冲突。NDI 输出走 NDI SDK,RTMP 走 librtmp,两者共享统一画布但出口独立(第九篇)。

Q7:为什么 NDI 解码不占 VPU?

A:NDI SDK 自己解完给 YCbCr 满帧,设备只用 RGA 转 NV12(第四节);而 NET 网络流是 H.264/HEVC,需 MPP 硬解(第四篇)。

Q8:4K NDI 源能接吗?

A:FULL 4K 约 1 Gbps,需万兆或降为 HX2(4K HX2 ≈ 25~80 Mbps)。设备编码输出固定 2160P/1080P/720P(3.3),NDI 输出受此约束。


九、结语

NDI 在 MEDAI-V1 上扮演"局域网实时矩阵 "角色:编码侧用 NDI|HX2 (HEVC 底子)把统一画布低带宽地广播出去,解码侧把 HX2/FULL 信号拉进来、由 NDI SDK 解完、RGA 转 NV12 汇入统一画布------全程不碰 MPP 的 RTMP/MP4 编码链,真正实现"一源多出口、多源一画布"。配合双网口共存、视频叠加、手势切源,NDI 让这台 4GB/32GB 的小盒子在演播室与多机位直播里有了专业级路由能力。

至此本专栏已覆盖:拉流 / 绿幕 / MPP 编解码 / UVC 解码 / 综合性能 / RGA 适配 / MPP×RGA 统一编码 / FFmpeg 拉流 / librtmp 推流 / V4L2-UVC / ALSA 音频 / RGA+DRM HDMI 输出 / NDI 框架,构成从采集→处理→编码→分发→呈现的完整 RK3576 音视频技术体系。

------ 本文档深度结合《MEDAI-V1 使用说明书》V1.0 编写,参数以说明书与《MEDAI V1 技术参数》为准。

相关推荐
铅笔小新z2 小时前
【stm32】RTC(实时时钟)
stm32·单片机·实时音视频
TK泰妞3 小时前
AI 带货视频工具怎么选?电商短视频创作者的实用评估指南
人工智能·音视频
EasyDSS3 小时前
告别单一场景局限!一套视频会议/直播/点播/集群对讲EasyDSS音视频平台四大功能解析
音视频·媒体·easydss
龙腾-虎跃1 天前
基于 SIP 协议的自研电话终端:FreeSWITCH 语音与视频通信实战
android·音视频·freeswitch·sip协议终端·wertc
2601_965619691 天前
AI视频制作:财经数据可视化视频怎么做:AI 自动生成图表动画的实操流程
人工智能·nginx·音视频
2601_965619691 天前
历史解说视频怎么做:青铜器瓷器文物画面自动对齐的完整教程
运维·nginx·音视频
2601_965454231 天前
AI视频制作:券商研报怎么转口播视频:PDF 解读、配音和图表自动成片的方法
人工智能·pdf·音视频
Latchh1 天前
iLovePDF免费版能批量压缩吗?4款替代对比2026
图像处理·人工智能·计算机视觉·音视频
goujunwe2 天前
短视频怎么做 GEO:优化字幕和文稿,提升 AI 文本抓取采信率
大数据·人工智能·音视频