#VideoPlayer
项目用到三方库:GSYVideoPlayer库

库是基于IJKPlayer进行开发的多功能播放器,同时拥有自定义功能,对界面进行功能扩展。

  • IJKPlayerIJKPlyaer是国内知名视频弹幕网站Bilibili所开发开源项目,其基于FFmpeg进行视频开发,能够在android以及ios进行视频开发,同时对于其so库,因为开源,所以可以自定义自己需要的功能,或者扩展,或者压缩。
  • FFmpeg一款跨平台进行视频编解码的工具。官方是这么介绍的
    FFmpeg is the leading multimedia framework, able to decode, encode, transcode, mux, demux, stream, filter and play pretty much anything that humans and machines have created. It supports the most obscure ancient formats up to the cutting edge. No matter if they were designed by some standards committee, the community or a corporation. It is also highly portable: FFmpeg compiles, runs, and passes our testing infrastructure FATE across Linux, Mac OS X, Microsoft Windows, the BSDs, Solaris, etc. under a wide variety of build environments, machine architectures, and configurations.
    FFmpeg是领先的多媒体框架,能够解码,编码, 转码,复用,解复用,流式传输,过滤和播放任何内容。否管是以前的还是现在最流行的视频格式。无论是由某些标准、社区还是公司设计的都可以支持。它还具有高度可移植性:FFmpeg 在各种构建环境,机器架构和配置下,跨Linux,Mac OS X,Microsoft Windows,BSD,Solaris等编译,运行和传递的测试基础架构 FATE

以上两种介绍完了,能了解的仅限于认知。最好看一下官方文案,了解一下源码,主要是开源的ijkplayer。ok,开始了解吧,踏上视频认知的道路。


###IJKPlayer
先了解一下这强大的视频播放开源库。

通过其例子来学习。example点击浏览,毕竟跟着实操,懂得快一些。
如果确实不好配备,点这里,下载官方的example包。

首先我们运行一下这个demo,看到是一个文件浏览

ijkplayer Android 结合seekbar github ijkplayer_ide


这个就不关心了,在意的是播放的地方。先看一下setting吧。

ijkplayer Android 结合seekbar github ijkplayer_视频播放_02


看到第一个后台播放 4.0+需要,那么这里的后台是如何后台的呢。

看代码。找到setting的地方。

@Override
    public void onCreatePreferences(Bundle bundle, String s) {
        addPreferencesFromResource(R.xml.settings);
    }

就这一个代码,欺负我没用过Preferences吧。只有不管了,先看看其他的吧

先选择一个视频播放瞅瞅。

ijkplayer Android 结合seekbar github ijkplayer_ide_03


ok,我们能看到旁边一堆的参数就是视频播放相关。我们看看代码,了解一下。

private static final String TAG = "VideoActivity";

	//播放地址
    private String mVideoPath;
    //播放地址
    private Uri    mVideoUri;

	//系统媒体控制,继承android.widget.MediaController
    private AndroidMediaController mMediaController;
    //自定义的播放器,继承android.widget.MediaController
    private IjkVideoView mVideoView;
    //``````
    private TextView mToastTextView;
    //.......
    private TableLayout mHudView;
    //抽屉,在这里用来装载tracks
    private DrawerLayout mDrawerLayout;
    //容器
    private ViewGroup mRightDrawer;

	//设置,一开始的设置应该能在这里找到用的
    private Settings mSettings;
    //标志位
    private boolean mBackPressed;

上面的东西,差不多了解,下面我们跟着代码,一步一步来。当然视频也要放着走。
我们肯定是先点击的Item然后开始播放的。那么看代码

mFileListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, final int position, final long id) {
                String url = mAdapter.getUrl(position);
                String name = mAdapter.getName(position);
                VideoActivity.intentTo(activity, url, name);
            }
        });

简单的跳转,VideoActivity的相应方法

public static Intent newIntent(Context context, String videoPath, String videoTitle) {
        Intent intent = new Intent(context, VideoActivity.class);
        intent.putExtra("videoPath", videoPath);
        intent.putExtra("videoTitle", videoTitle);
        return intent;
    }

    public static void intentTo(Context context, String videoPath, String videoTitle) {
        context.startActivity(newIntent(context, videoPath, videoTitle));
    }

我们知道我们得到了两个参数,一个地址,一个标题。与上面的变量是不是少了一个撒,应该两地址呀。所以下面肯定有一个判断的。

//判断action参数是什么,选择传入path还是uri
if (!TextUtils.isEmpty(intentAction)) {
            if (intentAction.equals(Intent.ACTION_VIEW)) {
                mVideoPath = intent.getDataString();
            } else if (intentAction.equals(Intent.ACTION_SEND)) {
                mVideoUri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
            }
        }
//将path存入'最近播放'的数据库里,所以这个类就是数据库的操作类
        if (!TextUtils.isEmpty(mVideoPath)) {
            new RecentMediaStorage(this).saveUrlAsync(mVideoPath);
        }

下面就是一些ui的初始化,不看了。找核心

mMediaController = new AndroidMediaController(this, false);
        mMediaController.setSupportActionBar(actionBar);

	// init player
        IjkMediaPlayer.loadLibrariesOnce(null);
        IjkMediaPlayer.native_profileBegin("libijkplayer.so");
        //初始化mediaview
        mVideoView = findViewById(R.id.video_view);
        //设置控制类
        mVideoView.setMediaController(mMediaController);
        //设置视图
        mVideoView.setHudView(mHudView);
        // prefer mVideoPath,判断播放源
        if (mVideoPath != null)
            mVideoView.setVideoPath(mVideoPath);
        else if (mVideoUri != null)
            mVideoView.setVideoURI(mVideoUri);
        else {
            Log.e(TAG, "Null Data Source\n");
            finish();
            return;
        }
        //开始播放
        mVideoView.start();

