Android-编译ijkplayer到播放视频再到可以直播拉流

编译ijkplayer

一 、 准备工作

首先先去github上先吧ijkplayer工程拉下来.进行编译.

ijkplayer下载地址

编译ijkplayer需要ndk.所以需要下载ndk.并且需要配置ndk的环境变量.

ndk下载地址

然后打开git命令行工具.切换ijkplayer最新版本,可以去tag里面进行查看

git checkout -B latest k0.8.8

这是我编译的项目,成功播放视频和拉流的工程

evideoview

二、准备编译

先进入我们下载好的ijkplayer目录,然后进入config目录,module-lite.sh中去修改我们需要支持的播放格式.

注 : 如果只是播放视频,则可以忽略以下修改支持的步骤..

cd ijkplayer
cd config
vim module-lite.sh

我这边对rtmp的格式进行支持,如果是disable的.改成enable.如果有些格式不支持,尽量关掉,这会影响到so库的大小 

# ./configure --list-protocols
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocols"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=async"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=bluray"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=concat"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=crypto"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=ffrtmpcrypt"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=ffrtmphttp"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=gopher"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=icecast"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=librtmp*"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=libssh"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=md5"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=mmsh"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=mmst"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtmp*"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtmp"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtmpt"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtp"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=sctp"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=srtp"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=subfile"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=unix"

修改完成之后保存,退出,如果是vim编辑器的化,先esc,然后:wq.进行退出,然后操作以下命令,进行从新生成module.sh文件

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

然后退出到根目录,也就是ijkplayer的目录输入,执行初始化操作.包括了把ffmpeg的代码拉取到本地等操作,这个命令执行时间比较长.得等一会.

sh init-android.sh

等待编译完成之后.

进去android目录下contrib目录

cd android/contrib

先清除一下 

./compile-ffmpeg.sh clean

然后再执行

./compile-ffmpeg.sh all

 如果ndk是正常的,就会继续走下去.如果没有,则需要从新检查一下ndk

这里开始离成功非常接近了.这里查看源码sh文件可以发现,不同的的指令,如果只是编译自己选择的版本,可以不输入all.因为all会非常慢.

case "$FF_TARGET" in
    "")
        echo_archs armv7a
        sh tools/do-compile-ffmpeg.sh armv7a
    ;;
    armv5|armv7a|arm64|x86|x86_64)
        echo_archs $FF_TARGET $FF_TARGET_EXTRA
        sh tools/do-compile-ffmpeg.sh $FF_TARGET $FF_TARGET_EXTRA
        echo_nextstep_help
    ;;
    all32)
        echo_archs $FF_ACT_ARCHS_32
        for ARCH in $FF_ACT_ARCHS_32
        do
            sh tools/do-compile-ffmpeg.sh $ARCH $FF_TARGET_EXTRA
        done
        echo_nextstep_help
    ;;
    all|all64)
        echo_archs $FF_ACT_ARCHS_64
        for ARCH in $FF_ACT_ARCHS_64
        do
            sh tools/do-compile-ffmpeg.sh $ARCH $FF_TARGET_EXTRA
        done
        echo_nextstep_help
    ;;
    clean)
        echo_archs FF_ACT_ARCHS_64
        for ARCH in $FF_ACT_ARCHS_ALL
        do
            if [ -d ffmpeg-$ARCH ]; then
                cd ffmpeg-$ARCH && git clean -xdf && cd -
            fi
        done
        rm -rf ./build/ffmpeg-*
    ;;
    check)
        echo_archs FF_ACT_ARCHS_ALL
    ;;
    *)
        echo_usage
        exit 1
    ;;
esac

此时编译如果返回则说明编译已经完成

--------------------
[*] create files for shared ffmpeg
--------------------

--------------------
[*] Finished
--------------------
# to continue to build ijkplayer, run script below,
sh compile-ijk.sh 

按照提示,需要执行

sh compile-ijk.sh

编译出来的文件在android目录下的ijkplayer的main目录中的libs目录下里面

ijkplayer/android/ijkplayer/ijkplayer-xxx/src/main/libs

这中间会出现3个so库,如果成功出现,说明你编译成功了

Android-编译ijkplayer到播放视频再到可以直播拉流_第1张图片

放到项目中就能够使用了,这时候项目就能够使用了.

以下是一个播放的例子

public class EVideoView extends FrameLayout {



