视频直播场景的主要结构及流程如下图所示:

请注意:

除了上述基本功能,开发者如果想使用 ZegoLiveRoom SDK 实现更高级的功能,例如混音、音频录制、外部滤镜等,请直接参考 互动视频-高级功能指南,本文不再赘述。

为了便于开发者更快理解 LiveDemo5 中的逻辑,下述每节会将功能核心源码片段挑出来并加以讲解。开发者亦可直接阅读 LiveDemo5 源码,两者是一致的。

1 单主播直播

单主播直播时,一个房间内仅有一个主播,不支持主播与观众的连麦互动。单主播模式下,发布直播时,需要声明 mPublishFlag 为 ZegoConstants.PublishFlag.SingleAnchor。

1.1 主播推流

主播向观众推送自己的画面,需先推流(即发布直播)。如何使用 SDK 实现推流请参考:初级功能指南-推流

LiveDemo5 中推流相关源码片段演示如下,仅供参考:

// SingleAnchorPublishActivity.java

@Override

protected void doBusiness(Bundle savedInstanceState) {

super.doBusiness(savedInstanceState);

mRoomID = ZegoRoomUtil.getRoomID(ZegoRoomUtil.ROOM_TYPE_SINGLE);

// 登录房间

mZegoLiveRoom.loginRoom(mRoomID, mPublishTitle, ZegoConstants.RoomRole.Anchor, new IZegoLoginCompletionCallback() {

@Override

public void onLoginCompletion(int errorCode, ZegoStreamInfo[] zegoStreamInfos) {

if(errorCode == 0){

handleAnchorLoginRoomSuccess(zegoStreamInfos);

}else {

handleAnchorLoginRoomFail(errorCode);

}

}

});

// 设置推流相关回调

mZegoLiveRoom.setZegoLivePublisherCallback(new IZegoLivePublisherCallback() {

@Override

public void onPublishStateUpdate(int stateCode, String streamID, HashMap streamInfo) {

//推流状态更新

if (stateCode == 0) {

handlePublishSucc(streamID, streamInfo);

} else {

handlePublishStop(stateCode, streamID);

}

}

@Override

public void onJoinLiveRequest(final int seq, String fromUserID, String fromUserName, String roomID) {

}

@Override

public void onPublishQualityUpdate(String streamID, ZegoStreamQuality streamQuality) {

// 推流质量回调

handlePublishQualityUpdate(streamID, streamQuality.quality, streamQuality.videoFPS, streamQuality.videoBitrate);

}

@Override

public AuxData onAuxCallback(int dataLen) {

return handleAuxCallback(dataLen);

}

@Override

public void onCaptureVideoSizeChangedTo(int width, int height) {

}

@Override

public void onMixStreamConfigUpdate(int errorCode, String streamID, HashMap streamInfo) {

}

});

// 设置房间回调

mZegoLiveRoom.setZegoRoomCallback(new IZegoRoomCallback() {

@Override

public void onKickOut(int reason, String roomID) {

}

@Override

public void onDisconnect(int errorCode, String roomID) {

handleDisconnect(errorCode, roomID);

}

@Override

public void onStreamUpdated(final int type, final ZegoStreamInfo[] listStream, final String roomID) {

if (listStream != null && listStream.length > 0) {

switch (type) {

case ZegoConstants.StreamUpdateType.Added:

handleStreamAdded(listStream, roomID);

break;

case ZegoConstants.StreamUpdateType.Deleted:

handleStreamDeleted(listStream, roomID);

break;

}

}

}

@Override

public void onStreamExtraInfoUpdated(ZegoStreamInfo[] zegoStreamInfos, String s) {

}

@Override

public void onRecvCustomCommand(String userID, String userName, String content, String roomID) {

}

});

// 设置 IM 回调

mZegoLiveRoom.setZegoIMCallback(new IZegoIMCallback() {

@Override

public void onUserUpdate(ZegoUserState[] listUser, int updateType) {

handleUserUpdate(listUser, updateType);

}

@Override

public void onRecvRoomMessage(String roomID, ZegoRoomMessage[] listMsg) {

handleRecvRoomMsg(roomID, listMsg);

}

@Override

public void onRecvConversationMessage(String roomID, String conversationID, ZegoConversationMessage message) {

handleRecvConversationMsg(roomID, conversationID, message);

}

});

// 设置音频前处理回调

mZegoLiveRoom.setZegoAudioPrepCallback(new IZegoAudioPrepCallback() {

@Override

public void onAudioPrep(ByteBuffer inData, int sampleCount, int bitDepth, int sampleRate, ByteBuffer outData) {

// do audioprep something

...

}

});

}

