[关闭]
@leaveye 2025-09-25T04:04:49.000000Z 字数 14023 阅读 128

问题记录与信息同步

debug ss528 net udp rtp


RTP 发送断流问题

改写的发送线程

  1. // git-version: da48d0c
  2. class RtpWriterIo : public ... {
  3. // 仅列举相关成员
  4. mutex _fifo_swap_mutex{};
  5. shared_ptr<list<BufferPtr>> _fifo_swap{}; // to swap from user thread to worker thread
  6. atomic_bool _worker_alive{};
  7. shared_ptr<thread> _worker_thread{};
  8. // debug only members
  9. struct {
  10. atomic_uint swap_in{};
  11. atomic_uint swap_out{};
  12. atomic_uint pending_count{};
  13. atomic_uint pending_size{};
  14. ...
  15. } watchdog_checkpoint{}; // 简化发送线程里用作 metrics
  16. }
  17. constexpr uint64_t MAX_GAP = 800;
  18. constexpr uint64_t TARGET_PPMS = 2;
  19. uint64_t now_ms(loop *loop) {
  20. (void) loop;
  21. // ::timeval tv{};
  22. // gettimeofday(&tv, nullptr);
  23. // return static_cast<uint64_t>((int64_t) tv.tv_sec * 1000) + static_cast<uint64_t>(tv.tv_usec) / 1000;
  24. using namespace chrono;
  25. return static_cast<uint64_t>(duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count());
  26. // loop->update();
  27. // return loop->now().count();
  28. }
  29. void RtpWriterIo::open(const Config &config, const SockAddr4 &peer_addr, const SockAddr4 &bind_addr) {
  30. ... // 参数载入,必要资源初始化
  31. if (simple_writer) { // 在简化发送线程的逻辑里,simple_writer = true
  32. _worker_alive = true;
  33. _worker_thread = std::make_shared<thread>([this]() { main(); }); // 发送线程初始化
  34. io.update_timer = get_resource<timer_handle>(L);
  35. io.update_timer->on<timer_event>([&](timer_event &, timer_handle &h) {
  36. char message[0x80] = "", *p = message, *q = (&message)[1];
  37. const auto &metrics = io.watchdog_checkpoint;
  38. p += snprintf(p, q - p, " %u>swap>%u", metrics.swap_in.load(), metrics.swap_out.load());
  39. p += snprintf(p, q - p, " pending %u/%u", metrics.pending_size.load(), metrics.pending_count.load());
  40. RX_MDEBUG(io.logger, "%s: ... wakeup @%lu ...%s", io.description.c_str(), now_ms(&h.parent()), message);
  41. });
  42. io.update_timer->start(timer_handle::time{1000}, timer_handle::time{200});
  43. return;
  44. }
  45. ... // 旧逻辑的初始化代码
  46. }
  47. void RtpWriterIo::main() {
  48. static const auto make_socket = [](const RtpWriterIo &io) -> int {
  49. const auto m = io.logger;
  50. const auto hint = io.description.c_str();
  51. const auto fd = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
  52. if (fd < 0) {
  53. RX_XMFATAL(EXIT_FAILURE, m, "%s: create socket failed: %d %s", hint, errno, strerror(errno));
  54. }
  55. const int on = 1;
  56. if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
  57. RX_MWARN(m, "%s: setsockopt(SO_REUSEADDR) failed: %d %s", hint, errno, strerror(errno));
  58. }
  59. if (bind(fd, reinterpret_cast<const sockaddr*>(&io.local), sizeof(io.local)) < 0) {
  60. ::close(fd);
  61. RX_XMFATAL(EXIT_FAILURE, m, "%s: bind(%s) failed: %d %s", hint, to_string(io.local).c_str(), errno, strerror(errno));
  62. }
  63. if (is_multicast(io.peer)) {
  64. ip_mreq mreq{};
  65. mreq.imr_multiaddr = io.peer.sin_addr;
  66. mreq.imr_interface = io.local.sin_addr;
  67. if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
  68. RX_MWARN(m, "%s: setsockopt(IP_ADD_MEMBERSHIP) failed: %d %s", hint, errno, strerror(errno));
  69. }
  70. const int loop = 0;
  71. if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) < 0) {
  72. RX_MWARN(m, "%s: setsockopt(IP_MULTICAST_LOOP) failed: %d %s", hint, errno, strerror(errno));
  73. }
  74. const int ttl = 64;
  75. if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
  76. RX_MWARN(m, "%s: setsockopt(IP_MULTICAST_TTL) failed: %d %s", hint, errno, strerror(errno));
  77. }
  78. }
  79. RX_MNOTICE(m, "%s: socket ready: %s", hint, to_string(io.peer).c_str());
  80. return fd;
  81. };
  82. const unique_ptr<int, void(*)(int*)> fd_closer(new int, [](int *p) { ::close(*p); delete p; });
  83. int fd = *fd_closer = make_socket(*this);
  84. const auto direct_sender = [this, &fd](BufferPtr &&buf) -> int {
  85. const auto rv = sendto(fd, buf->buf(), buf->len(), MSG_DONTWAIT, reinterpret_cast<const sockaddr*>(&peer), sizeof(peer));
  86. if (rv < 0) return -errno;
  87. return rv;
  88. };
  89. const auto desc = description.c_str();
  90. RX_INFO("%s: sender loop enter", desc);
  91. while (_worker_alive) {
  92. ts_updated = loop->now().count();
  93. const auto no_gap = iter(direct_sender);
  94. if (no_gap) continue;
  95. usleep(1000);
  96. }
  97. RX_INFO("%s: sender loop exit", desc);
  98. }
  99. bool RtpWriterIo::iter(const function<int(BufferPtr&&)> &sender) {
  100. const auto desc = description.c_str();
  101. auto &metrics = watchdog_checkpoint;
  102. thread_local uint max_packet_size = 0;
  103. auto no_gap = false;
  104. decltype(_fifo_swap) q_swap{};
  105. if (_fifo_swap) {
  106. auto guard = make_shared<lock_guard<mutex>>(_fifo_swap_mutex);
  107. q_swap = std::move(_fifo_swap);
  108. }
  109. auto &q_pending = fifo_pending;
  110. if (!q_swap || q_swap->empty()) {
  111. metrics.swap_out = 0;
  112. } else {
  113. no_gap = true;
  114. for (const auto &buf : *q_swap) {
  115. if (max_packet_size < buf->len())
  116. max_packet_size = buf->len();
  117. }
  118. const auto old_pending_count = q_pending.size();
  119. q_pending.splice(q_pending.end(), *std::move(q_swap));
  120. RX_MDEBUG(logger, "%s: swap in %zu packets, pending %zu", desc, q_pending.size() - old_pending_count, q_pending.size());
  121. metrics.swap_out = q_pending.size() - old_pending_count;
  122. // packets got, to check overflow
  123. }
  124. const auto ts_send_0 = now_ms(loop.get());
  125. thread_local uint64_t ts_send_last = 0;
  126. const auto ts_send_interval = !ts_send_last ? MAX_GAP / 4 : std::min(ts_send_0 - ts_send_last, MAX_GAP);
  127. auto want_write_bytes = max_packet_size * TARGET_PPMS * ts_send_interval;
  128. ts_send_last = ts_send_0;
  129. int rv = 0, n = 0, err = 0, sz = 0;
  130. while (!q_pending.empty()) {
  131. if (enable_rate_limit && want_write_bytes < q_pending.front()->len()) {
  132. no_gap = false;
  133. break;
  134. }
  135. ++n, no_gap = true;
  136. BufferPtr buf = std::move(q_pending.front());
  137. q_pending.pop_front();
  138. const auto len = buf->len();
  139. rv = sender(std::move(buf));
  140. if (rv < 0) {
  141. ++err;
  142. RX_MERROR(logger, "%s: send packet failed: %d", desc, rv);
  143. if (rv == -EAGAIN) no_gap = false;
  144. } else {
  145. sz += len;
  146. want_write_bytes -= len;
  147. }
  148. }
  149. const auto ts_send_1 = now_ms(loop.get());
  150. {
  151. char result[0x80] = "", *p = result, *q = (&result)[1];
  152. if (n) {
  153. const auto ts_send_elapsed = ts_send_1 - ts_send_0;
  154. p += snprintf(p, q - p, " in %lums (%lu~%lu)", ts_send_elapsed, ts_send_0, ts_send_1);
  155. if (err && n > err) p += snprintf(p, q - p, " ok %d", n - err);
  156. if (n > err) p += snprintf(p, q - p, " (%d bytes)", sz);
  157. if (err) p += snprintf(p, q - p, " error %d", err);
  158. if (!q_pending.empty()) p += snprintf(p, q - p, " pending %zu", q_pending.size());
  159. RX_MDEBUG(logger, "%s: sent %d %s%s", desc, n, n == 1 ? "packet" : "packets", result);
  160. } else if (q_pending.empty()) {
  161. RX_MDEBUG(logger, "%s: nothing to send", desc);
  162. } else {
  163. RX_MDEBUG(logger, "%s: skip send for rate control", desc);
  164. }
  165. }
  166. return no_gap;
  167. }
  168. void RtpStream::stopWriter() {
  169. auto &io = writer_;
  170. if (io.simple_writer) {
  171. io._worker_alive = false;
  172. std::move(io._worker_thread)->join();
  173. rtp_payload_encode_destroy(io.media); io.media = {};
  174. return;
  175. } else ... // 旧逻辑的释放代码
  176. }

