根据 ijkplayer 实现的一个简单的视频播放器

老规矩 先上完成效果图

视频播放.gif

编译 环境 及工具

AS4.0 jdk1.8 测试的android系统在7.1安卓板

1.app下的build.gradle 下的 Ijkplayer相关依赖


     //使用之前记得把ndk 的版本配置了

    android {
       // 指定jdk 1.8 版本
      compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
       }
    }


   dependencies {
    //https://github.com/bilibili/ijkplayer/blob/master/README.md 播放器
    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-exo:0.8.8';

    // Android常用的工具库  这个是用到的一个工具类库
    https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/README-CN.md
    implementation 'com.blankj:utilcodex:1.31.1'
}

2. 根据 Ijkplayer 自定义播放器View

  实现流程及思路

根据Ijkplayer  提供的 api   需要一个surfaceView 进行视频预览显示

1 自定义一个View 继承FrameLayout 

2 创建成功后 进行 IjkMediaPlayer 的实例化及其配置参数

3 设置要播放的视频地址   这里要进行设置 是否是第一次播放 需要先创建surfaceView  及其配置
如果不是第一次则执行进行 mMediaPlayer 的视图设置 进行surfaceView 的绑定

5 设置其加载及其播放和播放完成 异常等回调

4 动态实例化一个surfaceView  获得 Holder()句柄 进行设置创建回调

6 封装其IjkMediaPlayer  的启动 停止  暂停 进度 重置   释放  等api  具体看其自定义播放器的实现

7 添加了进度条和播放时间及总时间的布局及事件 

8 动态配置了是否展示进度条及播放时间的UI和动态设置播放器的高度随着视频的高度进行变化

// 后俩步是自行实现的使用方式

9 设置视频的加载回调 根据准备完毕的回调进行视频播放 IjkMediaPlayer .start();

10  根据视频的完成回调 进行视频播放 IjkMediaPlayer .start();或者下一个播放的逻辑

11 使用完记得进行停止和释放

2.1 抽象接口回调类

public abstract class VideoPlayerListener implements IMediaPlayer.OnBufferingUpdateListener,
        IMediaPlayer.OnCompletionListener, IMediaPlayer.OnPreparedListener,
        IMediaPlayer.OnInfoListener, IMediaPlayer.OnErrorListener, IMediaPlayer.OnSeekCompleteListener {
}

2.2 抽象播放接口类如下


import tv.danmaku.ijk.media.player.IMediaPlayer;

public abstract class VideoPlayerListener implements IMediaPlayer.OnBufferingUpdateListener,
        IMediaPlayer.OnCompletionListener, IMediaPlayer.OnPreparedListener,
        IMediaPlayer.OnInfoListener, IMediaPlayer.OnErrorListener, IMediaPlayer.OnSeekCompleteListener {
}

2.3 自定义播放器实现类如下
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Color;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;

import androidx.annotation.AttrRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.blankj.utilcode.util.ScreenUtils;
import com.blankj.utilcode.util.SizeUtils;

import java.io.IOException;

import tv.danmaku.ijk.media.player.IMediaPlayer;
import tv.danmaku.ijk.media.player.IjkMediaPlayer;

public class VideoPlayerIJK extends FrameLayout {
    private final String TAG = "VideoPlayerIJK";

    private static final int MSG_REFRESH = 100;//查询当前播放进度
    /**
     * 由ijkplayer提供,用于播放视频,需要给他传入一个surfaceView
     */
    private IMediaPlayer mMediaPlayer = null;

    /**
     * 视频文件地址
     */
    private String mPath = "";

    private SurfaceView surfaceView;

    private VideoPlayerListener listener;
    private Context mContext;
    private SeekBar seekBar;
    private TextView currentTime, totalTime;
    private int mVideoWidth, mVideoHeight;
    private int screenWidth;
    private boolean mVideoControlBar;//是否启动进度条和播放时间的显示
    private boolean isFollowTheVideoHeight = false;//是否跟随视频高度显示

    public void setFollowTheVideoHeight(boolean followTheVideoHeight) {
        isFollowTheVideoHeight = followTheVideoHeight;
    }

    public VideoPlayerIJK(@NonNull Context context) {
        super(context);
        initVideoView(context);
    }

    public VideoPlayerIJK(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initVideoView(context);
    }

    public VideoPlayerIJK(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initVideoView(context);
    }

    private void initVideoView(Context context) {
        mContext = context;
        screenWidth = ScreenUtils.getScreenWidth();
    }

    /**
     * 设置视频地址。
     * 根据是否第一次播放视频,做不同的操作。
     *
     * @param path 视频地址
     */
    public void setVideoPath(String path) {
        if (TextUtils.equals("", mPath)) {
            //如果是第一次播放视频,那就创建一个新的surfaceView
            mPath = path;
            createSurfaceView();
        } else {
            //否则就直接load
            defaultBar();
            mPath = path;
            load();
        }
    }