/**

* 设置推流 flag 为 ZegoConstants.PublishFlag.SingleAnchor

*/

@Override

protected void initPublishConfigs() {

// 单主播模式, 直推CDN

mPublishFlag = ZegoConstants.PublishFlag.SingleAnchor;

}

/**

* 开始推流

*/

@Override

protected void doPublish() {

if (mIsPublishing) {

stopPublish();

} else {

startPublish();

}

}

// BaseLiveActivity.java

/**

* 开始推流

*/

protected void startPublish() {

// 6.0及以上的系统需要在运行时申请CAMERA RECORD_AUDIO权限

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED

|| ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {

ActivityCompat.requestPermissions(this, new String[]{

Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO}, 101);

} else {

publishStream();

}

} else {

publishStream();

}

}

/**

* 设置推流信息,打开预览并推流

*/

protected void publishStream() {

if (TextUtils.isEmpty(mPublishStreamID)) {

return;

}

ViewLive freeViewLive = getFreeViewLive();

if (freeViewLive == null) {

return;

}

// 设置流信息

freeViewLive.setStreamID(mPublishStreamID);

freeViewLive.setPublishView(true);

// 初始化配置信息, 混流模式使用

initPublishConfigs();

// 输出发布状态

recordLog(MY_SELF + ": start publishing(" + mPublishStreamID + ")");

// 设置水印

mZegoLiveRoom.setWaterMarkImagePath("asset:watermark.png");

Rect rect = new Rect();

rect.left = 50;

rect.top = 20;

rect.right = 200;

rect.bottom = 170;

mZegoLiveRoom.setPreviewWaterMarkRect(rect);

mZegoLiveRoom.setPublishWaterMarkRect(rect);

// 开启流量自动控制

int properties = ZegoConstants.ZegoTrafficControlProperty.ZEGOAPI_TRAFFIC_FPS

| ZegoConstants.ZegoTrafficControlProperty.ZEGOAPI_TRAFFIC_RESOLUTION;

mZegoLiveRoom.enableTrafficControl(properties, true);

// 开始播放

mZegoLiveRoom.setPreviewView(freeViewLive.getTextureView());

mZegoLiveRoom.startPreview();

mZegoLiveRoom.enableMic(mEnableMic);

mZegoLiveRoom.enableCamera(mEnableCamera);

mZegoLiveRoom.startPublishing(mPublishStreamID, mPublishTitle, mPublishFlag);

mZegoLiveRoom.setPreviewViewMode(ZegoVideoViewMode.ScaleAspectFill);

}

1.2 观众拉流

观众想看到主播画面,需拉流(即观看直播)。如何使用 SDK 实现拉流请参考:初级功能指南-拉流

LiveDemo5 中拉流相关源码片段演示如下,仅供参考:

// SingleAnchorPlayActivity.java

@Override

