一,本文目的
帮助理解OWT-client-android是如何使用webrtc 代码库的;
帮助理解OWT-client-android如何与OWT Server交互,接口协议的功能实现;
二,功能概述和模块关系
编译OWT android客户端,涉及https://github.com/open-webrtc-toolkit 的3个目录,刚开始容易让人不明白。
owt-deps-webrtc 是OWT项目用到的webrtc源码,目前版本是m88,主要增加支持了h265编码。
编译owt-client-native, 包含自动下载owt-deps-webrtc;
1,实现PeerConnectionChannel,主要封装了webrtc peerconnection
2, 在win和linux, 加入了msdk的外部编解码器,(调用intel GPU编解码器)
3,在win实现了videorendererd3d11, d3d11的render?
4, 实现了会议功能和呼叫,p2psdk/conference, sdk/p2p
5,实现了全部SDK模块的object C封装,sdk/base/objc, sdk/conference/objc, sdk/p2p/objc
编译owt-client-android,需要用到owt-client-native编译输出的so和jar文件;
oem@svr1804://home/oem/git/owt-client-android/dependencies/libwebrtc$ ll
total 8
drwxrwxr-x 2 oem oem 4096 4月 23 22:41 ./
drwxrwxr-x 3 oem oem 4096 4月 19 09:50 ../
lrwxrwxrwx 1 oem oem 49 4月 23 22:41 arm64-v8a -> /home/oem/git/native/src/out/dist/debug/arm64-v8a/
lrwxrwxrwx 1 oem oem 53 4月 23 22:41 libwebrtc.jar -> /home/oem/git/native/src/out/dist/debug/libwebrtc.jar
三,对象创建和初始化连接过程
src/sample/conference/src/main/java/owt/sample/conference/MainActivity.java
protected void onCreate(Bundle savedInstanceState) {
initConferenceClient();
private void initConferenceClient() {
conferenceClient = new ConferenceClient(configuration);
conferenceClient.addObserver(this);
src/sdk/conference/src/main/java/owt/conference/ConferenceClient.java
public ConferenceClient(ConferenceClientConfiguration configuration) {
Connect (join room)启动过程:
src/sample/conference/src/main/java/owt/sample/conference/MainActivity.java
private View.OnClickListener joinRoom = new View.OnClickListener() {
//向服务器获取令牌,例如 uri=https://192.168.1.106:3004/createToken/, 提供username,room参数
String uri = serverUrl + "/createToken/";
String token = HttpUtils.request(uri, "POST", joinBody.toString(), true);
conferenceClient.join(token, new ActionCallback<ConferenceInfo>() {
//创建SignalingChannel实例,保存token, 调用connect(
src/sdk/conference/src/main/java/owt/conference/ConferenceClient.java
public synchronized void join(String token, ActionCallback<ConferenceInfo> callback) {
signalingChannel = new SignalingChannel(token, this);
signalingChannel.connect(configuration);
}
src/sdk/conference/src/main/java/owt/conference/SignalingChannel.java
SignalingChannel(String token, SignalingChannelObserver observer) {
this.token = token;
this.observer = observer;
}
void connect(final ConferenceClientConfiguration configuration) {
try {
//使用之前创建的令牌,创建IO.socket,例如 url= https://192.168.1.106:8080
socketClient = IO.socket(url, opt);
// Do not listen EVENT_DISCONNECT event on this phase.
socketClient.on(Socket.EVENT_CONNECT, connectedCallback)
.on(Socket.EVENT_CONNECT_ERROR, connectErrorCallback)
.on(Socket.EVENT_RECONNECTING, reconnectingCallback)
socketClient.connect();
Connect (join room)连接成功返回过程:
src/sdk/conference/src/main/java/owt/conference/SignalingChannel.java
private final Listener connectedCallback = args -> callbackExecutor.execute(() -> {
login();
//通过socket发送login,loginInfo参数:token,userAgent,protocol;
//登录房间成功,回调observer.onRoomConnected()
private void login() throws JSONException {
socketClient.emit("login", loginInfo,
(Ack) (Object... args) -> callbackExecutor.execute(() -> {
observer.onRoomConnected((JSONObject) args[1]);
src/sdk/conference/src/main/java/owt/conference/ConferenceClient.java
public void onRoomConnected(final JSONObject info) {
callbackExecutor.execute(() -> {
ConferenceInfo conferenceInfo;
try {
joinCallback.onSuccess(conferenceInfo);
src/sample/conference/src/main/java/owt/sample/conference/MainActivity.java
public void onSuccess(ConferenceInfo conferenceInfo) {
requestPermission();
private void requestPermission() {
onConnectSucceed();
//切换到会议界面
private void onConnectSucceed() {
四,publish发布流过程
src/sample/conference/src/main/java/owt/sample/conference/MainActivity.java
private View.OnClickListener publish = new View.OnClickListener() {
public void onClick(View v) {
//创建capturer,并关联到localStream,localStream再附加到localRenderer;
capturer = OwtVideoCapturer.create(vga ? 640 : 1280, vga ? 480 : 720, 30, true,
isCameraFront);
localStream = new LocalStream(capturer,
new MediaConstraints.AudioTrackConstraints());
localStream.attach(localRenderer);
//成功回调,调整UI,获取streams
ActionCallback<Publication> callback = new ActionCallback<Publication>() {
@Override
public void onSuccess(final Publication result) {
runOnUiThread(() -> {
String uri = serverUrl
+ "/rooms/" + conferenceInfo.id()
+ "/streams/" + result.id();
//失败返回,调整UI
public void onFailure(final OwtError error) {
conferenceClient.publish(localStream, setPublishOptions(), callback);
发出publish信令,执行publish;
src/sdk/conference/src/main/java/owt/conference/ConferenceClient.java
public synchronized void publish(final LocalStream localStream, final PublishOptions options,
final ActionCallback<Publication> callback) {
//发送publish信令消息
sendSignalingMessage("publish", publishMsg, args -> {
if (extractMsg(0, args).equals("ok")) {
//创建一个只发送的ConferencePeerConnectionChannel,不接收音视频
// Do not receive video and audio for publication cpcc.
ConferencePeerConnectionChannel pcChannel =
getPeerConnection(result.getString("id"), false, false);
//pcChannel执行publish
pcChannel.publish(localStream, options);
//发送publish信令消息
void sendSignalingMessage(final String type, final JSONObject message, final Ack ack) {
signalingChannel.sendMsg(type, message, ack);
src/sdk/conference/src/main/java/owt/conference/SignalingChannel.java
void sendMsg(String type, JSONObject msg, Ack ack) {
socketClient.emit(type, msg, ack);
//pcChannel执行publish, addStream(), createOffer();
src/sdk/conference/src/main/java/owt/conference/ConferencePeerConnectionChannel.java
void publish(LocalStream localStream, PublishOptions options) {
stream = localStream;
addStream(GetMediaStream(localStream));
createOffer();
src/sdk/base/src/main/java/owt/base/PeerConnectionChannel.java
//对peerConnection添加audioTracks,videoTracks, 关联audioRtpSenders,videoRtpSenders
protected void addStream(final MediaStream mediaStream) {
protected void createOffer() {
peerConnection.createOffer(PeerConnectionChannel.this, sdpConstraints);
public void onCreateSuccess(final SessionDescription sessionDescription) {
observer.onLocalDescription(key, localSdp);
peerConnection.createOffer()成功返回之后的流程:
src/sdk/base/src/main/java/owt/base/PeerConnectionChannel.java
protected void createOffer() {
peerConnection.createOffer(PeerConnectionChannel.this, sdpConstraints);
public void onCreateSuccess(final SessionDescription sessionDescription) {
observer.onLocalDescription(key, localSdp);
src/sdk/conference/src/main/java/owt/conference/ConferenceClient.java
public void onLocalDescription(final String id, final SessionDescription localSdp) {
// send local SDP
sendSignalingMessage("soac", msg, null);
//收到soac消息
public void onProgressMessage(JSONObject msg) {
case "soac":
pcChannel.processSignalingMessage(msg.getJSONObject("data"));
//收到soac消息, setRemoteDescription(), soac消息携带remote SDP ?
src/sdk/base/src/main/java/owt/base/PeerConnectionChannel.java
public void processSignalingMessage(JSONObject data) throws JSONException {
setRemoteDescription(remoteSdp);
private void setRemoteDescription(final SessionDescription remoteDescription) {
peerConnection.setRemoteDescription(PeerConnectionChannel.this, remoteDescription);
//收到ready消息,publish成功完成
src/sdk/conference/src/main/java/owt/conference/ConferenceClient.java
public void onProgressMessage(JSONObject msg) {
case "ready":
processAck(msg.getString("id"));
private void processAck(final String id) {
if (pubCallbacks.containsKey(id)) {
callback.onSuccess(publication);
}
if (subCallbacks.containsKey(id)) {
callback.onSuccess(subscription);
}
//刷新界面
src/sample/conference/src/main/java/owt/sample/conference/MainActivity.java
private View.OnClickListener publish = new View.OnClickListener() {
public void onSuccess(final Publication result) {
runOnUiThread(() -> {
五,subscribe订阅流过程
src/sample/conference/src/main/java/owt/sample/conference/MainActivity.java
private View.OnClickListener subscribe = new View.OnClickListener() {
public void onClick(View v) {
//显示"Remote Stream List"对话框,选择一个stream,接着显示编码格式选择对话框
singleChoiceDialog.setPositiveButton("ok",
(dialog, which) -> chooseCodec(
public void chooseCodec(RemoteStream remoteStream) {
//编码格式选择对话框
if (simulcastStreamMap.containsKey(remoteStream.id())) {
chooseRid(remoteStream, chooseVideoCodec);
} else {
subscribeForward(remoteStream, chooseVideoCodec, null);
}
public void subscribeForward(RemoteStream remoteStream, String videoCodec, String rid) {
conferenceClient.subscribe(remoteStream, options,
new ActionCallback<Subscription>() {
//订阅成功
public void onSuccess(Subscription result) {
remoteStream.attach(remoteRenderer);
runOnUiThread(() -> {
subscribeBtn.setVisibility(View.GONE);
unSubscribeBtn.setVisibility(View.VISIBLE);
//订阅失败
public void onFailure(OwtError error) {
src/sdk/conference/src/main/java/owt/conference/ConferenceClient.java
sendSignalingMessage("subscribe", subscribeMsg, args -> {
//发送"subscribe"信令,收到成功的‘ok’消息;创建pcChannel,调用 pcChannel.subscribe
ConferencePeerConnectionChannel pcChannel =
getPeerConnection(result.getString("id"), subVideo, subAudio);
pcChannel.subscribe(remoteStream, options);
src/sdk/conference/src/main/java/owt/conference/ConferencePeerConnectionChannel.java
void subscribe(RemoteStream remoteStream, SubscribeOptions options) {
createOffer();
protected void createOffer() {
peerConnection.createOffer(PeerConnectionChannel.this, sdpConstraints);
createOffer成功返回:
src/sdk/base/src/main/java/owt/base/PeerConnectionChannel.java
public void onCreateSuccess(final SessionDescription sessionDescription) {
observer.onLocalDescription(key, localSdp);
peerConnection.setLocalDescription(PeerConnectionChannel.this, localSdp);
src/sdk/conference/src/main/java/owt/conference/ConferenceClient.java
public void onLocalDescription(final String id, final SessionDescription localSdp) {
// send SDP offer to OWT server
sendSignalingMessage("soac", msg, null);
public void onProgressMessage(JSONObject msg) {
switch (msg.getString("status")) {
case "soac":
//收到SDP answer消息;
pcChannel.processSignalingMessage(msg.getJSONObject("data"));
break;
case "ready":
//收到ready消息,成功返回
processAck(msg.getString("id"));
private void processAck(final String id) {
callback.onSuccess(subscription);
src/sample/conference/src/main/java/owt/sample/conference/MainActivity.java
public void subscribeForward(RemoteStream remoteStream, String videoCodec, String rid) {
conferenceClient.subscribe(remoteStream, options,
public void onSuccess(Subscription result) {
//subscribe remoteStream 成功,刷新界面,开始渲染视频
remoteStream.attach(remoteRenderer);
runOnUiThread(() -> {
subscribeBtn.setVisibility(View.GONE);
unSubscribeBtn.setVisibility(View.VISIBLE);