引自:http://www.ithao123.cn/content-8733143.html
最近發現視頻直播類應用層出不窮,比如233手游直播,藍鯨直播,微錄客等等什么的,連android界大神老羅也在開發手游錄制類的應用,這里面的技術含量也是挺高的,需要了解android 系統的UI渲染機制以及很多系統底層的知識,而我最近也在想好好研究研究android 的UI渲染機制並以此作為深入了解android的入口
好了,在講代碼實現之前,我先講講TextureView, SurfaceTexture,OpenGL ES都是些什么鬼東西,我又是怎么使用這幾個東西來顯示一個視頻的。
TextureView 顧名思義也就是一個繼承了View的一個View控件而已,官網的解釋是這樣的:
A TextureView can be used to display a content stream. Such a content stream can for instance be a video or an OpenGL scene. The content stream can come from the application’s process as well as a remote process.
它能夠去顯示一個內容流,比如視頻流,OpenGL渲染的場景等。這些流可以是本地程序進程也可以是遠程進程流,有點繞,我的理解就是,比如既可以是本地視頻流,也可以是網絡視頻流。
注意的是: TextureView 采用的是硬件加速器去渲染,就類似視頻的硬解碼跟軟解碼,一個靠的是GPU解碼,一個靠CPU解碼。
那么如何去使用這個TextureView呢?
OK,現在SurfaceTexture就要上場了,從這兩個類的命名我們就知道TextureView重點是View,而SurfaceTexture 重點是Texture它的官網解釋:
Captures frames from an image stream as an OpenGL ES texture.The image stream may come from either camera preview or video decode.
也就是說它能捕獲一個圖像流的一幀來作為OpenGL 的texture也就是紋理。這個圖片流主要是來自相機的預覽或視頻的解碼。(我想這個特性是不應該可以用來做很多事了)。
到這兒,texture也有了,那么OpenGL也就可以出來干活了,它能夠綁定texture並將其在TextureView上一幀一幀的給繪制出來,就形成了我們所看到視頻圖像了(具體關於SurfaceTexture、TextureView大家可以參考這里)
說了這么,是該來點代碼來瞧瞧了,好的代碼就跟讀文學小說一樣,那樣的優美,並不是說我寫的代碼很優美啦,這只是追求。。。
代碼
先從MainActicity主類開始:
public class MainActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener, MediaPlayer.OnPreparedListener{ /**本地視頻的路徑*/ public String videoPath = Environment.getExternalStorageDirectory().getPath()+"/aoa.mkv"; private TextureView textureView; private MediaPlayer mediaPlayer; /** * 視頻繪制前的配置就發生在這個對象所在類中. * 真正的繪制工作則在它的子類中VideoTextureSurfaceRenderer */ private TextureSurfaceRenderer videoRenderer; private int surfaceWidth; private int surfaceHeight; private Surface surface; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textureView = (TextureView) findViewById(R.id.id_textureview); //注冊一個SurfaceTexture,用於監聽SurfaceTexure textureView.setSurfaceTextureListener(this); } /** * 播放視頻的入口,當SurfaceTexure可得到時被調用 */ private void playVideo() { if (mediaPlayer == null) { videoRenderer = new VideoTextureSurfaceRenderer(this, textureView.getSurfaceTexture(), surfaceWidth, surfaceHeight); surface = new Surface(videoRenderer.getSurfaceTexture()); initMediaPlayer(); } } private void initMediaPlayer() { this.mediaPlayer = new MediaPlayer(); try { mediaPlayer.setDataSource(videoPath); mediaPlayer.setSurface(surface); mediaPlayer.prepareAsync(); mediaPlayer.setOnPreparedListener(this); mediaPlayer.setLooping(true); } catch (IllegalArgumentException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (SecurityException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (IllegalStateException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } @Override public void onPrepared(MediaPlayer mp) { try { if (mp != null) { mp.start(); //視頻開始播放了 } } catch (IllegalStateException e) { e.printStackTrace(); } } @Override protected void onResume() { super.onResume(); if (textureView.isAvailable()) { playVideo(); } } @Override protected void onPause() { super.onPause(); if (videoRenderer != null) { videoRenderer.onPause(); } if (mediaPlayer != null) { mediaPlayer.release(); mediaPlayer =null; } } @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { surfaceWidth = width; surfaceHeight = height; playVideo(); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { return false; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { } }
這就是程序的入口類,關於Mediaplayer是怎么播放時視頻源的,我就在此就不說了,這里面其實還有很多東西的,大家可以自行的查查。有一點我需要說說就是,一般MediaPlayer.setSurface(param)
里面的參數param
都是SurfaceView.SurfaceHolder,而我這兒直接用的是Surface
(關於Surface可以參考這里),我這個視頻播放與其它的視頻播放的區別就在此。這篇先暫時寫在這兒啦,后續核心的繪制工作,就后面有空就再寫了。上面寫的如果有什么問題希望大家能多多指點,感激不盡!