Android 匯入EaseUI後實現音視訊功能
阿新 • • 發佈:2019-02-03
專案中使用EaseUI可以快速的實現簡單的聊天功能,但是由於EaseUI中沒有實現音視訊功能,因此如果需要實現音視訊功能,需要自己手動實現,不過由於EaseUI實現了大部分的功能,所以,只需要在此基礎上修改即可
1.首先需要音視訊的的介面
VideoCall佈局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true" >
<com.hyphenate.media.EMOppositeSurfaceView
android:id="@+id/opposite_surface"
android:layout_width="match_parent"
android:layout_height ="match_parent" />
<RelativeLayout
android:id="@+id/ll_btns"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="20dp"
android:paddingRight="20dp" >
<LinearLayout
android:id="@+id/ll_top_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginTop="5dp"
android:gravity="center_horizontal"
android:orientation="vertical" >
<TextView
android:id="@+id/tv_call_state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:textColor="@color/voip_interface_text_color"
android:textSize="22sp"
android:visibility="visible" />
<com.hyphenate.easeui.widget.MyChronometer
android:id="@+id/chronometer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Chronometer"
android:textColor="#fff"
android:visibility="invisible"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/tv_is_p2p"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#fff" />
<TextView
android:id="@+id/tv_nick"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="張三"
android:textColor="@android:color/white"
android:textSize="20sp"
tools:ignore="HardcodedText" />
</LinearLayout>
<!-- 演示視訊錄製功能 -->
<Button
android:id="@+id/btn_record_video"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/recording_video"
android:layout_below="@id/ll_top_container"
android:visibility="gone"
/>
<Button
android:id="@+id/btn_switch_camera"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/switch_camera"
android:layout_below="@id/btn_record_video"
/>
<Button
android:id="@+id/btn_capture_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/capture_image"
android:layout_below="@id/btn_switch_camera"
/>
<SeekBar
android:id="@+id/seekbar_y_detal"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_below="@id/btn_capture_image"
android:max="200"
android:progress="100"
/>
<!-- <Button
android:layout_marginTop="3dp"
android:id="@+id/btn_toggle_video_stream"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="暫停視訊"
android:layout_below="@id/btn_record_video"
/> -->
<TextView
android:id="@+id/tv_call_monitor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/local_surface"
android:layout_alignParentRight="true"
android:textColor="#afff"
android:textSize="12sp"
android:layout_marginBottom="6dp"
/>
<com.hyphenate.media.EMLocalSurfaceView
android:id="@+id/local_surface"
android:layout_width="100dp"
android:layout_height="120dp"
android:layout_marginTop="10dp"
android:layout_alignParentRight="true" />
<!--android:layout_above="@+id/ll_surface_baseline"-->
<LinearLayout
android:id="@+id/ll_surface_baseline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="vertical"
android:paddingTop="8dp" >
<View
android:layout_width="match_parent"
android:layout_height="1px" />
<LinearLayout
android:id="@+id/ll_bottom_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="18dp" >
<LinearLayout
android:id="@+id/ll_voice_control"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
tools:ignore="DisableBaselineAlignment">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical" >
<ImageView
android:id="@+id/iv_mute"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:src="@drawable/em_icon_mute_normal" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:gravity="center"
android:text="@string/mute"
android:textColor="#666167" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical" >
<ImageView
android:id="@+id/iv_handsfree"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:src="@drawable/em_icon_speaker_normal" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:gravity="center"
android:text="@string/Hands_free"
android:textColor="#666167" />
</LinearLayout>
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp" >
<Button
android:id="@+id/btn_hangup_call"
android:layout_width="fill_parent"
android:layout_height="55dp"
android:background="@drawable/em_call_hangup_bg"
android:gravity="center"
android:text="@string/hang_up"
android:textColor="@android:color/white"
android:textSize="20sp"
android:visibility="invisible" />
<LinearLayout
android:id="@+id/ll_coming_call"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:id="@+id/btn_refuse_call"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_weight="1"
android:background="@drawable/em_call_hangup_bg"
android:gravity="center"
android:text="@string/hang_up"
android:textColor="@android:color/white"
android:textSize="20sp" />
<Button
android:id="@+id/btn_answer_call"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_marginLeft="50dp"
android:layout_weight="1"
android:background="@drawable/em_call_answer_bg"
android:gravity="center"
android:text="@string/answer"
android:textColor="@android:color/white"
android:textSize="20sp" />
</LinearLayout>
</FrameLayout>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
<TextView
android:id="@+id/tv_network_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:textColor="@android:color/white"
android:layout_centerInParent="true"
/>
</RelativeLayout>
VioceCall佈局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#252C39"
android:orientation="vertical"
android:id="@+id/root_layout"
android:paddingLeft="20dp"
android:paddingRight="20dp" >
<LinearLayout
android:id="@+id/topLayout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="5dp"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical" >
<TextView
android:id="@+id/tv_call_state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/voip_interface_text_color"
android:textSize="22sp"
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:visibility="visible" />
<com.hyphenate.easeui.widget.MyChronometer
android:visibility="invisible"
android:id="@+id/chronometer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#fff"
android:text="Chronometer" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tv_is_p2p"
android:textColor="#fff"
/>
<TextView
android:id="@+id/tv_calling_duration"
android:layout_width="wrap_content"
android:layout_height="25dp"
android:textColor="@color/voip_interface_text_color"
android:textSize="15sp"
android:visibility="visible" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:gravity="center_horizontal"
android:orientation="vertical"
android:layout_weight="2" >
<ImageView
android:id="@+id/swing_card"
android:layout_width="60dp"
android:layout_height="60dp"
android:scaleType="fitXY"
android:layout_marginTop="10dp"
android:src="@drawable/em_default_avatar" />
<TextView
android:id="@+id/tv_nick"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:textColor="@android:color/white"
android:textSize="20sp"
android:text="張三"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/tv_network_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:textColor="@android:color/white" />
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/ll_voice_control"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
tools:ignore="DisableBaselineAlignment">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical" >
<ImageView
android:id="@+id/iv_mute"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:src="@drawable/em_icon_mute_normal" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:gravity="center"
android:textColor="#666167"
android:text="@string/mute" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical" >
<ImageView
android:id="@+id/iv_handsfree"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:src="@drawable/em_icon_speaker_normal" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:gravity="center"
android:textColor="#666167"
android:text="@string/Hands_free" />
</LinearLayout>
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp" >
<Button
android:id="@+id/btn_hangup_call"
android:layout_width="fill_parent"
android:layout_height="60dp"
android:background="@drawable/em_call_hangup_bg"
android:gravity="center"
android:text="@string/hang_up"
android:textColor="@android:color/white"
android:textSize="20sp"
android:visibility="invisible" />
<LinearLayout
android:id="@+id/ll_coming_call"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:id="@+id/btn_refuse_call"
android:layout_width="wrap_content"
android:layout_height="60dp"
android:layout_weight="1"
android:background="@drawable/em_call_hangup_bg"
android:gravity="center"
android:text="@string/hang_up"
android:textColor="@android:color/white"
android:textSize="20sp" />
<Button
android:id="@+id/btn_answer_call"
android:layout_width="wrap_content"
android:layout_height="60dp"
android:layout_marginLeft="30dp"
android:layout_weight="1"
android:background="@drawable/em_call_answer_bg"
android:gravity="center"
android:text="@string/answer"
android:textColor="@android:color/white"
android:textSize="20sp" />
</LinearLayout>
</FrameLayout>
</LinearLayout>
</LinearLayout>
視訊介面VideoCallActivity:
public class VideoCallActivity extends CallActivity implements OnClickListener {
private boolean isMuteState;
private boolean isHandsfreeState;
private boolean isAnswered;
private boolean endCallTriggerByMe = false;
private boolean monitor = true;
private TextView callStateTextView;
private LinearLayout comingBtnContainer;
private Button refuseBtn;
private Button answerBtn;
private Button hangupBtn;
private ImageView muteImage;
private ImageView handsFreeImage;
private TextView nickTextView;
private Chronometer chronometer;
private LinearLayout voiceContronlLayout;
private RelativeLayout rootContainer;
private LinearLayout topContainer;
private LinearLayout bottomContainer;
private TextView monitorTextView;
private TextView netwrokStatusVeiw;
private Handler uiHandler;
private boolean isInCalling;
boolean isRecording = false;
// private Button recordBtn;
private EMVideoCallHelper callHelper;
private Button toggleVideoBtn;
private BrightnessDataProcess dataProcessor = new BrightnessDataProcess();
// dynamic adjust brightness
class BrightnessDataProcess implements EMCameraDataProcessor {
byte yDelta = 0;
synchronized void setYDelta(byte yDelta) {
Log.d("VideoCallActivity", "brigntness uDelta:" + yDelta);
this.yDelta = yDelta;
}
// data size is width*height*2
// the first width*height is Y, second part is UV
// the storage layout detailed please refer 2.x demo CameraHelper.onPreviewFrame
@Override
public synchronized void onProcessData(byte[] data, Camera camera, final int width, final int height, final int rotateAngel) {
int wh = width * height;
for (int i = 0; i < wh; i++) {
int d = (data[i] & 0xFF) + yDelta;
d = d < 16 ? 16 : d;
d = d > 235 ? 235 : d;
data[i] = (byte)d;
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState != null){
finish();
return;
}
setContentView(R.layout.em_activity_video_call);
DemoHelper.getInstance().isVideoCalling = true;
callType = 1;
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
uiHandler = new Handler();
callStateTextView = (TextView) findViewById(R.id.tv_call_state);
comingBtnContainer = (LinearLayout) findViewById(R.id.ll_coming_call);
rootContainer = (RelativeLayout) findViewById(R.id.root_layout);
refuseBtn = (Button) findViewById(R.id.btn_refuse_call);
answerBtn = (Button) findViewById(R.id.btn_answer_call);
hangupBtn = (Button) findViewById(R.id.btn_hangup_call);
muteImage = (ImageView) findViewById(R.id.iv_mute);
handsFreeImage = (ImageView) findViewById(R.id.iv_handsfree);
callStateTextView = (TextView) findViewById(R.id.tv_call_state);
nickTextView = (TextView) findViewById(R.id.tv_nick);
chronometer = (Chronometer) findViewById(R.id.chronometer);
voiceContronlLayout = (LinearLayout) findViewById(R.id.ll_voice_control);
RelativeLayout btnsContainer = (RelativeLayout) findViewById(R.id.ll_btns);
topContainer = (LinearLayout) findViewById(R.id.ll_top_container);
bottomContainer = (LinearLayout) findViewById(R.id.ll_bottom_container);
monitorTextView = (TextView) findViewById(R.id.tv_call_monitor);
netwrokStatusVeiw = (TextView) findViewById(R.id.tv_network_status);
// recordBtn = (Button) findViewById(R.id.btn_record_video);
Button switchCameraBtn = (Button) findViewById(R.id.btn_switch_camera);
Button captureImageBtn = (Button) findViewById(R.id.btn_capture_image);
SeekBar YDeltaSeekBar = (SeekBar) findViewById(R.id.seekbar_y_detal);
refuseBtn.setOnClickListener(this);
answerBtn.setOnClickListener(this);
hangupBtn.setOnClickListener(this);
muteImage.setOnClickListener(this);
handsFreeImage.setOnClickListener(this);
rootContainer.setOnClickListener(this);
// recordBtn.setOnClickListener(this);
switchCameraBtn.setOnClickListener(this);
captureImageBtn.setOnClickListener(this);
YDeltaSeekBar.setOnSeekBarChangeListener(new YDeltaSeekBarListener());
msgid = UUID.randomUUID().toString();
isInComingCall = getIntent().getBooleanExtra("isComingCall", false);
username = getIntent().getStringExtra("username");
nickTextView.setText(username);
// local surfaceview
localSurface = (EMLocalSurfaceView) findViewById(R.id.local_surface);
localSurface.setZOrderMediaOverlay(true);
localSurface.setZOrderOnTop(true);
// remote surfaceview
oppositeSurface = (EMOppositeSurfaceView) findViewById(R.id.opposite_surface);
// set call state listener
addCallStateListener();
if (!isInComingCall) {// outgoing call
soundPool = new SoundPool(1, AudioManager.STREAM_RING, 0);
outgoing = soundPool.load(this, R.raw.em_outgoing, 1);
comingBtnContainer.setVisibility(View.INVISIBLE);
hangupBtn.setVisibility(View.VISIBLE);
String st = getResources().getString(R.string.Are_connected_to_each_other);
callStateTextView.setText(st);
EMClient.getInstance().callManager().setSurfaceView(localSurface, oppositeSurface);
handler.sendEmptyMessage(MSG_CALL_MAKE_VIDEO);
handler.postDelayed(new Runnable() {
public void run() {
streamID = playMakeCallSounds();
}
}, 300);
} else { // incoming call
callStateTextView.setText("Ringing");
if(EMClient.getInstance().callManager().getCallState() == EMCallStateChangeListener.CallState.IDLE
|| EMClient.getInstance().callManager().getCallState() == EMCallStateChangeListener.CallState.DISCONNECTED) {
// the call has ended
finish();
return;
}
voiceContronlLayout.setVisibility(View.INVISIBLE);
localSurface.setVisibility(View.INVISIBLE);
Uri ringUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
audioManager.setMode(AudioManager.MODE_RINGTONE);
audioManager.setSpeakerphoneOn(true);
ringtone = RingtoneManager.getRingtone(this, ringUri);
ringtone.play();
EMClient.getInstance().callManager().setSurfaceView(localSurface, oppositeSurface);
}
final int MAKE_CALL_TIMEOUT = 50 * 1000;
handler.removeCallbacks(timeoutHangup);
handler.postDelayed(timeoutHangup, MAKE_CALL_TIMEOUT);
// get instance of call helper, should be called after setSurfaceView was called
callHelper = EMClient.getInstance().callManager().getVideoCallHelper();
EMClient.getInstance().callManager().setCameraDataProcessor(dataProcessor);
}
class YDeltaSeekBarListener implements SeekBar.OnSeekBarChangeListener {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
dataProcessor.setYDelta((byte)(20.0f * (progress - 50) / 50.0f));
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
}
/**
* set call state listener
*/
void addCallStateListener() {
callStateListener = new EMCallStateChangeListener() {
@Override
public void onCallStateChanged(final CallState callState, final CallError error) {
switch (callState) {
case CONNECTING: // is connecting
runOnUiThread(new Runnable() {
@Override
public void run() {
callStateTextView.setText(R.string.Are_connected_to_each_other);
}
});
break;
case CONNECTED: // connected
runOnUiThread(new Runnable() {
@Override
public void run() {
callStateTextView.setText(R.string.have_connected_with);
}
});
break;
case ACCEPTED: // call is accepted
handler.removeCallbacks(timeoutHangup);
runOnUiThread(new Runnable() {
@Override
public void run() {
try {
if (soundPool != null)
soundPool.stop(streamID);
EMLog.d("EMCallManager", "soundPool stop ACCEPTED");
} catch (Exception e) {
}
openSpeakerOn();
((TextView)findViewById(R.id.tv_is_p2p)).setText(EMClient.getInstance().callManager().isDirectCall()
? R.string.direct_call : R.string.relay_call);
handsFreeImage.setImageResource(R.drawable.em_icon_speaker_on);
isHandsfreeState = true;
isInCalling = true;
chronometer.setVisibility(View.VISIBLE);
chronometer.setBase(SystemClock.elapsedRealtime());
// call durations start
chronometer.start();
nickTextView.setVisibility(View.INVISIBLE);
callStateTextView.setText(R.string.In_the_call);
// recordBtn.setVisibility(View.VISIBLE);
callingState = CallingState.NORMAL;
startMonitor();
}
});
break;
case NETWORK_DISCONNECTED:
runOnUiThread(new Runnable() {
public void run() {
netwrokStatusVeiw.setVisibility(View.VISIBLE);
netwrokStatusVeiw.setText(R.string.network_unavailable);
}
});
break;
case NETWORK_UNSTABLE:
runOnUiThread(new Runnable() {
public void run() {
netwrokStatusVeiw.setVisibility(View.VISIBLE);
if(error == CallError.ERROR_NO_DATA){
netwrokStatusVeiw.setText(R.string.no_call_data);
}else{
netwrokStatusVeiw.setText(R.string.network_unstable);
}
}
});
break;
case NETWORK_NORMAL:
runOnUiThread(new Runnable() {
public void run() {
netwrokStatusVeiw.setVisibility(View.INVISIBLE);
}
});
break;
case VIDEO_PAUSE:
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(getApplicationContext(), "VIDEO_PAUSE", Toast.LENGTH_SHORT).show();
}
});
break;
case VIDEO_RESUME:
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(getApplicationContext(), "VIDEO_RESUME", Toast.LENGTH_SHORT).show();
}
});
break;
case VOICE_PAUSE:
runOnUiThread(new Runnable() {
public void run() {