Android 音視頻深刻 十九 使用ijkplayer作個視頻播放器(附源碼下載)

項目地址
https://github.com/979451341/Myijkplayerjava

前段時候我以爲FFmpeg作個視頻播放器好難,雖然播放上沒問題,但暫停還有經過拖動進度條來設置播放進度,這些都即使作獲得,能夠那個延緩。。。。。android

如今學習一下目前移動端最知名的視頻播放器的框架ijkplayer,這個框架他是基於FFmpeg、SDL、還有安卓原生API MediaCodec之類的。他是沒有播放界面的,這個須要咱們去作,因此這個裏我就作個基於ijkplayer的視頻播放器,隨便淺顯的說一下ijkplayer的源碼,關於ijkplayer的源碼之後會專門出一篇博客說一下。git

1.首先了解一下ijkplayer咋用github

我這裏引入ijkplayer是經過添加依賴網絡

implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8'
implementation 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.8'

implementation 'tv.danmaku.ijk.media:ijkplayer-armv5: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'

implementation 'tv.danmaku.ijk.media:ijkplayer-exo:0.8.8'

而後說說ijkplayer是如何播放視頻的框架

ijkplayer每一次播放視頻都是經過建立Mediaplayer,而後賦值到一個接口類上,這裏他建立的時候可以挑選解碼的類型,是由於基於安卓原生API MediaCodec的話是硬解,速度快、兼容差,若是是基於FFmpeg則是軟解,速度慢、兼容好,不過這個兼容問題,由於咱們在引入依賴的時候把各個處理器相應的依賴,因此可使用硬解,兼容問題基本都是手機處理器不一樣產生的。ide

IjkMediaPlayer ijkMediaPlayer = new IjkMediaPlayer();
    ijkMediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_DEBUG);

// //開啓硬解碼
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);
IMediaPlayer mMediaPlayer = null;
mMediaPlayer = ijkMediaPlayer;函數

關於IjkMediaPlayer的源碼我只貼出一個函數的,從下面幾個loadLibrary看出來,他仍是基於FFmpeg、SDL底層實現的。oop

public static void loadLibrariesOnce(IjkLibLoader libLoader) {
    Class var1 = IjkMediaPlayer.class;
    synchronized(IjkMediaPlayer.class) {
        if(!mIsLibLoaded) {
            if(libLoader == null) {
                libLoader = sLocalLibLoader;
            }

            libLoader.loadLibrary("ijkffmpeg");
            libLoader.loadLibrary("ijksdl");
            libLoader.loadLibrary("ijkplayer");
            mIsLibLoaded = true;
        }

    }
}

好了,回到那個接口類的IMediaPlayer,源碼很少貼出來看一下,經過這些接口函數咱們均可以知道這個ijkplayer如何使用咱們都有了一個底,什麼setDataSource、setDisplay,設置播放源、設置播放的屏幕信息。還有start、stop、pause,視頻播放的開始、中止、暫停,還有一大堆的接口,這些都是爲了監聽播放器的狀態post