    /**
     * 新建一个surfaceview
     */
    private void createSurfaceView() {
        //生成一个新的surface view
        surfaceView = new SurfaceView(mContext);
        addView(surfaceView);
        surfaceView.getHolder().addCallback(new MySurfaceCallback());
        if (mVideoControlBar) {
            addVideoControlBar();
            defaultBar();
        }
    }

    /**
     * surfaceView的监听器
     */
    private class MySurfaceCallback implements SurfaceHolder.Callback {
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            //surfaceview创建成功后,加载视频
            load();
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {


        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {

        }
    }

    /**
     * 加载视频
     */
    private void load() {
        Log.e(TAG, "loads");
        //每次都要重新创建IMediaPlayer
        createPlayer();
        try {
            mMediaPlayer.setDataSource(mPath);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //给mediaPlayer设置视图
        mMediaPlayer.setDisplay(surfaceView.getHolder());

        mMediaPlayer.prepareAsync();
    }


    /**
     * 创建一个新的player
     */
    private void createPlayer() {
        if (mMediaPlayer != null) {
            mMediaPlayer.stop();
            mMediaPlayer.setDisplay(null);
            mMediaPlayer.release();
        }
        IjkMediaPlayer ijkMediaPlayer = new IjkMediaPlayer();
        ijkMediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_DEBUG);
        ijkMediaPlayer.setLogEnabled(false);
        //开启硬解码
        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);
            mMediaPlayer.setOnCompletionListener(listener);
        }
    }

    public void setListener(VideoPlayerListener listener) {
        this.listener = listener;
    }


    //-------======--------- 下面封装了一下控制视频的方法

    /**
     * 启动播放
     */
    public void start() {
        if (mMediaPlayer != null) {
            mMediaPlayer.start();

            setVideoSize();
            if (mVideoControlBar) {
                handler.sendEmptyMessageDelayed(MSG_REFRESH, 1000);
            }
        }
    }

    /**
     * 释放
     */
    public void release() {
        if (mMediaPlayer != null) {
            stop();
            mMediaPlayer.release();
            mMediaPlayer = null;
        }
    }

    /**
     * 暂停
     */
    public void pause() {
        if (mMediaPlayer != null) {
            mMediaPlayer.pause();
        }
    }

    /**
     * 停止
     */
    public void stop() {
        if (mMediaPlayer != null) {
            mMediaPlayer.stop();
            handler.removeCallbacksAndMessages(null);
        }
    }

    /**
     * 重置
     */
    public void reset() {
        if (mMediaPlayer != null) {
            mMediaPlayer.reset();
        }
    }

    /**
     * 获取视频总时间
     *
     * @return
     */
    public long getDuration() {
        if (mMediaPlayer != null) {
            return mMediaPlayer.getDuration();
        } else {
            return 0;
        }
    }

    /**
     * 当前播放时间
     *
     * @return
     */
    public long getCurrentPosition() {
        if (mMediaPlayer != null) {
            return mMediaPlayer.getCurrentPosition();
        } else {
            return 0;
        }
    }

    /**
     * 设置播放进度
     *
     * @param l
     */
    public void seekTo(long l) {
        if (mMediaPlayer != null) {
            mMediaPlayer.seekTo(l);
        }
    }

    /**
     * 是否循环播放
     *
     * @param isLoop
     */
    public void loop(boolean isLoop) {
        if (mMediaPlayer != null) {
            mMediaPlayer.setLooping(isLoop);
        }
    }


    public void setVideoControlBar(boolean isVideoControlBar) {
        mVideoControlBar = isVideoControlBar;
    }

    //给播放器动态设置一个进度条及当前播放时间和总时间
    public void addVideoControlBar() {
        LayoutParams videoBarParams = new LayoutParams(LayoutParams.MATCH_PARENT
                , SizeUtils.dp2px(50), Gravity.BOTTOM);
        LinearLayout videoBar = new LinearLayout(mContext);
        videoBar.setHorizontalGravity(LinearLayout.HORIZONTAL);
        videoBar.setLayoutParams(videoBarParams);
        videoBar.setGravity(Gravity.CENTER_VERTICAL);

        currentTime = new TextView(mContext);
        currentTime.setTextColor(Color.WHITE);
        currentTime.setTextSize(SizeUtils.sp2px(24));

        LinearLayout.LayoutParams currentTimeParams = new LinearLayout.
                LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        currentTimeParams.setMarginStart(SizeUtils.dp2px(30));
        currentTimeParams.setMarginEnd(SizeUtils.dp2px(30));
        currentTime.setLayoutParams(currentTimeParams);
        videoBar.addView(currentTime);

        LinearLayout.LayoutParams seekBarParams = new LinearLayout.
                LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 1.0f);
        seekBar = new SeekBar(mContext);
        seekBar.setMinimumWidth(LayoutParams.MATCH_PARENT);
        seekBar.setMax(100);
        seekBar.setLayoutParams(seekBarParams);
        videoBar.addView(seekBar);
        LinearLayout.LayoutParams totalmTimeParams = new LinearLayout.
                LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        totalmTimeParams.setMarginStart(SizeUtils.dp2px(30));
        totalmTimeParams.setMarginEnd(SizeUtils.dp2px(30));
        totalTime = new TextView(mContext);
        totalTime.setTextColor(Color.WHITE);
        totalTime.setTextSize(SizeUtils.sp2px(24));

        totalTime.setLayoutParams(totalmTimeParams);
        videoBar.addView(totalTime);
        addView(videoBar);
    }

    //刷新进度条和时间的主线程
    Handler handler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_REFRESH:
                    if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
                        refresh();
                        handler.sendEmptyMessageDelayed(MSG_REFRESH, 100);
                    }
                    break;
            }
        }
    };

    //计算当前时间和总时间
    private void refresh() {
        long current = getCurrentPosition() / 1000;
        long duration = getDuration() / 1000;
        long current_second = current % 60;
        long current_minute = current / 60;
        long total_second = duration % 60;
        long total_minute = duration / 60;
        if (currentTime != null) {
            currentTime.setText(current_minute + ":" + current_second);
        }
        if (totalTime != null) {
            totalTime.setText(total_minute + ":" + total_second);
        }
        if (duration != 0 && current != 0) {
            seekBar.setProgress((int) (current * 100 / duration));
        }
    }

    /**
     * 时间及进度的默认值
     */
    @SuppressLint("SetTextI18n")
    public void defaultBar() {
        if (mVideoControlBar && seekBar != null) {
            seekBar.setProgress(0);
            currentTime.setText("00:00");
            totalTime.setText("00:00");
        }
    }


    /**
     * 设置surfaceView 跟随视频的高度
     */
    public void setVideoSize() {
        if (surfaceView != null && isFollowTheVideoHeight) {
            mVideoWidth = mMediaPlayer.getVideoWidth();
            mVideoHeight = mMediaPlayer.getVideoHeight();
            Log.e(TAG, "h:" + mVideoHeight);
            if (mVideoWidth != 0 && mVideoHeight != 0) {
                //根据video的大小进行自适应设置
                double currentLayoutHeight;
                if (screenWidth > mVideoWidth) {
                    double s = (double) screenWidth / (double) mVideoWidth;
                    currentLayoutHeight = s * mVideoHeight;
                } else {
                    double s = (double) mVideoWidth / (double) screenWidth;
                    currentLayoutHeight = s * mVideoHeight;
                }
                LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT
                        , (int) currentLayoutHeight, Gravity.CENTER);
                surfaceView.setLayoutParams(layoutParams);
                Log.e(TAG, "h1:" + currentLayoutHeight);
            }
        }
    }
}

