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

最近在项目中要加入视频直播和点播功能,那么问题来了,我需要一个播放器来播放视频流,那该如何选择呢?除了原生的VideoView(VideoView表示臣妾做不到啊),还有一些播放器如Vitamio,B站开源的IjkPlayer等,当然各大直播云服务商也提供了自己的播放器。花两天时间调研了几家,顺便记录下来分享给大家,看一看到底哪家强。

太长不看版

这里选择了开源播放器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

Android视频直播源码开发直播平台、点播播放器哪家强?相关推荐

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

    直播平台软件开发中选择点播播放器哪家强? 太长不看版 这里选择了开源播放器IjkPlayer和直播云厂商播放器PLDroidPlayer作为测试样本. 数据统计 软硬编码 IjkPlayer PLDr ...

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

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

  3. 一对一直播源码 一对一视频交友源码开发

    一.什么是一对一视频交友源码 一对一直播交友源码指的就是交友软件的开发程序包,也即是开发者编写好固定模式的程序,让这些基础程序可以进行售卖流通,在这些基础程序上可以做一些二次开发,又不会对基础程序作出 ...

  4. 直播源码开发,Android 屏蔽返回键(后退键)

    直播源码开发,Android 屏蔽返回键(后退键) 方案一 在Activity中重写onBackPressed方法(推荐使用). @Override public void onBackPressed ...

  5. Android仿比心1对1直播源码开发,开机自动启动的实现

    在开发仿比心1对1直播源码过程中,常会要求仿比心1对1直播源码在系统开机时可以自动启动.这里,我将实现方法和遇到的一些问题总结于下文. 一. 实现 继承一个BroadcastReceiver用来处理B ...

  6. 直播系统开发,一对一直播源码开发

    如今,直播市场越发的火热,一对一直播源码也渐渐浮出水面,在直播大火的现在,您是不是也想要一款属于自己的直播平台呢?小编今天为您讲解一款一对一直播源码开发系统语言和媒体处理模块. 首先是系统开发语言 一 ...

  7. 一对一直播源码开发,一对一直播系统开发

    如今,直播市场越发的火热,一对一直播源码也渐渐浮出水面,在直播大火的现在,您是不是也想要一款属于自己的直播平台呢?小编今天为您讲解一款一对一直播源码开发系统语言和媒体处理模块. 首先是系统开发语言 一 ...

  8. 短视频app源码开发,短视频平台框架搭建

    科技发展,技术进步,音视频异军突起.无视频,不网络,短视频成为最重要的信息载体之一,是互联网核心组成部分.构建高效的短视频app源码,是慎之又慎的问题.从架构的角度,探讨短视频app源码的构建与技术选 ...

  9. 短视频+直播源码,直播老司机带你飞

    短视频+直播源码,直播老司机带你飞 脱胎于视频直播的短视频以近乎疯狂的速度攫取着互联网的流量,当抖音.快手的月活已经足够强大,他们又毫不犹豫地切入了直播腹地. 当互联网竞争进入下半场,依靠互联网人口. ...

最新文章

  1. Solaris Mount
  2. 如何找到 SAP Spartacus OCC cms page 发送请求时读取 API endpoint 的代码
  3. java基数排序 数组_万字长文带你掌握Java数组与排序,代码实现原理都帮你搞明白!...
  4. 张小龙:做 PC 版微信是一种破坏,本来不想做
  5. 负载均衡会话保持技术、原理、产品(以F5为例)
  6. nagios数据 mysql_读取NAGIOS数据库至MYSQL
  7. 常用的redis命令
  8. php 二分查找字符串,php实现的二分查找算法示例
  9. 我辞掉程序员的工作,体验了半年的自驾游生活
  10. TQ2440实现触摸屏和qt图形 解决segmentation fault
  11. X.509证书编码及解析
  12. 自动补全 (自定义)拼音分词器 搜索时注意事项
  13. ESP8266的FATAL EXCEPTION 28 29问题原因
  14. 移动端车牌识别,交警助力
  15. 新入手Seagate希捷1TB移动硬盘1t USB3.0一个
  16. Tomcat启动项目出现 javax.el.ELException
  17. MindManager2020官方中文版思维导图软件使用激活安装免费教程下载
  18. word中怎样输入标准破折号“——”
  19. 我的世界海洋java_我的世界Minecraft Java版18w15a发布
  20. Excel删除重复数据java_合并Excel范围中的数据,删除空白和重复项

热门文章

  1. centos7配置squid代理(透明代理)
  2. 《北风网网友录制Silverlight入门系列视频教程》共23课时/更新完毕[压缩包]
  3. 实现扫描图片出现3d模型的虚拟仿真技术
  4. matlab中画网格,matlab怎么画网格
  5. 搭建即可运营的秒收录导航网源码带广告管理完美运营版
  6. 无图片CSS圆角的五个实例
  7. 此语言无法安装在此计算机,[修复]无法在Windows 10中安装语言包 | MOS86
  8. Java实例类中的切面_Spring进行面向切面编程的一个简单例子
  9. 修改Opera浏览器主页搜索引擎
  10. python 云服务器 网站