• 玩转Android Camera开发(二):使用TextureView和SurfaceTexture预览Camera 基础拍照demo


    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

    百度云盘:



  • 相关阅读:
    观察者模式的新纪录
    字符串和json之间的互相转化
    改变自己:工作几年就该给自己“清零”啦
    观察者模式的记录
    background-position的百分比
    豌豆荚前端面试中的一个算法题
    javascript的原型与继承(2)
    javascript的原型和继承(1)
    百度客户端首页的图片轮换效果
    javascript中闭包的概念
  • 原文地址:https://www.cnblogs.com/liguangsunls/p/6921299.html
Copyright © 2020-2023  润新知