Android Mac下编译bilibili IjkPlayer以实现支持播放rtsp协议流资源

1.简介

最近项目需要rtsp协议流视频播放,也参考使用了主流的视频播放框架,诸如 饺子GSY等,这类播放器实际上是对播放UI的封装,底层播放引擎都是基于或者扩展自ijkplayer的。经过学习与使用,使用这类视频UI框架能够美化我们的视频播放UI界面,更好的进行视频播放的控制,但对于饺子而言其基础引擎支持的编码格式有限,对于我们复杂的工程需求往往需要支持直播协议的rtsp,rtmp协议流,故而对于这类需求的开发者需要使用ijkplayer引擎,通过编译源码的方式生成so库,来支持达到特定协议的目的。后面也将详细的介绍mac下编译ijkplayer源码生成so库的详细步骤,以及特定的工程问题技巧,希望对各位有一定的帮助。

2.编译ijkplayer源码

2.1下载ijkPlayer源码

   下载地址:https://github.com/Bilibili/ijkplayer,直接下载下来解压即可。

 2.2 环境搭建

像基本的java JDK Android Studio我这里就不说明了,主要说明下用于编译源码的ndk的配置。

查看 /ijkplayer-master/android/contrib/tools/do-detect-env.sh  如下有一段会检查NDK版本,可以发现检索的是11 12 13 14的版本,然而AS默认提供的NDK下载支持一般都比较新,造成检索到的预期要求编译的不匹配,而报“You need the NDKr10e or later”。

        case "$IJK_NDK_REL" in
            11*|12*|13*|14*)
                if test -d ${ANDROID_NDK}/toolchains/arm-linux-androideabi-4.9
                then
                    echo "NDKr$IJK_NDK_REL detected"
                else
                    echo "You need the NDKr10e or later"
                    exit 1
                fi
            ;;
            *)
                echo "You need the NDKr10e or later"
                exit 1
            ;;
        esac

那么查看下AS的可以下载的NDK版本已经到了18了,明显不是我们想要的,那么需要我们自己去下载ndk,并进行环境变量的配置 

ndk_14环境变量的下载

ndk下载地址: https://developer.android.google.cn/ndk/downloads/ 

