Android camera library CameraView source code analysis (2): taking photos

1 Introduction

During this period, I am usingnatario1/CameraView to implement filtered 预览, a> authors have never fixed it. , some of has entered the deep water area, and it gradually appears that it cannot meet our needs. However, as the project continues to deepen, the use of the packaging is quite in place, it indeed saved us a lot of time in the early stages of the project. Since function. 拍照, 录像
CameraView
CameraView
GithubissuesBUG

What should we do? The project urgently needs to implement related functions, so we can only bite the bullet and read its source code to solve these problems.
And this article is about CameraView the source code analysis of how to take pictures.

The following source code analysis is based onCameraView 2.7.2

implementation("com.otaliastudios:cameraview:2.7.2")

In order to better display on the blog, the code posted in this article has been partially streamlined.

Insert image description here

The entrance to taking photos iscameraView.takePicture(). Let’s start with this method.

2. CameraEngine.takePicture

cameraView.takePicture() will be called to mCameraEngine.takePicture(),
this PictureResult.Stub is a parameter encapsulation class, and one is recreated here a>PictureResult.Stub and pass it into the takePicture() method.
mCameraEngine is CameraEngine abstract class, and the implementation classes are Camera1Engine and Camera2Engine.

public void takePicture() {
    
    
    PictureResult.Stub stub = new PictureResult.Stub();
    mCameraEngine.takePicture(stub);
}

We take Camera2 as an example here. You can see that some parameters are assigned to the stub parameter encapsulation class (摄像头ID、图片格式等), and calledonTakePicture

public  void takePicture(final PictureResult.Stub stub) {
    
    
    final boolean metering = mPictureMetering;
    getOrchestrator().scheduleStateful("take picture", CameraState.BIND,
            new Runnable() {
    
    
		        @Override
		        public void run() {
    
    
		            if (isTakingPicture()) return;
		            if (mMode == Mode.VIDEO) {
    
    
		                throw new IllegalStateException("Can't take hq pictures while in VIDEO mode");
		            }
		            stub.isSnapshot = false;
		            stub.location = mLocation;
		            stub.facing = mFacing;
		            stub.format = mPictureFormat;
		            onTakePicture(stub, metering);
		        }
    });
}

3. onTakePicture

Let’s see nextonTakePicture()

set upRotation

stub.rotation = getAngles().offset(Reference.SENSOR, Reference.OUTPUT, Axis.RELATIVE_TO_SENSOR);

Set the size of the photo taken

 stub.size = getPictureSize(Reference.OUTPUT);

then calls mPictureRecorder.take(), mPictureRecorder is the PictureRecorder interface, and the specific implementation is Full2PictureRecorder , specially used to callCamera2 API to capture pictures.

mPictureRecorder = new Full2PictureRecorder(stub, this, builder,mPictureReader);
mPictureRecorder.take();

Let’s take a look at the complete key code

@EngineThread
@Override
protected void onTakePicture(@NonNull final PictureResult.Stub stub, boolean doMetering) {
    
    
    //...省略不重要代码...

	//设置Rotation
    stub.rotation = getAngles().offset(Reference.SENSOR, Reference.OUTPUT, Axis.RELATIVE_TO_SENSOR);
    //设置设定好拍照图片尺寸
    stub.size = getPictureSize(Reference.OUTPUT);
    
    //...省略不重要代码...
        
    CaptureRequest.Builder builder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
    applyAllParameters(builder, mRepeatingRequestBuilder);
    mPictureRecorder = new Full2PictureRecorder(stub, this, builder,
            mPictureReader);
    mPictureRecorder.take();
}

4. Full2PictureRecorder.take

Let’s see againFull2PictureRecorder.take()

@Override
public void take() {
    
    
    mAction.start(mHolder);
}

This is calledmAction.start(mHolder), let’s take a lookmActioninitialization

4.1 Initialize BaseAction