protected void doBusiness(Bundle savedInstanceState) {

super.doBusiness(savedInstanceState);

mZegoLiveRoom.loginRoom(mRoomID, ZegoConstants.RoomRole.Audience, new IZegoLoginCompletionCallback() {

@Override

public void onLoginCompletion(int errorCode, ZegoStreamInfo[] zegoStreamInfos) {

if(errorCode == 0){

handleAudienceLoginRoomSuccess(zegoStreamInfos);

}else {

handleAudienceLoginRoomFail(errorCode);

}

}

});

mZegoLiveRoom.setZegoLivePlayerCallback(new IZegoLivePlayerCallback() {

@Override

public void onPlayStateUpdate(int stateCode, String streamID) {

// 拉流状态更新

if (stateCode == 0) {

handlePlaySucc(streamID);

} else {

handlePlayStop(stateCode, streamID);

}

// 禁止连麦按钮

mTvPublisnControl.setEnabled(false);

}

@Override

public void onPlayQualityUpdate(String streamID, ZegoStreamQuality streamQuality) {

// 拉流质量回调

handlePlayQualityUpdate(streamID, streamQuality.quality, streamQuality.videoFPS, streamQuality.videoBitrate);

}

@Override

public void onInviteJoinLiveRequest(int seq, String fromUserID, String fromUserName, String roomID) {

}

@Override

public void onRecvEndJoinLiveCommand(String fromUserId, String fromUserName, String roomId) {

}

@Override

public void onVideoSizeChangedTo(String streamID, int width, int height) {

handleVideoSizeChanged(streamID, width, height);

}

});

mZegoLiveRoom.setZegoRoomCallback(new IZegoRoomCallback() {

@Override

public void onKickOut(int reason, String roomID) {

}

@Override

public void onDisconnect(int errorCode, String roomID) {

handleDisconnect(errorCode, roomID);

}

@Override

public void onStreamUpdated(final int type, final ZegoStreamInfo[] listStream, final String roomID) {

if (listStream != null && listStream.length > 0) {

switch (type) {

case ZegoConstants.StreamUpdateType.Added:

handleStreamAdded(listStream, roomID);

break;

case ZegoConstants.StreamUpdateType.Deleted:

handleStreamDeleted(listStream, roomID);

break;

}

}

}

@Override

public void onStreamExtraInfoUpdated(ZegoStreamInfo[] zegoStreamInfos, String s) {

}

@Override

public void onRecvCustomCommand(String userID, String userName, String content, String roomID) {

}

});

mZegoLiveRoom.setZegoIMCallback(new IZegoIMCallback() {

@Override

public void onUserUpdate(ZegoUserState[] listUser, int updateType) {

handleUserUpdate(listUser, updateType);

}

@Override

public void onRecvRoomMessage(String roomID, ZegoRoomMessage[] listMsg) {

handleRecvRoomMsg(roomID, listMsg);

}

@Override

public void onRecvConversationMessage(String roomID, String conversationID, ZegoConversationMessage message) {

handleRecvConversationMsg(roomID, conversationID, message);

}

});

}

// BasePlayActivity.java

/**

* 观众登录房间成功

*/

protected void handleAudienceLoginRoomSuccess(ZegoStreamInfo[] zegoStreamInfos) {

// 播放房间的流

if (zegoStreamInfos != null && zegoStreamInfos.length > 0) {

for (int i = 0; i < zegoStreamInfos.length; i++) {

startPlay(zegoStreamInfos[i].streamID);

}

}

// 分享主播视频

if (zegoStreamInfos != null && zegoStreamInfos.length > 0) {

for (ZegoStreamInfo info : zegoStreamInfos) {

ViewLive viewLive = getViewLiveByStreamID(info.streamID);

final HashMap mapInfo =

(new Gson()).fromJson(info.extraInfo, new TypeToken>() {

}.getType());

if (viewLive != null && mapInfo != null) {

boolean firstAnchor = Boolean.valueOf(mapInfo.get(Constants.FIRST_ANCHOR));

if (firstAnchor) {

List listUrls = new ArrayList<>();

listUrls.add(mapInfo.get(Constants.KEY_HLS));

listUrls.add(mapInfo.get(Constants.KEY_RTMP));

viewLive.setListShareUrls(listUrls);

break;

}

}

}

}

// 打印log

recordLog(MY_SELF + ": onLoginRoom success(" + mRoomID + "), streamCounts:" + zegoStreamInfos.length);

}

/**

* 房间内用户创建流

*/

