dplayer解析源码php调用,从demo分析ijk源码一:视频播放
ijk Android demo源码的整体结构如下
demo
ijkplayer-example是demo程序的主module,它依赖其它module,并实现一个简单的播放器程序
ijkplayer-java 是ijk库的Java实现代码,它的作用有三个
1、加载ijk的so
2、实现对ijk so的jni调用封装
3、封装IjkMediaPlayer供调用者直接使用
ijkplayer-exo 提供了一个使用 google exoplayer的实现封装IjkExoMediaPlayer,IjkExoMediaPlayer的实现继承了ijkplayer-java中的抽象类AbstractMediaPlayer,在ijkplayer-example调用的时候可与IjkMediaPlayer保持一致。
ijkplayer-arm64等,这类module中存放了与各芯片架构对应c源文件与编译ijk之后生成的so等,无Java实现,ijkplayer-example引入对应的moudle,可将该module所包含的so编译进最终的apk中。
一、ijkplayer-example
ijkplayer-example中的Java代码如下
example/
├── activities
│ ├── FileExplorerActivity.java
│ ├── RecentMediaActivity.java
│ ├── SampleMediaActivity.java
│ ├── SettingsActivity.java
│ └── VideoActivity.java
├── application
│ ├── AppActivity.java
│ └── Settings.java
├── content
│ ├── PathCursor.java
│ ├── PathCursorLoader.java
│ └── RecentMediaStorage.java
├── eventbus
│ └── FileExplorerEvents.java
├── fragments
│ ├── FileListFragment.java
│ ├── RecentMediaListFragment.java
│ ├── SampleMediaListFragment.java
│ ├── SettingsFragment.java
│ └── TracksFragment.java
├── services
│ └── MediaPlayerService.java
└── widget
├── media
│ ├── AndroidMediaController.java
│ ├── FileMediaDataSource.java
│ ├── IMediaController.java
│ ├── IRenderView.java
│ ├── IjkVideoView.java
│ ├── InfoHudViewHolder.java
│ ├── MeasureHelper.java
│ ├── MediaPlayerCompat.java
│ ├── SurfaceRenderView.java
│ ├── TableLayoutBinder.java
│ └── TextureRenderView.java
└── preference
└── IjkListPreference.java
播放器的实现在VideoActivity中,其余的Activity都是用来配合VideoActivity的
FileExplorerActivity 文件浏览器,可选择本机视频播放
RecentMediaActivity 记录最近的播放信息
SampleMediaActivity 提供了示例视频url,可直接播放
SettingsActivity 播放器参数设置
VideoActivity 播放器
二、如何使用ijkplayer-java封装的IjkMediaPlayer
Android系统播放器的使用是MediaPlayer + Surface,Surface可以通过SurfaceView或TextureView获取。
ijkplayer-example中封装了一个类IjkVideoView,IjkVideoView中演示了三种播放器实现的调用
IjkExoMediaPlayer在Ijkplayer-exo中对google exoplayer的调用封装
AndroidMediaPlayer对android系统播放器MediaPlayer的调用封装
IjkMediaPlayer在Ijkplayer-java中对ffmpeg的调用封装
1、创建IMediaPlayer
在使用ijk之前先加载so
IjkMediaPlayer.loadLibrariesOnce(null);
IjkMediaPlayer.native_profileBegin("libijkplayer.so");
IjkExoMediaPlayer、AndroidMediaPlayer、IjkMediaPlayer都实现了接口IMediaPlayer
public IMediaPlayer createPlayer(int playerType) {
IMediaPlayer mediaPlayer = null;
switch (playerType) {
case Settings.PV_PLAYER__IjkExoMediaPlayer: {
IjkExoMediaPlayer IjkExoMediaPlayer = new IjkExoMediaPlayer(mAppContext);
mediaPlayer = IjkExoMediaPlayer;
}
break;
case Settings.PV_PLAYER__AndroidMediaPlayer: {
AndroidMediaPlayer androidMediaPlayer = new AndroidMediaPlayer();
mediaPlayer = androidMediaPlayer;
}
break;
case Settings.PV_PLAYER__IjkMediaPlayer:
default: {
IjkMediaPlayer ijkMediaPlayer = null;
if (mUri != null) {
ijkMediaPlayer = new IjkMediaPlayer();
ijkMediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_DEBUG);
if (mSettings.getUsingMediaCodec()) {
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);
if (mSettings.getUsingMediaCodecAutoRotate()) {
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1);
} else {
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0);
}
if (mSettings.getMediaCodecHandleResolutionChange()) {
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 1);
} else {
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 0);
}
} else {
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0);
}
if (mSettings.getUsingOpenSLES()) {
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 1);
} else {
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 0);
}
String pixelFormat = mSettings.getPixelFormat();
if (TextUtils.isEmpty(pixelFormat)) {
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", IjkMediaPlayer.SDL_FCC_RV32);
} else {
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", pixelFormat);
}
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "http-detect-range-support", 0);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48);
}
mediaPlayer = ijkMediaPlayer;
}
break;
}
...
return mediaPlayer;
}
上面的代码根据设置界面的选择,可以创建对应类型的播放器,这里先忽略IjkExoMediaPlayer和AndroidMediaPlayer,重点关注IjkMediaPlayer。
2、创建Surface
Surface可以通过SurfaceView或TextureView获取,在ijkplayer-example中封装了一个接口IRenderView用于将SurfaceView和TextureView的实现和使用统一。
IRenderView
public interface IRenderView {
int AR_ASPECT_FIT_PARENT = 0; // without clip
int AR_ASPECT_FILL_PARENT = 1; // may clip
int AR_ASPECT_WRAP_CONTENT = 2;
int AR_MATCH_PARENT = 3;
int AR_16_9_FIT_PARENT = 4;
int AR_4_3_FIT_PARENT = 5;
View getView();
boolean shouldWaitForResize();
void setVideoSize(int videoWidth, int videoHeight);
void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen);
void setVideoRotation(int degree);
void setAspectRatio(int aspectRatio);
void addRenderCallback(@NonNull IRenderCallback callback);
void removeRenderCallback(@NonNull IRenderCallback callback);
interface ISurfaceHolder {
void bindToMediaPlayer(IMediaPlayer mp);
@NonNull
IRenderView getRenderView();
@Nullable
SurfaceHolder getSurfaceHolder();
@Nullable
Surface openSurface();
@Nullable
SurfaceTexture getSurfaceTexture();
}
interface IRenderCallback {
/**
* @param holder
* @param width could be 0
* @param height could be 0
*/
void onSurfaceCreated(@NonNull ISurfaceHolder holder, int width, int height);
/**
* @param holder
* @param format could be 0
* @param width
* @param height
*/
void onSurfaceChanged(@NonNull ISurfaceHolder holder, int format, int width, int height);
void onSurfaceDestroyed(@NonNull ISurfaceHolder holder);
}
}
TextureRenderView
public class TextureRenderView extends TextureView implements IRenderView {
...
}
SurfaceRenderView
public class SurfaceRenderView extends SurfaceView implements IRenderView {
...
}
这里略过了SurfaceRenderView和TextureRenderView的内部实现,它们的目的只有一个,用于生成Surface,供播放器渲染画面。
根据设置界面的选择创建SurfaceView或TextureView
public void setRender(int render) {
switch (render) {
case RENDER_NONE:
setRenderView(null);
break;
case RENDER_TEXTURE_VIEW: {
TextureRenderView renderView = new TextureRenderView(getContext());
if (mMediaPlayer != null) {
renderView.getSurfaceHolder().bindToMediaPlayer(mMediaPlayer);
renderView.setVideoSize(mMediaPlayer.getVideoWidth(), mMediaPlayer.getVideoHeight());
renderView.setVideoSampleAspectRatio(mMediaPlayer.getVideoSarNum(), mMediaPlayer.getVideoSarDen());
renderView.setAspectRatio(mCurrentAspectRatio);
}
setRenderView(renderView);
break;
}
case RENDER_SURFACE_VIEW: {
SurfaceRenderView renderView = new SurfaceRenderView(getContext());
setRenderView(renderView);
break;
}
default:
Log.e(TAG, String.format(Locale.getDefault(), "invalid render %d\n", render));
break;
}
}
3、打开视频
private void setVideoURI(Uri uri, Map headers) {
mUri = uri;
mHeaders = headers;
mSeekWhenPrepared = 0;
openVideo();
requestLayout();
invalidate();
}
private void openVideo() {
if (mUri == null || mSurfaceHolder == null) {
// 视频地址无效或Surface还未创建完成
return;
}
release(false);
AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);
// 获取音频焦点
am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
try {
mMediaPlayer = createPlayer(mSettings.getPlayer());
final Context context = getContext();
mMediaPlayer.setOnPreparedListener(mPreparedListener);
mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
mMediaPlayer.setOnCompletionListener(mCompletionListener);
mMediaPlayer.setOnErrorListener(mErrorListener);
mMediaPlayer.setOnInfoListener(mInfoListener);
mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
mMediaPlayer.setOnSeekCompleteListener(mSeekCompleteListener);
mMediaPlayer.setOnTimedTextListener(mOnTimedTextListener);
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);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setScreenOnWhilePlaying(true);
mPrepareStartTime = System.currentTimeMillis();
mMediaPlayer.prepareAsync();
if (mHudViewHolder != null)
mHudViewHolder.setMediaPlayer(mMediaPlayer);
mCurrentState = STATE_PREPARING;
attachMediaController();
} 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();
}
}
从openVideo中可以看到,IJKMediaPlayer的调用大致分为下面几个部分
设置监听 mMediaPlayer.setOnPreparedListener 等
设置视频源 mMediaPlayer.setDataSource(mUri.toString())
开始加载 mMediaPlayer.prepareAsync()
经过IJKMediaPlayer的封装之后,其调用方法基本与系统播放器MediaPlayer保持一致。
4、响应播放器状态变化
通过上面设置的监听,可以实时监听播放器的状态变化
mMediaPlayer.setOnPreparedListener(mPreparedListener)
监听视频加载,回调视频加载完成状态,需要在这里调用播放器的start方法
mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener)
监听视频尺寸变化,在onVideoSizeChanged中可获取视频的宽高信息,根据视频的宽高信息需要设置SurfaceView或TextureView的大小
mMediaPlayer.setOnCompletionListener(mCompletionListener)
视频播放完成监听,需要释放播放器
mMediaPlayer.setOnErrorListener(mErrorListener)
视频加载失败监听,需要释放播放器
mMediaPlayer.setOnInfoListener(mInfoListener)
通常用来做视频缓冲监听,701表示开始缓冲,702表示缓冲完成
mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener)
缓冲进度监听
mMediaPlayer.setOnSeekCompleteListener(mSeekCompleteListener)
seek状态监听,通过需要做seek保护,在上次seek未完成之前,不允许做新的seek操作
关于IJKMediaPlayer,需要特殊关注的是setOnInfoListener中回调的信息,
IJKMediaPlayer回调了比系统播放器更多的播放器信息,从demo中可以看到
private IMediaPlayer.OnInfoListener mInfoListener =
new IMediaPlayer.OnInfoListener() {
public boolean onInfo(IMediaPlayer mp, int arg1, int arg2) {
if (mOnInfoListener != null) {
mOnInfoListener.onInfo(mp, arg1, arg2);
}
switch (arg1) {
case IMediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING:
Log.d(TAG, "MEDIA_INFO_VIDEO_TRACK_LAGGING:");
break;
case IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START:
Log.d(TAG, "MEDIA_INFO_VIDEO_RENDERING_START:");
break;
case IMediaPlayer.MEDIA_INFO_BUFFERING_START:
Log.d(TAG, "MEDIA_INFO_BUFFERING_START:");
break;
case IMediaPlayer.MEDIA_INFO_BUFFERING_END:
Log.d(TAG, "MEDIA_INFO_BUFFERING_END:");
break;
case IMediaPlayer.MEDIA_INFO_NETWORK_BANDWIDTH:
Log.d(TAG, "MEDIA_INFO_NETWORK_BANDWIDTH: " + arg2);
break;
case IMediaPlayer.MEDIA_INFO_BAD_INTERLEAVING:
Log.d(TAG, "MEDIA_INFO_BAD_INTERLEAVING:");
break;
case IMediaPlayer.MEDIA_INFO_NOT_SEEKABLE:
Log.d(TAG, "MEDIA_INFO_NOT_SEEKABLE:");
break;
case IMediaPlayer.MEDIA_INFO_METADATA_UPDATE:
Log.d(TAG, "MEDIA_INFO_METADATA_UPDATE:");
break;
case IMediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE:
Log.d(TAG, "MEDIA_INFO_UNSUPPORTED_SUBTITLE:");
break;
case IMediaPlayer.MEDIA_INFO_SUBTITLE_TIMED_OUT:
Log.d(TAG, "MEDIA_INFO_SUBTITLE_TIMED_OUT:");
break;
case IMediaPlayer.MEDIA_INFO_VIDEO_ROTATION_CHANGED:
mVideoRotationDegree = arg2;
Log.d(TAG, "MEDIA_INFO_VIDEO_ROTATION_CHANGED: " + arg2);
if (mRenderView != null)
mRenderView.setVideoRotation(arg2);
break;
case IMediaPlayer.MEDIA_INFO_AUDIO_RENDERING_START:
Log.d(TAG, "MEDIA_INFO_AUDIO_RENDERING_START:");
break;
}
return true;
}
};
以上就是IJKMediaPlayer关于视频播放的实现。
dplayer解析源码php调用,从demo分析ijk源码一:视频播放相关推荐
- 红橙Darren视频笔记 从AIDL Demo分析Android源码走向
一 AIDL demo 1.1 服务端搭建 新建AIDL文件 // IUserCalc.aidl package com.example.aidlserver;// Declare any non-d ...
- mybatis源码之执行insert代码分析
系列文档: mybatis源码之创建SqlSessionFactory代码分析 mybatis源码之创建SqlSessionFactory代码分析 - mapper xml解析 mybatis源码之执 ...
- C#生成二维码、调用摄像头扫描二维码
二维码的生成和解码,有两个开源项目可以参考: 一个是google的zxing,另外一个是ThroughWork. zxing做的很全面,支持各种语言和平台,具体不多讲,自己查去.ThroughWork ...
- ffmpeg 调用 NVIDIA GPU 处理视频转码,笔记。和纯用CPU比起来,速度快5倍以上
参考别人的文章 FFMPEG 使用显卡加速转码 ffmpeg 调用 NVIDIA GPU 处理视频转码 ffmpeg 硬件加速视频转码指南 ffmpeg 硬件加速 wmv 视频转码 自己的关于ffmp ...
- cglib demo以及Enhancer源码解析
转载自:https://www.jianshu.com/p/20203286ccd9 先通过demo演示效果,然后进行源码分析 demo用Enhancer结合MethodInterceptor以及Ca ...
- 前端调用手机摄像头权限进行扫码解析
前端调用手机摄像头权限进行扫码解析(demo含Vue及原生) 前端调用手机摄像头权限进行扫码解析(demo含Vue及原生js写法) 引子 实践 此时已经可以成功调用摄像头,接下来集成进Vue工程中 最 ...
- C++源码的调用图生成
前言 之前受知乎用户mailto1587启发,写了个C++源码的调用图生成器,可以以图示法显示C++函数的调用关系, 代码放在了github仓库里,仅供参考: CodeSnippet/python/S ...
- 面试官系统精讲Java源码及大厂真题 - 20 SynchronousQueue 源码解析
20 SynchronousQueue 源码解析 更新时间:2019-10-15 11:19:50 只有在那崎岖的小路上不畏艰险奋勇攀登的人,才有希望达到光辉的顶点. --马克思 引导语 Synchr ...
- Spring源码深度解析(郝佳)-学习-Spring消息-整合RabbitMQ及源码解析
我们经常在Spring项目中或者Spring Boot项目中使用RabbitMQ,一般使用的时候,己经由前人将配置配置好了,我们只需要写一个注解或者调用一个消息发送或者接收消息的监听器即可,但是底 ...
最新文章
- 程序运行背后的那些事 ~ 【程序的编译(预处理操作)+链接】
- 【运维囧事】Citrix Xendesktop 与 XML 集成时添加信任关系
- CSharpGL(22)实现顺序无关的半透明渲染(Order-Independent-Transparency)
- 脑植入芯片实现脑机交互,脑神经链会如星链般放大马斯克的光环吗
- 第二篇:数据可视化 - 基本API
- 再破纪录!ECCV 2020 旷视研究院15篇成果总览
- 我爱的人,你知道我一直在
- http 412 precondition failed
- Acwing 240食物链(带权并查集)
- [jquery]高级篇--获取div子元素
- wingdings字体符号在哪_出版社编辑对标点符号和专业术语的要求
- maven:Java heap space内存不足错误的解决方法
- windows下CodeBlocks TMD-GCC安装及配置
- 在ubantu16.04系统下安装ros操作系统
- 王之泰201771010131《面向对象程序设计(java)》第十七周学习总结
- 7---可变参数+Collections集合工具类+冒泡排序+Map集合
- ios Symbol(s) not found for architecture arm64
- 社保的计算及缴纳地及个人部分和公司缴纳部分的一些疑问
- 将 vim 外的内容复制并粘贴到 Vim 里使用,如从windows系统复制内容到vim中使用
- Android视频监控实现(一)