断网延时增加问题

通过 netstate 查看发送缓存

接收侧 tcpdump 查看抓包样本。得到发送端的端口:v=39563, a=46657

  1. 20:41:22.269050 IP 192.168.80.60.39563 > 192.168.80.70.6666: UDP, length 1170
  2. 20:41:22.285755 IP 192.168.80.60.39563 > 192.168.80.70.6666: UDP, length 1400
  3. 20:41:22.285794 IP 192.168.80.60.39563 > 192.168.80.70.6666: UDP, length 1400
  4. 20:41:22.285803 IP 192.168.80.60.39563 > 192.168.80.70.6666: UDP, length 1400
  5. 20:41:22.285811 IP 192.168.80.60.39563 > 192.168.80.70.6666: UDP, length 90
  6. 20:41:22.287878 IP 192.168.80.60.46657 > 192.168.80.70.9660: UDP, length 507
  7. 20:41:22.302380 IP 192.168.80.60.39563 > 192.168.80.70.6666: UDP, length 1400
  8. 20:41:22.302415 IP 192.168.80.60.39563 > 192.168.80.70.6666: UDP, length 1400
  9. 20:41:22.302424 IP 192.168.80.60.39563 > 192.168.80.70.6666: UDP, length 1365
  10. 20:41:22.308916 IP 192.168.80.60.46657 > 192.168.80.70.9660: UDP, length 522

