Android 腾讯互动直播集成

最近刚用完互动直播sdk 做完直播,简单记录下。

官网:https://cloud.tencent.com/product/ilvb

随心博 demo:https://cloud.tencent.com/document/product/268/7658

直播接口:https://cloud.tencent.com/document/product/268/7685

直播API: https://zhaoyang21cn.github.io/iLiveSDK_Help/android_help/

一.旁路推流

首先先弄懂“旁路”的概念

凡是通过url观看的直播都是旁路直播,旁路直播要进行旁路推流,否则旁路直播无法观看直播。

向在app内我们是通过joinRoom() 加入的房间,所以不属于旁路直播。

二.主播方

大概流程是这个样子,根据自己需求进行变更

1.向服务器请求房间号。

2.根据房间号,创建房间。

 public void createRoom() {
        ILVLiveRoomOption hostOption = new ILVLiveRoomOption(”主播ID“)
                .roomDisconnectListener(this)
                .autoCamera(true)
                .videoMode(ILiveConstants.VIDEOMODE_BSUPPORT)
                .controlRole(CurLiveInfo.getCurRole())
                //角色是主播 一定不能设置错  主播走核心机房  观众走边缘机房
//                .controlRole("LiveMaster")
                .videoRecvMode(AVRoomMulti.VIDEO_RECV_MODE_SEMI_AUTO_RECV_CAMERA_VIDEO);
     
        int ret = ILVLiveManager.getInstance().createRoom(“房间ID”, hostOption, new ILiveCallBack() {
            @Override
            public void onSuccess(Object data) {
            
               if (getView()!=null)
                   getView().enterRoomComplete(MySelfInfo.getInstance().getIdStatus(), true);
                ILiveLog.d(TAG, "ILVB-SXB|startEnterRoom->create room sucess");
            }

            @Override
            public void onError(String module, int errCode, String errMsg) {
            
               if (getView()!=null)
                   getView().errRoomFail();
                ILiveLog.d(TAG, "ILVB-SXB|createRoom->create room failed:" + module + "|" + errCode + "|" + errMsg);
            }
        });
  
    }

4.创建房间成功后有旁路直播开启旁路推流。


public void pushStream() {
        /**
         * 旁路直播 退出房间时必须退出推流。否则会占用后台channel。
         */
        ILivePushOption option = new ILivePushOption();
        option.encode(ILivePushOption.Encode.HLS);
//        option.setRecordFileType(ILivePushOption.RecordFileType.RECORD_HLS_FLV_MP4);
        option.setRecordFileType(ILivePushOption.RecordFileType.RECORD_HLS_FLV_MP4);
//        option.channelName("跟ios协商");
        //推流格式 到时候跟ios统一
        pushStream(option);


    }
/**
     * 开启推流
     *
     * @param option
     */
    public void pushStream(ILivePushOption option) {
        if (!isPushed) {
            ILiveRoomManager.getInstance().startPushStream(option, new ILiveCallBack<ILivePushRes>() {
                @Override
                public void onSuccess(ILivePushRes data) {
//                List<ILivePushUrl> liveUrls = data.getUrls();
                    Log.i("pushStream", "推流成功!!!!!!!");
                 
                }

                @Override
                public void onError(String module, int errCode, String errMsg) {
                    Log.i("pushStream", "推流失败!!!!!!!");
                }
            });
        }
    }

5.退出房间前:在房间发送自定义消息告诉观众主播要退出房间,方便观众端切换主播已离开界面,并停止推流。

发送自定义消息退出房间

 public void sendExitRoomMSG() {
        ILVCustomCmd cmd = new ILVCustomCmd();
        cmd.setCmd(Constans.AVIMCMD_EXITLIVE);
        cmd.setParam(sendExitRoomJson());
        cmd.setType(ILVText.ILVTextType.eGroupMsg);
        ILVLiveManager.getInstance().sendCustomCmd(cmd, new ILiveCallBack<TIMMessage>() {
            @Override
            public void onSuccess(TIMMessage data) {
             
               if (isPushed) {
                    stopStream();
                }
                callExitRoom();

            }

            @Override
            public void onError(String module, int errCode, String errMsg) {
            }

        });
    }

