/* * Copyright (C) 2015-2017 Intel Corporation * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "RKISP2GraphConfigManager" #include "RKISP2GraphConfigManager.h" #include "RKISP2GraphConfig.h" #include "PlatformData.h" #include "LogHelper.h" #include "PerformanceTraces.h" #include "Camera3Request.h" using std::vector; using std::map; namespace android { namespace camera2 { namespace rkisp2 { /* should support at least 4 streams comparetd to HAL1 */ #define MAX_NUM_STREAMS 4 GraphConfigNodes::GraphConfigNodes() { } GraphConfigNodes::~GraphConfigNodes() { } RKISP2GraphConfigManager::RKISP2GraphConfigManager(int32_t camId, GraphConfigNodes *testNodes) : mCameraId(camId), mIsOnlyEnableMp(false), mGraphConfigPool("RKISP2GraphConfig") { status_t status = mGraphConfigPool.init(MAX_REQ_IN_FLIGHT * 2, RKISP2GraphConfig::reset); if (CC_UNLIKELY(status != OK)) { LOGE("Failed to initialize the pool of GraphConfigs"); } } RKISP2GraphConfigManager::~RKISP2GraphConfigManager() { // Check that all graph config objects are returned to the pool if(!mGraphConfigPool.isFull()) { LOGE("RKISP2GraphConfig pool is missing objects at destruction!"); } } #define streamSizeGT(s1, s2) (((s1)->width * (s1)->height) > ((s2)->width * (s2)->height)) #define streamSizeEQ(s1, s2) (((s1)->width * (s1)->height) == ((s2)->width * (s2)->height)) #define streamSizeGE(s1, s2) (((s1)->width * (s1)->height) >= ((s2)->width * (s2)->height)) status_t RKISP2GraphConfigManager::mapStreamToKey(const std::vector &streams, int& videoStreamCnt, int& stillStreamCnt) { // deal with CAMERA3_STREAM_OUTPUT streams and // CAMERA3_STREAM_BIDIRECTIONAL streams, input streams // are not processed here // Don't support RAW currently // Keep streams in order: BLOB, IMPL, YUV... std::vector availableStreams; camera3_stream_t * blobStream = nullptr; camera3_stream_t * rawStream = nullptr; int yuvNum = 0; int blobNum = 0; for (int i = 0; i < streams.size(); i++) { if ((streams[i]->stream_type != CAMERA3_STREAM_OUTPUT) && (streams[i]->stream_type != CAMERA3_STREAM_BIDIRECTIONAL)) continue; switch (streams[i]->format) { case HAL_PIXEL_FORMAT_BLOB: blobNum++; blobStream = streams[i]; break; case HAL_PIXEL_FORMAT_YCbCr_420_888: yuvNum++; availableStreams.push_back(streams[i]); break; case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: yuvNum++; availableStreams.insert(availableStreams.begin(), streams[i]); break; case HAL_PIXEL_FORMAT_RAW_OPAQUE: rawStream = streams[i]; break; default: LOGE("Unsupported stream format %d", streams.at(i)->format); return BAD_VALUE; } } // Current only one BLOB stream supported and always insert as fisrt stream if (blobStream) { availableStreams.insert(availableStreams.begin(), blobStream); } LOGI("@%s, blobNum:%d, yuvNum:%d", __FUNCTION__, blobNum, yuvNum); // Main output produces full size frames // Secondary output produces small size frames int mainOutputIndex = -1; int secondaryOutputIndex = -1; if (availableStreams.size() == 1) { mainOutputIndex = 0; } else if (availableStreams.size() == 2) { mainOutputIndex = (streamSizeGE(availableStreams[0], availableStreams[1])) ? 0 : 1; secondaryOutputIndex = mainOutputIndex ? 0 : 1; if(availableStreams[secondaryOutputIndex]->width > SP_MAX_WIDTH || availableStreams[secondaryOutputIndex]->height > SP_MAX_HEIGHT) { secondaryOutputIndex = -1; } if((secondaryOutputIndex >= 0) && (availableStreams[secondaryOutputIndex]->width == availableStreams[mainOutputIndex]->width) && (availableStreams[secondaryOutputIndex]->height == availableStreams[mainOutputIndex]->height)) { ALOGD("two stream are the same size, only use Mainpath!!"); secondaryOutputIndex = -1; } } else { mainOutputIndex = 0; // find the maxium size stream for (int i = 0; i < availableStreams.size(); i++) { if (streamSizeGT(availableStreams[i], availableStreams[mainOutputIndex])) mainOutputIndex = i; } for (int i = 0; i < availableStreams.size(); i++) { if (i == mainOutputIndex || availableStreams[i]->width > SP_MAX_WIDTH || availableStreams[i]->height > SP_MAX_HEIGHT) { continue ; } else { /* because ISP can output two different size streams * concurrently, so we prefer to use this feature. */ if(secondaryOutputIndex == -1) secondaryOutputIndex = i; if (streamSizeGT(availableStreams[i], availableStreams[secondaryOutputIndex])) secondaryOutputIndex = i; } } } //in stillcapture case, only enable one path can optimize performance if(mIsOnlyEnableMp) { secondaryOutputIndex = -1; LOGI("@%s : only enable mainpath for some special cases", __FUNCTION__); } LOGD("@%s, mainOutputIndex %d, secondaryOutputIndex %d ", __FUNCTION__, mainOutputIndex, secondaryOutputIndex); mStreamToSinkIdMap[availableStreams[mainOutputIndex]] = GCSS_KEY_IMGU_VIDEO; if (secondaryOutputIndex >= 0) mStreamToSinkIdMap[availableStreams[secondaryOutputIndex]] = GCSS_KEY_IMGU_PREVIEW; if(rawStream) mStreamToSinkIdMap[rawStream] = GCSS_KEY_IMGU_RAW; return OK; } /** * Initialize the state of the RKISP2GraphConfigManager after parsing the stream * configuration. * Perform the first level query to find a subset of settings that fulfill the * constrains from the stream configuration. * * TODO: Pass the new stream config modifier * * \param[in] streams List of streams required by the client. */ status_t RKISP2GraphConfigManager::configStreams(const vector &streams, uint32_t operationMode, int32_t testPatternMode) { PERFORMANCE_ATRACE_NAME("RKISP2GraphConfigManager::configStreams"); HAL_TRACE_CALL(CAM_GLBL_DBG_HIGH); UNUSED(operationMode); status_t ret = OK; //thera may be 4 output streams and 1 input stream in //CTS:testMandatoryReprocessConfigurations //we only support 4 streams max to config std::vector outputStream; for(auto stream : streams) { if(stream->stream_type != CAMERA3_STREAM_INPUT) outputStream.push_back(stream); } CheckError(outputStream.size() > MAX_NUM_STREAMS, BAD_VALUE, "@%s, Maximum number of outputStream %u exceeded: %zu", __FUNCTION__, MAX_NUM_STREAMS, outputStream.size()); /* * regenerate the stream resolutions vector if needed * We do this because we consume this vector for each stream configuration. * This allows us to have sequential stream numbers even when an input * stream is present. */ mStreamToSinkIdMap.clear(); int videoStreamCount = 0, stillStreamCount = 0; ret = mapStreamToKey(outputStream, videoStreamCount, stillStreamCount); CheckError(ret != OK, ret, "@%s, call mapStreamToKey fail, ret:%d", __FUNCTION__, ret); /* * Currently it is enough to refresh information in graph config objects * per stream config. Here we populate all gc objects in the pool. */ std::shared_ptr gc = nullptr; int poolSize = mGraphConfigPool.availableItems(); LOGD("@%s : poolSize:%d", __FUNCTION__, poolSize); for (int i = 0; i < poolSize; i++) { mGraphConfigPool.acquireItem(gc); ret = prepareGraphConfig(gc); CheckError(ret != OK, UNKNOWN_ERROR, "@%s, Failed to prepare graph config", __FUNCTION__); } CheckError(gc.get() == nullptr, UNKNOWN_ERROR, "@%s, Graph config is NULL, BUG!", __FUNCTION__); /** * since we map the max res stream to video, and the little one to preview, so * swapVideoPreview here is always false, by the way, please make sure * the video or still stream size >= preview stream size in graph_settings_.xml, zyc. */ gc->setMediaCtlConfig(mMediaCtl, mImgMediaCtl, false, false); // Get media control config for (size_t i = 0; i < MEDIA_TYPE_MAX_COUNT; i++) { mMediaCtlConfigsPrev[i] = mMediaCtlConfigs[i]; // Reset old values mMediaCtlConfigs[i].mLinkParams.clear(); mMediaCtlConfigs[i].mFormatParams.clear(); mMediaCtlConfigs[i].mSelectionParams.clear(); mMediaCtlConfigs[i].mSelectionVideoParams.clear(); mMediaCtlConfigs[i].mControlParams.clear(); mMediaCtlConfigs[i].mVideoNodes.clear(); mMediaCtlConfigs[i].mParamsOrder.clear(); } ret = gc->getSensorMediaCtlConfig(mCameraId, testPatternMode, &mMediaCtlConfigs[CIO2]); gc->dumpMediaCtlConfig(mMediaCtlConfigs[CIO2]); if (ret != OK) LOGE("Couldn't get mediaCtl config"); ret |= gc->getImguMediaCtlConfig(mCameraId, testPatternMode, &mMediaCtlConfigs[IMGU_COMMON], outputStream); gc->dumpMediaCtlConfig(mMediaCtlConfigs[IMGU_COMMON]); if (ret != OK) LOGE("Couldn't get Imgu mediaCtl config"); return OK; } /** * Prepare graph config object * * Use graph query results as a parameter to getGraph. The result will be given * to graph config object. * * \param[in/out] gc Graph Config object. */ status_t RKISP2GraphConfigManager::prepareGraphConfig(std::shared_ptr gc) { status_t status = OK; status = gc->prepare(this, mStreamToSinkIdMap); return status; } /** * Retrieve the current active media controller configuration for Sensor + ISA * * This method will be removed as we clean up the CaptureUnit * */ const MediaCtlConfig* RKISP2GraphConfigManager::getMediaCtlConfig(RKISP2IStreamConfigProvider::MediaType type) const { if (type >= MEDIA_TYPE_MAX_COUNT) { return nullptr; } /* * The size of this vector should be ideally 1, but in the future we will * have different sensor modes for the high-speed video. We should * know this at stream config time to filter them. * If there is more than one it means that there could be a potential change * of sensor mode for a new request. */ return &mMediaCtlConfigs[type]; } /** * Retrieve the previous media control configuration. */ const MediaCtlConfig* RKISP2GraphConfigManager::getMediaCtlConfigPrev(RKISP2IStreamConfigProvider::MediaType type) const { if (type >= MEDIA_TYPE_MAX_COUNT) { return nullptr; } if (type == CIO2) { if (mMediaCtlConfigsPrev[type].mControlParams.size() < 1) { return nullptr; } } else if (mMediaCtlConfigsPrev[type].mLinkParams.size() < 1) { return nullptr; } return &mMediaCtlConfigsPrev[type]; } std::shared_ptr RKISP2GraphConfigManager::getGraphConfig(Camera3Request &request) { std::shared_ptr gc; status_t status = OK; status = mGraphConfigPool.acquireItem(gc); if (CC_UNLIKELY(status != OK)) { LOGE("Failed to acquire RKISP2GraphConfig from pool!!- BUG"); return gc; } // Init graph config with the current request id gc->init(request.getId()); return gc; } /** * Used at stream configuration time to get the base graph that covers all * the possible request outputs that we have. This is used for pipeline * initialization. */ std::shared_ptr RKISP2GraphConfigManager::getBaseGraphConfig() { std::shared_ptr gc; status_t status = OK; status = mGraphConfigPool.acquireItem(gc); if (CC_UNLIKELY(status != OK || gc.get() == nullptr)) { LOGE("Failed to acquire RKISP2GraphConfig from pool!!- BUG"); return gc; } gc->init(0); return gc; } void RKISP2GraphConfigManager::getHwPathSize(const char* pathName, uint32_t &size) { std::vector ¶ms = mMediaCtlConfigs[IMGU_COMMON].mFormatParams; for (size_t i = 0; i < params.size(); i++) { if(strcmp(pathName, params[i].entityName.data()) == 0) { size = params[i].width * params[i].height; ALOGD("@%s Curconfig : pathName:%s, size:%dx%d", __FUNCTION__, pathName, params[i].width, params[i].height); } } } void RKISP2GraphConfigManager::getSensorOutputSize(uint32_t &size) { std::vector ¶ms = mMediaCtlConfigs[CIO2].mFormatParams; if(params.size() == 1) size = params[0].width * params[0].height; else size = 0; LOGI("@%s Curconfig: senor output size:%dx%d", __FUNCTION__, size == 0 ? 0 : params[0].width, size == 0 ? 0 : params[0].height); } /****************************************************************************** * HELPER METHODS ******************************************************************************/ /** * Check the gralloc hint flags and decide whether this stream should be served * by Video Pipe or Still Pipe */ bool RKISP2GraphConfigManager::isVideoStream(camera3_stream_t *stream) { bool display = false; bool videoEnc = false; display = CHECK_FLAG(stream->usage, GRALLOC_USAGE_HW_COMPOSER); display |= CHECK_FLAG(stream->usage, GRALLOC_USAGE_HW_TEXTURE); display |= CHECK_FLAG(stream->usage, GRALLOC_USAGE_HW_RENDER); videoEnc = CHECK_FLAG(stream->usage, GRALLOC_USAGE_HW_VIDEO_ENCODER); return (display || videoEnc); } void RKISP2GraphConfigManager::dumpMediaCtlConfig(const MediaCtlConfig &config) { size_t i = 0; LOGD("MediaCtl config w=%d ,height=%d" ,config.mCameraProps.outputWidth, config.mCameraProps.outputHeight); for (i = 0; i < config.mLinkParams.size() ; i++) { LOGD("Link Params srcName=%s srcPad=%d ,sinkName=%s, sinkPad=%d enable=%d" ,config.mLinkParams[i].srcName.c_str(), config.mLinkParams[i].srcPad, config.mLinkParams[i].sinkName.c_str(), config.mLinkParams[i].sinkPad, config.mLinkParams[i].enable); } for (i = 0; i < config.mFormatParams.size() ; i++) { LOGD("Format Params entityName=%s pad=%d ,width=%d, height=%d formatCode=%x" ,config.mFormatParams[i].entityName.c_str(), config.mFormatParams[i].pad, config.mFormatParams[i].width, config.mFormatParams[i].height, config.mFormatParams[i].formatCode); } for (i = 0; i < config.mSelectionVideoParams.size() ; i++) { LOGD("Selection video Params entityName=%s type=%d ,target=%d, flag=%d" ,config.mSelectionVideoParams[i].entityName.c_str(), config.mSelectionVideoParams[i].select.type, config.mSelectionVideoParams[i].select.target, config.mSelectionVideoParams[i].select.flags); } for (i = 0; i < config.mSelectionParams.size() ; i++) { LOGD("Selection Params entityName=%s pad=%d ,target=%d, top=%d left=%d width=%d, height=%d" ,config.mSelectionParams[i].entityName.c_str(), config.mSelectionParams[i].pad, config.mSelectionParams[i].target, config.mSelectionParams[i].top, config.mSelectionParams[i].left, config.mSelectionParams[i].width, config.mSelectionParams[i].height); } for (i = 0; i < config.mControlParams.size() ; i++) { LOGD("Control Params entityName=%s controlId=%x ,value=%d, controlName=%s" ,config.mControlParams[i].entityName.c_str(), config.mControlParams[i].controlId, config.mControlParams[i].value, config.mControlParams[i].controlName.c_str()); } } void RKISP2GraphConfigManager::dumpStreamConfig(const vector &streams) { bool display = false; bool videoEnc = false; bool zsl = false; for (size_t i = 0; i < streams.size(); i++) { display = CHECK_FLAG(streams[i]->usage, GRALLOC_USAGE_HW_COMPOSER); display |= CHECK_FLAG(streams[i]->usage, GRALLOC_USAGE_HW_TEXTURE); display |= CHECK_FLAG(streams[i]->usage, GRALLOC_USAGE_HW_RENDER); videoEnc = CHECK_FLAG(streams[i]->usage, GRALLOC_USAGE_HW_VIDEO_ENCODER); zsl = CHECK_FLAG(streams[i]->usage, GRALLOC_USAGE_HW_CAMERA_ZSL); LOGI("stream[%zu] (%s): %dx%d, fmt %s, max buffers:%d, gralloc hints (0x%x) display:%s, video:%s, zsl:%s", i, METAID2STR(android_scaler_availableStreamConfigurations_values, streams[i]->stream_type), streams[i]->width, streams[i]->height, METAID2STR(android_scaler_availableFormats_values, streams[i]->format), streams[i]->max_buffers, streams[i]->usage, display? "YES":"NO", videoEnc? "YES":"NO", zsl? "YES":"NO"); } } } // namespace rkisp2 } // namespace camera2 } // namespace android