发送侧通过 netstate 看到的发送缓存时有累积。

  1. >>>> 此时已经拔掉了 70 的网线
  2. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  3. 03:49:29> udp 0 0 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  4. 03:49:29> udp 0 0 192.168.80.60:46657 0.0.0.0:* 1875/transfer
  5. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  6. 03:49:30> udp 0 0 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  7. 03:49:30> udp 0 0 192.168.80.60:46657 0.0.0.0:* 1875/transfer
  8. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  9. 03:49:30> udp 0 0 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  10. 03:49:30> udp 0 0 192.168.80.60:46657 0.0.0.0:* 1875/transfer
  11. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  12. 03:49:30> udp 0 2304 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  13. 03:49:30> udp 0 0 192.168.80.60:46657 0.0.0.0:* 1875/transfer
  14. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  15. 03:49:30> udp 0 0 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  16. 03:49:30> udp 0 0 192.168.80.60:46657 0.0.0.0:* 1875/transfer
  17. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  18. 03:49:30> udp 0 0 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  19. 03:49:30> udp 0 0 192.168.80.60:46657 0.0.0.0:* 1875/transfer
  20. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  21. 03:49:31> udp 0 0 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  22. 03:49:31> udp 0 0 192.168.80.60:46657 0.0.0.0:* 1875/transfer
  23. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  24. 03:49:31> udp 0 0 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  25. 03:49:31> udp 0 0 192.168.80.60:46657 0.0.0.0:* 1875/transfer
  26. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  27. 03:49:31> udp 0 0 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  28. 03:49:31> udp 0 0 192.168.80.60:46657 0.0.0.0:* 1875/transfer
  29. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  30. 03:49:31> udp 0 0 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  31. 03:49:31> udp 0 1280 192.168.80.60:46657 0.0.0.0:* 1875/transfer
  32. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  33. 03:49:32> udp 0 0 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  34. 03:49:32> udp 0 0 192.168.80.60:46657 0.0.0.0:* 1875/transfer
  35. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  36. 03:49:32> udp 0 0 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  37. 03:49:32> udp 0 0 192.168.80.60:46657 0.0.0.0:* 1875/transfer
  38. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  39. 03:49:32> udp 0 0 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  40. 03:49:32> udp 0 0 192.168.80.60:46657 0.0.0.0:* 1875/transfer
  41. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  42. 03:49:32> udp 0 0 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  43. 03:49:32> udp 0 0 192.168.80.60:46657 0.0.0.0:* 1875/transfer
  44. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  45. 03:49:32> udp 0 0 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  46. 03:49:32> udp 0 0 192.168.80.60:46657 0.0.0.0:* 1875/transfer
  47. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  48. 03:49:33> udp 0 4608 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  49. 03:49:33> udp 0 0 192.168.80.60:46657 0.0.0.0:* 1875/transfer
  50. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  51. 03:49:33> udp 0 0 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  52. 03:49:33> udp 0 0 192.168.80.60:46657 0.0.0.0:* 1875/transfer
  53. >>>> 此时插上了 70 的网线
  54. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  55. 03:49:36> udp 0 0 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  56. 03:49:36> udp 0 0 192.168.80.60:46657 0.0.0.0:* 1875/transfer
  57. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  58. 03:49:36> udp 0 0 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  59. 03:49:36> udp 0 0 192.168.80.60:46657 0.0.0.0:* 1875/transfer
  60. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  61. 03:49:36> udp 0 0 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  62. 03:49:36> udp 0 0 192.168.80.60:46657 0.0.0.0:* 1875/transfer
  63. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  64. 03:49:36> udp 0 0 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  65. 03:49:36> udp 0 0 192.168.80.60:46657 0.0.0.0:* 1875/transfer
  66. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  67. 03:49:37> udp 0 0 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  68. 03:49:37> udp 0 0 192.168.80.60:46657 0.0.0.0:* 1875/transfer
  69. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  70. 03:49:37> udp 0 0 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  71. 03:49:37> udp 0 0 192.168.80.60:46657 0.0.0.0:* 1875/transfer
  72. ~ # netstat -aup 2>/dev/null | sed -n "/:\(39563\|46657\)/s|^|`date +%T`> |p"
  73. 03:49:37> udp 0 0 192.168.80.60:39563 0.0.0.0:* 1875/transfer
  74. 03:49:37> udp 0 0 192.168.80.60:46657 0.0.0.0:* 1875/transfer

