Zero, preface,

There are two main points today

1). The interface layout and view are modeled after the camera in my phone

2). Simple use of Camera, although Camera has been out of date, but still have a look, from brief to deep next article will introduce the replacement: Camera2 warm reminder: this article multi-picture warning, please watch Wifi ~

The permission application is resolved by itself

<uses-permission android:name="android.permission.CAMERA"/>

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

<uses-permission android:name="android.permission.RECORD_AUDIO"/>

SurfaceView and Camera

1.View drawing principle and limitations of ordinary View
View Redraw the View by refreshing. The Android system sends a VSYNC signal to redraw the screen, and the refresh interval is 16ms. If the View completes all the operations it needs to perform within 16ms, visually, there will be no lag; The other way around. In particular, interfaces that require frequent refreshes, such as games (60FPS +), will constantly block the main thread and cause the interface to stall.Copy the code
To compare The refresh Flush-time thread double-buffering
The common View Take the initiative to Only the main thread There is no
SurfaceView passive Allow child threads There are
SurfaceView is another drawing thread, it does not block the main thread, and it is implemented in the underlying mechanism of double buffering a View needs to refresh frequently, or the refresh of the data processing is heavy (may cause delays), consider using SurfaceView instead. Obviously, the camera needs to be refreshed frequently to capture images all the time, so SurfaceView is a good ideaCopy the code

2. Layout – the entire SurfaceView
<? The XML version = "1.0" encoding = "utf-8"? > <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".CameraActivity"> <SurfaceView android:id="@+id/id_sv_video" android:layout_width="match_parent" android:layout_height="match_parent"/> </android.support.constraint.ConstraintLayout>Copy the code

3. Use of SurfaceView and Camera
public class CameraActivity extends AppCompatActivity implements SurfaceHolder.Callback { @BindView(R.id.id_sv_video) SurfaceView mIdSvVideo; private Camera camera; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); mIdSvVideo.getHolder().addCallback(this); // Open the camera and rotate the display direction 90 degrees camera = camera.open (); camera.setDisplayOrientation(90); } @Override public void surfaceCreated(SurfaceHolder holder) { try { camera.setPreviewDisplay(holder); //Camera+SurfaceHolder camera.startPreview(); } catch (IOException e) {e.printStackTrace(); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { camera.release(); // Release resources}}Copy the code

Ii. Interface layout:

1. This is the built-in Camera layout


2. Download icon:iconfont+


3. Imitation interface

This is my copy of the layout, how specific layout, not the point of this article, look at the source code.


Third, data capture

1. Callback interface in Camera class
1.1 – PreviewCallback

By the test camera. StartPreview (); After that, the onPreviewFrame method of the PreviewCallback keeps calling back

This means that the listening method can get successive frames, which is where the video data comes from

public interface PreviewCallback{
    void onPreviewFrame(byte[] data, Camera camera);
};
Copy the code

1.2 – ShutterCallback

The moment the photo was taken

 @Deprecated
 public interface ShutterCallback{
    void onShutter();
 }
Copy the code

1.3 – PictureCallback

Callback after the photo is taken –data is the picture data

@Deprecated
public interface PictureCallback {
    void onPictureTaken(byte[] data, Camera camera);
};
Copy the code

1.4 – AutoFocusCallback

Autofocus monitoring

@Deprecated
public interface AutoFocusCallback{
    void onAutoFocus(boolean success, Camera camera);
}
Copy the code

2. Common methods
2.1. Photographing methods:takePicture
Camera open() Opens a Camera(generates an object) void startPreview() opens preview void stopPreview() closes preview void release() Frees resources void AutoFocus (AutoFocusCallback CB) autoFocusCopy the code
2.1. Photographing methods:takePicture
---->[Four parameters: TakePicture]------------- * @Param Shutter instant callback * @param RAW callback uncompressed raw data * @param PostView callback and PostView image data * @param */ public final void takePicture(ShutterCallback shutter, PictureCallback raw, PictureCallback postview, PictureCallback jpeg) {----> TakePicture. ------------- public final void takePicture(ShutterCallback shutter, PictureCallback raw, PictureCallback jpeg) { takePicture(shutter, raw, null, jpeg); }Copy the code

4. Realization of the function of taking photos

After taking a picture, ask camera.startpreview (); Start preview again, otherwise the screen won’t move

The file name of the test photo is dead (avoid taking too many test photos…) , you can use the current time as the file name

mIdIvSnap.setOnClickListener(v->{ camera.takePicture(new Camera.ShutterCallback() { @Override public void onShutter() { } }, new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { } }, new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { File pic = FileHelper.get().createFile("pic/hello.jpg"); FileOutputStream fos = null; try { fos = new FileOutputStream(pic); fos.write(data); fos.flush(); fos.close(); camera.startPreview(); } catch (IOException e) { e.printStackTrace(); try { assert fos ! = null; fos.close(); } catch (IOException e1) { e1.printStackTrace(); }}}); });Copy the code

5. Delay taking photos

The idea is to use a Handler to send a delayed message, centering a TextView first and hiding it


5.1: Delay button click effect

When selected, take a photo with a delay of 3s.

private boolean isDelay = false; . / / whether the delay mIdIvDelay setOnClickListener (v - > {if! isDelay) { mIdIvDelay.setImageTintList(ColorStateList.valueOf(0xffEFB90F)); } else { mIdIvDelay.setImageTintList(ColorStateList.valueOf(0xfffffffF)); } isDelay = ! isDelay; });Copy the code

5.2:Handler sends delayed messages
private static final int DEFAULT_DELAY_COUNT = 3 + 1; Private int mCurDelayCount = DEFAULT_DELAY_COUNT; Private mHandler = new Handler() {//Handler @override public void handleMessage(Message MSG) { super.handleMessage(msg); mIdTvCountDown.setText(mCurDelayCount + ""); }}; / / click the photo button mIdIvSnap setOnClickListener (v - > {if! IsDelay) {// takePicture(" PIC /hello.jpg"); return; } mIdTvCountDown.setVisibility(View.VISIBLE); mCurDelayCount = DEFAULT_DELAY_COUNT; mHandler.post(new Runnable() { @Override public void run() { if (mCurDelayCount > 0) { mCurDelayCount--; L.d(mCurDelayCount + L.l()); mHandler.postDelayed(this, 1000); / / delay 1 s mHandler. SendEmptyMessage (0); } else {takePicture(" PIC /hello.jpg"); mIdTvCountDown.setVisibility(View.GONE); }}}); }); /** * @param name /** @param name /** @param name pic/hello.jpg) */ private void takePicture(String name) { camera.takePicture(null, null, (data, camera) -> { File pic = FileHelper.get().createFile(name); FileOutputStream fos = null; try { fos = new FileOutputStream(pic); fos.write(data); fos.flush(); fos.close(); camera.startPreview(); } catch (IOException e) { e.printStackTrace(); try { assert fos ! = null; fos.close(); } catch (IOException e1) { e1.printStackTrace(); }}}); }Copy the code

Iii. Other relevant

1. Auto focus

Click on SurfaceView to autofocus (that is clear)

. / / automatic focusing mIdSvVideo setOnClickListener (v - > {camera. An autoFocus (new camera. AutoFocusCallback () {@ Override public void onAutoFocus(boolean success, Camera camera) { } }); });Copy the code

2. Change the focal length (i.e. zoom in and out)

I default to 10 levels, and then go back to the starting size

private int currZoom; MParameters = camera.getParameters(); / / / * * * Camera Parameters scaling encapsulation * / public void setZoom () {if (mParameters. IsZoomSupported ()) {/ / support zooming try {Camera. The Parameters params  = mParameters; final int maxZoom = params.getMaxZoom(); if (maxZoom == 0) return; currZoom = params.getZoom(); currZoom += maxZoom / 10; if (currZoom > maxZoom) { currZoom = 0; } params.setZoom(currZoom); camera.setParameters(params); String rate = new DecimalFormat("#.0").format(currZoom / (maxZoom / 10 * 2.f) + 1); mIdIvZoom.setText(rate + "x"); } catch (Exception e) { e.printStackTrace(); }} else {toastutil. show(this, "your phone does not support zoom!") ); }}Copy the code

3. The lights

private boolean isFlashLight; / / flash / / flash mIdIvSplash. SetOnClickListener (v - > {if! isFlashLight) { mIdIvSplash.setImageTintList(ColorStateList.valueOf(0xffEFB90F)); } else { mIdIvSplash.setImageTintList(ColorStateList.valueOf(0xfffffffF)); } isFlashLight = ! isFlashLight; mParameters.setFlashMode( isFlashLight? Camera.Parameters.FLASH_MODE_TORCH:Camera.Parameters.FLASH_MODE_OFF); camera.setParameters(mParameters); });Copy the code

Cut the camera

All right, here’s my face…