3. 具体的使用

3.1 activity_main 布局的实现
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.tjf.myijkplayer.views.VideoPlayerIJK
        android:id="@+id/mijkvideo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>


3.2 MainActivity 主页的流程实现
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import com.blankj.utilcode.util.LogUtils;
import com.tjf.myijkplayer.views.VideoPlayerIJK;
import com.tjf.myijkplayer.views.VideoPlayerListener;

import java.util.ArrayList;
import java.util.List;

import tv.danmaku.ijk.media.player.IMediaPlayer;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    List<String> videoList = new ArrayList<>();
    int videoIndex;
    VideoPlayerIJK mIjkvideo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mIjkvideo = findViewById(R.id.mijkvideo);
        setVideoUri();
        startPlayVideo();
    }


    public void setVideoUri() {
        videoList.clear();
        String videoPath = "http://v-cdn.zjol.com.cn/280443.mp4";
        String videoPath1 = "http://rbv01.ku6.com/omtSn0z_PTREtneb3GRtGg.mp4";
        videoList.add(videoPath);
        videoList.add(videoPath1);
    }

    public void startPlayVideo() {
        videoIndex = 0;
        mIjkvideo.setFollowTheVideoHeight(true);
        mIjkvideo.setVideoControlBar(true);
        mIjkvideo.setListener(new VideoPlayerListener() {

            @Override
            public void onBufferingUpdate(IMediaPlayer iMediaPlayer, int i) {

            }

            @Override
            public void onCompletion(IMediaPlayer iMediaPlayer) {
                LogUtils.iTag(TAG, "onCompletion");
                videoIndex++;
                if (videoIndex >= videoList.size()) {
                    videoIndex = 0;
                }
                mIjkvideo.setVideoPath(videoList.get(videoIndex));
            }

            @Override
            public boolean onError(IMediaPlayer iMediaPlayer, int i, int i1) {

                return false;
            }

            @Override
            public boolean onInfo(IMediaPlayer iMediaPlayer, int i, int i1) {
                return false;
            }

            @Override
            public void onPrepared(IMediaPlayer iMediaPlayer) {
                LogUtils.iTag("播放器-", iMediaPlayer.getVideoHeight());
                mIjkvideo.start();
            }

            @Override
            public void onSeekComplete(IMediaPlayer iMediaPlayer) {
                LogUtils.iTag(TAG, "onSeekComplete");
            }
        });
        mIjkvideo.setVideoPath(videoList.get(videoIndex));
    }

    @Override
    protected void onPause() {
        super.onPause();
        mIjkvideo.release();
    }
}

4 完结

demo 地址 https://gitee.com/code_tjf/myijkplayer.git

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容