Lesson 17-Classic Module Source Code Analysis

PeerConnection Source Code

Creation and Initialization of RTCPeerConnection

RTCPeerConnection Creation Process:

// 1. Create PeerConnectionFactory
rtc::scoped_refptr<PeerConnectionFactory> factory(
    PeerConnectionFactory::Create());

// 2. Create PeerConnection
rtc::scoped_refptr<RTCPeerConnection> pc(
    factory->CreatePeerConnection(
        configuration,  // RTCConfiguration
        constraints,    // MediaConstraints
        nullptr,        // PeerConnectionObserver
        nullptr,        // std::unique_ptr<CertificateVerifier>
        &error));       // Error information

// 3. Add media stream
rtc::scoped_refptr<MediaStreamInterface> stream = ...;
pc->AddStream(stream);

// 4. Create Offer/Answer
pc->CreateOffer(offer_observer, offer_answer_options);

Key Source Code Implementation:

// peer_connection.cc
rtc::scoped_refptr<RTCPeerConnection> RTCPeerConnection::Create(
    PeerConnectionFactory* factory,
    const PeerConnectionInterface::RTCConfiguration& configuration,
    const MediaConstraintsInterface* constraints,
    PeerConnectionObserver* observer,
    std::unique_ptr<CertificateVerifier> certificate_verifier,
    std::string* error) {
  // Validate input parameters
  if (!factory) {
    *error = "PeerConnectionFactory is null";
    return nullptr;
  }

  // Create PeerConnectionImpl instance
  rtc::scoped_refptr<RTCPeerConnection> pc(
      new rtc::RefCountedObject<RTCPeerConnectionImpl>(
          factory, configuration, constraints, observer,
          std::move(certificate_verifier)));

  // Initialize PeerConnection
  if (!pc->Initialize()) {
    *error = "Failed to initialize PeerConnection";
    return nullptr;
  }

  return pc;
}

bool RTCPeerConnectionImpl::Initialize() {
  // Create ICE transport
  ice_transport_ = CreateIceTransport();

  // Create DTLS transport
  dtls_transport_ = CreateDtlsTransport(ice_transport_.get());

  // Create media engine
  media_engine_ = factory_->media_engine();

  // Initialize state
  signaling_state_ = kStable;
  ice_connection_state_ = kIceConnectionNew;

  return true;
}

SDP Exchange and Negotiation Process

Complete SDP Negotiation Process:

  1. Create Offer
  2. Set Local Description
  3. Send Offer via Signaling Server
  4. Receive Answer
  5. Set Remote Description

Key Source Code Implementation:

// sdp_offer_answer.cc
rtc::scoped_refptr<SessionDescriptionInterface> RTCPeerConnectionImpl::CreateOffer(
    const CreateSessionDescriptionObserver* observer,
    const PeerConnectionInterface::RTCOfferAnswerOptions& options) {

  // Create Offer generator
  std::unique_ptr<SessionDescriptionInterface> offer(
      CreateSessionDescription(SdpType::kOffer, nullptr));

  // Add media descriptions
  for (const auto& track : local_streams_) {
    AddTrackToOffer(track, offer.get(), options);
  }

  // Set SDP attributes
  SetSdpAttributes(offer.get(), options);

  // Return generated Offer
  return offer;
}

void RTCPeerConnectionImpl::SetLocalDescription(
    SetSessionDescriptionObserver* observer,
    SessionDescriptionInterface* desc) {

  // Validate SDP
  if (!ValidateSessionDescription(desc, kLocal)) {
    observer->OnFailure("Invalid session description");
    return;
  }

  // Apply SDP to internal state
  ApplySessionDescription(desc, kLocal);

  // Notify observer
  observer->OnSuccess();

  // Trigger ICE restart (if needed)
  MaybeRestartIce();
}

ICE Candidate Collection and Exchange

ICE Candidate Collection Process:

  1. Collect Host Candidates
  2. Collect STUN Candidates
  3. Collect TURN Candidates
  4. Exchange Candidates via Signaling Server

Key Source Code Implementation:

// ice_transport.cc
void IceTransport::StartGathering() {
  // Start collecting host candidates
  port_allocator_->StartGettingPorts();

  // Send STUN requests to obtain server reflexive candidates
  stun_port_->Start();

  // Create TURN port if TURN server is configured
  if (config_.has_turn_server()) {
    turn_port_ = CreateTurnPort(config_.turn_server());
    turn_port_->Start();
  }
}

void IceTransport::OnCandidateGathered(const IceCandidateInterface* candidate) {
  // Add candidate to list
  local_candidates_.push_back(candidate);

  // Notify observer
  if (observer_) {
    observer_->OnIceCandidate(candidate);
  }
}

ICE Candidate Exchange:

// peer_connection.cc
void RTCPeerConnectionImpl::AddIceCandidate(
    const IceCandidateInterface* candidate) {

  // Validate candidate
  if (!candidate) {
    LOG(LS_ERROR) << "Null candidate";
    return;
  }

  // Add to ICE transport
  if (!ice_transport_->AddRemoteCandidate(candidate)) {
    LOG(LS_ERROR) << "Failed to add remote candidate";
    return;
  }

  // Trigger connection check
  ice_transport_->MaybeStartGathering();
}

Connection State Monitoring and Event Handling

Connection State Types:

enum SignalingState {
  kStable,
  kHaveLocalOffer,
  kHaveRemoteOffer,
  kHaveLocalPrAnswer,
  kHaveRemotePrAnswer,
  kClosed
};

enum IceConnectionState {
  kIceConnectionNew,
  kIceConnectionChecking,
  kIceConnectionConnected,
  kIceConnectionCompleted,
  kIceConnectionFailed,
  kIceConnectionDisconnected,
  kIceConnectionClosed,
  kIceConnectionMax
};

State Monitoring Implementation:

// peer_connection.cc
void RTCPeerConnectionImpl::UpdateConnectionState() {
  IceConnectionState new_state = CalculateIceConnectionState();

  if (new_state != ice_connection_state_) {
    ice_connection_state_ = new_state;
    observer_->OnIceConnectionChange(ice_connection_state_);
  }

  SignalingState new_signaling_state = CalculateSignalingState();

  if (new_signaling_state != signaling_state_) {
    signaling_state_ = new_signaling_state;
    observer_->OnSignalingChange(signaling_state_);
  }
}

void RTCPeerConnectionImpl::OnIceConnectionStateChange(
    PeerConnectionInterface::IceConnectionState new_state) {
  // Handle ICE connection state change
  switch (new_state) {
    case kIceConnectionFailed:
      // Attempt reconnection
      MaybeRestartIce();
      break;
    case kIceConnectionClosed:
      // Clean up resources
      Cleanup();
      break;
    default:
      break;
  }

  // Notify observer
  observer_->OnIceConnectionChange(new_state);
}

Error Handling and Recovery Mechanisms

Error Handling Strategies:

  1. Automatically restart ICE on connection failure
  2. Retry on SDP negotiation failure
  3. Switch candidates on network errors

Key Source Code Implementation:

// peer_connection.cc
void RTCPeerConnectionImpl::MaybeRestartIce() {
  if (ice_restart_pending_) {
    return;
  }

  ice_restart_pending_ = true;

  // Create new Offer
  CreateOffer(
      new rtc::RefCountedObject<CreateSessionDescriptionObserverImpl>(
          this, [this]() {
            // Set local Description
            SetLocalDescription(
                new rtc::RefCountedObject<SetSessionDescriptionObserverImpl>(
                    this),
                current_local_description_.get());

            ice_restart_pending_ = false;
          }),
      PeerConnectionInterface::RTCOfferAnswerOptions());
}

void RTCPeerConnectionImpl::OnFailure(const std::string& error) {
  LOG(LS_ERROR) << "PeerConnection error: " << error;

  // Notify observer
  if (observer_) {
    observer_->OnSignalingChange(kClosed);
  }

  // Clean up resources
  Cleanup();
}

MediaStream Source Code

MediaStream Creation and Track Management

MediaStream Creation Process:

// 1. Create media stream
rtc::scoped_refptr<MediaStreamInterface> stream(
    peer_connection_factory->CreateLocalMediaStream("stream_label"));

// 2. Add audio track
rtc::scoped_refptr<AudioTrackInterface> audio_track(
    peer_connection_factory->CreateAudioTrack("audio_label", audio_source));
stream->AddTrack(audio_track);

// 3. Add video track
rtc::scoped_refptr<VideoTrackInterface> video_track(
    peer_connection_factory->CreateVideoTrack("video_label", video_source));
stream->AddTrack(video_track);

Key Source Code Implementation:

// media_stream.cc
rtc::scoped_refptr<MediaStreamInterface> PeerConnectionFactory::CreateLocalMediaStream(
    const std::string& label) {

  // Create MediaStream implementation
  rtc::scoped_refptr<MediaStream> stream(new rtc::RefCountedObject<MediaStream>(label));

  return stream;
}

void MediaStream::AddTrack(AudioTrackInterface* track) {
  // Check for existing track with same ID
  if (FindAudioTrack(track->id())) {
    LOG(LS_ERROR) << "Audio track with id " << track->id() << " already exists";
    return;
  }

  // Add track
  audio_tracks_.push_back(track);

  // Notify observers
  for (auto& observer : observers_) {
    observer->OnTrackAdded(track);
  }
}

Media Track Constraints and Configuration

Track Constraints Example:

// Create audio constraints
MediaConstraints audio_constraints;
audio_constraints.AddOptionalConstraint("googEchoCancellation", "true");
audio_constraints.AddOptionalConstraint("googAutoGainControl", "true");

// Create video constraints
MediaConstraints video_constraints;
video_constraints.AddMandatoryConstraint("minWidth", "640");
video_constraints.AddMandatoryConstraint("minHeight", "480");
video_constraints.AddMandatoryConstraint("minFrameRate", "30");

// Create audio source
rtc::scoped_refptr<AudioSourceInterface> audio_source(
    peer_connection_factory->CreateAudioSource(&audio_constraints));

// Create video source
rtc::scoped_refptr<VideoTrackSourceInterface> video_source(
    peer_connection_factory->CreateVideoSource(video_constraints, nullptr));

Key Source Code Implementation:

// media_constraints.cc
bool MediaConstraints::AddMandatoryConstraint(const std::string& key,
                                             const std::string& value) {
  mandatory_.push_back(Constraint(key, value));
  return true;
}

bool MediaConstraints::AddOptionalConstraint(const std::string& key,
                                            const std::string& value) {
  optional_.push_back(Constraint(key, value));
  return true;
}

// video_track_source.cc
rtc::scoped_refptr<VideoTrackSourceInterface> PeerConnectionFactory::CreateVideoSource(
    const MediaConstraints* constraints,
    cricket::VideoCapturer* capturer) {

  // Create video source implementation
  rtc::scoped_refptr<VideoTrackSource> source(
      new rtc::RefCountedObject<VideoTrackSource>(capturer));

  // Apply constraints
  if (constraints) {
    source->ApplyConstraints(*constraints);
  }

  return source;
}

Media Stream Display and Control

Media Stream Display Implementation:

// Get video track
rtc::scoped_refptr<VideoTrackInterface> video_track = stream->GetVideoTracks()[0];

// Create video renderer
rtc::scoped_refptr<VideoRendererInterface> renderer(
    new rtc::RefCountedObject<VideoRenderer>(video_element));

// Add renderer to track
video_track->AddOrUpdateSink(renderer.get(), rtc::VideoSinkWants());

// Control media stream
stream->GetAudioTracks()[0]->set_enabled(false);  // Mute audio
stream->GetVideoTracks()[0]->set_enabled(false);  // Stop video

Key Source Code Implementation:

// media_stream_track.cc
void MediaStreamTrack::set_enabled(bool enabled) {
  if (enabled_ == enabled) {
    return;
  }

  enabled_ = enabled;

  // Notify observers
  for (auto& observer : observers_) {
    observer->OnChanged();
  }

  // Actually enable/disable track
  if (enabled) {
    Enable();
  } else {
    Disable();
  }
}

// video_track.cc
void VideoTrack::AddOrUpdateSink(rtc::VideoSinkInterface<webrtc::VideoFrame>* sink,
                                const rtc::VideoSinkWants& wants) {
  // Add or update renderer
  sinks_[sink] = wants;

  // Notify source update
  if (source_) {
    source_->AddOrUpdateSink(sink, wants);
  }
}

