Mobile Development 19 min read

How to Get, Build, and Extend WebRTC m79 Source for Windows, Android, and iOS

This guide explains how to obtain the WebRTC m79 source, compile it for Windows, Android, and iOS, walk through the basic signaling and peer‑connection workflow, and implement advanced video‑capture and audio‑volume features with custom C++ extensions, while unifying the codebase across platforms.

Douyu Streaming
Douyu Streaming
Douyu Streaming
How to Get, Build, and Extend WebRTC m79 Source for Windows, Android, and iOS

1. Source acquisition and compilation

All commands refer to the webrtc m79 source unless otherwise noted.

The source and build instructions are documented at webrtc.googlesource.com .

Windows platform

# Set environment variables
GYP_GENERATORS=msvs-ninja,ninja
GYP_MSVS_OVERRIDE_PATH=D:\Program Files (x86)\Microsoft Visual Studio\2017\Community
GYP_MSVS_VERSION=2017
DEPOT_TOOLS_WIN_TOOLCHAIN=0

fetch --nohooks webrtc
gclient sync

gn gen out/Debug --ide=vs2017 --args="is_debug=true target_cpu=\"x86\""
ninja -C out/Debug

Android platform

fetch --nohooks webrtc_android
gclient sync

cd src/
./build/install-build-deps.sh
./build/install-build-deps-android.sh
# To build for ARM64: use target_cpu="arm64"
# To build for 32‑bit x86: use target_cpu="x86"
# To build for 64‑bit x64: use target_cpu="x64"

gn gen out/Debug --args='target_os="android" target_cpu="arm"'
ninja -C out/Debug

iOS platform

fetch --nohooks webrtc_ios
gclient sync

./tools_webrtc/ios/build_ios_libs.sh

After downloading and compiling, the generated libraries can be linked into applications on each platform.

2. WebRTC interaction logic

WebRTC does not provide a complete audio‑video system; it only defines how a session is established and media is transmitted. A typical session involves exchanging SDP (media description) and ICE candidates via a signaling server (HTTP, WebSocket, or any custom protocol).

3. Basic application

The following code implements a LAN‑only P2P call; signaling is omitted.

General development flow:

Initialize

PeerConnectionFactoryInterface
rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> peer_connection_factory_;
peer_connection_factory_ = webrtc::CreatePeerConnectionFactory(
    nullptr, nullptr, signaling_thread.get(), nullptr,
    webrtc::CreateBuiltinAudioEncoderFactory(),
    webrtc::CreateBuiltinAudioDecoderFactory(),
    webrtc::CreateBuiltinVideoEncoderFactory(),
    webrtc::CreateBuiltinVideoDecoderFactory(),
    nullptr, nullptr);

Create

PeerConnectionInterface
rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection_;
peer_connection_ = peer_connection_factory_->CreatePeerConnection(config, nullptr, nullptr, this);

Add AudioTrack and

VideoTrack
rtc::scoped_refptr<webrtc::AudioTrackInterface> audio_track(
    peer_connection_factory_->CreateAudioTrack(kAudioLabel,
        peer_connection_factory_->CreateAudioSource(cricket::AudioOptions())));
auto result = peer_connection_->AddTrack(audio_track, {kStreamId});

rtc::scoped_refptr<CapturerTrackSource> video_device = CapturerTrackSource::Create();
rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track(
    peer_connection_factory_->CreateVideoTrack(kVideoLabel, video_device));
result = peer_connection_->AddTrack(video_track, {kStreamId});

If rendering is needed, attach a VideoSink to the video track

video_track_->AddOrUpdateSink(this, rtc::VideoSinkWants());

SDP and ICE candidate exchange (simplified)

// Client A creates offer
peer_connection_->CreateOffer(this, webrtc::PeerConnectionInterface::RTCOfferAnswerOptions());
peer_connection_->SetLocalDescription(DummySetSessionDescriptionObserver::Create(), desc);
// Send SDP to Client B via signaling

