package com.netease.nim.demo.avchat; import android.Manifest; import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.text.TextUtils; import android.util.Log; import android.util.Pair; import android.view.View; import android.widget.Toast; import com.netease.nim.demo.DemoCache; import com.netease.nim.demo.R; import com.netease.nim.demo.avchat.activity.AVChatExitCode; import com.netease.nim.demo.avchat.constant.CallStateEnum; import com.netease.nim.demo.avchat.widgets.MultiSelectDialog; import com.netease.nim.uikit.common.ui.dialog.DialogMaker; import com.netease.nim.uikit.common.util.log.LogUtil; import com.netease.nim.uikit.permission.BaseMPermission; import com.netease.nimlib.sdk.ResponseCode; import com.netease.nimlib.sdk.avchat.AVChatCallback; import com.netease.nimlib.sdk.avchat.AVChatManager; import com.netease.nimlib.sdk.avchat.constant.AVChatAudioEffectMode; import com.netease.nimlib.sdk.avchat.constant.AVChatControlCommand; import com.netease.nimlib.sdk.avchat.constant.AVChatType; import com.netease.nimlib.sdk.avchat.constant.AVChatUserRole; import com.netease.nimlib.sdk.avchat.model.AVChatData; import com.netease.nimlib.sdk.avchat.model.AVChatNotifyOption; import com.netease.nimlib.sdk.avchat.constant.AVChatMediaCodecMode; import com.netease.nimlib.sdk.avchat.model.AVChatParameters; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; /** * 音视频管理器, 音视频相关功能管理 * Created by hzxuwen on 2015/4/23. */ public class AVChatUI implements AVChatUIListener { // constant private static final String TAG = "AVChatUI"; // data private Context context; private AVChatData avChatData; private final AVChatListener aVChatListener; private String receiverId; private AVChatAudio avChatAudio; private AVChatVideo avChatVideo; private AVChatSurface avChatSurface; private AVChatParameters avChatParameters; private String videoAccount; // 发送视频请求,onUserJoin回调的user account private CallStateEnum callingState = CallStateEnum.INVALID; private long timeBase = 0; // view private View root; // state public boolean canSwitchCamera = false; private boolean isClosedCamera = false; public AtomicBoolean isCallEstablish = new AtomicBoolean(false); private final String[] BASIC_PERMISSIONS = new String[]{Manifest.permission.CAMERA,}; //是否在录制 private boolean isRecording = false; // 检查存储 private boolean recordWarning = false; List<Pair<String, Boolean>> recordList = new LinkedList<Pair<String, Boolean>>(); public interface AVChatListener { void uiExit(); } public AVChatUI(Context context, View root, AVChatListener listener) { this.context = context; this.root = root; this.aVChatListener = listener; this.avChatParameters = new AVChatParameters(); configFromPreference(PreferenceManager.getDefaultSharedPreferences(context)); updateAVChatOptionalConfig(); } //Config from Preference private int videoCropRatio; private boolean videoAutoRotate; private int videoQuality; private boolean serverRecordAudio; private boolean serverRecordVideo; private boolean defaultFrontCamera; private boolean autoCallProximity; private int videoHwEncoderMode; private int videoHwDecoderMode; private boolean videoFpsReported; private int audioEffectAecMode; private int audioEffectNsMode; private int videoMaxBitrate; private int deviceDefaultRotation; private int deviceRotationOffset; private boolean audioHighQuality; private boolean audioDtx; private void configFromPreference(SharedPreferences preferences) { videoCropRatio = Integer.parseInt(preferences.getString(context.getString(R.string.nrtc_setting_vie_crop_ratio_key), "0")); videoAutoRotate = preferences.getBoolean(context.getString(R.string.nrtc_setting_vie_rotation_key), true); videoQuality = Integer.parseInt(preferences.getString(context.getString(R.string.nrtc_setting_vie_quality_key), 0 + "")); serverRecordAudio = preferences.getBoolean(context.getString(R.string.nrtc_setting_other_server_record_audio_key), false); serverRecordVideo = preferences.getBoolean(context.getString(R.string.nrtc_setting_other_server_record_video_key), false); defaultFrontCamera = preferences.getBoolean(context.getString(R.string.nrtc_setting_vie_default_front_camera_key), true); autoCallProximity = preferences.getBoolean(context.getString(R.string.nrtc_setting_voe_call_proximity_key), true); videoHwEncoderMode = Integer.parseInt(preferences.getString(context.getString(R.string.nrtc_setting_vie_hw_encoder_key), 0 + "")); videoHwDecoderMode = Integer.parseInt(preferences.getString(context.getString(R.string.nrtc_setting_vie_hw_decoder_key), 0 + "")); videoFpsReported = preferences.getBoolean(context.getString(R.string.nrtc_setting_vie_fps_reported_key), true); audioEffectAecMode = Integer.parseInt(preferences.getString(context.getString(R.string.nrtc_setting_voe_audio_aec_key), 2 + "")); audioEffectNsMode = Integer.parseInt(preferences.getString(context.getString(R.string.nrtc_setting_voe_audio_ns_key), 2 + "")); String value1 = preferences.getString(context.getString(R.string.nrtc_setting_vie_max_bitrate_key), 0 + ""); videoMaxBitrate = Integer.parseInt(TextUtils.isDigitsOnly(value1) && !TextUtils.isEmpty(value1) ? value1 : 0 + ""); String value2 = preferences.getString(context.getString(R.string.nrtc_setting_other_device_default_rotation_key), 0 + ""); deviceDefaultRotation = Integer.parseInt(TextUtils.isDigitsOnly(value2) && !TextUtils.isEmpty(value2) ? value2 : 0 + ""); String value3 = preferences.getString(context.getString(R.string.nrtc_setting_other_device_rotation_fixed_offset_key), 0 + ""); deviceRotationOffset = Integer.parseInt(TextUtils.isDigitsOnly(value3) && !TextUtils.isEmpty(value3) ? value3 : 0 + ""); audioHighQuality = preferences.getBoolean(context.getString(R.string.nrtc_setting_voe_high_quality_key), false); audioDtx = preferences.getBoolean(context.getString(R.string.nrtc_setting_voe_dtx_key), true); } /** * 1, autoCallProximity: 语音通话时使用, 距离感应自动黑屏 * 2, videoCropRatio: 制定固定的画面裁剪比例,发送端有效 * 3, videoAutoRotate: 结合自己设备角度和对方设备角度自动旋转画面 * 4, serverRecordAudio: 需要服务器录制语音, 同时需要 APP KEY 下面开通了服务器录制功能 * 5, serverRecordVideo: 需要服务器录制视频, 同时需要 APP KEY 下面开通了服务器录制功能 * 6, defaultFrontCamera: 默认是否使用前置摄像头 * 7, videoQuality: 视频质量调整, 最高建议使用480P * 8, videoFpsReported: 是否开启视频绘制帧率汇报 * 9, deviceDefaultRotation: 99.99%情况下你不需要设置这个参数, 当设备固定在水平方向时,并且设备不会移动, 这时是无法确定设备角度的,可以设置一个默认角度 * 10, deviceRotationOffset: 99.99%情况下你不需要设置这个参数, 当你的设备传感器获取的角度永远偏移固定值时设置,用于修正旋转角度 * 11, videoMaxBitrate: 视频最大码率设置, 100K ~ 5M. 如果没有特殊需求不要去设置,会影响SDK内部的调节机制 * 12, audioEffectAecMode: 语音处理选择, 默认使用平台内置,当你发现平台内置不好用时可以设置到SDK内置 * 13, audioEffectNsMode: 语音处理选择, 默认使用平台内置,当你发现平台内置不好用时可以设置到SDK内置 * 14, videoHwEncoderMode: 视频编码类型, 默认情况下不用设置. * 15, videoHwDecoderMode: 视频解码类型, 默认情况下不用设置. * 16, audioHighQuality: 高清语音,采用更高的采样率来传输语音 * 17, audioDtx: 非连续发送,当监测到人声非活跃状态时减少数据包的发送 */ private void updateAVChatOptionalConfig() { avChatParameters.setBoolean(AVChatParameters.KEY_AUDIO_CALL_PROXIMITY, autoCallProximity); avChatParameters.setInteger(AVChatParameters.KEY_VIDEO_FIXED_CROP_RATIO, videoCropRatio); avChatParameters.setBoolean(AVChatParameters.KEY_VIDEO_ROTATE_IN_RENDING, videoAutoRotate); avChatParameters.setBoolean(AVChatParameters.KEY_SERVER_AUDIO_RECORD, serverRecordAudio); avChatParameters.setBoolean(AVChatParameters.KEY_SERVER_VIDEO_RECORD, serverRecordVideo); avChatParameters.setBoolean(AVChatParameters.KEY_VIDEO_DEFAULT_FRONT_CAMERA, defaultFrontCamera); avChatParameters.setInteger(AVChatParameters.KEY_VIDEO_QUALITY, videoQuality); avChatParameters.setBoolean(AVChatParameters.KEY_VIDEO_FPS_REPORTED, videoFpsReported); avChatParameters.setInteger(AVChatParameters.KEY_DEVICE_DEFAULT_ROTATION, deviceDefaultRotation); avChatParameters.setInteger(AVChatParameters.KEY_DEVICE_ROTATION_FIXED_OFFSET, deviceRotationOffset); if (videoMaxBitrate > 0) { avChatParameters.setInteger(AVChatParameters.KEY_VIDEO_MAX_BITRATE, videoMaxBitrate * 1024); } switch (audioEffectAecMode) { case 0: avChatParameters.setString(AVChatParameters.KEY_AUDIO_EFFECT_ACOUSTIC_ECHO_CANCELER, AVChatAudioEffectMode.DISABLE); break; case 1: avChatParameters.setString(AVChatParameters.KEY_AUDIO_EFFECT_ACOUSTIC_ECHO_CANCELER, AVChatAudioEffectMode.SDK_BUILTIN); break; case 2: avChatParameters.setString(AVChatParameters.KEY_AUDIO_EFFECT_ACOUSTIC_ECHO_CANCELER, AVChatAudioEffectMode.PLATFORM_BUILTIN); break; } switch (audioEffectNsMode) { case 0: avChatParameters.setString(AVChatParameters.KEY_AUDIO_EFFECT_NOISE_SUPPRESSOR, AVChatAudioEffectMode.DISABLE); break; case 1: avChatParameters.setString(AVChatParameters.KEY_AUDIO_EFFECT_NOISE_SUPPRESSOR, AVChatAudioEffectMode.SDK_BUILTIN); break; case 2: avChatParameters.setString(AVChatParameters.KEY_AUDIO_EFFECT_NOISE_SUPPRESSOR, AVChatAudioEffectMode.PLATFORM_BUILTIN); break; } switch (videoHwEncoderMode) { case 0: avChatParameters.setString(AVChatParameters.KEY_VIDEO_ENCODER_MODE, AVChatMediaCodecMode.MEDIA_CODEC_AUTO); break; case 1: avChatParameters.setString(AVChatParameters.KEY_VIDEO_ENCODER_MODE, AVChatMediaCodecMode.MEDIA_CODEC_SOFTWARE); break; case 2: avChatParameters.setString(AVChatParameters.KEY_VIDEO_ENCODER_MODE, AVChatMediaCodecMode.MEDIA_CODEC_HARDWARE); break; } switch (videoHwDecoderMode) { case 0: avChatParameters.setString(AVChatParameters.KEY_VIDEO_DECODER_MODE, AVChatMediaCodecMode.MEDIA_CODEC_AUTO); break; case 1: avChatParameters.setString(AVChatParameters.KEY_VIDEO_DECODER_MODE, AVChatMediaCodecMode.MEDIA_CODEC_SOFTWARE); break; case 2: avChatParameters.setString(AVChatParameters.KEY_VIDEO_DECODER_MODE, AVChatMediaCodecMode.MEDIA_CODEC_HARDWARE); break; } avChatParameters.setBoolean(AVChatParameters.KEY_AUDIO_HIGH_QUALITY, audioHighQuality); avChatParameters.setBoolean(AVChatParameters.KEY_AUDIO_DTX_ENABLE, audioDtx); //观众角色,多人模式下使用. IM Demo没有多人通话, 全部设置为AVChatUserRole.NORMAL. avChatParameters.setInteger(AVChatParameters.KEY_SESSION_MULTI_MODE_USER_ROLE, AVChatUserRole.NORMAL); } /** * ******************************初始化****************************** */ /** * 初始化,包含初始化音频管理器, 视频管理器和视频界面绘制管理器。 * * @return boolean */ public boolean initiation() { AVChatProfile.getInstance().setAVChatting(true); avChatAudio = new AVChatAudio(context,root.findViewById(R.id.avchat_audio_layout), this, this); avChatVideo = new AVChatVideo(context, root.findViewById(R.id.avchat_video_layout), this, this); avChatSurface = new AVChatSurface(context, this, root.findViewById(R.id.avchat_surface_layout)); return true; } /** * ******************************拨打和接听*************************** */ /** * 来电 */ public void inComingCalling(AVChatData avChatData) { this.avChatData = avChatData; receiverId = avChatData.getAccount(); AVChatSoundPlayer.instance().play(AVChatSoundPlayer.RingerTypeEnum.RING); if (avChatData.getChatType() == AVChatType.AUDIO) { onCallStateChange(CallStateEnum.INCOMING_AUDIO_CALLING); } else { onCallStateChange(CallStateEnum.INCOMING_VIDEO_CALLING); } } /** * 拨打音视频 */ public void outGoingCalling(String account, final AVChatType callTypeEnum) { DialogMaker.showProgressDialog(context, null); AVChatSoundPlayer.instance().play(AVChatSoundPlayer.RingerTypeEnum.CONNECTING); this.receiverId = account; AVChatNotifyOption notifyOption = new AVChatNotifyOption(); notifyOption.extendMessage = "extra_data"; // 默认forceKeepCalling为true,开发者如果不需要离线持续呼叫功能可以将forceKeepCalling设为false // notifyOption.forceKeepCalling = false; AVChatManager.getInstance().enableRtc(); this.callingState = (callTypeEnum == AVChatType.VIDEO ? CallStateEnum.VIDEO : CallStateEnum.AUDIO); AVChatManager.getInstance().setParameters(avChatParameters); if (callTypeEnum == AVChatType.VIDEO) { AVChatManager.getInstance().enableVideo(); AVChatManager.getInstance().startVideoPreview(); } AVChatManager.getInstance().call2(account, callTypeEnum, notifyOption, new AVChatCallback<AVChatData>() { @Override public void onSuccess(AVChatData data) { avChatData = data; DialogMaker.dismissProgressDialog(); //如果需要使用视频预览功能,在此进行设置,调用setupLocalVideoRender //如果不需要视频预览功能,那么删掉下面if语句代码即可 if (callTypeEnum == AVChatType.VIDEO) { List<String> deniedPermissions = BaseMPermission.getDeniedPermissions((Activity) context, BASIC_PERMISSIONS); if (deniedPermissions != null && !deniedPermissions.isEmpty()) { avChatVideo.showNoneCameraPermissionView(true); return; } initLargeSurfaceView(DemoCache.getAccount()); canSwitchCamera = true; onCallStateChange(CallStateEnum.OUTGOING_VIDEO_CALLING); } } @Override public void onFailed(int code) { LogUtil.d(TAG, "avChat call failed code->" + code); DialogMaker.dismissProgressDialog(); AVChatSoundPlayer.instance().stop(); if (code == ResponseCode.RES_FORBIDDEN) { Toast.makeText(context, R.string.avchat_no_permission, Toast.LENGTH_SHORT).show(); } else { Toast.makeText(context, R.string.avchat_call_failed, Toast.LENGTH_SHORT).show(); } closeSessions(-1); } @Override public void onException(Throwable exception) { LogUtil.d(TAG, "avChat call onException->" + exception); DialogMaker.dismissProgressDialog(); AVChatSoundPlayer.instance().stop(); } }); if (callTypeEnum == AVChatType.AUDIO) { onCallStateChange(CallStateEnum.OUTGOING_AUDIO_CALLING); } else { onCallStateChange(CallStateEnum.OUTGOING_VIDEO_CALLING); } } /** * 状态改变 * * @param stateEnum */ public void onCallStateChange(CallStateEnum stateEnum) { callingState = stateEnum; avChatSurface.onCallStateChange(stateEnum); avChatAudio.onCallStateChange(stateEnum); avChatVideo.onCallStateChange(stateEnum); } /** * 挂断 * * @param type 音视频类型 */ private void hangUp(final int type) { if (callingState == CallStateEnum.INCOMING_VIDEO_CALLING || callingState == CallStateEnum.VIDEO) { AVChatManager.getInstance().stopVideoPreview(); } if ((type == AVChatExitCode.HANGUP || type == AVChatExitCode.PEER_NO_RESPONSE || type == AVChatExitCode.CANCEL) && avChatData != null) { AVChatManager.getInstance().hangUp2(avChatData.getChatId(), new AVChatCallback<Void>() { @Override public void onSuccess(Void aVoid) { } @Override public void onFailed(int code) { LogUtil.d(TAG, "hangup onFailed->" + code); } @Override public void onException(Throwable exception) { LogUtil.d(TAG, "hangup onException->" + exception); } }); } AVChatManager.getInstance().disableRtc(); closeSessions(type); AVChatSoundPlayer.instance().stop(); } /** * 关闭本地音视频各项功能 * * @param exitCode 音视频类型 */ public void closeSessions(int exitCode) { //not user hang up active and warning tone is playing,so wait its end Log.i(TAG, "close session -> " + AVChatExitCode.getExitString(exitCode)); if (avChatAudio != null) avChatAudio.closeSession(exitCode); if (avChatVideo != null) avChatVideo.closeSession(exitCode); showQuitToast(exitCode); isCallEstablish.set(false); canSwitchCamera = false; isClosedCamera = false; aVChatListener.uiExit(); } /** * 给出结束的提醒 * * @param code */ public void showQuitToast(int code) { switch (code) { case AVChatExitCode.NET_CHANGE: // 网络切换 case AVChatExitCode.NET_ERROR: // 网络异常 case AVChatExitCode.CONFIG_ERROR: // 服务器返回数据错误 Toast.makeText(context, R.string.avchat_net_error_then_quit, Toast.LENGTH_SHORT).show(); break; case AVChatExitCode.PEER_HANGUP: case AVChatExitCode.HANGUP: if (isCallEstablish.get()) { Toast.makeText(context, R.string.avchat_call_finish, Toast.LENGTH_SHORT).show(); } break; case AVChatExitCode.PEER_BUSY: Toast.makeText(context, R.string.avchat_peer_busy, Toast.LENGTH_SHORT).show(); break; case AVChatExitCode.PROTOCOL_INCOMPATIBLE_PEER_LOWER: Toast.makeText(context, R.string.avchat_peer_protocol_low_version, Toast.LENGTH_SHORT).show(); break; case AVChatExitCode.PROTOCOL_INCOMPATIBLE_SELF_LOWER: Toast.makeText(context, R.string.avchat_local_protocol_low_version, Toast.LENGTH_SHORT).show(); break; case AVChatExitCode.INVALIDE_CHANNELID: Toast.makeText(context, R.string.avchat_invalid_channel_id, Toast.LENGTH_SHORT).show(); break; case AVChatExitCode.LOCAL_CALL_BUSY: Toast.makeText(context, R.string.avchat_local_call_busy, Toast.LENGTH_SHORT).show(); break; default: break; } } /** * ******************************* 接听与拒绝操作 ********************************* */ /** * 拒绝来电 */ private void rejectInComingCall() { /** * 接收方拒绝通话 * AVChatCallback 回调函数 */ AVChatManager.getInstance().hangUp2(avChatData.getChatId(), new AVChatCallback<Void>() { @Override public void onSuccess(Void aVoid) { LogUtil.d(TAG, "reject onSuccess-" ); } @Override public void onFailed(int code) { LogUtil.d(TAG, "reject onFailed->" + code); } @Override public void onException(Throwable exception) { LogUtil.d(TAG, "reject onException"); } }); closeSessions(AVChatExitCode.REJECT); AVChatSoundPlayer.instance().stop(); } /** * 拒绝音视频切换 */ private void rejectAudioToVideo() { AVChatManager.getInstance().sendControlCommand(avChatData.getChatId(), AVChatControlCommand.SWITCH_AUDIO_TO_VIDEO_REJECT, new AVChatCallback<Void>() { @Override public void onSuccess(Void aVoid) { LogUtil.i(TAG, "rejectAudioToVideo success"); onCallStateChange(CallStateEnum.AUDIO); updateRecordTip(); } @Override public void onFailed(int code) { LogUtil.i(TAG, "rejectAudioToVideo onFailed"); } @Override public void onException(Throwable exception) { LogUtil.i(TAG, "rejectAudioToVideo onException"); } }); } /** * 接听来电 */ private void receiveInComingCall() { //接听,告知服务器,以便通知其他端 if (callingState == CallStateEnum.INCOMING_AUDIO_CALLING) { onCallStateChange(CallStateEnum.AUDIO_CONNECTING); } else { onCallStateChange(CallStateEnum.VIDEO_CONNECTING); } AVChatManager.getInstance().enableRtc(); AVChatManager.getInstance().setParameters(avChatParameters); if (callingState == CallStateEnum.VIDEO_CONNECTING) { AVChatManager.getInstance().enableVideo(); AVChatManager.getInstance().startVideoPreview(); } AVChatManager.getInstance().accept2(avChatData.getChatId(), new AVChatCallback<Void>() { @Override public void onSuccess(Void aVoid) { LogUtil.i(TAG, "accept success"); isCallEstablish.set(true); canSwitchCamera = true; } @Override public void onFailed(int code) { if (code == -1) { Toast.makeText(context, "本地音视频启动失败", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(context, "建立连接失败", Toast.LENGTH_SHORT).show(); } LogUtil.e(TAG, "accept onFailed->" + code); closeSessions(AVChatExitCode.CANCEL); } @Override public void onException(Throwable exception) { LogUtil.d(TAG, "accept exception->" + exception); } }); AVChatSoundPlayer.instance().stop(); } /*************************** AVChatUIListener ******************************/ /** * 点击挂断或取消 */ @Override public void onHangUp() { if (isCallEstablish.get()) { hangUp(AVChatExitCode.HANGUP); } else { hangUp(AVChatExitCode.CANCEL); } } /** * 拒绝操作,根据当前状态来选择合适的操作 */ @Override public void onRefuse() { switch (callingState) { case INCOMING_AUDIO_CALLING: case AUDIO_CONNECTING: rejectInComingCall(); break; case INCOMING_AUDIO_TO_VIDEO: rejectAudioToVideo(); break; case INCOMING_VIDEO_CALLING: case VIDEO_CONNECTING: // 连接中点击拒绝 rejectInComingCall(); break; default: break; } } /** * 开启操作,根据当前状态来选择合适的操作 */ @Override public void onReceive() { switch (callingState) { case INCOMING_AUDIO_CALLING: receiveInComingCall(); onCallStateChange(CallStateEnum.AUDIO_CONNECTING); break; case AUDIO_CONNECTING: // 连接中,继续点击开启 无反应 break; case INCOMING_VIDEO_CALLING: receiveInComingCall(); onCallStateChange(CallStateEnum.VIDEO_CONNECTING); break; case VIDEO_CONNECTING: // 连接中,继续点击开启 无反应 break; case INCOMING_AUDIO_TO_VIDEO: receiveAudioToVideo(); default: break; } } @Override public void toggleMute() { if (!isCallEstablish.get()) { // 连接未建立,在这里记录静音状态 return; } else { // 连接已经建立 if (!AVChatManager.getInstance().isLocalAudioMuted()) { // isMute是否处于静音状态 // 关闭音频 AVChatManager.getInstance().muteLocalAudio(true); } else { // 打开音频 AVChatManager.getInstance().muteLocalAudio(false); } } } @Override public void toggleSpeaker() { AVChatManager.getInstance().setSpeaker(!AVChatManager.getInstance().speakerEnabled()); // 设置扬声器是否开启 } @Override public void toggleRecord() { if (isRecording) { //停止录制 isRecording = false; updateRecordTip(); if (recordList.size() == 3) { if (recordList.get(0).second) { AVChatManager.getInstance().stopAudioRecording(); } if (recordList.get(1).second) { AVChatManager.getInstance().stopAVRecording(DemoCache.getAccount()); } if (recordList.get(2).second) { AVChatManager.getInstance().stopAVRecording(receiverId); } } } else { //探测对话框 final MultiSelectDialog selectDialog = new MultiSelectDialog(context); selectDialog.setTitle("选择录制内容"); selectDialog.setMessage("录制的内容会被单独保存"); selectDialog.setMessageTextColor(context.getResources().getColor(R.color.color_grey_999999)); selectDialog.addItem("语音对话", false); if (CallStateEnum.isAudioMode(callingState)) { selectDialog.addItem("我的音频", false); } else { selectDialog.addItem("我的音视频", false); } if (CallStateEnum.isAudioMode(callingState)) { selectDialog.addItem("对方音频", false); } else { selectDialog.addItem("对方音视频", false); } selectDialog.addPositiveButton("开始录制", MultiSelectDialog.NO_TEXT_COLOR, MultiSelectDialog.NO_TEXT_SIZE, new View.OnClickListener() { @Override public void onClick(View view) { isRecording = true; updateRecordTip(); List<Pair<String, Boolean>> selectDialogList = selectDialog.getItemTextList(); if (selectDialogList.size() == 3) { if (selectDialogList.get(0).second) { AVChatManager.getInstance().startAudioRecording(); } if (selectDialogList.get(1).second) { AVChatManager.getInstance().startAVRecording(DemoCache.getAccount()); } if (selectDialogList.get(2).second) { AVChatManager.getInstance().startAVRecording(receiverId); } } recordList.clear(); recordList.addAll(selectDialogList); selectDialog.dismiss(); } }); selectDialog.addNegativeButton(context.getString(com.netease.nim.uikit.R.string.cancel), MultiSelectDialog.NO_TEXT_COLOR, MultiSelectDialog.NO_TEXT_SIZE, new View.OnClickListener() { @Override public void onClick(View view) { selectDialog.dismiss(); } }); selectDialog.show(); } } private void updateRecordTip() { if (CallStateEnum.isAudioMode(callingState)) { avChatAudio.showRecordView(isRecording, recordWarning); } if (CallStateEnum.isVideoMode(callingState)) { avChatVideo.showRecordView(isRecording, recordWarning); } } public void showRecordWarning() { recordWarning = true; updateRecordTip(); } public void resetRecordTip() { recordWarning = false; isRecording = false; updateRecordTip(); } @Override public void videoSwitchAudio() { /** * 请求视频切换到音频 */ AVChatManager.getInstance().sendControlCommand(avChatData.getChatId(), AVChatControlCommand.SWITCH_VIDEO_TO_AUDIO, new AVChatCallback<Void>() { @Override public void onSuccess(Void aVoid) { LogUtil.d(TAG, "videoSwitchAudio onSuccess"); //直接切换 AVChatManager.getInstance().disableVideo(); // 界面布局切换。 onCallStateChange(CallStateEnum.AUDIO); onVideoToAudio(); } @Override public void onFailed(int code) { LogUtil.d(TAG, "videoSwitchAudio onFailed"); } @Override public void onException(Throwable exception) { LogUtil.d(TAG, "videoSwitchAudio onException"); } }); } @Override public void audioSwitchVideo() { /** * 请求音频切换到视频 */ AVChatManager.getInstance().sendControlCommand(avChatData.getChatId(), AVChatControlCommand.SWITCH_AUDIO_TO_VIDEO, new AVChatCallback<Void>() { @Override public void onSuccess(Void aVoid) { LogUtil.d(TAG, "requestSwitchToVideo onSuccess"); onCallStateChange(CallStateEnum.OUTGOING_AUDIO_TO_VIDEO); updateRecordTip(); } @Override public void onFailed(int code) { LogUtil.d(TAG, "requestSwitchToVideo onFailed" + code); } @Override public void onException(Throwable exception) { LogUtil.d(TAG, "requestSwitchToVideo onException" + exception); } }); } @Override public void switchCamera() { AVChatManager.getInstance().switchCamera(); // 切换摄像头(主要用于前置和后置摄像头切换) } @Override public void closeCamera() { if (!isClosedCamera) { // 关闭摄像头 AVChatManager.getInstance().muteLocalVideo(true); isClosedCamera = true; avChatSurface.localVideoOff(); } else { // 打开摄像头 AVChatManager.getInstance().muteLocalVideo(false); isClosedCamera = false; avChatSurface.localVideoOn(); } } /** * 音频切换为视频的请求 */ public void incomingAudioToVideo() { onCallStateChange(CallStateEnum.INCOMING_AUDIO_TO_VIDEO); } /** * 同意音频切换为视频 */ private void receiveAudioToVideo() { AVChatManager.getInstance().sendControlCommand(avChatData.getChatId(), AVChatControlCommand.SWITCH_AUDIO_TO_VIDEO_AGREE, new AVChatCallback<Void>() { @Override public void onSuccess(Void aVoid) { LogUtil.d(TAG, "receiveAudioToVideo onSuccess"); // 切换 AVChatManager.getInstance().enableVideo(); onAudioToVideo(); initAllSurfaceView(videoAccount); } @Override public void onFailed(int code) { LogUtil.d(TAG, "receiveAudioToVideo onFailed"); } @Override public void onException(Throwable exception) { LogUtil.d(TAG, "receiveAudioToVideo onException"); } }); } /** * 初始化大小图像 * * @param largeAccount 对方的帐号 */ public void initAllSurfaceView(String largeAccount) { avChatSurface.initLargeSurfaceView(largeAccount); avChatSurface.initSmallSurfaceView(DemoCache.getAccount()); } public void initLargeSurfaceView(String account) { avChatSurface.initLargeSurfaceView(account); } public void initSmallSurfaceView() { avChatSurface.initSmallSurfaceView(DemoCache.getAccount()); } /** * 音频切换为视频 */ public void onAudioToVideo() { onCallStateChange(CallStateEnum.VIDEO); avChatVideo.onAudioToVideo(AVChatManager.getInstance().isLocalAudioMuted(), isRecording, recordWarning); // isMute是否处于静音状态 //打开视频 AVChatManager.getInstance().enableVideo(); AVChatManager.getInstance().startVideoPreview(); // 是否在发送视频 即摄像头是否开启 if (AVChatManager.getInstance().isLocalVideoMuted()) { AVChatManager.getInstance().muteLocalVideo(false); avChatSurface.localVideoOn(); isClosedCamera = false; } } /** * 视频切换为音频 */ public void onVideoToAudio() { //关闭视频 AVChatManager.getInstance().stopVideoPreview(); AVChatManager.getInstance().disableVideo(); // 判断是否静音,扬声器是否开启,对界面相应控件进行显隐处理。 avChatAudio.onVideoToAudio(AVChatManager.getInstance().isLocalAudioMuted(), AVChatManager.getInstance().speakerEnabled(), isRecording, recordWarning); } public void peerVideoOff() { avChatSurface.peerVideoOff(); } public void peerVideoOn() { avChatSurface.peerVideoOn(); } private boolean needRestoreLocalVideo = false; private boolean needRestoreLocalAudio = false; //恢复视频和语音发送 public void resumeVideo() { if (needRestoreLocalVideo) { AVChatManager.getInstance().muteLocalVideo(false); needRestoreLocalVideo = false; } if (needRestoreLocalAudio) { AVChatManager.getInstance().muteLocalAudio(false); needRestoreLocalAudio = false; } } //关闭视频和语音发送. public void pauseVideo() { if (!AVChatManager.getInstance().isLocalVideoMuted()) { AVChatManager.getInstance().muteLocalVideo(true); needRestoreLocalVideo = true; } if (!AVChatManager.getInstance().isLocalAudioMuted()) { AVChatManager.getInstance().muteLocalAudio(true); needRestoreLocalAudio = true; } } public boolean canSwitchCamera() { return canSwitchCamera; } public CallStateEnum getCallingState() { return callingState; } public String getVideoAccount() { return videoAccount; } public void setVideoAccount(String videoAccount) { this.videoAccount = videoAccount; } public String getAccount() { if (receiverId != null) return receiverId; return null; } public long getTimeBase() { return timeBase; } public void setTimeBase(long timeBase) { this.timeBase = timeBase; } public AVChatData getAvChatData() { return avChatData; } }