玩轉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

百度雲盤:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM