WebRTC QoS优化(双链路笔记)

一、背景

手机设备有wifi和cellular网络,两种都是无线信号,可能由于一些距离wifi、基站覆盖等原因会出现一段时间网络变差的情况。可以考虑使用wifi和cellular智能切换,保证音视频传输的流畅性。并且可以善意提醒用户,当前wifi信号差,正在使用流量。提醒用户主动改善网络条件。

二、实现

1、ICE协商,感知Candidate网络类型 

  • 本端网络类型感知

       webrtc的rtc::Network结构体里面有网络类型参数AdapterType type_,定义如下:

enum AdapterType {
  // This enum resembles the one in Chromium net::ConnectionType.
  ADAPTER_TYPE_UNKNOWN = 0,
  ADAPTER_TYPE_ETHERNET = 1 << 0,
  ADAPTER_TYPE_WIFI = 1 << 1,
  ADAPTER_TYPE_CELLULAR = 1 << 2,  // This is CELLULAR of unknown type.
  ADAPTER_TYPE_VPN = 1 << 3,
  ADAPTER_TYPE_LOOPBACK = 1 << 4,
  // ADAPTER_TYPE_ANY is used for a network, which only contains a single "any
  // address" IP address (INADDR_ANY for IPv4 or in6addr_any for IPv6), and can
  // use any/all network interfaces. Whereas ADAPTER_TYPE_UNKNOWN is used
  // when the network uses a specific interface/IP, but its interface type can
  // not be determined or not fit in this enum.
  ADAPTER_TYPE_ANY = 1 << 5,
  ADAPTER_TYPE_CELLULAR_2G = 1 << 6,
  ADAPTER_TYPE_CELLULAR_3G = 1 << 7,
  ADAPTER_TYPE_CELLULAR_4G = 1 << 8,
  ADAPTER_TYPE_CELLULAR_5G = 1 << 9
};

这个参数的底层实现:

BasicNetworkManager::CreateNetworks------通过getifaddrs系统函数获取ifaddrs参数
->BasicNetworkManager::ConvertIfAddrs
->BasicNetworkManager::GetInterfaceInfo
->AdapterType GetAdapterTypeFromName----根据网卡名称区分网络类型

// A cautious note that this method may not provide an accurate adapter type
// based on the string matching. Incorrect type of adapters can affect the
// result of the downstream network filtering, see e.g.
// BasicPortAllocatorSession::GetNetworks when
// PORTALLOCATOR_DISABLE_COSTLY_NETWORKS is turned on.
AdapterType GetAdapterTypeFromName(absl::string_view network_name) {
  if (MatchTypeNameWithIndexPattern(network_name, "lo")) {
    // Note that we have a more robust way to determine if a network interface
    // is a loopback interface by checking the flag IFF_LOOPBACK in ifa_flags of
    // an ifaddr struct. See ConvertIfAddrs in this file.
    return ADAPTER_TYPE_LOOPBACK;
  }

  if (MatchTypeNameWithIndexPattern(network_name, "eth")) {
    return ADAPTER_TYPE_ETHERNET;
  }

  if (MatchTypeNameWithIndexPattern(network_name, "wlan") ||
      MatchTypeNameWithIndexPattern(network_name, "v4-wlan")) {
    return ADAPTER_TYPE_WIFI;
  }

  if (MatchTypeNameWithIndexPattern(network_name, "ipsec") ||
      MatchTypeNameWithIndexPattern(network_name, "tun") ||
      MatchTypeNameWithIndexPattern(network_name, "utun") ||
      MatchTypeNameWithIndexPattern(network_name, "tap")) {
    return ADAPTER_TYPE_VPN;
  }
#if defined(WEBRTC_IOS)
  // Cell networks are pdp_ipN on iOS.
  if (MatchTypeNameWithIndexPattern(network_name, "pdp_ip")) {
    return ADAPTER_TYPE_CELLULAR;
  }
  if (MatchTypeNameWithIndexPattern(network_name, "en")) {
    // This may not be most accurate because sometimes Ethernet interface
    // name also starts with "en" but it is better than showing it as
    // "unknown" type.
    // TODO(honghaiz): Write a proper IOS network manager.
    return ADAPTER_TYPE_WIFI;
  }
#elif defined(WEBRTC_ANDROID)
  if (MatchTypeNameWithIndexPattern(network_name, "rmnet") ||
      MatchTypeNameWithIndexPattern(network_name, "rmnet_data") ||
      MatchTypeNameWithIndexPattern(network_name, "v4-rmnet") ||
      MatchTypeNameWithIndexPattern(network_name, "v4-rmnet_data") ||
      MatchTypeNameWithIndexPattern(network_name, "clat") ||
      MatchTypeNameWithIndexPattern(network_name, "ccmni")) {
    return ADAPTER_TYPE_CELLULAR;
  }
#endif

  return ADAPTER_TYPE_UNKNOWN;
}
bool BasicNetworkManager::CreateNetworks(
    bool include_ignored,
    std::vector<std::unique_ptr<Network>>* networks) const {
  struct ifaddrs* interfaces;
  int error = getifaddrs(&interfaces);-----获取地址信息
  if (error != 0) {
    RTC_LOG_ERR(LS_ERROR) << "getifaddrs failed to gather interface data: "
                          << error;
    return false;
  }

  std::unique_ptr<IfAddrsConverter> ifaddrs_converter(CreateIfAddrsConverter());
  ConvertIfAddrs(interfaces, ifaddrs_converter.get(), include_ignored,
                 networks);

  freeifaddrs(interfaces);
  return true;
}
  •  远端网络类型感知

     远端网络类型目前没有标准协议定义,但是可以使用sdp协议的networkcost参数来识别。

       

 webrtc有这方面定义,可以作为参考。

constexpr uint16_t kNetworkCostMax = 999;
constexpr uint16_t kNetworkCostCellular2G = 980;
constexpr uint16_t kNetworkCostCellular3G = 910;
constexpr uint16_t kNetworkCostCellular = 900;
constexpr uint16_t kNetworkCostCellular4G = 500;
constexpr uint16_t kNetworkCostCellular5G = 250;
constexpr uint16_t kNetworkCostUnknown = 50;
constexpr uint16_t kNetworkCostLow = 10;
constexpr uint16_t kNetworkCostMin = 0;

一些draft也有这方面的建议。 draft-thatcher-ice-network-cost-01ICE Network Cost: Dynamically selecting ICE candidate pairs based on relative cost of network interfaces (Internet-Draft, 2017)https://datatracker.ietf.org/doc/html/draft-thatcher-ice-network-cost-01

 2、对各种网络类型链接进行优先级排序

      可以根据实际应用场景进行适配,一般用户对流量比较敏感,Cellular优先级需要低于wifi。

3、根据TWCC探测网络的情况,动态切换ICE链路

  1.  根据TWCC可以实时感知网络丢包、抖动、瓶颈带宽。可以根据这些参数判断网络是否需要切换,尝试新路径。
  2. 在切换前,发送probe报文,比对各个链路的网络情况。
  3. 需要注意不要仅仅凭借丢包的情况就切换网络,因为无线信号会受到其他信号干扰传输异常造成丢包。这时候只要判断出瓶颈带宽足够,根据RTT延时参数,动态选择加FEC或者NACK重传就好。

猜你喜欢

转载自blog.csdn.net/CrystalShaw/article/details/129749109