直播软件源码开发语音聊天室功能,只看主播可不够

发布来源:云豹科技
发布人:云豹科技
2021-02-26 10:05:23

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

 图片1.png图片2.png


如上图所示,在云豹语音直播间有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

声明:
以上内容为云豹科技作者本人原创,未经作者本人同意,禁止转载,否则将追究相关法律责任
立即查看