项目地址
https://github.com/979451341/Myijkplayer

前段时候我觉得FFmpeg做个视频播放器好难,虽然播放上没问题,但暂停还有通过拖动进度条来设置播放进度,这些都即便做得到,可以那个延缓。。。。。

现在学习一下目前移动端最知名的视频播放器的框架ijkplayer,这个框架他是基于FFmpeg、SDL、还有安卓原生API

MediaCodec之类的。他是没有播放界面的,这个需要我们去做,所以这个里我就做个基于ijkplayer的视频播放器,随便浅显的说一下ijkplayer的源码,关于ijkplayer的源码以后会专门出一篇博客说一下。

1.首先了解一下ijkplayer咋用

我这里引入ijkplayer是通过添加依赖

    implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8'
implementation 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.8' implementation 'tv.danmaku.ijk.media:ijkplayer-armv5:0.8.8'
implementation 'tv.danmaku.ijk.media:ijkplayer-arm64:0.8.8'
implementation 'tv.danmaku.ijk.media:ijkplayer-x86:0.8.8'
implementation 'tv.danmaku.ijk.media:ijkplayer-x86_64:0.8.8' implementation 'tv.danmaku.ijk.media:ijkplayer-exo:0.8.8'

然后说说ijkplayer是如何播放视频的

ijkplayer每一次播放视频都是通过创建Mediaplayer,然后赋值到一个接口类上,这里他创建的时候能够挑选解码的类型,是因为基于安卓原生API

MediaCodec的话是硬解,速度快、兼容差,如果是基于FFmpeg则是软解,速度慢、兼容好,不过这个兼容问题,因为我们在引入依赖的时候把各个处理器相应的依赖,所以可以使用硬解,兼容问题基本都是手机处理器不同产生的。

        IjkMediaPlayer ijkMediaPlayer = new IjkMediaPlayer();
ijkMediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_DEBUG); // //开启硬解码
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);
IMediaPlayer mMediaPlayer = null;
mMediaPlayer = ijkMediaPlayer;

关于IjkMediaPlayer的源码我只贴出一个函数的,从下面几个loadLibrary看出来,他还是基于FFmpeg、SDL底层实现的。

    public static void loadLibrariesOnce(IjkLibLoader libLoader) {
Class var1 = IjkMediaPlayer.class;
synchronized(IjkMediaPlayer.class) {
if(!mIsLibLoaded) {
if(libLoader == null) {
libLoader = sLocalLibLoader;
} libLoader.loadLibrary("ijkffmpeg");
libLoader.loadLibrary("ijksdl");
libLoader.loadLibrary("ijkplayer");
mIsLibLoaded = true;
} }
}

好了,回到那个接口类的IMediaPlayer,源码不多贴出来看一下,通过这些接口函数我们都可以知道这个ijkplayer如何使用我们都有了一个底,什么setDataSource、setDisplay,设置播放源、设置播放的屏幕信息。还有start、stop、pause,视频播放的开始、停止、暂停,还有一大堆的接口,这些都是为了监听播放器的状态

