前言image.png

image.png
webrtc越来越弱化流的概念,而是将轨交给用户。视频轨和音频轨。
image.png

实现

从下面的箭头位置打断点
image.png
先手动双击启动peerconnection_server.exe,peerconnection_client.exe,然后client单击连接信令服务器。此时再单击vs2019的调试器启动peerconnection_client并单击连接,此时该页面就会显示前面连接进来的client。双击该用户。
image.png
此时就会进来该断点处。
image.png
该部分代码逻辑

MainWnd::OnDefaultAction

  1. else if (ui_ == LIST_PEERS) {
  2. LRESULT sel = ::SendMessage(listbox_, LB_GETCURSEL, 0, 0);
  3. if (sel != LB_ERR) {
  4. LRESULT peer_id = ::SendMessage(listbox_, LB_GETITEMDATA, sel, 0);
  5. if (peer_id != -1 && callback_) {
  6. callback_->ConnectToPeer(peer_id);
  7. }
  8. }
  9. 先获取clientAid,然后连接

Conductor::ConnectToPeer

  1. void Conductor::ConnectToPeer(int peer_id) {
  2. RTC_DCHECK(peer_id_ == -1);
  3. RTC_DCHECK(peer_id != -1);
  4. if (peer_connection_.get()) {
  5. main_wnd_->MessageBox(
  6. "Error", "We only support connecting to one peer at a time", true);
  7. return;
  8. }
  9. // 初始化PeerConnection
  10. if (InitializePeerConnection()) {
  11. peer_id_ = peer_id;
  12. peer_connection_->CreateOffer(
  13. this, webrtc::PeerConnectionInterface::RTCOfferAnswerOptions());
  14. } else {
  15. main_wnd_->MessageBox("Error", "Failed to initialize PeerConnection", true);
  16. }
  17. }

Conductor::InitializePeerConnection

  1. bool Conductor::InitializePeerConnection() {
  2. RTC_DCHECK(!peer_connection_factory_);
  3. RTC_DCHECK(!peer_connection_);
  4. peer_connection_factory_ = webrtc::CreatePeerConnectionFactory(
  5. nullptr /* network_thread */, nullptr /* worker_thread */,
  6. nullptr /* signaling_thread */, nullptr /* default_adm */,
  7. webrtc::CreateBuiltinAudioEncoderFactory(),
  8. webrtc::CreateBuiltinAudioDecoderFactory(),
  9. webrtc::CreateBuiltinVideoEncoderFactory(),
  10. webrtc::CreateBuiltinVideoDecoderFactory(), nullptr /* audio_mixer */,
  11. nullptr /* audio_processing */);
  12. if (!peer_connection_factory_) {
  13. main_wnd_->MessageBox("Error", "Failed to initialize PeerConnectionFactory",
  14. true);
  15. DeletePeerConnection();
  16. return false;
  17. }
  18. if (!CreatePeerConnection(/*dtls=*/true)) {
  19. main_wnd_->MessageBox("Error", "CreatePeerConnection failed", true);
  20. DeletePeerConnection();
  21. }
  22. AddTracks();
  23. return peer_connection_ != nullptr;
  24. }

里面主要是 webrtc::CreatePeerConnectionFactory,和Conductor::CreatePeerConnection。

webrtc::CreatePeerConnectionFactory

image.png

Conductor::CreatePeerConnection

  1. bool Conductor::CreatePeerConnection(bool dtls) {
  2. RTC_DCHECK(peer_connection_factory_);
  3. RTC_DCHECK(!peer_connection_);
  4. // 先设定一些参数,包括sdp方式,使能dtls和添加iceserver信息
  5. webrtc::PeerConnectionInterface::RTCConfiguration config;
  6. //新版本是默认unifiedPlan,也可以选择PlanB
  7. config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
  8. config.enable_dtls_srtp = dtls;
  9. // 设置ice server
  10. webrtc::PeerConnectionInterface::IceServer server;
  11. server.uri = GetPeerConnectionString();
  12. config.servers.push_back(server);
  13. // 调用PeerConnectionFactoryInterface::CreatePeerConnection
  14. peer_connection_ = peer_connection_factory_->CreatePeerConnection(
  15. config, nullptr, nullptr, this);
  16. return peer_connection_ != nullptr;
  17. }

PeerConnectionFactoryInterface::CreatePeerConnection

PROXYMETHOD4(rtc::scoped_refptr,
CreatePeerConnection,
const PeerConnectionInterface::RTCConfiguration&,
std::unique_ptr,
std::unique_ptr,
PeerConnectionObserver*)
image.png
这里传入的参数 peer_connection
= peerconnection_factory->CreatePeerConnection(
config, nullptr, nullptr, this);
最后的是this,则该类Conductor一定是继承了 webrtc::PeerConnectionObserver ,并实现它的一些方法。

当PeerConnection对象创建好后,还需要为其添加本地音视频轨,这是非常关键的一步。对于刚入门的读者来说,这一步是很容易被遗忘的。如果没有添加本地音视频轨,WebRTC内部就无法为其产生带有媒体信息的SDP,媒体协商时就会失败,双方也就无法进行通信了。所以Conductor::CreatePeerConnection调用完后,Conductor::InitializePeerConnection函数里面就会调用Conductor::AddTracks添加音视频轨。

Conductor::AddTracks

  1. void Conductor::AddTracks() {
  2. if (!peer_connection_->GetSenders().empty()) {
  3. return; // Already added tracks.
  4. }
  5. // 创建音频轨,并添加到peerconnection
  6. rtc::scoped_refptr<webrtc::AudioTrackInterface> audio_track(
  7. peer_connection_factory_->CreateAudioTrack(
  8. kAudioLabel, peer_connection_factory_->CreateAudioSource(
  9. cricket::AudioOptions())));
  10. auto result_or_error = peer_connection_->AddTrack(audio_track, {kStreamId});
  11. if (!result_or_error.ok()) {
  12. RTC_LOG(LS_ERROR) << "Failed to add audio track to PeerConnection: "
  13. << result_or_error.error().message();
  14. }
  15. // 创建视频设备
  16. rtc::scoped_refptr<CapturerTrackSource> video_device =
  17. CapturerTrackSource::Create();
  18. if (video_device) {
  19. // 创建视频轨
  20. rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track_(
  21. peer_connection_factory_->CreateVideoTrack(kVideoLabel, video_device));
  22. // 显示本地视频
  23. main_wnd_->StartLocalRenderer(video_track_);
  24. // 添加视频轨
  25. result_or_error = peer_connection_->AddTrack(video_track_, {kStreamId});
  26. if (!result_or_error.ok()) {
  27. RTC_LOG(LS_ERROR) << "Failed to add video track to PeerConnection: "
  28. << result_or_error.error().message();
  29. }
  30. } else {
  31. RTC_LOG(LS_ERROR) << "OpenVideoCaptureDevice failed";
  32. }
  33. // 切换到显示视频渲染界面
  34. main_wnd_->SwitchToStreamingUI();
  35. }

视频源video_device是由自定义类CapturerTrackSource的静态方法Create()生成的。在Create()方法内部会遍历设备列表,从中找到第一个可用的设备,然后通过该设备采集视频

边看视频,边看电子书《WebRTC音视频实时互动技术:原理、实战与源码分析-李超编著》