Screen Sharing Implementation

Screen Sharing Process:

  1. Obtain screen capture source
  2. Create video track
  3. Add to media stream
  4. Add to PeerConnection

Key Source Code Implementation:

// Create screen capture source
rtc::scoped_refptr<DesktopCapturer> capturer(
    DesktopCapturer::CreateScreenCapturer());

// Create video source
rtc::scoped_refptr<VideoTrackSourceInterface> video_source(
    peer_connection_factory->CreateVideoSource(
        MediaConstraints(), capturer.get()));

// Create video track
rtc::scoped_refptr<VideoTrackInterface> video_track(
    peer_connection_factory->CreateVideoTrack("screen_share", video_source));

// Add to media stream
rtc::scoped_refptr<MediaStreamInterface> stream(
    peer_connection_factory->CreateLocalMediaStream("screen_stream"));
stream->AddTrack(video_track);

// Add to PeerConnection
peer_connection->AddStream(stream);

Screen Capture Source Implementation:

// desktop_capturer.cc
bool DesktopCapturer::CaptureFrame(webrtc::VideoFrame* frame) {
  // Capture screen frame
  DesktopFrame* desktop_frame = CaptureDesktop();

  if (!desktop_frame) {
    return false;
  }

  // Convert to VideoFrame
  *frame = webrtc::VideoFrame::Builder()
      .set_video_frame_buffer(desktop_frame)
      .set_timestamp_us(clock_->TimeInMicroseconds())
      .set_rotation(webrtc::kVideoRotation_0)
      .build();

  return true;
}

Media Stream Recording and Playback

Media Stream Recording Implementation:

// Create file writer
rtc::scoped_refptr<FileVideoSink> file_sink(
    new rtc::RefCountedObject<FileVideoSink>("output.webm"));

// Add to video track
video_track->AddOrUpdateSink(file_sink.get(), rtc::VideoSinkWants());

// Stop recording
video_track->RemoveSink(file_sink.get());

Key Source Code Implementation:

// file_video_sink.cc
void FileVideoSink::OnFrame(const webrtc::VideoFrame& frame) {
  // Write frame to file
  if (!writer_) {
    writer_ = CreateWebmWriter(filename_);
  }

  // Convert frame format and write
  EncodedImage encoded_image;
  encoder_->Encode(frame, &encoded_image);

  writer_->WriteFrame(encoded_image);
}

// Media stream playback
rtc::scoped_refptr<MediaStreamInterface> LoadRecordedStream(const std::string& filename) {
  // Create media stream
  rtc::scoped_refptr<MediaStreamInterface> stream(
      peer_connection_factory->CreateLocalMediaStream("replay_stream"));

  // Create video track
  rtc::scoped_refptr<VideoTrackInterface> video_track(
      peer_connection_factory->CreateVideoTrack("replay_video", nullptr));

  // Set custom video source (read from file)
  rtc::scoped_refptr<VideoTrackSourceInterface> video_source(
      new rtc::RefCountedObject<FileVideoSource>(filename));
  video_track->SetSource(video_source);

  // Add to media stream
  stream->AddTrack(video_track);

  return stream;
}

DataChannel Source Code

RTCDataChannel Creation and Configuration

DataChannel Creation Process:

// 1. Create DataChannel configuration
webrtc::DataChannelInit config;
config.ordered = true;
config.maxRetransmitTime = 3000;  // 3 seconds
config.maxRetransmits = 10;
config.protocol = "sctp";

// 2. Create DataChannel
rtc::scoped_refptr<RTCDataChannel> channel(
    peer_connection->CreateDataChannel("data_channel", &config));

// 3. Set observer
channel->RegisterObserver(this);

Key Source Code Implementation:

// data_channel.cc
rtc::scoped_refptr<RTCDataChannel> RTCPeerConnection::CreateDataChannel(
    const std::string& label,
    const DataChannelInit* config) {

  // Create SCTP transport (if not already created)
  if (!sctp_transport_) {
    sctp_transport_ = CreateSctpTransport();
  }

  // Create DataChannel implementation
  rtc::scoped_refptr<DataChannel> channel(
      new rtc::RefCountedObject<DataChannel>(
          this, label, config, sctp_transport_));

  // Add to channel list
  data_channels_.push_back(channel);

  return channel;
}

