stable version of 2110-20 RX with simple SDP parser
This commit is contained in:
+153
-6
@@ -4,6 +4,8 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
@@ -32,11 +34,136 @@ struct ST2110ReceiverConfig {
|
||||
uint8_t payload_type = 96;
|
||||
int width = 1920;
|
||||
int height = 1080;
|
||||
int depth = 8;
|
||||
int fps_num = 25;
|
||||
int fps_den = 1;
|
||||
int framebuff_cnt = 3;
|
||||
int mxl_latency_frames = 2;
|
||||
std::string mxl_index_mode = "rtp";
|
||||
|
||||
bool direct_v210() const { return depth == 10; }
|
||||
enum st20_fmt transport_fmt() const {
|
||||
return direct_v210() ? ST20_FMT_YUV_422_10BIT : ST20_FMT_YUV_422_8BIT;
|
||||
}
|
||||
enum st_frame_fmt output_fmt() const {
|
||||
return direct_v210() ? ST_FRAME_FMT_V210 : ST_FRAME_FMT_UYVY;
|
||||
}
|
||||
};
|
||||
|
||||
inline std::string st2110_trim(std::string s) {
|
||||
const auto first = s.find_first_not_of(" \t\r\n");
|
||||
if (first == std::string::npos) return {};
|
||||
const auto last = s.find_last_not_of(" \t\r\n");
|
||||
return s.substr(first, last - first + 1);
|
||||
}
|
||||
|
||||
inline bool st2110_parse_int(const std::string& text, int* out) {
|
||||
char* end = nullptr;
|
||||
const long value = std::strtol(text.c_str(), &end, 10);
|
||||
if (!end || *end != '\0') return false;
|
||||
*out = static_cast<int>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void st2110_parse_exactframerate(const std::string& value,
|
||||
ST2110ReceiverConfig& cfg) {
|
||||
const auto slash = value.find('/');
|
||||
if (slash == std::string::npos) {
|
||||
if (!st2110_parse_int(value, &cfg.fps_num)) {
|
||||
throw std::runtime_error("invalid SDP exactframerate: " + value);
|
||||
}
|
||||
cfg.fps_den = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string num = value.substr(0, slash);
|
||||
const std::string den = value.substr(slash + 1);
|
||||
if (!st2110_parse_int(num, &cfg.fps_num) || !st2110_parse_int(den, &cfg.fps_den)) {
|
||||
throw std::runtime_error("invalid SDP exactframerate: " + value);
|
||||
}
|
||||
}
|
||||
|
||||
inline void st2110_apply_fmtp_param(const std::string& key, const std::string& value,
|
||||
ST2110ReceiverConfig& cfg, std::string& sampling) {
|
||||
if (key == "width") {
|
||||
if (!st2110_parse_int(value, &cfg.width)) {
|
||||
throw std::runtime_error("invalid SDP width: " + value);
|
||||
}
|
||||
} else if (key == "height") {
|
||||
if (!st2110_parse_int(value, &cfg.height)) {
|
||||
throw std::runtime_error("invalid SDP height: " + value);
|
||||
}
|
||||
} else if (key == "depth") {
|
||||
if (!st2110_parse_int(value, &cfg.depth)) {
|
||||
throw std::runtime_error("invalid SDP depth: " + value);
|
||||
}
|
||||
} else if (key == "sampling") {
|
||||
sampling = value;
|
||||
} else if (key == "exactframerate") {
|
||||
st2110_parse_exactframerate(value, cfg);
|
||||
}
|
||||
}
|
||||
|
||||
inline void st2110_parse_fmtp(const std::string& line, ST2110ReceiverConfig& cfg,
|
||||
std::string& sampling) {
|
||||
const auto space = line.find(' ');
|
||||
if (space == std::string::npos) return;
|
||||
|
||||
std::stringstream params(line.substr(space + 1));
|
||||
std::string item;
|
||||
while (std::getline(params, item, ';')) {
|
||||
item = st2110_trim(item);
|
||||
if (item.empty()) continue;
|
||||
|
||||
const auto eq = item.find('=');
|
||||
if (eq == std::string::npos) continue;
|
||||
const std::string key = st2110_trim(item.substr(0, eq));
|
||||
const std::string value = st2110_trim(item.substr(eq + 1));
|
||||
st2110_apply_fmtp_param(key, value, cfg, sampling);
|
||||
}
|
||||
}
|
||||
|
||||
inline void st2110_apply_sdp(const std::string& sdp, ST2110ReceiverConfig& cfg) {
|
||||
std::stringstream lines(sdp);
|
||||
std::string line;
|
||||
std::string sampling = "YCbCr-4:2:2";
|
||||
|
||||
while (std::getline(lines, line)) {
|
||||
line = st2110_trim(line);
|
||||
if (line.rfind("m=video ", 0) == 0) {
|
||||
std::stringstream media(line.substr(8));
|
||||
int port = 0;
|
||||
std::string proto;
|
||||
int payload = 0;
|
||||
if (media >> port >> proto >> payload) {
|
||||
if (port < 0 || port > 65535 || payload < 0 || payload > 255) {
|
||||
throw std::runtime_error("SDP m=video port or payload out of range");
|
||||
}
|
||||
cfg.udp_port = static_cast<uint16_t>(port);
|
||||
cfg.payload_type = static_cast<uint8_t>(payload);
|
||||
}
|
||||
} else if (line.rfind("c=IN IP4 ", 0) == 0) {
|
||||
std::string addr = line.substr(9);
|
||||
const auto slash = addr.find('/');
|
||||
if (slash != std::string::npos) addr.resize(slash);
|
||||
cfg.mcast_ip = st2110_trim(addr);
|
||||
} else if (line.rfind("a=source-filter:incl IN IP4 ", 0) == 0) {
|
||||
std::stringstream filter(line.substr(28));
|
||||
std::string group;
|
||||
std::string source;
|
||||
if (filter >> group >> source) {
|
||||
cfg.source_ip = source;
|
||||
}
|
||||
} else if (line.rfind("a=fmtp:", 0) == 0) {
|
||||
st2110_parse_fmtp(line.substr(7), cfg, sampling);
|
||||
}
|
||||
}
|
||||
|
||||
if (sampling != "YCbCr-4:2:2" || (cfg.depth != 8 && cfg.depth != 10)) {
|
||||
throw std::runtime_error("only SDP YCbCr-4:2:2 depth=8 or depth=10 is supported currently");
|
||||
}
|
||||
}
|
||||
|
||||
inline void st2110_set_ip(uint8_t dst[MTL_IP_ADDR_LEN], const std::string& ip) {
|
||||
if (inet_pton(AF_INET, ip.c_str(), dst) != 1) {
|
||||
throw std::runtime_error("invalid IP address: " + ip);
|
||||
@@ -88,19 +215,33 @@ inline ST2110ReceiverConfig parse_st2110_receiver_config(const nlohmann::json& c
|
||||
out.flow_id = cfg.at("video_flow_id").at("id").get<std::string>();
|
||||
out.ifname = cfg.at("interface").get<std::string>();
|
||||
out.local_ip = cfg.at("local_ip").get<std::string>();
|
||||
out.source_ip = cfg.at("source_ip").get<std::string>();
|
||||
out.mcast_ip = cfg.at("mcast_ip").get<std::string>();
|
||||
out.udp_port = st2110_checked_u16(cfg, "udp_port");
|
||||
out.payload_type = st2110_checked_u8(cfg, "payload_type", 96);
|
||||
|
||||
if (cfg.contains("sdp")) {
|
||||
st2110_apply_sdp(cfg.at("sdp").get<std::string>(), out);
|
||||
}
|
||||
|
||||
if (cfg.contains("source_ip")) out.source_ip = cfg.at("source_ip").get<std::string>();
|
||||
if (cfg.contains("mcast_ip")) out.mcast_ip = cfg.at("mcast_ip").get<std::string>();
|
||||
if (cfg.contains("udp_port")) out.udp_port = st2110_checked_u16(cfg, "udp_port");
|
||||
out.payload_type = st2110_checked_u8(cfg, "payload_type", out.payload_type);
|
||||
out.width = cfg.value("width", out.width);
|
||||
out.height = cfg.value("height", out.height);
|
||||
out.depth = cfg.value("depth", out.depth);
|
||||
out.fps_num = cfg.value("fps_num", out.fps_num);
|
||||
out.fps_den = cfg.value("fps_den", out.fps_den);
|
||||
out.framebuff_cnt = cfg.value("framebuff_cnt", out.framebuff_cnt);
|
||||
out.mxl_latency_frames = cfg.value("mxl_latency_frames", out.mxl_latency_frames);
|
||||
out.mxl_index_mode = cfg.value("mxl_index_mode", out.mxl_index_mode);
|
||||
|
||||
if (out.width <= 0 || out.height <= 0) {
|
||||
throw std::runtime_error("width and height must be positive");
|
||||
}
|
||||
if (out.depth != 8 && out.depth != 10) {
|
||||
throw std::runtime_error("only depth=8 and depth=10 are supported currently");
|
||||
}
|
||||
if (out.source_ip.empty() || out.mcast_ip.empty() || out.udp_port == 0) {
|
||||
throw std::runtime_error("source_ip, mcast_ip and udp_port are required unless provided by sdp");
|
||||
}
|
||||
if (out.width % 6 != 0) {
|
||||
throw std::runtime_error("width must be divisible by 6 for v210 output");
|
||||
}
|
||||
@@ -110,6 +251,12 @@ inline ST2110ReceiverConfig parse_st2110_receiver_config(const nlohmann::json& c
|
||||
if (out.framebuff_cnt < 2 || out.framebuff_cnt > ST20_FB_MAX_COUNT) {
|
||||
throw std::runtime_error("framebuff_cnt must be in [2, ST20_FB_MAX_COUNT]");
|
||||
}
|
||||
if (out.mxl_latency_frames < 1 || out.mxl_latency_frames > 30) {
|
||||
throw std::runtime_error("mxl_latency_frames must be in [1, 30]");
|
||||
}
|
||||
if (out.mxl_index_mode != "rtp" && out.mxl_index_mode != "live") {
|
||||
throw std::runtime_error("mxl_index_mode must be 'rtp' or 'live'");
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
@@ -173,8 +320,8 @@ public:
|
||||
ops.height = static_cast<uint32_t>(cfg.height);
|
||||
ops.fps = st2110_to_st_fps(cfg.fps_num, cfg.fps_den);
|
||||
ops.interlaced = false;
|
||||
ops.transport_fmt = ST20_FMT_YUV_422_8BIT;
|
||||
ops.output_fmt = ST_FRAME_FMT_UYVY;
|
||||
ops.transport_fmt = cfg.transport_fmt();
|
||||
ops.output_fmt = cfg.output_fmt();
|
||||
ops.device = ST_PLUGIN_DEVICE_AUTO;
|
||||
ops.framebuff_cnt = static_cast<uint16_t>(cfg.framebuff_cnt);
|
||||
ops.flags = ST20P_RX_FLAG_BLOCK_GET;
|
||||
|
||||
Reference in New Issue
Block a user