• 玩转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

     1 <span style="font-family:Comic Sans MS;font-size:18px;">package org.yanzi.camera.preview;  
     2   
     3 import org.yanzi.camera.CameraInterface;  
     4   
     5 import android.content.Context;  
     6 import android.graphics.PixelFormat;  
     7 import android.graphics.SurfaceTexture;  
     8 import android.util.AttributeSet;  
     9 import android.util.Log;  
    10 import android.view.SurfaceHolder;  
    11 import android.view.SurfaceView;  
    12 import android.view.TextureView;  
    13   
    14 public class CameraTextureView extends TextureView implements TextureView.SurfaceTextureListener {  
    15     private static final String TAG = "yanzi";  
    16     Context mContext;  
    17     SurfaceTexture mSurface;  
    18     public CameraTextureView(Context context, AttributeSet attrs) {  
    19         super(context, attrs);  
    20         // TODO Auto-generated constructor stub  
    21         mContext = context;  
    22         this.setSurfaceTextureListener(this);  
    23     }  
    24     @Override  
    25     public void onSurfaceTextureAvailable(SurfaceTexture surface, int width,  
    26             int height) {  
    27         // TODO Auto-generated method stub  
    28         Log.i(TAG, "onSurfaceTextureAvailable...");  
    29         mSurface = surface;  
    30 //      CameraInterface.getInstance().doStartPreview(surface, 1.33f);  
    31     }  
    32     @Override  
    33     public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {  
    34         // TODO Auto-generated method stub  
    35         Log.i(TAG, "onSurfaceTextureDestroyed...");  
    36         CameraInterface.getInstance().doStopCamera();  
    37         return true;  
    38     }  
    39     @Override  
    40     public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width,  
    41             int height) {  
    42         // TODO Auto-generated method stub  
    43         Log.i(TAG, "onSurfaceTextureSizeChanged...");  
    44     }  
    45     @Override  
    46     public void onSurfaceTextureUpdated(SurfaceTexture surface) {  
    47         // TODO Auto-generated method stub  
    48         Log.i(TAG, "onSurfaceTextureUpdated...");  
    49           
    50     }  
    51       
    52     /* 让Activity能得到TextureView的SurfaceTexture 
    53      * @see android.view.TextureView#getSurfaceTexture() 
    54      */  
    55     public SurfaceTexture _getSurfaceTexture(){  
    56         return mSurface;  
    57     }  
    58 }  
    59 </span>  

    二、在布局文件里把它加上就行了,因为他的父类就是View,当成一般的View就行

     
     1 <span style="font-family:Comic Sans MS;font-size:18px;"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
     2     xmlns:tools="http://schemas.android.com/tools"  
     3     android:layout_width="match_parent"  
     4     android:layout_height="match_parent"  
     5     tools:context=".CameraActivity" >  
     6     <FrameLayout  
     7         android:layout_width="wrap_content"  
     8         android:layout_height="wrap_content" >  
     9         <org.yanzi.camera.preview.CameraTextureView  
    10             android:id="@+id/camera_textureview"  
    11             android:layout_width="0dip"  
    12             android:layout_height="0dip" />  
    13     </FrameLayout>  
    14     <ImageButton  
    15         android:id="@+id/btn_shutter"  
    16         android:layout_width="wrap_content"  
    17         android:layout_height="wrap_content"  
    18         android:background="@drawable/btn_shutter_background"  
    19         android:layout_alignParentBottom="true"  
    20         android:layout_centerHorizontal="true"   
    21         android:layout_marginBottom="10dip"/>  
    22 </RelativeLayout>  
    23 </span>  

    三、在CameraInterface里,我封装了两个函数:

     
     1 <span style="font-family:Comic Sans MS;font-size:18px;">/**使用Surfaceview开启预览 
     2      * @param holder 
     3      * @param previewRate 
     4      */  
     5     public void doStartPreview(SurfaceHolder holder, float previewRate){  
     6         Log.i(TAG, "doStartPreview...");  
     7         if(isPreviewing){  
     8             mCamera.stopPreview();  
     9             return;  
    10         }  
    11         if(mCamera != null){  
    12             try {  
    13                 mCamera.setPreviewDisplay(holder);  
    14             } catch (IOException e) {  
    15                 // TODO Auto-generated catch block  
    16                 e.printStackTrace();  
    17             }  
    18             initCamera(previewRate);  
    19         }  
    20   
    21   
    22     }  
    23     /**使用TextureView预览Camera 
    24      * @param surface 
    25      * @param previewRate 
    26      */  
    27     public void doStartPreview(SurfaceTexture surface, float previewRate){  
    28         Log.i(TAG, "doStartPreview...");  
    29         if(isPreviewing){  
    30             mCamera.stopPreview();  
    31             return;  
    32         }  
    33         if(mCamera != null){  
    34             try {  
    35                 mCamera.setPreviewTexture(surface);  
    36             } catch (IOException e) {  
    37                 // TODO Auto-generated catch block  
    38                 e.printStackTrace();  
    39             }  
    40             initCamera(previewRate);  
    41         }  
    42           
    43     }</span>  

    分别对应Surfaceview和TextureView预览。可以看到就是传进来的参数不一样,initCamera()的东西都一样。

     
     1 <span style="font-family:Comic Sans MS;font-size:18px;">    private void initCamera(float previewRate){  
     2         if(mCamera != null){  
     3   
     4             mParams = mCamera.getParameters();  
     5             mParams.setPictureFormat(PixelFormat.JPEG);//设置拍照后存储的图片格式  
     6 //          CamParaUtil.getInstance().printSupportPictureSize(mParams);  
     7 //          CamParaUtil.getInstance().printSupportPreviewSize(mParams);  
     8             //设置PreviewSize和PictureSize  
     9             Size pictureSize = CamParaUtil.getInstance().getPropPictureSize(  
    10                     mParams.getSupportedPictureSizes(),previewRate, 800);  
    11             mParams.setPictureSize(pictureSize.width, pictureSize.height);  
    12             Size previewSize = CamParaUtil.getInstance().getPropPreviewSize(  
    13                     mParams.getSupportedPreviewSizes(), previewRate, 800);  
    14             mParams.setPreviewSize(previewSize.width, previewSize.height);  
    15   
    16             mCamera.setDisplayOrientation(90);  
    17   
    18 //          CamParaUtil.getInstance().printSupportFocusMode(mParams);  
    19             List<String> focusModes = mParams.getSupportedFocusModes();  
    20             if(focusModes.contains("continuous-video")){  
    21                 mParams.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);  
    22             }  
    23             mCamera.setParameters(mParams);   
    24             mCamera.startPreview();//开启预览  
    25   
    26   
    27   
    28             isPreviewing = true;  
    29             mPreviwRate = previewRate;  
    30   
    31             mParams = mCamera.getParameters(); //重新get一次  
    32             Log.i(TAG, "最终设置:PreviewSize--With = " + mParams.getPreviewSize().width  
    33                     + "Height = " + mParams.getPreviewSize().height);  
    34             Log.i(TAG, "最终设置:PictureSize--With = " + mParams.getPictureSize().width  
    35                     + "Height = " + mParams.getPictureSize().height);  
    36         }  
    37     }</span>  

    四、在Activity里,依旧开一个线程去open Camera:

     
    1 <span style="font-family:Comic Sans MS;font-size:18px;">        Thread openThread = new Thread(){  
    2             @Override  
    3             public void run() {  
    4                 // TODO Auto-generated method stub  
    5                 CameraInterface.getInstance().doOpenCamera(CameraActivity.this);  
    6             }  
    7         };  
    8         openThread.start();</span>  

    在Camera Open完的回调里开预览:

    1 <span style="font-family:Comic Sans MS;font-size:18px;">    @Override  
    2     public void cameraHasOpened() {  
    3         // TODO Auto-generated method stub  
    4         SurfaceTexture surface = textureView._getSurfaceTexture();  
    5         CameraInterface.getInstance().doStartPreview(surface, previewRate);  
    6     }</span>  

    之后就能正常运行了,可以看到与前文Surfaceview预览Camera 改动非常之小。

     

    几个注意事项:

    1、TextureView是Android 4.0之后加入的,低版本么这个类。TextureView必须工作在开启硬件加速的环境中,也即配置文件里Activity的设置项里:android:hardwareAccelerated="true" 默认的这个属性就是true,因此不用再写了。但如果写成false,可以看到onSurfaceTextureAvailable()这个回调就进不来了,TextureView没有了SurfaceTexture还玩个屁啊。

    2、本文demo打开camera并预览的正常log是:

     
     1 <span style="font-family:Comic Sans MS;font-size:18px;">    Line 417: 06-22 12:37:43.682 I/yanzi   ( 4917): Camera open....  
     2     Line 489: 06-22 12:37:43.758 I/yanzi   ( 4917): onSurfaceTextureAvailable...  
     3     Line 533: 06-22 12:37:43.819 I/yanzi   ( 4917): Camera open over....  
     4     Line 535: 06-22 12:37:43.819 I/yanzi   ( 4917): doStartPreview...  
     5     Line 537: 06-22 12:37:43.825 I/yanzi   ( 4917): PictureSize : w = 1280h = 720  
     6     Line 539: 06-22 12:37:43.825 I/yanzi   ( 4917): PreviewSize:w = 800h = 448  
     7     Line 555: 06-22 12:37:43.874 I/yanzi   ( 4917): 最终设置:PreviewSize--With = 800Height = 448  
     8     Line 557: 06-22 12:37:43.874 I/yanzi   ( 4917): 最终设置:PictureSize--With = 1280Height = 720  
     9     Line 577: 06-22 12:37:44.106 I/yanzi   ( 4917): onSurfaceTextureUpdated...  
    10     Line 579: 06-22 12:37:44.138 I/yanzi   ( 4917): onSurfaceTextureUpdated...  
    11     Line 583: 06-22 12:37:44.169 I/yanzi   ( 4917): onSurfaceTextureUpdated...  
    12     Line 585: 06-22 12:37:44.220 I/yanzi   ( 4917): onSurfaceTextureUpdated...  
    13     Line 587: 06-22 12:37:44.253 I/yanzi   ( 4917): onSurfaceTextureUpdated...</span>  

    测试手机为中兴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()这个函数,我又封装了个:

    1 <span style="font-family:Comic Sans MS;font-size:18px;">    /* 让Activity能得到TextureView的SurfaceTexture 
    2      * @see android.view.TextureView#getSurfaceTexture() 
    3      */  
    4     public SurfaceTexture _getSurfaceTexture(){  
    5         return mSurface;  
    6     }</span>  

    这里的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

    百度云盘:

  • 相关阅读:
    Java高并发(1)
    Java基础知识之常见关键字(1)
    jQuery中attr()与prop()区别介绍
    win7下delphi中的help文档问题
    Delphi TMemo 可以显示、编辑多行文本
    TcxGrid
    Delphi学习手记——单引号和双引号的区别
    sql 系统表协助集合
    VCL安装有哪几种方法?
    sqlserver2008 服务器实例连接
  • 原文地址:https://www.cnblogs.com/zl1991/p/5340085.html
Copyright © 2020-2023  润新知