CarRecorder源码解析二(循环录像分析)

循环录像的整体逻辑比较简单,但代码还是比较复杂的,逻辑是刚开机就开始循环录像,每两分钟通过MediaRecorder录制一段视频,当视频所占内存卡的大小到达某一个预设的值时,开始自动删除最老的视频。

## 一:如何实现开始结束开始的逻辑的?

循环录像的入口在surfaceView的生命周期onCreate中
当surfaceView创建的时候,初始化VideoRecorder,在此时开始循环录像。

问题:surfaceView什么时候被创建?
WindowManager.addView(view, wmParams);在服务里面添加view是否会被显示?

  if (recorder == null) {
            recorder = VideoRecorder.getInstance(holder, this);
            recorder.clearShareImageCache();

        }
    ----------------------------------------------------------
     if (firstBoot) {
            checkExternalSDcardSize();
            if (checkSDcard() >= nAvailableMin) {
                recorder.start(true);//开始录像
            }
            firstBoot = false;
        }

#### 开始录像:

public boolean start(boolean start) {//录像时传过来的参数为true
Log.i("test", "media recorder start=" + start);
if (isTestMode()) {
return false;
}
if (mCamera == null) {
return false;
}
bStart = start;
if (bStart) {//这个参数为true是开始录像,为false是结束录像
return startRecord();
} else {
return stopRecord();
}
}

若是为true则走startRecord()方法,bStart为全局变量,此时为true;下面来看startRecord()方法的具体实现.

“`
public boolean startRecord() {
//如果SD卡没有挂载则结束方法返回
if (!android.os.Environment.getExternalStorageState().equals(
android.os.Environment.MEDIA_MOUNTED)) {
return false;
}
// add by xrx
try {
//创建存放视频文件的目录
File fileDir = new File(VIDEOS_PATH_NORMAL);
if (!fileDir.exists())
fileDir.mkdirs();
fileDir = null;
//创建存放缩略图的目录
fileDir = new File(THUMBNAIL_PATH);
if (!fileDir.exists())
fileDir.mkdirs();
fileDir = null;
//在一段视频录制完成之前,格式为时间.temp的格式
szVideoFile = VIDEOS_PATH_NORMAL
+ new SimpleDateFormat(“yyyyMMdd_HHmmss”, Locale.CHINA)
.format(new Date()) + “.temp”;
//准备预览,设置参数
CameraUtils.prepareRecord(surfaceHolder, RECORD_VIDEO_WIDTH,
RECORD_VIDEO_HEIGHT, persistUtils.getRecordAudioEnable(),
30, nBitRate, 1000 * 60 * persistUtils.getRecordTime(),
szVideoFile);
//开启录像,若是开启成功,则返回true,否则返回false
if (CameraUtils.startRecord(this, this)) {

            bRecording = true;//为true则当前正在录像
            if (recorderListener != null) {
                recorderListener.onStarted();
            }
        }
        isUploadImage1 = false;
        isUploadImage2 = false;

        return true;

    } catch (Exception e) {

        start(false);
    }
    return false;
}

“`

 由上代码可见,录像的具体逻辑实现是在CameraUtils里面实现的,具体的方法是CameraUtils.prepareRecord和CameraUtils.startRecord方法,点进去看一下,最长时长具体是怎么起作用的:

控制时长的代码如下:

 /**
     * Sets the maximum duration (in ms) of the recording session.
     * Call this after setOutFormat() but before prepare().
     * After recording reaches the specified duration, a notification
     * will be sent to the {@link android.media.MediaRecorder.OnInfoListener}
     * with a "what" code of {@link #MEDIA_RECORDER_INFO_MAX_DURATION_REACHED}
     * and recording will be stopped. Stopping happens asynchronously, there
     * is no guarantee that the recorder will have stopped by the time the
     * listener is notified.
     *
     * @param max_duration_ms the maximum duration in ms (if zero or negative, disables the duration limit)
     *
     */
    public native void setMaxDuration(int max_duration_ms) throws IllegalArgumentException;

