文章转载只能用于非商业性质,且不能带有虚拟货币、积分、注册等附加条件。转载须注明出处http://blog.csdn.net/flowingflying/以及作者@恺风Wei。
media framework提供录像功能,录像的同时也包括了录音。录像要通过Surface的UI对象来显示Camera的内容,然后在从Surface中copy。对于一些复杂的应用,即使不需将视频显示给用户,也仍需要一个Surface对象。
UI界面
小例子的界面如下,这是一个横放的UI,我们在AndroidManifest.xml指定必须用横排方式:
<activity android:screenOrientation="landscape" android:name=".RecordVideoActivity" android:label="录像" />
layout文件代码片段如下:
… …
<VideoView android:id="@+id/videoview"
android:layout_width="250dip"
android:layout_height="200dip"/>
… …
在Surface中显示Camera内容
核心是从video view中获取SurfaceHolder,作为Camera显示的preview,下面看看有关的代码片段:
public class RecordVideoActivity extends Activity implements SurfaceHolder.Callback{//回调用于获知Surface是否准备好来显示video图像
… …
private VideoView mVideoView = null;
private SurfaceHolder mHolder = null;
private Camera mCamera = null; //属于android.hardware.Camera包,要求android.permission.CAMERA
. …
private boolean initCamera(){
try{
debug("try to init Camera()...");
//【步骤1】获取摄像头,通过lock(),持有摄像头资源。我们可以对摄像头的参数进行重新设定,键下面注释掉的代码,将图像转90度,并设为黑白色。
mCamera = Camera.open();
mCamera.lock();
/* mCamera.setDisplayOrientation(90);
Camera.Parameters params = mCamera.getParameters();
params.setColorEffect(Camera.Parameters.EFFECT_MONO);
mCamera.setParameters(params); */
//【步骤2】从VideoView中获得SurfaceHolder,并设置监听器
mHolder = mVideoView.getHolder();
mHolder.addCallback(this);
debug("Initialized camera successfully..");
return true;
}catch(Exception e){
debug("Initialized camera error!");
e.printStackTrace();
return false;
}
}
private void releaseCamera(){
if(mCamera != null){
try{
mCamera.reconnect(); //重新获取Camera,在录像中,会unlock()摄像头,对它进行操作,需要reconnect()
}catch(Exception e){
e.printStackTrace();
}
mCamera.release(); //释放资源
mCamera = null;
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
debug("surfaceCreated()");
try{ //【步骤3】当surface创建后,Camera可以在上面显示内容,也就是preview。无论我们要的是图片(takePicture())还是录像,都可以从preview中获取。在startPreview()后,我们可以进行录像的初始化。
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
}catch(Exception e){
debug("ERROR:Could not start the preview");
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
debug("surfaceChanged : width=" + width + " height=" + height + " format=" + format);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
debug("surfaceDestroyed()");
}
private void debug(String info){
Log.d("WEI",info);
}
}
Camera连接MediaRecorder,进行录像
在初始化摄像头后,我们将进行初始化录像,开始并结束录像,相关的代码如下:
public class RecordVideoActivity extends Activity implements SurfaceHolder.Callback,
MediaRecorder.OnErrorListener, MediaRecorder.OnInfoListener{
private MediaRecorder mRecorder = null;
private String mOutputFileName= … ;
… …
// 释放recorder()
private void releaseRecorder(){
if(mRecorder != null){
mRecorder.release();
mRecorder = null;
}
}
// 【步骤4】初始化MediaRecorder
private void initRecorder(){
if(mRecorder != null)
return;
//如输出文件存在,删除之
File outFile = new File(mOutputFileName);
if(outFile.exists())
outFile.delete();
try{
//【4.1】将camera unlock,以便将其与MediaRecorder相连。
mCamera.stopPreview();
mCamera.unlock(); //在videoview上看到图像冻结
mRecorder = new MediaRecorder();
mRecorder.setCamera(mCamera); //reference中明确指出在此之前必须进行android.hardware.Camera.unlock()。
/*【4.2】设置media recorder的属性。本例录像包括了录音,因此需要RECORD_AUDIO的permission,录像写在SD卡上, 需要WRITE_EXTERNAL_STORAGE*/
mRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mRecorder.setVideoSize(176 , 144 );
mRecorder.setVideoFrameRate(15);
mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mRecorder.setMaxDuration(7000); //设置了最大时长,设为7秒,也可以限制文件大小
mRecorder.setPreviewDisplay(mHolder.getSurface()); //设置preview,显示内容
mRecorder.setOutputFile(mOutputFileName);
//【4.3】准备录像。录像不提供异步准备,和录音不同。
mRecorder.prepare();
debug("MediaRecorder initialized");
}catch(Exception e){
debug("initRecorder() failed!");
e.printStackTrace();
}
}
private void beginRecord(){
//【步骤5】设置监听器,并开始录音
mRecorder.setOnInfoListener(this);
mRecorder.setOnInfoListener(this);
mRecorder.start();
debug("Recording");
}
private void stopRecord(){
if(mRecorder != null){
//【步骤6】停止监听,并停止录像
mRecorder.setOnErrorListener(null);
mRecorder.setOnInfoListener(null);
try{
mRecorder.stop();
}catch(IllegalStateException e){
//如果要求已经停止的recorder再次stop(),会出现异常
debug("ERROR: stopRecord() " + e.toString());
e.printStackTrace();
}
//【步骤7】释放资源
releaseRecorder();
releaseCamera();
debug("Stop Record");
}
}
@Override //【步骤5.1】设置监听器的回调函数。onInfo()和onError()很相似,前者非 错误出现,包括最大时长和最大文件大小。在触碰上限时,应停止录像。
public void onInfo(MediaRecorder mr, int what, int extra) {
debug("onInfo(): what=" + what + " extra=" + extra);
if(what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED){//最长时长7秒到达
debug("max duration reached");
stopRecord();
Toast.makeText(this, "Max duration reached, stop record", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onError(MediaRecorder mr, int what, int extra) {
debug("OnError() : what=" + what + " extra=" + extra);
stopRecord();
Toast.makeText(this, "Error, stop record", Toast.LENGTH_SHORT).show();
}
}
如何播放本地视频之前已经学习过,这里不再重复。
获取摄像头信息
下面是选择前置摄像头进行录像的代码片段:
private boolean initCamera(){
try{
int num = Camera.getNumberOfCameras(); //获取摄像头数目
int cameraId = 0; //摄像头ID,从0~N-1
for(int i = 0 ; i < num ; i ++){
CameraInfo cameraInfo = new CameraInfo();
Camera.getCameraInfo(i, cameraInfo); //获取指定Id摄像头的信息,例如前置后置,能否关闭拍照声音
if(cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT){
cameraId = i;
break;
}
}
mCamera = Camera.open(cameraId); //打开制定Id的摄像头
mCamera.lock(); … …
}catch(Exception e){
… …
}
}
选择摄像头后,我们还需要获取进一步的信息。通过surfaceChanged(),在某实体机上运行的例子得到:surfaceChanged : width=375 height=300 format=4。这里给出的是surface的信息。如果我们在初始化media recorder中,将mRecorder.setVideoSize(375,300);将会抛出异常,及录像的video并不支持这样的长宽尺寸,问题关键在于,我们要获取摄像头的信息。在Android2.2开始,提供CameraProfile和CamcorderProfile类。CameraProfile提供的信息很少,只有CameraProfile.getJpegEncodingQualityParameter(),但Camecorder的信息量就比较大,下面重写initRecorder()。
private void initRecorder(){
if(mRecorder != null)
return;
try{
mCamera.stopPreview();
mCamera.unlock();
mRecorder = new MediaRecorder();
mRecorder.setCamera(mCamera);
mRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
//这里得到的是缺省,不代表其他的不支持,所以这也是一定的局限
CamcorderProfile camProf = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
String fileExtension = ".mp4";
if(camProf.fileFormat == MediaRecorder.OutputFormat.THREE_GPP)
fileExtension=".3gp";
mOutputFileName = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES)+ "/record000" + fileExtension;
File outFile = new File(mOutputFileName);
if(outFile.exists())
outFile.delete();
mRecorder.setOutputFile(mOutputFileName);
mRecorder.setOutputFormat(camProf.fileFormat);
mRecorder.setVideoFrameRate(camProf.videoFrameRate);
mRecorder.setVideoSize(camProf.videoFrameWidth , camProf.videoFrameHeight);
mRecorder.setVideoEncoder(camProf.videoCodec); //2:H264,3:MEPG_4_SP
mRecorder.setAudioEncoder(camProf.audioCodec); //3:AAC
mRecorder.setMaxDuration(camProf.duration * 1000);
mRecorder.setPreviewDisplay(mHolder.getSurface());
mRecorder.prepare();
}catch(Exception e){
debug("initRecorder() failed!");
e.printStackTrace();
}
}
小例子代码在:Pro Android学习:media framworks小例子
相关链接:我的Android开发相关文章