一、背景
手机设备有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;
2、对各种网络类型链接进行优先级排序
可以根据实际应用场景进行适配,一般用户对流量比较敏感,Cellular优先级需要低于wifi。
3、根据TWCC探测网络的情况,动态切换ICE链路
- 根据TWCC可以实时感知网络丢包、抖动、瓶颈带宽。可以根据这些参数判断网络是否需要切换,尝试新路径。
- 在切换前,发送probe报文,比对各个链路的网络情况。
- 需要注意不要仅仅凭借丢包的情况就切换网络,因为无线信号会受到其他信号干扰传输异常造成丢包。这时候只要判断出瓶颈带宽足够,根据RTT延时参数,动态选择加FEC或者NACK重传就好。