Commit 24778a77 authored by sheteng's avatar sheteng

语音房间

parent 1a98f9aa
......@@ -13,3 +13,4 @@
.externalNativeBuild
.cxx
local.properties
/.idea/
......@@ -3,9 +3,12 @@
<component name="DesignSurface">
<option name="filePathToZoomLevelMap">
<map>
<entry key="..\:/ccwangluo/ccim_sdk_android/app/src/main/res/layout/activity_connect_channel.xml" value="0.3328125" />
<entry key="..\:/ccwangluo/ccim_sdk_android/app/src/main/res/layout/activity_login.xml" value="0.1612008072653885" />
<entry key="..\:/ccwangluo/ccim_sdk_android/app/src/main/res/layout/activity_main2.xml" value="0.1612008072653885" />
<entry key="..\:/ccwangluo/ccim_sdk_android/app/src/main/res/layout/activity_test.xml" value="0.3328125" />
<entry key="..\:/ccwangluo/ccim_sdk_android/app/src/main/res/layout/fragment_message.xml" value="0.291213768115942" />
<entry key="..\:/ccwangluo/ccim_sdk_android/app/src/main/res/layout/fragment_notifications.xml" value="0.3328125" />
<entry key="..\:/ccwangluo/ccim_sdk_android/app/src/main/res/layout/msg_row_item.xml" value="0.2617753623188406" />
<entry key="..\:/ccwangluo/ccim_sdk_android/app/src/main/res/layout/text_row_item.xml" value="0.291213768115942" />
<entry key="..\:/ccwangluo/ccim_sdk_android/app/src/main/res/menu/bottom_nav_menu.xml" value="0.32864583333333336" />
......
......@@ -8,7 +8,7 @@ android {
defaultConfig {
applicationId "com.ccwangluo.xiyuan22"
minSdkVersion 17
minSdkVersion 24
targetSdkVersion 30
versionCode 1
versionName "1.0"
......@@ -55,6 +55,7 @@ dependencies {
implementation 'org.greenrobot:eventbus:3.1.1'
// implementation(name: 'ccim-release', ext: 'aar')
implementation(name: 'janus-release', ext: 'aar')
implementation project(path: ':ccim')
implementation 'com.google.protobuf:protobuf-java:3.13.0'
implementation 'com.google.protobuf:protobuf-java-util:3.12.0'
......
......@@ -3,6 +3,7 @@
package="com.ccwangluo.im">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<application
android:name=".App"
......@@ -10,8 +11,11 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.CcwangluoIm">
android:supportsRtl="true">
<activity
android:name=".ui.login.TestActivity"
android:launchMode="singleTop"
android:exported="false" />
<activity
android:name=".ui.dashboard.ConnectActivity"
android:exported="false" />
......
......@@ -97,7 +97,7 @@ public class ChannelMsgAdapter extends BaseAdapter {
public void onFailed(int errorCode) {
System.out.println("onFailed");
}
});
},1.0f);
}
}
});
......
......@@ -103,7 +103,7 @@ public class PrivateMsgAdapter extends BaseAdapter {
public void onFailed(int errorCode) {
System.out.println("onFailed");
}
});
},0.3f);
// CCIMClient.getInstance().getAudioDuration(message.getMsgId(), new ResultEventCallback<Long>() {
// @Override
// public void onSuccess(Long aLong) {
......
package com.ccwangluo.im.ui.login;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import com.ccwangluo.im.R;
public class TestActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
System.out.println("TestActivity onCreate");
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
System.out.println("TestActivity onNewIntent");
}
}
\ No newline at end of file
......@@ -7,6 +7,8 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
......@@ -28,6 +30,7 @@ import com.ccwangluo.im.adapter.ChannelAdapter;
import com.ccwangluo.im.data.LoginViewModel;
import com.ccwangluo.im.databinding.FragmentNotificationsBinding;
import com.ccwangluo.im.listener.ChannelClickListener;
import com.ccwangluo.im.ui.login.TestActivity;
import com.ccwangluo.im.util.ToastUtil;
import java.util.ArrayList;
......@@ -167,6 +170,30 @@ public class NotificationsFragment extends Fragment {
builder.show();
}
});
binding.joinRoom.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
CCIMClient.getInstance().joinAudioRoom(getContext(), 1234, new ResultEventCallback<Void>() {
@Override
public void onSuccess(Void unused) {
ToastUtil.show(getContext(),"加入成功");
}
@Override
public void onFailed(int errorCode) {
ToastUtil.show(getContext(),"加入失败");
}
});
}
});
binding.exitRoom.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
CCIMClient.getInstance().exitAudioRoom();
}
});
return root;
}
......
......@@ -69,7 +69,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:hint="ip"
android:text="10.17.4.207"
android:text="106.15.237.67"
android:imeActionLabel="@string/action_sign_in_short"
android:imeOptions="actionDone"
android:selectAllOnFocus="true"
......
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.login.TestActivity">
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
......@@ -24,6 +24,19 @@
android:id="@+id/join_channel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:layout_marginLeft="10dp"
android:text="加入房间"
android:id="@+id/join_room"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:layout_marginLeft="10dp"
android:text="退出房间"
android:id="@+id/exit_room"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<ListView
......
......@@ -8,7 +8,7 @@ android {
buildToolsVersion "30.0.3"
defaultConfig {
minSdkVersion 16
minSdkVersion 24
targetSdkVersion 30
versionCode 3
versionName "1.0.2"
......@@ -40,6 +40,7 @@ android {
}
}
protobuf {
//配置protoc编译器
protoc {
......@@ -57,7 +58,14 @@ protobuf {
}
}
}
repositories {
flatDir {
dirs 'libs'
}
}
dependencies {
compileOnly(name: 'janus-release', ext: 'aar')
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.protobuf:protobuf-java:3.13.0'
implementation 'com.google.protobuf:protobuf-java-util:3.12.0'
......@@ -68,4 +76,5 @@ dependencies {
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
implementation 'com.karumi:dexter:6.2.3'
implementation 'org.webrtc:google-webrtc:1.0.28513'
}
\ No newline at end of file
package com.ccwangluo.ccim;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.media.MediaPlayer;
import android.os.Environment;
......@@ -10,6 +11,8 @@ import android.text.TextUtils;
import androidx.annotation.NonNull;
import com.ccwangluo.ccim.audio.JanusService;
import com.ccwangluo.ccim.audio.ServiceDelegate;
import com.ccwangluo.ccim.listener.OnClientConnectListener;
import com.ccwangluo.ccim.listener.OnConnectListener;
import com.ccwangluo.ccim.listener.OnPlayAudioCallback;
......@@ -31,6 +34,10 @@ import com.ccwangluo.ccim.modle.User;
import com.ccwangluo.ccim.util.ErrorCode;
import com.ccwangluo.ccim.util.MediaHelper;
import com.ccwangluo.ccim.util.PermissionUtils;
import com.github.helloiampau.janus.generated.Bundle;
import com.github.helloiampau.janus.generated.JanusEvent;
import com.github.helloiampau.janus.generated.JanusPlugins;
import com.github.helloiampau.janusclientsdk.JanusConfImpl;
import com.google.gson.Gson;
import com.google.protobuf.util.JsonFormat;
import com.karumi.dexter.Dexter;
......@@ -63,6 +70,7 @@ public class CCIMClient {
private Long lastSendTimemillis = 0L; //上次发送消息时间,用于时间间隔
private int maxSendMsgInterval = 0;
private int maxAudioDuration = 0;
private String audioServer;
private final int minAudioDuration = 500;
public String downloadPrefix;
public String cachePath;
......@@ -75,7 +83,7 @@ public class CCIMClient {
socketManager = new SocketManager(callBackManager, executor);
channelManager = new ChannelManager(socketManager, callBackManager);
messageManager = new MessageManager(socketManager, callBackManager, executor);
contactManager = new ContactManager(socketManager, callBackManager, messageManager,executor);
contactManager = new ContactManager(socketManager, callBackManager, messageManager, executor);
}
private static CCIMClient mInstance;
......@@ -104,6 +112,7 @@ public class CCIMClient {
maxSendMsgInterval = clientConfig.getSendMsgInterval();
maxAudioDuration = clientConfig.getMaxAudioDuration();
downloadPrefix = clientConfig.getDownloadPrefix();
audioServer = clientConfig.getAudioServer();
getContactList(true, new ResultEventCallback<PullContactsRes>() {
@Override
public void onSuccess(PullContactsRes pullContactsRes) {
......@@ -636,9 +645,10 @@ public class CCIMClient {
/**
* 播放音乐
* volume 范围0~1
*/
public void startPlayAudio(Long msgId, OnPlayAudioCallback callback) {
messageManager.playAudio(msgId, downloadPrefix, cachePath, callback);
public void startPlayAudio(Long msgId, OnPlayAudioCallback callback, Float volume) {
messageManager.playAudio(msgId, downloadPrefix, cachePath, callback, volume);
}
/**
......@@ -662,6 +672,27 @@ public class CCIMClient {
messageManager.updateReadOffset(userId, msgid, callback);
}
/**
* 加入房间
*/
public void joinAudioRoom(Context context, int roomId, ResultEventCallback<Void> callback) {
JanusConfImpl conf = new JanusConfImpl();
conf.url(audioServer);
conf.plugin(JanusPlugins.VIDEOROOM);
janusService = new JanusService(context, roomId, user, callback);
janusService.start(context,conf);
}
/**
* 退出房间
*/
public void exitAudioRoom() {
if (janusService != null) {
janusService.stop();
}
}
private JanusService janusService;
public void logout() {
isConnected = false;
......
package com.ccwangluo.ccim.audio;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.room.Room;
import com.ccwangluo.ccim.listener.ResultEventCallback;
import com.ccwangluo.ccim.modle.User;
import com.ccwangluo.ccim.util.ErrorCode;
import com.github.helloiampau.janus.generated.Bundle;
import com.github.helloiampau.janus.generated.Janus;
import com.github.helloiampau.janus.generated.JanusCommands;
import com.github.helloiampau.janus.generated.JanusData;
import com.github.helloiampau.janus.generated.JanusError;
import com.github.helloiampau.janus.generated.JanusEvent;
import com.github.helloiampau.janusclientsdk.JanusConfImpl;
import com.github.helloiampau.janusclientsdk.JanusDelegate;
import com.github.helloiampau.janusclientsdk.JanusFactory;
import com.github.helloiampau.janusclientsdk.rtc.AppRTCAudioManager;
import com.github.helloiampau.janusclientsdk.rtc.MediaBundle;
import com.google.gson.Gson;
import com.karumi.dexter.Dexter;
import com.karumi.dexter.MultiplePermissionsReport;
import com.karumi.dexter.PermissionToken;
import com.karumi.dexter.listener.PermissionRequest;
import com.karumi.dexter.listener.multi.MultiplePermissionsListener;
import org.webrtc.AudioTrack;
import org.webrtc.DataChannel;
import org.webrtc.EglBase;
import org.webrtc.VideoTrack;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
public class JanusService extends JanusDelegate {
private Janus _janus;
private EglBase _rootEgl;
private User user;
private Integer roomId;
private Boolean isJoin = false;
private AppRTCAudioManager audioManager;
private ResultEventCallback<Void> callback;
public JanusService(Context context, Integer roomId, User user, ResultEventCallback<Void> callback) {
audioManager = AppRTCAudioManager.create(context.getApplicationContext());
this.user = user;
this.roomId = roomId;
this.callback = callback;
}
public void start(Context context, JanusConfImpl conf) {
Dexter.withContext(context).withPermissions(Manifest.permission.RECORD_AUDIO).withListener(new MultiplePermissionsListener() {
@Override
public void onPermissionsChecked(MultiplePermissionsReport report) {
JanusFactory factory = new JanusFactory();
_janus = factory.create(conf,JanusService.this , context.getApplicationContext());
_rootEgl = factory.rootEgl();
_janus.init();
if (audioManager != null) {
audioManager.start((selectedAudioDevice, availableAudioDevices) -> {
}
);
}
}
@Override
public void onPermissionRationaleShouldBeShown(List<PermissionRequest> permissions, PermissionToken token) {
callback.onFailed(ErrorCode.ERROR_PERMISSION_DENIE);
}
}).check();
}
public void stop() {
this._janus.close();
}
public void hangup() {
this._janus.hangup();
}
@Override
public void onClose() {
if (audioManager != null) {
audioManager.stop();
}
}
@Override
public void onError(JanusError error, Bundle context) {
}
@Override
public void onReady() {
Bundle payload = Bundle.create();
payload.setInt("room", roomId);
payload.setString("display", new Gson().toJson(user));
this._janus.dispatch(JanusCommands.JOIN, payload);
}
@Override
public void onHangup(String reason) {
}
@Override
public void onMediaChanged(MediaBundle media) {
}
@Override
public void onDatachannelMessage(DataChannel.Buffer buffer) {
}
@Override
public String onOffer(String sdp) {
return sdp;
}
@Override
public String onAnswer(String sdp) {
return sdp;
}
@Override
public void onEvent(JanusEvent event, Bundle context) {
JanusData data = event.data();
if (data.getString("janus", "").equals("error") && context.getString("command", "").equals(JanusCommands.JOIN)) {
System.out.println("加入失败");
if (callback!= null) {
callback.onFailed(ErrorCode.JOIN_AUDIO_FAILED);
}
return;
}
if (data.getString("videoroom", "").equals("joined")) {
if (!isJoin) {
isJoin = true;
Bundle payload = Bundle.create();
payload.setBool("audio", true);
payload.setBool("video", false);
payload.setBool("data", false);
this._janus.dispatch(JanusCommands.PUBLISH, payload);
System.out.println("加入成功");
List<JanusData> publishers = data.getList("publishers");
subscribePublishers(publishers);
if (callback!= null) {
callback.onSuccess(null);
}
}
return;
}
List<JanusData> publishers = data.getList("publishers");
if(publishers.size() > 0) {
subscribePublishers(publishers);
return;
}
}
private void subscribePublishers(List<JanusData> publishers) {
if(publishers.size() > 0) {
for(JanusData publisher : publishers) {
Bundle payload = Bundle.create();
payload.setInt("room", roomId);
payload.setInt("feed", publisher.getInt("id", -1));
// payload.setInt("feed", publisher.getString("display", ""));
// Publisher pub = new Publisher(publisher);
// publishersMap.put(pub.handleId, pub);
this._janus.dispatch(JanusCommands.SUBSCRIBE, payload);
}
}
}
}
package com.ccwangluo.ccim.audio;
import com.github.helloiampau.janus.generated.Bundle;
import com.github.helloiampau.janus.generated.JanusEvent;
public interface ServiceDelegate {
public void onEvent(JanusEvent event, Bundle payload);
}
......@@ -251,7 +251,7 @@ public class MessageManager {
}
public void playAudio(Long msgId, String downloadPrefix, String cachePath, OnPlayAudioCallback resultCallback) {
public void playAudio(Long msgId, String downloadPrefix, String cachePath, OnPlayAudioCallback resultCallback,Float volume) {
executor.execute(() -> {
MessageContent messageContent = db.messageDao().getMsgById(msgId);
if (messageContent == null) {
......@@ -268,7 +268,7 @@ public class MessageManager {
FileUtils.downLoadFile(url, file, new ResultEventCallback<File>() {
@Override
public void onSuccess(File file) {
MediaHelper.getInstance().play(file.getAbsolutePath(), resultCallback);
MediaHelper.getInstance().play(file.getAbsolutePath(), resultCallback,volume);
}
@Override
......@@ -277,7 +277,7 @@ public class MessageManager {
}
});
} else {
MediaHelper.getInstance().play(file.getAbsolutePath(), resultCallback);
MediaHelper.getInstance().play(file.getAbsolutePath(), resultCallback,volume);
}
});
......
......@@ -15,5 +15,7 @@ public class ErrorCode {
public static final int MSG_CONTENT_IS_EMPTY = 910; //发送消息体为空
public static final int START_VOICE_FAILED = 911; //录音创建失败
public static final int ERROR_PERMISSION_DENIE = 912; //权限拒绝
public static final int JOIN_AUDIO_FAILED = 913; //加入语音失败
}
package com.ccwangluo.ccim.util;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.os.Build;
......@@ -106,7 +107,7 @@ public class MediaHelper {
*
* @param filePath
*/
public void play(String filePath, OnPlayAudioCallback resultCallback) {
public void play(String filePath, OnPlayAudioCallback resultCallback, Float volume) {
if (mediaPlayer == null) {
mediaPlayer = new MediaPlayer();
} else {
......@@ -117,6 +118,8 @@ public class MediaHelper {
}
try {
mediaPlayer.setDataSource(filePath);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setVolume(volume, volume);
mediaPlayer.prepare();
mediaPlayer.start();
resultCallback.onStart();
......
......@@ -2,8 +2,6 @@ syntax = "proto3";
package ccim.protocol;
option csharp_namespace = "CCIM.Protocol";
import "channel.proto";
import "common.proto";
import "contact.proto";
......@@ -118,6 +116,7 @@ message ConnectResponse {
int32 send_msg_interval = 1;
int32 max_audio_duration = 2;
string download_prefix = 3; // 下载地址前缀
string audio_server = 4; // 语音服务器
}
bool connected = 1; //是否连接成功
int32 heart_beat_interval = 2;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment