1.初识Ijkplayer
Android系统提供的MediaPlayer支持的编码格式有限,这里推荐一款开源的媒体播放器Ijkplayer.
ijkplayer 是一个基于 ffplay 的轻量级 Android/iOS 视频播放器。
对应的git路径:https://github.com/Bilibili/ijkplayer
在Android端,基本上还是沿用SurfaveView+MediaController的架构。就是将MediaPlayer替换成Ijkplayer,支持 ffmpeg的各种编码方式。
2.依赖so的编译
需要准备的工具链:NDK,目前最近的分支已经支持ndk13,android studio或者是官网可以下载到该版本。
个人使用的编译方法是git取源码,在mac上进行编译,然后再把编译好的so放到对应的项目中去。
从git说明的Before Build一步一步操作到buuld android 就可以编译出对应的so文件。
3.使用demo
步骤:1.把编译好的so放到jni对应的目录。
2.编译ijkplayer-java,已aar的方式导入项目(ps:我是直接copy源码到项目自己编译)
3.将example中的widget目录和service目录导入项目。
4.创建自己的activity,使用example中的IjkVideoView来播放。
需要注意的是这种导入方法,需要手动需要res中的string,color,layout等内容,选择性的导入项目。
我本地做的时候把hubview给砍了。
初始化mediaplayer,绑定mediaController,开启video就好了。
// init player
IjkMediaPlayer.loadLibrariesOnce(null);
IjkMediaPlayer.native_profileBegin("libijkplayer.so");
videoView = (IjkVideoView) findViewById(R.id.video_view);
videoView.setMediaController(mMediaController);
videoView.setVideoURI(Uri.parse(inputUrl));
videoView.start();
4.介绍一下IjkVideoView的基础架构。
个人理解理顺MediaPlayer,SurfaceView,MediaController这3个资源的关系,这个代码也就比较清晰了:
这里使用setVideoURI的流程来做说明。
Step1 IjkVideoView初始化
initRenders();//初始化surfaceview界面资源
Step2 setViedoURI
AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);
am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
try {
mMediaPlayer = createPlayer(mSettings.getPlayer());//mediaPlayer 初始化配置。
// TODO: create SubtitleController in MediaPlayer, but we need
// a context for the subtitle renderers
final Context context = getContext();
// REMOVED: SubtitleController
// REMOVED: mAudioSession
mMediaPlayer.setOnPreparedListener(mPreparedListener); //Onprepared回调。mediaplayer的流程可以看官网doc的说明。下面的类似。
mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener) //surfaceview图片大小变更回调,ffplay的回调。
mMediaPlayer.setOnCompletionListener(mCompletionListener);
mMediaPlayer.setOnErrorListener(mErrorListener);
mMediaPlayer.setOnInfoListener(mInfoListener);
mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
mMediaPlayer.setOnSeekCompleteListener(mSeekCompleteListener);
mCurrentBufferPercentage = 0;
String scheme = mUri.getScheme();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
mSettings.getUsingMediaDataSource() &&
(TextUtils.isEmpty(scheme) || scheme.equalsIgnoreCase("file"))) {
IMediaDataSource dataSource = new FileMediaDataSource(new File(mUri.toString()));
mMediaPlayer.setDataSource(dataSource);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
mMediaPlayer.setDataSource(mAppContext, mUri, mHeaders);
} else {
mMediaPlayer.setDataSource(mUri.toString());
}
bindSurfaceHolder(mMediaPlayer, mSurfaceHolder); //mediaplayer绑定surfaceview.
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setScreenOnWhilePlaying(true);
mPrepareStartTime = System.currentTimeMillis();
mMediaPlayer.prepareAsync();
if ((mHudViewHolder != null) && (mSettings.getEnableBackgroundPlay()))
mHudViewHolder.setMediaPlayer(mMediaPlayer);
// REMOVED: mPendingSubtitleTracks
// we don't set the target state here either, but preserve the
// target state that was there before.
mCurrentState = STATE_PREPARING;
attachMediaController(); //mediaplayer绑定mediacontrol.
} catch (IOException ex) {
Log.w(TAG, "Unable to open content: " + mUri, ex);
mCurrentState = STATE_ERROR;
mTargetState = STATE_ERROR;
mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
} catch (IllegalArgumentException ex) {
Log.w(TAG, "Unable to open content: " + mUri, ex);
mCurrentState = STATE_ERROR;
mTargetState = STATE_ERROR;
mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
} finally {
// REMOVED: mPendingSubtitleTracks.clear();
}
后面就是处理各种回调。
第一篇先到这里,后续边学边补充。