1. 程式人生 > >玩轉Android Camera開發(二):使用TextureView和SurfaceTexture預覽Camera 基礎拍照demo

玩轉Android Camera開發(二):使用TextureView和SurfaceTexture預覽Camera 基礎拍照demo

處理 pict all sans 格式 facet ace ets nth

Google自Android4.0出了TextureView。為什麽推出呢?就是為了彌補Surfaceview的不足。另外一方面也是為了平衡GlSurfaceView。當然這是本人揣度的。

關於TextureView、Surfaceview、SurfaceTexture、GLSurfaceView的關系,待咱家推出GLSurfaceview預覽Camera後再專門分析。

本文主要介紹使用TextureView預覽Camera。

事實上關於怎樣用TextureView預覽Camera,官網已經給出了demo,參見這裏。另外,鏈接1 鏈接2也給出了完整的預覽Camera的demo。但都是一堆東西染在一塊。本文就利用前文 搭建的一個輕量級的Camera框架來高速替換掉Surfaceview。由於用Surfaceview預覽的話傳一個SurfaceHolder進去,用Textureview預覽的話須要傳進去一個SurfaceTexture。其它的Camera流程不變。

一、新建CameraTextureView類繼承TextureView,並實現TextureView.SurfaceTextureListener接口。

實現這個接口就像實現SurfaceHolder.Callback,最基本的目的是在SurfaceTexture準備好後能夠知道,也即onSurfaceTextureAvailable這個函數。

CameraTextureView.java

package org.yanzi.camera.preview;

import org.yanzi.camera.CameraInterface;

import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.SurfaceTexture;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;

public class CameraTextureView extends TextureView implements TextureView.SurfaceTextureListener {
	private static final String TAG = "yanzi";
	Context mContext;
	SurfaceTexture mSurface;
	public CameraTextureView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
		mContext = context;
		this.setSurfaceTextureListener(this);
	}
	@Override
	public void onSurfaceTextureAvailable(SurfaceTexture surface, int width,
			int height) {
		// TODO Auto-generated method stub
		Log.i(TAG, "onSurfaceTextureAvailable...");
		mSurface = surface;
//		CameraInterface.getInstance().doStartPreview(surface, 1.33f);
	}
	@Override
	public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
		// TODO Auto-generated method stub
		Log.i(TAG, "onSurfaceTextureDestroyed...");
		CameraInterface.getInstance().doStopCamera();
		return true;
	}
	@Override
	public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width,
			int height) {
		// TODO Auto-generated method stub
		Log.i(TAG, "onSurfaceTextureSizeChanged...");
	}
	@Override
	public void onSurfaceTextureUpdated(SurfaceTexture surface) {
		// TODO Auto-generated method stub
		Log.i(TAG, "onSurfaceTextureUpdated...");
		
	}
	
	/* 讓Activity能得到TextureView的SurfaceTexture
	 * @see android.view.TextureView#getSurfaceTexture()
	 */
	public SurfaceTexture _getSurfaceTexture(){
		return mSurface;
	}
}
二、在布局文件中把它加上即可了,由於他的父類就是View。當成一般的View即可

<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"
    tools:context=".CameraActivity" >
    <FrameLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" >
        <org.yanzi.camera.preview.CameraTextureView
            android:id="@+id/camera_textureview"
            android:layout_width="0dip"
            android:layout_height="0dip" />
    </FrameLayout>
    <ImageButton
        android:id="@+id/btn_shutter"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/btn_shutter_background"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true" 
        android:layout_marginBottom="10dip"/>
</RelativeLayout>

三、在CameraInterface裏,我封裝了兩個函數:

/**使用Surfaceview開啟預覽
	 * @param holder
	 * @param previewRate
	 */
	public void doStartPreview(SurfaceHolder holder, float previewRate){
		Log.i(TAG, "doStartPreview...");
		if(isPreviewing){
			mCamera.stopPreview();
			return;
		}
		if(mCamera != null){
			try {
				mCamera.setPreviewDisplay(holder);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			initCamera(previewRate);
		}


	}
	/**使用TextureView預覽Camera
	 * @param surface
	 * @param previewRate
	 */
	public void doStartPreview(SurfaceTexture surface, float previewRate){
		Log.i(TAG, "doStartPreview...");
		if(isPreviewing){
			mCamera.stopPreview();
			return;
		}
		if(mCamera != null){
			try {
				mCamera.setPreviewTexture(surface);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			initCamera(previewRate);
		}
		
	}
分別相應Surfaceview和TextureView預覽。能夠看到就是傳進來的參數不一樣,initCamera()的東西都一樣。

	private void initCamera(float previewRate){
		if(mCamera != null){

			mParams = mCamera.getParameters();
			mParams.setPictureFormat(PixelFormat.JPEG);//設置拍照後存儲的圖片格式
//			CamParaUtil.getInstance().printSupportPictureSize(mParams);
//			CamParaUtil.getInstance().printSupportPreviewSize(mParams);
			//設置PreviewSize和PictureSize
			Size pictureSize = CamParaUtil.getInstance().getPropPictureSize(
					mParams.getSupportedPictureSizes(),previewRate, 800);
			mParams.setPictureSize(pictureSize.width, pictureSize.height);
			Size previewSize = CamParaUtil.getInstance().getPropPreviewSize(
					mParams.getSupportedPreviewSizes(), previewRate, 800);
			mParams.setPreviewSize(previewSize.width, previewSize.height);

			mCamera.setDisplayOrientation(90);

//			CamParaUtil.getInstance().printSupportFocusMode(mParams);
			List<String> focusModes = mParams.getSupportedFocusModes();
			if(focusModes.contains("continuous-video")){
				mParams.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
			}
			mCamera.setParameters(mParams);	
			mCamera.startPreview();//開啟預覽



			isPreviewing = true;
			mPreviwRate = previewRate;

			mParams = mCamera.getParameters(); //又一次get一次
			Log.i(TAG, "終於設置:PreviewSize--With = " + mParams.getPreviewSize().width
					+ "Height = " + mParams.getPreviewSize().height);
			Log.i(TAG, "終於設置:PictureSize--With = " + mParams.getPictureSize().width
					+ "Height = " + mParams.getPictureSize().height);
		}
	}

四、在Activity裏,依然開一個線程去open Camera:

		Thread openThread = new Thread(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				CameraInterface.getInstance().doOpenCamera(CameraActivity.this);
			}
		};
		openThread.start();

在Camera Open完的回調裏開預覽:

	@Override
	public void cameraHasOpened() {
		// TODO Auto-generated method stub
		SurfaceTexture surface = textureView._getSurfaceTexture();
		CameraInterface.getInstance().doStartPreview(surface, previewRate);
	}

之後就能正常運行了。能夠看到與前文Surfaceview預覽Camera 修改非常之小。



幾個註意事項:

1、TextureView是Android 4.0之後增加的,低版本號麽這個類。TextureView必須工作在開啟硬件加速的環境中。也即配置文件中Activity的設置項裏:android:hardwareAccelerated="true" 默認的這個屬性就是true,因此不用再寫了。但假設寫成false,能夠看到onSurfaceTextureAvailable()這個回調就進不來了。TextureView沒有了SurfaceTexture還玩個屁啊。

2、本文demo打開camera並預覽的正常log是:

	Line 417: 06-22 12:37:43.682 I/yanzi   ( 4917): Camera open....
	Line 489: 06-22 12:37:43.758 I/yanzi   ( 4917): onSurfaceTextureAvailable...
	Line 533: 06-22 12:37:43.819 I/yanzi   ( 4917): Camera open over....
	Line 535: 06-22 12:37:43.819 I/yanzi   ( 4917): doStartPreview...
	Line 537: 06-22 12:37:43.825 I/yanzi   ( 4917): PictureSize : w = 1280h = 720
	Line 539: 06-22 12:37:43.825 I/yanzi   ( 4917): PreviewSize:w = 800h = 448
	Line 555: 06-22 12:37:43.874 I/yanzi   ( 4917): 終於設置:PreviewSize--With = 800Height = 448
	Line 557: 06-22 12:37:43.874 I/yanzi   ( 4917): 終於設置:PictureSize--With = 1280Height = 720
	Line 577: 06-22 12:37:44.106 I/yanzi   ( 4917): onSurfaceTextureUpdated...
	Line 579: 06-22 12:37:44.138 I/yanzi   ( 4917): onSurfaceTextureUpdated...
	Line 583: 06-22 12:37:44.169 I/yanzi   ( 4917): onSurfaceTextureUpdated...
	Line 585: 06-22 12:37:44.220 I/yanzi   ( 4917): onSurfaceTextureUpdated...
	Line 587: 06-22 12:37:44.253 I/yanzi   ( 4917): onSurfaceTextureUpdated...

測試手機為中興Geek。這個手機Camera還是非常牛逼的,比手裏的華為G700強。就是偶爾會連不上Camera Service,汗。

從log能夠看到。onSurfaceTextureAvailable這個回調須要一定時間。Camera.open()這句話用了130多ms。但有兩點跟Surfaceview不同。第一,TextureView創建過程中沒有進到onSurfaceTextureSizeChanged()這個函數裏。而SurfaceView在創建過程中,從無到有的時候會進到大小發生變化回調裏。

第二。onSurfaceTextureUpdated()這個函數每上來一幀數據,這塊就進來一次

這是跟Surfaceview相比。最偉大的一個地方。

通過這個接口。能夠將上來的SurfaceTexture送給OpenGL再去處理。

這個回調是實時的。而非用Camera的PreviewCallback這樣的2次回調的方式。

從時間看,基本上每32ms左右上來一幀數據,即每秒30幀。跟本手機的Camera的性能吻合。

3、Camera再運行startPreview時必須保證TextureView的SurfaceTexture上來了,假設由於一些性能原因onSurfaceTextureAvailable()這個回調上不來就開預覽,就開不了的。假設發生這樣的情況,就在onSurfaceTextureAvailable()回調裏運行open和startPreview操作,保證萬無一失。

4、TextureView本身就有getSurfaceTexture()這個函數,我又封裝了個:

	/* 讓Activity能得到TextureView的SurfaceTexture
	 * @see android.view.TextureView#getSurfaceTexture()
	 */
	public SurfaceTexture _getSurfaceTexture(){
		return mSurface;
	}
這裏的mSurface就是onSurfaceTextureAvailable()回調裏傳上來的SurfaceTexture。測試證明。開預覽時直接調

textureView.getSurfaceTexture(),把它傳給Camera: mCamera.setPreviewTexture(surface);也是能正常預覽的。

可是推薦使用前者,原因見官方上的這段話:

A TextureView‘s SurfaceTexture can be obtained either by invoking getSurfaceTexture() or by using a TextureView.SurfaceTextureListener. It is important to know that a SurfaceTexture is available only after the TextureView is attached to a window (and onAttachedToWindow() has been invoked.) It is therefore highly recommended you use a listener to be notified when the SurfaceTexture becomes available.

兩種方式獲得SurfaceTexture,推薦使用監聽。由於僅僅有在TextureView運行完onAttachedToWindow時,它的tSurfaceTexture才上來。

5、SurfaceTexture和TextureView的關系:

Using a TextureView is simple: all you need to do is get its SurfaceTexture. The SurfaceTexture can then be used to render content

假設說TextureView是一幅畫的話,那SurfaceTexture就是畫布,真正渲染的載體是SurfaceTexture。

6、TextureView能夠像一般View運行各種變化。當中有個textureView.setAlpha(1.0f);默認不寫這句話,它的alpha也是1.0f,即不透明。

假設設成透明0.0f,能夠看到啥都看不到了,這一點跟Surfaceview剛好相反。

Surfaceview的SurfaceHolder一般要設一下Transparent即透明。但TextureView由於是個view,不論什麽一個png的照片透明度設成0肯定啥都看不到。

7、假設覺得預覽個Camera這就是TextureView和SurfaceTexture的使命的話,就大錯特錯了,真正用意是和OpenGL無縫連接。

--------------------本文系原創。轉載請註明作者yanzi1225627

版本號號:PlayCamera_V2.0.0[2014-6-22].zip

CSDN下載鏈接:http://download.csdn.net/detail/yanzi1225627/7540903

百度雲盤:



玩轉Android Camera開發(二):使用TextureView和SurfaceTexture預覽Camera 基礎拍照demo