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

2024-06-22 11:12

本文主要是介绍Android视频直播、点播播放器哪家强?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

这里只是对主要功能点进行对比,更多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 “ I J K N D K R E L " i n 10 e ∗ ) . . . . . . . . e c h o " I J K N D K R E L = IJK_NDK_REL" in 10e*) ........ echo "IJK_NDK_REL= IJKNDKREL"in10e)........echo"IJKNDKREL=IJK_NDK_REL”
case “$IJK_NDK_REL” in
11*|12*|13*|14*)
if test -d A N D R O I D N D K / t o o l c h a i n s / a r m − l i n u x − a n d r o i d e a b i − 4.9 t h e n e c h o " N D K r {ANDROID_NDK}/toolchains/arm-linux-androideabi-4.9 then echo "NDKr ANDROIDNDK/toolchains/armlinuxandroideabi4.9thenecho"NDKrIJK_NDK_REL detected"
else
echo “You need the NDKr10e or later”
exit 1
fi
;;
*)
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= A N D R O I D N D K : ANDROID_NDK: ANDROIDNDK:ANDROID_SDK:$PATH

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

编译IjkPlayer

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

sudo apt-get update
sudo apt-get install git //安装git
sudo apt-get install yasm //安装yasm

sudo 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.sh

cd …
cd android/contrib
./compile-ffmpeg.sh clean //清理

cd ~/ijkplayer-android //返回源码根目录
./init-android.sh //主要是去下载ffmpeg

cd 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;

@Override
public IMediaPlayer newInstance() {
//创建IjkMeidaPlayer对象
mIjkMediaPlayer = new IjkMediaPlayer();
return this;
}

@Override
public void prepareAsync() throws IllegalStateException {
mIjkMediaPlayer.prepareAsync();
}

@Override
public void start() throws IllegalStateException {
mIjkMediaPlayer.start();
}

@Override
public 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;

@Override
public IMediaPlayer newInstance() {
//创建PLDroidPlayer的PLMediaPlayer对象
mMediaPlayer = new PLMediaPlayer(mContext, mAvOptions);
return this;
}

@Override
public void prepareAsync() throws IllegalStateException {
mMediaPlayer.prepareAsync();
}

@Override
public void start() throws IllegalStateException {
mMediaPlayer.start();
}

@Override
public 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使用的MediaPlayer
private 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();
}
}

@Override
public 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 {

@Override
public 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的log
LogUtils.getInstance().setOnUpdateLogListener(new LogUtils.OnUpdateLogListener() {
@Override
public void onUpdate(final long timestamp, final String msg) {
//在主线程刷新界面
post(new Runnable() {
@Override
public void run() {
String dateString = mSimpleDateFormat.format(new Date(timestamp));
String log = dateString + ": " + msg + “\n”;
//添加一行log
stringBuilder.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);
//初始化LogUtils
LogUtils.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{

@Override

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取
aPlayer iMediaPlayer) {
//播放结束,停止采样
LogUtils.getInstance().stop();
}

测试IjkPlayer

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

public class IjkPlayerActivity extends AppCompatActivity{

@Override

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

[外链图片转存中…(img-LZuhXrot-1719020883659)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取

这篇关于Android视频直播、点播播放器哪家强?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1084122

相关文章

Android Mainline基础简介

《AndroidMainline基础简介》AndroidMainline是通过模块化更新Android核心组件的框架,可能提高安全性,本文给大家介绍AndroidMainline基础简介,感兴趣的朋... 目录关键要点什么是 android Mainline?Android Mainline 的工作原理关键

如何解决idea的Module:‘:app‘platform‘android-32‘not found.问题

《如何解决idea的Module:‘:app‘platform‘android-32‘notfound.问题》:本文主要介绍如何解决idea的Module:‘:app‘platform‘andr... 目录idea的Module:‘:app‘pwww.chinasem.cnlatform‘android-32

Android实现打开本地pdf文件的两种方式

《Android实现打开本地pdf文件的两种方式》在现代应用中,PDF格式因其跨平台、稳定性好、展示内容一致等特点,在Android平台上,如何高效地打开本地PDF文件,不仅关系到用户体验,也直接影响... 目录一、项目概述二、相关知识2.1 PDF文件基本概述2.2 android 文件访问与存储权限2.

Android Studio 配置国内镜像源的实现步骤

《AndroidStudio配置国内镜像源的实现步骤》本文主要介绍了AndroidStudio配置国内镜像源的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录一、修改 hosts,解决 SDK 下载失败的问题二、修改 gradle 地址,解决 gradle

在Android平台上实现消息推送功能

《在Android平台上实现消息推送功能》随着移动互联网应用的飞速发展,消息推送已成为移动应用中不可或缺的功能,在Android平台上,实现消息推送涉及到服务端的消息发送、客户端的消息接收、通知渠道(... 目录一、项目概述二、相关知识介绍2.1 消息推送的基本原理2.2 Firebase Cloud Me

基于Python和MoviePy实现照片管理和视频合成工具

《基于Python和MoviePy实现照片管理和视频合成工具》在这篇博客中,我们将详细剖析一个基于Python的图形界面应用程序,该程序使用wxPython构建用户界面,并结合MoviePy、Pill... 目录引言项目概述代码结构分析1. 导入和依赖2. 主类:PhotoManager初始化方法:__in

Android中Dialog的使用详解

《Android中Dialog的使用详解》Dialog(对话框)是Android中常用的UI组件,用于临时显示重要信息或获取用户输入,本文给大家介绍Android中Dialog的使用,感兴趣的朋友一起... 目录android中Dialog的使用详解1. 基本Dialog类型1.1 AlertDialog(

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

Python基于wxPython和FFmpeg开发一个视频标签工具

《Python基于wxPython和FFmpeg开发一个视频标签工具》在当今数字媒体时代,视频内容的管理和标记变得越来越重要,无论是研究人员需要对实验视频进行时间点标记,还是个人用户希望对家庭视频进行... 目录引言1. 应用概述2. 技术栈分析2.1 核心库和模块2.2 wxpython作为GUI选择的优

Android Kotlin 高阶函数详解及其在协程中的应用小结

《AndroidKotlin高阶函数详解及其在协程中的应用小结》高阶函数是Kotlin中的一个重要特性,它能够将函数作为一等公民(First-ClassCitizen),使得代码更加简洁、灵活和可... 目录1. 引言2. 什么是高阶函数?3. 高阶函数的基础用法3.1 传递函数作为参数3.2 Lambda