最终效果图如下:
说起Android的播放器,就会想起MediaPlayer,但是MediaPlayer本身的坑太多,连google都推出了ExoPlayer来代替MediaPlayer,如今网上已经开源了许多成熟的功能强大的播放器。比如bilibili的Ijkplayer,google的ExoPlayer,GSYVideoPlayer等。
下面介绍一下ijkplayer。ijkplayer是一款基于ffplay的轻量级的跨平台的播放器,有类似于MediaPlayer的API,github上有超过23000+个star,ijkplayer底层是c实现的,Android通过jni的方式调用。
ijkplayer地址:https://github.com/bilibili/ijkplayer
ijkplayer的集成方式有两种:
# required
allprojects {
repositories {
jcenter()
}
}
dependencies {
# required, enough for most devices.
compile 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8'
compile 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.8'
# Other ABIs: optional
compile 'tv.danmaku.ijk.media:ijkplayer-armv5:0.8.8'
compile 'tv.danmaku.ijk.media:ijkplayer-arm64:0.8.8'
compile 'tv.danmaku.ijk.media:ijkplayer-x86:0.8.8'
compile 'tv.danmaku.ijk.media:ijkplayer-x86_64:0.8.8'
# ExoPlayer as IMediaPlayer: optional, experimental
compile 'tv.danmaku.ijk.media:ijkplayer-exo:0.8.8'
}
如果你需要一些特殊的配置,比如默认ijkplayer是不支持https的,你需要手动配置并编译支持https的so。
1.ndk r10 ndk是向上兼容的,所以版本选择比较低一点的比较好
2.homebrew环境 ,许多软件都可以通过homebrew安装,比如git,homebrew的安装方式:在终端输入
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
3.git,如果安装好了homebrew,就可以直接使用brew install git 命令安装git了
4.nasm/yasm, 直接通过brew install nasm或者 brew install yasm安装, 这个是ffmpeg为了加快编译速度需要使用的。Ps:我在Mac上安装yasm后使用ffmpeg编译还是提示我yasm not found, 不知道怎么解决,最后安装了nasm才解决。
5.ffmpeg,ffmpeg编译so时会用到,这里我是通过brew install ffmpeg方式安装的,
Ps:途中会下载许多的依赖,并且可能会提示报错,比如:
xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun
这个是说无效的路径,找不到这个路径,其实就是没有安装xcode的命令行工具,解决办法就是:打开终端使用:xcode-select --install解决。
还有一个错误:
python3 link Error: An unexpected error occurred during the brew link step
这是homebrew在安装相应的依赖报的错,原因是我们没有给/usr/local/设置权限。解决办法是
使用命令sudo chown -R $(whoami) /usr/local/python3 然后使用brew link python3重新link一下,最后重新执行brew install ffmpeg即可。
6.编辑bash_profile文件配置环境变量这里配置sdk和ndk环境变量即可
git clone https://github.com/Bilibili/ijkplayer.git ijkplayer-android
cd ijkplayer-android
git checkout -B latest k0.8.8
./init-android.sh
# 如果要添加对https的支持(可选)
./init-android-openssl.sh
cd android/contrib
./compile-ffmpeg.sh clean
# 编译 openssl(可选)
./compile-openssl.sh clean
./compile-openssl.sh all
# 默认编译arm-v7的so,all代表编译所有平台下的so,我们可以有选择性的编译指定的平台so
./compile-ffmpeg.sh all
cd ..
# 默认编译arm-v7的so,all代表编译所有平台下的so,我们可以有选择性的编译指定的平台so
./compile-ijk.sh all
编译成功以后cd到ijkplayer目录下,此时的so是支持https的,直接把对应平台下的so拷贝到项目中的jnilibs去。
你可以直接这么依赖,版本号要和编译so的分支的版本号一致
compile 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8'
也可以直接把编译生成的ijkplayer-java依赖到项目里去都可以。
主要封装了一些MediaPlayer常用的方法
public class CusIjkPlayer extends FrameLayout {
/**
* 由ijkplayer提供,用于播放视频,需要给他传入一个surfaceView
*/
private IMediaPlayer mMedialPlayer = null;
/**
* 视频文件地址
*/
private String mPath;
/**
* 视频请求header
*/
private Map mHeader;
private SurfaceView mSurfaceView;
private Context mContext;
private boolean mEnableMediaCodec;
private AudioManager mAudioManager;
private AudioFocusHelper mAudioFousHelper;
private VideoListener mListener;
public CusIjkPlayer(Context context) {
this(context, null);
}
public CusIjkPlayer(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CusIjkPlayer(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
mContext = context;
setBackgroundColor(Color.BLACK);
createSurfaceView();
mAudioManager = (AudioManager) mContext.getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
mAudioFousHelper = AudioFocusHelper.getInstance(mContext);
}
private void createSurfaceView() {
mSurfaceView = new SurfaceView(mContext);
mSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (mMedialPlayer != null) {
mMedialPlayer.setDisplay(holder);
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
});
LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, Gravity.CENTER);
addView(mSurfaceView, 0, layoutParams);
}
private IMediaPlayer createPlayer() {
IjkMediaPlayer ijkMediaPlayer = new IjkMediaPlayer();
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_clear", 1);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 1);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", IjkMediaPlayer.SDL_FCC_RV32);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "skip_loop_filter", 1);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "http-detect-range-support", 48);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "min-frames", 100);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "enable-accurate-seek", 1);
ijkMediaPlayer.setVolume(1.0f, 1.0f);
setEnableMediaCodec(ijkMediaPlayer, mEnableMediaCodec);
return ijkMediaPlayer;
}
/**
* 设置是否开启硬解码
*/
private void setEnableMediaCodec(IjkMediaPlayer ijkMediaPlayer, boolean isEnable) {
int value = isEnable ? 1 : 0;
// 开启硬解码
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", value);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", value);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", value);
}
public void setEnableMediaCodec(boolean mEnableMediaCodec) {
this.mEnableMediaCodec = mEnableMediaCodec;
}
private void setListener(IMediaPlayer player) {
if (mListener != null) {
player.setOnBufferingUpdateListener(new IMediaPlayer.OnBufferingUpdateListener() {
@Override
public void onBufferingUpdate(IMediaPlayer iMediaPlayer, int i) {
mListener.onBufferingUpdate(iMediaPlayer, i);
}
});
player.setOnCompletionListener(new IMediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(IMediaPlayer iMediaPlayer) {
mListener.onCompletion(iMediaPlayer);
}
});
player.setOnErrorListener(new IMediaPlayer.OnErrorListener() {
@Override
public boolean onError(IMediaPlayer iMediaPlayer, int i, int i1) {
return mListener.onError(iMediaPlayer, i, i1);
}
});
player.setOnInfoListener(new IMediaPlayer.OnInfoListener() {
@Override
public boolean onInfo(IMediaPlayer iMediaPlayer, int i, int i1) {
return mListener.onInfo(iMediaPlayer, i, i1);
}
});
player.setOnPreparedListener(new IMediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(IMediaPlayer iMediaPlayer) {
mListener.onPrepared(iMediaPlayer);
}
});
player.setOnVideoSizeChangedListener(new IMediaPlayer.OnVideoSizeChangedListener() {
@Override
public void onVideoSizeChanged(IMediaPlayer iMediaPlayer, int i, int i1, int i2, int i3) {
mListener.onVideoSizeChanged(iMediaPlayer, i, i1, i2, i3);
}
});
player.setOnSeekCompleteListener(new IMediaPlayer.OnSeekCompleteListener() {
@Override
public void onSeekComplete(IMediaPlayer iMediaPlayer) {
mListener.onSeekComplete(iMediaPlayer);
}
});
}
}
public void setVideoListener(final VideoListener videoListener) {
mListener = videoListener;
}
public void setPath(String path) {
setPath(path, null);
}
public void setPath(String path, Map header) {
mPath = path;
mHeader = header;
}
public void load() throws IOException {
if (mMedialPlayer != null) {
mMedialPlayer.stop();
mMedialPlayer.release();
}
mMedialPlayer = createPlayer();
setListener(mMedialPlayer);
mMedialPlayer.setDisplay(mSurfaceView.getHolder());
mMedialPlayer.setDataSource(mContext, Uri.parse(mPath), mHeader);
mMedialPlayer.prepareAsync();
}
public void start() {
if (mMedialPlayer != null) {
mMedialPlayer.start();
mAudioFousHelper.startFocus();
}
}
public void release() {
if (mMedialPlayer != null) {
mMedialPlayer.reset();
mMedialPlayer.release();
mMedialPlayer = null;
mAudioFousHelper.stopFocus();
}
}
public void pause() {
if (mMedialPlayer != null) {
mMedialPlayer.pause();
}
}
public void stop() {
if (mMedialPlayer != null) {
mMedialPlayer.stop();
mAudioFousHelper.stopFocus();
}
}
public void resume() {
if (mMedialPlayer != null) {
mMedialPlayer.start();
}
}
public void reset() {
if (mMedialPlayer != null) {
mMedialPlayer.reset();
mAudioFousHelper.stopFocus();
}
}
public long getDuration() {
if (mMedialPlayer != null) {
return mMedialPlayer.getDuration();
} else {
return 0;
}
}
public void seekTo(long l) {
if (mMedialPlayer != null) {
mMedialPlayer.seekTo(l);
}
}
public boolean isPlaying() {
if (mMedialPlayer != null) {
return mMedialPlayer.isPlaying();
}
return false;
}
public long getCurrentDuration() {
if (mMedialPlayer != null) {
return mMedialPlayer.getCurrentPosition();
}
return 0;
}
public void setVolume(int volume) {
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume / 3, AudioManager.FLAG_PLAY_SOUND);
}
public int getVolume() {
int currentVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
return currentVolume * 3;
}
public int getMaxVolume() {
int currentVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
return currentVolume * 3;
}
@Override
public boolean performClick() {
return super.performClick();
}
}
github地址:https://github.com/zhoumoumou123year/IjkPlayerDemo
参考链接:
https://www.jianshu.com/p/cb7868a09756
https://www.jianshu.com/p/a57bbdd78798