Native Android player does not support avi format video playback, need to introduce a third-party player. Ijkplayer is a lightweight open source player produced by STATION B, which can support multiple formats of video playback through compilation and configuration. This article records the whole process of compiling and using ijkPlayer under MAC.

Development environment: MacOS-11.2.3 +Android Studio-4.1.2+ iJkPlayer-0.8.8

Compile ijkplayer

You can follow the compilation steps on the official website, and notes will be indicated where errors are easy to occur.

  1. Install YASM and PKG-config using Homebrew.

  2. Ndk-r10 ~ NDK-R14 is required to compile FFMPEG in ijkPlayer.

  3. Run in sequence on the command line:

    # Clone ijkPlayer project source code
    git clone https://github.com/Bilibili/ijkplayer.git ijkplayer-android
    
    Go to ijkPlayer directory
    cd ijkplayer-android
    
    Switch to the latest code branchGit Checkout -b latest k0.8.8Check and download the FFMPEG code
    # Note that github is very easy to download failure due to the speed of the Internet
    Or try a few more times and let the code download in batches
    ./init-android.sh
    
    You can manually modify the module.sh configuration in ijkplayer-android/config
    Linux /perf_event. H: No such file or directory, need to add configuration:
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-linux-perf"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bzlib"
    # 1.module-default.sh supports more formats (fully compiled, almost all formats supported), and larger files
    # 2.module-lite-hevc.sh supports fewer formats, including hevc
    # 3. Module-lite.sh supports fewer formats (default)
    cd config
    rm module.sh
    ln -s module-lite.sh module.sh
    source module.sh
    * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-muxer=avi"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=avi"
    Export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-demuxers"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxers"
    # enable-muxer/enable-demuxer/enable-demuxer/disable-demuxers/disable-demuxers/disable-demuxers/disable-demuxers/disable-demuxers
    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    
    # ffmPEG requires NDK, ijkPlayer requires NDK 10 ~ 14. There are two ways to configure the NDK
    # 1. Add the system environment to ~/.zshrc
    # terminal enter vim ~/.zshrc
    # In the open file, insert:
    # export ANDROID_NDK=/Users/ XXXXX /android-ndk-r14b
    # export PATH=${PATH}:${ANDROID_NDK}
    # terminal enter source ~/.zshrc to make it take effect
    Type ndk-build -v to verify that the NDK configuration takes effect
    # But the first method does not work for me, ffmPEG will still tell me that ANDROID_NDK is not found, so I recommend using the second method if the first method does not work
    # 2. In the terminal command line, run export to import the path. The terminal type :export ANDROID_NDK=/Users/ XXXXX /android-ndk-r14b, then compile ffmPEG
    cd android/contrib
    export ANDROID_NDK=/Users/xxxxx/android-ndk-r14b
    ./compile-ffmpeg.sh clean
    ./compile-ffmpeg.sh all
    
    cd. ./compile-ijk.sh all# finally compiled so libraries in ijkplayer - android/android/ijkplayer ijkplayer - ${} CPU/SRC/main/libs directory
    # CPU = arm64/armv5/armv7a/x86/x86_64
    
    Copy the code

