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 {
    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 }  

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

  1.  1 <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>  

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

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

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

 1  private void initCamera(float previewRate){
 2
 3 if(mCamera != null){
 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             mCamera.setDisplayOrientation(90);
16 //          CamParaUtil.getInstance().printSupportFocusMode(mParams);
17             List<String> focusModes = mParams.getSupportedFocusModes();
18 if(focusModes.contains("continuous-video")){
19                 mParams.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
20             }
21             mCamera.setParameters(mParams);
22             mCamera.startPreview();//开启预览
23             isPreviewing = true;
24             mPreviwRate = previewRate;
25             mParams = mCamera.getParameters(); //重新get一次
26             Log.i(TAG, "最终设置:PreviewSize--With = " + mParams.getPreviewSize().width
27                     + "Height = " + mParams.getPreviewSize().height);
28             Log.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
4             public void run() {
5                 // TODO Auto-generated method stub
6                 CameraInterface.getInstance().doOpenCamera(CameraActivity.this);
7             }
8         };
9         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是:

 1    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...

测试手机为中兴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

时间: 2024-08-28 01:41:44

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,官网已经给出

玩转Android Camera开发(三):国内首发---使用GLSurfaceView预览Camera 基础拍照demo

GLSurfaceView是OpenGL中的一个类,也是能够预览Camera的,并且在预览Camera上有其独到之处. 独到之处在哪?当使用Surfaceview无能为力.痛不欲生时就仅仅有使用GLSurfaceView了.它能够真正做到让Camera的数据和显示分离,所以搞明确了这个,像Camera仅仅开预览不显示这都是小菜,妥妥的. Android4.0的自带Camera源代码是用SurfaceView预览的.但到了4.2就换成了GLSurfaceView来预览. 现在到了4.4又用了自家的

【转】玩转Android Camera开发(三):国内首发---使用GLSurfaceView预览Camera 基础拍照demo

http://blog.csdn.net/yanzi1225627/article/details/33339965 GLSurfaceView是OpenGL中的一个类,也是可以预览Camera的,而且在预览Camera上有其独到之处.独到之处在哪?当使用Surfaceview无能为力.痛不欲生时就只有使用GLSurfaceView了,它能够真正做到让Camera的数据和显示分离,所以搞明白了这个,像Camera只开预览不显示这都是小菜,妥妥的.Android4.0的自带Camera源码是用Su

玩转Android Camera开发(一):Surfaceview预览Camera,基础拍照功能完整demo

杂家前文是在2012年的除夕之夜仓促完成,后来很多人指出了一些问题,琐事缠身一直没有进行升级.后来随着我自己的使用,越来越发现不出个升级版的demo是不行了.有时候就连我自己用这个demo测一些性能.功能点,用着都不顺手.当初代码是在linux下写的,弄到windows里下全是乱码.还要自己改几分钟才能改好.另外,很多人说不能正常预览,原因是我在布局里把Surfaceview的尺寸写死了.再有就是initCamera()的时候设参数失败,直接黑屏退出,原因也是我把预览尺寸和照片尺寸写死了.再有就

Android开发实践:掌握Camera的预览方向和拍照方向

Android的Camera相关应用开发中,有一个必须搞清楚的知识点,就是Camera的预览方向和拍照方向,本文就重点讨论一下这个问题. 图像的Sensor方向:手机Camera的图像数据都是来自于摄像头硬件的图像传感器(Image Sensor),这个Sensor被固定到手机之后是有一个默认的取景方向的,这个方向如下图所示,坐标原点位于手机横放时的左上角: Camera的预览方向:由于手机屏幕可以360度旋转,为了保证用户无论怎么旋转手机都能看到"正确"的预览画面(这个"正

Android实现小视频的录制和预览,界面和功能靠拢微信。

不过是看淡利益,看穿时间,推己及人. 先看一下效果图: 1,实现的功能 a)全屏幕预览录制播放. b)录制时间可定制(本实例15秒),录制按钮动画效果. c)录制完成即刻播放,可保存删除文件. d)录制按钮可以随意拖动复位. 2,可拓展的功能 a)可添加点击拍照功能. b)可添加开关闪关灯功能. c)对焦功能.(缺陷) d)可添加播放暂停功能. e)可分段录制,拼接视屏. 3,未实现的微信效果 a)录制视频时,拖动录制按钮实现焦距调整. b)视频压缩. 4,注意事项 a)本例以goole官方Ca

Android实战技巧之四十七:不用预览拍照与图片缩放剪裁

副标题:Take Picture without preview Android Google出于对隐私的保护,制定了一条门槛,即在Android应用开发中编写拍照程序是必需要有图像预览的.这会对那些恶意程序比如Android中泛滥的Service在后台偷偷记录手机用户的行为与周边信息.这样的门槛还包括手机厂商自带的相机软件在拍照时必须是有声音,这样要避免一些偷拍的情况. 处于技术调研与一些特殊无害场景的使用,我们要用到不用预览的拍照.此文就是以此为背景,做的一些调研.只是用不多与五款手机测试,

android GridView显示相同尺寸图片以及预览功能

项目描述: GridView加载图片,在程序中控制各个图片尺寸一致,点击图片进行预览,可以滑动切换查看不同的界面,可以手势控制图片缩放,效果图如下: 1.GridView控制每个控件大小一致 GridView中的控件大小在程序中控制,思路就是获取屏幕宽高,减去控件之间的空隙,除以每一行控件的个数,就是控件的宽,可以将控件的高设置与宽一致. 首先获取屏幕宽高 public static int screenWidth;//屏幕宽度 WindowManager windowManager = get

移动端上传照片 预览+draw on Canvas demo(解决iOS等设备照片旋转90度的bug)

背景: 本人的一个移动端H5项目,需求如下: 手机相册选取或拍摄照片后在页面上预览 然后绘制在canvas画布上. 这里,我们先看一个demo(http://jsfiddle.net/q3011893/83qfqpk8/embedded/) 操作步骤: 1.点击选择文件,拍摄一张照片,此时"预览:"文字下会显示你刚才拍摄的照片: 2.再点击"draw on Canvas",该按钮下的画布会绘制你刚才拍摄的照片. 正常的结果: 正文: 让input file支持拍照+