package com.c9mj.platform.live.mvp.presenter.impl; import android.text.TextUtils; import com.c9mj.platform.live.api.LiveAPI; import com.c9mj.platform.live.mvp.model.DanmuBean; import com.c9mj.platform.live.mvp.model.LiveDetailBean; import com.c9mj.platform.live.mvp.model.LivePandaBean; import com.c9mj.platform.live.mvp.presenter.ILivePlayPresenter; import com.c9mj.platform.live.mvp.view.ILivePlayActivity; import com.c9mj.platform.util.DanmuUtil; import com.c9mj.platform.util.GsonHelper; import com.c9mj.platform.util.retrofit.HttpSubscriber; import com.c9mj.platform.util.retrofit.RetrofitHelper; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.IOException; import java.net.Socket; import java.util.ArrayList; import io.reactivex.BackpressureStrategy; import io.reactivex.Flowable; import io.reactivex.FlowableEmitter; import io.reactivex.FlowableOnSubscribe; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; import io.reactivex.subscribers.DefaultSubscriber; import io.reactivex.subscribers.DisposableSubscriber; /** * author: LMJ * date: 2016/9/20 */ public class LivePlayPresenterImpl implements ILivePlayPresenter { private final ILivePlayActivity view; /********************** * 以下是弹幕Socket连接相关 *****************/ private ArrayList<String> chatRoomList = new ArrayList<>(); private String socketIP; private int socketPort; private boolean isConnectSuccess = false; //心跳包相关 private boolean isAlreadySendHeart = false; //是否已发送心跳包 private boolean isHeartStop = false; //用于外部控制心跳线程结束 //弹幕接收相关 private boolean isReceiveStop = false; //用于外部控制接收线程结束 private Socket socket = null; private BufferedInputStream bis; private BufferedOutputStream bos; public LivePlayPresenterImpl(ILivePlayActivity view) { this.view = view; } @Override public void getLiveDetail(String live_type, String live_id, String game_type) { RetrofitHelper.getLiveHelper().create(LiveAPI.class) .getLiveDetail(live_type, live_id, game_type) .compose(RetrofitHelper.<LiveDetailBean>handleLiveResult()) .subscribe(new HttpSubscriber<LiveDetailBean>() { @Override public void _onNext(LiveDetailBean liveDetailBean) { view.updateLiveDetail(liveDetailBean); } @Override public void _onError(String message) { view.showError(message); } }); } @Override public void getDanmuDetail(String roomid, String live_type) { if (live_type.equals("panda")) { RetrofitHelper.getPandaHelper().create(LiveAPI.class) .getPandaChatroom(roomid) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new DefaultSubscriber<LivePandaBean>() { @Override public void onComplete() { } @Override public void onError(Throwable e) { // view.showError("弹幕服务器接口已过期,请刷新直播列表!"); view.showError(e.getMessage()); } @Override public void onNext(LivePandaBean detailPandaBean) { if (detailPandaBean.getErrno() == 0) { view.updateChatDetail(detailPandaBean); } else { view.showError(detailPandaBean.getErrmsg()); } } }); } else { view.showError("直播平台:" + live_type + "!不是熊猫TV的弹幕池!"); } } @Override public void connectToChatRoom(String roomid, final LivePandaBean pandaBean) { chatRoomList = pandaBean.getData().getChat_addr_list(); if (chatRoomList == null || chatRoomList.size() == 0) { view.showError("无法连接至弹幕服务器!"); return; } Flowable.create(new FlowableOnSubscribe<String>() { @Override public void subscribe(FlowableEmitter<String> e) throws Exception { //接收响应数据 byte readData[] = new byte[6]; socketIP = chatRoomList.get(0).split(":")[0]; socketPort = Integer.parseInt(chatRoomList.get(0).split(":")[1]); socket = new Socket(socketIP, socketPort); bis = new BufferedInputStream(socket.getInputStream());//得到Socket输入流 bos = new BufferedOutputStream(socket.getOutputStream());//得到Socket输出流 //发送建立连接请求 bos.write(DanmuUtil.getConnectData(pandaBean.getData())); bos.flush(); bis.read(readData); int isLength = bis.read(); if (isLength >= 6) { if (!(readData[0] == DanmuUtil.RESPONSE[0] && readData[1] == DanmuUtil.RESPONSE[1] && readData[2] == DanmuUtil.RESPONSE[2] && readData[3] == DanmuUtil.RESPONSE[3])) isConnectSuccess = false; else { isConnectSuccess = true; e.onNext("connect success!"); } } else { isConnectSuccess = false; } } }, BackpressureStrategy.DROP) .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new DefaultSubscriber<String>() { @Override public void onNext(String result) { //连接成功 if (isConnectSuccess) { runHeartDanmuOnRxJava(); } } @Override public void onError(Throwable e) { } @Override public void onComplete() { } }); } @Override public void closeConnection() { if (socket != null && bis != null && bos != null && socket.isConnected()) { try { isHeartStop = true;//停止心跳线程 isReceiveStop = true;//停止弹幕消息接收 bis.close(); bos.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 1.心跳包 * 2.接收弹幕 */ private void runHeartDanmuOnRxJava() { //心跳包 // Flowable.create(new FlowableOnSubscribe<String>() { // @Override // public void subscribe(FlowableEmitter<String> e) throws Exception { // int autoConnectedTime = 0; // int maxConnectedTime = 5;//自动断线重连次数 // while (!isHeartStop){ // bos.write(DanmuUtil.getHeartData());//发送心跳包 // bos.flush(); // if (isAlreadySendHeart == true){ // connectToChatRoom(roomid, pandaBean); //连接断开,自动重新连接 // autoConnectedTime++; // if (autoConnectedTime > maxConnectedTime){//超过最大重连次数 // autoConnectedTime = 0; // e.onNext("无法连接至弹幕服务器!"); // }else { // Thread.sleep(3 * 1000);//3s后再次尝试发送心跳包 // continue; // } // } // isAlreadySendHeart = true;//修改标志已发送心跳包 // autoConnectedTime = 0;//断线重连计数置零 // // //等到300s再次发送心跳包 // Thread.sleep(300 * 1000); // } //// e.onNext("断开弹幕服务器连接!"); // } // }, BackpressureStrategy.DROP) // .subscribeOn(Schedulers.newThread()) // .observeOn(AndroidSchedulers.mainThread()) // .subscribe(new DefaultSubscriber<String>() { // // @Override // public void onComplete() { // closeConnection(); // } // // @Override // public void onError(Throwable e) { //// view.showError(e.getMessage()); // } // // @Override // public void onNext(String result) { // } // }); //接收弹幕 Flowable.create(new FlowableOnSubscribe<String>() { @Override public void subscribe(FlowableEmitter<String> e) throws Exception { byte[] messege = new byte[1024]; while (!isReceiveStop) { //读取服务器返回信息,并获取返回信息的整体字节长度 int recvLen = bis.read(messege, 0, messege.length); //根据实际获取的字节数初始化返回信息内容长度 byte[] realBuf = new byte[recvLen]; //按照实际获取的字节长度读取返回信息 System.arraycopy(messege, 0, realBuf, 0, recvLen); if (recvLen >= 4) {//成功接收到数据 //分析帧头 if (realBuf[0] == DanmuUtil.RECEIVE_MSG[0] &&//1.弹幕 realBuf[1] == DanmuUtil.RECEIVE_MSG[1] && realBuf[2] == DanmuUtil.RECEIVE_MSG[2] && realBuf[3] == DanmuUtil.RECEIVE_MSG[3]) { //{"type":"1","time":1477356608,"data":{"from":{"__plat":"android","identity":"30","level":"4","msgcolor":"","nickName":"看了还说了","rid":"45560306","sp_identity":"0","userName":""},"to":{"toroom":"15161"},"content":"我去"}} String content = new String(realBuf, "UTF-8"); //第一条弹幕 int danmuFromIndex = content.indexOf("{\"type"); int danmuToIndex = content.indexOf("}}"); //第二条弹幕(可有) int danmuFromIndex_2 = content.lastIndexOf("{\"type"); int danmuToIndex_2 = content.lastIndexOf("}}"); String danmu;//存放弹幕 danmu = content.substring(danmuFromIndex, danmuToIndex + 2); if (TextUtils.isEmpty(danmu)) {//为空不发射事件 continue; } e.onNext(danmu); //如果存在第二条弹幕 if (!(danmuFromIndex == danmuFromIndex_2 && danmuToIndex == danmuToIndex_2)) { danmu = content.substring(danmuFromIndex_2, danmuToIndex_2 + 2); if (TextUtils.isEmpty(danmu)) { continue; } e.onNext(danmu); } } else if (messege[0] == DanmuUtil.HEART_BEAT_RESPONSE[0] &&//2.心跳包 messege[1] == DanmuUtil.HEART_BEAT_RESPONSE[1] && messege[2] == DanmuUtil.HEART_BEAT_RESPONSE[2] && messege[3] == DanmuUtil.HEART_BEAT_RESPONSE[3]) { //接收到响应,重置心跳包发送标志位 isAlreadySendHeart = false; } } } } }, BackpressureStrategy.DROP) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new DisposableSubscriber<String>() { @Override public void onComplete() { } @Override public void onError(Throwable e) { // view.showError(e.getMessage()); } @Override public void onNext(String result) { if (isReceiveStop) { if (!isDisposed()) { dispose(); return; } } parseDanmu(result); } }); } /** * 解析所接收弹幕的Json * * @param danmu */ private void parseDanmu(final String danmu) { Flowable.create(new FlowableOnSubscribe<DanmuBean>() { @Override public void subscribe(FlowableEmitter<DanmuBean> e) throws Exception { //解析弹幕Json int one = danmu.indexOf("1"); if (danmu.substring(one, one + 1).equals("1")) { DanmuBean danmuBean = (DanmuBean) GsonHelper.parseJson(danmu, DanmuBean.class); e.onNext(danmuBean); } } }, BackpressureStrategy.BUFFER) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new DefaultSubscriber<DanmuBean>() { @Override public void onComplete() { } @Override public void onError(Throwable e) { // view.showError(e.getMessage()); } @Override public void onNext(DanmuBean bean) { view.receiveDanmu(bean, false); } }); } }