protected void handleStreamAdded(final ZegoStreamInfo[] listStream, final String roomID) {

if (listStream != null && listStream.length > 0) {

for (int i = 0; i < listStream.length; i++) {

recordLog(listStream[i].userName + ": added stream(" + listStream[i].streamID + ")");

startPlay(listStream[i].streamID);

}

}

}

1.3 API 调用时序图

单主播直播 API 调用时序图如下所示:

2 多主播直播

2.1 功能介绍

多主播直播是主播与观众连麦,使观众也成为主播的互动功能。多主播直播的推流、拉流流程,同单主播基本一致,不同点在于,主播发布直播时,需要声明 mPublishFlag 为 ZegoConstants.PublishFlag.JoinPublish。

如果开发者要实现多主播(连麦)直播,请参考:高级功能指南-连麦

连麦功能涉及的 Demo 源码较多,请开发者参考功能指南,自行查阅 LiveDemo5 源码。

2.2 系统架构图

连麦系统架构图如下所示:

2.3 API 调用时序图

多主播直播 API 调用时序图如下所示:

3 混流直播

3.1 功能介绍

混流直播模式下,Zego 服务器会将多路单独的音视频流混成一路音视频流。混流直播的推流、拉流流程,同单主播基本一致,不同点在于:

主播发布直播时,需要声明 mPublishFlag 为 ZegoConstants.PublishFlag.MixStream。

观众可以使用 mixStreamID 选择拉一路混流数据,也可使用 streamID 分别拉多路单流数据,取决于用户需求,SDK 不做强制要求。

混流直播模式下,支持主播与观众连麦。

对于上述第 3 点,多主播(连麦)直播与混流直播中的连麦区别是:

连麦模式:若两个主播连麦,观众端需要拉两路单独音视频流播放两个主播的画面。

混流模式:若两个主播连麦,Zego 混流服务器会把两路单独的音视频流混合成一路音视频流。观众端有两个选择:1、拉一路混流播放两主播的画面。2、拉两路单独的流播放两个主播的画面。

此时,混流直播相对于多主播直播的优势:

观众端只要拉一路混流就可以成功播放两个主播的画面。

只需要分享一个直播链接,观众端就可以通过回放录播文件,看到两个主播的画面。

如果开发者要实现主播推流混流,请参考:高级功能指南-混流

LiveDemo5 中设置混流相关的源码片段演示如下,仅供参考:

// MixStreamPublishActivity.java

@Override

protected void doBusiness(Bundle savedInstanceState) {

super.doBusiness(savedInstanceState);

mRoomID = ZegoRoomUtil.getRoomID(ZegoRoomUtil.ROOM_TYPE_MIX);

// 登录房间

mZegoLiveRoom.loginRoom(mRoomID, mPublishTitle, ZegoConstants.RoomRole.Anchor, new IZegoLoginCompletionCallback() {

@Override

public void onLoginCompletion(int errorCode, ZegoStreamInfo[] zegoStreamInfos) {

if (errorCode == 0) {

handleAnchorLoginRoomSuccess(zegoStreamInfos);

} else {

handleAnchorLoginRoomFail(errorCode);

}

}

});

// 设置各种回调

...

}

/**

* 推混流

*/

@Override

protected void handlePublishSuccMix(String streamID, HashMap info) {

super.handlePublishSucc(streamID);

ZegoMixStreamInfo mixStreamInfo = new ZegoMixStreamInfo();

mixStreamInfo.streamID = mPublishStreamID;

mixStreamInfo.top = 0;

mixStreamInfo.bottom = ZegoApiManager.getInstance().getZegoAvConfig().getVideoEncodeResolutionHeight();

mixStreamInfo.left = 0;

mixStreamInfo.right = ZegoApiManager.getInstance().getZegoAvConfig().getVideoEncodeResolutionWidth();

mMixStreamInfos.add(mixStreamInfo);

startMixStream();

}

/**

* 房间内用户创建流.

*/