public interface IMediaPlayer {
。。。。。。 void setDisplay(SurfaceHolder var1); void setDataSource(Context var1, Uri var2) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException; @TargetApi(14)
void setDataSource(Context var1, Uri var2, Map<String, String> var3) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException; void setDataSource(FileDescriptor var1) throws IOException, IllegalArgumentException, IllegalStateException; void setDataSource(String var1) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException; String getDataSource(); void prepareAsync() throws IllegalStateException; void start() throws IllegalStateException; void stop() throws IllegalStateException; void pause() throws IllegalStateException; void setScreenOnWhilePlaying(boolean var1); int getVideoWidth(); int getVideoHeight(); boolean isPlaying(); void seekTo(long var1) throws IllegalStateException; long getCurrentPosition(); long getDuration(); void release(); void reset(); void setVolume(float var1, float var2); int getAudioSessionId(); MediaInfo getMediaInfo(); /** @deprecated */
@Deprecated
void setLogEnabled(boolean var1); /** @deprecated */
@Deprecated
boolean isPlayable(); void setOnPreparedListener(IMediaPlayer.OnPreparedListener var1); void setOnCompletionListener(IMediaPlayer.OnCompletionListener var1); void setOnBufferingUpdateListener(IMediaPlayer.OnBufferingUpdateListener var1); void setOnSeekCompleteListener(IMediaPlayer.OnSeekCompleteListener var1); void setOnVideoSizeChangedListener(IMediaPlayer.OnVideoSizeChangedListener var1); void setOnErrorListener(IMediaPlayer.OnErrorListener var1); void setOnInfoListener(IMediaPlayer.OnInfoListener var1); void setOnTimedTextListener(IMediaPlayer.OnTimedTextListener var1); void setAudioStreamType(int var1); /** @deprecated */
@Deprecated
void setKeepInBackground(boolean var1); int getVideoSarNum(); int getVideoSarDen(); /** @deprecated */
@Deprecated
void setWakeMode(Context var1, int var2); void setLooping(boolean var1); boolean isLooping(); ITrackInfo[] getTrackInfo(); void setSurface(Surface var1); void setDataSource(IMediaDataSource var1); public interface OnTimedTextListener {
void onTimedText(IMediaPlayer var1, IjkTimedText var2);
} public interface OnInfoListener {
boolean onInfo(IMediaPlayer var1, int var2, int var3);
} public interface OnErrorListener {
boolean onError(IMediaPlayer var1, int var2, int var3);
} public interface OnVideoSizeChangedListener {
void onVideoSizeChanged(IMediaPlayer var1, int var2, int var3, int var4, int var5);
} public interface OnSeekCompleteListener {
void onSeekComplete(IMediaPlayer var1);
} public interface OnBufferingUpdateListener {
void onBufferingUpdate(IMediaPlayer var1, int var2);
} public interface OnCompletionListener {
void onCompletion(IMediaPlayer var1);
} public interface OnPreparedListener {
void onPrepared(IMediaPlayer var1);
}
}

2.写界面

全屏播放,潜入式

    //正真的全屏,隐藏了状态栏、AtionBar、导航栏
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus && Build.VERSION.SDK_INT >= 19) {
View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
}

然后上下两个栏目,负责一些播放器的控制,顶部负责设置返回、设置按钮,底部需要设置播放/暂停按钮、播放进度条、停止按钮

    <com.example.zth.two.VideoPlayerIJK
android:id="@+id/ijk_player"
android:layout_width="match_parent"
android:layout_height="match_parent"/> <include
android:id="@+id/include_play_top"
layout="@layout/include_play_top"
android:layout_alignParentTop="true"
android:layout_width="match_parent"
android:layout_height="50dp" /> <include
android:id="@+id/include_play_bottom"
layout="@layout/include_play_bottom"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="50dp" />

关于上下两个栏目在用户观看视频时需要隐藏,在用户点击屏幕则显示两个栏目,供用户使用
这个则是需要通过计时器来完成记录目前距离上一次用户点击屏幕的时间,如果视频超过3秒,则隐藏栏目,如果点击屏幕则恢复,关于隐藏和恢复使用Animation来完成。

        timer = new Timer();
timerTask = new TimerTask() {
@Override
public void run() {
long t = System.currentTimeMillis();
if (t - time > 3000 && menu_visible) {
time = t;
handler.post(new Runnable() {
@Override
public void run() {
Animation animation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.move_bottom);
rl_bottom.startAnimation(animation);
Animation animation_top = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.move_top);
rl_top.startAnimation(animation_top);
menu_visible = false;
}
});
} }
};

还需要加载框,这个在视频加载完的接口回调里隐藏他

        <ProgressBar
android:id="@+id/pb_loading"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_centerInParent="true"
android:layout_marginTop="60dp"
android:indeterminate="false"
android:indeterminateDrawable="@drawable/video_loading"
android:padding="5dp" />

3.播放实现

这里我是使用了一个另一个博主封装的ijkplayer的类

说一下这个类的运行过程

一开始创建MediaPlayer做一些配置,赋值给一个接口类,并且暴露了接口

        IjkMediaPlayer ijkMediaPlayer = new IjkMediaPlayer();
ijkMediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_DEBUG); // //开启硬解码
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1); mMediaPlayer = ijkMediaPlayer; if (listener != null) {
mMediaPlayer.setOnPreparedListener(listener);
mMediaPlayer.setOnInfoListener(listener);
mMediaPlayer.setOnSeekCompleteListener(listener);
mMediaPlayer.setOnBufferingUpdateListener(listener);
mMediaPlayer.setOnErrorListener(listener);
}

然后设置播放源,这个播放源能够是本地视频路径、网络视频url、还可以是网络RTMP推流url,还要讲SurfaceView的配置信息给他

        try {
mMediaPlayer.setDataSource(mPath);
} catch (IOException e) {
e.printStackTrace();
}
//给mediaPlayer设置视图
mMediaPlayer.setDisplay(surfaceView.getHolder()); mMediaPlayer.prepareAsync();

想开始播放就调用IMediaPlayer的那些控制函数

    public void start() {
if (mMediaPlayer != null) {
mMediaPlayer.start();
}
}

还有关于activity的生命周期控制,其中native_profileEnd就相当于很智能的暂停,当屏幕回的时候就继续播放视频。

    @Override
protected void onStop() {
IjkMediaPlayer.native_profileEnd();
handler.removeCallbacksAndMessages(null);
super.onStop(); }

结束Activity的时候就停止播放视频并释放资源

    @Override
protected void onDestroy() {
if (ijkPlayer != null) {
ijkPlayer.stop();
ijkPlayer.release();
ijkPlayer = null;
} super.onDestroy();
}

最后还有播放进度,我这里是通过handler自己调用自己循环更新播放时间显示和进度条

        handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REFRESH:
if (ijkPlayer.isPlaying()) {
refresh();
handler.sendEmptyMessageDelayed(MSG_REFRESH, 1000);
} break;
} }
}; private void refresh() {
long current = ijkPlayer.getCurrentPosition() / 1000;
long duration = ijkPlayer.getDuration() / 1000;
Log.v("zzw", current + " " + duration);
long current_second = current % 60;
long current_minute = current / 60;
long total_second = duration % 60;
long total_minute = duration / 60;
String time = current_minute + ":" + current_second + "/" + total_minute + ":" + total_second;
tvTime.setText(time);
if(duration != 0){
seekBar.setProgress((int) (current * 100 / duration));
} }

在用户拖动进度条的时候取消handler发送消息,拖动结束再接续更新播放进度

        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
//进度改变
} @Override
public void onStartTrackingTouch(SeekBar seekBar) {
//开始拖动 handler.removeCallbacksAndMessages(null); } @Override
public void onStopTrackingTouch(SeekBar seekBar) {
//停止拖动
ijkPlayer.seekTo(ijkPlayer.getDuration() * seekBar.getProgress() / 100);
handler.sendEmptyMessageDelayed(MSG_REFRESH, 100);
}
});

关于播放网络视频和网络RTMP推流,播放本身没问题就是不能获取视频时长和视频当前时间,不过暂停和停止还有效。

还有就是如果想要重新播放需要重新setVideoPath然后start

看看效果
本地视频播放

播放RTMP推流

播放网络视频

ijkplayer的使用看起来是不是很简单,但是还没有实现小窗口和全屏之间的切换。。。。。。这两天我再看看ijkplayer,再和各位说说

博客首发地址
http://blog.csdn.net/z979451341

