直播软件源码开发语音聊天室功能,只看主播可不够
云豹直播软件源码开发团队近日接到不少客户的开发需求,要求增加“语音聊天室功能”,用于提高用户留存率,促进消费,云豹科技自然能够实现这点小小的要求,下面从源码的角度就为大家介绍此功能的具体实现方式。


如上图所示,在云豹语音直播间有8个麦位,观众可以申请上麦,主播可以同意和拒绝用户的申请。当主播同意之后,用户会收到主播同意上麦的消息,这时候观众的头像就显示在了麦位上。
与此同时,观众开始推流,在推流成功后,把自己的播流地址广播给直播间所有人,其他人收到广播之后就开始播放该上麦观众的流,就能听到改观众的声音了。此时,主播可以控制每个上麦观众的开麦闭麦,以及对上麦观众强制下麦等操作。这就是语音直播室的功能实现。
直播软件源码中实现该功能的代码如下:
public class LiveVoiceLinkMicViewHolder extends AbsViewHolder implements ITXLivePushListener {
private static final int USER_COUNT = 8;
private List<LiveVoiceLinkMicBean> mList;
private LiveVoiceLinkMicAdapter mAdapter;
private TXLivePusher mLivePusher;
private LivePushListener mLivePushListener;
private boolean mStartPush;
private TXLivePlayer[] mLivePlayerArr;
private Handler mHandler;
public LiveVoiceLinkMicViewHolder(Context context, ViewGroup parentView) {
super(context, parentView);
}
@Override
protected int getLayoutId() {
return R.layout.view_live_voice_link_mic;
}
@Override
public void init() {
mList = new ArrayList<>();
mLivePlayerArr = new TXLivePlayer[USER_COUNT];
for (int i = 0; i < USER_COUNT; i++) {
mList.add(new LiveVoiceLinkMicBean());
}
RecyclerView recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new GridLayoutManager(mContext, 4, GridLayoutManager.VERTICAL, false));
mAdapter = new LiveVoiceLinkMicAdapter(mContext, mList);
recyclerView.setAdapter(mAdapter);
if (mContext instanceof LiveAudienceActivity) {
findViewById(R.id.voice_link_mic_container).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
((LiveAudienceActivity) mContext).light();
}
});
}
}
/**
* 用户上麦
*
* @param toUid 上麦人员的uid
* @param toName 上麦人员的name
* @param toAvatar 上麦人员的头像
* @param position 上麦人员的位置
*/
public void onUserUpMic(String toUid, String toName, String toAvatar, int position) {
if (TextUtils.isEmpty(toUid)) {
return;
}
LiveVoiceLinkMicBean bean = mList.get(position);
bean.setUid(toUid);
bean.setUserName(toName);
bean.setAvatar(toAvatar);
bean.setStatus(Constants.VOICE_CTRL_OPEN);
bean.setFaceIndex(-1);
bean.setUserStream(null);
if (mAdapter != null) {
mAdapter.notifyItemChanged(position);
}
if (mHandler != null) {
mHandler.removeMessages(position);
}
}
/**
* 用户下麦
*
* @param toUid 下麦人员的uid
*/
public void onUserDownMic(String toUid) {
int position = getUserPosition(toUid);
if (position >= 0 && position < USER_COUNT) {
LiveVoiceLinkMicBean bean = mList.get(position);
bean.setUid(null);
bean.setUserName(null);
bean.setAvatar(null);
bean.setStatus(Constants.VOICE_CTRL_EMPTY);
bean.setFaceIndex(-1);
bean.setUserStream(null);
if (mAdapter != null) {
mAdapter.notifyItemChanged(position);
}
if (mHandler != null) {
mHandler.removeMessages(position);
}
}
}
/**
* 语音聊天室--主播控制麦位 闭麦开麦禁麦等
*
* @param position 麦位
* @param status 麦位的状态 -1 关麦; 0无人; 1开麦 ; 2 禁麦;
*/
public void onControlMicPosition(int position, int status) {
LiveVoiceLinkMicBean bean = mList.get(position);
bean.setStatus(status);
if (mAdapter != null) {
mAdapter.notifyItemChanged(position, Constants.PAYLOAD);
}
}
/**
* 语音聊天室--收到上麦观众发送表情的消息
*
* @param uid 上麦观众的uid
* @param faceIndex 表情标识
*/
public void onVoiceRoomFace(String uid, int faceIndex) {
int position = getUserPosition(uid);
if (position >= 0 && position < USER_COUNT) {
LiveVoiceLinkMicBean bean = mList.get(position);
bean.setFaceIndex(faceIndex);
if (mAdapter != null) {
mAdapter.notifyItemChanged(position, Constants.VOICE_FACE);
}
if (mHandler == null) {
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
int pos = msg.what;
LiveVoiceLinkMicBean bean0 = mList.get(pos);
bean0.setFaceIndex(-1);
if (mAdapter != null) {
mAdapter.notifyItemChanged(pos, Constants.VOICE_FACE);
}
}
};
} else {
mHandler.removeMessages(position);
}
mHandler.sendEmptyMessageDelayed(position, 5000);
}
}
/**
* 设置静音
*/
public void setPushMute(boolean pushMute) {
if (mLivePusher != null) {
mLivePusher.setMute(pushMute);
}
}
/**
* 开始推流
*/
public void startPush(String pushUrl, LivePushListener pushListener) {
mLivePushListener = pushListener;
if (mLivePusher == null) {
mLivePusher = new TXLivePusher(mContext);
TXLivePushConfig livePushConfig = new TXLivePushConfig();
// livePushConfig.enableAEC(true);//开启回声消除:连麦时必须开启,非连麦时不要开启
livePushConfig.enablePureAudioPush(true);
mLivePusher.setConfig(livePushConfig);
mLivePusher.setPushListener(this);
}
mLivePusher.setMute(false);
mLivePusher.startPusher(pushUrl);
}
@Override
public void onPushEvent(int e, Bundle bundle) {
if (e == TXLiveConstants.PUSH_ERR_OPEN_MIC_FAIL) {
ToastUtil.show(R.string.live_push_failed_2);
} else if (e == TXLiveConstants.PUSH_ERR_NET_DISCONNECT || e == TXLiveConstants.PUSH_ERR_INVALID_ADDRESS) {
// L.e(mTag, "网络断开,推流失败------>");
} else if (e == TXLiveConstants.PUSH_EVT_PUSH_BEGIN) {//推流成功
// L.e(mTag, "mStearm--->推流成功");
if (!mStartPush) {
mStartPush = true;
if (mLivePushListener != null) {
mLivePushListener.onPushStart();
}
}
}
}
@Override
public void onNetStatus(Bundle bundle) {
}
/**
* 停止推流
*/
public void stopPush() {
mStartPush = false;
if (mLivePusher != null) {
mLivePusher.stopPusher();
}
}
/**
* 停止播放
*/
public void stopPlay(String uid) {
int position = getUserPosition(uid);
if (position >= 0 && position < USER_COUNT) {
TXLivePlayer player = mLivePlayerArr[position];
if (player != null && player.isPlaying()) {
player.stopPlay(false);
}
}
}
/**
* 停止所有播放
*/
public void stopAllPlay() {
for (TXLivePlayer player : mLivePlayerArr) {
if (player != null && player.isPlaying()) {
player.stopPlay(false);
}
}
}
/**
* 语音聊天室--播放上麦观众的低延时流
*
* @param uid 上麦观众的uid
* @param pull 上麦观众的低延时流地址
* @param userStream 上麦观众的流名,主播混流用
*/
public void playAccStream(String uid, String pull, String userStream) {
int position = getUserPosition(uid);
if (position >= 0 && position < USER_COUNT) {
LiveVoiceLinkMicBean bean = mList.get(position);
bean.setUserStream(userStream);
TXLivePlayer player = mLivePlayerArr[position];
if (player == null) {
player = new TXLivePlayer(mContext);
TXLivePlayConfig playConfig = new TXLivePlayConfig();
playConfig.enableAEC(true);
playConfig.setAutoAdjustCacheTime(true);
playConfig.setMaxAutoAdjustCacheTime(5.0f);
playConfig.setMinAutoAdjustCacheTime(1.0f);
player.setConfig(playConfig);
mLivePlayerArr[position] = player;
}
player.startPlay(pull, TXLivePlayer.PLAY_TYPE_LIVE_RTMP_ACC);
}
}
/**
* 获取用户在麦上的位置
*/
public int getUserPosition(String uid) {
if (!TextUtils.isEmpty(uid)) {
for (int i = 0; i < USER_COUNT; i++) {
LiveVoiceLinkMicBean bean = mList.get(i);
if (uid.equals(bean.getUid())) {
return i;
}
}
}
return -1;
}
/**
* 获取用户
*/
public LiveVoiceLinkMicBean getUserBean(int position) {
if (position >= 0 && position < USER_COUNT) {
return mList.get(position);
}
return null;
}
/**
* 获取用户
*/
public LiveVoiceLinkMicBean getUserBean(String toUid) {
return getUserBean(getUserPosition(toUid));
}
/**
* 主播混流时候获取上麦用户的Stream
*/
public List<String> getUserStreamForMix() {
List<String> list = null;
for (int i = 0; i < USER_COUNT; i++) {
String userStream = mList.get(i).getUserStream();
if (!TextUtils.isEmpty(userStream)) {
if (list == null) {
list = new ArrayList<>();
}
list.add(userStream);
}
}
return list;
}
/**
* 显示房间用户数据
*/
public void showUserList(JSONArray arr) {
for (int i = 0; i < USER_COUNT; i++) {
LiveVoiceLinkMicBean bean = mList.get(i);
JSONObject obj = arr.getJSONObject(i);
bean.setUid(obj.getString("id"));
bean.setUserName(obj.getString("user_nicename"));
bean.setAvatar(obj.getString("avatar"));
bean.setStatus(obj.getIntValue("status"));
}
if (mAdapter != null) {
mAdapter.notifyDataSetChanged();
}
}
public List<LiveVoiceGiftBean> getVoiceGiftUserList() {
List<LiveVoiceGiftBean> list = null;
for (int i = 0; i < USER_COUNT; i++) {
LiveVoiceLinkMicBean bean = mList.get(i);
if (!bean.isEmpty()) {
LiveVoiceGiftBean giftUserBean = new LiveVoiceGiftBean();
giftUserBean.setUid(bean.getUid());
giftUserBean.setAvatar(bean.getAvatar());
giftUserBean.setType(i);
if (list == null) {
list = new ArrayList<>();
}
list.add(giftUserBean);
}
}
return list;
}
@Override
public void release() {
stopAllPlay();
if (mLivePusher != null) {
mLivePusher.stopPusher();
mLivePusher.setPushListener(null);
}
mLivePusher = null;
mLivePushListener = null;
if (mHandler != null) {
mHandler.removeCallbacksAndMessages(null);
}
mHandler = null;
super.release();
}
}这样就实现了语音直播室功能,如果您需要开发直播软件源码,搭建直播平台,欢迎联系云豹科技,我们出售成品直播源码,支持二次开发。
声明:以上内容为云豹科技原创,未经公司同意,禁止转载,否则将追究相关法律责任www.yunbaokj.com






鲁公网安备 37090202000844号