停止推流

 /**
     * 退出直播的时候关闭推流
     */
    public void stopStream() {
        ILiveRoomManager.getInstance().stopPushStream(streamChannelID, new ILiveCallBack() {
            @Override
            public void onSuccess(Object data) {
                SxbLog.e(TAG, "stopPush->success");
                isPushed = false;
            }

            @Override
            public void onError(String module, int errCode, String errMsg) {
                SxbLog.e(TAG, "stopPush->failed:" + module + "|" + errCode + "|" + errMsg);
            }
        });

    }

6.退出房间。

private void callExitRoom() {
        ILiveSDK.getInstance().getAvVideoCtrl().setLocalVideoPreProcessCallback(null);
        quitLiveRoom();
    }

三.观众方

大概流程是这个样子,根据自己需求进行变更。

1.向服务器请求房间号。

2.根据房间号加入房间。

 //加入房间
    public void joinRoom(int roomId) {
        ILVLiveManager.getInstance().quitRoom(null);
        this.rommId = roomId + "";
        ILVLiveRoomOption memberOption = new ILVLiveRoomOption(ConfigUtils.getUid())
                .autoCamera(false)
                .roomDisconnectListener(this)
                .videoMode(ILiveConstants.VIDEOMODE_BSUPPORT)
                .controlRole("Guest")
                .authBits(AVRoomMulti.AUTH_BITS_JOIN_ROOM | AVRoomMulti.AUTH_BITS_RECV_AUDIO | AVRoomMulti.AUTH_BITS_RECV_CAMERA_VIDEO | AVRoomMulti.AUTH_BITS_RECV_SCREEN_VIDEO)
                .videoRecvMode(AVRoomMulti.VIDEO_RECV_MODE_SEMI_AUTO_RECV_CAMERA_VIDEO)
                .autoMic(false);
        int ret = ILVLiveManager.getInstance().joinRoom(roomId, memberOption, new ILiveCallBack() {
            @Override
            public void onSuccess(Object data) {
                ILiveLog.d(TAG, "ILVB-Suixinbo|startEnterRoom->join room sucess");
                if (null != mGuestLiveView)

                    mGuestLiveView.enterRoomComplete(true);
            }

            @Override
            public void onError(String module, int errCode, String errMsg) {
                LogUtils.d("suyan", "===========加入房间失败" + module + errCode + errMsg);
                if (null != mGuestLiveView) {
                    mGuestLiveView.enterRoomComplete(false);
                }
            }
        });
    }

3.加入房间成功后,在房间发送自定义消息,告诉房间内有人加入,方便主播统计目前的房间人数。

 /**
     * 发送信令,点赞,进入房间
     */

    public int sendGroupCmd(int cmd, String param) {
        ILVCustomCmd customCmd = new ILVCustomCmd();
        //1.传cmd
        customCmd.setCmd(cmd);
        //2.创建自定义数据
        String json = sendCusJsn();
        //3.传自定义数据
        customCmd.setParam(json);
        //4.设置自定义类型
        customCmd.setType(ILVText.ILVTextType.eGroupMsg);
        return sendCmd(customCmd);
    }

4.退出房间前:发送房间自定义消息,告诉主播观众退出,方便主播统计目前的在线人数。

5.退出直播间。

  public void startExitRoom() {
        if (ILiveSDK.getInstance() != null && ILiveSDK.getInstance().getAvVideoCtrl() != null) {
            ILiveSDK.getInstance().getAvVideoCtrl().setLocalVideoPreProcessCallback(null);
        }
        quitLiveRoom();
    }
 private void quitLiveRoom() {
        ILVLiveManager.getInstance().quitRoom(new ILiveCallBack() {
            @Override
            public void onSuccess(Object data) {
                //1.回调给页面
                if (null != mGuestLiveView) {
                    LogUtils.d("suyan", "=========退出成功");
                    mGuestLiveView.quiteRoomComplete(true);
                }
            }

            @Override
            public void onError(String module, int errCode, String errMsg) {
                if (null != mGuestLiveView) {
                    mGuestLiveView.quiteRoomComplete(false);
                }
            }
        });
    }

