Android Camera开发:使用TextureView和SurfaceTexture预览Camera 基础拍照demo

幽沉谢世事,俯默窥唐虞。这篇文章主要讲述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. 1 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 { 15private static final String TAG = "yanzi"; 16Context mContext; 17SurfaceTexture mSurface; 18public CameraTextureView(Context context, AttributeSet attrs) { 19super(context, attrs); 20// TODO Auto-generated constructor stub 21mContext = context; 22this.setSurfaceTextureListener(this); 23} 24@Override 25public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, 26int height) { 27// TODO Auto-generated method stub 28Log.i(TAG, "onSurfaceTextureAvailable..."); 29mSurface = surface; 30 //CameraInterface.getInstance().doStartPreview(surface, 1.33f); 31} 32@Override 33public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 34// TODO Auto-generated method stub 35Log.i(TAG, "onSurfaceTextureDestroyed..."); 36CameraInterface.getInstance().doStopCamera(); 37return true; 38} 39@Override 40public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, 41int height) { 42// TODO Auto-generated method stub 43Log.i(TAG, "onSurfaceTextureSizeChanged..."); 44} 45@Override 46public void onSurfaceTextureUpdated(SurfaceTexture surface) { 47// TODO Auto-generated method stub 48Log.i(TAG, "onSurfaceTextureUpdated..."); 49 50} 51 52/* 让Activity能得到TextureView的SurfaceTexture 53* @see android.view.TextureView#getSurfaceTexture() 54*/ 55public SurfaceTexture _getSurfaceTexture(){ 56return mSurface; 57} 58 }

二、在布局文件里把它加上就行了,因为他的父类就是View,当成一般的View就行
  1. 1 < RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2xmlns:tools="http://schemas.android.com/tools" 3android:layout_width="match_parent" 4android:layout_height="match_parent" 5tools:context=".CameraActivity" > 6< FrameLayout 7android:layout_width="wrap_content" 8android:layout_height="wrap_content" > 9< org.yanzi.camera.preview.CameraTextureView 10android:id="@+id/camera_textureview" 11android:layout_width="0dip" 12android:layout_height="0dip" /> 13< /FrameLayout> 14< ImageButton 15android:id="@+id/btn_shutter" 16android:layout_width="wrap_content" 17android:layout_height="wrap_content" 18android:background="@drawable/btn_shutter_background" 19android:layout_alignParentBottom="true" 20android:layout_centerHorizontal="true" 21android:layout_marginBottom="10dip"/> 22 < /RelativeLayout>


三、在CameraInterface里,我封装了两个函数:
1/**使用Surfaceview开启预览 2 3* @param holder 4* @param previewRate 5*/ 6public void doStartPreview(SurfaceHolder holder, float previewRate){ 7Log.i(TAG, "doStartPreview..."); 8if(isPreviewing){ 9mCamera.stopPreview(); 10return; 11} 12if(mCamera != null){ 13try { 14mCamera.setPreviewDisplay(holder); 15} catch (IOException e) { 16// TODO Auto-generated catch block 17e.printStackTrace(); 18} 19initCamera(previewRate); 20} 21 22 23} 24/**使用TextureView预览Camera 25* @param surface 26* @param previewRate 27*/ 28public void doStartPreview(SurfaceTexture surface, float previewRate){ 29Log.i(TAG, "doStartPreview..."); 30if(isPreviewing){ 31mCamera.stopPreview(); 32return; 33} 34if(mCamera != null){ 35try { 36mCamera.setPreviewTexture(surface); 37} catch (IOException e) { 38// TODO Auto-generated catch block 39e.printStackTrace(); 40} 41initCamera(previewRate); 42} 43 44}

 
  分别对应Surfaceview和TextureView预览。可以看到就是传进来的参数不一样,initCamera()的东西都一样。
1private void initCamera(float previewRate){ 2 3 if(mCamera != null){ 4mParams = mCamera.getParameters(); 5mParams.setPictureFormat(PixelFormat.JPEG); //设置拍照后存储的图片格式 6 //CamParaUtil.getInstance().printSupportPictureSize(mParams); 7 //CamParaUtil.getInstance().printSupportPreviewSize(mParams); 8 //设置PreviewSize和PictureSize 9Size pictureSize = CamParaUtil.getInstance().getPropPictureSize( 10mParams.getSupportedPictureSizes(),previewRate, 800); 11mParams.setPictureSize(pictureSize.width, pictureSize.height); 12Size previewSize = CamParaUtil.getInstance().getPropPreviewSize( 13mParams.getSupportedPreviewSizes(), previewRate, 800); 14mParams.setPreviewSize(previewSize.width, previewSize.height); 15mCamera.setDisplayOrientation(90); 16 //CamParaUtil.getInstance().printSupportFocusMode(mParams); 17List< String> focusModes = mParams.getSupportedFocusModes(); 18 if(focusModes.contains("continuous-video")){ 19mParams.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); 20} 21mCamera.setParameters(mParams); 22mCamera.startPreview(); //开启预览 23isPreviewing = true; 24mPreviwRate = previewRate; 25mParams = mCamera.getParameters(); //重新get一次 26Log.i(TAG, "最终设置:PreviewSize--With = " + mParams.getPreviewSize().width 27+ "Height = " + mParams.getPreviewSize().height); 28Log.i(TAG, "最终设置:PictureSize--With = " + mParams.getPictureSize().width 29+ "Height = " + mParams.getPictureSize().height); 30} 31}

四、在Activity里,依旧开一个线程去open Camera
1 Thread openThread = new Thread(){ 2 3@Override 4public void run() { 5// TODO Auto-generated method stub 6CameraInterface.getInstance().doOpenCamera(CameraActivity.this); 7} 8}; 9openThread.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是:
1Line 417: 06-22 12:37:43.682 I/yanzi( 4917): Camera open.... 2Line 489: 06-22 12:37:43.758 I/yanzi( 4917): onSurfaceTextureAvailable... 3Line 533: 06-22 12:37:43.819 I/yanzi( 4917): Camera open over.... 4Line 535: 06-22 12:37:43.819 I/yanzi( 4917): doStartPreview... 5Line 537: 06-22 12:37:43.825 I/yanzi( 4917): PictureSize : w = 1280h = 720 6Line 539: 06-22 12:37:43.825 I/yanzi( 4917): PreviewSize:w = 800h = 448 7Line 555: 06-22 12:37:43.874 I/yanzi( 4917): 最终设置:PreviewSize--With = 800Height = 448 8Line 557: 06-22 12:37:43.874 I/yanzi( 4917): 最终设置:PictureSize--With = 1280Height = 720 9Line 577: 06-22 12:37:44.106 I/yanzi( 4917): onSurfaceTextureUpdated... 10Line 579: 06-22 12:37:44.138 I/yanzi( 4917): onSurfaceTextureUpdated... 11Line 583: 06-22 12:37:44.169 I/yanzi( 4917): onSurfaceTextureUpdated... 12Line 585: 06-22 12:37:44.220 I/yanzi( 4917): onSurfaceTextureUpdated... 13Line 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操作,保证万无一失。
【Android Camera开发:使用TextureView和SurfaceTexture预览Camera 基础拍照demo】 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

    推荐阅读