《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方法來下預覽了,該邏輯執行完成後,咱們相機的預覽就起來了。 ---------------------