上一篇文章咱們主要講了視頻播放器開發以前須要準備的一直個知識,TextureView,用於對圖像流的處理。這篇文章開始構建一個基礎的視頻播放器。java
在以前的文章已經說過了,播放器也是一個view,咱們要在這個view上播放視頻流。因此咱們要自定義一個簡單的viewgroup,好比繼承FrameLayout。還出就是佈局簡單,其餘控件能夠往上面添加。你們見過的視頻播放器的控制器都是放在視頻的上方的。這樣就是用FrameLayout佈局是最好的。android
class SmallVideoPlayer extends FrameLayout 複製代碼
這是一個用於承載顯示‘數據流’的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的是全球領先的多媒體框架,可以解碼,編碼,轉碼,複用,解複用,流,過濾器和播放大部分的視頻格式。它提供了錄製、轉換以及流化音視頻的完整解決方案。這裏咱們也用它。bash
在項目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及其寬高。那咱們在這個方法裏播放視頻就能夠了:ide
@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();
複製代碼
以上就完成了視頻播放器的初體驗。其實你會發現這是多麼簡單啊。實際上來講一個高級開發人員並無比你高明多少,他們比你多的其實只是經驗和思路。因此想要從初中級開發跳躍到高級開發,須要你不斷的思考,獨立實現業務需求。若是給你一個大模塊,你能利用你的知識,和網絡獲取的資料就能實現,你離高級開發工程師就不遠了。
下一期,我會爲你們梳理播放器更多的狀況,甚至爲視頻播放器添加一個控制器
喜歡本文章,或者個人系列性文章的話,歡迎關注我