请在历史ndk版本中选择版本14对应的mac版本进行下载。(文末有网盘下载

ndk_14 环境变量配置

1.控制台命令行 开发配置文件:
cd ~
open -e .bash_profile 

2.
# 变量名为ANDROID_NDK 路径填你解压到的路径即可
export ANDROID_NDK=/Users/apple/Library/Android/android-ndk-r14b
# 注册变量
export PATH=${PATH}:${ANDROID_NDK}

3.保存配置文件:
source .bash_profile

4. 检查ndk配置 
ndk-build

效果如下即配置OK,重新启动一个控制台。

2.3 编译ijkplayer源码步骤

a. 打开 /ijkplayer-master/config/module-lite.sh

添加如下配置:用以支持rtsp流协议以及mpeg4硬解码

# 支持rtsp流
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtp"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=tcp"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=rtsp"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=sdp"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=rtp"
# 支持mpeg4解码
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=mpeg4"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=mpeg4"

b.  创建ndk编译软链接

rm module.sh
ln -s module-lite.sh module.sh

c. 返回源码根目录执行清理

cd android/contrib
./compile-ffmpeg.sh clean

d. 返回源码根目录,下载ffmpeg

cd ~/ijkplayer-master
./init-android.sh

f. 执行编译

cd android/contrib
./compile-ffmpeg.sh clean
./compile-ffmpeg.sh all
cd ..
./compile-ijk.sh all

经过以上步骤,就会在/ijkplayer-master/android/ijkplayer  中的module中生成对应的so库,我们通过Android Studio打开 ijkplayer 会看到如下.so库:

OK了,这样我们就能播放对应的资源流视频了。(文末有编译好的线程的so库)

3. 饺子视频播放器中切换ijkplayer引擎

     引入饺子播放器我这里就不涉及了,主要讲引擎切换,饺子默认的引擎支持的协议有限(硬解码格式),拓展的需要自己去使用ijkplayer引擎通过添加。

  a. Gradle引入:

    implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8'
    implementation 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.4'
    implementation 'com.google.android.exoplayer:exoplayer:2.8.4'

 b. 新建 JZMediaIjkplayer.java类,这是饺子demo里的,我直接给出来 

public class JZMediaIjkplayer extends JZMediaInterface implements IMediaPlayer.OnPreparedListener, IMediaPlayer.OnVideoSizeChangedListener, IMediaPlayer.OnCompletionListener, IMediaPlayer.OnErrorListener, IMediaPlayer.OnInfoListener, IMediaPlayer.OnBufferingUpdateListener, IMediaPlayer.OnSeekCompleteListener, IMediaPlayer.OnTimedTextListener {
    IjkMediaPlayer ijkMediaPlayer;

    @Override
    public void start() {
        ijkMediaPlayer.start();
    }

    @Override
    public void prepare() {
        ijkMediaPlayer = new IjkMediaPlayer();
        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 60);
        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-fps", 0);
        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "fps", 30);
        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48);
        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", IjkMediaPlayer.SDL_FCC_YV12);
        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0);
        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "nobuffer");
        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "max-buffer-size", 1024);
        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "min-frames", 3);
        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 1);
        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probsize", "4096");
        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", "2000000");


        ijkMediaPlayer.setOnPreparedListener(JZMediaIjkplayer.this);
        ijkMediaPlayer.setOnVideoSizeChangedListener(JZMediaIjkplayer.this);
        ijkMediaPlayer.setOnCompletionListener(JZMediaIjkplayer.this);
        ijkMediaPlayer.setOnErrorListener(JZMediaIjkplayer.this);
        ijkMediaPlayer.setOnInfoListener(JZMediaIjkplayer.this);
        ijkMediaPlayer.setOnBufferingUpdateListener(JZMediaIjkplayer.this);
        ijkMediaPlayer.setOnSeekCompleteListener(JZMediaIjkplayer.this);
        ijkMediaPlayer.setOnTimedTextListener(JZMediaIjkplayer.this);

        try {
            ijkMediaPlayer.setDataSource(jzDataSource.getCurrentUrl().toString());
            ijkMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            ijkMediaPlayer.setScreenOnWhilePlaying(true);
            ijkMediaPlayer.prepareAsync();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void pause() {
        ijkMediaPlayer.pause();
    }

    @Override
    public boolean isPlaying() {
        return ijkMediaPlayer.isPlaying();
    }

    @Override
    public void seekTo(long time) {
        ijkMediaPlayer.seekTo(time);
    }

    @Override
    public void release() {
        if (ijkMediaPlayer != null)
            ijkMediaPlayer.release();
    }

    @Override
    public long getCurrentPosition() {
        return ijkMediaPlayer.getCurrentPosition();
    }

    @Override
    public long getDuration() {
        return ijkMediaPlayer.getDuration();
    }

    @Override
    public void setSurface(Surface surface) {
        ijkMediaPlayer.setSurface(surface);
    }

    @Override
    public void setVolume(float leftVolume, float rightVolume) {
        ijkMediaPlayer.setVolume(leftVolume, rightVolume);
    }

    @Override
    public void onPrepared(IMediaPlayer iMediaPlayer) {
        ijkMediaPlayer.start();
        if (jzDataSource.getCurrentUrl().toString().toLowerCase().contains("mp3")) {
            JZMediaManager.instance().mainThreadHandler.post(new Runnable() {
                @Override
                public void run() {
                    if (JzvdMgr.getCurrentJzvd() != null) {
                        JzvdMgr.getCurrentJzvd().onPrepared();
                    }
                }
            });
        }
    }

    @Override
    public void onVideoSizeChanged(IMediaPlayer iMediaPlayer, int i, int i1, int i2, int i3) {
        JZMediaManager.instance().currentVideoWidth = iMediaPlayer.getVideoWidth();
        JZMediaManager.instance().currentVideoHeight = iMediaPlayer.getVideoHeight();
        JZMediaManager.instance().mainThreadHandler.post(new Runnable() {
            @Override
            public void run() {
                if (JzvdMgr.getCurrentJzvd() != null) {
                    JzvdMgr.getCurrentJzvd().onVideoSizeChanged();
                }
            }
        });
    }

    @Override
    public void onCompletion(IMediaPlayer iMediaPlayer) {
        JZMediaManager.instance().mainThreadHandler.post(new Runnable() {
            @Override
            public void run() {
                if (JzvdMgr.getCurrentJzvd() != null) {
                    JzvdMgr.getCurrentJzvd().onAutoCompletion();
                }
            }
        });
    }

    @Override
    public boolean onError(IMediaPlayer iMediaPlayer, final int what, final int extra) {
        JZMediaManager.instance().mainThreadHandler.post(new Runnable() {
            @Override
            public void run() {
                if (JzvdMgr.getCurrentJzvd() != null) {
                    JzvdMgr.getCurrentJzvd().onError(what, extra);
                }
            }
        });
        return true;
    }

    @Override
    public boolean onInfo(IMediaPlayer iMediaPlayer, final int what, final int extra) {
        JZMediaManager.instance().mainThreadHandler.post(new Runnable() {
            @Override
            public void run() {
                if (JzvdMgr.getCurrentJzvd() != null) {
                    if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {
                        JzvdMgr.getCurrentJzvd().onPrepared();
                    } else {
                        JzvdMgr.getCurrentJzvd().onInfo(what, extra);
                    }
                }
            }
        });
        return false;
    }

    @Override
    public void onBufferingUpdate(IMediaPlayer iMediaPlayer, final int percent) {
        JZMediaManager.instance().mainThreadHandler.post(new Runnable() {
            @Override
            public void run() {
                if (JzvdMgr.getCurrentJzvd() != null) {
                    JzvdMgr.getCurrentJzvd().setBufferProgress(percent);
                }
            }
        });
    }

    @Override
    public void onSeekComplete(IMediaPlayer iMediaPlayer) {
        JZMediaManager.instance().mainThreadHandler.post(new Runnable() {
            @Override
            public void run() {
                if (JzvdMgr.getCurrentJzvd() != null) {
                    JzvdMgr.getCurrentJzvd().onSeekComplete();
                }
            }
        });
    }

    @Override
    public void onTimedText(IMediaPlayer iMediaPlayer, IjkTimedText ijkTimedText) {

    }
}

 c. 加入so库

     将上一步编译的so库,复制到我们现在的项目中 

