视频聊天程序

YUV2RGB的算法

http://www.icodelogic.com/?p=605

http://blog.sina.com.cn/s/blog_62ef2f14010183et.html

 

视频聊天的应用可以从下面的框图示意。 

 

 所以需要从camera获取视频数据(YUV420sp),压缩成H264/MPEG4/H263的包,再传递到对方。接收对方的压缩包,解压出来显示到LCD上。

Android里通过给camera设定 previewcallback函数可以获取每一个Peview帧的yuv数据。

我们现在看看如何按照你想要求的预览尺寸打开camera的并且获取视频数据的。

下面是打开camera的代码片断,他包在一个VideoCameraView类里面。

public class VideoCameraView extends SurfaceView  implements SurfaceHolder.Callback,
        android.hardware.Camera.PreviewCallback {

...

 

private android.hardware.Camera mCamera = null ;

 private double mAspectRatio = 3.0 / 3.0;

 private int preview_w ;
 private int preview_h ;
 private int preview_yuvbytes ; 
 private byte[] bu  ;

 private boolean buffFilled = false ;

 private boolean mRec = false ;

 public void openCamera(int w , int h){

  mRec = false ;
  
  if( surfaceHolder == null  )
   return ;
  mCamera = android.hardware.Camera.open() ;
  try {
   mCamera.setPreviewDisplay(surfaceHolder);
  }catch(IOException e ){
   Log.e(TAG,"mCamera.setPreviewDisplay( " + surfaceHolder +") fail"  ) ;
   return ;
  }

  android.hardware.Camera.Parameters p = mCamera.getParameters() ;

////得到最接近要求的尺寸
  List<android.hardware.Camera.Size>  listPreview = p.getSupportedPreviewSizes() ;
  Log.v(TAG, "preview size is "+listPreview) ;
  int ii = -1 ; 
  int delta = 0x7fffff ;
  for( int i = 0 ; i < listPreview.size() ; i ++) {
   android.hardware.Camera.Size size = listPreview.get(i) ;
   String ws = Integer.toString(size.width);
   String hs = Integer.toString(size.height) ;   
   Log.v(TAG, "elements "+i+":"+ws+"x"+hs) ;
   if( java.lang.Math.abs(size.width - w ) < delta ) {
    delta = java.lang.Math.abs(size.width - w ) ;
    ii = i ;
   }
  }
  preview_w = listPreview.get(ii).width ;
  preview_h = listPreview.get(ii).height ;
  preview_yuvbytes = preview_w*preview_h*3/2 ;


  mAspectRatio = (double)preview_w / preview_h;
  p.setPreviewSize( preview_w , preview_h ) ;

  List<int[]>  fpRange = p.getSupportedPreviewFpsRange() ;
  int max = 100 ;
  int min = 0 ;
  for(int i = 0  ; i < fpRange.size() ; i ++ ) {
   int[] fpr = fpRange.get(i) ;
   Log.v(TAG, "min "+ fpr[0]+ " max " + fpr[1]) ;   
  }  

  mCamera.setParameters(p);  
  bu = new byte[preview_yuvbytes] ;
   
  mCamera.setPreviewCallbackWithBuffer( this ) ;  
  
  android.hardware.Camera.CameraInfo cameraInfo = new android.hardware.Camera.CameraInfo() ; 
  mCamera.getCameraInfo( 0 , cameraInfo ) ;
  rotateAngle = cameraInfo.orientation ;
  Log.v(TAG,"Camera.CameraInfo.orientation="+ cameraInfo.orientation );  
  //mCamera.setDisplayOrientation(cameraInfo.orientation) ;
  //prepareCapture();
  requestLayout() ;  
  timeStart = System.currentTimeMillis() ;
  onPreviewCalled = 0 ;
  mCamera.startPreview();
 }

}

这里有几个问题需要说明一下:

1    你传进来的尺寸可能不是camera支持的,所以要找一个最靠近你要求的尺寸。

2    预览的长宽比可能和你开始布局的长宽比不一致,这样预览到的画面就会变形,所以需要requestLayout() ,并且要重写onMeasure函数,如下:

    protected void onMeasure(int widthSpec, int heightSpec) {
        int previewWidth = MeasureSpec.getSize(widthSpec);
        int previewHeight = MeasureSpec.getSize(heightSpec);

        if (previewWidth > previewHeight * mAspectRatio) {
            previewWidth = (int) (previewHeight * mAspectRatio + .5);
        } else {
            previewHeight = (int) (previewWidth / mAspectRatio + .5);
        }

        // Ask children to follow the new preview dimension.
        super.onMeasure(MeasureSpec.makeMeasureSpec(previewWidth, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(previewHeight, MeasureSpec.EXACTLY));
    }

  请注意:mAspectRatio 是我们在openCamera时计算得到的。

3  需要在应用层new一个preview_yuvbytes大小的内存通过 addCallbackBuffer 传到android系统里去,然后使用setPreviewCallbackWithBuffer来设定回调函数要是setPreviewCallback来设回调函数的话,那么GC会被频繁启动,因为回调送来的内存块是每次都重新分配的,很容易到达需要垃圾处理的门槛,性能会大大降低。而我们采用setPreviewCallbackWithBuffer并且在openCamera时分配这块内存,每次把这块内存压缩使用之后,又重新addCallbackBuffer 到系统里去,就不会大量分配内存,GC也不会启动。请看下面的代码片:

 public void startRec() {
  mRec = true ; 
  mCamera.addCallbackBuffer( bu ) ;
 }

 public void onPreviewFrame (byte[] data, android.hardware.Camera camera){
    if( mRec )
      buffFilled = true ;    
 }

 

 public int  encodeOneFrame(byte[] bitstream , int bitStreamLength){
  int i = 0 ;
  while( (i++ < 10) && (buffFilled == false) ) {
   try {
    Thread.sleep(10) ;
   }catch( InterruptedException e) {
    
   }   
  }
  if( buffFilled == false )
   return 0 ;
  int nn = nativeEncodeOneFrameH264( bu , bitstream ,  bitStreamLength ,..... ) ;
  buffFilled =  false ;
  mCamera.addCallbackBuffer( bu ) ;
  return nn ;
 }

 

 

 

你可能感兴趣的:(游戏,视频,andriod)