    /**
     * 由ijkplayer提供,用于播放视频,需要给他传入一个surfaceView
     */
    private             IMediaPlayer mMediaPlayer = null;
    public static final int          VIDEO_START  = 1;
    public static final int          VIDEO_PAUSE  = 2;
    /**
     * 视频文件地址
     */
    private             String       mPath        = "";
    private SurfaceView         surfaceView;
    private VideoPlayerListener listener;
    private Context             mContext;
    private boolean isInitMediaPlay = true;
    private Handler mHandler        = new Handler(Looper.getMainLooper());
    private Uri     mURI;
    private boolean isURi;

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

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

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

    private void initVideoView(Context context) {
        mContext = context;
        //获取焦点
        setFocusable(true);

    }

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

    /**
     * 设置视频地址。
     * 根据是否第一次播放视频,做不同的操作。
     *
     * @param path the path of the video.
     */
    public void setVideoPath(File path) {
        setVideoPath(path.getAbsolutePath());
    }

    /**
     * 新建一个surfaceview
     */
    private void createSurfaceView() {
        //生成一个新的surface view
        surfaceView = new SurfaceView(mContext);
        surfaceView.getHolder().addCallback(new SurfaceCallback());
        LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT
                , LayoutParams.MATCH_PARENT, Gravity.CENTER);
        surfaceView.setLayoutParams(layoutParams);
        // 如果有两个surface,则会冲突,需要设置永远置顶
        surfaceView.setZOrderOnTop(true);
        this.addView(surfaceView);
    }

    public int getVideoWidth() {
        if (mMediaPlayer != null) {
            return mMediaPlayer.getVideoWidth();
        }
        return 0;
    }

    public int getVideoHeight() {
        if (mMediaPlayer != null) {
            return mMediaPlayer.getVideoHeight();
        }
        return 0;
    }

    public boolean isPlaying() {
        if (mMediaPlayer != null) {
            return mMediaPlayer.isPlaying();

        } else {
            return false;
        }

    }


    private void postProgress() {
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                // 如果停下,就不再更新进度
                if (isPlaying()) {
                    return;
                }
                if (mOnPlayStatusChangeListener != null) {
                    mOnPlayStatusChangeListener.onProgressChange(getCurrentPosition(), getDuration());
                }
                mHandler.postDelayed(this, 15);
            }
        }, 15);
    }

    public void setVideoPath(Uri parse) {
        isURi = true;
        //如果是第一次播放视频,那就创建一个新的surfaceView
        this.mURI = parse;
        createSurfaceView();

    }


    /**
     * surfaceView的监听器
     */
    private class SurfaceCallback implements SurfaceHolder.Callback {
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            // 其实这里只用对 MediaPlayer.setDisplay(surfaceView.getHolder());
            // 进行重新赋值就可以了,如果是重新创建,将会重新播放
            // 但是act被回收的时候,MediaPlayer可能会出现null,所以直接调用全部方法
            load();
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            // surfaceview创建成功后,加载视频
            start();
        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            // 因为每次变小都会将surface缩小。所以需要进行暂停,当回来的时候,会重新调用load方法。
            pause();
        }
    }

    /**
     * 加载视频
     */
    private void load() {
        // 每次都要重新创建IMediaPlayer
        createPlayer();
        // 理论上说是不允许修改多次的路径
        String dataSource = mMediaPlayer.getDataSource();
        try {
            if (dataSource == null) {
                if (isURi) {
                    mMediaPlayer.setDataSource(mContext, mURI, new HashMap());
                } else {
                    mMediaPlayer.setDataSource(mPath);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 给mediaPlayer设置视图
        mMediaPlayer.setDisplay(surfaceView.getHolder());
        if (isInitMediaPlay) {
            mMediaPlayer.prepareAsync();
            isInitMediaPlay = false;
        }
    }


    /**
     * 创建一个新的player,防止多次初始化,并且act被回收后,导致null问题.
     */
    private void createPlayer() {
        if (mMediaPlayer == null) {
//            IjkMediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_DEBUG);
//            ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "enable-accurate-seek", 1);
            // 开启硬解码
//            IjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);
            IjkMediaPlayer.loadLibrariesOnce(null);
            IjkMediaPlayer.native_profileBegin("libijkplayer.so");
            mMediaPlayer = new IjkMediaPlayer();
            mMediaPlayer.setScreenOnWhilePlaying(true);
            try {

                Method method = Class.forName("tv.danmaku.ijk.media.player.IjkMediaPlayer")
                        .getDeclaredMethod("setOption", int.class, String.class, long.class);
                // 硬编码
                method.invoke(mMediaPlayer, IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);
                method.invoke(mMediaPlayer, IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1);
                method.invoke(mMediaPlayer, IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 1);

                // 设置是否开启环路过滤: 0开启,画面质量高,解码开销大,48关闭,画面质量差点,解码开销小
                method.invoke(mMediaPlayer, IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48);
                // 是否缓冲
                method.invoke(mMediaPlayer, IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 1);
                // 设置缓冲区,单位是kb
                method.invoke(mMediaPlayer, IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-buffer-size", 8 * 8 * 2 * 1024);
                method.invoke(mMediaPlayer, IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 0);
//                mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER,"max-buffer-size",maxCacheSize);

//                method.invoke(mMediaPlayer,IjkMediaPlayer.OPT_CATEGORY_FORMAT, "timeout", 20000);
                method.invoke(mMediaPlayer, IjkMediaPlayer.OPT_CATEGORY_FORMAT, "buffer_size", 8 * 8 * 2 * 1024);
                method.invoke(mMediaPlayer, IjkMediaPlayer.OPT_CATEGORY_FORMAT, "infbuf", 1);  // 无限读
                method.invoke(mMediaPlayer, IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", 100L);
//                method.invoke(mMediaPlayer, IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 10240L);
                method.invoke(mMediaPlayer, IjkMediaPlayer.OPT_CATEGORY_FORMAT, "flush_packets", 1L);
//  关闭播放器缓冲,这个必须关闭,否则会出现播放一段时间后,一直卡主,控制台打印 FFP_MSG_BUFFERING_START
//                method.invoke(mMediaPlayer, IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0L);
//                method.invoke(mMediaPlayer, IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1L);

                // 另一个反射
//
                Method method1 = Class.forName("tv.danmaku.ijk.media.player.IjkMediaPlayer")
                        .getDeclaredMethod("setOption", int.class, String.class, String.class);
                method1.invoke(mMediaPlayer, IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_transport", "tcp");
                method1.invoke(mMediaPlayer, IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_flags", "prefer_tcp");
                method1.invoke(mMediaPlayer, IjkMediaPlayer.OPT_CATEGORY_FORMAT, "allowed_media_types", "video");
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            mMediaPlayer.setLooping(false);
            isInitMediaPlay = true;
            if (listener != null) {
                mMediaPlayer.setOnPreparedListener(listener);
                mMediaPlayer.setOnInfoListener(listener);
                mMediaPlayer.setOnSeekCompleteListener(listener);
                mMediaPlayer.setOnBufferingUpdateListener(listener);
                mMediaPlayer.setOnErrorListener(listener);
                mMediaPlayer.setOnVideoSizeChangedListener(listener);
                mMediaPlayer.setOnTimedTextListener(listener);
                mMediaPlayer.setOnCompletionListener(listener);
            }

        }
    }


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

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

    public void start() {
        if (mMediaPlayer != null && !mMediaPlayer.isPlaying()) {
            mMediaPlayer.start();
            postProgress();
            if (mOnPlayStatusChangeListener != null) {
                mOnPlayStatusChangeListener.onStatusChange(VIDEO_START);
            }
        }
    }

    public void release() {
        if (mMediaPlayer != null) {
            mMediaPlayer.reset();
            mMediaPlayer.setDisplay(null);
            mMediaPlayer.release();
            mMediaPlayer = null;
            surfaceView = null;
            this.removeAllViews();
        }
    }


    public void pause() {
        if (mMediaPlayer != null) {
            mMediaPlayer.pause();
            if (mOnPlayStatusChangeListener != null) {
                mOnPlayStatusChangeListener.onStatusChange(VIDEO_PAUSE);
            }
        }
    }

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


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


    public long getDuration() {
        if (mMediaPlayer != null) {
            return mMediaPlayer.getDuration();
        } else {
            return 0;
        }
    }

    public void toggle() {
        if (mMediaPlayer != null) {
            if (mMediaPlayer.isPlaying()) {
                pause();
            } else {
                start();
            }
        }
    }


    public long getCurrentPosition() {
        if (mMediaPlayer != null) {
            return mMediaPlayer.getCurrentPosition();
        } else {
            return 0;
        }
    }


    public void seekTo(long l) {
        if (mMediaPlayer != null) {
            mMediaPlayer.seekTo(l);
        }
    }

    private onPlayStatusChangeListener mOnPlayStatusChangeListener;

    public void setOnPlayStatusChangeListener(onPlayStatusChangeListener onPlayStatusChangeListener) {
        mOnPlayStatusChangeListener = onPlayStatusChangeListener;
    }

    public interface onPlayStatusChangeListener {
        void onStatusChange(int status);

        void onProgressChange(long progress, long max);
    }
}

这是我编译的项目,成功播放视频和拉流的工程

evideoview

你可能感兴趣的:(android,Android开发)