LibNiceConnectionソースコード分析

// ice接收数据回调
void cb_nice_recv(NiceAgent* agent, guint stream_id, guint component_id,
    guint len, gchar* buf, gpointer user_data) {
  if (user_data == NULL || len == 0) {
    return;
  }
  LibNiceConnection* nicecon = reinterpret_cast<LibNiceConnection*>(user_data);
  nicecon->onData(component_id, reinterpret_cast<char*> (buf), static_cast<unsigned int> (len));
}

// 有新的candidate回调
void cb_new_candidate(NiceAgent *agent, guint stream_id, guint component_id, gchar *foundation,
    gpointer user_data) {
  LibNiceConnection *conn = reinterpret_cast<LibNiceConnection*>(user_data);
  std::string found(foundation);
  conn->getCandidate(stream_id, component_id, found);
}

// 收集candidate结束
void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer user_data) {
  LibNiceConnection *conn = reinterpret_cast<LibNiceConnection*>(user_data);
  conn->gatheringDone(stream_id);
}

// ice组件状态
void cb_component_state_changed(NiceAgent *agent, guint stream_id,
    guint component_id, guint state, gpointer user_data) {
  if (state == NICE_COMPONENT_STATE_CONNECTED) {
  } else if (state == NICE_COMPONENT_STATE_FAILED) {
    LibNiceConnection *conn = reinterpret_cast<LibNiceConnection*>(user_data);
    conn->updateComponentState(component_id, IceState::FAILED);
  }
}

// 选取新的candidate对,并回调ice为ready状态
void cb_new_selected_pair(NiceAgent *agent, guint stream_id, guint component_id,
    gchar *lfoundation, gchar *rfoundation, gpointer user_data) {
  LibNiceConnection *conn = reinterpret_cast<LibNiceConnection*>(user_data);
  conn->updateComponentState(component_id, IceState::READY);
}

// 初始化成员变量
LibNiceConnection::LibNiceConnection(boost::shared_ptr<LibNiceInterface> libnice, const IceConfig& ice_config)
  : IceConnection{ice_config},
    lib_nice_{libnice}, agent_{NULL}, loop_{NULL}, candsDelivered_{0}, receivedLastCandidate_{false} {
  #if !GLIB_CHECK_VERSION(2, 35, 0)
  g_type_init();
  #endif
}

// 关闭ice链接
LibNiceConnection::~LibNiceConnection() {
  this->close();
}

// 关闭ice链接实现
void LibNiceConnection::close() {
  boost::mutex::scoped_lock lock(close_mutex_);
  if (this->checkIceState() == IceState::FINISHED) {
    return;
  }
  ELOG_DEBUG("%s message:closing", toLog());
  this->updateIceState(IceState::FINISHED);
  if (loop_ != NULL) {
    ELOG_DEBUG("%s message:main loop quit", toLog());
    g_main_loop_quit(loop_);
  }
  cond_.notify_one();
  listener_.reset();
  boost::system_time const timeout = boost::get_system_time() + boost::posix_time::milliseconds(5);
  ELOG_DEBUG("%s message: m_thread join, this: %p", toLog(), this);
  if (!m_Thread_.timed_join(timeout)) {
    ELOG_DEBUG("%s message: interrupt thread to close, this: %p", toLog(), this);
    m_Thread_.interrupt();
  }
  if (loop_ != NULL) {
    ELOG_DEBUG("%s message:Unrefing loop", toLog());
    g_main_loop_unref(loop_);
    loop_ = NULL;
  }
  if (agent_ != NULL) {
    ELOG_DEBUG("%s message: unrefing agent", toLog());
    g_object_unref(agent_);
    agent_ = NULL;
  }
  if (context_ != NULL) {
    ELOG_DEBUG("%s message: Unrefing context", toLog());
    g_main_context_unref(context_);
    context_ = NULL;
  }
  ELOG_DEBUG("%s message: closed, this: %p", toLog(), this);
}

// 上层回调接收到的数据包
void LibNiceConnection::onData(unsigned int component_id, char* buf, int len) {
  IceState state;
  {
    boost::mutex::scoped_lock lock(close_mutex_);
    state = this->checkIceState();
  }
  if (state == IceState::READY) {
    packetPtr packet (new DataPacket());
    memcpy(packet->data, buf, len);
    packet->comp = component_id;
    packet->length = len;
    packet->received_time_ms = ClockUtils::timePointToMs(clock::now());
    if (auto listener = getIceListener().lock()) {
      listener->onPacketReceived(packet);
    }
  }
}