由注释可见,当达到最大时长时会回调OnInfoListener的方法onInfo,方法的具体实现是在VideoRecorder中,代码如下:

@Override
    public void onInfo(MediaRecorder mr, int what, int extra) {
        switch (what) {
            case MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED: {
                stopRecord();
                notyfyMediaAdd(new File(szVideoFile));
            }
            break;
            case MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED: {
                stopRecord();
                notyfyMediaAdd(new File(szVideoFile));
            }
            break;
            case MediaRecorder.MEDIA_RECORDER_ERROR_UNKNOWN: {

                start(false);
            }
            break;
        }
    }

可见,当达到最大时长时,调用了 stopRecord()方法,随即循环录像停止,代码如下:

private boolean stopRecord() {
        boolean bRet = false;

        CameraUtils.stopRecord();
        if (szVideoFile != null) {
            if (szVideoFile.endsWith("temp")) {
                File videoFile = new File(szVideoFile);
                szVideoFile = szVideoFile.replace("temp", "3gp");
                videoFile.renameTo(new File(szVideoFile));
                // add by xrx 获取视频第一帧对应的缩略图
                getVideoThumbnailFile(szVideoFile);
                CarRecorderDebug.printfRECORDERLog("new file renameTo "
                        + szVideoFile);
            }
        }
        try {
            mCamera.lock();
            CarRecorderDebug.printfRECORDERLog("mCamera.lock() ");
            Thread.sleep(100);
        } catch (Exception e) {

            e.printStackTrace();
        }

        checkLockFile();
        bRet = true;
        bRecording = false;

        if (recorderListener != null) {
            recorderListener.onStoped(bStart);

        }

        return bRet;
    }

由上代码可见在完成录制之后先处理缩略图又调用了recorderListener的onStoped(bStart)方法,方法的具体实现是在PreviewService中完成的,代码如下:

@Override
 public void onStoped(boolean bStart) {
                // recordIcon.setVisibility(View.GONE);
                setRecording(false);
                CarRecorderDebug
                        .printfRECORDERLog("setRecorderListener onStoped");
                recordButton.setImageResource(R.drawable.start_button);
                if (bStart)
                    if (isSDExists(PreviewService.this)) {
                        recorder.start(true);
                    }
            }

由上代码可见,当停止时根据bStart参数来判断要不要进行下一次录像,这个参数是当录像开始时传入的,为true,详见VideoRecorder的start(boolean isStart)方法,这时又一次开始录像。


二:如何 实现录像的自动删除的?

代码如下:

 Handler updateTime = new Handler();
    Runnable updateTimeThread = new Runnable() {
        @Override
        public void run() {
            long available = checkSDcard();
            if (available < 0)
                available = 0;

            timeText.setText(new SimpleDateFormat(
                    getString(R.string.carrecorder_time_format), Locale.CHINA)
                    .format(new Date()));
                    //使用post实际是让代码在主线程中执行
            updateTime.postDelayed(updateTimeThread, 1000);
        }
    };

以上代码的逻辑实际是实现一个定时循环的效果:在一个子线程里面通过handler发送消息可指定在多久之后执行,handler收到消息执行业务逻辑,当时间一到,再次执行子线程的逻辑,以此循环

自动删除录像的逻辑放在checkSDcard()方法中,代码如下:

public long checkSDcard() {
        File rootPath = new File(recorder.SD_ROOT);
        if (!isSDExists(this)) {
            showWarning(true, getString(R.string.cardverify));
            return -1;
        }

        checkExternalSDcardSize();

        long available = rootPath.getFreeSpace() / 1024 / 1024;
        Log.i("test", "recorder available=" + available);
        while ((available = rootPath.getFreeSpace() / 1024 / 1024) < nAvailableMin && deleteFile()) {
            available = rootPath.getFreeSpace() / 1024 / 1024;
            Log.i("test", "recorder available=" + available);
        }

        if (available < nAvailableMin) {
            showWarning(true, getString(R.string.lowstorage));

        } else {
            showWarning(false, null);
        }
        return available;
    }

