直播平台软件开发中选择点播播放器哪家强?

太长不看版

这里选择了开源播放器IjkPlayer和直播云厂商播放器PLDroidPlayer作为测试样本。

数据统计

软硬编码 IjkPlayer PLDroidPlayer
首开(ms) 内存 min,avg,max(MB) CPU min,avg,max(%) 首开(ms) 内存 min,avg,max(MB) CPU min,avg,max(%)
软编码 1559 64.49,110.19,114.92 5.00,30.69,80.72 198 32.34,87.41,93.47 3.11,30.25,67.18
硬编码 2280 45.37,48.81,52.34 1.36,10.10,17.37 174 30.98,81.67,85.87 2.00,28.00,69.23

包体

对比点 IjkPlayer PLDroidPlayer
版本 0.8.4 2.0.3
jar/aar包 66KB(java) + 1342KB(armv7a)=1408KB 80KB
so库(armeabi-v7a,不带Https功能) 2.58MB 2.27MB
总计 3.96MB 2.35MB

注:

  1. IjkPlayer通过gradle下载下来为aar包,存放在目录C:\Users\(用户名)\.gradle\caches\modules-2\files-2.1\tv.danmaku.ijk.media。PLDroidPlayer为jar包。
  2. IjkPlayer至少需要用到两个包,分别是java包和armv7a包。
  3. IjkPlayer和PLDroid均可以支持Https,IjkPlayer需要单独编译,PLDroid只需添加libqcOpenSSL.so库即可。这里对比的是不带Https功能的。

功能点

直播平台软件开发中选择点播播放器哪家强?这里只是对主要功能点进行对比,更多PLDroidPlayer功能点介绍可以查看github.com/pili-engine…,而ijkPlayer并没有对其功能进行介绍:github.com/Bilibili/ij…

功能 IjkPlayer PLDroidPlayer
版本 0.8.4 2.0.3
RTMP 支持 支持
HLS 支持 支持
HTTP-FLV 支持 支持
HTTPS 支持(需要单独编译) 支持
硬解码 支持 支持
是否需要编译 需要 不需要
播放控件 不提供 提供
UI定制 可以 可以
文档 不完善 完善
是否开源 开源 不开源
集成难度 略麻烦 容易
技术支持

测试样本选择

先说下在测试样本选择上我是如何考虑的:

  1. 开源播放器大V选一家,直播云厂商选一家。
    ijk无可厚非是开源播放器中的首选,关于如何玩转ijk的文章也很多,口碑也比较好。而云服务厂商好歹是靠卖直播、点播产品赚钱的,播放器作为直播点播服务中重要的一环也是人家吃饭的工具,而且可以说被很多直播大客户验证过的,最重要的这些播放器SDK竟然都是免费的,不拿来用太浪费了。
  2. 视频云厂商播放器评测的选择。
    因为我司原生app本身体积比较大了,所以对我来说包体大小是第一要考虑的点。而所有在云厂商里,我选了包体最小的七牛作为测试样本。大家可以根据自己业务需求再选择测试样本。

当然,这里只是做了几次数据采样,需要结果更具说服力,可能还需要更多的测试条件和测试数据,不过我们可以从当前获取到的数据推断:

  1. 不管是软解码和硬解码,PLDroidPlayer的首开速度都要远快于IjkPlayer;
  2. 在软解码条件下,PLDroidPlayer的Cpu和内存消耗都要略低于IjkPlayer;
  3. 在硬解码条件下,PLDroidPlayer的Cpu和内存消耗都要高于IjkPlayer。

基础

在进行对比之前,我们需要对直播相关的基础概念做一些简单介绍,如果对这一块比较熟悉的同学可以跳过。

视频直播

视频直播就是视频数据从采集端(摄像头)通过网络实时推送到播放端(手机,电脑,电视等),我们最早接触到的视频直播可能就是电视直播了,但随着智能手机发展,移动直播兴起,它的视频采集端是手机,播放端通常也是手机。

视频点播

视频点播就是一段已经录制好的视频数据,用户可以点击播放。由于是已经录制完成的视频数据,所以还可以控制播放进度。

