Android Camera 使用一例,視頻聊天app


 視頻聊天的應用可以從下面的框圖示意。 

 

 所以需要從camera獲取視頻數據(YUV420sp),壓縮成H264/MPEG4/H263的包,再傳遞到對方。接收對方的壓縮包,解壓出來顯示到LCD上。

Android里通過給camera設定 previewcallback函數可以獲取每一個Peview幀的yuv數據。

我們現在看看如何按照你想要求的預覽尺寸打開camera的並且獲取視頻數據的。

下面是打開camera的代碼片斷,他包在一個VideoCameraView類里面。

 1 public class VideoCameraView extends SurfaceView  implements SurfaceHolder.Callback,
 2         android.hardware.Camera.PreviewCallback {
 3 
 4 ...
 5 
 6  
 7 
 8 private android.hardware.Camera mCamera = null ;
 9 
10  private double mAspectRatio = 3.0 / 3.0;
11 
12  private int preview_w ;
13  private int preview_h ;
14  private int preview_yuvbytes ; 
15  private byte[] bu  ;
16 
17  private boolean buffFilled = false ;
18 
19  private boolean mRec = false ;
20 
21  public void openCamera(int w , int h){
22 
23   mRec = false ;
24   
25   if( surfaceHolder == null  )
26    return ;
27   mCamera = android.hardware.Camera.open() ;
28   try {
29    mCamera.setPreviewDisplay(surfaceHolder);
30   }catch(IOException e ){
31    Log.e(TAG,"mCamera.setPreviewDisplay( " + surfaceHolder +") fail"  ) ;
32    return ;
33   }
34 
35   android.hardware.Camera.Parameters p = mCamera.getParameters() ;
36 
37 ////得到最接近要求的尺寸
38   List<android.hardware.Camera.Size>  listPreview = p.getSupportedPreviewSizes() ;
39   Log.v(TAG, "preview size is "+listPreview) ;
40   int ii = -1 ; 
41   int delta = 0x7fffff ;
42   for( int i = 0 ; i < listPreview.size() ; i ++) {
43    android.hardware.Camera.Size size = listPreview.get(i) ;
44    String ws = Integer.toString(size.width);
45    String hs = Integer.toString(size.height) ;   
46    Log.v(TAG, "elements "+i+":"+ws+"x"+hs) ;
47    if( java.lang.Math.abs(size.width - w ) < delta ) {
48     delta = java.lang.Math.abs(size.width - w ) ;
49     ii = i ;
50    }
51   }
52   preview_w = listPreview.get(ii).width ;
53   preview_h = listPreview.get(ii).height ;
54   preview_yuvbytes = preview_w*preview_h*3/2 ;
55 
56 
57   mAspectRatio = (double)preview_w / preview_h;
58   p.setPreviewSize( preview_w , preview_h ) ;
59 
60   List<int[]>  fpRange = p.getSupportedPreviewFpsRange() ;
61   int max = 100 ;
62   int min = 0 ;
63   for(int i = 0  ; i < fpRange.size() ; i ++ ) {
64    int[] fpr = fpRange.get(i) ;
65    Log.v(TAG, "min "+ fpr[0]+ " max " + fpr[1]) ;   
66   }  
67 
68   mCamera.setParameters(p);  
69   bu = new byte[preview_yuvbytes] ;
70    
71   mCamera.setPreviewCallbackWithBuffer( this ) ;  
72   
73   android.hardware.Camera.CameraInfo cameraInfo = new android.hardware.Camera.CameraInfo() ; 
74   mCamera.getCameraInfo( 0 , cameraInfo ) ;
75   rotateAngle = cameraInfo.orientation ;
76   Log.v(TAG,"Camera.CameraInfo.orientation="+ cameraInfo.orientation );  
77   //mCamera.setDisplayOrientation(cameraInfo.orientation) ;
78   //prepareCapture();
79   requestLayout() ;  
80   timeStart = System.currentTimeMillis() ;
81   onPreviewCalled = 0 ;
82   mCamera.startPreview();
83  }
84 
85 }

 

這里有幾個問題需要說明一下:

1    你傳進來的尺寸可能不是camera支持的,所以要找一個最靠近你要求的尺寸。

2    預覽的長寬比可能和你開始布局的長寬比不一致,這樣預覽到的畫面就會變形,所以需要requestLayout() ,並且要重寫onMeasure函數,如下:

  

 1   protected void onMeasure(int widthSpec, int heightSpec) {
 2         int previewWidth = MeasureSpec.getSize(widthSpec);
 3         int previewHeight = MeasureSpec.getSize(heightSpec);
 4 
 5         if (previewWidth > previewHeight * mAspectRatio) {
 6             previewWidth = (int) (previewHeight * mAspectRatio + .5);
 7         } else {
 8             previewHeight = (int) (previewWidth / mAspectRatio + .5);
 9         }
10 
11         // Ask children to follow the new preview dimension.
12         super.onMeasure(MeasureSpec.makeMeasureSpec(previewWidth, MeasureSpec.EXACTLY),
13                 MeasureSpec.makeMeasureSpec(previewHeight, MeasureSpec.EXACTLY));
14     }

 

  請注意:mAspectRatio 是我們在openCamera時計算得到的。

 需要在應用層new一個preview_yuvbytes大小的內存通過 addCallbackBuffer 傳到android系統里去,然后使用setPreviewCallbackWithBuffer來設定回調函數。要是setPreviewCallback來設回調函數的話,那么GC會被頻繁啟動,因為回調送來的內存塊是每次都重新分配的,很容易到達需要垃圾處理的門檻,性能會大大降低。而我們采用setPreviewCallbackWithBuffer並且在openCamera時分配這塊內存,每次把這塊內存壓縮使用之后,又重新addCallbackBuffer 到系統里去,就不會大量分配內存,GC也不會啟動。請看下面的代碼片:

 

 1 public void startRec() {
 2   mRec = true ; 
 3   mCamera.addCallbackBuffer( bu ) ;
 4  }
 5 
 6  public void onPreviewFrame (byte[] data, android.hardware.Camera camera){
 7     if( mRec )
 8       buffFilled = true ;    
 9  }
10 
11  
12 
13  public int  encodeOneFrame(byte[] bitstream , int bitStreamLength){
14   int i = 0 ;
15   while( (i++ < 10) && (buffFilled == false) ) {
16    try {
17     Thread.sleep(10) ;
18    }catch( InterruptedException e) {
19     
20    }   
21   }
22   if( buffFilled == false )
23    return 0 ;
24   int nn = nativeEncodeOneFrameH264( bu , bitstream ,  bitStreamLength ,..... ) ;
25   buffFilled =  false ;
26   mCamera.addCallbackBuffer( bu ) ;
27   return nn ;
28  }

 

 demo鏈接:http://nchc.dl.sourceforge.net/project/avccodecdemo/avccodecDemo-src-apk.zip

原文鏈接:http://blog.csdn.net/brooknew/article/details/7998833


免責聲明!

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



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