自己封装了一个观众端的使用类,也是根据互动直播demo改的

package com.jyjt.ydyl.txlive.presenters;

import android.app.Activity;
import android.content.Context;
import android.text.TextUtils;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.jyjt.ydyl.Entity.LiveCusEntity;
import com.jyjt.ydyl.tools.ConfigUtils;
import com.jyjt.ydyl.tools.Constans;
import com.jyjt.ydyl.tools.LogUtils;
import com.jyjt.ydyl.tools.ToastUtil;
import com.jyjt.ydyl.txlive.LiveDetailActivity;
import com.jyjt.ydyl.txlive.event.LiveMessageEvent;
import com.jyjt.ydyl.txlive.views.GuestLiveView;
import com.tencent.TIMConversationType;
import com.tencent.TIMCustomElem;
import com.tencent.TIMElem;
import com.tencent.TIMElemType;
import com.tencent.TIMGroupSystemElem;
import com.tencent.TIMGroupSystemElemType;
import com.tencent.TIMMessage;
import com.tencent.av.sdk.AVRoomMulti;
import com.tencent.ilivesdk.ILiveCallBack;
import com.tencent.ilivesdk.ILiveConstants;
import com.tencent.ilivesdk.ILiveSDK;
import com.tencent.ilivesdk.core.ILiveLog;
import com.tencent.ilivesdk.core.ILiveRoomManager;
import com.tencent.ilivesdk.core.ILiveRoomOption;
import com.tencent.livesdk.ILVCustomCmd;
import com.tencent.livesdk.ILVLiveConstants;
import com.tencent.livesdk.ILVLiveManager;
import com.tencent.livesdk.ILVLiveRoomOption;
import com.tencent.livesdk.ILVText;
import com.tencent.openqq.protocol.imsdk.im_common;

import org.json.JSONObject;
import org.json.JSONTokener;

import java.util.Observable;
import java.util.Observer;

/**
 * 观众端,直播帮助类
 * <p>
 * 1.加入房间
 * 2.离开房间
 * 3.发送点赞,和加入房间指令
 * 4.解析传过来的自定义消息(点赞,其他成员加入,用户退出直播, 达到上限,主播回来了)
 * 5.房间链接断开监听
 * <p>
 * 苏艳
 */

public class GuestLiveHelper implements ILiveRoomOption.onRoomDisconnectListener, Observer {
    private final String TAG = "LiveHelper";
    private GuestLiveView mGuestLiveView;
    public Activity mContext;
    private String rommId;
    Gson gson;

    public GuestLiveHelper(Activity context, GuestLiveView mGuestLiveView) {
        mContext = context;
        this.mGuestLiveView = mGuestLiveView;
        LiveMessageEvent.getInstance().addObserver(this);
        GsonBuilder builder = new GsonBuilder();
        gson = builder.create();
    }

    //加入房间
    public void joinRoom(int roomId) {
        ILVLiveManager.getInstance().quitRoom(null);
        this.rommId = roomId + "";
        ILVLiveRoomOption memberOption = new ILVLiveRoomOption(ConfigUtils.getUid())
                .autoCamera(false)
                .roomDisconnectListener(this)
                .videoMode(ILiveConstants.VIDEOMODE_BSUPPORT)
                .controlRole("Guest")
                .authBits(AVRoomMulti.AUTH_BITS_JOIN_ROOM | AVRoomMulti.AUTH_BITS_RECV_AUDIO | AVRoomMulti.AUTH_BITS_RECV_CAMERA_VIDEO | AVRoomMulti.AUTH_BITS_RECV_SCREEN_VIDEO)
                .videoRecvMode(AVRoomMulti.VIDEO_RECV_MODE_SEMI_AUTO_RECV_CAMERA_VIDEO)
                .autoMic(false);
        int ret = ILVLiveManager.getInstance().joinRoom(roomId, memberOption, new ILiveCallBack() {
            @Override
            public void onSuccess(Object data) {
                ILiveLog.d(TAG, "ILVB-Suixinbo|startEnterRoom->join room sucess");
                if (null != mGuestLiveView)

                    mGuestLiveView.enterRoomComplete(true);
            }

            @Override
            public void onError(String module, int errCode, String errMsg) {
                LogUtils.d("suyan", "===========加入房间失败" + module + errCode + errMsg);
                if (null != mGuestLiveView) {
                    mGuestLiveView.enterRoomComplete(false);
                }
            }
        });
    }