直播协议

直播协议常见的有三种:RTMP、Http-FLV和HLS。

  • RTMP: 基于TCP协议,由Adobe设计,将音视频数据切割成小的数据包在互联网上传输,延时3s以内,但拆包组包复杂,在海量并发情况下不稳定。由于不是基于Http协议,存在被防火墙墙掉的可能性。
  • Http-FLV:基于Http协议,由Adobe设计,在大块音视频数据头部添加标记信息,延时3s以内,海量并发稳定,手机浏览器支持不足。
  • HLS:基于Http协议,由Apple设计,将视频数据切分成片段(10s以内),由m3u8索引文件进行管理,高延时(10s到30s),手机浏览器支持较好,可通过网页转发直播链接。

软编码和硬编码

直播平台软件开发中音视频数据在互联网上传输之前,由于存在冗余数据,需要进行压缩编码,编码存在两种方式,一种是软编码一种是硬编码。

  • 软编码:使用CPU进行编码
  • 硬编码:使用非CPU进行编码,如GPU等

软解码和硬解码

数据进行编码之后传输到播放端,就要进行解码,那么解码也有两种方式,一种是软解码一种是硬解码。

  • 软解码:使用CPU进行解码
  • 硬解码:使用非CPU进行解码

IjkPlayer

IjkPlayer是B站开源播放器,地址为:github.com/Bilibili/ij…,基于音视频编解码库FFmpeg,支持常用的直播协议。IjkPlayer只提供播放器引擎库,不提供UI界面,所以使用IjkPlayer时还需要对UI界面进行二次封装,不过Github上有一些基于ijkplayer二次开发的播放器,他们对UI界面做了比较好的封装。

通过git命令:

git clone https://github.com/Bilibili/ijkplayer.git复制代码

下载ijkplayer完整项目,然后使用Android Studio打开目录:ijkplayer\android\ijkplayer,这个是Android的Demo项目,运行之后,效果如下:

但是,我们暂时还是无法播放示例视频列表当中的视频,还需要编译so库。在编译so库的过程中,躺坑躺到怀疑人生,跟大家分享一下,避免跟我一样踩坑。

Windows下编译IjkPlayer

首先大家最好不要在Windows环境下编译,因为我使用的是Windows系统,所以没有多想,下载代码后安装Cygwin,然后在Cygwin中安装make,yasm,装完之后以为大功告成,开始在Cygwin的命令行中执行编译脚本,但执行时报错,因为sh脚本还需要转换成unix版本,于是在Cygwin中又装了个dos2unix,将ijkplayer中的所有的sh脚本全部转换了一遍,然后再执行脚本,在读取一个配置文件configure又出了问题,还是文件格式问题,所以可以预见即使解决了这个文件,后续可能还有一大堆的文件存在这样的问题,细思极恐,果断弃坑。

Ubuntu下编译IjkPlayer

弃坑Windows后又在电脑的虚拟机上安装了Ubuntu系统,可是等在Ubuntu上面搭建完Android开发环境后,发现硬盘空间不足了,不要问我为什么空间不足,反正就是不足了,我能怎么办,我只能选择原谅自己咯。后来想到我原来的一台笔记本里面装了Ubuntu 16.04,于是擦擦上面的灰,开机启动。

搭建Android开发环境

1.安装JDK

sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
sudo apt-get install oracle-java8-installer
sudo apt-get install oracle-java8-set-default//在命令行中使用java -version,java, javac命令不报命令找不到,就算安装成功

2.安装Android Studio

去官网或者国内站点下载Linux版本的Android Studio,这里,我使用的是Android Studio3.0版本。下载完成后,将Android Studio解压到/opt目录,然后使用命令行执行Andorid Studio中的bin/studio.sh启动Android Studio,进入向导界面,向导界面最后确认去下载SDK。下载完成后SDK路径为/home/xxx(用户名)/Android/Sdk。

3.下载NDK