protected void handleMixStreamAdded(final ZegoStreamInfo[] listStream, final String roomID) {

ZegoAvConfig zegoAvConfig = ZegoApiManager.getInstance().getZegoAvConfig();

int width = zegoAvConfig.getVideoEncodeResolutionWidth();

int height = zegoAvConfig.getVideoEncodeResolutionHeight();

if (listStream != null && listStream.length > 0) {

for (ZegoStreamInfo streamInfo : listStream) {

recordLog(streamInfo.userName + ": onStreamAdd(" + streamInfo.streamID + ")");

startPlay(streamInfo.streamID);

if (mMixStreamInfos.size() == 1) {

ZegoMixStreamInfo mixStreamInfo = new ZegoMixStreamInfo();

mixStreamInfo.streamID = streamInfo.streamID;

mixStreamInfo.top = (int) (height * 2.0 / 3);

mixStreamInfo.bottom = height;

mixStreamInfo.left = (int) (width * 2.0 / 3);

mixStreamInfo.right = width;

mMixStreamInfos.add(mixStreamInfo);

} else if (mMixStreamInfos.size() == 2) {

ZegoMixStreamInfo mixStreamInfo = new ZegoMixStreamInfo();

mixStreamInfo.streamID = streamInfo.streamID;

mixStreamInfo.top = (int) (height * 2.0 / 3);

mixStreamInfo.bottom = height;

mixStreamInfo.left = (int) (width * 1.0 / 3) - 10;

mixStreamInfo.right = (int) (width * 2.0 / 3) - 10;

mMixStreamInfos.add(mixStreamInfo);

}

}

startMixStream();

}

}

3.2 系统架构图

混流系统架构图如下所示:

3.3 API 调用时序图

混流直播 API 调用时序图如下所示:

4 实现结构图

单主播、多主播、混流模式的实现区别如下:

单主播模式:publishFlag = ZegoConstants.PublishFlag.SingleAnchor,推流端推流到 CDN,拉流端从 CDN 拉流。

多主播模式:publishFlag = ZegoConstants.PublishFlag.JoinPublish,推流端推流到 Zego 媒体服务器,拉流端从 CDN 拉多路单流。

混流模式:publishFlag = ZegoConstants.PublishFlag.MixStream,推流端推流到 Zego 媒体服务器,拉流端有两个选择:可以从 CDN 拉多路单流,也可以从 CDN 拉一路混流。

数据流结构图如下:

