CarRecorder源码分析(一)随拍随录
来源:互联网 发布:db2查看数据库列表 编辑:程序博客网 时间:2024/06/06 01:57
主体结构:行车记录仪的代码逻辑全部放在PreviewService中,UI显示也在其中,UI的显示只是在Service中,通过WindowManager实现的,MainActivvity没有什么作用。
MainActivity中的业务逻辑:
- ### 1
//在onCreate中启动服务,在服务里面根据action来显示UI Intent intent = new Intent(MainActivity.this, PreviewService.class); intent.putExtra("setup_ui", true); startService(intent);
- ### 2
//收到该广播后,将activity销毁,该广播在PreviewService中发出,隐藏UI返回到主界面@Override public void onReceive(Context context, Intent intent) { String msg = intent.getAction(); if (msg.equals(ACTION_FINISH_SELF)) { if (context instanceof Activity) { ((Activity) context).finish(); } } }
一:随拍的流程:
入口是在onStartCommand中,启动随拍的代码如下:
else if (ACTION_GET_PICTURE_SHARE.equals(intent.getAction())) { if (checkState_window() > 0) { return super.onStartCommand(intent, flags, startId); } state_window = 2; try { if (mIsSharing) return Service.START_STICKY_COMPATIBILITY;; mIsSharing = true; //更新UI,当随拍的时候可以看见类似一个对话框弹出来,实际是用了WindowManager的updateViewLayout方法 updateViewPosition(SW_SHOW_SHARE_PICTURE); recorder.startShotPicture(mShareCallBack); } catch (Exception e) { e.printStackTrace(); } }
- 可以看到,更新完UI之后调用了VideoRecorder的startShotPicture(mShareCallBack)方法,并传入EventCallBack接口的引用;EventCallBack接口的定义如下:
interface IShareCallBack { void onVideoComplete(String strFile); void onPictureShareComplete(String strFile);}public class EventCallBack { private IShareCallBack mCallBack; public void onPictureShareComplete(String strFile) { mCallBack.onPictureShareComplete(strFile); } public void onVideoComplete(String strFile) { mCallBack.onVideoComplete(strFile); } public void setCallBack(IShareCallBack callback) { this.mCallBack = callback; } }
由上可见在EventCallBack类有三个方法,分别是当随拍完成时的回调,当随录完成时的回调,这两个方法中具体业务的实现,是在IShareCallBack接口中定义的,第三个方法是setCallBack,并传入了一个IShareCallBack对象,传入这个对象的同时,要实现 onVideoComplete(String strFile);onPictureShareComplete(String strFile);两个方法,这两个方法即是最终实现完成随拍随录的业务逻辑的。
继续来看VideoRecorder的startShotPicture(mShareCallBack)做了什么事情
public void startShotPicture(EventCallBack shareCallBack) { File fileDir = new File(CAMERA_SHARING_PATH); if (!fileDir.exists()) { fileDir.mkdirs(); } mIsSharePicture = true; persistUtils.setPreviewPushEnable(true); mShareCallBack = shareCallBack; }
- 此方法可看出三个有用的信息,①: persistUtils.setPreviewPushEnable(true);即是让onPreviewCallBack的onPreviewFrame开始出数据,出来的数据用于随拍随录等
②mShareCallBack = shareCallBack;在VideoRecorder;即将EventCallBack传过来。
③mIsSharePicture为true,即当前的状态为随拍的状态,数据从onPreviewFrame出来,再来看onPreviewFrame中做的事情。
//将摄像头的一帧数据传入getSharePictureFilegetSharePictureFile(data);//getSharePictureFile的实现如下:private void getSharePictureFile(byte[] data) { Camera.Parameters parameters = mCamera.getParameters(); //定义文件名 SimpleDateFormat storeDate = new SimpleDateFormat("yyyyMMddHHmmss"); String times = storeDate.format(new Date(System.currentTimeMillis())); mShareFile = CAMERA_SHARING_PATH + times + ".jpg"; File imageFile = new File(mShareFile);//将一个byte[]数组变成一张图片,并存储在磁盘中 VideoEncoder.getFrameJpegFileWithTime(mShareFile, data, parameters.getPreviewFormat(), PREVIEW_WIDTH, PREVIEW_HEIGHT, 1f, isRecording()); mIsSharePicture = false; persistUtils.setPreviewPushEnable(false); //随拍完成 mShareCallBack.onPictureShareComplete(mShareFile); }
接着我们来看VideoEncoder.getFrameJpegFileWithTime方法具体的实现逻辑
public static void getFrameJpegFileWithTime(String jpgFile, byte[] srcData, int format, int width, int height, float zoom, boolean isrec) { File imageFile = new File(jpgFile); BufferedOutputStream bos = null; try { YuvImage yuv = new YuvImage(srcData, format, width, height, null); ByteArrayOutputStream stream = new ByteArrayOutputStream(); yuv.compressToJpeg(new Rect(0, 0, width, height), 100, stream); Bitmap bitmap = BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size()); stream.close(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); bitmap = drawStringToBitmap(bitmap, sdf.format(System.currentTimeMillis()), 40); Matrix matrix = new Matrix(); matrix.postScale(zoom, zoom); Bitmap newBmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); //将处理后的bitmap转化为jpg图片保存在磁盘中 newBmp.compress(Bitmap.CompressFormat.JPEG, 80, bos); srcData = null; bitmap.recycle(); bitmap = null; newBmp.recycle(); newBmp = null; bos.flush(); bos.close(); } catch (IOException e) { e.printStackTrace(); } }
- 可以看到该方法将传过来的byte数组压缩成一个jpg文件并保存在磁盘中,并在图片的右上角将当前的时间写了进去,处理完成之后调用随拍完成的方法onPictureShareComplete(file),这时界面就将处理后的照片显示出来,1S之后退回到主界面,相关代码如下:
@Override public void onPictureShareComplete(final String strFile) { x.task().post(new Runnable() { @Override public void run() { if (pool != null && sourceid != -1) pool.play(sourceid, 1, 1, 0, 0, 1); FileInputStream fs = null; try { fs = new FileInputStream(strFile); } catch (FileNotFoundException e) { e.printStackTrace(); } if (fs != null) { Bitmap bitmap = BitmapFactory.decodeStream(fs); mSharePictureView.setImageBitmap(bitmap); mSharePictureView.setVisibility(View.VISIBLE); } mShareFile = strFile; Message msg = new Message(); msg.what = 2; handlerShare.sendMessage(msg); } }); }
- 当随拍的图片处理完毕之后,显示已经处理的图片显示在界面上,显示完成之后通过handlerShare发送消息,我们来看看handler是怎么处理的
else if (msg.what == 2) { try { Thread.sleep(1000); updateViewPosition(SW_HIDE_SHARE_PICTURE); if (mIsSharing) sendShareBroadcast("com.cxb.sharepicture", mShareFile); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } closeActivity(); }
- 先是将显示的随拍照片隐藏,然后发送一个携带随拍文件路径的广播出去,最后关闭activity,发送出去之后,在Launcher中接收,并通过Mqtt的前台代理将图片发送至服务器。至此,随拍的流程分析完毕
二:随录的流程分析
随录的入口和随拍一样,应该可以确切的说,行车记录仪的绝大多数的业务逻辑都在ostartCommand中,循环录像除外,入口代码如下:
else if (ACTION_GET_VIDEO_SHARE.equals(intent.getAction())) { if (checkState_window() > 0) { return super.onStartCommand(intent, flags, startId); } state_window = 3; try { if (mIsSharing) return Service.START_STICKY_COMPATIBILITY;; mIsSharing = true; updateViewPosition(SW_SHOW_SHARE_VIDEO); } catch (Exception e) { e.printStackTrace(); } }
在此方法中调用了updateViewPosition,直接更新UI
//随录时,显示6S的倒计时 shareTimeLayout.setVisibility(View.VISIBLE); //显示底部的取消按钮 shareButtonLayout.setVisibility(View.VISIBLE); // 在第4秒发送一个检查信号,如果encode在4秒前已结束,则可能由异常引起。如果4前时没结束,但是录制时间没有变化,也可能是encode出现问题 handlerShare.postDelayed(new Runnable() { @Override public void run() { Message msg = new Message(); msg.what = 201601181; handlerShare.sendMessage(msg); } }, 4000); //该方法启动随录,处理具体的业务逻辑 if (recorder != null) recorder.startEncodeVideo(mShareCallBack); //该方法更新UI每300毫秒更新一下时间 new Thread(new ThreadShareTime()).start();
- ### 问题:VideoEncoder的getEncodeTime方法,时间具体是如何计算的,逻辑是怎样的?
recorder.startEncodeVideo(mShareCallBack)方法具体做了哪些事?
public void startEncodeVideo(EventCallBack shareCallBack) { //以当前的时间戳为随录文件的名称 File fileDir = new File(CAMERA_SHARING_PATH); if (!fileDir.exists()) fileDir.mkdirs(); SimpleDateFormat storeDate = new SimpleDateFormat("yyyyMMddHHmmss"); String times = storeDate.format(new Date(System.currentTimeMillis())); mShareCallBack = shareCallBack; //构造方法的作用是初始化VideoReccorder中的参数 mVideoEncoder = new VideoEncoder(PREVIEW_WIDTH, PREVIEW_HEIGHT, 10, 250000); //为视频文件命名 mShareFile = CAMERA_SHARING_PATH + times + ".mp4"; //准备编码 mVideoEncoder.startEncode(mShareFile); //允许onPreviewFrame出数据 persistUtils.setPreviewPushEnable(true); if (state == STATE_IDLE) new Thread(new ThreadShare()).start(); }
我们来看看这个方法具体执行了哪些逻辑:①首先是为随录的文件命名;②然后初始化编码VideoRecorder中的参数,即是VideoEnconder的实例化;③startEncode,这个方法是准备开始编码,将参数都设置好,算是初始化的第二步,④ persistUtils.setPreviewPushEnable(true),此方法允许onPreviewFrame开始出数据,有了数据才可以进行编码;⑤new Thread(new ThreadShare()).start(),将摄像头出来的数据丢给VideoEncoder进行编码,编完6S的视频之后交由Mqtt发送至服务器,随录的主要流程至此完结,之后会对循环录像进行一个剖析
阅读代码涉及的知识点:
WindowManager的使用,SurfaceView的生命周期,AnimationDrawable的使用,Bitmap的处理等
- CarRecorder源码分析(一)随拍随录
- CarRecorder源码解析二(循环录像分析)
- 源码分析(一)
- JUnit源码分析(一)
- osworkflow源码分析(一)
- Log4net源码分析(一)
- Mangos源码分析(一)
- Notepad++源码分析(一)
- Log4net源码分析(一)
- ConcurrentHashMap 源码分析 (一)
- rabbitmq源码分析(一)
- rabbitmq源码分析(一)
- Notepad++源码分析(一)
- ADB源码分析(一)
- Tomcat源码分析(一)
- Nginx源码分析(一)
- FFMPEG源码分析(一)
- ADB源码分析(一)
- JRTPLIB 介绍
- 【.net】DbProviderFactories找不到请求的 .Net Framework 数据提供程序。可能没有安装”的问题
- 在Windows下搭建基于nginx的视频直播和点播系统
- 欢迎使用CSDN-markdown编辑器
- 【Struts】Struts和Servlet比较
- CarRecorder源码分析(一)随拍随录
- 原型模式
- Java 程序员 面试前必备知识
- 在64位的CentOS 6.8上安装系统性能监测工具Monitorix
- Android RxJava 实现RxBus
- Intellij idea破解
- sn集群开发感想
- java发送邮件
- Debug Assertion Failed! Expression: _pFirstBlock == pHead