Bootstrap

Ubuntu编译ijkplayer so库并播放本地raw/assets文件

博主的上一篇文章《ffmpeg的介绍,编译与使用

一,前期基础知识储备

1. ijkplayer 官方项目地址:https://github.com/Bilibili/ijkplayer

ijkplayer 是一个基于 ffmpeg 的轻量级 Android/iOS 视频播放器。实现了跨平台功能,API易于集成;编译配置可裁剪,方便控制安装包大小;支持硬件加速解码,更加省电。可以通过编译来实现更多格式的支持,可以说只要是 ffmpeg 支持的格式 ijkplayer 就支持。

2. Ubuntu 下载地址:https://cn.ubuntu.com/

官方中文名“友帮拓”,也有班图、乌班图、乌斑兔、乌帮图、笨兔等非官方译名。

Ubuntu是一个以桌面应用为主的Linux操作系统,是世界上最流行的Linux系统之一。

1)虚拟机下体验:

运行虚拟机,在虚拟机中获得更完整的体验。下载安装虚拟机。成功安装后运行虚拟机,选择菜单栏中的“管理”→“导入虚拟电脑”选项,将所下载体验镜像导入虚拟机,最后运行Ubuntu虚拟机,这样就可以获得比较真实的体验。在虚拟机中几乎可以完成所有操作,没有限制。

小白菜Windows10系统安装Linux(ubuntu)虚拟机超详细教程

本文,选择这一种方式在Windows10系统中安装Ubutntu。

2)制作Live USB
还有一-种获得真实体验的途径,即制作LiveUSB,它比虚拟机更进一步,直接跑在真实的硬件环境中,更快且更加节省资源。

Ubuntu18.04/20.04完整新手安装教程

二,上手编译

具体步骤如下:

1. 配置环境 (windows)

1)安装 VMware 虚拟机并安装 Ubuntu 系统;

2)下载NDK SDK,上传至Ubuntu;

3) 配置好NDK SDK环境变量;

4) 安装git、yasm 和 make;

2. 正式编译 - 参考 ijkplayer 中编译 Android的步骤 Build Android

3. 项目中使用编译好的so库

1. 配置环境

1)安装 VMware 虚拟机并安装 Ubuntu 系统

小白菜Windows10系统安装Linux(ubuntu)虚拟机超详细教程

2)下载NDK SDK,上传至Ubuntu

下载好 Linux 版本的的 Android SDK 和 NDK,这里选择的分别是 android-sdk_r24.4.1-linux.tgz 和 android-ndk-r10e-linux-x86_64.zip,下载后可以使用如下命令解压文件:

tar -xvf android-sdk_r24.4.1-linux.tar

unzip android-ndk-r10e-linux-x86_64.zip

切记不要将 NDK 目录放在虚拟机的共享目录下,为保证编译顺利进行应将 NDK 目录放在 Ubuntu 的系统目录,也就是 /home/用户名 下面的目录。

3) 配置好NDK SDK环境变量

在 Ubuntu 下的 /home/用户名/ ,按 Ctrl+h 查看 .bashrc 文件并配置 SDK 和 NDK 环境变量,参考如下:

NDK=/home/chinstyle/android/android-ndk-r10e
export NDK
ADB=/home/chinstyle/android/android-sdk-linux/platform-tools
export ADB
# ANDROID_NDK和ANDROID_SDK路径
ANDROID_NDK=/home/chinstyle/android/android-ndk-r10e
export ANDROID_NDK
ANDROID_SDK=/home/chinstyle/android/android-sdk-linux
export ANDROID_SDK 
# 加入到PATH路径
PATH=${PATH}:${NDK}:${ADB}:${ANDROID_NDK}:${ANDROID_SDK}

配置完成后保存并关闭 .bashrc,打开 Terminal 输入 ndk-build -v 查看 ndk 是否配置成功,运行日志如下则配置成功:

Ctrl + Alt + T - 打开终端。

若是提示 ndk -build 权限不够,则需要给权限,建议给整个ndk文件夹权限。

chmod -R 777 文件夹
参数-R是递归的意思
777表示开放所有权限

4) 安装git、yasm 和 make

sudo apt-get update
sudo apt install git
sudo apt install yasm
sudo apt install make

使用 git --version 和 make -v 查看 git 和 make 工具是否安装成功,成功则显示对应版本号,参考如下:

chinstyle@chinstyle-virtual-machine:~$ git --version
git version 2.25.1
chinstyle@chinstyle-virtual-machine:~$ make -v
GNU Make 4.2.1
为 x86_64-pc-linux-gnu 编译
Copyright (C) 1988-2016 Free Software Foundation, Inc.
许可证:GPLv3+:GNU 通用公共许可证第 3 版或更新版本<http://gnu.org/licenses/gpl.html>。
本软件是自由软件:您可以自由修改和重新发布它。
在法律允许的范围内没有其他保证。

2. 正式编译

参考 ijkplayer 中编译 Android的步骤 Build Android

//clone ijkplayer源码
git clone https://github.com/Bilibili/ijkplayer.git ijkplayer
cd ijkplayer
git checkout -B latest k0.8.8
//使用更轻量的module-lite.sh
cd ijkplayer/config
rm module.sh
ln -s module-lite module.sh
//下载ffmpeg源码 - 耗时较长
cd ijkplayer
./init-android.sh
//编译arm64 ffmpeg
./compile-ffmpeg.sh clean
./compile-ffmpeg.sh arm64 // 若是使用all 则编译所有架构的so
//编译ijkplayer,生成arm64 so文件
cd ijkplayer/android
./compile-ijk.sh arm64 // 若是使用all 则编译所有架构的so

提几点注意事项:

1)git checkout -B latest k0.8.8 - 要切换到此分支,要不然后续使用C++方法会缺失;

2)ln -s module-lite module.sh - ijkplayer 提供了3个版本的编译脚本配置

module-default.sh:默认,如果你喜欢更多类型可以用这个;
module-lite-hevc.sh:如果您更喜欢较小的二进制大小的编解码器/格式(包括hevc功能)
module-lite.sh:如果您更喜欢较小的二进制大小的编解码器/格式(默认情况下)

区别:lite.sh相当于在default.sh的基础上,关闭了所有的解码器等全体操作,然后按照需求,比如开启对应的解码器。具体可以打开编译脚本查看。你可以打开module.sh自行进行修改。

3)compile-ffmpeg.sh arm64 & compile-ijk.sh arm64 :这里只编译arm64位的so库,这样编译时间短点。若是需要编译所有架构的so,则把arm64换成all就行了。

编译时间较长,需要耐心等待。

4)编译结果 - 生成对应的so库

编译的ijkplayer 项目整体如下:

3. 使用编译好的so库

1)添加ijkplayer依赖

    //ijkplayer player
    implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8'
    //ijkplayer so文件
    implementation 'tv.danmaku.ijk.media:ijkplayer-arm64:0.8.8'

这是直接使用ijkplayer提供的依赖,可以直接使用。

我们这里选择使用自己刚刚编译好的so。把“ijkplayer-java” 和 “ijkplayer-arm64” 2个项目全部拷贝至我们的项目中,以项目依赖的方式直接使用。

setting.gradle
include ':app', 'ijkplayer-arm64','ijkplayer-java'


build.gradle
//    implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8'
//    implementation 'tv.danmaku.ijk.media:ijkplayer-arm64:0.8.8'

    api project(path: ':ijkplayer-arm64')
    api project(path: ':ijkplayer-java')

2)播放raw/assets下的音乐文件

    // 播放raw下的音乐
    public void onIjkPlayRaw(View view) {
        //实例化播放内核
        tv.danmaku.ijk.media.player.IjkMediaPlayer ijkPlayer = new tv.danmaku.ijk.media.player.IjkMediaPlayer();
        //获得播放源访问入口
        AssetFileDescriptor afd = getResources().openRawResourceFd(R.raw.ynew); // 注意这里的区别
        //构建IjkPlayer能识别的IMediaDataSource,下面的RawDataSourceProvider实现了IMediaDataSource接口
        RawDataSourceProvider sourceProvider = new RawDataSourceProvider(afd);
        //给IjkPlayer设置播放源
        ijkPlayer.setDataSource(sourceProvider);
        //设置准备就绪状态监听
        ijkPlayer .setOnPreparedListener((IMediaPlayer.OnPreparedListener) mp -> {
            // 开始播放
            ijkPlayer.start();
        });
        //准备播放
        ijkPlayer.prepareAsync();
    }


    // 播放assets下的音乐
    public void onIjkPlayAsset(View view) {
        tv.danmaku.ijk.media.player.IjkMediaPlayer ijkPlayer = new tv.danmaku.ijk.media.player.IjkMediaPlayer();
        AssetManager am = getAssets();
        try {
            AssetFileDescriptor afd = am.openFd("intput.aac");
            RawDataSourceProvider sourceProvider = new RawDataSourceProvider(afd);
            ijkPlayer.setDataSource(sourceProvider);
        } catch (IOException e) {
            e.printStackTrace();
        }
        ijkPlayer .setOnPreparedListener((IMediaPlayer.OnPreparedListener) mp -> {
            ijkPlayer.start();
        });
        ijkPlayer.prepareAsync();
    }
// ijkplayer播放本地文件的入口
public class RawDataSourceProvider implements IMediaDataSource {
    private AssetFileDescriptor mDescriptor;

    private byte[]  mMediaBytes;

    public RawDataSourceProvider(AssetFileDescriptor descriptor) {
        this.mDescriptor = descriptor;
    }

    @Override
    public int readAt(long position, byte[] buffer, int offset, int size) throws IOException {
        if(position + 1 >= mMediaBytes.length){
            return -1;
        }

        int length;
        if(position + size < mMediaBytes.length){
            length = size;
        }else{
            length = (int) (mMediaBytes.length - position);
            if(length > buffer.length)
                length = buffer.length ;

            length--;
        }
        System.arraycopy(mMediaBytes, (int) position, buffer, offset, length);

        return length;
    }

    @Override
    public long getSize() throws IOException {
        long length  = mDescriptor.getLength();
        if(mMediaBytes == null){
            InputStream inputStream = mDescriptor.createInputStream();
            mMediaBytes = readBytes(inputStream);
        }


        return length;
    }

    @Override
    public void close() throws IOException {
        if(mDescriptor != null)
            mDescriptor.close();

        mDescriptor = null;
        mMediaBytes = null;
    }

    private byte[] readBytes(InputStream inputStream) throws IOException {
        ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();

        int bufferSize = 1024;
        byte[] buffer = new byte[bufferSize];

        int len = 0;
        while ((len = inputStream.read(buffer)) != -1) {
            byteBuffer.write(buffer, 0, len);
        }

        return byteBuffer.toByteArray();
    }

    public static RawDataSourceProvider create(Context context, Uri uri){
        try {
            AssetFileDescriptor fileDescriptor = context.getContentResolver().openAssetFileDescriptor(uri, "r");
            return new RawDataSourceProvider(fileDescriptor);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}

至此,我们编译ijkplayer就完成了。

参考文章

如何正确编译ijkplayer

ijkplayer编译so库真没那么难

一步步带你编译哔哩哔哩ijkPlayer

播放Raw/Assets音视频方法总结

;