// sctp_transport.cc
rtc::scoped_refptr<SctpTransport> RTCPeerConnection::CreateSctpTransport() {
  // Create SCTP transport
  rtc::scoped_refptr<SctpTransport> transport(
      new rtc::RefCountedObject<SctpTransport>(
          network_thread_, ice_transport_));

  // Initialize SCTP
  transport->Start(5000);  // 5-second timeout

  return transport;
}

Data Sending and Receiving Implementation

Data Sending Implementation:

// Send text data
channel->Send(TextMessage("Hello, WebRTC!"));

// Send binary data
std::vector<uint8_t> binary_data = {0x01, 0x02, 0x03};
channel->Send(BinaryMessage(binary_data));

Key Source Code Implementation:

// data_channel.cc
bool DataChannel::Send(const DataBuffer& buffer) {
  // Check channel state
  if (state_ != kOpen) {
    LOG(LS_ERROR) << "Channel is not open";
    return false;
  }

  // Check message size limit
  if (buffer.size() > max_message_size_) {
    LOG(LS_ERROR) << "Message too large";
    return false;
  }

  // Send data via SCTP
  if (!sctp_transport_->Send(buffer)) {
    LOG(LS_ERROR) << "Failed to send data via SCTP";
    return false;
  }

  return true;
}

// sctp_transport.cc
bool SctpTransport::Send(const DataBuffer& buffer) {
  // Create SCTP data chunk
  SctpPacket packet;
  packet.AddData(buffer.data.cdata(), buffer.data.size(), buffer.binary);

  // Send packet
  return socket_->Send(packet);
}

Data Receiving Implementation:

// Implement DataChannelObserver interface
class DataChannelObserverImpl : public DataChannelObserver {
public:
  void OnMessage(const DataBuffer& buffer) override {
    if (buffer.binary) {
      // Process binary data
      ProcessBinaryData(buffer.data);
    } else {
      // Process text data
      ProcessTextData(buffer.data);
    }
  }
};

// Register observer
channel->RegisterObserver(new DataChannelObserverImpl());

Data Channel State Management

DataChannel State Types:

enum DataChannelState {
  kConnecting,
  kOpen,
  kClosing,
  kClosed
};

State Management Implementation:

// data_channel.cc
void DataChannel::UpdateState() {
  DataChannelState new_state = CalculateState();

  if (new_state != state_) {
    DataChannelState old_state = state_;
    state_ = new_state;

    // Notify observer
    if (observer_) {
      observer_->OnStateChange();
    }

    // Handle state transition
    switch (state_) {
      case kOpen:
        OnOpen();
        break;
      case kClosed:
        OnClose();
        break;
      default:
        break;
    }
  }
}

void DataChannel::OnSctpNotification(const SctpNotification& notification) {
  switch (notification.type) {
    case SCTP_ASSOC_CHANGE:
      if (notification.state == SCTP_COMM_UP) {
        SetState(kOpen);
      } else if (notification.state == SCTP_COMM_LOST ||
                 notification.state == SCTP_SHUTDOWN_COMP) {
        SetState(kClosed);
      }
      break;
    case SCTP_PEER_ADDR_CHANGE:
      // Handle peer address change
      break;
    default:
      break;
  }
}

Data Fragmentation and Reassembly

Data Fragmentation Strategy:

  1. Fragment by maximum message size
  2. Fragment by SCTP maximum transmission unit (MTU)
  3. Reliable/unreliable transmission modes

Key Source Code Implementation:

// data_channel.cc
bool DataChannel::Send(const DataBuffer& buffer) {
  // Check if fragmentation is needed
  if (buffer.size() <= max_message_size_) {
    // Send directly
    return sctp_transport_->Send(buffer);
  } else {
    // Send fragmented
    return SendFragments(buffer);
  }
}