    //退出房间
    public void startExitRoom() {
        if (ILiveSDK.getInstance() != null && ILiveSDK.getInstance().getAvVideoCtrl() != null) {
            ILiveSDK.getInstance().getAvVideoCtrl().setLocalVideoPreProcessCallback(null);
        }
        quitLiveRoom();
    }

    private void quitLiveRoom() {
        ILVLiveManager.getInstance().quitRoom(new ILiveCallBack() {
            @Override
            public void onSuccess(Object data) {
                //1.回调给页面
                if (null != mGuestLiveView) {
                    LogUtils.d("suyan", "=========退出成功");
                    mGuestLiveView.quiteRoomComplete(true);
                }
            }

            @Override
            public void onError(String module, int errCode, String errMsg) {
                if (null != mGuestLiveView) {
                    mGuestLiveView.quiteRoomComplete(false);
                }
            }
        });
    }


    /**
     * 发送信令,点赞,进入房间
     */

    public int sendGroupCmd(int cmd, String param) {
        ILVCustomCmd customCmd = new ILVCustomCmd();
        //1.传cmd
        customCmd.setCmd(cmd);
        //2.创建自定义数据
        String json = sendCusJsn();
        //3.传自定义数据
        customCmd.setParam(json);
        //4.设置自定义类型
        customCmd.setType(ILVText.ILVTextType.eGroupMsg);
        return sendCmd(customCmd);
    }

    //邀请上麦用的,暂时用不到
    public int sendC2CCmd(final int cmd, String param, String destId) {
        ILVCustomCmd customCmd = new ILVCustomCmd();
        customCmd.setDestId(destId);
        customCmd.setCmd(cmd);
        customCmd.setParam(param);
        customCmd.setType(ILVText.ILVTextType.eC2CMsg);
        return sendCmd(customCmd);
    }

