android直播功能实现,Android视频直播功能实现流程
视频直播场景的主要结构及流程如下图所示:
请注意:
除了上述基本功能,开发者如果想使用 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视频直播功能实现流程相关推荐
- 一对一视频直播源码|手机视频直播平台android源码/可打包视频直播
源码介绍 一对一视频直播源码|手机视频直播平台android源码集视频.语音.动态.直播.社交等功能于一身. 前端功能说明: 登入注册:手机验证码登入.编辑个人信息.上传头像 首页直播:查看主播用户. ...
- 如何评判在线直播源码优劣?视频直播软件开发经验之谈
直播经过这些年的不断发展,使用者逐渐增加,在视频直播软件开发行业从业者的不断努力下,在线直播源码变得越来越完善,甚至已有了一些开发窍门的积累和判断视频直播软件优劣性的指标,本篇文章就来讲讲,该如何评判 ...
- 直播源码开发视频直播平台,不得不了解的流程...
随着直播行业的不断发展,直播平台市场已基本稳定,诸如斗鱼.虎牙等头部直播平台都已选择上市之路.如今开发视频直播平台还有市场吗?答案是"有".现在的视频直播平台在垂直细分领域的发展越 ...
- Android开发基于RTMP实现视频直播
前言 近两年时间,视频直播可谓大火.在视频直播领域,有不同的商家提供各种的商业解决方案,包括软硬件设备,摄像机,编码器,流媒体服务器等.本文要讲解的是如何使用一系列免费工具,打造一套视频直播方案. 视 ...
- centos连接xrdp桌面黑屏_小鹅通|视频直播桌面共享模式|视频直播OBS使用教程
点击蓝字关注我们 视频直播-OBS使用教程 应用场景 OBS直播主要用于与学员共享桌面的教学直播场景 配合网页工具完成完整课堂直播(网页端用于评论互动,OBS用于视频推流) 一.网页端使用教程 网页端 ...
- 一对一直播源码 一对一视频直播软件未来发展趋势
一对一视频直播软件对比传统直播平台,直播方式更简单自由,同时也更加私密,比传统直播平台更能保护用户私密,又能增加主播与用户之间的互动. 一对一视频直播的方式颠覆了人们对传统直播的看法,一对一直播凭借高 ...
- 网络直播的发展和视频直播APP系统软件的简单介绍
网络直播平台自2012年起,在以YY为代表的一部分提供视频实时直播的网络平台上兴起,随着网络直播技术的日益发展,游戏等相关产业的飞速发展,以斗鱼为代表的一部分网络公司,在借鉴YY等平台的直播功能的基础 ...
- 双机位摄像机视频直播(多机位摄像机视频直播)时硬件连接示意图
2017-6-24,长沙,下了场好大的雨,空气真好. 下图是一个双机位直播的接线示意图(同样适应于多机位),图上是以保利威视采集卡为例的接线示意,当然采集卡也可以换成其他牌子,接线方式是一样的. 值得 ...
- 一种全新的直播形式:一对一视频直播系统
直播在当下正成为一种新的趋势,视频直播如此火爆主要因为网络带宽和速度在逐渐提高,网络成本在逐渐下降,视频直播,既是一种娱乐活动,也是一种传播方式.如今,视频直播广泛的运用在各个行业,电商.游戏.教育. ...
- 直播软件开发IOS直播客户端SDK,视频直播APP源码【开源】
当前视频直播非常火爆,手机端的视频直播也非常火爆,PGC.UGC的视频直播门槛都降低了很多. 本文介绍一个:IOS 客户端直播的SDK,代码完全开源. 直播时代:让IOS普通开发者一天内做出一个RTM ...
最新文章
- 机器学习系列(9)_机器学习算法一览(附Python和R代码)
- Metail Design入门(一)
- oracle 600 侯,oracle在导入数据时报600错误的解决方法
- USB2.0协议学习笔记---USB工作过程(类的方法)
- 干货 | 一文轻松了解NLP所有相关任务简介!
- Silverlight中的ControlTemplate
- mysql 存储过程 查询语句怎么写_mysql 查询数据库中的存储过程与函数的语句
- 超赞,1万字的后端面试题及面试经验分享!
- LateX安装下载使用详细教程
- Linux系统工具sar查看主机性能指标(内存、CPU、IO)
- 转载: DEV控件:gridControl常用属性设置_人生一世 草木一秋_百度空间
- SAP CO TCode
- Android 快速集成阿里云OSS服务2020
- 隐马尔科夫模型(HMM)等文章记录
- MongoDB 学习笔记八 复制、分片、备份与恢复、监控
- MATLAB 如何让图形变美?[第一期]
- 【笔记】案例研究(红酒和白酒)
- 软件系统维护是一项不吸引人的工作_工作流程管理系统六大特点,助您工作更高效...
- 2021-08-31 CoreDNS:Kubernetes内部域名解析原理、弊端及优化方式
- 来,开局先创建一个 app
热门文章
- python版雪花算法生成唯一ID
- [Style Transfer]——Boundary and Entropy-driven Adversarial Learning for Fundus Image Segmentation
- 蓝牙耳机哪款适合新手?高颜值小巧,女孩子最爱五款蓝牙耳机推荐
- oracle怎么 查询表名,oracle查询表名
- 计算机史话pdf百度云,学生应知科技知识·计算机史话.pdf
- 根据RGB值,JSP页面显示相应颜色色块
- MySQL 的索引、执行计划、优化器算法
- cesium 缩放_Cesium 中的图形变换:局部平移、缩放、旋转思路及代码实现
- 深度神经网络算法有哪些,深度神经网络主要模型
- 有关java中equals()与hashCode()的探讨