Android 音视频深入 十九 使用ijkplayer做个视频播放器(附源码下载)的更多相关文章

  1. Android 音视频深入 一 AudioRecord录音生成pcm转换为wav(附源码下载)

    本篇项目地址,名字是AudioRecord录音(能暂停,将pch转换为wav),求starhttps://github.com/979451341/Audio-and-video-learning-m ...

  2. 仿酷狗音乐播放器开发日志十九——CTreeNodeUI的bug修复二(附源码)

    转载请说明原出处,谢谢 今天本来打算把仿酷狗播放列表的子控件拖动插入功能做一下,但是仔细使用播放列表控件时发现了几个逻辑错误,由于我的播放 列表控件是基于CTreeViewUI和CTreeNodeUI ...

  3. Android中Canvas绘图基础详解(附源码下载) (转)

    Android中Canvas绘图基础详解(附源码下载) 原文链接  http://blog.csdn.net/iispring/article/details/49770651   AndroidCa ...

  4. Android网络:HTTP之利用HttpURLConnection访问网页、获取网络图片实例 (附源码)

    http://blog.csdn.net/yanzi1225627/article/details/22222735 如前文所示的TCP局域网传送东西,除了对传输层的TCP/UDP支持良好外,Andr ...

  5. Android 音视频深入 二十 FFmpeg视频压缩(附源码下载)

    项目源码https://github.com/979451341/FFmpegCompress 这个视频压缩是通过类似在mac终端上输入FFmpeg命令来完成,意思是我们需要在Android上达到能够 ...

  6. Android 音视频深入 十五 FFmpeg 推流mp4文件(附源码下载)

    源码地址https://github.com/979451341/Rtmp 1.配置RTMP服务器 这个我不多说贴两个博客分别是在mac和windows环境上的,大家跟着弄 MAC搭建RTMP服务器h ...

  7. Android 音视频深入 十八 FFmpeg播放视频,有声音(附源码下载)

    项目地址https://github.com/979451341/AudioVideoStudyCodeTwo/tree/master/FFmpegv%E6%92%AD%E6%94%BE%E8%A7% ...

  8. Android 音视频深入 十六 FFmpeg 推流手机摄像头,实现直播 (附源码下载)

    源码地址https://github.com/979451341/RtmpCamera/tree/master 配置RMTP服务器,虽然之前说了,这里就直接粘贴过来吧 1.配置RTMP服务器 这个我不 ...

  9. Android 音视频深入 十一 FFmpeg和AudioTrack播放声音(附源码下载)

    项目地址,求starhttps://github.com/979451341/AudioVideoStudyCodeTwo/tree/master/FFmpeg%E6%92%AD%E6%94%BE%E ...

随机推荐

  1. vue 去中心化的路由拆分方案:require.context

    代码地址:https://github.com/lisiyizu/vue-router-dynamic

  2. PHP内核深入研究 - 数组及其遍历顺序

    事实上,广义上来讲,PHP就是C语言应用在Web上的一个模板,PHP中smarty模板用得比较多,就好比JSP是Java Servlet的模板一样(喔,对了,JSP中有个JSTL标签),复杂的模板语法 ...

  3. redis缓存数据库

    redis 介绍 redis是业界主流的key-value nosql 数据库之一.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set( ...

  4. 基于OpenCV做“三维重建”(4)--相机姿态还原和实现三维重建

    v当我们构建成功了viz,就可以使用3维效果给我们提供的便利,进一步进行一些3维的操作. 在这个动画中,注意图片后面的那个黑线,对应的是相机的位置. /*----------------------- ...

  5. 设计的一些kubernetes面试题目

    这几个月参与了几场面试,设计了多道面试题,觉得可以综合考察应聘人对kubernetes的掌握情况.在这里分享下,供应聘人自查以及其他面试官参考. 这些面试题的设计初衷并不是考察kubernetes的使 ...

  6. Codeforces Gym 101623A - 动态规划

    题目传送门 传送门 题目大意 给定一个长度为$n$的序列,要求划分成最少的段数,然后将这些段排序使得新序列单调不减. 考虑将相邻的相等的数缩成一个数. 假设没有分成了$n$段,考虑最少能够减少多少划分 ...

  7. mysql 数据备份和还原

    使用mysqldump命令备份 使用root用户备份test数据库下的person表 mysqldump -u root -p test person > D:\backup.sql 备份多个数 ...

  8. topcoder srm 640 div1

    problem1 link 首先使用两个端点颜色不同的边进行连通.答案是$n-1-m$.其中$m$是联通分量的个数. problem2 link 首先构造一个最小割的模型.左边的$n_{1}$个点与源 ...

  9. Linux系统 磁盘IO过高排查总结

    最近做的一个电商网站因为磁盘 I/O 过高导致访问速度奇慢,问题存在两个月有余未得到解决办法.此次排查原因的经验可以作下次问题的参考. 1.会看懂 top 系统命令出来的各项参数.此次是无意中发现 u ...

  10. C# 利用反射动态给模型Model 赋值

    https://www.cnblogs.com/waitingfor/articles/2220669.html object ff = Activator.CreateInstance(tt, nu ...