由于前段时间一直忙于开发,没有及时记录开发过程中遇到的问题,现在只能靠回忆来写一些印象深刻的坑了,本篇文章先把本系列的最后一篇补上,前面只是做到了把流推上去,现在还需要把流订阅下来。
记得当时是遇到几个问题的,其中一个是订阅其他流后,自己发布的视频就没有声音了,其他问题已经记不清楚了,大家如果遇到什么问题说下说不定能帮助想起了。
感接时有点疑惑,推流时已经建立了peerconnection,收流时是不是可以直接用这个connection就行了,查了些资料,如果只是两个人通讯确实可行,不过对于我们这个需要拉多个人视频的就不合适了,出发在connection上加上区分流所属用户字段,那就复杂了也没比较,janus也不支持,所以实际的peerconnection是这样的:
推流的peerconnection归推流,拉流的peerconnection归拉流,互补相干.
你在连接Janus,join到一个房间后,如果有另外一个用户publish流成功,你会收到一个信令告诉你有人发布媒体了,并告诉你详细信息,这时发起一个subscribe必须要把该用户的streamname带上,这个是关键,关键,关键,只有这样Janus才知道这个peerconnection是要发该用户的数据的。
Janus收到该信令后,会发起createoffer流程,等待该信令把Janus的SDP发过来,然后我们再回复里把answer 的SDP回复过去(publish是我们发offer,Janus回answer),就完成了SDP的交换:
void KKRtcConnection::onJanusPeerSdp(std::string sdptype, std::string sdp)
{
webrtc::SdpParseError error;
rtc::Optional type_maybe = webrtc::SdpTypeFromString(sdptype);
if (!type_maybe) {
RTC_LOG(LS_ERROR) << "Unknown SDP type: " << sdptype;
return;
}
SdpType type = *type_maybe;
if (!InitializePeerConnection())
{
///onError callback
return;
}
std::unique_ptr session_description = webrtc::CreateSessionDescription(type, sdp, &error);
if (!session_description) {
blog(emLOG_WARNING, "%s Can't parse received session description message. SdpParseError was: %s",__func__ ,error.description.c_str() );
return;
}
blog(emLOG_INFO, "%s Received session description ",__func__);
peer_connection_->SetRemoteDescription(DummySetSessionDescriptionObserver::Create(), session_description.release());
if (type == SdpType::kOffer) {
peer_connection_->CreateAnswer(this, NULL);
}
#ifdef DEBUG_LOG
blog(emLOG_INFO, " curthreadId:%u %s proccess end(%d) handleId=%d", rtc::CurrentThreadId(), __func__, __LINE__,handleid_);
#endif
}
然后是创建初始化peerconnection来收集ICE:
peer_connection_factory_ = webrtc::CreatePeerConnectionFactory( rtc::Thread::Current(), rtc::Thread::Current(),
reinterpret_cast(adm_.get()),
webrtc::CreateBuiltinAudioEncoderFactory(),
webrtc::CreateBuiltinAudioDecoderFactory(), NULL,NULL);
webrtc::PeerConnectionInterface::RTCConfiguration config;
webrtc::FakeConstraints constraints;
if (dtls)
{
constraints.AddOptional(webrtc::MediaConstraintsInterface::kEnableDtlsSrtp, "true");
}
else
{
constraints.AddOptional(webrtc::MediaConstraintsInterface::kEnableDtlsSrtp, "false");
}
constraints.AddOptional(webrtc::MediaConstraintsInterface::kCpuOveruseDetection, "false");
peer_connection_ = peer_connection_factory_->CreatePeerConnection(config, &constraints, NULL, NULL, this);
auto ptr = new rtc::RefCountedObject();
peer_connection_->GetStats(ptr);
CreatePeerConnection调用设置了回调接口PeerConnectionObserver的实现类this, 调用后每次ICE状态回调,主要这三个函数就可以了:
OnIceCandidate
OnIceGatheringChange
OnAddStream
在OnIceCandidate把IceCandidate信令发给Janus, 在OnIceGatheringChange把IceComplete信令发给Janus,
OnAddStream里设置remote帧来了后的回调处理类:
void KKRtcConnection::OnAddStream( rtc::scoped_refptr stream)
{
#ifdef DEBUG_LOG
blog(emLOG_INFO,"%s %d", __func__ ,stream->id() );
#endif
webrtc::VideoTrackVector tracks = stream->GetVideoTracks();
// Only render the first track.
if (!tracks.empty()) {
webrtc::VideoTrackInterface* track = tracks[0];
rendered_track_ = track;
rendered_track_->AddOrUpdateSink(this, rtc::VideoSinkWants());
}
stream->Release();
#ifdef DEBUG_LOG
blog(emLOG_INFO, " curthreadId:%u %s proccess end(%d) handleId=%d", rtc::CurrentThreadId(), __func__, __LINE__, handleid_);
#endif
}
然后在帧回调里处理对方的帧预览就可以啦。