《Android Camera架构》
《Android Camera进程间通讯类总结》
《Android Camera模块解析之拍照》
《Android Camera模块解析之视频录制》
《Android Camera原理之CameraDeviceCallbacks回调模块》
《Android Camera原理之openCamera模块(一)》
《Android Camera原理之openCamera模块(二)》
《Android Camera原理之createCaptureSession模块》
《Android Camera原理之setRepeatingRequest与capture模块》
《Android Camera原理之编译》
《Android Camera原理之camera provider启动》
《Android Camera原理之cameraserver与cameraprovider是怎样联系的》
《Android Camera原理之camera service与camera provider session会话与capture request轮转》
《Android Camera原理之camera HAL底层数据结构与类总结》
《Android Camera原理之camera service类与接口关系》android
以前讲解过camera2 api之间的关系以及使用camera2 api拍照的功能,本文咱们讲解一下如何利用camera2 实现录制视频的功能。在讲解功能实现的基础上多探讨一些。
拍照和录制视频的前期功能都是相似的,在拍照以前会有camera preview功能,录制视频以前也是有这个功能的,惟一的不一样就是抓取的数据不一样,拍照抓取的是image,视频抓取的video,数据组织格式不同。git
Android L版本引入了Camera2 api,以前《Android Camera模块解析之拍照》中已经详细介绍了camera2 api主要类之间的调用关系。录制视频主要是调用了CameraDevice与CameraCaptureSession来录制视频,使用一个自定义的TextureView来渲染输出的数据,preview界面使用TextureView来承载。github
- 1.布局中建立一个自定义的TextureView,前文已经介绍了为何使用TextureView来渲染camera preview界面了。
- 2.实现
TextureView.SurfaceTextureListener
方法,监听当前的TextureView来监听Camera preview界面。- 3.实现
CameraDevice.StateCallback
来监听CameraDevice的状态 ,能够监听到Camera device 打开、链接、断开等状态,在这些状态中能够操做录制、中止录制等等。- 4.开始启动camera preview,设置MediaRecorder接受的视频格式。
- 5.使用CameraDevice实例调用
createCaptureRequest(CameraDevice.TEMPLATE_RECORD)
,建立一个CaptureRequest.Builder
对象。- 6.实现
CameraCaptureSession.StateCallback
方法,使用CameraDevice实例调用createCaptureSession(surfaces, new CameraCaptureSession.StateCallback(){})
。- 7.MediaRecorder 的实例调用
start()
和stop()
方法开始视频录制和中止录制操做。- 8.在
onResume()
和onPause()
中作好控制方法。
1.1 设置TextureView显示界面canvas
private TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) { openCamera(width, height); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) { configureTransform(width, height); } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { return true; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { } };
if (mTextureView.isAvailable()) { openCamera(mTextureView.getWidth(), mTextureView.getHeight()); } else { mTextureView.setSurfaceTextureListener(mSurfaceTextureListener); }
在设置SurfaceTextureListener以前,有一个判断,mTextureView.isAvailable()判断d当前TextureView设置的mSurface是否存在,这个mSurface就是SurfaceTexture,SurfaceTexture是从图片流中捕捉图片帧的介质api
public boolean isAvailable() { return mSurface != null; }
这个mSurface是怎么来的呢?缓存
Surface设置流程.jpgsession
在TextureView 绘制的时候,获取当前的Texture绘制层。数据结构
public final void draw(Canvas canvas) { // NOTE: Maintain this carefully (see View#draw) mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN; /* Simplify drawing to guarantee the layer is the only thing drawn - so e.g. no background, scrolling, or fading edges. This guarantees all drawing is in the layer, so drawing properties (alpha, layer paint) affect all of the content of a TextureView. */ if (canvas.isHardwareAccelerated()) { DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas; TextureLayer layer = getTextureLayer(); if (layer != null) { applyUpdate(); applyTransformMatrix(); mLayer.setLayerPaint(mLayerPaint); // ensure layer paint is up to date displayListCanvas.drawTextureLayer(layer); } } }
关键的判断:canvas.isHardwareAccelerated() 硬件加速开启的状况下才能进一步使用TextureView来渲染surface。而后判断当前是否有SurfaceTexture,若是没有的化,构造一个新的SurfaceTexture对象,当前的TextureView就有一个SurfaceTexture对象了。架构
1.2 执行openCameraapp
《Android Camera原理之openCamera模块(一)》
《Android Camera原理之openCamera模块(二)》
两篇文章已经介绍了openCamera的底层逻辑。
manager.openCamera(cameraId, mStateCallback, null);
private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(@NonNull CameraDevice cameraDevice) { mCameraDevice = cameraDevice; startPreview(); mCameraOpenCloseLock.release(); if (null != mTextureView) { configureTransform(mTextureView.getWidth(), mTextureView.getHeight()); } } @Override public void onDisconnected(@NonNull CameraDevice cameraDevice) { mCameraOpenCloseLock.release(); cameraDevice.close(); mCameraDevice = null; } @Override public void onError(@NonNull CameraDevice cameraDevice, int error) { mCameraOpenCloseLock.release(); cameraDevice.close(); mCameraDevice = null; Activity activity = getActivity(); if (null != activity) { activity.finish(); } } };
在获得当前camera device已经onOpened回调以后,咱们真正开始预览功能。
1.3 设置预览
SurfaceTexture texture = mTextureView.getSurfaceTexture(); assert texture != null; texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); Surface previewSurface = new Surface(texture); mPreviewBuilder.addTarget(previewSurface); mCameraDevice.createCaptureSession(Collections.singletonList(previewSurface), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession session) { mPreviewSession = session; updatePreview(); } @Override public void onConfigureFailed(@NonNull CameraCaptureSession session) { Activity activity = getActivity(); if (null != activity) { Toast.makeText(activity, "Failed", Toast.LENGTH_SHORT).show(); } } }, mBackgroundHandler);
Surface封装的SurfaceTexture是CameraDevice预览渲染的主要媒介,image stream渲染回执就在这上面进行的。
mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
这儿是设置预览界面,这是一个CaptureRequest.Builder对象,在camera2 api中CaptureRequest是一个重要的创举,能够设置camera request请求缓存,稍后会讲解这儿的底层原理。
public void createCaptureSession(List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler) throws CameraAccessException { List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size()); for (Surface surface : outputs) { outConfigurations.add(new OutputConfiguration(surface)); } createCaptureSessionInternal(null, outConfigurations, callback, checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null); }
捕获当前的surface流,能够实现渲染出当前camera device前的影像。至此,camera preview流程已经完成工做,接下来开始录制视频的工做。
2.1 设置MediaRecorder属性
private void setUpMediaRecorder() throws IOException { final Activity activity = getActivity(); if (null == activity) { return; } mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); if (mNextVideoAbsolutePath == null || mNextVideoAbsolutePath.isEmpty()) { mNextVideoAbsolutePath = getVideoFilePath(getActivity()); } mMediaRecorder.setOutputFile(mNextVideoAbsolutePath); mMediaRecorder.setVideoEncodingBitRate(10000000); mMediaRecorder.setVideoFrameRate(30); mMediaRecorder.setVideoSize(mVideoSize.getWidth(), mVideoSize.getHeight()); mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); switch (mSensorOrientation) { case SENSOR_ORIENTATION_DEFAULT_DEGREES: mMediaRecorder.setOrientationHint(DEFAULT_ORIENTATIONS.get(rotation)); break; case SENSOR_ORIENTATION_INVERSE_DEGREES: mMediaRecorder.setOrientationHint(INVERSE_ORIENTATIONS.get(rotation)); break; } mMediaRecorder.prepare(); }
- 设置音频和视频源,就是声音从麦克风中取,视频从Surface界面上取,就是从屏幕上取。
- 设置输出文件格式和输出文件。
- 设置视频编码码率和帧率,码率和帧率能够显示当前视频是否卡顿。
- 设置视频宽高。
- 设置音频和视频编码,音频使用 AAC编码,视频使用H264编码。
MediaRecorder.prepare();
执行MediaRecorder.prepare();开始启动MediaRecorder录制。
prepare()函数中主要的工做是设置输出文件File,准备开始IO;设置底层音视频编码缓存,开始执行编码工做。底层的解析放在后续进行。
public void prepare() throws IllegalStateException, IOException { if (mPath != null) { RandomAccessFile file = new RandomAccessFile(mPath, "rw"); try { _setOutputFile(file.getFD()); } finally { file.close(); } } else if (mFd != null) { _setOutputFile(mFd); } else if (mFile != null) { RandomAccessFile file = new RandomAccessFile(mFile, "rw"); try { _setOutputFile(file.getFD()); } finally { file.close(); } } else { throw new IOException("No valid output file"); } _prepare(); }
2.2 开始录制工做
getActivity().runOnUiThread(new Runnable() { @Override public void run() { // UI mButtonVideo.setText(R.string.stop); mIsRecordingVideo = true; // Start recording mMediaRecorder.start(); } });
录制工做须要放在主线程中进行,否则获取不到UI界面的信息。
MediaRecorder涉及到不少native方法,在本文中不一一展开,可是后续详细分析的时候回谈到这些native方法的具体是作什么的。
mMediaRecorder.start();必定要在MediaRecorder.prepare();以后,由于prepare()不设置输出文件和准备音视频编码方式,后续的start()便不能继续工做了。
项目源码:https://github.com/googlesamples/android-Camera2Video
小礼物走一走,来简书关注我