使用Android Studio下载完SDK后,不用创建项目,直接打开SDK Manager,在里面去下载NDK,下载完成之后存放在sdk目录下的ndk-bundle目录,但这里下载的NDK版本是比较新的版本16,而ijkplayer编译也是不支持的,因为在ijkplayer\android\contrib\tools中有个sh脚本会检查编译环境,其中有一段代码会检查NDK版本:

case "$IJK_NDK_REL" in10e*)........echo "IJK_NDK_REL=$IJK_NDK_REL"case "$IJK_NDK_REL" in11*|12*|13*|14*)if test -d ${ANDROID_NDK}/toolchains/arm-linux-androideabi-4.9thenecho "NDKr$IJK_NDK_REL detected"elseecho "You need the NDKr10e or later"exit 1fi;;*)echo "You need the NDKr10e or later"exit 1;;esac;;
esac复制代码

可以看出来这里只支持10e,11,12,13,14,所以ndk版本低了不行,高了也不行,没办法,我们得去重新去官网下载低一点的版本,如r14b。

4.配置SDK和NDK路径

找到/home/(用户名)/目录,使用快捷键Ctrl + H显示隐藏文件,找到.bashrc文件打开,配置自己的SDK和NDK路径,例如:

export ANDROID_NDK=/home/leon/Android/andriod-ndk-r14b
export ANDROID_SDK=/home/leon/Android/Sdk
export PATH=$ANDROID_NDK:$ANDROID_SDK:$PATH复制代码

配置完成后,重启命令行,输入ndk-build命令,如果不报命令行找不到,说明NDK环境变量配置成功。

编译IjkPlayer

Android环境搭建好后,就可以参考官方文档着手编译ijkplayer了。

sudo apt-get update
sudo apt-get install git //安装git
sudo apt-get install yasm //安装yasmsudo dpkg-reconfigure dash //在弹出提示框选择“否”来使用bash//下载ijkplayer到ijkplayer-android目录
git clone https://github.com/Bilibili/ijkplayer.git ijkplayer-android
cd ijkplayer-android //使用默认配置
cd config
rm module.sh
ln -s module-lite.sh module.shcd ..
cd android/contrib
./compile-ffmpeg.sh clean //清理cd ~/ijkplayer-android //返回源码根目录
./init-android.sh //主要是去下载ffmpegcd android/contrib
./compile-ffmpeg.sh clean
./compile-ffmpeg.sh all //编译ffmpeg,all是全部编译,需要等待一段时间cd .. //回到ijkplayer-android/android
./compile-ijk.sh all //编译ijkplayer复制代码

编译完成后,在android/ijkplayer目录下各个库模块当中找到生成的so库:

既然so库已经生成,就可以使用Andorid Studio再次打开ijkplayer中的安卓示例项目(android/ijkplayer),运行后就可以播放示例视频了。这个带有so库的示例项目我已上传到Github,地址为github.com/uncleleonfa…,欢迎下载。

PLDroidPlayer

PLDroidPlayer 是七牛推出的一款适用于 Android 平台的播放器 SDK,采用全自研的跨平台播放内核,拥有丰富的功能和优异的性能,可高度定制化和二次开发。示例项目地址为:github.com/pili-engine…。
PLDroidPlayer的集成要比ijkPlayer简单很多,不用自己编译so库,不用自己创建SurfaceView和TextureView来播放视频。可参考官方开发指南集成即可。

测试开发

直播平台软件开发中为了保证测试的变量只是播放器引擎本身(这里暂时将播放器引擎简单的理解为各个播放器的MediaPlayer),我们定义一个公共的UI界面即VideoView来播放视频流,然后通过代理模式去代理不同的播放器引擎。这样VideoView在播放视频时,可以通过代理使用不同的播放引擎(MediaPlayer)来播放。我们这里主要测试播放器播放视频首开的时间,播放器播放视频过程中Cpu,内存的占用情况。测试项目地址为:github.com/uncleleonfa…,测试项目运行效果:

IMediaPlayer

定义统一的MediaPlayer接口。

public interface IMediaPlayer {void prepareAsync() throws IllegalStateException;void start() throws IllegalStateException;void stop() throws IllegalStateException;void pause() throws IllegalStateException;void release();void reset();.......
}