丢包问题

UDP 配置

缺省状态下:

  1. ~ # ulimit
  2. unlimited
  3. ~ # grep ^ /proc/sys/net/core/rmem_*
  4. /proc/sys/net/core/rmem_default:212992
  5. /proc/sys/net/core/rmem_max:212992
  6. ~ # transfer # 筛选后的输出
  7. udp_socket: fd=35 bind(192.168.80.70:7804) rxbuf get=212992 set=212992 get=425984
  8. udp_socket: fd=28 bind(192.168.80.70:7804) rxbuf get=212992 set=15360000 get=425984
  9. udp_socket: fd=35 bind(192.168.80.70:7802) rxbuf get=212992 set=425984 get=425984

进行配置后:

  1. # 配置
  2. ulimit -n 999999
  3. ulimit -c unlimited
  4. (cd /proc/sys/net/core
  5. echo 4097152 >rmem_default
  6. echo 16777216 >rmem_max
  7. #echo 4097152 >wmem_default
  8. echo 16777216 >wmem_max
  9. )
  10. ~ # ulimit
  11. unlimited
  12. ~ # grep ^ /proc/sys/net/core/rmem_*
  13. /proc/sys/net/core/rmem_default:4097152
  14. /proc/sys/net/core/rmem_max:16777216
  15. ~ # transfer # 筛选后的输出,几次不同的 RCVBUFSZ 参数
  16. udp_socket: fd=28 bind(192.168.80.70:7804) rxbuf get=4097152 set=15360000 get=30720000
  17. udp_socket: fd=35 bind(192.168.80.70:7804) rxbuf get=4097152 set=30720000 get=33554432
  18. udp_socket: fd=34 bind(192.168.80.70:7804) rxbuf get=4097152 set=33554432 get=33554432

