Pro Android学习笔记(一三三):Media Frameworks(8):MediaRecorder进行录像

时间:2022-12-26 13:13:46

文章转载只能用于非商业性质,且不能带有虚拟货币、积分、注册等附加条件。转载须注明出处http://blog.csdn.net/flowingflying/以及作者@恺风Wei

media framework提供录像功能,录像的同时也包括了录音。录像要通过Surface的UI对象来显示Camera的内容,然后在从Surface中copy。对于一些复杂的应用,即使不需将视频显示给用户,也仍需要一个Surface对象。

UI界面

小例子的界面如下,这是一个横放的UI,我们在AndroidManifest.xml指定必须用横排方式:

Pro Android学习笔记(一三三):Media Frameworks(8):MediaRecorder进行录像

<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开发相关文章