Ijkplayer easy to use (Kotlin version)

  1. Import the compiled.so file.

    • Create a jniLibs directory under app/ SRC /main in the project and move the. So package of the required CPU model to jniLibs. In x86 models, for example, will ijkplayer – android/android/ijkplayer/ijkplayer – x86 / SRC/main/libs directory folder moves to jniLibs directory.

    • Add ijkPlayer dependencies to your app’s build.gradle file.

      // Must depend on
      implementation 'TV. Danmaku. Ijk. Media: ijkplayer - Java: 0.8.8'
      
      Ijkplayer does not need to add the following dependencies, which are the default Settings of ijkPlayer. If you do not build your own so library, add the following dependencies
      // // Other ABIs: optional
      / / implementation 'TV. Danmaku. Ijk. Media: ijkplayer - armv7a: 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'
      
      // ExO player package, experimental features
      / / implementation 'TV. Danmaku. Ijk. Media: ijkplayer - exobiology: 0.8.8'
      Copy the code
  2. Custom playback control. Ijkplayer does not provide a player control for us to use, so we need to combine it with surfaceView as a play control class. Basically create a SurfaceView and assign it to IMediaPlayer.

    class IJKVideoPlayer : FrameLayout {
        /** * Is provided by ijkPlayer to play video, you need to pass it a surfaceView */
        private var mMediaPlayer: IMediaPlayer? = null
    
        /** * Video file address */
        private var mPath = ""
    
        private lateinit var surfaceView: SurfaceView
    
        private lateinit var listener: VideoPlayerListener
        private var mContext: Context? = null
    
    
        constructor(context: Context) : super(context) {
            initVideoView(context)
        }
    
    
        constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
            initVideoView(context)
        }
    
        constructor( context: Context, attrs: AttributeSet? .@AttrRes defStyleAttr: Int
        ) : super(context, attrs, defStyleAttr) {
            initVideoView(context)
        }
    
    
        private fun initVideoView(context: Context) {
            mContext = context
    
            // Get the focus. ~
            isFocusable = true
        }
    
    
        /** * Set the video address. * Do different things depending on whether the video is playing for the first time. * *@param path the path of the video.
         */
        fun setVideoPath(path: String) {
            if (TextUtils.equals("", mPath)) {
                // Create a new surfaceView if the video is playing for the first time
                mPath = path
                createSurfaceView()
            } else {
                // Otherwise load directly
                mPath = path
                load()
            }
        }
    
    
        /** * Create a new surfaceView */
        private fun createSurfaceView(a) {
            // Create a new Surface View
            surfaceView = SurfaceView(mContext)
            surfaceView.holder.addCallback(LmnSurfaceCallback())
            val layoutParams = LayoutParams(
                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, Gravity.CENTER
            )
            surfaceView.layoutParams = layoutParams
            this.addView(surfaceView)
        }
    
        /** * surfaceView listener */
        private inner class LmnSurfaceCallback : SurfaceHolder.Callback {
            override fun surfaceCreated(holder: SurfaceHolder) {}
            override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
                // After surfaceView is created successfully, the video is loaded
                load()
            }
    
            override fun surfaceDestroyed(holder: SurfaceHolder){}}/**
         * 加载视频
         */
        private fun load(a) {
            // Re-create the IMediaPlayer each time
            createPlayer()
            try{ mMediaPlayer!! .dataSource = mPath }catch (e: IOException) {
                e.printStackTrace()
            }
            // Set the view for mediaPlayermMediaPlayer!! .setDisplay(surfaceView.holder) mMediaPlayer!! .prepareAsync() }/** * create a new player */
        private fun createPlayer(a) {
            if(mMediaPlayer ! =null) { mMediaPlayer!! .stop() mMediaPlayer!! .setDisplay(null) mMediaPlayer!! .release() }val ijkMediaPlayer = IjkMediaPlayer()
            IjkMediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_DEBUG)
    
    Ijkmediaplayer. setOption(ijkMediaplayer. OPT_CATEGORY_PLAYER, "mediacodec", 1);
            mMediaPlayer = ijkMediaPlayer
            (mMediaPlayer as IjkMediaPlayer).setOnPreparedListener(listener)
            (mMediaPlayer as IjkMediaPlayer).setOnInfoListener(listener)
            (mMediaPlayer as IjkMediaPlayer).setOnSeekCompleteListener(listener)
            (mMediaPlayer as IjkMediaPlayer).setOnBufferingUpdateListener(listener)
            (mMediaPlayer as IjkMediaPlayer).setOnErrorListener(listener)
        }
    
        fun setListener(listener: VideoPlayerListener) {
            this.listener = listener
            if(mMediaPlayer ! =null) { mMediaPlayer!! .setOnPreparedListener(listener) } }/** * -------======--------- Below encapsulates the control video method */
        fun start(a) {
            if(mMediaPlayer ! =null) { mMediaPlayer!! .start() } }fun release(a) {
            if(mMediaPlayer ! =null) { mMediaPlayer!! .reset() mMediaPlayer!! .release() mMediaPlayer =null}}fun pause(a) {
            if(mMediaPlayer ! =null) { mMediaPlayer!! .pause() } }fun stop(a) {
            if(mMediaPlayer ! =null) { mMediaPlayer!! .stop() } }fun reset(a) {
            if(mMediaPlayer ! =null) { mMediaPlayer!! .reset() } }fun getDuration(a): Long {
            return if(mMediaPlayer ! =null) { mMediaPlayer!! .duration }else {
                0}}fun getCurrentPosition(a): Long {
            return if(mMediaPlayer ! =null) { mMediaPlayer!! .currentPosition }else {
                0}}fun seekTo(l: Long) {
            if(mMediaPlayer ! =null) { mMediaPlayer!! .seekTo(l) } } }Copy the code
  3. Set listeners. Defines a listener that inherits IMediaPlayer’s n listeners.

    abstract class VideoPlayerListener : IMediaPlayer.OnBufferingUpdateListener.IMediaPlayer.OnCompletionListener.IMediaPlayer.OnPreparedListener.IMediaPlayer.OnInfoListener.IMediaPlayer.OnVideoSizeChangedListener.IMediaPlayer.OnErrorListener.IMediaPlayer.OnSeekCompleteListener
    Copy the code

    When implementing this abstract class, the most important thing is to get the video to play in the onPrepared() method.

  4. Place the player control in the XML.

        <com.bupt.compose3dvideo.ijkplayer.IJKVideoPlayer
            android:id="@+id/ijk_player"
            android:layout_width="@dimen/dimen_0"
            android:layout_height="@dimen/dimen_0"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/add_video"
            android:layout_marginTop="@dimen/dimen_16"
            android:layout_marginBottom="@dimen/dimen_100"/>
    Copy the code
  5. Initialize and call in the activity/ Fragment.

    Initialization:

     		// Load the native library
            try {
                IjkMediaPlayer.loadLibrariesOnce(null)
                IjkMediaPlayer.native_profileBegin("libijkplayer.so")}catch (e: Exception) {
                this.finish()
            }
    		/ / use
            binding.ijkPlayer.setVideoPath(filePath)
            binding.ijkPlayer.setListener(object : VideoPlayerListener() {
                override fun onBufferingUpdate(p0: IMediaPlayer? , p1:Int) {
                    TODO("Not yet implemented")}override fun onCompletion(p0: IMediaPlayer?). {
                    TODO("Not yet implemented")}// Play the video when you are ready
                override fun onPrepared(p0: IMediaPlayer?).{ p0!! .start() }override fun onInfo(p0: IMediaPlayer? , p1:Int, p2: Int): Boolean {
                    TODO("Not yet implemented")}override fun onVideoSizeChanged(p0: IMediaPlayer? , p1:Int, p2: Int, p3: Int, p4: Int) {
                    TODO("Not yet implemented")}override fun onError(p0: IMediaPlayer? , p1:Int, p2: Int): Boolean {
                    TODO("Not yet implemented")}override fun onSeekComplete(p0: IMediaPlayer?). {
                    TODO("Not yet implemented")}})Copy the code

    Close native library in onStop() :

    IjkMediaPlayer.native_profileEnd();
    Copy the code

Matters needing attention

  1. IjkMediaPlayer’s setDataSource is not recommended to be reassigned. It is required every time you change the video source: player.release() -> create new player -> player.setdatasource

    But the SurfaceView doesn’t need to be recreated every time.

  2. Set playback speed: ijkMediaPlayer.setSpeed ();

  3. Some important video message return codes (which can be obtained from the listener’s onInfo() method) :

int MEDIA_INFO_VIDEO_RENDERING_START = 3;// The video is ready to render
int MEDIA_INFO_BUFFERING_START = 701;// Start buffering
int MEDIA_INFO_BUFFERING_END = 702;// Buffering ends
int MEDIA_INFO_VIDEO_ROTATION_CHANGED = 10001;// Video select information
int MEDIA_ERROR_SERVER_DIED = 100;// Video interrupts. Generally, the video source is abnormal or the video type is not supported.
int MEDIA_ERROR_IJK_PLAYER = - 10000..// Usually there is a problem with the video source or the data format is not supported, such as the audio is not AAC, etc
int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;// Data errors have no valid collection
Copy the code

Reference article:

Ijkplayer is really not that hard to compile the so library

IOS development FFmpeg compiler clipping support AVI

Ijkplayer compiles the NDK path

Ijkplayer is a great player for Android