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.
-
Install YASM and PKG-config using Homebrew.
-
Ndk-r10 ~ NDK-R14 is required to compile FFMPEG in ijkPlayer.
-
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)
-
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
-
-
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
-
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.
-
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
-
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
-
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.
-
Set playback speed: ijkMediaPlayer.setSpeed ();
-
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