该方法返回一个可用的内存大小,每次要进行判断内存的大小

  while ((available = rootPath.getFreeSpace() / 1024 / 1024) < nAvailableMin && deleteFile()) {
            available = rootPath.getFreeSpace() / 1024 / 1024;
            Log.i("test", "recorder available=" + available);
        }

具体可见while循环中的条件,当可用的磁盘可用内存大于安全内存(代码中为360M)的时候,不进行删除,当小于可用内存的时候开始删除文件,删除的方法较为简单,不在赘述。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是基于不同继承方式的行车记录仪类设计和测试代码: 方法一:公有继承 ```c++ #include<iostream> using namespace std; // 摄像机类 class Camera { public: void shoot() { cout << "摄像中..." << endl; } void setQuality() { cout << "设置图像质量..." << endl; } private: int code; // 编码算法 }; // 公有继承方式的行车记录仪类 class PublicCarRecorder : public Camera { public: void operateMenu() { cout << "操作菜单..." << endl; } void senseSpeed() { cout << "车速传感器..." << endl; } }; int main() { cout << "公有继承方式:\n"; PublicCarRecorder carRecorder; // 行车记录仪的芯片可以使用摄像机的摄像、图像质量设定功能 carRecorder.shoot(); carRecorder.setQuality(); // 行车记录仪用户可以操作行车记录仪的操作菜单和摄像机的摄像功能 carRecorder.operateMenu(); // 其他成员访问属性 // carRecorder.code; // 错误,无法访问私有成员 return 0; } ``` 方法:私有继承 ```c++ #include<iostream> using namespace std; // 摄像机类 class Camera { public: void shoot() { cout << "摄像中..." << endl; } void setQuality() { cout << "设置图像质量..." << endl; } private: int code; // 编码算法 }; // 私有继承方式的行车记录仪类 class PrivateCarRecorder : private Camera { public: void operateMenu() { cout << "操作菜单..." << endl; } void senseSpeed() { cout << "车速传感器..." << endl; } using Camera::shoot; // 将摄像机的摄像功能公开 using Camera::setQuality; // 将摄像机的图像质量设定功能公开 }; int main() { cout << "私有继承方式:\n"; PrivateCarRecorder carRecorder; // 行车记录仪的芯片可以使用摄像机的摄像、图像质量设定功能 carRecorder.shoot(); carRecorder.setQuality(); // 行车记录仪用户仅仅可以操作行车记录仪的操作菜单 carRecorder.operateMenu(); // 其他成员访问属性 // carRecorder.code; // 错误,无法访问私有成员 return 0; } ``` 方法三:保护继承 ```c++ #include<iostream> using namespace std; // 摄像机类 class Camera { public: void shoot() { cout << "摄像中..." << endl; } void setQuality() { cout << "设置图像质量..." << endl; } private: int code; // 编码算法 }; // 保护继承方式的行车记录仪类 class ProtectedCarRecorder : protected Camera { public: void operateMenu() { cout << "操作菜单..." << endl; } void senseSpeed() { cout << "车速传感器..." << endl; } using Camera::shoot; // 将摄像机的摄像功能公开 using Camera::setQuality; // 将摄像机的图像质量设定功能公开 }; int main() { cout << "保护继承方式:\n"; ProtectedCarRecorder carRecorder; // 行车记录仪的芯片可以使用摄像机的摄像、图像质量设定功能 carRecorder.shoot(); carRecorder.setQuality(); // 行车记录仪用户仅仅可以操作行车记录仪的操作菜单 carRecorder.operateMenu(); // 其他成员访问属性 // carRecorder.code; // 错误,无法访问私有成员 return 0; } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值