其他的工具了解,都是配置视频的。看来核心还在IJKPlayerView

if (id == R.id.action_toggle_ratio) {
			//关于视频尺寸,测了一下,有全屏,适应,比例
            int aspectRatio = mVideoView.toggleAspectRatio();
            String aspectRatioText = MeasureHelper.getAspectRatioText(this, aspectRatio);
            mToastTextView.setText(aspectRatioText);
            mMediaController.showOnce(mToastTextView);
            return true;
        } else if (id == R.id.action_toggle_player) {
        //选择播放的没有任何绘制渲染
            int player = mVideoView.togglePlayer();
            String playerText = IjkVideoView.getPlayerText(this, player);
            mToastTextView.setText(playerText);
            mMediaController.showOnce(mToastTextView);
            return true;
        } else if (id == R.id.action_toggle_render) {
            //选择绘制 渲染视频
            int render = mVideoView.toggleRender();
            String renderText = IjkVideoView.getRenderText(this, render);
            mToastTextView.setText(renderText);
            mMediaController.showOnce(mToastTextView);
            return true;
        } else if (id == R.id.action_show_info) {
	        //字面意思
            mVideoView.showMediaInfo();
        } else if (id == R.id.action_show_tracks) {
	        //上面的抽屉装载的内容
            if (mDrawerLayout.isDrawerOpen(mRightDrawer)) {
                Fragment f = getSupportFragmentManager().findFragmentById(R.id.right_drawer);
                if (f != null) {
                    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
                    transaction.remove(f);
                    transaction.commit();
                }
                mDrawerLayout.closeDrawer(mRightDrawer);
            } else {
                Fragment f = TracksFragment.newInstance();
                FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
                transaction.replace(R.id.right_drawer, f);
                transaction.commit();
                mDrawerLayout.openDrawer(mRightDrawer);
            }
        }

换场地,IjkVideoView.java有1000+行,有点惊悚。慢慢来。
从第一行下来,我们看到有状态码。直白的字面意思

private String TAG = "IjkVideoView";
    // settable by the client
    private Uri mUri;
    private Map<String, String> mHeaders;

    // all possible internal states
    private static final int STATE_ERROR = -1;
    private static final int STATE_IDLE = 0;
    private static final int STATE_PREPARING = 1;
    private static final int STATE_PREPARED = 2;
    private static final int STATE_PLAYING = 3;
    private static final int STATE_PAUSED = 4;
    private static final int STATE_PLAYBACK_COMPLETED = 5;
    // mCurrentState is a VideoView object's current state.
    // mTargetState is the state that a method caller intends to reach.
    // For instance, regardless the VideoView object's current state,
    // calling pause() intends to bring the object to a target state
    // of STATE_PAUSED.
    private int mCurrentState = STATE_IDLE;
    private int mTargetState = STATE_IDLE;

这里有个重点

// All the stuff we need for playing and showing a video
    private IRenderView.ISurfaceHolder mSurfaceHolder = null;
    private IMediaPlayer mMediaPlayer = null;

我们看到了一个holderview和一个mediaplayer的接口,过去看看。
IRenderView.java

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;

这很明显就是剪切视频,以什么状况显示。
剩下的自己看一下。
IMediaPlayer是官方封装好的接口,应该就是对视频做出各种操作用,基本就是生命周期的绑定与释放,视频的播放监听等。look,look
开头就是一堆状态码。挪开。
void setDisplay(SurfaceHolder var1);SurfaceHolder熟不熟悉,我们在IRenderView里面是不是见过它,所以这里表名我们可以自己也holder,然后一系列之类的自行封装ijkplayer

void setDataSource(Context var1, Uri var2) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException;

    @TargetApi(14)
    void setDataSource(Context var1, Uri var2, Map<String, String> var3) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException;

    void setDataSource(FileDescriptor var1) throws IOException, IllegalArgumentException, IllegalStateException;

    void setDataSource(String var1) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException;

    String getDataSource();

一堆的source,那就是播放源了。


void prepareAsync() throws IllegalStateException;

    void start() throws IllegalStateException;

    void stop() throws IllegalStateException;

    void pause() throws IllegalStateException;

这里是对视频的操作,
emmmm,分析的东西有点多,先到这。

总结一下ijk的使用

  • 找到uri
  • 本地文件或者远程播放地址
  • 数据源IMediaDataSource 复写改接口实现 file network
  • 设置播放源 imediaplay.setDataSource(IMediaDateSource s)

还有void setDataSource(Context var1, Uri var2, Map<String, String> var3)

  • 设置视频播放渲染视图(TextureView or SurfaceView)
  • 视图继承自定义
  • 继成自IMediaPlayer 各种监听,各种视频控制,各种视频状态etc.
  • 自定义渲染逻辑 RenderView or TextureView
  • 视频控制
  • ijk没有自己的控制层,只有类似于安卓系统本身的视频播放
系统是MediaPlayer,ijk是IMediaPlayer
  • 通过回调监听对视频进行控制
  • 由于没有控制层,所以控制层可以自定义想要的样子

那么ijkplayermediaPlayer用法差不多,为什么ijk这么流行呢?
分析一下:

  • 支持格式:其so库可以自定义视频支持的格式,包括音频(就是扩展)
  • 视图渲染:一般是surfaceView,但我们可以定一个TextureView给ijk
  • 我想不到了~~~~-_-||

放两图

1、大致流程图

ijkplayer Android 结合seekbar github ijkplayer_ide_04


2、使用过程图

ijkplayer Android 结合seekbar github ijkplayer_ide_05


放一个下载example的地址

ijk-example