// 发送ice数据包
int LibNiceConnection::sendData(unsigned int component_id, const void* buf, int len) {
  int val = -1;
  if (this->checkIceState() == IceState::READY) {
    val = lib_nice_->NiceAgentSend(agent_, 1, component_id, len, reinterpret_cast<const gchar*>(buf));
  }
  if (val != len) {
    ELOG_DEBUG("%s message: Sending less data than expected, sent: %d, to_send: %d", toLog(), val, len);
  }
  return val;
}

// 开启ice连接,设置远端candidate,收集本地candidates
void LibNiceConnection::start() {
    boost::mutex::scoped_lock lock(close_mutex_);
    if (this->checkIceState() != INITIAL) {
      return;
    }
    context_ = g_main_context_new();
    ELOG_DEBUG("%s message: creating Nice Agent", toLog());
    nice_debug_enable(FALSE);
    // Create a nice agent
    agent_ = lib_nice_->NiceAgentNew(context_);
    loop_ = g_main_loop_new(context_, FALSE);
    m_Thread_ = boost::thread(&LibNiceConnection::mainLoop, this);
    GValue controllingMode = { 0 };
    g_value_init(&controllingMode, G_TYPE_BOOLEAN);
    g_value_set_boolean(&controllingMode, false);
    g_object_set_property(G_OBJECT(agent_), "controlling-mode", &controllingMode);

    GValue checks = { 0 };
    g_value_init(&checks, G_TYPE_UINT);
    g_value_set_uint(&checks, 100);
    g_object_set_property(G_OBJECT(agent_), "max-connectivity-checks", &checks);


    if (ice_config_.stun_server.compare("") != 0 && ice_config_.stun_port != 0) {
      GValue val = { 0 }, val2 = { 0 };
      g_value_init(&val, G_TYPE_STRING);
      g_value_set_string(&val, ice_config_.stun_server.c_str());
      g_object_set_property(G_OBJECT(agent_), "stun-server", &val);

      g_value_init(&val2, G_TYPE_UINT);
      g_value_set_uint(&val2, ice_config_.stun_port);
      g_object_set_property(G_OBJECT(agent_), "stun-server-port", &val2);

      ELOG_DEBUG("%s message: setting stun, stun_server: %s, stun_port: %d",
                 toLog(), ice_config_.stun_server.c_str(), ice_config_.stun_port);
    }

    // Connect the signals
    g_signal_connect(G_OBJECT(agent_), "candidate-gathering-done",
        G_CALLBACK(cb_candidate_gathering_done), this);
    g_signal_connect(G_OBJECT(agent_), "component-state-changed",
        G_CALLBACK(cb_component_state_changed), this);
    g_signal_connect(G_OBJECT(agent_), "new-selected-pair",
        G_CALLBACK(cb_new_selected_pair), this);
    g_signal_connect(G_OBJECT(agent_), "new-candidate",
        G_CALLBACK(cb_new_candidate), this);

    // Create a new stream and start gathering candidates
    ELOG_DEBUG("%s message: adding stream, iceComponents: %d", toLog(), ice_config_.ice_components);
    lib_nice_->NiceAgentAddStream(agent_, ice_config_.ice_components);
    gchar *ufrag = NULL, *upass = NULL;
    lib_nice_->NiceAgentGetLocalCredentials(agent_, 1, &ufrag, &upass);
    ufrag_ = std::string(ufrag); g_free(ufrag);
    upass_ = std::string(upass); g_free(upass);

    // Set our remote credentials.  This must be done *after* we add a stream.
    if (ice_config_.username.compare("") != 0 && ice_config_.password.compare("") != 0) {
      ELOG_DEBUG("%s message: setting remote credentials in constructor, ufrag:%s, pass:%s",
                 toLog(), ice_config_.username.c_str(), ice_config_.password.c_str());
      this->setRemoteCredentials(ice_config_.username, ice_config_.password);
    }
    // Set Port Range: If this doesn't work when linking the file libnice.sym has to be modified to include this call
    if (ice_config_.min_port != 0 && ice_config_.max_port != 0) {
      ELOG_DEBUG("%s message: setting port range, min_port: %d, max_port: %d",
                 toLog(), ice_config_.min_port, ice_config_.max_port);
      lib_nice_->NiceAgentSetPortRange(agent_, (guint)1, (guint)1, (guint)ice_config_.min_port,
          (guint)ice_config_.max_port);
    }

    if (!ice_config_.network_interface.empty()) {
      const char* public_ip = lib_nice_->NiceInterfacesGetIpForInterface(ice_config_.network_interface.c_str());
      if (public_ip) {
        lib_nice_->NiceAgentAddLocalAddress(agent_, public_ip);
      }
    }

    if (ice_config_.turn_server.compare("") != 0 && ice_config_.turn_port != 0) {
      ELOG_DEBUG("%s message: configuring TURN, turn_server: %s , turn_port: %d, turn_username: %s, turn_pass: %s",
                 toLog(), ice_config_.turn_server.c_str(),
          ice_config_.turn_port, ice_config_.turn_username.c_str(), ice_config_.turn_pass.c_str());

      for (unsigned int i = 1; i <= ice_config_.ice_components ; i++) {
        lib_nice_->NiceAgentSetRelayInfo(agent_,
            1,
            i,
            ice_config_.turn_server.c_str(),     // TURN Server IP
            ice_config_.turn_port,               // TURN Server PORT
            ice_config_.turn_username.c_str(),   // Username
            ice_config_.turn_pass.c_str());       // Pass
      }
    }

    if (agent_) {
      for (unsigned int i = 1; i <= ice_config_.ice_components; i++) {
        lib_nice_->NiceAgentAttachRecv(agent_, 1, i, context_, reinterpret_cast<void*>(cb_nice_recv), this);
      }
    }
    ELOG_DEBUG("%s message: gathering, this: %p", toLog(), this);
    lib_nice_->NiceAgentGatherCandidates(agent_, 1);
}

