一对一音视频通话都需要稳定、清晰和流畅,以确保良好的用户体验,常用的使用场景如下:
在Android平台上实现一对一音视频通话,你可以使用WebRTC,WebRTC提供了实时音视频通话的功能。以下是一个简单的步骤说明如何实现:
RTMP是一种基于TCP的流媒体协议,主要用于视频直播。它提供了实时传输音频和视频的功能,可以用于一对一或一对多的场景,RTMP可用于内网或公网环境下,缺点是需要单独部署RTMP Server,数据通过RTMP Server中转,配合低延迟的RTMP Player,互动可以很轻松的在毫秒级。
以大牛直播SDK的demo为例,RTMP推送的代码如下:
class ButtonPushStartListener implements OnClickListener
{
public void onClick(View v)
{
if (isPushingRtmp)
{
stopPush();
btnPushStartStop.setText("推送RTMP");
isPushingRtmp = false;
return;
}
Log.i(PUSH_TAG, "onClick start push rtmp..");
if (libPublisher == null)
return;
InitPusherAndSetConfig();
Log.i(PUSH_TAG, "videoWidth: "+ pushVideoWidth + " videoHeight: " + pushVideoHeight + " pushType:" + pushType);
if ( libPublisher.SmartPublisherSetURL(publisherHandle, publishURL) != 0 )
{
Log.e(PUSH_TAG, "Failed to set rtmp pusher URL..");
}
int startRet = libPublisher.SmartPublisherStartPublisher(publisherHandle);
if (startRet != 0) {
isPushingRtmp = false;
Log.e(TAG, "Failed to start push stream..");
return;
}
CheckInitAudioRecorder();
btnPushStartStop.setText("停止推送 ");
isPushingRtmp = true;
};
停止RTMP推送:
//停止rtmp推送
private void stopPush() {
if(!isPushingRtmp)
{
return;
}
if ( !isRTSPPublisherRunning) {
if (audioRecord_ != null) {
Log.i(TAG, "stopPush, call audioRecord_.StopRecording..");
audioRecord_.Stop();
if (audioRecordCallback_ != null) {
audioRecord_.RemoveCallback(audioRecordCallback_);
audioRecordCallback_ = null;
}
audioRecord_ = null;
}
}
if (libPublisher != null) {
libPublisher.SmartPublisherStopPublisher(publisherHandle);
}
if (!isRTSPPublisherRunning) {
if (publisherHandle != 0) {
if (libPublisher != null) {
libPublisher.SmartPublisherClose(publisherHandle);
publisherHandle = 0;
}
}
}
}
RTMP播放:
btnPlaybackStartStopPlayback.setOnClickListener(new Button.OnClickListener()
{
// @Override
public void onClick(View v) {
if(isPlaybackViewStarted)
{
btnPlaybackStartStopPlayback.setText("开始播放 ");
if ( playerHandle != 0 )
{
libPlayer.SmartPlayerStopPlay(playerHandle);
libPlayer.SmartPlayerClose(playerHandle);
playerHandle = 0;
}
isPlaybackViewStarted = false;
}
else
{
Log.i(PLAY_TAG, "Start playback stream++");
playerHandle = libPlayer.SmartPlayerOpen(curContext);
if(playerHandle == 0)
{
Log.e(PLAY_TAG, "sur faceHandle with nil..");
return;
}
libPlayer.SetSmartPlayerEventCallbackV2(playerHandle,
new EventHandePlayerV2());
libPlayer.SmartPlayerSetSur face(playerHandle, playerSur faceView); //if set the second param with null, it means it will playback audio only..
libPlayer.SmartPlayerSetRenderScaleMode(playerHandle, 1);
libPlayer.SmartPlayerSetExternalAudioOutput(playerHandle, new PlayerExternalPcmOutput());
libPlayer.SmartPlayerSetAudioOutputType(playerHandle, 1);
libPlayer.SmartPlayerSetBuffer(playerHandle, playbackBuffer);
libPlayer.SmartPlayerSetFastStartup(playerHandle, isPlaybackFastStartup?1:0);
if ( isPlaybackMute )
{
libPlayer.SmartPlayerSetMute(playerHandle, isPlaybackMute?1:0);
}
if (isPlaybackHardwareDecoder) {
int isSupportHevcHwDecoder = libPlayer.SetSmartPlayerVideoHevcHWDecoder(playerHandle,1);
int isSupportH264HwDecoder = libPlayer
.SetSmartPlayerVideoHWDecoder(playerHandle,1);
Log.i(TAG, "isSupportH264HwDecoder: " + isSupportH264HwDecoder + ", isSupportHevcHwDecoder: " + isSupportHevcHwDecoder);
}
libPlayer.SmartPlayerSetAudioVolume(playerHandle, curAudioVolume);
libPlayer.SmartPlayerSetUrl(playerHandle, playbackUrl);
int iPlaybackRet = libPlayer.SmartPlayerStartPlay(playerHandle);
if( iPlaybackRet != 0 )
{
libPlayer.SmartPlayerClose(playerHandle);
playerHandle = 0;
Log.e(PLAY_TAG, "StartPlayback strem failed..");
return;
}
btnPlaybackStartStopPlayback.setText("停止播放 ");
btnPlaybackPopInputUrl.setEnabled(false);
btnPlaybackHardwareDecoder.setEnabled(false);
btnPlaybackSetPlayBuffer.setEnabled(false);
btnPlaybackFastStartup.setEnabled(false);
isPlaybackViewStarted = true;
Log.i(PLAY_TAG, "Start playback stream--");
}
}
});
纯内网环境下,两个终端可同时开启轻量级RTSP服务,然后相互拉取对方回调上来的RTSP URL,通过回音消除等,实现智能化场景的一对一音视频互动,不然智能门禁等场景,均可使用,实测延迟毫秒级,不影响互动体验,效果非常好:
对应的代码如下:
//Author: daniusdk.com
//启动/停止RTSP服务
class ButtonRtspServiceListener implements OnClickListener {
public void onClick(View v) {
if (isRTSPServiceRunning) {
stopRtspService();
btnRtspService.setText("启动RTSP服务");
btnRtspPublisher.setEnabled(false);
isRTSPServiceRunning = false;
return;
}
Log.i(TAG, "onClick start rtsp service..");
rtsp_handle_ = libPublisher.OpenRtspServer(0);
if (rtsp_handle_ == 0) {
Log.e(TAG, "创建rtsp server实例失败! 请检查SDK有效性");
} else {
int port = 8554;
if (libPublisher.SetRtspServerPort(rtsp_handle_, port) != 0) {
libPublisher.CloseRtspServer(rtsp_handle_);
rtsp_handle_ = 0;
Log.e(TAG, "创建rtsp server端口失败! 请检查端口是否重复或者端口不在范围内!");
}
//String user_name = "admin";
//String password = "12345";
//libPublisher.SetRtspServerUserNamePassword(rtsp_handle_, user_name, password);
if (libPublisher.StartRtspServer(rtsp_handle_, 0) == 0) {
Log.i(TAG, "启动rtsp server 成功!");
} else {
libPublisher.CloseRtspServer(rtsp_handle_);
rtsp_handle_ = 0;
Log.e(TAG, "启动rtsp server失败! 请检查设置的端口是否被占用!");
}
btnRtspService.setText("停止RTSP服务");
btnRtspPublisher.setEnabled(true);
isRTSPServiceRunning = true;
}
}
}
发布RTSP流:
//发布/停止RTSP流
class ButtonRtspPublisherListener implements OnClickListener {
public void onClick(View v) {
if (isRTSPPublisherRunning) {
stopRtspPublisher();
if (!isPushingRtmp) {
ConfigControlEnable(true);
}
btnRtspPublisher.setText("发布RTSP流");
btnGetRtspSessionNumbers.setEnabled(false);
btnRtspService.setEnabled(true);
isRTSPPublisherRunning = false;
return;
}
Log.i(TAG, "onClick start rtsp publisher..");
if (!isPushingRtmp) {
InitPusherAndSetConfig();
}
if (publisherHandle == 0) {
Log.e(TAG, "Start rtsp publisher, publisherHandle is null..");
return;
}
String rtsp_stream_name = "stream1";
libPublisher.SetRtspStreamName(publisherHandle, rtsp_stream_name);
libPublisher.ClearRtspStreamServer(publisherHandle);
libPublisher.AddRtspStreamServer(publisherHandle, rtsp_handle_, 0);
if (libPublisher.StartRtspStream(publisherHandle, 0) != 0) {
Log.e(TAG, "调用发布rtsp流接口失败!");
return;
}
if (!isPushingRtmp) {
if (pushType == 0 || pushType == 1) {
CheckInitAudioRecorder(); //enable pure video publisher..
}
ConfigControlEnable(false);
}
startLayerPostThread();
btnRtspPublisher.setText("停止RTSP流");
btnGetRtspSessionNumbers.setEnabled(true);
btnRtspService.setEnabled(false);
isRTSPPublisherRunning = true;
}
}
获取RTSP流会话链接数:
//当前RTSP会话数弹出框
private void PopRtspSessionNumberDialog(int session_numbers) {
final EditText inputUrlTxt = new EditText(this);
inputUrlTxt.setFocusable(true);
inputUrlTxt.setEnabled(false);
String session_numbers_tag = "RTSP服务当前客户会话数: " + session_numbers;
inputUrlTxt.setText(session_numbers_tag);
AlertDialog.Builder builderUrl = new AlertDialog.Builder(this);
builderUrl
.setTitle("内置RTSP服务")
.setView(inputUrlTxt).setNegativeButton("确定", null);
builderUrl.show();
}
//获取RTSP会话数
class ButtonGetRtspSessionNumbersListener implements OnClickListener {
public void onClick(View v) {
if (libPublisher != null && rtsp_handle_ != 0) {
int session_numbers = libPublisher.GetRtspServerClientSessionNumbers(rtsp_handle_);
Log.i(TAG, "GetRtspSessionNumbers: " + session_numbers);
PopRtspSessionNumberDialog(session_numbers);
}
}
}
播放RTSP不再赘述,和播放RTMP一样,只是URL类型不一样,需要注意的是,不管走RTMP还是RTSP,都需要开启回音消除。
Android平台一对一互动,纯内网环境下,不部署单独的流媒体服务器,走轻量级RTSP服务真的非常方便,如果需要扩展到公网业务,建议可以考虑RTMP,如果有很好的开发能力,也可以考虑WebRTC,具体根据实际场景选择即可。