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