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

摘要:
Google自Android4.0出了TextureView,为什么推出呢?就是为了弥补Surfaceview的不足,另外一方面也是为了平衡GlSurfaceView,当然这是本人揣度的。关于TextureView、Surfaceview、SurfaceTexture、GLSurfaceView的关系,待咱家推出GLSurfaceview预览Camera后再专门分析。本文主要介绍使用TextureView预览Camera。其实关于如何用TextureView预览Camera,官网已经给出了demo,参见这里。本文就利用前文搭建的一个轻量级的Camera框架来快速替换掉Surfaceview。因为用Surfaceview预览的话传一个SurfaceHolder进去,用Textureview预览的话需要传进去一个SurfaceTexture。其他的Camera流程不变。

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;">packageorg.yanzi.camera.preview;  
2   
3 importorg.yanzi.camera.CameraInterface;  
4   
5 importandroid.content.Context;  
6 importandroid.graphics.PixelFormat;  
7 importandroid.graphics.SurfaceTexture;  
8 importandroid.util.AttributeSet;  
9 importandroid.util.Log;  
10 importandroid.view.SurfaceHolder;  
11 importandroid.view.SurfaceView;  
12 importandroid.view.TextureView;  
13   
14 public class CameraTextureView extends TextureView implementsTextureView.SurfaceTextureListener {  
15     private static final String TAG = "yanzi";  
16 Context mContext;  
17 SurfaceTexture mSurface;  
18     publicCameraTextureView(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, intwidth,  
26             intheight) {  
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 booleanonSurfaceTextureDestroyed(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, intwidth,  
41             intheight) {  
42         //TODO Auto-generated method stub  
43         Log.i(TAG, "onSurfaceTextureSizeChanged...");  
44 }  
45 @Override  
46     public voidonSurfaceTextureUpdated(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     publicSurfaceTexture _getSurfaceTexture(){  
56         returnmSurface;  
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:   
11             android:layout_width="0dip"  
12             android:layout_height="0dip" />  
13     </FrameLayout>  
14     <ImageButton  
15         android:   
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 * @paramholder 
3 * @parampreviewRate 
4      */  
5     public void doStartPreview(SurfaceHolder holder, floatpreviewRate){  
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 * @paramsurface 
25 * @parampreviewRate 
26      */  
27     public void doStartPreview(SurfaceTexture surface, floatpreviewRate){  
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(floatpreviewRate){  
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 = newThread(){  
2 @Override  
3             public voidrun() {  
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 voidcameraHasOpened() {  
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     publicSurfaceTexture _getSurfaceTexture(){  
5         returnmSurface;  
6     }</span>  

这里的mSurface就是onSurfaceTextureAvailable()回调里传上来的SurfaceTexture。测试证明,开预览时直接调

textureView.getSurfaceTexture(),把它传给Camera:mCamera.setPreviewTexture(surface);也是能正常预览的。但是推荐使用前者,原因见官方上的这段话:

A TextureView's SurfaceTexture can be obtained either by invokinggetSurfaceTexture()or by using aTextureView.SurfaceTextureListener. It is important to know that a SurfaceTexture is available only after the TextureView is attached to a window (andonAttachedToWindow()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 itsSurfaceTexture. TheSurfaceTexturecan 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》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Vue-pdf实现在线预览PDF文件在浏览器上开发GO和Vue!(基于code-server)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Android使用TextureView播放视频

1.引言 如果你想显示一段在线视频或者任意的数据流比如视频或者OpenGL 场景,你可以用android中的TextureView做到。 1).TextureView的兄弟SurfaceView 应用程序的视频或者opengl内容往往是显示在一个特别的UI控件中:SurfaceView。SurfaceView的工作方式是创建一个置于应用窗口之后的新窗口。...

二维码扫描开源库ZXing定制化【转】

转自:http://www.cnblogs.com/sickworm/p/4562081.html 最近在用ZXing这个开源库做二维码的扫描模块,开发过程的一些代码修改和裁剪的经验和大家分享一下。 建议: 如果需要集成到自己的app上,而不是做一个demo,不推荐用ZXing的Android外围开发模块,只用核心的core目录的代码就好了。androi...

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

是在2012年的除夕之夜仓促完成,后来很多人指出了一些问题,琐事缠身一直没有进行升级。后来随着我自己的使用,越来越发现不出个升级版的demo是不行了。有时候就连我自己用这个demo测一些性能、功能点,用着都不顺手。当初代码是在linux下写的,弄到windows里下全是乱码。还要自己改几分钟才能改好。另外,很多人说不能正常预览,原因是我在布局里把Surfa...

Android使用SurfaceView实现墨迹天气的风车效果

SurfaceView也是继承自View,它和我们以前接触到的View(Button、TextView等)最大的不同是,SurfaceView可以有一个单独的线程进行绘制,这个线程区别于UI线程(主线程),因此SurfaceView绘制并不占用主线程资源。 SurfaceView实现通常是自定义,继承SurfaceView并实现SurfaceHolder....

Android SurfaceView

一、SurfaceView简介SurfaceView可以在主线程之外的线程中向屏幕绘图上。这样可以避免画图任务繁重的时候造成主线程阻塞,从而提高了程序的反应速度。在游戏开发中多用到SurfaceView,游戏中的背景、人物、动画等等尽量在画布canvas中画出。什么是Surface在这里要先说说什么是Surface。简单的说Surface对应了一块屏幕缓冲...

mediaplayer与surfaceView,无法播放问题

mediaplayer需要在surfaceView创建之后才能创建,不然会导致错误。 1 surfaceholder =msurface.getHolder(); 2 surfaceholder.setKeepScreenOn(true); 3 surfaceholder.addCallback(new SurfaceV...