/ / change camera mIdIvSwitch setOnClickListener (v - > {if! isBack) { mIdIvSwitch.setImageTintList(ColorStateList.valueOf(0xffEFB90F)); } else { mIdIvSwitch.setImageTintList(ColorStateList.valueOf(0xfffffffF)); } changeCamera(isBack ? BACK : FRONT); isBack = ! isBack; }); private void changeCamera(int type) { camera.stopPreview(); camera.release(); camera = openCamera(type); try { camera.setPreviewDisplay(mHolder); camera.setDisplayOrientation(90); } catch (IOException e) {e.printStackTrace(); } camera.startPreview(); } private Camera openCamera(int type) { int frontIndex = -1; int backIndex = -1; int cameraCount = Camera.getNumberOfCameras(); Camera.CameraInfo info = new Camera.CameraInfo(); for (int cameraIndex = 0; cameraIndex < cameraCount; cameraIndex++) { Camera.getCameraInfo(cameraIndex, info); if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { frontIndex = cameraIndex; } else if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) { backIndex = cameraIndex; } } if (type == FRONT && frontIndex ! = -1) { return Camera.open(frontIndex); } else if (type == BACK && backIndex ! = -1) { return Camera.open(backIndex); } return null; }Copy the code

Iv. Collection of video data

The Android Google support PreviewCallback. OnPreviewFrame YUV format commonly used has two kinds:

One is NV21 and one is YV12. Android generally uses the YCbCr_420_SP format (NV21) by default.

1. Achieve the interface effect

Switching between photo and video, under video: start recording when turning red (even number of clicks), stop when turning blue (odd number of clicks)

