Android進階:十一、視訊播放器初體驗
上一篇文章我們主要講了視訊播放器開發之前需要準備的一直個知識,TextureView,用於對影象流的處理。這篇文章開始構建一個基礎的視訊播放器。
一、準備工作
在之前的文章已經說過了,播放器也是一個view,我們要在這個view上播放視訊流。所以我們要自定義一個簡單的viewgroup,比如繼承FrameLayout。還出就是佈局簡單,其他控制元件可以往上面新增。大家見過的視訊播放器的控制器都是放在視訊的上方的。這樣就是用FrameLayout佈局是最好的。
class SmallVideoPlayer extends FrameLayout
二、初始化TextureView
這是一個用於承載顯示‘資料流’的View,它不會建立新的視窗來顯示內容。它是將內容流直接投放到View中,並且可以和其它普通View一樣進行移動,旋轉,縮放,動畫等變化。
TextureView初始化方式如下,並且我們這個播放器View要實現其監聽方法:
class SmallVideoPlayer extends FrameLayout implements TextureView.SurfaceTextureListener private void initTextureView() { if (mTextureView == null) { mTextureView = new TextureView(mContext); mTextureView.setSurfaceTextureListener(this); } }
然後我們把這個TextureView新增到我們的視訊播放器的view上,並且設定跟視訊播放器View一樣大小:
private void addTextureView() { removeView(mTextureView); LayoutParams params = new LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER); addView(mTextureView, 0, params); }
三、初始化播放器核心
一個公司想要實現機的播放器核心需要一定的成本,所以大部分公司都選擇使用第三方的核心,比如bilibili開源的ijkplayer。ijkplayer是一個基於FFmpeg的輕量級Android/iOS視訊播放器。FFmpeg的是全球領先的多媒體框架,能夠解碼,編碼,轉碼,複用,解複用,流,過濾器和播放大部分的視訊格式。它提供了錄製、轉換以及流化音視訊的完整解決方案。這裡我們也用它。
在專案module的gradle裡面新增依賴:
implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.3' implementation 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.3' implementation 'tv.danmaku.ijk.media:ijkplayer-armv5:0.8.3' implementation 'tv.danmaku.ijk.media:ijkplayer-x86:0.8.3'
編譯成功之後我們就可以在程式碼裡面用它了,這個也很簡單一般不會出什麼問題。ijplayer裡面提供了一個IMediaPlayer,我們初始化它即可:
private void initMediaPlayer() { if (mMediaPlayer == null) { mMediaPlayer = new IjkMediaPlayer(); mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); } }
準備工作都做好了,我們要在什麼時候開始播放呢?當然是TextureView準備好之後就可以播放了,TextuerView的draw方法中會呼叫TextureLayer layer = getTextureLayer();方法,而getTextureLayer()這個方法中當surface建立成功之後會執行我們實現的介面方法:
if (mListener != null && createNewSurface) { mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight()); }
從上面程式碼可以看出當我們設定了mListener,並且建立surface成功之後會為我們回撥onSurfaceTextureAvailable方法,並傳遞給我們一個mSurface及其寬高。那我們在這個方法裡播放視訊就可以了:
@Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { if (mSurfaceTexture == null) { mSurfaceTexture = surface; openMediaPlayer(); } else { mTextureView.setSurfaceTexture(mSurfaceTexture); } }
我們自己建立一個SurfaceTexture物件儲存TextureView給我傳遞的SurfaceTexture物件,然後開啟視訊播放。如果你自己實現了SurfaceTexture,你也可以用你自己的。
private void openMediaPlayer() { // 螢幕常亮 setKeepScreenOn(true); // 設定dataSource try { mMediaPlayer.setDataSource(mContext.getApplicationContext(), Uri.parse(mUrl)); if (mSurface == null) { mSurface = new Surface(mSurfaceTexture); } mMediaPlayer.setSurface(mSurface); mMediaPlayer.prepareAsync(); } catch (IOException e) { e.printStackTrace(); } }
使用ijplayer播放視訊很簡單,只要為其設定資料來源即可。但是為了能讓視訊顯示出來,也就是能在view上播放出來,我們需要使用Surface。
建立物件private Surface mSurface;,傳入剛才儲存的SurfaceTexture物件:mSurface = new Surface(mSurfaceTexture);,然後把這個surface物件傳遞給播放器即可,最後使用播放器開始播放,注意這個方法是同步的。
完成以上步驟,簡單的視訊播放器就可以完成了。
程式碼:
我們把程式碼進行整理如下:
播放器
public class SmallVideoPlayer extends FrameLayout implements TextureView.SurfaceTextureListener { private TextureView mTextureView; private SurfaceTexture mSurfaceTexture; private Surface mSurface; private AudioManager mAudioManager; private IMediaPlayer mMediaPlayer; private Context mContext; private String mUrl; public SmallVideoPlayer(@NonNull Context context) { this(context, null); } public SmallVideoPlayer(@NonNull Context context, @Nullable AttributeSet attrs) { this(context, attrs, -1); } public SmallVideoPlayer(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.mContext = context; } public void setUp(String url) { mUrl = url; } public void start() { initAudioManager(); initMediaPlayer(); initTextureView(); addTextureView(); } private void initAudioManager() { if (mAudioManager == null) { mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { mAudioManager.requestAudioFocus(new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).build()); } else { mAudioManager.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); } } } private void initMediaPlayer() { if (mMediaPlayer == null) { mMediaPlayer = new IjkMediaPlayer(); mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); } } private void initTextureView() { if (mTextureView == null) { mTextureView = new TextureView(mContext); mTextureView.setSurfaceTextureListener(this); } } private void addTextureView() { removeView(mTextureView); LayoutParams params = new LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER); addView(mTextureView, 0, params); } @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { if (mSurfaceTexture == null) { mSurfaceTexture = surface; openMediaPlayer(); } else { mTextureView.setSurfaceTexture(mSurfaceTexture); } } private void openMediaPlayer() { // 螢幕常亮 setKeepScreenOn(true); // 設定dataSource try { mMediaPlayer.setDataSource(mContext.getApplicationContext(), Uri.parse(mUrl)/*, mHeaders*/); if (mSurface == null) { mSurface = new Surface(mSurfaceTexture); } mMediaPlayer.setSurface(mSurface); mMediaPlayer.prepareAsync(); } catch (IOException e) { e.printStackTrace(); } } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { return mSurfaceTexture == null; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { } }
使用佈局:
在activity中的佈局加上即可
<com.example.ycm.smallvideoplayer.smallvideoplayer.SmallVideoPlayer android:id="@+id/small_video_player" android:layout_width="match_parent" android:layout_height="200dp" />
在activity中獲取播放器view,然後設定一個視訊URL,開啟播放即可。
SmallVideoPlayer mSmallVideoPlayer; = findViewById(R.id.small_video_player); mSmallVideoPlayer.setUp("http://tanzi27niu.cdsb.mobi/wps/wp-content/uploads/2017/05/2017-05-17_17-33-30.mp4"); mSmallVideoPlayer.start();
以上就完成了視訊播放器的初體驗。其實你會發現這是多麼簡單啊。實際上來說一個高階開發人員並沒有比你高明多少,他們比你多的其實只是經驗和思路。所以想要從初中級開發跳躍到高階開發,需要你不斷的思考,獨立實現業務需求。如果給你一個大模組,你能利用你的知識,和網路獲取的資料就能實現,你離高階開發工程師就不遠了。
下一期,我會為大家梳理播放器更多的情況,甚至為視訊播放器新增一個控制器
喜歡本文章,或者我的系列性文章的話,歡迎關注我