RTC Framework Design for Cloud Music Apps: A Unified Real-Time Communication Architecture
The article describes NetEase Cloud Music’s unified real‑time communication framework, which consolidates disparate audio/video features into a single, container‑based architecture using a common IPlayer interface, dynamic method generation, switchable players, and state‑machine management to streamline development, ensure business isolation, and provide seamless, controllable RTC services across all apps.
This article introduces the design and implementation of a unified RTC (Real-Time Communication) framework for NetEase Cloud Music's various applications.
What is RTC: RTC (Real-Time Communication) provides high-concurrency, low-latency, HD smooth, and secure real-time audio and video services across all scenarios. It enables features like one-on-one and multi-party video calls. Service providers include Agora, NetEase IM, Volcano Engine, and Tencent Cloud.
Background: With multiple Cloud Music apps involving RTC business (audio/video livestreaming, PK battles, party rooms, 1v1 chat), different teams were building separate implementations, leading to duplicated work. A unified RTC framework was needed to improve development efficiency.
Design Principles: Six key principles: Function Aggregation (encapsulating features in containers), Business Isolation (separate containers for different businesses), Unified Invocation (single entry point), State Maintenance (precise state management), Seamless Switching (transparent container switching), and Core Controllability (monitoring and fault warning).
Solution - Player Encapsulation: Different Player types (streaming push, streaming pull, RTC Player) share common interfaces. The IPlayer interface defines start, stop, and setParam methods. To handle diverse Player-specific methods (mute, volume control), a dynamic method invocation approach using APT (Annotation Processing Tool) and JavaPoet generates wrapper classes that map keys to methods.
interface IPlayer<DS : IDataSource, CB : ICallback> {
fun start(ds: DS)
fun stop()
fun <T : Any> setParam(key: String, value: T?)
......
}Player Switching: For partial RTC scenarios, SwitchablePlayer uses decorator pattern to wrap actual Players and add switching capabilities. Switching is triggered when the IDataSource type changes. The framework provides caching for reusable Players.
abstract fun getPlayer(ds: IDataSource): AbsPlayer<out IDataSource, out ICallback>?
class LivePKSwitchablePlayer : SwitchablePlayer(false) {
override fun getPlayer(ds: IDataSource): AbsPlayer<out IDataSource, out ICallback> {
return when (ds) {
is LiveDataSource -> LivePlayer()
is PKDataSource -> PKPlayer()
else -> LivePlayer()
}
}
}Process Encapsulation: Using StateMachine for state management with status codes observable by upper layers. LiveData handles status and event notifications.
class RtcHolder : IRtcHolder {
private val _rtcState = MutableLiveData(RtcStatus.IDLE)
private val _rtcEvent = MutableLiveData(RtcEvent.IDLE)
val rtcState = _rtcState.distinctUntilChanged()
val rtcEvent = _rtcEvent.distinctUntilChanged()
private val callBack = object : IRtcCallBack {
override fun onCurrentStateChange(stateCode: Int) {
_rtcState.value = stateCode
}
override fun onEvent(eventCode: Int) {
_rtcEvent.value = eventCode
}
}
}The framework provides three main concerns for business callers: Start/Stop entry points,通话状态 monitoring, and core event callbacks. This architecture enables efficient development across different RTC scenarios while maintaining clean separation of concerns.
NetEase Cloud Music Tech Team
Official account of NetEase Cloud Music Tech Team
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.