private boolean isPhoto = true; Private Boolean isRecoding; Private int clickRecordCount = 0; / / record the screen as you click screen number / / switch to recording mIdTvVideo setOnClickListener (v - > {mIdTvVideo. SetTextColor xffefb90f (0); mIdTvPic.setTextColor(0xfffffffF); mIdIvSnap.setImageTintList(ColorStateList.valueOf(0xff0FC2EF)); isPhoto = false; }); / / switch to photo mIdTvPic. SetOnClickListener (v - > {mIdTvVideo. SetTextColor (0 XFFFFFFFF); mIdIvSnap.setImageTintList(ColorStateList.valueOf(0x88ffffff)); mIdTvPic.setTextColor(0xffEFB90F); isPhoto = true; }); / / start button mIdIvSnap. SetOnClickListener (v - > {the if (isPhoto) {takePhoto (); // Take a picture} else {if (clickRecordCount % 2 == 0) {recodeVideo(); // Record} else {stopRecodeVideo(); // stop recording}} clickRecordCount++; }); /** * video */ private void recodeVideo() {isRecoding = true; mIdIvSnap.setImageTintList(ColorStateList.valueOf(0xffff0000)); camera.startPreview(); } private void stopRecodeVideo() {isRecoding = false; mIdIvSnap.setImageTintList(ColorStateList.valueOf(0xff0FC2EF)); } / / video video camera. SetPreviewCallback ((data, camera) -> { if (isRecoding) { L.d("onPreviewFrame--" + Thread.currentThread().getName() + L.l()); //TODO to collect data}});Copy the code

2. The size of the data

Take a picture of more than 2M, video also got? Randomly set two sizes have no effect…

What are the fixed sizes supported by the Camera…

mParameters.setPictureSize(720, 480); / / set the image size mParameters. SetPreviewSize (720, 480); // Set the preview sizeCopy the code

/ / look at the Size of the support List < Camera. The Size > pictureSizes = Camera. The getParameters () getSupportedPictureSizes (); List<Camera.Size> previewSizes = camera.getParameters().getSupportedPreviewSizes(); for (int i = 0; i < pictureSizes.size(); i++) { Camera.Size pSize = pictureSizes.get(i); L.d("PictureSize.width = " + pSize.width + "--------PictureSize.height = " + pSize.height); } for (int i = 0; i < previewSizes.size(); i++) { Camera.Size pSize = previewSizes.get(i); L.d("previewSize.width = " + pSize.width + "-------previewSize.height = " + pSize.height); } PictureSize.width = 5184--------PictureSize.height = 3880 PictureSize.width = 4608--------PictureSize.height = 3456 PictureSize.width = 4608--------PictureSize.height = 2592 PictureSize.width = 4608--------PictureSize.height = 2304 PictureSize.width = 4608--------PictureSize.height = 2176 PictureSize.width = 4608--------PictureSize.height = 2126 PictureSize.width = 4160--------PictureSize.height = 3120 PictureSize.width = 4160--------PictureSize.height = 2340 PictureSize.width = 4000--------PictureSize.height = 3000 PictureSize.width = 3840--------PictureSize.height = 2160 PictureSize.width = 3264--------PictureSize.height = 2448 PictureSize.width = 3264--------PictureSize.height = 1632 PictureSize.width = 3264--------PictureSize.height = 1552 PictureSize.width = 3264--------PictureSize.height = 1504 PictureSize.width = 3200--------PictureSize.height = 2400 PictureSize.width = 2592--------PictureSize.height = 1944 PictureSize.width = 2592--------PictureSize.height = 1940 PictureSize.width = 2592--------PictureSize.height = 1296 PictureSize.width = 2592--------PictureSize.height = 1232 PictureSize.width = 2592--------PictureSize.height = 1458 PictureSize.width = 2560--------PictureSize.height = 1920 PictureSize.width = 2688--------PictureSize.height = 1512 PictureSize.width = 2304--------PictureSize.height = 1728 PictureSize.width = 2304--------PictureSize.height = 1296 PictureSize.width = 2048--------PictureSize.height = 1536 PictureSize.width = 1920--------PictureSize.height = 1080 PictureSize.width = 1840--------PictureSize.height = 1380 PictureSize.width = 1600--------PictureSize.height = 1200 PictureSize.width = 1600--------PictureSize.height = 900 PictureSize.width = 1440--------PictureSize.height = 1080 PictureSize.width = 1280--------PictureSize.height = 960 PictureSize.width = 1280--------PictureSize.height = 768 PictureSize.width = 1280--------PictureSize.height = 720 PictureSize.width = 1024--------PictureSize.height = 768 PictureSize.width = 800--------PictureSize.height = 600 PictureSize.width = 800--------PictureSize.height = 480 PictureSize.width = 720--------PictureSize.height = 480 PictureSize.width = 640--------PictureSize.height = 480 PictureSize.width = 352--------PictureSize.height = 288 PictureSize.width = 320--------PictureSize.height = 240 PictureSize.width = 176--------PictureSize.height = 144 previewSize.width = 2160-------previewSize.height = 1080 previewSize.width = 1920-------previewSize.height = 1080 previewSize.width = 1600-------previewSize.height = 900 previewSize.width = 1520-------previewSize.height = 720 previewSize.width = 1440-------previewSize.height = 1080 previewSize.width = 1280-------previewSize.height = 960 previewSize.width = 1280-------previewSize.height = 720 previewSize.width = 960-------previewSize.height = 720 previewSize.width = 720-------previewSize.height = 480 previewSize.width = 640-------previewSize.height = 480 previewSize.width = 352-------previewSize.height = 288 previewSize.width = 320-------previewSize.height = 240 previewSize.width = 176-------previewSize.height = 144Copy the code

3. Video data collection

We can’t parse the data yet, so let’s keep it

/ / video video camera. SetPreviewCallback ((data, camera) - > {the if (isRecoding) {collectData (data); }}); /** * @param data */ private void collectData(byte[] data) {try {mfosvideo.write (data); } catch (IOException e) { e.printStackTrace(); }} mFosVideo */ private void recodeVideo() {isRecoding = true; File videoFile = FileHelper.get().createFile("video/hello"); try { mFosVideo = new FileOutputStream(videoFile); } catch (FileNotFoundException e) { e.printStackTrace(); } mIdIvSnap.setImageTintList(ColorStateList.valueOf(0xffff0000)); camera.startPreview(); } /** * Stop video close stream */ private void stopRecodeVideo() {isRecoding = false; mIdIvSnap.setImageTintList(ColorStateList.valueOf(0xff0FC2EF)); try { mFosVideo.flush(); mFosVideo.close(); } catch (IOException e) { e.printStackTrace(); }}Copy the code

V. Collection of video dataCamera+MediaRecorder

MediaRecorder can not only record frequency, but also record video combined with Camera


1. There is also a limit to the size of supported videos
videoSize.width = 2160-------videoSize.height = 1080
videoSize.width = 1920-------videoSize.height = 1080
videoSize.width = 1280-------videoSize.height = 960
videoSize.width = 1440-------videoSize.height = 720
videoSize.width = 1280-------videoSize.height = 720
videoSize.width = 864-------videoSize.height = 480
videoSize.width = 800-------videoSize.height = 480
videoSize.width = 720-------videoSize.height = 480
videoSize.width = 640-------videoSize.height = 480
videoSize.width = 352-------videoSize.height = 288
videoSize.width = 320-------videoSize.height = 240
videoSize.width = 176-------videoSize.height = 144
Copy the code

Video recording assistance class
/** * Author: Zhang Feng Jieteilie <br/> * Time: 2019/1/80008:16:29 <br/> * Email: [email protected]<br/> * Description: */ Public Class VideoRecorderUtils {private MediaRecorder MediaRecorder; private Camera camera; private SurfaceHolder.Callback callback; private SurfaceView surfaceView; private int height, width; public static Point WH_2160X1080 = new Point(2160, 1080); public static Point WH_1920X1080 = new Point(1920, 1080); public static Point WH_1280X960 = new Point(1280, 960); public static Point WH_1440X720 = new Point(1440, 720); public static Point WH_1280X720 = new Point(1280, 720); public static Point WH_864X480 = new Point(864, 480); public static Point WH_800X480 = new Point(800, 480); public static Point WH_720X480 = new Point(720, 480); public static Point WH_640X480 = new Point(640, 480); public static Point WH_352X288 = new Point(352, 288); public static Point WH_320X240 = new Point(320, 240); public static Point WH_176X144 = new Point(176, 144); public void create(SurfaceView surfaceView,Point point) { this.surfaceView = surfaceView; surfaceView.setKeepScreenOn(true); callback = new SurfaceHolder.Callback() { public void surfaceCreated(SurfaceHolder holder) { camera = Camera.open(); width = point.x; height = point.y; mediaRecorder = new MediaRecorder(); } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { doChange(holder); } @Override public void surfaceDestroyed(SurfaceHolder holder) { if (camera ! = null) { camera.release(); camera = null; }}}; surfaceView.getHolder().addCallback(callback); } private void doChange(SurfaceHolder holder) { try { camera.setPreviewDisplay(holder); camera.setDisplayOrientation(90); camera.startPreview(); } catch (IOException e) { e.printStackTrace(); } } public void stopRecord() { mediaRecorder.release(); camera.release(); mediaRecorder = null; camera = Camera.open(); mediaRecorder = new MediaRecorder(); doChange(surfaceView.getHolder()); } public void stop() { if (mediaRecorder ! = null && camera ! = null) { mediaRecorder.release(); camera.release(); } } public void destroy() { if (mediaRecorder ! = null && camera ! = null) { mediaRecorder.release(); camera.release(); mediaRecorder = null; camera = null; }} /** * @param name Name of the video */ public void startRecord(String path, String path, String path) String name) { camera.unlock(); mediaRecorder.setCamera(camera); mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER); mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); mediaRecorder.setVideoEncodingBitRate(700 * 1024); mediaRecorder.setVideoSize(width, height); mediaRecorder.setVideoFrameRate(24); File file = new File(path); if (! file.exists()) { file.mkdirs(); } mediaRecorder.setOutputFile(path + File.separator + name + ".mp4"); File file1 = new File(path + File.separator + name + ".mp4"); if (file1.exists()) { file1.delete(); } mediaRecorder.setPreviewDisplay(surfaceView.getHolder().getSurface() mediaRecorder.setOrientationHint(0); try { mediaRecorder.prepare(); mediaRecorder.start(); } catch (IOException e) { e.printStackTrace(); }}}Copy the code

3. Use of helper classes

To avoid clutter, create a new Activity class

Core methods used:

private boolean isRecording; mVideoRecorderUtils = new VideoRecorderUtils(); mVideoRecorderUtils.create(mIdSvVideo, VideoRecorderUtils.WH_720X480); path = Environment.getExternalStorageDirectory().getAbsolutePath(); mIdIvSnap.setOnClickListener(view -> { if (! isRecording) { mVideoRecorderUtils.startRecord(path, "Video"); } else { mVideoRecorderUtils.stopRecord(); } isRecording = ! isRecording; });Copy the code

I’ll write about the special effects of the Camera


Postscript: Jie wen standard

1. Growth record and Errata of this paper
Program source code The date of note
V0.1 – making The 2018-1-8 Android Multimedia Camera Related operations](www.jianshu.com/p/6db677f9d…)
2. More about me
Pen name QQ WeChat hobby
Zhang Feng Jie te Li 1981462002 zdl1994328 language
My lot My Jane books I’m the nuggets Personal website
3. The statement

1—- This article is originally written by Zhang Fengjie, please note if reproduced

2—- welcome the majority of programming enthusiasts to communicate with each other 3—- personal ability is limited, if there is something wrong welcome to criticize and testify, must be humble to correct 4—- see here, I thank you here for your love and support