android直播功能实现,Android视频直播功能实现流程相关推荐

  1. 一对一视频直播源码|手机视频直播平台android源码/可打包视频直播

    源码介绍 一对一视频直播源码|手机视频直播平台android源码集视频.语音.动态.直播.社交等功能于一身. 前端功能说明: 登入注册:手机验证码登入.编辑个人信息.上传头像 首页直播:查看主播用户. ...

  2. 如何评判在线直播源码优劣?视频直播软件开发经验之谈

    直播经过这些年的不断发展,使用者逐渐增加,在视频直播软件开发行业从业者的不断努力下,在线直播源码变得越来越完善,甚至已有了一些开发窍门的积累和判断视频直播软件优劣性的指标,本篇文章就来讲讲,该如何评判 ...

  3. 直播源码开发视频直播平台,不得不了解的流程...

    随着直播行业的不断发展,直播平台市场已基本稳定,诸如斗鱼.虎牙等头部直播平台都已选择上市之路.如今开发视频直播平台还有市场吗?答案是"有".现在的视频直播平台在垂直细分领域的发展越 ...

  4. Android开发基于RTMP实现视频直播

    前言 近两年时间,视频直播可谓大火.在视频直播领域,有不同的商家提供各种的商业解决方案,包括软硬件设备,摄像机,编码器,流媒体服务器等.本文要讲解的是如何使用一系列免费工具,打造一套视频直播方案. 视 ...

  5. centos连接xrdp桌面黑屏_小鹅通|视频直播桌面共享模式|视频直播OBS使用教程

    点击蓝字关注我们 视频直播-OBS使用教程 应用场景 OBS直播主要用于与学员共享桌面的教学直播场景 配合网页工具完成完整课堂直播(网页端用于评论互动,OBS用于视频推流) 一.网页端使用教程 网页端 ...

  6. 一对一直播源码 一对一视频直播软件未来发展趋势

    一对一视频直播软件对比传统直播平台,直播方式更简单自由,同时也更加私密,比传统直播平台更能保护用户私密,又能增加主播与用户之间的互动. 一对一视频直播的方式颠覆了人们对传统直播的看法,一对一直播凭借高 ...

  7. 网络直播的发展和视频直播APP系统软件的简单介绍

    网络直播平台自2012年起,在以YY为代表的一部分提供视频实时直播的网络平台上兴起,随着网络直播技术的日益发展,游戏等相关产业的飞速发展,以斗鱼为代表的一部分网络公司,在借鉴YY等平台的直播功能的基础 ...

  8. 双机位摄像机视频直播(多机位摄像机视频直播)时硬件连接示意图

    2017-6-24,长沙,下了场好大的雨,空气真好. 下图是一个双机位直播的接线示意图(同样适应于多机位),图上是以保利威视采集卡为例的接线示意,当然采集卡也可以换成其他牌子,接线方式是一样的. 值得 ...

  9. 一种全新的直播形式:一对一视频直播系统

    直播在当下正成为一种新的趋势,视频直播如此火爆主要因为网络带宽和速度在逐渐提高,网络成本在逐渐下降,视频直播,既是一种娱乐活动,也是一种传播方式.如今,视频直播广泛的运用在各个行业,电商.游戏.教育. ...

  10. 直播软件开发IOS直播客户端SDK,视频直播APP源码【开源】

    当前视频直播非常火爆,手机端的视频直播也非常火爆,PGC.UGC的视频直播门槛都降低了很多. 本文介绍一个:IOS 客户端直播的SDK,代码完全开源. 直播时代:让IOS普通开发者一天内做出一个RTM ...

最新文章

  1. 机器学习系列(9)_机器学习算法一览(附Python和R代码)
  2. Metail Design入门(一)
  3. oracle 600 侯,oracle在导入数据时报600错误的解决方法
  4. USB2.0协议学习笔记---USB工作过程(类的方法)
  5. 干货 | 一文轻松了解NLP所有相关任务简介!
  6. Silverlight中的ControlTemplate
  7. mysql 存储过程 查询语句怎么写_mysql 查询数据库中的存储过程与函数的语句
  8. 超赞,1万字的后端面试题及面试经验分享!
  9. LateX安装下载使用详细教程
  10. Linux系统工具sar查看主机性能指标(内存、CPU、IO)
  11. 转载: DEV控件:gridControl常用属性设置_人生一世 草木一秋_百度空间
  12. SAP CO TCode
  13. Android 快速集成阿里云OSS服务2020
  14. 隐马尔科夫模型(HMM)等文章记录
  15. MongoDB 学习笔记八 复制、分片、备份与恢复、监控
  16. MATLAB 如何让图形变美?[第一期]
  17. 【笔记】案例研究(红酒和白酒)
  18. 软件系统维护是一项不吸引人的工作_工作流程管理系统六大特点,助您工作更高效...
  19. 2021-08-31 CoreDNS:Kubernetes内部域名解析原理、弊端及优化方式
  20. 来,开局先创建一个 app

热门文章

  1. python版雪花算法生成唯一ID
  2. [Style Transfer]——Boundary and Entropy-driven Adversarial Learning for Fundus Image Segmentation
  3. 蓝牙耳机哪款适合新手?高颜值小巧,女孩子最爱五款蓝牙耳机推荐
  4. oracle怎么 查询表名,oracle查询表名
  5. 计算机史话pdf百度云,学生应知科技知识·计算机史话.pdf
  6. 根据RGB值,JSP页面显示相应颜色色块
  7. MySQL 的索引、执行计划、优化器算法
  8. cesium 缩放_Cesium 中的图形变换:局部平移、缩放、旋转思路及代码实现
  9. 深度神经网络算法有哪些,深度神经网络主要模型
  10. 有关java中equals()与hashCode()的探讨