mAction = new BaseAction() {
    
    
    @Override
    protected void onStart(ActionHolder holder) {
    
     //省略了代码,这里只看结构 }

    @Override
    public void onCaptureStarted(ActionHolder holder,CaptureRequest request) {
    
     //省略了代码,这里只看结构 }

    @Override
    public void onCaptureCompleted(ActionHolder holder,CaptureRequest request,TotalCaptureResult result) {
    
     //省略了代码,这里只看结构 }
};
  • mActionisBaseActionabstract class, existsonStart, onCaptureStarted, onCaptureProgressed, < /span>onCaptureCompleted etc. method.
  • mHolder is passed in from the constructor methodCamera2Engine and implements the ActionHolder interface.

4.2 BaseAction.onStart

After calls mAction.start(mHolder), mAction and mHolder will be associated, that is, BaseAction and Camera2Engine will be associated. The specific code is Camera2Engine.addAction(BaseAction), add it to the Actions list, and call back at the appropriate time. BaseAction’s onCaptureStarted, onCaptureProgressed, onCaptureCompleted methods.

mActionAfter and mHolder are associated, the onStart method will be called. Here are some settings for the builder mPictureBuilder value

@Override
protected void onStart(@NonNull ActionHolder holder) {
    
    
    super.onStart(holder);

	//mPictureBuilder是一个建造者,这里给建造者设置一些值
    mPictureBuilder.addTarget(mPictureReader.getSurface());
    if (mResult.format == PictureFormat.JPEG) {
    
    
        mPictureBuilder.set(CaptureRequest.JPEG_ORIENTATION, mResult.rotation);
    }
    mPictureBuilder.setTag(CameraDevice.TEMPLATE_STILL_CAPTURE);
    
    //应用这个建造者
    holder.applyBuilder(this, mPictureBuilder);
}

Look againonCaptureStarted, calling dispatchOnShutter to call back

@Override
public void onCaptureStarted(@NonNull ActionHolder holder,
                             @NonNull CaptureRequest request) {
    
    
    super.onCaptureStarted(holder, request);
    if (request.getTag() == (Integer) CameraDevice.TEMPLATE_STILL_CAPTURE) {
    
    
        dispatchOnShutter(false);
        setState(STATE_COMPLETED);
    }
}

4.3 BaseAction.onCaptureCompleted

Let’s look at onCaptureCompleted again, mainly because some special processing has been done in the DNG format.

@Override
public void onCaptureCompleted(ActionHolder holder, CaptureRequest request, TotalCaptureResult result) {
    
    
    if (mResult.format == PictureFormat.DNG) {
    
    
        mDngCreator = new DngCreator(holder.getCharacteristics(this), result);
        mDngCreator.setOrientation(ExifHelper.getExifOrientation(mResult.rotation));
        if (mResult.location != null) {
    
    
            mDngCreator.setLocation(mResult.location);
        }
    }
}

It turns out that this is not the key point, so where is the key point?

5. Set OnImageAvailableListener to listen

InFull2PictureRecorder initialization constructor, there is also this sentence

mPictureReader.setOnImageAvailableListener(this, WorkerHandler.get().getHandler());

In of Android, the method is used to register a callback listener for each image Receive notifications when data is available. Camera2 APIsetOnImageAvailableListener

5.1 onImageAvailable callback

Look at theonImageAvailable callback method, which will callandroid.media.ImageReader.acquireNextImage() to obtain image data.
Then if it is in JPEG format, the readJpegImage() method will be called to read the image data
Finally Will calldispatchResult to distribute data.

@Override
public void onImageAvailable(ImageReader reader) {
    
    
    Image image = null;
    try {
    
    
        image = reader.acquireNextImage();
        switch (mResult.format) {
    
    
            case JPEG: readJpegImage(image); break;
            case DNG: readRawImage(image); break;
            default: throw new IllegalStateException("Unknown format: " + mResult.format);
        }
    } catch (Exception e) {
    
    
        mResult = null;
        mError = e;
        dispatchResult();
        return;
    } finally {
    
    
        if (image != null) {
    
    
            image.close();
        }
    }
    
    dispatchResult();
}

5.2 Reading JPEG data

Let’s take a look firstreadJpegImage()method

private void readJpegImage(@NonNull Image image) {
    
    
	//从Iamge中读取数据
    ByteBuffer buffer = image.getPlanes()[0].getBuffer();
    byte[] bytes = new byte[buffer.remaining()];
    buffer.get(bytes);
    mResult.data = bytes;
    
    //根据Exif设置rotation
    mResult.rotation = 0;
    ExifInterface exif = new ExifInterface(new ByteArrayInputStream(mResult.data));
    int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
            ExifInterface.ORIENTATION_NORMAL);
    mResult.rotation = ExifHelper.getOrientation(exifOrientation);
}

5.3 Distribution callback

Looking againdispatchResult will eventually call in CameraView, and this method will traverseCallback list, calldispatchOnPictureTakenmListenersonPictureTaken()

@Override
public void dispatchOnPictureTaken(final PictureResult.Stub stub) {
    
    
	mUiHandler.post(new Runnable() {
    
    
	    @Override
	    public void run() {
    
    
	        PictureResult result = new PictureResult(stub);
	        for (CameraListener listener : mListeners) {
    
    
	            listener.onPictureTaken(result);
	        }
	    }
	});
}

When was and mListeners added? There is a method in CameraView that specifically adds callbacks directly. addCameraListener

public void addCameraListener(CameraListener cameraListener) {
    
    
    mListeners.add(cameraListener);
}

5.4 Set callback

So as long as we add this callback and implement the onPictureTaken method, we can obtain the image information after taking the photo in onPictureTaken().

binding.cameraView.addCameraListener(object : CameraListener() {
    
    
    override fun onPictureTaken(result: PictureResult) {
    
    
        super.onPictureTaken(result)
        //拍照回调
        val bitmap = BitmapFactory.decodeByteArray(result.data, 0, result.data.size)
        bitmap?.also {
    
    
            Toast.makeText(this@Test2Activity, "拍照成功", Toast.LENGTH_SHORT).show()
            //将Bitmap设置到ImageView上
            binding.img.setImageBitmap(it)
            
            val file = getNewImageFile()
            //保存图片到指定目录
            ImageUtils.save(it, file, Bitmap.CompressFormat.JPEG)
        }
    }
})

6. Others

6.1 CameraView source code analysis series

Android Camera Library CameraView Source Code Analysis (1): Preview-CSDN Blog
Android Camera Library CameraView Source Code Analysis (2): Taking Photos-CSDN Blog

Guess you like

Origin blog.csdn.net/EthanCo/article/details/134545086