IMeidaPlayerProxy

定义MeidaPlayer的代理接口,所有MediaPlayer的代理必须实现newInstance接口创建MediaPlayer。

interface IMediaPlayerProxy {IMediaPlayer newInstance();
}复制代码

IjkPlayer的MediaPlayer的代理

在使用IjkPlayer之前需要添加依赖,并且将编译好的so库添加到项目中的jniLibs下。

//添加ijkplayer依赖
dependencies {compile 'tv.danmaku.ijk.media:ijkplayer-java:0.8.4'compile 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.4'compile 'tv.danmaku.ijk.media:ijkplayer-x86:0.8.4'}//IjkMediaPlayer代理,实现IMediaPlayer接口
public class IjkMediaPlayerProxy implements IMediaPlayerProxy, IMediaPlayer {//声明一个IjkMediaPlayer对象private IjkMediaPlayer mIjkMediaPlayer;@Overridepublic IMediaPlayer newInstance() {//创建IjkMeidaPlayer对象mIjkMediaPlayer = new IjkMediaPlayer();return this;}@Overridepublic void prepareAsync() throws IllegalStateException {mIjkMediaPlayer.prepareAsync();}@Overridepublic void start() throws IllegalStateException {mIjkMediaPlayer.start();}@Overridepublic void stop() throws IllegalStateException {mIjkMediaPlayer.stop();}......
}复制代码

PLDroidPlayer的MediaPlayer代理

在使用PLMediaPlayer之前参考官方文档集成PLDroidPlayer

//PLMediaPlayer代理,实现IMediaPlayer接口
public class PLMediaPlayerProxy implements IMediaPlayerProxy, IMediaPlayer {//定义PLMediaPlayer对象private PLMediaPlayer mMediaPlayer;//AVOptions为MediaPlayer的选项配置,例如可以配置开启硬解码private AVOptions mAvOptions;@Overridepublic IMediaPlayer newInstance() {//创建PLDroidPlayer的PLMediaPlayer对象mMediaPlayer = new PLMediaPlayer(mContext, mAvOptions);return this;}@Overridepublic void prepareAsync() throws IllegalStateException {mMediaPlayer.prepareAsync();}@Overridepublic void start() throws IllegalStateException {mMediaPlayer.start();}@Overridepublic void stop() throws IllegalStateException {mMediaPlayer.stop();}........
}复制代码

VideoView

VideoView仿照原生VideoView的实现,这里主要修改的是MediaPlayer的逻辑,方便配置使用不同播放器的MediaPlayer。

public class VideoView extends SurfaceView implements IMediaPlayer.OnPreparedListener,IMediaPlayer.OnErrorListener,IMediaPlayer.OnCompletionListener,IMediaPlayer.OnInfoListener,IMediaPlayer.OnVideoSizeChangeListener{//定义MediaPlayer代理private IMediaPlayerProxy mMediaPlayerProxy;//定义VideoView使用的MediaPlayerprivate IMediaPlayer mMediaPlayer;//设置MediaPlayer的代理public void setMediaPlayerProxy(IMediaPlayerProxy mediaPlayerProxy) {mMediaPlayerProxy = mediaPlayerProxy;}//打开视频private void openVideo() {if (mVideoPath == null) {return;}release();//使用代理创建对应的MediaPlayer对象mMediaPlayer = mMediaPlayerProxy.newInstance();mMediaPlayer.setScreenOnWhilePlaying(true);mMediaPlayer.setDisplay(mSurfaceHolder);mMediaPlayer.setLogEnabled(BuildConfig.DEBUG);mMediaPlayer.setOnPreparedListener(this);mMediaPlayer.setOnInfoListener(this);mMediaPlayer.setOnCompletionListener(this);mMediaPlayer.setOnErrorListener(this);mMediaPlayer.setOnVideoSizeChangeListener(this);try {mMediaPlayer.setDataSource(mVideoPath);......mMediaPlayer.prepareAsync();} catch (IOException e) {e.printStackTrace();}}@Overridepublic void onPrepared(IMediaPlayer iMediaPlayer) {iMediaPlayer.start();//开始播放}
}

LogUtils

LogUtils用于采样cpu和内存数据,里面使用ScheduledThreadPoolExecutor每隔1s采样一次数据。

//开始采样
public void start() {scheduler.scheduleWithFixedDelay(new SampleTask(), 0L, 1000L, TimeUnit.MILLISECONDS);
}
//停止采样
public void stop() {scheduler.shutdown();
}//采样任务
private class SampleTask implements Runnable {@Overridepublic void run() {float cpu = sampleCPU(); //采样CPU使用float mem = sampleMemory(); //采样内存使用}
}复制代码

LogView

LogView是打印Log的自定义控件,它由一个TextView和ScrollView组成,TextView在ScrollView内部,用来显示log,ScrollView用来滚动。

public class LogView extends RelativeLayout {public LogView(@NonNull Context context, @Nullable AttributeSet attrs) {super(context, attrs);LayoutInflater.from(context).inflate(R.layout.view_log, this);final TextView textView = findViewById(R.id.tv);final ScrollView scrollView = findViewById(R.id.scroll_view);final StringBuilder stringBuilder = new StringBuilder();//监听LogUtils的logLogUtils.getInstance().setOnUpdateLogListener(new LogUtils.OnUpdateLogListener() {@Overridepublic void onUpdate(final long timestamp, final String msg) {//在主线程刷新界面post(new Runnable() {@Overridepublic void run() {String dateString = mSimpleDateFormat.format(new Date(timestamp));String log = dateString + ": " + msg + "\n";//添加一行logstringBuilder.append(log);//设置log显示textView.setText(stringBuilder.toString());//滚动ScrollView到底部scrollView.fullScroll(View.FOCUS_DOWN);}});}});}}复制代码

测试

测试视频流是:

//点播MP4视频
String path = "http://hc.yinyuetai.com/uploads/videos/common/2B40015FD4683805AAD2D7D35A80F606.mp4?sc=364e86c8a7f42de3&br=783&rd=Android";
//HLS直播流
String path = "http://ivi.bupt.edu.cn/hls/cctv1hd.m3u8";复制代码

在VieoView中,MediaPlayer开始准备播放之前,初始化LogUtils,埋点记录MediaPlayer的准备时间。

try {//设置视频源mMediaPlayer.setDataSource(mVideoPath);//初始化LogUtilsLogUtils.getInstance().init(getContext());//记录开始准备时间LogUtils.getInstance().onStartPrepare();mMediaPlayer.prepareAsync();
} catch (IOException e) {e.printStackTrace();
}复制代码

当MediaPlayer准备好后,会回调onPrepared,再次记录准备结束时间,这样,准备结束时间减去准备开始时间就是MediaPlayer准备耗时,即我们的首开时间。

//准备好后的回调
@Override
public void onPrepared(IMediaPlayer iMediaPlayer) {//记录准备结束时间LogUtils.getInstance().onEndPrepare();//开始播放iMediaPlayer.start();//开始每隔1s采样,播放结束后停止采样,主要用于点播采样LogUtils.getInstance().start();//开始每隔1s采样,采样5min,5min之后,自行停止,主要用于直播采样//LogUtils.getInstance().startForDuration(5);
}//播放结束
@Override
public void onCompletion(IMediaPlayer iMediaPlayer) {//播放结束,停止采样LogUtils.getInstance().stop();
}复制代码

测试IjkPlayer

创建一个IjkPlayerActivity使用IjkMediaPlayer来播放视频。

public class IjkPlayerActivity extends AppCompatActivity{@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_ijkplayer);//初始化IjkPlayerIjkMediaPlayer.loadLibrariesOnce(null);IjkMediaPlayer.native_profileBegin("libijkplayer.so");VideoView videoView = findViewById(R.id.video_view);//设置IjkMediaPlayer代理videoView.setMediaPlayerProxy(new IjkMediaPlayerProxy());String path = "视频url"//设置视频urlvideoView.setVideoPath(path);}@Overrideprotected void onStop() {super.onStop();//通知IjkMediaPlayer结束IjkMediaPlayer.native_profileEnd();}}

另外,直播平台软件开发中在VideoView初始化MediaPlayer时,可以调用enableMediaCodec()来开启IjkPlayer的硬解码:

private void openVideo() {.......mMediaPlayer.enableMediaCodec();}复制代码

测试PLDroidPlayer

创建一个PLDroidPlayerActivity使用PLMediaPlayer来播放视频。

public class PLDroidPlayerActivity extends AppCompatActivity{@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_pldroid);VideoView mVideoView = findViewById(R.id.video_view);//配置AVoptions来开启硬编码,默认为软编码AVOptions avOptions = new AVOptions();avOptions.setInteger(AVOptions.KEY_MEDIACODEC, AVOptions.MEDIA_CODEC_HW_DECODE);mVideoView.setMediaPlayerProxy(new PLMediaPlayerProxy(this, avOptions));String path = "视频url";mVideoView.setVideoPath(path);}}复制代码

测试结果

Round 1

  • 机型:Moto G (2代)
  • 系统版本:5.1
  • 数据源:String path = "ivi.bupt.edu.cn/hls/cctv1hd…";
  • 软硬编码:软解码
  • 采样时长:5min
  • IjkPlayer版本:0.8.4
  • PLDroid版本:2.0.3

IjkPlayer结果

  • 首开时间:1559ms
  • CPU占比最小值:5.00%
  • CPU占比最大值:80.72%
  • 平均CPU占比:30.69%
  • 内存使用最小值:63.49MB
  • 内存使用最大值:114.92MB
  • 平均内存:110.19MB

PLDroidPlayer结果

  • 首开时间:198ms
  • CPU占比最小值:3.11%
  • CPU占比最大值:67.18%
  • 平均CPU占比:30.25%
  • 内存使用最小值:32.34MB
  • 内存使用最大值:93.47MB
  • 平均内存:87.41MB

Round 2

  • 机型:Moto G (2代)
  • 系统版本:5.1
  • 数据源:String path = "ivi.bupt.edu.cn/hls/cctv1hd…";
  • 软硬编码:硬解码
  • 采样时长:5min
  • IjkPlayer版本:0.8.4
  • PLDroid版本:2.0.3

IjkPlayer结果

  • 首开时间:2280ms
  • CPU占比最小值:1.36%
  • CPU占比最大值:17.37%
  • 平均CPU占比:10.10%
  • 内存使用最小值:45.37MB
  • 内存使用最大值:52.34MB
  • 平均内存:48.81MB

PLDroidPlayer结果

  • 首开时间:174ms
  • CPU占比最小值:2.00%
  • CPU占比最大值:69.23%
  • 平均CPU占比:28.00%
  • 内存使用最小值:30.98MB
  • 内存使用最大值:85.87MB
  • 平均内存:81.67MB

直播平台软件开发中选择点播播放器哪家强?
本文转载自网络,感谢(进击的范大叔)的分享,转载仅为分享干货知识,如有侵权欢迎联系云豹科技进行删除处理

直播平台软件开发中选择点播播放器哪家强?相关推荐

  1. Android视频直播源码开发直播平台、点播播放器哪家强?

    Android视频直播源码开发直播平台.点播播放器哪家强? 最近在项目中要加入视频直播和点播功能,那么问题来了,我需要一个播放器来播放视频流,那该如何选择呢?除了原生的VideoView(VideoV ...

  2. Android视频直播、点播播放器哪家强?

    } IMeidaPlayerProxy 定义MeidaPlayer的代理接口,所有MediaPlayer的代理必须实现newInstance接口创建MediaPlayer. interface IMe ...

  3. 直播平台软件开发都使用了什么协议呢?

    直播平台软件开发都使用了什么协议呢? 最近直播比较火,很多人都喜欢看直播,那一个直播系统里面都有哪些组成部分,都使用了什么协议呢? 直播平台软件开发中无论是直播还是点播,其实都是对于视频数据的传输.一 ...

  4. 盘点直播直播平台软件开发技术中的编解码、直播协议、网络传输与简单实现

    盘点直播直播平台软件开发技术中的编解码.直播协议.网络传输与简单实现 编解码 视频封装格式就是我们通常所说的 .mp4,.flv,.ogv,.webm 等,它其实就是一个盒子,用来将实际的视频流以一定 ...

  5. 直播网站源码直播平台软件开发iOS动手做一个直播(原理篇)

    直播网站源码直播平台软件开发iOS动手做一个直播(原理篇) 上篇文章主要给出了代码,但是并没有详细说明直播相关的知识,这篇文章就说一下直播的相关理论知识.附上直播代码篇地址. ###推流 腾讯直播平台 ...

  6. 直播平台软件开发的前端如何实现整套视频直播技术流程

    直播平台软件开发的前端如何实现整套视频直播技术流程 正文 下面按照目录大纲来一个一个讲解. 1. 直播技术的介绍 直播技术涵盖很广,现如今大家广泛了解的就有视频网站的个人直播.手机直播.安防方面的摄像 ...

  7. 直播平台软件开发的流媒体直播为什么延迟很高

    直播平台软件开发的流媒体直播为什么延迟很高 通信技术的发展促进了视频点播和直播业务的兴起,4G 和 5G 网络技术的进步也使得流媒体技术变得越来越重要,但是网络技术并不能解决流媒体直播的高延迟问题,本 ...

  8. 直播平台软件开发,展示弹窗常见API详解

    直播平台软件开发,展示弹窗常见API详解 showToast 演示代码 wx.showToast({title: "购买失败",icon: "error",du ...

  9. 直播平台软件开发,input限制上传数量,规定图片上传数量

    直播平台软件开发,input限制上传数量,规定图片上传数量 /*** [fileCountCheck 上传文件数量检测]* @param {[Object]} filesObj [文件对象|input ...

最新文章

  1. sql server 2008连接oracle操作步骤详细记录,SQLServer连接Oracle详细步骤
  2. 互联网企业安全高级指南3.7.1 攻防驱动修改
  3. 大型机房综合布线图!绝对一流!
  4. 费马小定理与素数判定
  5. java annotation list_Java 注解 (Annotation)你可以这样学
  6. C++Odd Even Sort奇偶排序的实现算法(附完整源码)
  7. Terraform入门 - 4. destroy 基础设施
  8. Windows 安装 MySQL 8.0.11
  9. 服务器硬盘raid5扩容,超实用,物理服务器RAID扩容详解
  10. php fpm高并发,php-fpm 高并发、502解决方案
  11. 2019杭电多校Problem 5 Snowy Smile题解
  12. 小尺寸2.4G SMD贴片天线方案 CA-C03 CrossAir贴片天线
  13. 关于 kubernetes网络(CNI规范)中Calico,NetworkPolicy(网络策略)方面的一些笔记
  14. HEVC中低复杂度量化技术
  15. ServiceComb 课程
  16. A001-185-2521-李子泓
  17. C 中的auto register static extern的使用
  18. 【MySQL】MySQL高手是如何练成的?
  19. 人工智能导论学习笔记(考前复习)
  20. 移动端 ios 字体偏上问题

热门文章

  1. 微软急疯了?部分用户称PC自动升级到Win10
  2. linux module load, show, 包管理,找到包的路径
  3. 自动驾驶在挑战中进化的感知能力
  4. 关于在Opengl中先平移后旋转和先旋转后平移的效果不一样的原因
  5. (灵感)集设网关于设计灵感的网站
  6. 【Dash搭建可视化网站】项目13:销售数据可视化大屏制作步骤详解
  7. kube-scheduler源码分析(一)之 NewSchedulerCommand
  8. 如何启动一个vue项目
  9. 计算机科学与技术前瞻教育总结,计算机科学与技术专业的研究状况及前瞻.doc...
  10. Citavi、Endnote、Mendeley 功能大比拼!到底哪一款才是众望所归的终极文献管理软件?