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
Github
issues
BUG
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.
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 lookmAction
initialization
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) {
//省略了代码,这里只看结构 }
};
mAction
isBaseAction
abstract class, existsonStart
,onCaptureStarted
,onCaptureProgressed
, < /span>onCaptureCompleted
etc. method.mHolder
is passed in from the constructor methodCamera2Engine
and implements theActionHolder
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.
mAction
After 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 API
setOnImageAvailableListener
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, calldispatchOnPictureTaken
mListeners
onPictureTaken()
@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