// Client B receives SDP, creates PeerConnection, then creates answer
peer_connection_->SetRemoteDescription(DummySetSessionDescriptionObserver::Create(), remote_desc);
peer_connection_->CreateAnswer(this, webrtc::PeerConnectionInterface::RTCOfferAnswerOptions());
peer_connection_->SetLocalDescription(DummySetSessionDescriptionObserver::Create(), desc);
// Exchange ICE candidates (may arrive before SDP, cache if needed)
peer_connection_->AddIceCandidate(candidate);

When no errors occur, media data starts flowing to the VideoSink and the audio device module for playback.

4. Advanced applications

Two practical use‑cases on the client side are presented.

4.1 Video self‑capture

Live‑stream SDKs often provide beauty filters, but the vanilla WebRTC source lacks such features. To feed processed RGBA/NV12 textures into WebRTC, you need to understand the video‑capture pipeline.

4.1.1 Understanding the video pipeline

VideoSourceInterface

: provides add / remove methods for video frames. VideoSinkInterface: unified output interface. VideoTrack holds a VideoSourceInterface; the encoder is attached to the track.

The data flow is: VideoTrackVideoRtpSenderWebRtcVideoVChannelWebRtcVideoSendStreamVideoSendStream

VideoStreamEncoder
VideoBroadcaster

implements both VideoSourceInterface and VideoSinkInterface, managing a std::vector<VideoSinkInterface*> of sinks. CapturerTrackSource implements VideoSourceInterface and forwards frames to the broadcaster, which then distributes them to all registered sinks.

4.1.2 Solution

/* VideoSourceImpl.h */
class VideoSourceImpl : public VideoSourceInterface<VideoFrame> {
 public:
  virtual ~VideoSourceImpl();
  static VideoSourceImpl* Create(size_t width, size_t height, size_t target_fps, size_t capture_device_index);
  void AddOrUpdateSink(VideoSinkInterface<VideoFrame>* sink, const VideoSinkWants& wants) override;
  void RemoveSink(VideoSinkInterface<VideoFrame>* sink) override;
  void OnFrame(VideoFrame& frame) override;
 private:
  VideoSourceImpl();
  VideoBroadcaster broadcaster_;
};

/* VideoSourceImpl.cc */
VideoSourceImpl* VideoSourceImpl::Create(size_t width, size_t height, size_t fps, size_t idx) {
  auto v = std::make_unique<VideoSourceImpl>();
  return v.release();
}
VideoSourceImpl::VideoSourceImpl() {}
VideoSourceImpl::~VideoSourceImpl() {}
void VideoSourceImpl::AddOrUpdateSink(VideoSinkInterface<VideoFrame>* sink, const VideoSinkWants& wants) {
  broadcaster_.AddOrUpdateSink(sink, wants);
}
void VideoSourceImpl::RemoveSink(VideoSinkInterface<VideoFrame>* sink) { broadcaster_.RemoveSink(sink); }
void VideoSourceImpl::OnFrame(VideoFrame& frame) { broadcaster_.OnFrame(frame); }

Pass a VideoSourceImpl instance to VideoTrack; external texture/RGBA/NV12 data can be injected via VideoSourceImpl::OnFrame.

4.2 Audio local‑volume callback

In multi‑person voice chat, displaying real‑time volume levels is useful, but vanilla WebRTC lacks this feature.

4.2.1 Audio pipeline and ADM

AudioDeviceModule

: abstracts platform‑specific audio capture/playback; owns an AudioDeviceBuffer. AudioDeviceBuffer: holds an AudioTransport for data flow. AudioTransport: interface for feeding captured audio ( RecordedDataIsAvailable) and requesting playback data ( NeedMorePlayData). AudioProcessing: performs 3A (AEC, AGC, NS) on captured audio. AudioMixer: mixes remote audio streams. AudioState: orchestrates the above components.