    private int sendCmd(final ILVCustomCmd cmd) {
        return ILVLiveManager.getInstance().sendCustomCmd(cmd, new ILiveCallBack() {
            @Override
            public void onSuccess(Object data) {
                LogUtils.d(TAG, "suyan" + cmd.getCmd() + "|" + cmd.getParam() + "====发送成功");
            }

            @Override
            public void onError(String module, int errCode, String errMsg) {
//                Toast.makeText(mContext, "sendCmd->failed:" + module + "|" + errCode + "|" + errMsg, Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public void onRoomDisconnect(int errCode, String errMsg) {
        if (null != mGuestLiveView) {
            //房间异常退出,通常指断网,或者没电
            mGuestLiveView.roomDisconnect(errCode, errMsg);
        }
    }

    // 解析文本消息
    private void processTextMsg(LiveMessageEvent.SxbMsgInfo info) {
        if (null == info.data || !(info.data instanceof ILVText)) {
            return;
        }
        ILVText text = (ILVText) info.data;
        if (text.getType() == ILVText.ILVTextType.eGroupMsg
                && !text.getDestId().equals(rommId)) {
            if (TextUtils.isEmpty(rommId)) {
                LogUtils.d("suyan","========当前的roomid为空");
            }
            return;
        }

        String name = info.senderId;
        if (null != info.profile && !TextUtils.isEmpty(info.profile.getNickName())) {
            name = info.profile.getNickName();
        }

        if (null != mGuestLiveView)
            mGuestLiveView.refreshText(text.getText(), name);
    }

    // 解析自定义信令
    private void processCmdMsg(LiveMessageEvent.SxbMsgInfo info) {
        if (null == info.data || !(info.data instanceof ILVCustomCmd)) {
            return;
        }
        ILVCustomCmd cmd = (ILVCustomCmd) info.data;
        if (cmd.getType() == ILVText.ILVTextType.eGroupMsg
                && !cmd.getDestId().equals(rommId)) {
            if (TextUtils.isEmpty(rommId)) {
                LogUtils.d("suyan","========当前的roomid为空");
            }
            return;
        }

        String name = info.senderId;
        if (null != info.profile && !TextUtils.isEmpty(info.profile.getNickName())) {
            name = info.profile.getNickName();
        }

        handleCustomMsg(cmd.getCmd(), cmd.getParam(), info.senderId, name);
    }

    private void processOtherMsg(LiveMessageEvent.SxbMsgInfo info) {
        if (null == info.data || !(info.data instanceof TIMMessage)) {
            return;
        }
        TIMMessage currMsg = (TIMMessage) info.data;

        // 过滤非当前群组消息
        if (currMsg.getConversation() != null && currMsg.getConversation().getPeer() != null) {
            if (currMsg.getConversation().getType() == TIMConversationType.Group
                    && !rommId.equals(currMsg.getConversation().getPeer())) {
                if (TextUtils.isEmpty(rommId)) {
                    LogUtils.d("suyan","========当前的roomid为空");
                }
                return;
            }
        }

        for (int j = 0; j < currMsg.getElementCount(); j++) {
            if (currMsg.getElement(j) == null)
                continue;
            TIMElem elem = currMsg.getElement(j);
            TIMElemType type = elem.getType();


            //系统消息
            if (type == TIMElemType.GroupSystem) {  // 群组解散消息
                if (TIMGroupSystemElemType.TIM_GROUP_SYSTEM_DELETE_GROUP_TYPE == ((TIMGroupSystemElem) elem).getSubtype()) {
                    if (null != mGuestLiveView)
                        mGuestLiveView.dismissGroup("host", null);
                }
            } else if (type == TIMElemType.Custom) {
                try {
                    final String strMagic = "__ACTION__";
                    String customText = new String(((TIMCustomElem) elem).getData(), "UTF-8");
                    if (!customText.startsWith(strMagic))   // 检测前缀
                        continue;
                    JSONTokener jsonParser = new JSONTokener(customText.substring(strMagic.length() + 1));
                    JSONObject json = (JSONObject) jsonParser.nextValue();
                    String action = json.optString("action", "");
                    if (action.equals("force_exit_room") || action.equals("force_disband_room")) {
                        JSONObject objData = json.getJSONObject("data");
                        String strRoomNum = objData.optString("room_num", "");
                        if (strRoomNum.equals(String.valueOf(ILiveRoomManager.getInstance().getRoomId()))) {
                            if (null != mGuestLiveView) {
                                mGuestLiveView.forceQuitRoom("管理员已将房间解散或将您踢出房间!");
                            }
                        }
                    }
                } catch (Exception e) {
                }
            }
        }
    }

    @Override
    public void update(Observable observable, Object o) {

        LiveMessageEvent.SxbMsgInfo info = (LiveMessageEvent.SxbMsgInfo) o;
        LogUtils.d("suyan", "======收到消息" + info.msgType);
        switch (info.msgType) {
            case LiveMessageEvent.MSGTYPE_TEXT:
                processTextMsg(info);
                break;
            case LiveMessageEvent.MSGTYPE_CMD:
                processCmdMsg(info);
                break;
            case LiveMessageEvent.MSGTYPE_OTHER:
                LogUtils.d("suyan", "=======其他消息");
                processOtherMsg(info);
            case LiveMessageEvent.MSGTYPE_DDETAIL:
                LogUtils.d("suyan", "=======刷新房间数据");
                processDetailMsg(info);
                break;
        }
    }

    // 解析自定义信令
    private void processDetailMsg(LiveMessageEvent.SxbMsgInfo info) {
        if (null != info && !TextUtils.isEmpty(info.senderId)) {
            if (null != mGuestLiveView) {
                mGuestLiveView.refreshData(info.senderId);
            }
        }

    }

    private void handleCustomMsg(int action, String param, String identifier, String nickname) {
        LogUtils.d(TAG, "handleCustomMsg->action: " + action);
        if (null == mGuestLiveView) {
            return;
        }
        LiveCusEntity mliveCusEntity = updateGson(param);
        if (mliveCusEntity != null) {
            LogUtils.d("suyan", "===========解析后的数据" + mliveCusEntity.getContent().getLike_num() + "=" + mliveCusEntity.getContent().getOnline_num() + "=" + mliveCusEntity.getUser().getUid());
        }
        switch (action) {
            case Constans.AVIMCMD_PRAISE://点赞
                mGuestLiveView.refreshGoodLike();
                break;
            case Constans.AVIMCMD_ENTERLIVE://其他成员加入
                mGuestLiveView.memberJoin(identifier, nickname);
                break;
            case Constans.AVIMCMD_EXITLIVE: //用户退出直播
                mGuestLiveView.hostOffline(identifier, nickname);
                break;
            case ILVLiveConstants.ILVLIVE_CMD_LINKROOM_LIMIT:   // 达到上限
                LogUtils.d("suyan","========到达上限");
                break;
            case Constans.AVIMCMD_HOST_BACK://主播回来了
                LogUtils.d("suyan","========主播回来了");
                //昵称:nickname  id:identifier
            case Constans.AVIMCMD_PRAISE_TOTAL://观众进入房间,主播发送现在的总点赞数
                try {
                    if (mliveCusEntity != null && !TextUtils.isEmpty(mliveCusEntity.getContent().getLike_num())) {
                        mGuestLiveView.refreshTotalGoodLike(Long.parseLong(mliveCusEntity.getContent().getLike_num()));
                    }
                } catch (NumberFormatException e) {
                    e.printStackTrace();
                }
            default:
                break;
        }
    }

    //解析jsong
    public LiveCusEntity updateGson(String jsonTest) {
        if (!TextUtils.isEmpty(jsonTest)) {
            LiveCusEntity mLiveCusEntity;
            try {
                mLiveCusEntity = gson.fromJson(jsonTest, LiveCusEntity.class);
                return mLiveCusEntity;
            } catch (JsonSyntaxException e) {
                e.printStackTrace();
                return null;
            }
        } else {
            return null;
        }
    }

    //发送json
    public String sendCusJsn() {
        LiveCusEntity liveCusEntity = new LiveCusEntity();
        LiveCusEntity.LiveContent mLiveContent = new LiveCusEntity.LiveContent();
        liveCusEntity.setContent(mLiveContent);
        LiveCusEntity.User mUser = new LiveCusEntity.User();
        mUser.setUid(ConfigUtils.getUid());
        liveCusEntity.setUser(mUser);
        String json = gson.toJson(liveCusEntity, LiveCusEntity.class);
        LogUtils.d("suyan", "==========自定义数据" + json);
        return json;
    }

    //销毁
    public void onDestory() {
        mGuestLiveView = null;
        mContext = null;
        LiveMessageEvent.getInstance().deleteObserver(this);
        ILVLiveManager.getInstance().quitRoom(null);
        ILiveRoomManager.getInstance().onDestory();
        ILVLiveManager.getInstance().onDestory();
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,264评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,549评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,389评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,616评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,461评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,351评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,776评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,414评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,722评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,760评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,537评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,381评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,787评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,030评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,304评论 1 252
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,734评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,943评论 2 336

推荐阅读更多精彩内容

  • 主要功能 互动直播定义 互动直播(Interactive Live Video Broadcasting),顾名思...
    0蛐蛐0阅读 5,919评论 1 8
  • 全局创建context? 创建一个全局的context,然后退出SDK层房间时不销毁只是停止context。 SD...
    Carden阅读 1,413评论 0 2
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,142评论 25 707
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,656评论 2 59
  • 启动电脑发现,搜狗拼音出问题了。图标变大,输入没有选项。经查找,如下步骤解决问题 1,删除~/.config目录下...
    南王农夫阅读 951评论 0 0