// ice主循环
void LibNiceConnection::mainLoop() {
  // Start gathering candidates and fire event loop
  ELOG_DEBUG("%s message: starting g_main_loop, this: %p", toLog(), this);
  if (agent_ == NULL || loop_ == NULL) {
    return;
  }
  g_main_loop_run(loop_);
  ELOG_DEBUG("%s message: finished g_main_loop, this: %p", toLog(), this);
}

// 将远端candidates设置到ice agent中
bool LibNiceConnection::setRemoteCandidates(const std::vector<CandidateInfo> &candidates, bool is_bundle) {
  if (agent_ == NULL) {
    this->close();
    return false;
  }
  GSList* candList = NULL;
  ELOG_DEBUG("%s message: setting remote candidates, candidateSize: %lu, mediaType: %d",
             toLog(), candidates.size(), ice_config_.media_type);

  for (unsigned int it = 0; it < candidates.size(); it++) {
    NiceCandidateType nice_cand_type;
    CandidateInfo cinfo = candidates[it];
    // If bundle we will add the candidates regardless the mediaType
    if (cinfo.componentId != 1 || (!is_bundle && cinfo.mediaType != ice_config_.media_type ))
      continue;

    switch (cinfo.hostType) {
      case HOST:
        nice_cand_type = NICE_CANDIDATE_TYPE_HOST;
        break;
      case SRFLX:
        nice_cand_type = NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE;
        break;
      case PRFLX:
        nice_cand_type = NICE_CANDIDATE_TYPE_PEER_REFLEXIVE;
        break;
      case RELAY:
        nice_cand_type = NICE_CANDIDATE_TYPE_RELAYED;
        break;
      default:
        nice_cand_type = NICE_CANDIDATE_TYPE_HOST;
        break;
    }
    if (cinfo.hostPort == 0) {
      continue;
    }
    NiceCandidate* thecandidate = nice_candidate_new(nice_cand_type);
    thecandidate->username = strdup(cinfo.username.c_str());
    thecandidate->password = strdup(cinfo.password.c_str());
    thecandidate->stream_id = (guint) 1;
    thecandidate->component_id = cinfo.componentId;
    thecandidate->priority = cinfo.priority;
    thecandidate->transport = NICE_CANDIDATE_TRANSPORT_UDP;
    nice_address_set_from_string(&thecandidate->addr, cinfo.hostAddress.c_str());
    nice_address_set_port(&thecandidate->addr, cinfo.hostPort);

    std::ostringstream host_info;
    host_info << "hostType: " << cinfo.hostType
         << ", hostAddress: " << cinfo.hostAddress
         << ", hostPort: " << cinfo.hostPort;

    if (cinfo.hostType == RELAY || cinfo.hostType == SRFLX) {
      nice_address_set_from_string(&thecandidate->base_addr, cinfo.rAddress.c_str());
      nice_address_set_port(&thecandidate->base_addr, cinfo.rPort);
      ELOG_DEBUG("%s message: adding relay or srflx remote candidate, %s, rAddress: %s, rPort: %d",
                 toLog(), host_info.str().c_str(),
                 cinfo.rAddress.c_str(), cinfo.rPort);
    } else {
      ELOG_DEBUG("%s message: adding remote candidate, %s, priority: %d, componentId: %d, ufrag: %s, pass: %s",
          toLog(), host_info.str().c_str(), cinfo.priority, cinfo.componentId, cinfo.username.c_str(),
          cinfo.password.c_str());
    }
    candList = g_slist_prepend(candList, thecandidate);
  }
  // TODO(pedro): Set Component Id properly, now fixed at 1
  lib_nice_->NiceAgentSetRemoteCandidates(agent_, (guint) 1, 1, candList);
  g_slist_free_full(candList, (GDestroyNotify)&nice_candidate_free);

  return true;
}

