1. 程式人生 > >Android使用TextureView播放視訊

Android使用TextureView播放視訊

1.引言

如果你想顯示一段線上視訊或者任意的資料流比如視訊或者OpenGL 場景,你可以用android中的TextureView做到。

1).TextureView的兄弟SurfaceView

應用程式的視訊或者opengl內容往往是顯示在一個特別的UI控制元件中:SurfaceView。SurfaceView的工作方式是建立一個置於應用視窗之後的新視窗。這種 方式的效率非常高,因為SurfaceView視窗重新整理的時候不需要重繪應用程式的視窗(android普通視窗的檢視繪製機制是一層一層的,任何一個子元素或者 是區域性的重新整理都會導致整個檢視結構全部重繪一次,因此效率非常低下,不過滿足普通應用介面的需求還是綽綽有餘),但是SurfaceView也有一些非常  不便的限制。

因為SurfaceView的內容不在應用視窗上,所以不能使用變換(平移、縮放、旋轉等)。也難以放在ListView或者ScrollView中,不能使用UI控制元件的一些特性比如View.setAlpha()

2).為了解決這個問題 Android 4.0中引入了TextureView。

TextureView與SurfaceView相比,TextureView並沒有建立一個單獨的Surface用來繪製,這使得它可以像一般的View一樣執行一些變換操作,設定透明度等。另外,Textureview必須在硬體加速開啟的視窗中。

專案中碰到的問題:1.之前用SurfaceView播放視訊的時候,從圖片切換到播放視訊,會出現黑屏的現象。

                                 2.SurfaceView靈活性沒有TextureView好。

2.專案原始碼

MainActivity.java檔案

<span style="font-size:14px;">package com.example.textureviewvideo;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

import android.app.Activity;
import android.content.res.AssetManager;
import android.graphics.SurfaceTexture;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.TextureView.SurfaceTextureListener;
import android.widget.ImageView;

public class MainActivity extends Activity implements SurfaceTextureListener{
//	private TextureView textureView;
	private MediaPlayer mMediaPlayer;
	private Surface surface;
	
	private ImageView videoImage;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		TextureView textureView=(TextureView) findViewById(R.id.textureview);
		textureView.setSurfaceTextureListener(this);//設定監聽函式  重寫4個方法
		
//		textureView=new TextureViewTest(this);
//		setContentView(textureView);
		videoImage=(ImageView) findViewById(R.id.video_image);
	}
	
	@Override
	public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width,int height) {
		System.out.println("onSurfaceTextureAvailable onSurfaceTextureAvailable");
		surface=new Surface(surfaceTexture);
		new PlayerVideo().start();//開啟一個執行緒去播放視訊
	}
	@Override
	public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width,int height) {
		System.out.println("onSurfaceTextureSizeChanged onSurfaceTextureSizeChanged");
	}
	
	@Override
	public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
		System.out.println("onSurfaceTextureDestroyed onSurfaceTextureDestroyed");
		surfaceTexture=null;
		surface=null;
		mMediaPlayer.stop();
		mMediaPlayer.release();
		return true;
	}
	
	@Override
	public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
//		System.out.println("onSurfaceTextureUpdated onSurfaceTextureUpdated");
	}
	
	private class PlayerVideo extends Thread{
		@Override
		public void run(){
			 try {
				  File file=new File(Environment.getExternalStorageDirectory()+"/ansen.mp4");
				  if(!file.exists()){//檔案不存在
					  copyFile();
				  }
				  mMediaPlayer= new MediaPlayer();
				  mMediaPlayer.setDataSource(file.getAbsolutePath()); 
				  mMediaPlayer.setSurface(surface);
				  mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
				  mMediaPlayer.setOnPreparedListener(new OnPreparedListener() {
					@Override
					public void onPrepared(MediaPlayer mp){
						videoImage.setVisibility(View.GONE);
						mMediaPlayer.start();
					}
				  });
				  mMediaPlayer.prepare();
			  } catch (Exception e) {  
				  e.printStackTrace();
			  }
	    }
	}
	
	public interface PlayerController{
		public void play();
	}
	
	/**
	 * 如果sdcard沒有檔案就複製過去
	 */
	private void copyFile() {
	    AssetManager assetManager = this.getAssets();
	    InputStream in = null;
	    OutputStream out = null;
	    try {
	        in = assetManager.open("ansen.mp4");
	        String newFileName = Environment.getExternalStorageDirectory()+"/ansen.mp4";
	        out = new FileOutputStream(newFileName);
	        byte[] buffer = new byte[1024];
	        int read;
	        while ((read = in.read(buffer)) != -1) {
	            out.write(buffer, 0, read);
	        }
	        in.close();
	        in = null;
	        out.flush();
	        out.close();
	        out = null;
	    } catch (Exception e) {
	        Log.e("tag", e.getMessage());
	    }
	}
}</span>

TextureView建立的時顯示圖片,然後初始化播放器,預載入視訊,如果視訊檔案不存在,從assets下copy一份到sdcard目錄下,視訊載入完畢隱藏圖片,我這邊圖片預設顯示的是android專案自帶的圖片,你們可以根據需求顯示想要的圖片。

activity_main.xml佈局檔案

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextureView
        android:id="@+id/textureview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    
    
    <ImageView
        android:id="@+id/video_image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/ic_launcher"/>
</RelativeLayout>
放了一個TextureView跟一個ImageView  TextureView初始化顯示ImageView...當視訊播放的時候隱藏ImageView,並且切換過去的時候不會出現黑屏。

3.效果圖

 

下載原始碼

其他問題:如果播放線上視訊出現閃屏的問題,需要開啟一個執行緒非同步播放視訊,然後再用handle延時隱藏圖片。我用的是延時300毫秒

	private void sendEmpryMessage(){
		handler.sendEmptyMessageDelayed(0,300);//給主執行緒傳送一個隱藏圖片的訊息
	}

推薦下自己建立的android QQ群:202928390 歡迎大家的加入.

推薦一個Android開發者必關注公眾號,每週都有原創乾貨