直播的集成
写在前面
最近可能需要集成直播间的功能,当然华数这种企业不敢去做主播直播这种业务,也就是报道一下大型的活动而已,所以就将集成直播这种需求的文档发出来,给大家一点点参考,也是让我自己重新回顾一下直播的流程。
需要整个集成代码的童鞋可以留言。
移动直播的概念
由手机一端作为视频采集端,采集视频信息,通过网络实时传递到服务器,再由服务器推送到视频播放端的过程.
流媒体:采用流式传输的方式在Internet播放的媒体格式。这种播放形式不同于以往的播放形式,采集端以数据包的形式一个一个的发送到服务器,服务器将数据包推送给播放端.这样播放端不需要获取到完整的视频文件,就可以实现对采集端数据的播放任务.
直播中采用的文件传输方式就是流媒体文件传输方式.
注意:
直播中涉及到两个重要的概念就是:
视频采集端
视频播放端
视频直播相关的概念
编码的概念:
编码是用一些简单的字符来表达一定的信息,例如,如果明天是晴天用1来表示,阴天就是0
原理:
视频图像数据有极强的相关性,也就是说有大量的冗余信息。
通过压缩技术将数据中的冗余信息去掉(去除数据之间的相关性)。
压缩技术?
压缩技术就是消除数据间的相关性,一定的信息量尽量用较少的字符来表示
压缩技术分类:
帧内图像数据压缩技术、
帧间图像数据压缩技术
熵编码压缩技术。
视频压缩原理:
视频压缩中的每一帧画面都有相似的地方,在压缩时只保留与前一帧不同的地方及将信号中部分感觉不出的分量压缩掉或“掩蔽掉”,即祛除冗余压缩.
总结:相同的图像移除,不同的图像保留。
硬编码和软编码
硬编码:通过调用Android系统自带的Camera录制视频,实际上是调用了底层的高清编码硬件模块,也即显卡,不使用CPU,速度快
软编码:使用CPU进行编码,如常见C/C++代码,一般编译生成的二进制都是的,速度相对较慢。例如使用Android NDK编译H264生成so库,编写jni接口,再使用java调用so库。
视频文件特征
帧率:
每秒显示的图片数。影响画面流畅度,与画面流畅度成正比:帧率越大,画面越流畅;帧率越小,画面越有跳动感。由于人类眼睛的特殊生理结构,如果所看画面之帧率高于16的时候,就会认为是连贯的,此现象称之为视觉暂留。并且当帧速达到一定数值后,再增长的话,人眼也不容易察觉到有明显的流畅度提升了。
分辨率:
图片的长度和宽度,即图片的尺寸
码率:
把每秒显示的图片进行压缩后的数据量。影响体积,与体积成正比:码率越大,体积越大;码率越小,体积越小。(体积=码率×时间) 帧率X分辨率=压缩前的每秒数据量(单位应该是若干个字节)压缩比=压缩前的每秒数据量/码率(对于同一个视频源并采用同一种视频编码算法,则:压缩比越高,画面质量越差。)
所谓“清晰”,是指画面十分细腻,没有马赛克。并不是分辨率越高图像就越清晰。 简单说:在码率一定的情况下,分辨率与清晰度成反比关系:分辨率越高,图像越不清晰,分辨率越低,图像越清晰。在分辨率一定的情况下,码率与清晰度成正比关系,码率越高,图像越清晰;码率越低,图像越不清晰。
但是,事实情况却不是这么简单。可以这么说:在码率一定的情况下,分辨率在一定范围内取值都将是清晰的;同样地,在分辨率一定的情况下,码率在一定范围内取值都将是清晰的。 在视频压缩的过程中,I帧是帧内图像数据压缩,是独立帧。而P帧则是参考I帧进行帧间图像数据压缩,不是独立帧。在压缩后的视频中绝大多数都是P帧,故视频质量主要由P帧表现出来。由于P帧不是独立帧,而只是保存了与邻近的I帧的差值,故实际上并不存在分辨率的概念,应该看成一个二进制差值序列。而该二进制序列在使用熵编码压缩技术时会使用量化参数进行有损压缩,视频的质量直接由量化参数决定,而量化参数会直接影响到压缩比和码率。 视频质量可以通过主观和客观方式来表现,主观方式就是通常人们提到的视频清晰度,而客观参数则是量化参数或者压缩比或者码率。在视频源一样,压缩算法也一样的前提下比较,量化参数,压缩比和码率之间是有直接的比例关系的。 分辨率的变化又称为重新采样。由高分辨率变成低分辨率称为下采样,由于采样前数据充足,只需要尽量保留更多的信息量,一般可以获得相对较好的结果。而由低分辨率变成高分辨率称为上采样,由于需要插值等方法来补充(猜测)缺少的像素点,故必然会带有失真,这就是一种视频质量(清晰度)的损失。
CDN(内容分发网络):
CDN的全称是Content Delivery,Network,即内容分发网络。
作用:
尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。
原理:
通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。
目的:
使用户可就近取得所需内容,解决Internet网络拥挤的状况,提高用户访问网站的响应速度。
实现视频直播相关协议
RTMP协议:
RTMP是Real Time Messaging
Protocol(实时消息传输协议)的首字母缩写。该协议基于TCP,是一个协议族,包括RTMP基本协议及RTMPT/RTMPS/RTMPE等多种变种。
RTMP是一种设计用来进行实时数据通信的网络协议,主要用来在Flash/AIR平台和支持RTMP协议的流媒体/交互服务器之间进行音视频和数据通信。
支持该协议的软件包括Adobe Media Server/Ultrant Media Server/red5等。
RTMP协议传输数据的特点:
在RTMP协议中信令和媒体数据都称之为Message,在网络中传输这些Message,为了区分它们肯定是要加一个Message head的,所以RTMP协议也有一个Message head。
由于RTMP协议是基于TCP的,由于TCP的包长度是有限制的(一般来说不超过1500个字节),而RTMP的Message长度是有可能很大的,像一个视频帧的包可能会有几十甚至几千K,这个问题就必然有一个分片的问题,在RTMP协议中对应的说法就是chunk(分块),每一个Message + head都是由一个和多个chunk组成的。
RTCP:
实时通讯(Real-time communications)
采用与数据包相同的分发机制,将控制包周期性传输到所有会话参与者中。底层协议必须提供数据和控制包的多路发送
HLS介绍
HTTP Live Streaming(HLS)是苹果公司(Apple
Inc.)实现的基于HTTP的流媒体传输协议,可实现流媒体的直播和点播。
HLS点播,将媒体文件进行分段,边播放,边下载。
注意:分段非常小。
HLS工作原理
把整个媒体流分成一个个小的基于HTTP的文件。
当媒体流正在播放时,客户端可以选择从不同的备用源中以不同的速率下载分段文件,允许流媒体会话适应不同的数据速率。
在开始一个流媒体会话时,客户端会下载一个包含元数据的M3U8描述文件,用于寻找可用的媒体文件(ts)
由此可见:
HLS传输内容包括两部分,一是M3U8描述文件,二是TS媒体文件。
直播云
1,七牛直播云
这个公司虽然不像BAT那么有名气,但是公司技术很牛,整个公司确实是在一心研究技术.最开始做图片云存储,最近在搞直播,推荐这家公司的sdk.
2,金山云
采用最新H.265编码
3,阿里云
技术没问题,但专业性未知.
4,腾讯云
做视频聊天应该属于专业级别,因为腾讯QQ很早就有音视频聊天功能.
5,AnyRTC
小公司,不知道稳定性如何,最后没有采取这个公司的sdk
直播采集端和播放端的逻辑介绍
逻辑图直播实践-准备工作(案列:使用腾讯直播云)
1.首先导入依赖库:
与直播相关的jar
txrtmpsdk.jar
与条形码和二维码相关的jar
Barcodescanner-core-1.8.4.aar
Zxing-1.8.4.aar
Zxing-core-3.2.1.jar
2.添加与直播有关的动态链接库
3.添加对应的资源文件
资源文件直播实践-开始推流和停止推流的处理
1.初始化视图
初始化播放视频的View
初始化播放按钮view
2.初始化数据
初始化推送器
初始化推送配置
3.初始化监听
设置推送监听
初始化视图:
初始化视图初始化数据:
初始化数据初始化监听:
初始化监听修正码率实现:
修正码率推流:
推流推流监听的实现:
推流监听直播实践-VideoView的生命周期处理
注意实现:VideoView的生命周期和当前的Fragment生命周期就行同步。
同步生命周期:
同步生命周期 同步生命周期 同步生命周期切换摄像头
初始化我们的摄像头切换按钮 切换直播实践-实现摄像头对焦功能
初始化 对焦处理闪光灯
初始化 开启闪光灯直播实践-美颜和美白处理
当前的Fragment实现SeekBar.OnSeekBarChangeListener
@Override
public voidonProgressChanged(SeekBar seekBar,intprogress,booleanfromUser) {
switch(seekBar.getId()) {
caseR.id.beauty_seekbar:
mBeautyLevel= progress;
break;
caseR.id.whitening_seekbar:
mWhiteningLevel= progress;
break;
}
if(mLivePusher!=null) {
//进行美颜美白处理booleanisBeauty =mLivePusher.setBeautyFilter(mBeautyLevel,mWhiteningLevel);
if(!isBeauty) {
Toast.makeText(getActivity(),"当前机型不支持美颜", Toast.LENGTH_SHORT).show();
}
}
}
@Override
public voidonStartTrackingTouch(SeekBar seekBar) {
}
@Override
public voidonStopTrackingTouch(SeekBar seekBar) {
}
直播实践-修改码率
//设置码率的方法
private voidfitOrAdjustBitrate(intcheckedId){
switch(checkedId){
caseR.id.radio_btn_fix_720p:
if(mPusher!=null){
mConfig.setVideoResolution(TXLiveConstants.VIDEO_RESOLUTION_TYPE_720_1280);
mConfig.setAutoAdjustBitrate(false);
mConfig.setVideoBitrate(1500);
mPusher.setConfig(mConfig);}
mBtnBitrate.setBackgroundResource(R.drawable.fix_bitrate);
break;
caseR.id.radio_btn_fix_540p:
if(mPusher!=null){
mConfig.setVideoResolution(TXLiveConstants.VIDEO_RESOLUTION_TYPE_540_960);
mConfig.setAutoAdjustBitrate(false);
mConfig.setVideoBitrate(1000);
mPusher.setConfig(mConfig);}
mBtnBitrate.setBackgroundResource(R.drawable.fix_bitrate);
break;
caseR.id.radio_btn_fix_360p:
if(mPusher!=null){
mConfig.setVideoResolution(TXLiveConstants.VIDEO_RESOLUTION_TYPE_360_640);
mConfig.setAutoAdjustBitrate(false);
mConfig.setVideoBitrate(700);
mPusher.setConfig(mConfig);}
mBtnBitrate.setBackgroundResource(R.drawable.fix_bitrate);
break;
caseR.id.radio_btn_auto:
if(mPusher!=null){
//设置界面画质
mConfig.setVideoResolution(TXLiveConstants.VIDEO_RESOLUTION_TYPE_360_640);
//设置开启自适应码率
mConfig.setAutoAdjustBitrate(true);
//设置最大码率
mConfig.setMaxVideoBitrate(1000);
//设置最小码率
mConfig.setMinVideoBitrate(500);
//设置常规码率
mConfig.setVideoBitrate(700);
mPusher.setConfig(mConfig);
}
mBtnBitrate.setBackgroundResource(R.drawable.auto_bitrate);
break;
}
}
直播实践-播放直播视频的处理逻辑
初始化监听:
private voidinitLisenter() {
mVideoPlay=false;
//检查当前播放的是否直播数据源if(isLive){
//隐藏加载的布局progressGroup.setVisibility(View.GONE);
}
//点击开始播放mBtnPlay.setOnClickListener(newView.OnClickListener() {
@Override
public voidonClick(View v) {
if(mVideoPlay) {//正在播放
//判断是否播放的是直播视频if(!isLive) {//不是直播if(mVideoPause) {
mLivePlayer.resume();
mBtnPlay.setBackgroundResource(R.drawable.play_pause);
}else{
mLivePlayer.pause();
mBtnPlay.setBackgroundResource(R.drawable.play_start);
}
mVideoPause= !mVideoPause;
}else{//是直播stopPlayRtmp();
mVideoPlay= !mVideoPlay;
}
}else{//没有播放
//开始播放直播,不成功,则不修改播放状态if(startPlayRtmp()) {//开始播放mVideoPlay= !mVideoPlay;
}
}
}
});
}
播放视频 停止播放 监听播放直播实践-播放器的生命周期处理
播放器的生命周期和我们的Fragment生命周期进行同步
@Override
public voidonResume() {
super.onResume();
if(mPlayerView!=null){
mPlayerView.onResume();
}
}
@Override
public voidonStop() {
super.onStop();
if(mPlayerView!=null){
mPlayerView.onStop();
}
}
@Override
public voidonDestroy() {
super.onDestroy();
if(mLivePlayer!=null){
mLivePlayer.stopPlay(true);
}
if(mPlayerView!=null){
mPlayerView.onDestroy();
}
}
直播实践-切换横竖屏
播放的横竖屏幕切换 是否开启硬件加速直播实践-自适应和填充屏幕的处理
直播实践-缓存策略处理
//设置缓存模式private voidsetCacheStrategy(intcacheStrategy) {
if(mCurrentCacheStrategy==cacheStrategy){
return;
}
switch(cacheStrategy){
//极速模式caseCACHE_STRATEGY_FAST:
mPlayConfig.setAutoAdjustCacheTime(true);
mPlayConfig.setMaxAutoAdjustCacheTime(CACHE_TIME_FAST);
mPlayConfig.setMinAutoAdjustCacheTime(CACHE_TIME_FAST);
mLivePlayer.setConfig(mPlayConfig);
break;
//流畅模式caseCACHE_STRATEGY_SMOOTH:
mPlayConfig.setAutoAdjustCacheTime(false);
mPlayConfig.setCacheTime(CACHE_TIME_SMOOTH);
mLivePlayer.setConfig(mPlayConfig);
break;
//自动模式caseCACHE_STRATEGY_AUTO:
mPlayConfig.setAutoAdjustCacheTime(true);
mPlayConfig.setMaxAutoAdjustCacheTime(CACHE_TIME_AUTO_MAX);
mPlayConfig.setMinAutoAdjustCacheTime(CACHE_TIME_AUTO_MIN);
mLivePlayer.setConfig(mPlayConfig);
break;
}
}
结束语
业余时间随便写一下,其实这种调用本身已经难度不是特别大,基本上已经在SDK中被集成好了。