// 回调candidate_receive状态到上层
void LibNiceConnection::gatheringDone(uint stream_id) {
  ELOG_DEBUG("%s message: gathering done, stream_id: %u", toLog(), stream_id);
  updateIceState(IceState::CANDIDATES_RECEIVED);
}

// 获取本地cadidates
void LibNiceConnection::getCandidate(uint stream_id, uint component_id, const std::string &foundation) {
  GSList* lcands = lib_nice_->NiceAgentGetLocalCandidates(agent_, stream_id, component_id);
  // We only want to get the new candidates
  if (candsDelivered_ <= g_slist_length(lcands)) {
    lcands = g_slist_nth(lcands, (candsDelivered_));
  }
  for (GSList* iterator = lcands; iterator; iterator = iterator->next) {
    char address[NICE_ADDRESS_STRING_LEN], baseAddress[NICE_ADDRESS_STRING_LEN];
    NiceCandidate *cand = reinterpret_cast<NiceCandidate*>(iterator->data);
    nice_address_to_string(&cand->addr, address);
    nice_address_to_string(&cand->base_addr, baseAddress);
    candsDelivered_++;
    if (strstr(address, ":") != NULL) {  // We ignore IPv6 candidates at this point
      continue;
    }
    CandidateInfo cand_info;
    cand_info.componentId = cand->component_id;
    cand_info.foundation = cand->foundation;
    cand_info.priority = cand->priority;
    cand_info.hostAddress = std::string(address);
    cand_info.hostPort = nice_address_get_port(&cand->addr);
    if (cand_info.hostPort == 0) {
      continue;
    }
    cand_info.mediaType = ice_config_.media_type;

    /*
     *    NICE_CANDIDATE_TYPE_HOST,
     *    NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE,
     *    NICE_CANDIDATE_TYPE_PEER_REFLEXIVE,
     *    NICE_CANDIDATE_TYPE_RELAYED,
     */
    switch (cand->type) {
      case NICE_CANDIDATE_TYPE_HOST:
        cand_info.hostType = HOST;
        break;
      case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
        cand_info.hostType = SRFLX;
        cand_info.rAddress = std::string(baseAddress);
        cand_info.rPort = nice_address_get_port(&cand->base_addr);
        break;
      case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
        cand_info.hostType = PRFLX;
        break;
      case NICE_CANDIDATE_TYPE_RELAYED:
        char turnAddres[NICE_ADDRESS_STRING_LEN];
        nice_address_to_string(&cand->turn->server, turnAddres);
        cand_info.hostType = RELAY;
        cand_info.rAddress = std::string(baseAddress);
        cand_info.rPort = nice_address_get_port(&cand->base_addr);
        break;
      default:
        break;
    }
    cand_info.netProtocol = "udp";
    cand_info.transProtocol = ice_config_.transport_name;
    cand_info.username = ufrag_;
    cand_info.password = upass_;
    // localCandidates->push_back(cand_info);
    if (auto listener = this->getIceListener().lock()) {
      listener->onCandidate(cand_info, this);
    }
  }
  // for nice_agent_get_local_candidates, the caller owns the returned GSList as well as the candidates
  // contained within it.
  // let's free everything in the list, as well as the list.
  g_slist_free_full(lcands, (GDestroyNotify)&nice_candidate_free);
}