class AudioDeviceModule : public rtc::RefCountInterface {
 public:
  static rtc::scoped_refptr<AudioDeviceModule> Create(AudioLayer audio_layer, TaskQueueFactory* task_queue_factory);
  virtual int32_t RegisterAudioCallback(AudioTransport* audioCallback) = 0;
  virtual int32_t StartPlayout() = 0;
  virtual int32_t StopPlayout() = 0;
  virtual bool Playing() const = 0;
  virtual int32_t StartRecording() = 0;
  virtual int32_t StopRecording() = 0;
  virtual bool Recording() const = 0;
 protected:
  ~AudioDeviceModule() override {}
};
class AudioDeviceBuffer {
  AudioTransport* audio_transport_cb_;
  // ... other members ...
};

int32_t AudioDeviceBuffer::RegisterAudioCallback(AudioTransport* audio_callback) {
  if (playing_ || recording_) return -1;
  audio_transport_cb_ = audio_callback;
  return 0;
}
class AudioTransport {
 public:
  virtual int32_t RecordedDataIsAvailable(const void* audioSamples, size_t nSamples,
      size_t nBytesPerSample, size_t nChannels, uint32_t samplesPerSec,
      uint32_t total_delay_ms, int32_t clockDrift, uint32_t currentMicLevel,
      bool keyPressed, uint32_t& newMicLevel) = 0;
  virtual int32_t NeedMorePlayData(size_t nSamples, size_t nBytesPerSample,
      size_t nChannels, uint32_t samplesPerSec, void* audioSamples,
      size_t& nSamplesOut, int64_t* elapsed_time_ms, int64_t* ntp_time_ms) = 0;
 protected:
  virtual ~AudioTransport() {}
};

Platform‑specific AudioDeviceModule implementations (Windows, Android, iOS) each contain a concrete AudioDeviceBuffer which holds a platform‑specific AudioTransport. The link is established via AudioDeviceModule::RegisterAudioCallback.

4.2.2 Custom solution

// CustomADMWrapper.h
class CustomADMWrapper : public webrtc::AudioDeviceModule, public webrtc::AudioTransport {
 public:
  // AudioTransport overrides
  int32_t RecordedDataIsAvailable(const void* audioSamples, size_t nSamples,
      size_t nBytesPerSample, size_t nChannels, uint32_t samples_per_sec,
      uint32_t total_delay_ms, int32_t clockDrift, uint32_t currentMicLevel,
      bool keyPressed, uint32_t& newMicLevel) override;
};

// CustomADMWrapper.cc
int32_t CustomADMWrapper::RecordedDataIsAvailable(const void* audioSamples, size_t nSamples,
    size_t nBytesPerSample, size_t nChannels, uint32_t samples_per_sec,
    uint32_t total_delay_ms, int32_t clockDrift, uint32_t currentMicLevel,
    bool keyPressed, uint32_t& newMicLevel) {
  // Compute volume level and report it upstream.
  return res;
}

5. Unified source management

WebRTC maintains separate branches for each platform. By extracting the common core into a single repository and keeping platform‑specific third‑party libraries in separate repos, developers can modify the core in one place and build for Windows, Android, or iOS without branch switching.

6. System architecture

Core layer: exposes public APIs, integrates WebRTC and signaling SDKs, manages business logic and logging.

Adapter layer: JNI on Android, Objective‑C on iOS, direct calls on Windows.

API layer: provides platform‑specific interfaces, integrates ADM, codec, and other platform features.

Core library wraps platform‑specific builds. DYRtcClient module integrates signaling SDK, WebRTC, and logging. DYRTCEngineKit_Windows/Android/iOS modules implement platform‑specific capture, encoding, and ADM logic.

By unifying the source and business logic, maintenance costs for WebRTC‑based products are greatly reduced.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Cross-platformCompilationC++Video processingaudio processingWebRTC
Douyu Streaming
Written by

Douyu Streaming

Official account of Douyu Streaming Development Department, sharing audio and video technology best practices.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.