初始化代码

  1. explicit udp_socket(const socket_address &addr) : _addr(addr) {
  2. const auto fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
  3. if (fd < 0) throw c_runtime_error("socket() failed.");
  4. sockaddr_storage addr_store{};
  5. uv_ip4_addr(addr.ip.c_str(), addr.port, reinterpret_cast<sockaddr_in*>(&addr_store));
  6. if (bind(fd, reinterpret_cast<const sockaddr*>(&addr_store), sizeof(addr_store)) < 0) {
  7. const auto err = errno;
  8. close(fd);
  9. throw c_runtime_error("bind() failed.", err);
  10. }
  11. message_builder msg{};
  12. try {
  13. int rxbuf{-1};
  14. socklen_t len{};
  15. len = sizeof(rxbuf);
  16. if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rxbuf, &len) < 0)
  17. throw c_runtime_error("getsockopt() failed.");
  18. msg.putf(" get=%d", rxbuf);
  19. rxbuf = atoi(rx_loadenv(RCVBUFSZ));
  20. len = sizeof(rxbuf);
  21. if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rxbuf, len) < 0)
  22. throw c_runtime_error("setsockopt() failed.");
  23. msg.putf(" set=%d", rxbuf);
  24. len = sizeof(rxbuf);
  25. if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rxbuf, &len) < 0)
  26. throw c_runtime_error("getsockopt() failed.");
  27. msg.putf(" get=%d", rxbuf);
  28. } catch (c_runtime_error &e) {
  29. msg.putf(" %s", e.what());
  30. }
  31. RX_INFO("udp_socket: fd=%d bind(%s) rxbuf %s", fd, addr.str().c_str(), msg.str(1));
  32. _fd = fd;
  33. }

RTP 配置

Sender side

in RtpStream.cc, RtpWriterIo::open()

  1. io.udp_link = get_resource<udp_handle>(L);
  2. io.udp_link->bind(reinterpret_cast<sockaddr&>(io.local), udp_handle::udp_flags::REUSEADDR);
  3. if (is_multicast(io.peer)) {
  4. io.udp_link->multicast_membership(to_string(io.peer.sin_addr), to_string(io.local.sin_addr), udp_handle::membership::JOIN_GROUP);
  5. io.udp_link->multicast_loop(false);
  6. io.udp_link->multicast_ttl(64);
  7. }

Receiver side

in RtpStream.cc, rtp_handle::make_udp()

  1. auto udp = get_resource<udp_handle>(_loop, [local, peer](close_event &, udp_handle &h) {
  2. RX_TRACE("!!! udp %p close !!! %s <- %s", &h, local.str().c_str(), peer.str().c_str());
  3. h.reset();
  4. });
  5. RX_TRACE("!!! udp %p open !!! %s <- %s", udp.get(), local.str().c_str(), peer.str().c_str());
  6. udp->bind(local, udp_handle::udp_flags::REUSEADDR);
  7. if (is_multicast(peer.ip)) {
  8. udp->multicast_membership(peer.ip, local.ip, membership::JOIN_GROUP);
  9. }
  10. udp->recv_buffer_size(buffer_size);
  11. RX_NOTICE("udp rcvbufsz=%d want=%d arg=%d", udp->recv_buffer_size(), buffer_size, max_kbps);
  12. // 01:52:35.106004 NOTICE | udp rcvbufsz=30720000 want=15360000 arg=20000 <<XFER/RtpStream.cc:191
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注