// 设置远端证书
void LibNiceConnection::setRemoteCredentials(const std::string& username, const std::string& password) {
  ELOG_DEBUG("%s message: setting remote credentials, ufrag: %s, pass: %s",
             toLog(), username.c_str(), password.c_str());
  lib_nice_->NiceAgentSetRemoteCredentials(agent_, (guint) 1, username.c_str(), password.c_str());
}

// 回调ice状态
void LibNiceConnection::updateComponentState(unsigned int component_id, IceState state) {
  ELOG_DEBUG("%s message: new ice component state, newState: %u, transportName: %s, componentId %u, iceComponents: %u",
             toLog(), state, ice_config_.transport_name.c_str(), component_id, ice_config_.ice_components);
  comp_state_list_[component_id] = state;
  if (state == IceState::READY) {
    for (unsigned int i = 1; i <= ice_config_.ice_components; i++) {
      if (comp_state_list_[i] != IceState::READY) {
        return;
      }
    }
  } else if (state == IceState::FAILED) {
    if (receivedLastCandidate_) {
      ELOG_WARN("%s message: component failed, ice_config_.transport_name: %s, componentId: %u",
                toLog(), ice_config_.transport_name.c_str(), component_id);
      for (unsigned int i = 1; i <= ice_config_.ice_components; i++) {
        if (comp_state_list_[i] != IceState::FAILED) {
          return;
        }
      }
    } else {
      ELOG_WARN("%s message: failed and not received all candidates, newComponentState:%u", toLog(), state);
      return;
    }
  }
  this->updateIceState(state);
}

// 获取candidate 的host类型
std::string getHostTypeFromCandidate(NiceCandidate *candidate) {
  switch (candidate->type) {
    case NICE_CANDIDATE_TYPE_HOST: return "host";
    case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE: return "serverReflexive";
    case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE: return "peerReflexive";
    case NICE_CANDIDATE_TYPE_RELAYED: return "relayed";
    default: return "unknown";
  }
}

// 获取被选中的candidate对
CandidatePair LibNiceConnection::getSelectedPair() {
  char ipaddr[NICE_ADDRESS_STRING_LEN];
  CandidatePair selectedPair;
  NiceCandidate* local, *remote;
  lib_nice_->NiceAgentGetSelectedPair(agent_, 1, 1, &local, &remote);
  nice_address_to_string(&local->addr, ipaddr);
  selectedPair.erizoCandidateIp = std::string(ipaddr);
  selectedPair.erizoCandidatePort = nice_address_get_port(&local->addr);
  selectedPair.erizoHostType = getHostTypeFromCandidate(local);
  ELOG_DEBUG("%s message: selected pair, local_addr: %s, local_port: %d, local_type: %s",
              toLog(), ipaddr, nice_address_get_port(&local->addr), selectedPair.erizoHostType.c_str());
  nice_address_to_string(&remote->addr, ipaddr);
  selectedPair.clientCandidateIp = std::string(ipaddr);
  selectedPair.clientCandidatePort = nice_address_get_port(&remote->addr);
  selectedPair.clientHostType = getHostTypeFromCandidate(local);
  ELOG_INFO("%s message: selected pair, remote_addr: %s, remote_port: %d, remote_type: %s",
             toLog(), ipaddr, nice_address_get_port(&remote->addr), selectedPair.clientHostType.c_str());
  return selectedPair;
}

// 设置接收最新candiate的标记
void LibNiceConnection::setReceivedLastCandidate(bool hasReceived) {
  ELOG_DEBUG("%s message: setting hasReceivedLastCandidate, hasReceived: %u", toLog(), hasReceived);
  this->receivedLastCandidate_ = hasReceived;
}

// 通过LibNiceInterfaceImp对象和ice_config创建LibNiceConnection对象
LibNiceConnection* LibNiceConnection::create(const IceConfig& ice_config) {
  return new LibNiceConnection(boost::shared_ptr<LibNiceInterface>(new LibNiceInterfaceImpl()), ice_config);
}

 

おすすめ

転載: blog.csdn.net/tong5956/article/details/108362556