视频数据流的获取
Android设备视频数据的获取,是调用Camera,所以需要在AndroidManifest中添加以下的权限:
如果当前设备不支持自动对焦,则相关的设置将不起作用。
首先,是拿到Camera对象并且设置摄像头采集的参数配置:
mCamera = Camera.open(mCamId);
Camera.Parameters params = mCamera.getParameters();//摄像头的参数
参数如果不设置的话,一般会采用默认值,但现在市面上的所有设备配置不相同,如果全部按照默认的来可能出现未知的错误。例如相机拍照和预览的分辨率设置,如果和真实的手机不一样就会报错。
params.setPictureSize(mEncoder.getPreviewWidth(), mEncoder.getPreviewHeight());//保存的照片的尺寸
params.setPreviewSize(mEncoder.getPreviewWidth(), mEncoder.getPreviewHeight());//预览分辨率
如果你不知道自己的设备支持哪些拍照或者是预览时的分辨率,Camera提供了相应的方法:
for(int num = 0; num < params.getSupportedPreviewSizes().size(); num++){
Log.d(TAG,params.getSupportedPreviewSizes().get(num).width+"*"
+params.getSupportedPreviewSizes().get(num).height);
}
上面的代码获取的是设备支持的所有的预览分辨率。下面贴一下我们设备相应的分辨率:
除了设置采集的视频数据流的分辨率,还可以设置以下的相应参数:
params.setPreviewSize(mEncoder.getPreviewWidth(), mEncoder.getPreviewHeight());//设置预览分辨率
params.setPreviewFpsRange(int min, int max);//设置摄像头采集时的帧率
之前的setPreviewFrameRate方法被现在的setPreviewFpsRange方法取代,该方法是设置摄像头每秒采集多少帧的视频数据流,min一般是用户自己预期设置的值,max是设备所能支持的最大帧数值(我的设备帧数的支持7.5到30),这个值计算时需要再*1000.
当然设备支持的帧率也可以通过Camera提供的相应的方法获得:
for(int num = 0; num < params.getSupportedPreviewFpsRange().size(); num++)
{
int[] SupPreRange = params.getSupportedPreviewFpsRange().get(num);
Log.d(TAG, "< " + num + " >" + " Min = " + SupPreRange[0]
+ " Max = " + SupPreRange[1]);
}
params.setPreviewFormat(ImageFormat.NV21);//NV21 设置预览帧格式 默认是NV21(YUV420SP)格式
关于这些我的上篇博客有着更加详细的介绍
[上篇博客链接](http://blog.csdn.net/qq_26986211)
params.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);//关闭闪光灯
params.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);//曝光平衡
params.setSceneMode(Camera.Parameters.SCENE_MODE_AUTO);//设置场景模式
if (!params.getSupportedFocusModes().isEmpty()) {
params.setFocusMode(params.getSupportedFocusModes().get(0));//自动对焦的效果
}
当然了,这些参数不一定在所有的设备上都是通用的,需要根据自的情况而定。如果你设置了相关的参数,而设备又不支持,那么这么的方法就不会被执行。
mCamera.setParameters(params);//设置配置的参数
mCamera.setDisplayOrientation(mPreviewRotation);//设置屏幕的旋转角度
上面主要是针对垂直来说,因为这样设置的话一般是设置90,180,270。所以当用户大幅度转动设备的时候,设备就直接旋转了,如果需要自适应预览接连始终是正确的(这里的正确是指,无论用户怎么旋转,设备不会出现大幅度的旋转,保证了预览界面是正常的),就需要将重写setDisplayOrientation方法:
public static void setCameraDisplayOrientation ( Activity activity ,
int cameraId , android . hardware . Camera camera ) {
android . hardware . Camera . CameraInfo info =
new android . hardware . Camera . CameraInfo ();
android . hardware . Camera . getCameraInfo ( cameraId , info );
int rotation = activity . getWindowManager (). getDefaultDisplay ()
. getRotation ();
int degrees = 0 ;
switch ( rotation ) {
case Surface . ROTATION_0 : degrees = 0 ; break ;
case Surface . ROTATION_90 : degrees = 90 ; break ;
case Surface . ROTATION_180 : degrees = 180 ; break ;
case Surface . ROTATION_270 : degrees = 270 ; break ;
}
int result ;
if ( info . facing == Camera . CameraInfo . CAMERA_FACING_FRONT ) {
result = ( info . orientation + degrees ) % 360 ;
result = ( 360 - result ) % 360 ; // compensate the mirror
} else { // back-facing
result = ( info . orientation - degrees + 360 ) % 360 ;
}
camera . setDisplayOrientation ( result );
}
接下就是预览回调的注册了,在Camera提供了三种方法:
setPreviewCallback(Camera.PreviewCallback)
setOneShotPreviewCallback(Camera.PreviewCallback)
setPreviewCallbackWithBuffer(Camera.PreviewCallback)
setPreviewCallback(Camera.PreviewCallback) 。这个方法不需要用户自己分配相应的Buffer数组,由系统默认自动分配。只要预览帧可用,该方法就会一直被调用,此时其他的回调函数将会被覆盖。
setOneShotPreviewCallback(Camera.PreviewCallback)。这个方法也不需要用户自己分配数组大小,从字面上也可以理解出来,调用一次之后,数据将会被清楚,但该方法可以随时被调用,执行此函数时,其他的回调函数将会被覆盖。
setPreviewCallbackWithBuffer(Camera.PreviewCallback)。使用该函数之前我们需要指定一个字节数组作为缓冲区,大小一般也是有用户自己根据实际情况自己设置的。由于摄像头采集的数据流是YUV格式的,一般 Y = width*height,U = Y/4,V = Y/4;所以YUV数据的大小是width*height*3/2,所以字节数组的大小一般是width*height*3/2。需要先调mCamera.addCallbackBuffer()方法,参数是分配大小的字节数组。所以这两个方法是绑定在一起的使用的。
回调函数的具体注册使用如下:以setPreviewCallbackWithBuffer为例
mCamera.setPreviewCallbackWithBuffer(this);
接下来就是视频真正开始预览,获取数据了
mCamera.startPreview();
开始预览之后,同时之前也设置了回调函数,程序就会自动调用onPreviewFrame函数,在主类继承了implements SurfaceHolder.Callback, Camera.PreviewCallback。系统就会重载onPreviewFrame函数。
public void onPreviewFrame(byte[] data, Camera camera) {
//将取得的视频数据发送出去,在这了你也可以实现别的功能
onGetYuvFrame(data);
//这个接口调用前,我们需要提前分配一块buffer,并且这个接口调用一定要放在onPreviewFrame()回调中:
camera.addCallbackBuffer(mYuvPreviewFrame);
}
该函数的参数中 data就是最原始的视频数据流,如果你需要进行预览时的一些摄像头操作,一般也可以在onPreviewFrame函数中进行设置,例如人脸识别,但与视频数据打交道的时候,算法一般是非常的复杂的,所以一般是开启新的线程进行后续的操作。
最后,摄像头不用的时候一定要释放资源:如果程序中加入了previewCallback,在surfaceDestroy释放camera的时候,最好执行myCamera.setOneShotPreviewCallback(null); 或者myCamera.setPreviewCallback(null);中止这种回调,然后再释放camera更安全。否则可能会报错。
//关闭摄像头
private void stopCamera() {
if (mCamera != null) {
// 停止预览前需要将摄像头的回调函数设置为空
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
音频数据流的获取
Android音频设备是麦克风,使用时需要添加以下权限:
音频的获取相比较视频获取来说,步骤简单得多,并没有太多复杂的配置:
if (mic != null) {
return;
}
//根据自己设置的音频格式配置相应的数组大小,用于存储数据,同时可以提高效率,节约空间
int bufferSize = 2 * AudioRecord.getMinBufferSize(SrsEncoder.ASAMPLERATE, SrsEncoder.ACHANNEL, AudioFormat.ENCODING_PCM_16BIT);
mic = new AudioRecord(MediaRecorder.AudioSource.MIC, SrsEncoder.ASAMPLERATE, SrsEncoder.ACHANNEL, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
mic.startRecording();
byte pcmBuffer[] = new byte[2048];
while (aloop && !Thread.interrupted()) {
int size = mic.read(pcmBuffer, 0, pcmBuffer.length);
if (size <= 0) {
break;
}
//将获取的数据发送出去
mEncoder.onGetPcmFrame(pcmBuffer, size);
}
这里介绍一下参数:
ASAMPLERATE:音频采样率,有44100、22050、11025、4000、8000 等,代表了采样的品质高低,采样率越高品质越高。
ACHANNEL:声道设置:android支持双声道立体声和单声道。MONO单声道,STEREO立体声
AudioFormat.ENCODING_PCM_16BIT:采样大小为16bit 还可以设置成8bit
这列涉及到要算一个PCM音频流的码率,采样率值×采样大小值×声道数bps。一个采样率为44.1KHz,采样大小为16bit,双声道的PCM编码的WAV文件,它的数据速率则为 44.1K×16×2 =1411.2 Kbps。我们常说128K的MP3,对应的WAV的参数,就是这个1411.2 Kbps,这个参数也被称为数据带宽,它和ADSL中的带宽是一个概念。将码率除以8,就可以得到这个WAV的数据速率,即176.4KB/s。这表示存储一秒钟采样率为44.1KHz,采样大小为16bit,双声道的PCM编码的音频信号,需要176.4KB的空间,1分钟则约为10.34M,这对大部分用户是不可接受的,尤其是喜欢在电脑上听音乐的朋友,要降低磁盘占用,只有2种方法,降低采样指标或者压缩。降低指标是不可取的,因此专家们研发了各种压缩方案。也就是我们后来说到的编码压缩。
总结一下,如有不足之处请大家见谅,望指出批评。我是Mr.小艾