public interface IMediaPlayer {
。。。。。。

void setDisplay(SurfaceHolder var1);

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();

void prepareAsync() throws IllegalStateException;

void start() throws IllegalStateException;

void stop() throws IllegalStateException;

void pause() throws IllegalStateException;

void setScreenOnWhilePlaying(boolean var1);

int getVideoWidth();

int getVideoHeight();

boolean isPlaying();

void seekTo(long var1) throws IllegalStateException;

long getCurrentPosition();

long getDuration();

void release();

void reset();

void setVolume(float var1, float var2);

int getAudioSessionId();

MediaInfo getMediaInfo();

/** @deprecated */
@Deprecated
void setLogEnabled(boolean var1);

/** @deprecated */
@Deprecated
boolean isPlayable();

void setOnPreparedListener(IMediaPlayer.OnPreparedListener var1);

void setOnCompletionListener(IMediaPlayer.OnCompletionListener var1);

void setOnBufferingUpdateListener(IMediaPlayer.OnBufferingUpdateListener var1);

void setOnSeekCompleteListener(IMediaPlayer.OnSeekCompleteListener var1);

void setOnVideoSizeChangedListener(IMediaPlayer.OnVideoSizeChangedListener var1);

void setOnErrorListener(IMediaPlayer.OnErrorListener var1);

void setOnInfoListener(IMediaPlayer.OnInfoListener var1);

void setOnTimedTextListener(IMediaPlayer.OnTimedTextListener var1);

void setAudioStreamType(int var1);

/** @deprecated */
@Deprecated
void setKeepInBackground(boolean var1);

int getVideoSarNum();

int getVideoSarDen();

/** @deprecated */
@Deprecated
void setWakeMode(Context var1, int var2);

void setLooping(boolean var1);

boolean isLooping();

ITrackInfo[] getTrackInfo();

void setSurface(Surface var1);

void setDataSource(IMediaDataSource var1);

public interface OnTimedTextListener {
    void onTimedText(IMediaPlayer var1, IjkTimedText var2);
}

public interface OnInfoListener {
    boolean onInfo(IMediaPlayer var1, int var2, int var3);
}

public interface OnErrorListener {
    boolean onError(IMediaPlayer var1, int var2, int var3);
}

public interface OnVideoSizeChangedListener {
    void onVideoSizeChanged(IMediaPlayer var1, int var2, int var3, int var4, int var5);
}

public interface OnSeekCompleteListener {
    void onSeekComplete(IMediaPlayer var1);
}

public interface OnBufferingUpdateListener {
    void onBufferingUpdate(IMediaPlayer var1, int var2);
}

public interface OnCompletionListener {
    void onCompletion(IMediaPlayer var1);
}

public interface OnPreparedListener {
    void onPrepared(IMediaPlayer var1);
}

}

2.寫界面

全屏播放,潛入式

//正真的全屏,隱藏了狀態欄、AtionBar、導航欄
@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    if (hasFocus && Build.VERSION.SDK_INT >= 19) {
        View decorView = getWindow().getDecorView();
        decorView.setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
    }
}

而後上下兩個欄目,負責一些播放器的控制,頂部負責設置返回、設置按鈕,底部須要設置播放/暫停按鈕、播放進度條、中止按鈕

<com.example.zth.two.VideoPlayerIJK
    android:id="@+id/ijk_player"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

<include
    android:id="@+id/include_play_top"
    layout="@layout/include_play_top"
    android:layout_alignParentTop="true"
    android:layout_width="match_parent"
    android:layout_height="50dp" />

<include
    android:id="@+id/include_play_bottom"
    layout="@layout/include_play_bottom"
    android:layout_alignParentBottom="true"
    android:layout_width="match_parent"
    android:layout_height="50dp" />

關於上下兩個欄目在用戶觀看視頻時須要隱藏,在用戶點擊屏幕則顯示兩個欄目,供用戶使用
這個則是須要經過計時器來完成記錄目前距離上一次用戶點擊屏幕的時間,若是視頻超過3秒,則隱藏欄目,若是點擊屏幕則恢復,關於隱藏和恢復使用Animation來完成。

timer = new Timer();
    timerTask = new TimerTask() {
        @Override
        public void run() {
            long t = System.currentTimeMillis();
            if (t - time > 3000 && menu_visible) {
                time = t;
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        Animation animation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.move_bottom);
                        rl_bottom.startAnimation(animation);
                        Animation animation_top = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.move_top);
                        rl_top.startAnimation(animation_top);
                        menu_visible = false;
                    }
                });
            }

        }
    };

還須要加載框,這個在視頻加載完的接口回調裏隱藏他

<ProgressBar
        android:id="@+id/pb_loading"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_centerInParent="true"
        android:layout_marginTop="60dp"
        android:indeterminate="false"
        android:indeterminateDrawable="@drawable/video_loading"
        android:padding="5dp" />

3.播放實現

這裏我是使用了一個另外一個博主封裝的ijkplayer的類

說一下這個類的運行過程

一開始建立MediaPlayer作一些配置,賦值給一個接口類,而且暴露了接口

IjkMediaPlayer ijkMediaPlayer = new IjkMediaPlayer();
    ijkMediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_DEBUG);

// //開啓硬解碼
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);

mMediaPlayer = ijkMediaPlayer;

    if (listener != null) {
        mMediaPlayer.setOnPreparedListener(listener);
        mMediaPlayer.setOnInfoListener(listener);
        mMediaPlayer.setOnSeekCompleteListener(listener);
        mMediaPlayer.setOnBufferingUpdateListener(listener);
        mMediaPlayer.setOnErrorListener(listener);
    }

而後設置播放源,這個播放源可以是本地視頻路徑、網絡視頻url、還能夠是網絡RTMP推流url,還要講SurfaceView的配置信息給他

try {
        mMediaPlayer.setDataSource(mPath);
    } catch (IOException e) {
        e.printStackTrace();
    }
    //給mediaPlayer設置視圖
    mMediaPlayer.setDisplay(surfaceView.getHolder());

    mMediaPlayer.prepareAsync();

想開始播放就調用IMediaPlayer的那些控制函數

public void start() {
    if (mMediaPlayer != null) {
        mMediaPlayer.start();
    }
}

還有關於activity的生命週期控制,其中native_profileEnd就至關於很智能的暫停,當屏幕回的時候就繼續播放視頻。

@Override
protected void onStop() {
    IjkMediaPlayer.native_profileEnd();
    handler.removeCallbacksAndMessages(null);
    super.onStop();

}

結束Activity的時候就中止播放視頻並釋放資源

@Override
protected void onDestroy() {
    if (ijkPlayer != null) {
        ijkPlayer.stop();
        ijkPlayer.release();
        ijkPlayer = null;
    }

    super.onDestroy();
}

最後還有播放進度,我這裏是經過handler本身調用本身循環更新播放時間顯示和進度條

handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_REFRESH:
                    if (ijkPlayer.isPlaying()) {
                        refresh();
                        handler.sendEmptyMessageDelayed(MSG_REFRESH, 1000);
                    }

                    break;
            }

        }
    };

private void refresh() {
    long current = ijkPlayer.getCurrentPosition() / 1000;
    long duration = ijkPlayer.getDuration() / 1000;
    Log.v("zzw", current + " " + duration);
    long current_second = current % 60;
    long current_minute = current / 60;
    long total_second = duration % 60;
    long total_minute = duration / 60;
    String time = current_minute + ":" + current_second + "/" + total_minute + ":" + total_second;
    tvTime.setText(time);
    if(duration != 0){
        seekBar.setProgress((int) (current * 100 / duration));
    }

}

在用戶拖動進度條的時候取消handler發送消息,拖動結束再接續更新播放進度

seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
            //進度改變
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
            //開始拖動

            handler.removeCallbacksAndMessages(null);

        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
            //中止拖動
            ijkPlayer.seekTo(ijkPlayer.getDuration() * seekBar.getProgress() / 100);
            handler.sendEmptyMessageDelayed(MSG_REFRESH, 100);
        }
    });

關於播放網絡視頻和網絡RTMP推流,播放自己沒問題就是不能獲取視頻時長和視頻當前時間,不過暫停和中止還有效。

還有就是若是想要從新播放須要從新setVideoPath而後start

看看效果
本地視頻播放

播放RTMP推流

播放網絡視頻

ijkplayer的使用看起來是否是很簡單,可是尚未實現小窗口和全屏之間的切換。。。。。。這兩天我再看看ijkplayer,再和各位說說
博客首發地址
http://blog.csdn.net/z979451341

相關文章
相關標籤/搜索