d. 使用

        Jzvd.setMediaInterface(new JZMediaIjkplayer());
        mJzVideoPlayer.setUp(url,
                "水产监控",
                Jzvd.SCREEN_WINDOW_NORMAL);

好了已经支持我们自己定义的引擎了,那么对应的协议编码问题也是能解决的。

4. 积累的一个排错技巧

阐述下我的项目问题,就是我们后台提供给我的是rtsp协议流的直播视频,在主流播放器上是可以播放的,但是我一开始的播放器是不支持的,其实问题不是出现在协议上,出现在的是编解码的问题。

下面看下BUG

下面我们解决下 No codec could be found with id 13 以及 tv.danmaku.ijk.media.player.IjkMediaPlayer: Error (-10000,0) 

这里提示的是我们缺少硬解码支持,那么怎么添加对应的解码支持呢。

ijkPlayer-master文件夹搜索 avcodec.h 文件,如下所示。

从video codecs开始,ID从1开始自增,那么13对应的是 AV_CODEC_ID_MPEG4,

所以在编译生成.so文件前在 module-lite.sh中加入如下:

# 支持mpeg4解码
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=mpeg4"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=mpeg4"

enum AVCodecID {
    AV_CODEC_ID_NONE,

    /* video codecs */
    AV_CODEC_ID_MPEG1VIDEO,
    AV_CODEC_ID_MPEG2VIDEO, ///< preferred ID for MPEG-1/2 video decoding
#if FF_API_XVMC
    AV_CODEC_ID_MPEG2VIDEO_XVMC,
#endif /* FF_API_XVMC */
    AV_CODEC_ID_H261,
    AV_CODEC_ID_H263,
    AV_CODEC_ID_RV10,
    AV_CODEC_ID_RV20,
    AV_CODEC_ID_MJPEG,
    AV_CODEC_ID_MJPEGB,
    AV_CODEC_ID_LJPEG,
    AV_CODEC_ID_SP5X,
    AV_CODEC_ID_JPEGLS,
    AV_CODEC_ID_MPEG4,
    AV_CODEC_ID_RAWVIDEO,
    AV_CODEC_ID_MSMPEG4V1,
    AV_CODEC_ID_MSMPEG4V2,
    AV_CODEC_ID_MSMPEG4V3,
    AV_CODEC_ID_WMV1,
    AV_CODEC_ID_WMV2,
    AV_CODEC_ID_H263P,
    AV_CODEC_ID_H263I,
    AV_CODEC_ID_FLV1,
    AV_CODEC_ID_SVQ1,
    AV_CODEC_ID_SVQ3,
    AV_CODEC_ID_DVVIDEO,
    AV_CODEC_ID_HUFFYUV,
    AV_CODEC_ID_CYUV,
    AV_CODEC_ID_H264,
    AV_CODEC_ID_INDEO3,
    AV_CODEC_ID_VP3,
    AV_CODEC_ID_THEORA,
    AV_CODEC_ID_ASV1,
    AV_CODEC_ID_ASV2,
    AV_CODEC_ID_FFV1,
    AV_CODEC_ID_4XM,
    AV_CODEC_ID_VCR1,
    AV_CODEC_ID_CLJR,
    AV_CODEC_ID_MDEC,
。。。。。

那么以后我们也可以通过报错信息来查询缺少哪一个硬解码,自由的进行编译生成库。 

参考博客

ijkplayer开启rtsp与MJPEG的支持

使用ijkPLayer播放rtsp协议地址

ijkplayer开启rtsp,并且支持录制和截图功能

编译ijkplayer,并添加rtsp、rtmp支持,解决无法播放、unknown、延迟问题

ndk_14_mac 网盘下载:

链接:https://pan.baidu.com/s/1MCWnRPBxBTZMhi5RKAnv1A  密码:0tot

已编译的支持rtsp协议so库的demo地址:

https://github.com/crazyzhangxl/ijkplayer

猜你喜欢

转载自blog.csdn.net/crazyZhangxl/article/details/83341203
今日推荐