bool DataChannel::SendFragments(const DataBuffer& buffer) {
  size_t offset = 0;
  while (offset < buffer.size()) {
    size_t fragment_size = std::min(max_message_size_, buffer.size() - offset);
    DataBuffer fragment(buffer.data.cdata() + offset, fragment_size, buffer.binary);

    if (!sctp_transport_->Send(fragment)) {
      return false;
    }

    offset += fragment_size;
  }

  return true;
}

Data Reassembly Implementation:

// sctp_transport.cc
void SctpTransport::OnDataReceived(const SctpPacket& packet) {
  // Extract data chunk
  SctpDataChunk chunk;
  if (!packet.GetDataChunk(&chunk)) {
    return;
  }

  // Check if data is fragmented
  if (chunk.is_unordered()) {
    // Handle unordered data
    HandleUnorderedData(chunk);
  } else {
    // Handle ordered data
    HandleOrderedData(chunk);
  }
}

void SctpTransport::HandleOrderedData(const SctpDataChunk& chunk) {
  // Check if it is a fragment
  if (chunk.is_beginning_fragment()) {
    // Start new fragmented message
    current_fragment_ = std::make_unique<DataFragment>();
    current_fragment_->stream_id = chunk.stream_id();
    current_fragment_->ppid = chunk.ppid();
    current_fragment_->data = chunk.data();
    current_fragment_->is_complete = false;
  } else if (chunk.is_middle_fragment()) {
    // Add to current fragment
    if (current_fragment_ && current_fragment_->stream_id == chunk.stream_id()) {
      current_fragment_->data.append(chunk.data());
    }
  } else if (chunk.is_end_fragment()) {
    // Complete fragment
    if (current_fragment_ && current_fragment_->stream_id == chunk.stream_id()) {
      current_fragment_->data.append(chunk.data());
      current_fragment_->is_complete = true;

      // Deliver complete message
      if (current_fragment_->is_complete) {
        DeliverMessage(*current_fragment_);
        current_fragment_.reset();
      }
    }
  } else {
    // Single complete message
    DeliverMessage(chunk);
  }
}

Data Transmission Security

Data Security Mechanisms:

  1. DTLS encryption (mandatory for all WebRTC communication)
  2. Message integrity protection
  3. Anti-replay attack protection

Key Source Code Implementation:

// sctp_transport.cc
bool SctpTransport::Send(const DataBuffer& buffer) {
  // Encrypt data
  std::vector<uint8_t> encrypted_data;
  if (!dtls_transport_->Encrypt(buffer.data, &encrypted_data)) {
    return false;
  }

  // Create SCTP data chunk
  SctpPacket packet;
  packet.AddData(encrypted_data.data(), encrypted_data.size(), buffer.binary);

  // Send packet
  return socket_->Send(packet);
}

bool SctpTransport::OnDataReceived(const SctpPacket& packet) {
  // Extract encrypted data
  std::vector<uint8_t> encrypted_data;
  if (!packet.GetEncryptedData(&encrypted_data)) {
    return false;
  }

  // Decrypt data
  DataBuffer decrypted_buffer;
  if (!dtls_transport_->Decrypt(encrypted_data, &decrypted_buffer)) {
    return false;
  }

  // Process decrypted data
  // ... (same as previous data processing logic)
}

// dtls_transport.cc
bool DtlsTransport::Encrypt(const std::vector<uint8_t>& plaintext,
                           std::vector<uint8_t>* ciphertext) {
  // Encrypt data using DTLS session
  return ssl_socket_->Write(plaintext.data(), plaintext.size(), ciphertext);
}

bool DtlsTransport::Decrypt(const std::vector<uint8_t>& ciphertext,
                           DataBuffer* plaintext) {
  // Decrypt data using DTLS session
  std::vector<uint8_t> decrypted_data;
  if (!ssl_socket_->Read(ciphertext.data(), ciphertext.size(), &decrypted_data)) {
    return false;
  }

  *plaintext = DataBuffer(decrypted_data.data(), decrypted_data.size(), false);
  return true;
}

This is a comprehensive analysis of the WebRTC source code, covering the key implementation details of the PeerConnection, MediaStream, and DataChannel core components. These source code analyses help in deeply understanding the working principles and internal mechanisms of WebRTC.

Share your love