《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
Camera操做过程当中最重要的四个步骤:缓存
CameraManager-->openCamera ---> 打开相机
CameraDeviceImpl-->createCaptureSession ---> 建立捕获会话
CameraCaptureSession-->setRepeatingRequest ---> 设置预览界面
CameraDeviceImpl-->capture ---> 开始捕获图片
以前咱们介绍过openCamera模块:
《Android Camera原理之openCamera模块(一)》
《Android Camera原理之openCamera模块(二)》
其中讲到了Camera打开过程当中不少类,Java层的,native层的,捋一捋这些类关系,进一步分析openCamera成功以后执行的CameraDeviceImpl-->createCaptureSession,openCamera执行成功的回调CameraDevice.StateCallback的onOpened(CameraDevice cameraDevice)方法,当前这个CameraDevice参数就是当前已经打开的相机设备。
获取了相机设备,接下来要建立捕捉会话,会话创建成功,能够在当前会话的基础上设置相机预览界面,这时候咱们调整摄像头,就能看到屏幕上渲染的相机输入流了,接下来咱们能够操做拍照片、拍视频等操做。session
上面是createCaptureSession执行流程,涉及到的代码模块流程很是复杂,这儿只是提供了核心的一些流程。接下来咱们会从代码结构和代码功能的基础上讲解这一块的内容。数据结构
1.CameraDeviceImpl->createCaptureSession
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转化为OutputConfiguration,OutputConfiguration是一个描述camera输出数据的类,其中包括Surface和捕获camera会话的特定设置。从其类的定义来看,它是一个实现Parcelable的类,说明其一定要跨进程传输。架构
CameraDeviceImpl->createCaptureSession传入的Surface列表有几个?
这儿的一个Surface表示输出流,Surface表示有多个输出流,咱们有几个显示载体,就须要几个输出流。
对于拍照而言,有两个输出流:一个用于预览、一个用于拍照。
对于录制视频而言,有两个输出流:一个用于预览、一个用于录制视频。
参考源码最好理解了,下面是拍照的时候执行的代码:第一个surface是用于预览的,第二个surface,因为是拍照,因此使用ImageReader对象来获取捕获的图片,ImageReader在构造函数的时候调用nativeGetSurface获取Surface,这个Surface做为拍照的Surface来使用。ide
mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {ss
}
@Override
public void onConfigureFailed(
@NonNull CameraCaptureSession cameraCaptureSession) {
}
}, null
);
视频录制的也是同样的道理,视频录制使用MediaRecorder来获取视频信息,MediaRecorder在构造的时候也调用nativeGetSurface获取Surface。函数
ImageReader->OnImageAvailableListener回调
ImageR饿啊的人能够读取Surface对象的图片数据,将其转化为本地能够识别的数据,图片的长宽、时间信息等等。这些image数据信息的采集后续会详细说明,这儿先一笔带过。this
private class SurfaceImage extends android.media.Image {
public SurfaceImage(int format) {
mFormat = format;
}
@Override
public void close() {
ImageReader.this.releaseImage(this);
}
public ImageReader getReader() {
return ImageReader.this;
}
private class SurfacePlane extends android.media.Image.Plane {
private SurfacePlane(int rowStride, int pixelStride, ByteBuffer buffer) {
//......
}
final private int mPixelStride;
final private int mRowStride;
private ByteBuffer mBuffer;
}
private long mNativeBuffer;
private long mTimestamp;
private int mTransform;
private int mScalingMode;
private SurfacePlane[] mPlanes;
private int mFormat = ImageFormat.UNKNOWN;
// If this image is detached from the ImageReader.
private AtomicBoolean mIsDetached = new AtomicBoolean(false);
private synchronized native SurfacePlane[] nativeCreatePlanes(int numPlanes,
int readerFormat);
private synchronized native int nativeGetWidth();
private synchronized native int nativeGetHeight();
private synchronized native int nativeGetFormat(int readerFormat);
private synchronized native HardwareBuffer nativeGetHardwareBuffer();
}
在准备拍照以前,还会设置一下ImageReader的OnImageAvailableListener回调接口,调用setOnImageAvailableListener(OnImageAvailableListener listener, Handler handler)设置当前的OnImageAvailableListener 对象。这儿回调接口只有一个onImageAvailable函数,表示当前的捕捉的image已经可用了。而后咱们在onImageAvailable回调函数中操做当前捕获的图片。spa
public interface OnImageAvailableListener {
void onImageAvailable(ImageReader reader);
}
2.CameraDeviceImpl->createCaptureSessionInternal
private void createCaptureSessionInternal(InputConfiguration inputConfig,
List<OutputConfiguration> outputConfigurations,
CameraCaptureSession.StateCallback callback, Executor executor,
int operatingMode, CaptureRequest sessionParams)
传入的几个参数中,inputConfig为null,咱们只需关注outputConfigurations便可。
createCaptureSessionInternal函数中代码不少,可是重要的就是执行配置Surface.net
configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations,
operatingMode, sessionParams);
if (configureSuccess == true && inputConfig != null) {
input = mRemoteDevice.getInputSurface();
}
若是配置surface成功,返回一个Input surface这个input surface是用户本地设置的一个输入流。接下来这个input对象会在构造CameraCaptureSessionImpl对象时被传入。 具体参考4.CameraCaptureSessionImpl构造函数
3.CameraDeviceImpl->configureStreamsChecked
下面这张图详细列出配置输入输出流函数中执行的主要步骤,因为当前的inputConfig为null,因此核心的执行就是下面粉红色框中的过程——建立输出流
mRemoteDevice.beginConfigure();与mRemoteDevice.endConfigure(operatingMode, null);中间的过程是IPC通知service端告知当前正在处理输入输出流。执行完mRemoteDevice.endConfigure(operatingMode, null);返回success = true;若是中间被终端了,那么success确定不为true。
3.1 检查输入流
checkInputConfiguration(inputConfig);
当前inputConfig为null,因此这部分不执行。
3.2 检查输出流
检查当前缓存的输出流数据列表,若是当前的输出流信息已经在列表中,则没必要要从新建立流,若是没有则须要建立流。
// Streams to create
HashSet<OutputConfiguration> addSet = new HashSet<OutputConfiguration>(outputs);
// Streams to delete
List<Integer> deleteList = new ArrayList<Integer>();
for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
int streamId = mConfiguredOutputs.keyAt(i);
OutputConfiguration outConfig = mConfiguredOutputs.valueAt(i);
if (!outputs.contains(outConfig) || outConfig.isDeferredConfiguration()) {
deleteList.add(streamId);
} else {
addSet.remove(outConfig); // Don't create a stream previously created
}
}
private final SparseArray<OutputConfiguration> mConfiguredOutputs = new SparseArray<>();
mConfiguredOutputs是内存中的输出流缓存列表,每次建立输出流都会把streamId和输出流缓存在这个SparseArray中。
这个部分代码操做完成以后:
addSet就是要即将要建立输出流的集合列表。
deleteList就是即将要删除的streamId列表,保证当前mConfiguredOutputs列表中的输出流数据是最新可用的。
下面是删除过时输出流的地方:
// Delete all streams first (to free up HW resources)
for (Integer streamId : deleteList) {
mRemoteDevice.deleteStream(streamId);
mConfiguredOutputs.delete(streamId);
}
下面是建立输出流的地方:
// Add all new streams
for (OutputConfiguration outConfig : outputs) {
if (addSet.contains(outConfig)) {
int streamId = mRemoteDevice.createStream(outConfig);
mConfiguredOutputs.put(streamId, outConfig);
}
}
3.3 mRemoteDevice.createStream(outConfig)
根据《Android Camera进程间通讯类总结》中分析的状况,这个IPC调用直接调用到CameraDeviceClient.h中的virtual binder::Status createStream( const hardware::camera2::params::OutputConfiguration &outputConfiguration, /*out*/ int32_t* newStreamId = NULL) override;
其实第一个参数outputConfiguration表示输出surface,第2个参数是out属性的,表示IPC执行以后返回的参数。该方法中主要就是下面的这段代码了。
const std::vector<sp<IGraphicBufferProducer>>& bufferProducers =
outputConfiguration.getGraphicBufferProducers();
size_t numBufferProducers = bufferProducers.size();
//......
for (auto& bufferProducer : bufferProducers) {
//......
sp<Surface> surface;
res = createSurfaceFromGbp(streamInfo, isStreamInfoValid, surface, bufferProducer,
physicalCameraId);
//......
surfaces.push_back(surface);
}
//......
int streamId = camera3::CAMERA3_STREAM_ID_INVALID;
std::vector<int> surfaceIds;
err = mDevice->createStream(surfaces, deferredConsumer, streamInfo.width,
streamInfo.height, streamInfo.format, streamInfo.dataSpace,
static_cast<camera3_stream_rotation_t>(outputConfiguration.getRotation()),
&streamId, physicalCameraId, &surfaceIds, outputConfiguration.getSurfaceSetID(),
isShared);
for循环中使用outputConfiguration.getGraphicBufferProducers()获得的GraphicBufferProducers建立出对应的surface,同时会对这些surface对象进行判断,检查它们的合法性,合法的话就会将它们加入到surfaces集合中,而后调用mDevice->createStream进一步执行流的建立。
这里就要说一说Android显示系统的一些知识了,你们要清楚,Android上最终绘制在屏幕上的buffer都是在显存中分配的,而除了这部分外,其余都是在内存中分配的,buffer管理的模块有两个,一个是framebuffer,一个是gralloc,framebuffer用来将渲染好的buffer显示到屏幕上,而gralloc用于分配buffer,咱们相机预览的buffer轮转也不例外,它所申请的buffer根上也是由gralloc来分配的,在native层的描述是一个private_handle_t指针,而中间会通过多层的封装,这些buffer都是共享的。
只不过它的生命周期中的某个时刻只能属于一个全部者,而这些全部者的角色在不断的变换,这也就是Android中最经典的生产者--消费者的循环模型了,生产者就是BufferProducer,消费者就是BufferConsumer,每个buffer在它的生命周期过程当中转换时都会被锁住,这样它的全部者角色发生变化,而其余对象想要修改它就不可能了,这样就保证了buffer同步。
参考:https://blog.csdn.net/sinat_22657459/article/details/79370295
3.4 mDevice->createStream
首先将上一步传入的surface,也就是之后的consumer加入到队列中,而后调用重载的createStream方法进一步处理。这里的参数width就表示咱们要配置的surface的宽度,height表示高度,format表示格式,这个format格式是根据surface查询ANativeWindow获取的——anw->query(anw, NATIVE_WINDOW_FORMAT, &format)咱们前面已经说过,dataSpace的类型为android_dataspace,它表示咱们buffer轮转时,buffer的大小,接下来定义一个Camera3OutputStream局部变量,这个也就是咱们说的配置流了,接下来的if/else判断会根据咱们的意图,建立不一样的流对象,好比咱们要配置拍照流,它的format格式为HAL_PIXEL_FORMAT_BLOB,因此就执行第一个if分支,建立一个Camera3OutputStream,建立完成后,执行*id = mNextStreamId++,给id指针赋值,这也就是当前流的id了,因此它是递增的。通常状况下,mStatus的状态在initializeCommonLocked()初始化经过调用internalUpdateStatusLocked方法被赋值为STATUS_UNCONFIGURED状态,因此这里的switch/case分支中就进入case STATUS_UNCONFIGURED,而后直接break跳出了,因此局部变量wasActive的值为false,最后直接返回OK。
到这里,createStream的逻辑就执行完成了,仍是要提醒你们,createStream的逻辑是在framework中的for循环里执行的,咱们的建立至关于只配置了一个surface,若是有多个surface的话,这里会执行屡次,相应的Camera3OutputStream流的日志也会打印屡次,这对于你们定位问题也很是有帮助。
3.5 mRemoteDevice.endConfigure
binder::Status CameraDeviceClient::endConfigure(int operatingMode,
const hardware::camera2::impl::CameraMetadataNative& sessionParams) {
//......
status_t err = mDevice->configureStreams(sessionParams, operatingMode);
//......
}
这里传入的第二个参与通常为null,调用到
Camera3Device::configureStreams
--->Camera3Device::filterParamsAndConfigureLocked
--->Camera3Device::configureStreamsLocked
Camera3Device::configureStreamsLocked中会直接调用HAL层的配置流方法:res = mInterface->configureStreams(sessionBuffer, &config, bufferSizes);
完成输入流的配置
if (mInputStream != NULL && mInputStream->isConfiguring()) {
res = mInputStream->finishConfiguration();
if (res != OK) {
CLOGE("Can't finish configuring input stream %d: %s (%d)",
mInputStream->getId(), strerror(-res), res);
cancelStreamsConfigurationLocked();
return BAD_VALUE;
}
}
完成输出流的配置
for (size_t i = 0; i < mOutputStreams.size(); i++) {
sp<Camera3OutputStreamInterface> outputStream =
mOutputStreams.editValueAt(i);
if (outputStream->isConfiguring() && !outputStream->isConsumerConfigurationDeferred()) {
res = outputStream->finishConfiguration();
if (res != OK) {
CLOGE("Can't finish configuring output stream %d: %s (%d)",
outputStream->getId(), strerror(-res), res);
cancelStreamsConfigurationLocked();
return BAD_VALUE;
}
}
}
outputStream->finishConfiguration()
--->Camera3Stream::finishConfiguration
--->Camera3OutputStream::configureQueueLocked
--->Camera3OutputStream::configureConsumerQueueLocked
关于一下Camera3OutputStream::configureConsumerQueueLocked核心的执行步骤:
// Configure consumer-side ANativeWindow interface. The listener may be used
// to notify buffer manager (if it is used) of the returned buffers.
res = mConsumer->connect(NATIVE_WINDOW_API_CAMERA,
/*listener*/mBufferReleasedListener,
/*reportBufferRemoval*/true);
if (res != OK) {
ALOGE("%s: Unable to connect to native window for stream %d",
__FUNCTION__, mId);
return res;
}
mConsumer就是配置时建立的surface,咱们链接mConsumer,而后分配须要的空间大小。下面申请底层的ANativeWindow窗口,这是一个OpenCL 的窗口。对应的Android设备上通常是两种:Surface和SurfaceFlinger
int maxConsumerBuffers;
res = static_cast<ANativeWindow*>(mConsumer.get())->query(
mConsumer.get(),
NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &maxConsumerBuffers);
if (res != OK) {
ALOGE("%s: Unable to query consumer undequeued"
" buffer count for stream %d", __FUNCTION__, mId);
return res;
}
至此,CameraDeviceImpl->configureStreamsChecked分析完成,接下里咱们须要根据配置stream结果来建立CameraCaptureSession
4.CameraCaptureSessionImpl构造函数
try {
// configure streams and then block until IDLE
configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations,
operatingMode, sessionParams);
if (configureSuccess == true && inputConfig != null) {
input = mRemoteDevice.getInputSurface();
}
} catch (CameraAccessException e) {
configureSuccess = false;
pendingException = e;
input = null;
if (DEBUG) {
Log.v(TAG, "createCaptureSession - failed with exception ", e);
}
}
配置流完成以后,返回configureSuccess表示当前配置是否成功。
而后建立CameraCaptureSessionImpl的时候要用到:
CameraCaptureSessionCore newSession = null;
if (isConstrainedHighSpeed) {
ArrayList<Surface> surfaces = new ArrayList<>(outputConfigurations.size());
for (OutputConfiguration outConfig : outputConfigurations) {
surfaces.add(outConfig.getSurface());
}
StreamConfigurationMap config =
getCharacteristics().get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
SurfaceUtils.checkConstrainedHighSpeedSurfaces(surfaces, /*fpsRange*/null, config);
newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++,
callback, executor, this, mDeviceExecutor, configureSuccess,
mCharacteristics);
} else {
newSession = new CameraCaptureSessionImpl(mNextSessionId++, input,
callback, executor, this, mDeviceExecutor, configureSuccess);
}
// TODO: wait until current session closes, then create the new session
mCurrentSession = newSession;
if (pendingException != null) {
throw pendingException;
}
mSessionStateCallback = mCurrentSession.getDeviceStateCallback();
通常执行CameraCaptureSessionImpl构造函数。
CameraCaptureSessionImpl(int id, Surface input,
CameraCaptureSession.StateCallback callback, Executor stateExecutor,
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
Executor deviceStateExecutor, boolean configureSuccess) {
if (callback == null) {
throw new IllegalArgumentException("callback must not be null");
}
mId = id;
mIdString = String.format("Session %d: ", mId);
mInput = input;
mStateExecutor = checkNotNull(stateExecutor, "stateExecutor must not be null");
mStateCallback = createUserStateCallbackProxy(mStateExecutor, callback);
mDeviceExecutor = checkNotNull(deviceStateExecutor,
"deviceStateExecutor must not be null");
mDeviceImpl = checkNotNull(deviceImpl, "deviceImpl must not be null");
mSequenceDrainer = new TaskDrainer<>(mDeviceExecutor, new SequenceDrainListener(),
/*name*/"seq");
mIdleDrainer = new TaskSingleDrainer(mDeviceExecutor, new IdleDrainListener(),
/*name*/"idle");
mAbortDrainer = new TaskSingleDrainer(mDeviceExecutor, new AbortDrainListener(),
/*name*/"abort");
if (configureSuccess) {
mStateCallback.onConfigured(this);
if (DEBUG) Log.v(TAG, mIdString + "Created session successfully");
mConfigureSuccess = true;
} else {
mStateCallback.onConfigureFailed(this);
mClosed = true; // do not fire any other callbacks, do not allow any other work
Log.e(TAG, mIdString + "Failed to create capture session; configuration failed");
mConfigureSuccess = false;
}
}
构造函数执行的最后能够看到,当前configureSuccess=true,执行mStateCallback.onConfigureFailed(this),若是失败,执行mStateCallback.onConfigureFailed(this)回调。
小结 createCaptureSession的过程就分析完了,它是咱们相机预览最重要的条件,通常session建立成功,那么咱们的预览就会正常,session建立失败,则预览必定黑屏,你们若是有碰到相机黑屏的问题,最大的疑点就是这里,session建立完成后,framework会经过CameraCaptureSession.StateCallback类的public abstract void onConfigured(@NonNull CameraCaptureSession session)回调到应用层,通知咱们session建立成功了,那么咱们就可使用回调方法中的CameraCaptureSession参数,调用它的setRepeatingRequest方法来下预览了,该逻辑执行完成后,咱们相机的预览就起来了。 ---------------------