blob: b99d2fe56c12113bb4add1a7025e558a24ba4ce5 [file] [log] [blame]
/*
* Copyright (C) 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 "GraphConfig"
#include "GraphConfig.h"
#include "GraphConfigManager.h"
#include "LogHelper.h"
#include "FormatUtils.h"
#include "NodeTypes.h"
#include "Camera3GFXFormat.h"
#include "PlatformData.h"
#include <GCSSParser.h>
#include <v4l2device.h>
#include <linux/v4l2-subdev.h>
#include <algorithm>
#include "MediaEntity.h"
using GCSS::GraphConfigNode;
using std::string;
using std::vector;
using std::map;
using std::set;
namespace android {
namespace camera2 {
extern int32_t gDumpType;
namespace gcu = graphconfig::utils;
// TODO: Change the format attribute natively as integer attribute
#ifndef VIDEO_RECORDING_FORMAT
#define VIDEO_RECORDING_FORMAT TILE
#endif
#define MEDIACTL_PAD_OUTPUT_NUM 2
#define MEDIACTL_PAD_VF_NUM 3
#define MEDIACTL_PAD_PV_NUM 4
#define SCALING_FACTOR 1
const string csi2_without_port = "rockchip-sy-mipi-dphy";
/* isp port name*/
const string MEDIACTL_INPUTNAME = "input";
const string MEDIACTL_OUTPUTNAME = "output";
/*video entity name*/
const string MEDIACTL_PARAMETERNAME = "rkisp1-input-params";
const string MEDIACTL_VIDEONAME = "rkisp1_mainpath";
const string MEDIACTL_STILLNAME = "rkisp1_mainpath";
const string MEDIACTL_PREVIEWNAME = "rkisp1_selfpath";
const string MEDIACTL_POSTVIEWNAME = "postview";
const string MEDIACTL_STATNAME = "rkisp1-statistics";
GraphConfig::GraphConfig() :
mManager(nullptr),
mSettings(nullptr),
mReqId(0),
mMetaEnabled(false),
mFallback(false),
mPipeType(PIPE_PREVIEW),
mSourceType(SRC_NONE)
{
//CLEAR(mProgramGroup);
mSourcePortName.clear();
mSinkPeerPort.clear();
mStreamToSinkIdMap.clear();
mStream2TuningMap.clear();
createKernelListStructures();
mCSIBE = CSI_BE + "0";
mMainNodeName.clear();
mSecondNodeName.clear();
}
GraphConfig::~GraphConfig()
{
fullReset();
}
/*
* Full reset
* This is called whenever we want to reset the whole object. Currently that
* is only, when GraphConfig object is destroyed.
*/
void GraphConfig::fullReset()
{
mSourcePortName.clear();
mSinkPeerPort.clear();
mStreamToSinkIdMap.clear();
mStreamIds.clear();
deleteKernelInfo();
delete mSettings;
mSettings = nullptr;
mManager = nullptr;
mReqId = 0;
mStream2TuningMap.clear();
}
/*
* Reset
* This is called per frame
*/
void GraphConfig::reset(GraphConfig *me)
{
if (CC_LIKELY(me != nullptr)) {
me->mReqId = 0;
} else {
LOGE("Trying to reset a null GraphConfig - BUG!");
}
}
void GraphConfig::deleteKernelInfo() {
}
void GraphConfig::createKernelListStructures()
{
}
const GCSS::IGraphConfig* GraphConfig::getInterface(Node *node) const
{
if (!node)
return nullptr;
return node;
}
const GCSS::IGraphConfig* GraphConfig::getInterface() const
{
return mSettings;
}
/**
* Per frame initialization of graph config.
* Updates request id
* \param[in] reqId
*/
void GraphConfig::init(int32_t reqId)
{
mReqId = reqId;
}
/**
* Prepare graph config once per stream config.
* \param[in] manager
* \param[in] settings
* \param[in] streamToSinkIdMap
* \param[in] active
*/
status_t GraphConfig::prepare(GraphConfigManager *manager,
Node *settings,
StreamToSinkMap &streamToSinkIdMap,
bool fallback)
{
mStreamIds.clear();
mManager = manager;
mSettings = settings;
mFallback = fallback;
status_t ret = OK;
if (CC_UNLIKELY(settings == nullptr)) {
LOGW("Settings is nullptr!! - BUG?");
return UNKNOWN_ERROR;
}
ret = analyzeSourceType();
if (ret != OK) {
LOGE("Failed to analyze source type");
return ret;
}
ret = getActiveOutputPorts(streamToSinkIdMap);
if (ret != OK) {
LOGE("Failed to get output ports");
return ret;
}
ret = generateKernelListsForStreams();
if (ret != OK) {
LOGE("Failed to generate kernel list");
return ret;
}
calculateSinkDependencies();
storeTuningModes();
return ret;
}
/**
* Store the tuning modes for each stream id into a map that can be used on a
* per frame basis.
* This method is executed once per stream configuration.
* The tuning mode is used by AIC to find the correct tuning tables in CPF.
*
*/
void GraphConfig::storeTuningModes()
{
GraphConfigNode::const_iterator it = mSettings->begin();
css_err_t ret = css_err_none;
GraphConfigNode *result = nullptr;
int32_t tuningMode = 0;
int32_t streamId = 0;
mStream2TuningMap.clear();
while (it != mSettings->end()) {
ret = mSettings->getDescendant(GCSS_KEY_TYPE, "program_group", it, &result);
if (ret == css_err_none) {
ret = result->getValue(GCSS_KEY_STREAM_ID, streamId);
if (ret != css_err_none) {
string pgName;
// This should not fail
ret = result->getValue(GCSS_KEY_NAME, pgName);
LOGW("Failed to find stream id for PG %s", pgName.c_str());
continue;
}
tuningMode = 0; // default value in case it is not found
ret = result->getValue(GCSS_KEY_TUNING_MODE, tuningMode);
if (ret != css_err_none) {
string pgName;
// This should not fail
ret = result->getValue(GCSS_KEY_NAME, pgName);
LOGW("Failed t find tuning mode for PG %s, defaulting to %d",
pgName.c_str(), tuningMode);
}
mStream2TuningMap[streamId] = tuningMode;
}
}
}
/**
* Retrieve the tuning mode associated with a given stream id.
*
* The tuning mode is defined by IQ-studio and represent and index to different
* set of tuning parameters in the AIQB (a.k.a CPF)
*
* The tuning mode is an input parameter for AIC.
* \param [in] streamId Identifier for the branch (video/still/isa)
* \return tuning mode, if stream id is not found defaults to 0
*/
int32_t GraphConfig::getTuningMode(int32_t streamId)
{
auto item = mStream2TuningMap.find(streamId);
if (item != mStream2TuningMap.end()) {
return item->second;
}
LOGW("Could not find tuning mode for requested stream id %d", streamId);
return 0;
}
/*
* According to the node, analyze the source type:
* TPG or sensor
*/
status_t GraphConfig::analyzeSourceType()
{
css_err_t ret = css_err_none;
Node *inputDevNode = nullptr;
ret = mSettings->getDescendant(GCSS_KEY_SENSOR, &inputDevNode);
if (ret == css_err_none) {
mSourceType = SRC_SENSOR;
mSourcePortName = SENSOR_PORT_NAME;
} else {
LOG1("No sensor node from the graph");
}
return OK;
}
/**
* Finds the sink nodes and the output port peer. Use streamToSinkIdMap
* since we are intrested only in sinks that serve a stream. Takes an
* internal copy of streamToSinkIdMap to be used later.
*
* \param[in] streamToSinkIdMap to get the virtual sink id from a client stream pointer
* \return OK in case of success.
* \return UNKNOWN_ERROR or BAD_VALUE in case of fail.
*/
status_t GraphConfig::getActiveOutputPorts(const StreamToSinkMap &streamToSinkIdMap)
{
status_t status = OK;
css_err_t ret = css_err_none;
NodesPtrVector sinks;
mStreamToSinkIdMap.clear();
mStreamToSinkIdMap = streamToSinkIdMap;
mSinkPeerPort.clear();
StreamToSinkMap::const_iterator it;
it = streamToSinkIdMap.begin();
for (; it != streamToSinkIdMap.end(); ++it) {
sinks.clear();
status = graphGetSinksByName(GCSS::ItemUID::key2str(it->second), sinks);
if (CC_UNLIKELY(status != OK) || sinks.empty()) {
string sinkName = GCSS::ItemUID::key2str(it->second);
LOGE("Found %zu sinks, expecting 1 for sink %s", sinks.size(),
sinkName.c_str());
return BAD_VALUE;
}
Node *sink = sinks[0];
Node *outputPort = nullptr;
// Get the sinkname for getting the output port
string sinkName;
ret = sink->getValue(GCSS_KEY_NAME, sinkName);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to get sink name");
return BAD_VALUE;
}
LOG2("sink name %s", sinkName.c_str());
int32_t streamId = -1;
ret = sink->getValue(GCSS_KEY_STREAM_ID, streamId);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to get stream id");
return BAD_VALUE;
}
LOG2("stream id %d", streamId);
outputPort = getOutputPortForSink(sinkName);
if (outputPort == nullptr) {
LOGE("No output port found for sink");
return UNKNOWN_ERROR;
}
LOG2("output port name %s", NODE_NAME(outputPort));
mSinkPeerPort[sink] = outputPort;
}
return OK;
}
string GraphConfig::getNodeName(Node * node)
{
string nodeName("");
if (node == nullptr) {
LOGE("Node is nullptr");
return nodeName;
}
node->getValue(GCSS_KEY_NAME, nodeName);
return nodeName;
}
/**
* Finds the output port which is the peer to the sink node.
*
* Gets root node, and finds the sink with the given name. Use portGetPeer()
* to find the output port.
* \return GraphConfigNode in case of success.
* \return nullptr in case of fail.
*/
GraphConfig::Node *GraphConfig::getOutputPortForSink(const string &sinkName)
{
css_err_t ret = css_err_none;
status_t retErr = OK;
Node *rootNode = nullptr;
Node *portNode = nullptr;
Node *peerNode = nullptr;
rootNode = mSettings->getRootNode();
if (rootNode == nullptr) {
LOGE("Couldn't get root node, BUG!");
return nullptr;
}
ret = rootNode->getDescendantByString(sinkName, &portNode);
if (ret != css_err_none) {
LOGE("Error getting sink");
return nullptr;
}
retErr = portGetPeer(portNode, &peerNode);
if (retErr != OK) {
LOGE("Error getting peer");
return nullptr;
}
return portNode;
}
/**
* Returns true if the given node is used to output a video record
* stream. The sink name is found and used to find client stream from the
* mStreamToSinkIdMap.
* Then the video encoder gralloc flag is checked from the stream flags of the
* client stream.
* \param[in] peer output port to find the sink node of.
* \return true if sink port serves a video record stream.
* \return false if sink port does not serve a video record stream.
*/
bool GraphConfig::isVideoRecordPort(Node *sink)
{
css_err_t ret = css_err_none;
string sinkName;
camera3_stream_t* clientStream = nullptr;
if (sink == nullptr) {
LOGE("No sink node provided");
return false;
}
ret = sink->getValue(GCSS_KEY_NAME, sinkName);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to get sink name");
return false;
}
// Find the client stream for the sink port
StreamToSinkMap::iterator it1;
it1 = mStreamToSinkIdMap.begin();
for (; it1 != mStreamToSinkIdMap.end(); ++it1) {
if (GCSS::ItemUID::key2str(it1->second) == sinkName) {
clientStream = it1->first;
break;
}
}
if (clientStream == nullptr) {
LOGE("Failed to find client stream");
return false;
}
if (CHECK_FLAG(clientStream->usage, GRALLOC_USAGE_HW_VIDEO_ENCODER)) {
LOG2("%s is video record port", NODE_NAME(sink));
return true;
}
return false;
}
/**
* Takes a stream id, and checks if it exists in the graph.
*
* \param[in] streamId
* \return true if found, false otherwise
*/
bool GraphConfig::hasStreamInGraph(int streamId)
{
status_t status;
StreamsVector streamsFound;
status = graphGetStreamIds(streamsFound);
if (status != OK)
return false;
for (size_t i = 0; i < streamsFound.size(); i++) {
if (streamsFound[i] == streamId)
return true;
}
return false;
}
/**
* check whether the kernel is in this stream
*
* \param[in] streamId stream id.
* \param[in] kernelId kernel id.
* \param[out] whether the kernel in this stream
* \return true the kernel is in this stream
* \return false the kernel isn't in this stream.
*
*/
bool GraphConfig::isKernelInStream(uint32_t streamId, uint32_t kernelId)
{
return false;
}
/**
* get program group id for some kernel
*
* \param[in] streamId stream id.
* \param[in] kernelId kernel pal uuid.
* \param[out] program group id that contain this kernel with the same stream id
* \return error if can't find the kernel id in any ot the PGs in this stream
*/
status_t GraphConfig::getPgIdForKernel(const uint32_t streamId, const int32_t kernelId, int32_t &pgId)
{
css_err_t ret = css_err_none;
status_t retErr;
NodesPtrVector programGroups;
// Get all program groups with the stream id
retErr = streamGetProgramGroups(streamId, programGroups);
if (retErr != OK) {
LOGE("ERROR: couldn't get program groups");
return retErr;
}
// Go through all the program groups with the selected streamID
for (uint32_t i = 0; i < programGroups.size(); i++) {
/* Iterate through program group nodes, find kernel and get the PG id */
GCSS::GraphConfigItem::const_iterator it = programGroups[i]->begin();
while (it != programGroups[i]->end()) {
Node *kernelNode = nullptr;
// Look for kernel with the requested uuid
ret = programGroups[i]->getDescendant(GCSS_KEY_PAL_UUID,
kernelId,
it,
&kernelNode);
if (ret != css_err_none)
continue;
ret = programGroups[i]->getValue(GCSS_KEY_PG_ID, pgId);
if (CC_LIKELY(ret == css_err_none)) {
LOG2("got the pgid:%d for kernel id:%d in stream:%d",
pgId, kernelId, streamId);
return NO_ERROR;
}
LOGE("ERROR: Couldn't get pg id for kernel %d", kernelId);
return BAD_VALUE;
}
}
LOGE("ERROR: Couldn't get pal_uuid");
return BAD_VALUE;
}
/**
* Retrieve all the sinks in the current graph configuration that match the
* input parameter string in their name attribute.
*
* If the name to match is empty it returns all the nodes of type sink
*
* \param[in] name String containing the name to match.
* \param[out] sink List of sinks that match that name
* \return OK in case of success
* \return UNKNOWN_ERROR if no sinks were found in the graph config.
*
*/
status_t GraphConfig::graphGetSinksByName(const std::string &name,
NodesPtrVector &sinks)
{
css_err_t ret = css_err_none;
GraphConfigNode *result;
NodesPtrVector allSinks;
string foundName;
GraphConfigNode::const_iterator it = mSettings->begin();
while (it != mSettings->end()) {
ret = mSettings->getDescendant(GCSS_KEY_TYPE, "sink", it, &result);
if (ret == css_err_none) {
allSinks.push_back(result);
}
}
if (allSinks.empty()) {
LOGE("Failed to find any sinks -check graph config file");
return UNKNOWN_ERROR;
}
/*
* if the name is empty it means client wants all sinks.
*/
if (name.empty()) {
sinks = allSinks;
return OK;
}
for (size_t i = 0; i < allSinks.size(); i++) {
allSinks[i]->getValue(GCSS_KEY_NAME, foundName);
if (foundName.find(name) != string::npos) {
sinks.push_back(allSinks[i]);
}
}
return OK;
}
/*
* Imgu helper functions
*/
status_t GraphConfig::graphGetDimensionsByName(const std::string &name,
int &widht, int &height)
{
widht = 0;
height = 0;
Node *csiBEOutput = nullptr;
// Get csi_be node. If not found, try csi_be_soc. If not found return error.
int ret = mSettings->getDescendantByString(name, &csiBEOutput);
if (ret != css_err_none) {
LOGE("Error: Couldn't find node: %s", name.c_str());
return UNKNOWN_ERROR;
}
ret = getDimensions(csiBEOutput, widht, height);
if (ret != OK) {
LOGE("Error: Couldn't find dimensions from <%s>", name.c_str());
return UNKNOWN_ERROR;
}
return OK;
}
/*
* Imgu helper functions
*/
status_t GraphConfig::graphGetDimensionsByName(const std::string &name,
unsigned short &widht, unsigned short &height)
{
int w, h;
int ret = graphGetDimensionsByName(name, w, h);
widht = w;
height = h;
return ret;
}
/**
* This method creates SinkDependency structure for every active sink found in
* the graph. These structs allow quick access to information that is required
* by other methods.
* Active sinks are the ones that have a connection to an active port.
* This list of active sinks(mSinkPeerPort) has to be filled before this method
* is executed.
* For every virtual sink we store the name (as a key) and the terminal id of
* the input port of the stream associated with that stream. This input port
* will be the destination of the buffers from the capture unit.
*
* This method is used during init()
* If we would have different settings per frame then this would be enough
* to detect the active ISA nodes, but we are not there yet. we are still using
* the base graph settings every frame.
*/
void GraphConfig::calculateSinkDependencies()
{
status_t status = OK;
Node *streamInputPort = nullptr;
NodesPtrVector sinks;
std::string sinkName;
SinkDependency aSinkDependency;
uint32_t stageId; //not needed
mSinkDependencies.clear();
mIsaOutputPort2StreamId.clear();
map<Node*, Node*>::iterator sinkIter = mSinkPeerPort.begin();
for (; sinkIter != mSinkPeerPort.end(); sinkIter++) {
sinkIter->first->getValue(GCSS_KEY_NAME, sinkName);
aSinkDependency.sinkGCKey = GCSS::ItemUID::str2key(sinkName);
aSinkDependency.streamId = sinkGetStreamId(sinkIter->first);
status = streamGetInputPort(aSinkDependency.streamId,
&streamInputPort);
if (CC_UNLIKELY(status != OK)) {
LOGE("Failed to get input port for stream %d associated to sink %s",
aSinkDependency.streamId, sinkName.c_str());
continue;
}
status = portGetFourCCInfo(*streamInputPort,
stageId,
aSinkDependency.streamInputPortId);
if (CC_UNLIKELY(status != OK)) {
LOGE("Failed to get stream %d input port 4CC code",
aSinkDependency.streamId);
continue;
}
LOG2("Adding dependency %s stream id %d", sinkName.c_str(), aSinkDependency.streamId);
mSinkDependencies.push_back(aSinkDependency);
// get the output port of capture unit
Node* isaOutPutPort = nullptr;
status = portGetPeer(streamInputPort, &isaOutPutPort);
if (CC_UNLIKELY(status != OK)) {
LOGE("Fail to get isa output port for sink %s", sinkName.c_str());
continue;
}
std::string fullName;
status = portGetFullName(isaOutPutPort, fullName);
if (CC_UNLIKELY(status != OK)) {
LOGE("Fail to get isa output port name");
continue;
}
int32_t streamId = portGetStreamId(isaOutPutPort);
if (streamId != -1 &&
mIsaOutputPort2StreamId.find(fullName) == mIsaOutputPort2StreamId.end())
mIsaOutputPort2StreamId[fullName] = streamId;
}
}
/**
* This method is used by the GC Manager that has access to the request
* to inform us of what are the active sinks.
* Using the sink dependency information we can then know which ISA ports
* are active for this GC.
*
* Once we have different settings per request then we can incorporate this
* method into calculateSinkDependencies.
*
* \param[in] activeSinks Vector with GCSS_KEY's of the active sinks in a
* request
*/
void GraphConfig::setActiveSinks(std::vector<uid_t> &activeSinks)
{
mIsaActiveDestinations.clear();
uid_t activeDest = 0;
for (size_t i = 0; i < activeSinks.size(); i++) {
for (size_t j = 0; j < mSinkDependencies.size(); j++) {
if (mSinkDependencies[j].sinkGCKey == activeSinks[i]) {
activeDest = mSinkDependencies[j].streamInputPortId;
mIsaActiveDestinations[activeDest] = activeDest;
}
}
}
}
/**
* This method is used by the GC Manager that has access to the request
* to inform us of what will the stream id be used.
* Using the sink dependency information we can then know which stream ids
* are active for this GC.
*
* Once we have different settings per request then we can incorporate this
* method into calculateSinkDependencies.
*
* \param[in] activeSinks Vector with GCSS_KEY's of the active sinks in a
* request
*/
void GraphConfig::setActiveStreamId(const std::vector<uid_t> &activeSinks)
{
mActiveStreamId.clear();
int32_t activeStreamId = 0;
status_t status = NO_ERROR;
for (size_t i = 0; i < activeSinks.size(); i++) {
for (size_t j = 0; j < mSinkDependencies.size(); j++) {
if (mSinkDependencies[j].sinkGCKey == activeSinks[i]) {
activeStreamId = mSinkDependencies[j].streamId;
mActiveStreamId.insert(activeStreamId);
// get the input port for this stream
GraphConfig::Node *port;
GraphConfig::Node *peer;
status = streamGetInputPort(activeStreamId, &port);
if (status != NO_ERROR) {
LOGD("Fail to get input port for this stream %d", activeStreamId);
continue;
}
status = portGetPeer(port, &peer);
if (status != NO_ERROR) {
LOGE("fail to get peer for the port");
continue;
}
// get peer's stream Id
activeStreamId = portGetStreamId(peer);
if (activeStreamId == -1) {
LOGE("fail to get the stream id for %s peer port %s", NODE_NAME(port), NODE_NAME(peer));
continue;
}
if (mActiveStreamId.find(activeStreamId) == mActiveStreamId.end())
mActiveStreamId.insert(activeStreamId);
}
}
}
}
/**
* returns the number of buffers the ISA will produce for a given request.
*/
int32_t GraphConfig::getIsaOutputCount() const
{
return mIsaActiveDestinations.size();
}
bool GraphConfig::isIsaOutputDestinationActive(uid_t destinationPortId) const
{
std::map<uid_t, uid_t>::const_iterator it = mIsaActiveDestinations.begin();
for (; it != mIsaActiveDestinations.end(); ++it) {
if (destinationPortId == it->first) {
return true;
}
}
return false;
}
bool GraphConfig::isIsaStreamActive(int32_t streamId) const
{
if (mActiveStreamId.find(streamId) != mActiveStreamId.end())
return true;
return false;
}
status_t GraphConfig::getActiveDestinations(std::vector<uid_t> &terminalIds) const
{
std::map<uid_t, uid_t>::const_iterator it = mIsaActiveDestinations.begin();
for (; it != mIsaActiveDestinations.end(); ++it) {
terminalIds.push_back(it->first);
}
return OK;
}
/**
* Query the connection info structs for a given pipeline defined by
* stream id.
*
* \param[in] sinkName to be used as key to get pipeline connections
* \param[out] stream id connect with sink
* \param[out] connections for pipeline configuation
* \return OK in case of success.
* \return UNKNOWN_ERROR or BAD_VALUE in case of fail.
* \if sinkName is not supported, NAME_NOT_FOUND is returned.
* \sink name support list as below defined in graph_descriptor.xml
* \<sink name="video0"/>
* \<sink name="video1"/>
* \<sink name="video2"/>
* \<sink name="still0"/>
* \<sink name="still1"/>
* \<sink name="still2"/>
* \<sink name="raw"/>
*/
status_t GraphConfig::pipelineGetInternalConnections(
const std::string &sinkName,
int &streamId,
std::vector<PSysPipelineConnection> &confVector)
{
HAL_TRACE_CALL(CAMERA_DEBUG_LOG_LEVEL2);
status_t status = OK;
css_err_t ret = css_err_none;
NodesPtrVector sinks;
NodesPtrVector programGroups;
NodesPtrVector alreadyConnectedPorts;
Node *peerPort = nullptr;
Node *port = nullptr;
PSysPipelineConnection aConnection;
CLEAR(aConnection);
alreadyConnectedPorts.clear();
status = graphGetSinksByName(sinkName, sinks);
if (CC_UNLIKELY(status != OK) || sinks.empty()) {
LOGD("No %s sinks in graph", sinkName.c_str());
return NAME_NOT_FOUND;
}
streamId = sinkGetStreamId(sinks[0]);
if (CC_UNLIKELY(streamId <= 0)) {
LOGE("Sink node lacks stream id attribute - fix your config");
return BAD_VALUE;
}
status = streamGetProgramGroups(streamId, programGroups);
if (CC_UNLIKELY(status != OK || programGroups.empty())) {
LOGE("No Program groups associated with stream id %d", streamId);
return BAD_VALUE;
}
for (size_t i = 0; i < programGroups.size(); i++) {
Node::const_iterator it = programGroups[i]->begin();
while (it != programGroups[i]->end()) {
ret = programGroups[i]->getDescendant(GCSS_KEY_TYPE, "port",
it, &port);
if (ret != css_err_none)
continue;
/*
* Since we are iterating through the ports
* check if this port is already connected to avoid setting
* the connection twice
*/
if (std::find(alreadyConnectedPorts.begin(),
alreadyConnectedPorts.end(),
port) != alreadyConnectedPorts.end()) {
continue;
}
LOG1("Configuring Port from PG[%zu]", i);
status = portGetFormat(port, aConnection.portFormatSettings);
if (CC_UNLIKELY(status != OK)) {
LOGE("Failed to get port format info in port from PG[%zu] "
"from stream id %d", i, streamId);
return BAD_VALUE;
}
if (aConnection.portFormatSettings.enabled == 0) {
LOG1("Port from PG[%zu] from stream id %d disabled",
i, streamId);
confVector.push_back(aConnection);
continue;
} else {
LOG1("Port: 0x%x format(%dx%d)fourcc: %s bpl: %d bpp: %d",
aConnection.portFormatSettings.terminalId,
aConnection.portFormatSettings.width,
aConnection.portFormatSettings.height,
v4l2Fmt2Str(aConnection.portFormatSettings.fourcc),
aConnection.portFormatSettings.bpl,
aConnection.portFormatSettings.bpp);
}
/*
* for each port get the connection info and pass it
* to the pipeline object
*/
status = portGetConnection(port,
aConnection.connectionConfig,
&peerPort);
if (CC_UNLIKELY(status != OK )) {
LOGE("Failed to create connection info in port from PG[%zu]"
"from stream id %d", i, streamId);
return BAD_VALUE;
}
aConnection.hasEdgePort = false;
if (isPipeEdgePort(port)) {
camera3_stream_t *clientStream = nullptr;
status = portGetClientStream(peerPort, &clientStream);
if (CC_UNLIKELY(status != OK)) {
LOGE("Failed to find client stream for v-sink");
return UNKNOWN_ERROR;
}
aConnection.stream = clientStream;
aConnection.hasEdgePort = true;
}
confVector.push_back(aConnection);
alreadyConnectedPorts.push_back(port);
alreadyConnectedPorts.push_back(peerPort);
}
}
return OK;
}
/**
* Find distinct stream ids from the graph and return them in a vector.
* \param streamIds Vector to be populated with stream ids.
*/
status_t GraphConfig::graphGetStreamIds(StreamsVector &streamIds)
{
GraphConfigNode *result;
int32_t streamId = -1;
css_err_t ret;
GraphConfigNode::const_iterator it = mSettings->begin();
while (it != mSettings->end()) {
bool found = false;
// Find all program groups
ret = mSettings->getDescendant(GCSS_KEY_TYPE, "hw",
it, &result);
if (ret != css_err_none)
continue;
ret = result->getValue(GCSS_KEY_STREAM_ID, streamId);
if (ret != css_err_none)
continue;
// If stream id is not yet in vector, add it
StreamsVector::iterator ite = streamIds.begin();
for(; ite !=streamIds.end(); ++ite) {
if (streamId == *ite) {
found = true;
break;
}
}
if (found)
continue;
streamIds.push_back(streamId);
}
if (streamIds.empty()) {
LOGE("Failed to find any streamIds %d)", streamId);
return UNKNOWN_ERROR;
}
return OK;
}
/**
* Retrieve the stream id associated with a given sink.
* The stream id represents the branch of the PSYS processing nodes that
* precedes this sink.
* This id is used for IQ tunning purposes.
*
* \param[in] sink sink node to query
* \return stream id or -1 in case of error
*/
int32_t GraphConfig::sinkGetStreamId(Node *sink)
{
css_err_t ret = css_err_none;
int32_t streamId = -1;
string type;
if (CC_UNLIKELY(sink == nullptr)) {
LOGE("Invalid Node, cannot get the sink stream id");
return -1;
}
ret = sink->getValue(GCSS_KEY_TYPE, type);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to get Node Type");
return -1;
}
if (CC_UNLIKELY(type != "sink")) {
LOGE("Node is not a sink");
return -1;
}
ret = sink->getValue(GCSS_KEY_STREAM_ID, streamId);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to get stream ID");
return -1;
}
return streamId;
}
int32_t GraphConfig::portGetStreamId(Node *port)
{
css_err_t ret = css_err_none;
GraphConfig::Node *ancestor = nullptr;
int32_t streamId = -1;
if (CC_UNLIKELY(port == nullptr)) {
LOGE("Invalid Node, cannot get the port stream id");
return -1;
}
ret = port->getAncestor(&ancestor);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to get port's ancestor");
return -1;
}
ret = ancestor->getValue(GCSS_KEY_STREAM_ID, streamId);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to get stream ID %s", NODE_NAME(ancestor));
return -1;
}
return streamId;
}
/**
* Retrieve a list of program groups that belong to a given stream id.
* Iterates through the graph configuration storing the program groups
* that match this stream id into the provided vector.
*
* \param[in] streamId Id of the stream to match.
* \param[out] programGroups Vector with the nodes that match the criteria.
*/
status_t GraphConfig::streamGetProgramGroups(int32_t streamId,
NodesPtrVector &programGroups)
{
css_err_t ret = css_err_none;
GraphConfigNode *result;
NodesPtrVector allProgramGroups;
int32_t streamIdFound = -1;
GraphConfigNode::const_iterator it = mSettings->begin();
while (it != mSettings->end()) {
ret = mSettings->getDescendant(GCSS_KEY_TYPE, "hw",
it, &result);
if (ret == css_err_none)
allProgramGroups.push_back(result);
}
if (allProgramGroups.empty()) {
LOGE("Failed to find any HW's for stream id %d"
" BUG(check graph config file)", streamId);
return UNKNOWN_ERROR;
}
for (size_t i = 0; i < allProgramGroups.size(); i++) {
ret = allProgramGroups[i]->getValue(GCSS_KEY_STREAM_ID, streamIdFound);
if ((ret == css_err_none) && (streamIdFound == streamId)) {
programGroups.push_back(allProgramGroups[i]);
}
}
return OK;
}
status_t GraphConfig::streamGetInputPort(int32_t streamId, Node **port)
{
css_err_t ret = css_err_none;
Node *result = nullptr;
Node *pgNode = nullptr;
int32_t streamIdFound = -1;
*port = nullptr;
Node::const_iterator it = mSettings->begin();
while (it != mSettings->end()) {
ret = mSettings->getDescendant(GCSS_KEY_TYPE, "hw",
it, &pgNode);
if (ret != css_err_none)
continue;
ret = pgNode->getValue(GCSS_KEY_STREAM_ID, streamIdFound);
if ((ret == css_err_none) && (streamIdFound == streamId)) {
Node::const_iterator it = pgNode->begin();
while (it != pgNode->end()) {
ret = pgNode->getDescendant(GCSS_KEY_TYPE, "port",
it, &result);
if (ret != css_err_none)
continue;
int32_t direction = portGetDirection(result);
if (direction == PORT_DIRECTION_INPUT) {
/*
* TODO: add check that the port is at the edge of the stream
* this could be done checking that the peer's port is in a PG
* that belongs to a different stream id or to a virtual sink
*
*/
*port = result;
return OK;
}
}
}
}
return (*port == nullptr) ? BAD_VALUE : OK;
}
/**
*
* Traverse the graph settings to find program groups that belong to
* the given stream id.
* collect the output ports of those program groups whose peer has a different
* stream ID.
* It also stores the UID of the peer port of each output port.
* This is useful to detect wither the peer is active or not.
*
* \param [in] streamId The stream id we want to find the output ports from.
* \param [out] outputPorts Vector of pointers to Nodes with the ports.
* \param [out] peerPorts Vector of pointers to Nodes with the peer ports.
*/
status_t
GraphConfig::streamGetConnectedOutputPorts(int32_t streamId,
NodesPtrVector &outputPorts,
NodesPtrVector &peerPorts)
{
css_err_t ret = css_err_none;
status_t status = OK;
Node *pgNode = nullptr;
Node *port;
Node *peer = nullptr;
int32_t streamIdFound = -1;
int32_t peerStreamId = 0;
outputPorts.clear();
peerPorts.clear();
Node::const_iterator it = mSettings->begin();
while (it != mSettings->end()) {
ret = mSettings->getDescendant(GCSS_KEY_TYPE, "program_group",
it, &pgNode);
if (ret != css_err_none)
continue;
ret = pgNode->getValue(GCSS_KEY_STREAM_ID, streamIdFound);
if ((ret == css_err_none) && (streamIdFound == streamId)) {
Node::const_iterator it = pgNode->begin();
while (it != pgNode->end()) {
ret = pgNode->getDescendant(GCSS_KEY_TYPE, "port", it, &port);
if (ret != css_err_none)
continue;
int32_t direction = portGetDirection(port);
if (direction == PORT_DIRECTION_OUTPUT) {
status = portGetPeer(port, &peer);
if (status == INVALID_OPERATION)
continue; // disabled terminal
if (status == OK) {
peerStreamId = portGetStreamId(peer);
if (peerStreamId != streamId) {
outputPorts.push_back(port);
peerPorts.push_back(peer);
}
}
}
}
}
}
if (outputPorts.empty())
LOGW("No outputports for stream %d", streamId);
return OK;
}
/**
* Retrieve the graph config node of the port that is connected to a given port.
*
* \param[in] port Node with the info of the port that we want to find its peer.
* \param[out] peer Pointer to a node where the peer node reference will be
* stored
* \return OK
* \return INVALID_OPERATION if the port is disabled.
* \return BAD_VALUE if any of the graph settings is incorrect.
*/
status_t GraphConfig::portGetPeer(Node *port, Node **peer)
{
css_err_t ret = css_err_none;
int32_t enabled = 1;
string peerName;
if (CC_UNLIKELY(port == nullptr || peer == nullptr)) {
LOGE("Invalid Node, cannot get the peer port");
return BAD_VALUE;
}
ret = port->getValue(GCSS_KEY_ENABLED, enabled);
if (ret == css_err_none && !enabled) {
LOG1("This port is disabled, keep on getting the connection");
return INVALID_OPERATION;
}
ret = port->getValue(GCSS_KEY_PEER, peerName);
if (ret != css_err_none) {
LOGE("Error getting peer attribute");
return BAD_VALUE;
}
ret = mSettings->getDescendantByString(peerName, peer);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to find peer by name %s", peerName.c_str());
return BAD_VALUE;
}
return OK;
}
/**
* Generate the connection configuration information for a given port.
*
* This connection configuration information is required by CIPF to build
* the pipeline
*
* \param[in] port Pointer to the port node
* \param[out] connectionInfo Reference to the connection info object
* \param[out] peerPort Reference to the peer port
* \return OK in case of success
* \return BAD_VALUE in case of error while retrieving the information.
* \return INVALID_OPERATION in case of the port being disabled.
*/
status_t GraphConfig::portGetConnection(Node *port,
ConnectionConfig &connectionInfo,
Node **peerPort)
{
status_t status = OK;
css_err_t ret = css_err_none;
int32_t direction;
status = portGetPeer(port, peerPort);
if (CC_UNLIKELY(status != OK)) {
if (status == INVALID_OPERATION) {
LOGE("Port %s disabled, cannot get the connection",
getNodeName(port).c_str());
} else {
LOGE("Failed to get the peer port for port %s",getNodeName(port).c_str());
}
return status;
}
ret = port->getValue(GCSS_KEY_DIRECTION, direction);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to get port direction");
return BAD_VALUE;
}
/*
* Iterations are not used
*/
connectionInfo.mSinkIteration = 0;
connectionInfo.mSourceIteration = 0;
if (direction == PORT_DIRECTION_INPUT) {
// input port is the sink in a connection
status = portGetFourCCInfo(*port,
connectionInfo.mSinkStage,
connectionInfo.mSinkTerminal);
if (CC_UNLIKELY(status != OK)) {
LOGE("Failed to create fourcc info for sink port");
return BAD_VALUE;
}
if (*peerPort != nullptr && !portIsVirtual(*peerPort)) {
status = portGetFourCCInfo(**peerPort,
connectionInfo.mSourceStage,
connectionInfo.mSourceTerminal);
if (CC_UNLIKELY(status != OK)) {
LOGE("Failed to create fourcc info for source port");
return BAD_VALUE;
}
} else {
connectionInfo.mSourceStage = 0;
connectionInfo.mSourceTerminal = 0;
}
} else {
//output port is the source in a connection
status = portGetFourCCInfo(*port,
connectionInfo.mSourceStage,
connectionInfo.mSourceTerminal);
if (CC_UNLIKELY(status != OK)) {
LOGE("Failed to create fourcc info for source port");
return BAD_VALUE;
}
if (*peerPort != nullptr && !portIsVirtual(*peerPort)) {
status = portGetFourCCInfo(**peerPort,
connectionInfo.mSinkStage,
connectionInfo.mSinkTerminal);
if (CC_UNLIKELY(status != OK)) {
LOGE("Failed to create fourcc info for sink port");
return BAD_VALUE;
}
} else {
connectionInfo.mSinkStage = 0;
connectionInfo.mSinkTerminal = 0;
}
}
return status;
}
/**
* Retrieve the format information of a port
* if the port doesn't have any format set, it gets the format from the peer
* port (i.e. the port connected to this one)
*
* \param[in] port Port to query the format.
* \param[out] format Format settings for this port.
*/
status_t GraphConfig::portGetFormat(Node *port,
PortFormatSettings &format)
{
GraphConfigNode *peerNode = nullptr; // The peer port node
GraphConfigNode *tmpNode = port; // The port node node we are interrogating
css_err_t ret = css_err_none;
uint32_t stageId; // ignored
if (CC_UNLIKELY(port == nullptr)) {
LOGE("Invalid parameter, could not get port format");
return BAD_VALUE;
}
ret = port->getValue(GCSS_KEY_ENABLED, format.enabled);
if (ret != css_err_none) {
// if not present by default is enabled
format.enabled = 1;
}
status_t status = portGetFourCCInfo(*tmpNode, stageId, format.terminalId);
if (CC_UNLIKELY(status != OK)) {
LOGE("Could not get port uid");
return INVALID_OPERATION;
}
// if disabled there is no need to query the format
if (format.enabled == 0) {
return OK;
}
format.width = 0;
format.height = 0;
ret = port->getValue(GCSS_KEY_WIDTH, format.width);
if (ret != css_err_none) {
/*
* It could be the port configuration is not in settings, that is normal
* it means that we need to ask the format from the peer.
*/
ret = portGetPeer(port, &peerNode);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Could not find peer port - Fix your graph");
return BAD_VALUE;
}
tmpNode = peerNode;
ret = tmpNode->getValue(GCSS_KEY_WIDTH, format.width);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Could not find port format info: width (from peer)");
return BAD_VALUE;
}
}
ret = tmpNode->getValue(GCSS_KEY_HEIGHT, format.height);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Could not find port format info: height");
return BAD_VALUE;
}
string fourccFormat;
ret = tmpNode->getValue(GCSS_KEY_FORMAT, fourccFormat);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Could not find port format info: fourcc");
return BAD_VALUE;
}
format.fourcc = get_fourcc(fourccFormat[0],fourccFormat[1],
fourccFormat[2],fourccFormat[3]);
format.bpl = gcu::getBpl(format.fourcc, format.width);
LOG1("bpl set to %d for %s", format.bpl, fourccFormat.c_str());
// if settings are specifying bpl, owerwrite the calculated one
int bplFromSettings = 0;
ret = tmpNode->getValue(GCSS_KEY_BYTES_PER_LINE, bplFromSettings);
if (CC_UNLIKELY(ret == css_err_none)) {
LOG1("Overwriting bpl(%d) from settings %d", format.bpl, bplFromSettings);
format.bpl = bplFromSettings;
}
format.bpp = gcu::getBppFromCommon(format.fourcc);
return OK;
}
/**
* Return the port direction
*
* \param[in] port Reference to port Graph node
* \return 0 if it is an input port
* \return 1 if it is an output port
*/
int32_t GraphConfig::portGetDirection(Node *port)
{
int32_t direction = 0;
css_err_t ret = css_err_none;
ret = port->getValue(GCSS_KEY_DIRECTION, direction);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to retrieve port direction, default to input");
}
return direction;
}
/**
* Return the port full name
* The port full name is made out from:
* - the name program group it belongs to
* - the name of the port
* separated by ":"
*
* \param[in] port Reference to port Graph node
* \param[out] fullName reference to a string to store the full name
*
* \return OK if everything went fine.
* \return BAD_VALUE if any of the graph queries failed.
*/
status_t GraphConfig::portGetFullName(Node *port, string &fullName)
{
string portName, ancestorName;
Node *ancestor;
css_err_t ret = css_err_none;
if (CC_UNLIKELY(port == nullptr)) {
LOGE("Invalid parameter, could not get port full name");
return BAD_VALUE;
}
ret = port->getAncestor(&ancestor);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to retrieve port ancestor");
return BAD_VALUE;
}
ret = ancestor->getValue(GCSS_KEY_NAME, ancestorName);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to get ancestor name for port");
port->dumpNodeTree(port, 1);
return BAD_VALUE;
}
ret = port->getValue(GCSS_KEY_NAME, portName);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to retrieve port name");
return BAD_VALUE;
}
fullName = ancestorName + ":" + portName;
return OK;
}
/**
* Return true if the port is a virtual port, this is the end point
* of the graph.
* Virtual ports are the nodes of type sink.
*
* \param[in] port Reference to port Graph node
* \return true if it is a virtual port
* \return false if it is not a virtual port
*/
bool GraphConfig::portIsVirtual(Node *port)
{
string type;
css_err_t ret = css_err_none;
ret = port->getValue(GCSS_KEY_TYPE, type);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to retrieve port type, default to input");
}
return (type == string("sink"));
}
/**
* For a given port node it constructs the fourCC code used in the connection
* object. This is constructed from the program group id.
*
* \param[in] portNode
* \param[out] stageId Fourcc code that describes the PG where this node is
* contained
* \param[out] terminalID Fourcc code that describes the port, in FW jargon,
* this is a PG terminal.
* \return OK in case of no error
* \return BAD_VALUE in case some error is found
*/
status_t GraphConfig::portGetFourCCInfo(Node &portNode,
uint32_t &stageId, uint32_t &terminalId)
{
Node *pgNode; // The Program group node
css_err_t ret = css_err_none;
int32_t portId;
string type, subsystem;
ret = portNode.getValue(GCSS_KEY_ID, portId);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to get port's id");
portNode.dumpNodeTree(&portNode, 1);
return BAD_VALUE;
}
ret = portNode.getAncestor(&pgNode);
if (CC_UNLIKELY(ret != css_err_none || pgNode == nullptr)) {
LOGE("Failed to get port ancestor");
return BAD_VALUE;
}
ret = pgNode->getValue(GCSS_KEY_TYPE, type);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to get port's ancestor type ");
pgNode->dumpNodeTree(pgNode, 1);
return BAD_VALUE;
}
ret = pgNode->getValue(GCSS_KEY_SUBSYSTEM, subsystem);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to get port's ancestor subsystem ");
pgNode->dumpNodeTree(pgNode, 1);
return BAD_VALUE;
}
if (type == string("hw")) {
stageId = 0;
terminalId = portId;
}
return OK;
}
/**
* Return the the terminal id of the peer port.
*
* Given a name of a port in canonical format (i.e. isa:non_scaled_ouptut)
* this method returns the terminal uid (the fourcc code) associated with its
* peer port.
*
* \param[in] name Port name in canonical format
* \param[out] terminalId Terminal id of the peer port
*
* \return BAD_VALUE in case name is empty
* \return INVALID_OPERATION if the port or peer was not found in the graph
* \return OK
*/
status_t GraphConfig::portGetPeerIdByName(string name,
uid_t &terminalId)
{
uid_t stageId; // not used
css_err_t ret;
status_t retErr;
Node *portNode = nullptr;
Node *peerNode = nullptr;
if (name.empty())
return BAD_VALUE;
ret = mSettings->getDescendantByString(name, &portNode);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to find port %s.", name.c_str());
return INVALID_OPERATION;
}
retErr = portGetPeer(portNode, &peerNode);
if (ret != OK || peerNode == nullptr) {
LOGE("Failed to find peer for port %s.", name.c_str());
return INVALID_OPERATION;
}
portGetFourCCInfo(*peerNode, stageId, terminalId);
return OK;
}
/**
* This method is used by pSysIsaTask to get the stream ids
* which are used in setting, at the same time return the
* mIsaOutputPort2StreamId
* \param[out] isaStreamIdVector
* \param[out] isaOutputPort2StreamIdMap
*/
status_t GraphConfig::getIsaStreamIds(vector<int32_t> &isaStreamIdVector,
map<string, int32_t> &isaOutputPort2StreamIdMap)
{
for (auto isaOutputPort : mIsaOutputPort2StreamId) {
int32_t streamIdFound = isaOutputPort.second;
// save the stream id into the vector
unsigned int i = 0;
for (; i < isaStreamIdVector.size(); i ++) {
if (isaStreamIdVector[i] == streamIdFound) {
break;
}
}
if (i == isaStreamIdVector.size())
isaStreamIdVector.push_back(streamIdFound);
}
if (isaStreamIdVector.size() == 0) {
LOGE("Fail to get stream id");
return UNKNOWN_ERROR;
}
isaOutputPort2StreamIdMap = mIsaOutputPort2StreamId;
return OK;
}
/**
* retrieve the pointer to the client stream associated with a virtual sink
*
* I.e. access the mapping done at stream config time between the pointers
* to camera3_stream_t and the names video0, video1, still0 etc...
*
* \param[in] port Node to the virtual sink (with name videoX or stillX etc..)
* \param[out] stream Pointer to the client stream associated with that virtual
* sink.
* \return OK
* \return BAD_VALUE in case of invalid parameters (null pointers)
* \return INVALID_OPERATION in case the Node is not a virtual sink.
*/
status_t GraphConfig::portGetClientStream(Node *port, camera3_stream_t **stream)
{
css_err_t ret = css_err_none;
string portName;
if (CC_UNLIKELY(!port || !stream)) {
LOGE("Could not get client stream - bad parameters");
return BAD_VALUE;
}
if (!portIsVirtual(port)) {
LOGE("Trying to find the client stream from a non virtual port");
return INVALID_OPERATION;
}
ret = port->getValue(GCSS_KEY_NAME, portName);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to get name for port");
port->dumpNodeTree(port, 1);
return BAD_VALUE;
}
uid_t vPortId = GCSS::ItemUID::str2key(portName);
*stream = mManager->getStreamByVirtualId(vPortId);
return OK;
}
/**
* A port is at the edge of the video stream (pipeline) if its peer is in a PG
* that has a different streamID (a.k.a. pipeline id) or if its peer is a
* virtual sink.
*
* Here we check for both conditions and return true if this port is at either
* edge of a pipeline
*/
bool GraphConfig::isPipeEdgePort(Node *port)
{
status_t status = OK;
css_err_t ret = css_err_none;
GraphConfig::Node *peer = nullptr;
GraphConfig::Node *peerAncestor = nullptr;
int32_t portDirection;
int32_t streamId = -1;
int32_t peerStreamId = -1;
string peerType;
portDirection = portGetDirection(port);
status = portGetPeer(port, &peer);
if (status == INVALID_OPERATION) {
LOG1("port is disabled, so it is an edge port");
return true;
}
if (CC_UNLIKELY(status != OK)) {
LOGE("Failed to create fourcc info for source port");
return false;
}
streamId = portGetStreamId(port);
if (CC_UNLIKELY(streamId < 0))
return false;
/*
* get the stream id of the peer port
* we also check the ancestor for that. If the peer is a virtual sink then
* it does not have ancestor.
*/
if (!portIsVirtual(peer)) {
ret = peer->getAncestor(&peerAncestor);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to get peer's ancestor");
return false;
}
ret = peerAncestor->getValue(GCSS_KEY_STREAM_ID, peerStreamId);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to get stream ID of peer PG");
return false;
}
/*
* Retrieve the type of node the peer ancestor is. It could be is not a
* program group node but a sink or hw block
*/
peerAncestor->getValue(GCSS_KEY_TYPE, peerType);
}
if (portDirection == GraphConfig::PORT_DIRECTION_INPUT) {
/*
* input port,
* if the peer is a source or hw block then it is on the edge,
* or if the peer is on a different stream id
*/
if ((streamId != peerStreamId) || (peerType == string("hw"))) {
return true;
}
} else {
/*
* output port,
* if the peer is a virtual port, or has a different stream id
* then it is on the edge,
*/
if (portIsVirtual(peer) || (streamId != peerStreamId)) {
return true;
}
}
return false;
}
/**
* Parse the information of the sensor node in the graph and store it in the
* provided SourceNodeInfo struct.
*
* \param[in] sensorNode Pointer to the graph config node that represents
* the sensor.
* \param[out] info Data structure where the information is stored.
*/
status_t GraphConfig::parseSensorNodeInfo(Node* sensorNode,
SourceNodeInfo &info)
{
status_t retErr = OK;
css_err_t ret = css_err_none;
string metadata;
string tmp;
ret = sensorNode->getValue(GCSS_KEY_CSI_PORT, info.csiPort);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Error: Couldn't get csi port from the graph");
return UNKNOWN_ERROR;
}
ret = sensorNode->getValue(GCSS_KEY_SENSOR_NAME, info.name);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Error: Couldn't get sensor name from sensor");
return UNKNOWN_ERROR;
}
ret = sensorNode->getValue(GCSS_KEY_LINK_FREQ, info.link_freq);
if (CC_UNLIKELY(ret != css_err_none)) {
info.link_freq = "0"; // default to zero
}
// Find i2c address for the sensor from sensor info
const CameraHWInfo *camHWInfo = PlatformData::getCameraHWInfo();
for (size_t i = 0; i < camHWInfo->mSensorInfo.size(); i++) {
if (camHWInfo->mSensorInfo[i].mSensorName == info.name)
info.i2cAddress = camHWInfo->mSensorInfo[i].mI2CAddress;
}
if (info.i2cAddress == "") {
LOGE("Couldn't get i2c address from Platformdata");
return UNKNOWN_ERROR;
}
ret = sensorNode->getValue(GCSS_KEY_METADATA, metadata);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Error: Couldn't get metadata enabled from sensor");
return UNKNOWN_ERROR;
}
info.metadataEnabled = atoi(metadata.c_str());
ret = sensorNode->getValue(GCSS_KEY_MODE_ID, info.modeId);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Error: Couldn't get sensor mode id from sensor");
return UNKNOWN_ERROR;
}
ret = sensorNode->getValue(GCSS_KEY_BAYER_ORDER, info.nativeBayer);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Error: Couldn't get native bayer order from sensor");
return UNKNOWN_ERROR;
}
retErr = getDimensions(sensorNode, info.output.w, info.output.h);
if (retErr != OK) {
LOGE("Error: Couldn't get values from sensor");
return UNKNOWN_ERROR;
}
ret = sensorNode->getValue(GCSS_KEY_INTERLACED, tmp);
if (ret != css_err_none) {
LOGW("Couldn't get interlaced field from sensor");
} else {
info.interlaced = atoi(tmp.c_str());
}
// v-flip is not mandatory. Some sensors may not have this control
ret = sensorNode->getValue(GCSS_KEY_VFLIP, info.verticalFlip);
// h-flip is not mandatory. Some sensors may not have this control
ret = sensorNode->getValue(GCSS_KEY_HFLIP, info.horizontalFlip);
Node *port0Node = nullptr;
ret = sensorNode->getDescendant(GCSS_KEY_PORT_0, &port0Node);
if (ret != css_err_none) {
LOGE("Error: Couldn't get port_0");
return UNKNOWN_ERROR;
}
ret = port0Node->getValue(GCSS_KEY_FORMAT, tmp);
if (ret != css_err_none) {
LOGE("Error: Couldn't get format from the graph");
return UNKNOWN_ERROR;
}
/* find mbus format from common format in settings */
info.output.mbusFormat = gcu::getMBusFormat(get_fourcc(tmp[0], tmp[1],
tmp[2], tmp[3]));
/*
* Get size and cropping from pixel array to use in format and selection
*/
Node *pixelArrayOutput = nullptr;
ret = sensorNode->getDescendantByString("pixel_array:output",
&pixelArrayOutput);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Error: Couldn't get pixel array node from the graph");
return UNKNOWN_ERROR;
}
retErr = getDimensions(pixelArrayOutput, info.pa.out.w,
info.pa.out.h,
info.pa.out.l,
info.pa.out.t);
if (retErr != OK) {
LOGE("Error: Couldn't get values from pixel array output");
return UNKNOWN_ERROR;
}
info.pa.name = info.name+ " " + info.i2cAddress;
/* Populate the formats for each subdevice
* The format for the Pixel Array is determined by the native bayer order
* and the bpp selected by the settings.
* We extract the bpp from the format in the sensor port.
*
* The format in the sensor output port may be different that the
* pixel array format because the sensor may be changing the effective
* bayer order by flipping or internal cropping
* */
int32_t bpp = gcu::getBpp(info.output.mbusFormat);
info.pa.out.mbusFormat = gcu::getMBusFormat(info.nativeBayer, bpp);
return OK;
}
/*
* Create mediaCtlConfig structure to configure sensor mode. All settings are
* retrieved from graph config settings and applied one by one.
*
* TODO a lot of string values because of android gcss keys, which does not
* have support for ints. Some could be moved to gcss_keys
*
* \param[out] mediaCtlConfig the struct to populate
* \return OK at success
* \return UNKNOWN ERROR at failure
*/
status_t GraphConfig::getMediaCtlData(MediaCtlConfig *mediaCtlConfig)
{
CheckAndLogError((!mediaCtlConfig), BAD_VALUE, "@%s null ptr\n",
__FUNCTION__);
ConfigProperties cameraProps; // camera properties
css_err_t ret = css_err_none;
status_t retErr = OK;
string formatStr;
SourceNodeInfo sourceInfo;
Node *sourceNode = nullptr;
string csi2;
if (mSourceType == SRC_SENSOR) {
ret = mSettings->getDescendant(GCSS_KEY_SENSOR, &sourceNode);
if (ret != css_err_none) {
LOGE("Error: Couldn't get sensor node from the graph");
return UNKNOWN_ERROR;
}
retErr = parseSensorNodeInfo(sourceNode, sourceInfo);
if (retErr != OK) {
LOGE("Error: Couldn't get sensor node info");
return UNKNOWN_ERROR;
}
// get the next entity port of the "ov5670 binner 11-0010" which is dynamical.
// it could "rkisp1-csi2 0" or "rkisp1-csi2 1", then it will get 0 or 1.
int port = 0;
std::shared_ptr<MediaEntity> entity = nullptr;
string entityName = sourceInfo.name + " " + sourceInfo.i2cAddress;
LOG1("entityName:%s\n", entityName.c_str());
status_t ret = mMediaCtl->getMediaEntity(entity, entityName.c_str());
if (ret != NO_ERROR) {
LOGE("@%s, fail to call getMediaEntity, ret:%d\n", __FUNCTION__, ret);
return UNKNOWN_ERROR;
}
std::vector<media_link_desc> links;
entity->getLinkDesc(links);
LOG1("@%s, links number:%zu\n", __FUNCTION__, links.size());
if (links.size()) {
struct media_pad_desc* pad = &links[0].sink;
LOG1("@%s, sink entity:%d, flags:%d, index:%d\n",
__FUNCTION__, pad->entity, pad->flags, pad->index);
struct media_entity_desc entityDesc;
mMediaCtl->findMediaEntityById(pad->entity, entityDesc);
LOG1("@%s, name:%s\n", __FUNCTION__, entityDesc.name);
string name = entityDesc.name;
std::size_t p = name.find(" ");
if (p != std::string::npos) {
string s;
s.append(name, p + 1, 1);
port = std::stoi(s);
LOG1("@%s, port:%d\n", __FUNCTION__, port);
}
}
// get csi2 and cio2 names
//csi2 = csi2_without_port + std::to_string(port);
//mCSIBE = CSI_BE + std::to_string(port);
csi2 = csi2_without_port;
mCSIBE = CSI_BE;
/* TODO: should config according to real topology, sensor to isp link is decided by the sensor interface,
* link is different for mipi and dvp.
* mipi sensor --- > csi ---> isp
* dvp sensor ----> isp
*/
addLinkParams(entityName, 0, csi2, 0, 1, MEDIA_LNK_FL_ENABLED, mediaCtlConfig);
LOG1(" csi2 is:%s, cio2 is:%s\n", csi2.c_str(), mCSIBE.c_str());
} else {
LOGE("Error: No source");
return UNKNOWN_ERROR;
}
#if 0//needn't get the default expsure here, get it from 3a lib instead
// Add control params
retErr = addControls(sourceNode, sourceInfo, mediaCtlConfig);
if (retErr != OK) {
return UNKNOWN_ERROR;
}
#endif
/*
* Add Camera properties to mediaCtlConfig
*/
int id = 0;
ret = sourceNode->getValue(GCSS_KEY_ID, id);
if (ret != css_err_none) {
LOGE("Error: Couldn't get sensor id from sensor");
return UNKNOWN_ERROR;
}
std::string cameraName = sourceInfo.name + " " + sourceInfo.modeId;
cameraProps.outputWidth = sourceInfo.output.w;
cameraProps.outputHeight = sourceInfo.output.h;
cameraProps.id = id;
cameraProps.name = cameraName.c_str();
mediaCtlConfig->mCameraProps = cameraProps;
mediaCtlConfig->mFTCSize.Width = sourceInfo.output.w;
mediaCtlConfig->mFTCSize.Height = sourceInfo.output.h;
Node *pixelFormatterIn = nullptr, *pixelFormatterOut = nullptr;
Node *csiBEOutput = nullptr, *csiBESocOutput = nullptr;
int pfInW = 0, pfInH = 0, pfOutW = 0, pfOutH = 0, pfLeft = 0, pfTop = 0;
bool pfPresent = false;
// Get csi_be node. If not found, try csi_be_soc. If not found return error.
ret = mSettings->getDescendantByString("csi_be:output", &csiBEOutput);
if (ret != css_err_none) {
ret = mSettings->getDescendantByString("csi_be_soc:output", &csiBESocOutput);
if (ret != css_err_none) {
LOGE("Error: Couldn't get csi_be or csi_be_soc nodes from the graph");
return UNKNOWN_ERROR;
}
// get format from _soc
ret = csiBESocOutput->getValue(GCSS_KEY_FORMAT, formatStr);
if (ret != css_err_none) {
LOGE("Error: Couldn't get format from the graph");
return UNKNOWN_ERROR;
}
} else {
ret = csiBEOutput->getValue(GCSS_KEY_FORMAT, formatStr);
if (ret != css_err_none) {
LOGE("Error: Couldn't get format from the graph");
return UNKNOWN_ERROR;
}
}
/* consistency check, we have at least one CSI-BE */
if (CC_UNLIKELY(csiBESocOutput == nullptr && csiBEOutput == nullptr)) {
LOGE("Error: CSI BE Output nullptr");
return UNKNOWN_ERROR;
}
std::string pixelFormatterInput = "bxt_pixelformatter:input";
std::string pixelFormatterOutput = "bxt_pixelformatter:output";
std::string inputPort;
std::string outputPort;
if (csiBEOutput != nullptr) {
inputPort = "csi_be:" + pixelFormatterInput;
outputPort = "csi_be:" + pixelFormatterOutput;
} else {
inputPort = "csi_be_soc:" + pixelFormatterInput;
outputPort = "csi_be_soc:" + pixelFormatterOutput;
}
/* Get cropping values from the pixel formatter input. Output resolution
* comes from the csi be output. Some graphs may not use pixel formatter.*/
ret = mSettings->getDescendantByString(inputPort,
&pixelFormatterIn);
if (ret != css_err_none) {
LOGW("Couldn't get pixel formatter input, skipping");
} else {
pfPresent = true;
ret = mSettings->getDescendantByString(outputPort,
&pixelFormatterOut);
if (ret != css_err_none) {
LOGE("Error: Couldn't get pixel formatter output");
return UNKNOWN_ERROR;
}
retErr = getDimensions(pixelFormatterIn, pfInW, pfInH, pfLeft, pfTop);
if (retErr != OK) {
LOGE("Error: Couldn't get values from pixel formatter input");
return UNKNOWN_ERROR;
}
retErr = getDimensions(pixelFormatterOut, pfOutW, pfOutH);
if (retErr != OK) {
LOGE("Error: Couldn't get values from pixel formatter output");
return UNKNOWN_ERROR;
}
}
int csiBEOutW = 0, csiBEOutH = 0;
int csiBESocOutW = 0, csiBESocOutH = 0;
if (csiBEOutput != nullptr) {
retErr = getDimensions(csiBEOutput, csiBEOutW,csiBEOutH);
if (retErr != OK) {
LOGE("Error: Couldn't values from csi be output");
return UNKNOWN_ERROR;
}
} else {
retErr = getDimensions(csiBESocOutput, csiBESocOutW, csiBESocOutH);
if (retErr != OK) {
LOGE("Error: Couldn't get values from csi be soc out");
return UNKNOWN_ERROR;
}
LOG1("pfInW:%d, pfLeft:%d, pfTop:%d,pfOutW:%d,pfOutH:%d,csiBESocOutW:%d,csiBESocOutH:%d",
pfInW, pfLeft, pfTop, pfOutW, pfOutH, csiBESocOutW,csiBESocOutH);
}
/* Boolean to tell whether there is pixel formatter cropping.
* This affects which selections are made */
bool pixelFormatter = false;
if (pfInW != pfOutW || pfInH != pfOutH || pfLeft != 0 || pfTop != 0)
pixelFormatter = true;
/*
* If CSI BE SOC is not used, we must have ISA. Get video crop, scaled and
* non scaled output from ISA and apply the formats. Otherwise add formats
* for CSI BE SOC.
*/
Node *isaNode = nullptr;
Node *cropVideoIn = nullptr, *cropVideoOut = nullptr;
int videoCropW = 0, videoCropH = 0, videoCropT = 0, videoCropL = 0;
int videoCropOutW = 0, videoCropOutH = 0;
/*
* First get and set values when CSI BE SOC is not used
*/
if (csiBESocOutput == nullptr) {
ret = mSettings->getDescendant(GCSS_KEY_CSI_BE, &isaNode);
if (ret != css_err_none) {
LOGE("Error: Couldn't get isa node");
return UNKNOWN_ERROR;
}
// Check if there is video cropping available. It is zero as default.
ret = isaNode->getDescendantByString("csi_be:output",
&cropVideoOut);
if (ret == css_err_none) {
ret = isaNode->getDescendantByString("csi_be:input",
&cropVideoIn);
}
if (ret == css_err_none) {
retErr = getDimensions(cropVideoIn, videoCropW, videoCropH,
videoCropL, videoCropT);
if (retErr != OK) {
LOGE("Error: Couldn't get values from crop video input");
return UNKNOWN_ERROR;
}
retErr = getDimensions(cropVideoOut, videoCropOutW, videoCropOutH);
if (retErr != OK) {
LOGE("Error: Couldn't get values from crop video output");
return UNKNOWN_ERROR;
}
}
}
/* Set sensor pixel array parameter to the attributes in 'sensor_mode' node,
* ignore the attributes in pixel_array and binner node due to upstream driver
* removed binner and scaler subdev.
*/
addFormatParams(sourceInfo.pa.name, sourceInfo.output.w, sourceInfo.output.h,
0, sourceInfo.output.mbusFormat, 0, 0, mediaCtlConfig);
// rkisp1-csi2 0 or 1
addFormatParams(csi2, csiBESocOutW, csiBESocOutH,
0, sourceInfo.output.mbusFormat, 0, 0, mediaCtlConfig);
addFormatParams(csi2, csiBESocOutW, csiBESocOutH,
1, sourceInfo.output.mbusFormat, 0, 0, mediaCtlConfig);
/* Start populating selections into mediaCtlConfig
* entity name, width, height, left crop, top crop, target, pad, config */
addSelectionParams(sourceInfo.pa.name,
sourceInfo.pa.out.w,
sourceInfo.pa.out.h,
sourceInfo.pa.out.l,
sourceInfo.pa.out.t,
V4L2_SEL_TGT_CROP, 0 /* sink pad */, mediaCtlConfig);
if (gDumpType & CAMERA_DUMP_MEDIA_CTL)
dumpMediaCtlConfig(*mediaCtlConfig);
return OK;
}
status_t GraphConfig::getNodeInfo(const ia_uid uid, const Node &parent, int* width, int* height)
{
status_t status = OK;
Node *node = nullptr;
status = parent.getDescendant(uid, &node);
if (status != css_err_none) {
LOGE("pipe log <%s> node is not present in graph (descriptor or settings) - continuing.",GCSS::ItemUID::key2str(uid));
return UNKNOWN_ERROR;
}
status = node->getValue(GCSS_KEY_WIDTH, *width);
if (status != css_err_none) {
LOGE("pipe log Could not get width for <%s>", NODE_NAME(node));
return UNKNOWN_ERROR;
}
if (width == 0) {
LOGE("pipe log Could not get width for <%s>", NODE_NAME(node));
return UNKNOWN_ERROR;
}
status = node->getValue(GCSS_KEY_HEIGHT, *height);
if (status != css_err_none) {
LOGE("pipe log Could not get height for <%s>", NODE_NAME(node));
return UNKNOWN_ERROR;
}
if (height == 0) {
LOGE("pipe log Could not get height for <%s>", NODE_NAME(node));
return UNKNOWN_ERROR;
}
return status;
}
/*
* Imgu specific function
*/
status_t GraphConfig::getImguMediaCtlData(int32_t cameraId,
int32_t testPatternMode,
MediaCtlConfig *mediaCtlConfig,
MediaCtlConfig *mediaCtlConfigVideo,
MediaCtlConfig *mediaCtlConfigStill)
{
CheckAndLogError(
(!mediaCtlConfig || !mediaCtlConfigVideo || !mediaCtlConfigStill),
BAD_VALUE, "@%s null ptr\n", __FUNCTION__);
int ret;
Node *imgu = nullptr;
Node *preview = nullptr, *video = nullptr, *still = nullptr, *output = nullptr, *input = nullptr;
int width = 0, height = 0, format = 0;
int enabled = 1;
std::string kImguName = "rkisp1-isp-subdev";
ret = mSettings->getDescendant(GCSS_KEY_IMGU, &imgu);
if (ret != css_err_none) {
LOGE("Error: Couldn't get imgu node");
return UNKNOWN_ERROR;
}
// TODO: not done
mSettings->getDescendant(GCSS_KEY_IMGU_PREVIEW, &preview);
mSettings->getDescendant(GCSS_KEY_IMGU_VIDEO, &video);
mSettings->getDescendant(GCSS_KEY_IMGU_STILL, &still);
imgu->getDescendant(GCSS_KEY_OUTPUT, &output);
imgu->getDescendant(GCSS_KEY_INPUT, &input);
struct lut {
uint32_t uid;
string name;
int ipuNodeName;
Node *pipe;
int pad;
};
vector<lut> uids;
uids.clear();
uids = {
{ GCSS_KEY_IMGU_STILL, MEDIACTL_STILLNAME, IMGU_NODE_STILL, still, -1 },
{ GCSS_KEY_INPUT, kImguName, IMGU_NODE_INPUT,input, 0 },
{ GCSS_KEY_OUTPUT, kImguName, -1, output, MEDIACTL_PAD_OUTPUT_NUM },
{ GCSS_KEY_IMGU_VIDEO, MEDIACTL_VIDEONAME, IMGU_NODE_VIDEO, video, 0 },
{ GCSS_KEY_IMGU_PREVIEW, MEDIACTL_PREVIEWNAME, IMGU_NODE_VF_PREVIEW, preview, 0 },
};
int ispOutWidth = 0, ispOutHeight = 0;
for (int i = 0; i < uids.size(); i++) {
string name = uids[i].name;
Node *pipe = uids[i].pipe;
if (pipe == nullptr) {
LOGD("<%u> node is not present in graph (descriptor or settings) - continuing.", uids[i].uid);
continue;
}
// Assume enabled="1" by default. Explicitly set to 0 in settings, if necessary.
enabled = 1;
ret = pipe->getValue(GCSS_KEY_ENABLED, enabled);
if (ret != css_err_none) {
LOG1("Attribute 'enabled' not present in <%s>. Assuming enabled=\"1\"", NODE_NAME(pipe));
}
if (!enabled) {
LOG1("Node <%s> not enabled - continuing", NODE_NAME(pipe));
continue;
}
ret = pipe->getValue(GCSS_KEY_WIDTH, width);
if (ret != css_err_none) {
LOGE("Could not get width for <%s>", NODE_NAME(pipe));
return UNKNOWN_ERROR;
}
if (width == 0)
continue;
ret = pipe->getValue(GCSS_KEY_HEIGHT, height);
if (ret != css_err_none) {
LOGE("Could not get height for <%s>", NODE_NAME(pipe));
return UNKNOWN_ERROR;
}
string fourccFormat = "";
ret = pipe->getValue(GCSS_KEY_FORMAT, fourccFormat);
if (ret != css_err_none) {
LOGE("Could not get format for <%s>", NODE_NAME(pipe));
return UNKNOWN_ERROR;
}
format = gcu::getV4L2Format(get_fourcc(fourccFormat[0],
fourccFormat[1],
fourccFormat[2],
fourccFormat[3]));
if (GCSS::ItemUID::key2str(uids[i].uid) == GC_PREVIEW ||
GCSS::ItemUID::key2str(uids[i].uid) == GC_STILL ||
GCSS::ItemUID::key2str(uids[i].uid) == GC_VIDEO) {
int nodeWidth = 0, nodeHeight = 0;
/* Use BGGR as bayer format when specific sensor receives test pattern request */
if (testPatternMode != ANDROID_SENSOR_TEST_PATTERN_MODE_OFF) {
const RKISP1CameraCapInfo* capInfo = getRKISP1CameraCapInfo(cameraId);
CheckAndLogError(capInfo == nullptr, UNKNOWN_ERROR,
"@%s: failed to get cameraCapInfo",
__FUNCTION__);
if (name.compare(MEDIACTL_INPUTNAME) == 0 &&
!capInfo->getTestPatternBayerFormat().empty()) {
format = gcu::getV4L2Format(capInfo->getTestPatternBayerFormat());
}
}
addFormatParams(name, width, height, uids[i].pad,
format, 0, 0, mediaCtlConfig);
// Get path crop info
ret = getNodeInfo(GCSS_KEY_IMGU_PCRP, *pipe, &nodeWidth, &nodeHeight);
if (ret != OK) {
LOGE("pipe log name: %s can't get info!", name.c_str());
return UNKNOWN_ERROR;
} else {
struct v4l2_selection select;
CLEAR(select);
/* videodev2.h says don't use *_MPLAEN */
select.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
select.target = V4L2_SEL_TGT_CROP;
select.flags = 0;
select.r.left = (ispOutWidth - nodeWidth) / 2;
select.r.top = (ispOutHeight - nodeHeight) / 2;
select.r.width = nodeWidth;
select.r.height = nodeHeight;
addSelectionVideoParams(name.c_str(), select, mediaCtlConfig);
LOG2("pipe log name: %s crop size %dx%d", name.c_str(), nodeWidth, nodeHeight);
}
LOG2("Adding video node: %s", NODE_NAME(pipe));
addImguVideoNode(uids[i].ipuNodeName, name, mediaCtlConfig);
addLinkParams(kImguName, MEDIACTL_PAD_OUTPUT_NUM, name, 0, 1, MEDIA_LNK_FL_ENABLED, mediaCtlConfig);
} else if (GCSS::ItemUID::key2str(uids[i].uid) == GC_INPUT) {
int nodeWidth = 0, nodeHeight = 0;
int32_t iMbusFormat = gcu::getMBusFormat(get_fourcc(fourccFormat[0],
fourccFormat[1],
fourccFormat[2],
fourccFormat[3]));
addFormatParams(name, width, height, uids[i].pad,
iMbusFormat, 0, 0, mediaCtlConfig);
// Get isp input crop info
ret = getNodeInfo(GCSS_KEY_IMGU_IAC, *pipe, &nodeWidth, &nodeHeight);
if (ret != OK) {
LOGW("pipe log name: %s can't get info!", name.c_str());
return UNKNOWN_ERROR;
} else
addSelectionParams(name, nodeWidth, nodeHeight, 0, 0, V4L2_SEL_TGT_CROP, uids[i].pad, mediaCtlConfig);
/* TODO: related to sensor interface, see getMediaCtlData*/
addLinkParams(mCSIBE, 1, kImguName, 0, 1, MEDIA_LNK_FL_ENABLED, mediaCtlConfig);
} else if (GCSS::ItemUID::key2str(uids[i].uid) == GC_OUTPUT) {
int nodeWidth = 0, nodeHeight = 0;
int32_t oMbusFormat = gcu::getMBusFormat(get_fourcc(fourccFormat[0],
fourccFormat[1],
fourccFormat[2],
fourccFormat[3]));
/* use limit range if test pattern mode is selected */
int quantization = testPatternMode != ANDROID_SENSOR_TEST_PATTERN_MODE_OFF ?
V4L2_QUANTIZATION_LIM_RANGE : V4L2_QUANTIZATION_DEFAULT;
addFormatParams(name, width, height, uids[i].pad,
oMbusFormat, 0, quantization, mediaCtlConfig);
// Get isp output crop info
ret = getNodeInfo(GCSS_KEY_IMGU_ISM, *pipe, &nodeWidth, &nodeHeight);
if (ret != OK) {
LOGW("pipe log name: %s can't get info!", name.c_str());
return UNKNOWN_ERROR;
} else
addSelectionParams(name, nodeWidth, nodeHeight, 0, 0, V4L2_SEL_TGT_CROP, uids[i].pad, mediaCtlConfig);
ispOutWidth = nodeWidth;
ispOutHeight = nodeHeight;
} else {
LOGE("pipe log name: wrong node %s !", GCSS::ItemUID::key2str(uids[i].uid));
return UNKNOWN_ERROR;
}
}
LOG2("Adding stats node");
addImguVideoNode(IMGU_NODE_STAT, MEDIACTL_STATNAME, mediaCtlConfig);
addLinkParams(kImguName, 3, MEDIACTL_STATNAME, 0, 1, MEDIA_LNK_FL_ENABLED, mediaCtlConfig);
LOG2("Adding parameter node");
addImguVideoNode(IMGU_NODE_PARAM, MEDIACTL_PARAMETERNAME, mediaCtlConfig);
addLinkParams(MEDIACTL_PARAMETERNAME, 0, kImguName, 1, 1, MEDIA_LNK_FL_ENABLED, mediaCtlConfig);
return ret;
}
void GraphConfig::setMediaCtlConfig(std::shared_ptr<MediaController> mediaCtl,
bool swapVideoPreview,
bool enableStill)
{
mMediaCtl = mediaCtl;
}
/*
* Imgu helper function
*/
bool GraphConfig::doesNodeExist(string nodeName)
{
int ret;
Node *node = nullptr;
ret = mSettings->getDescendantByString(nodeName, &node);
if (ret != css_err_none) {
LOGD("Node <%s> was not found.", nodeName.c_str());
return false;
}
/* There is no good way to search if node exists or not.
* Because mSettings has both descriptor and settings combined we
* need to ask for specific value see if node really exists in the
* settings side. */
int width;
ret = node->getValue(GCSS_KEY_WIDTH, width);
if (ret != css_err_none) {
LOGD("Node <%s> was not found.", nodeName.c_str());
return false;
}
return true;
}
/*
* Get values for MediaCtlConfig control params.
* \Node sensorNode pointer to sensor node
*/
status_t GraphConfig::addControls(const Node *sensorNode,
const SourceNodeInfo &sourceInfo,
MediaCtlConfig* config)
{
css_err_t ret = css_err_none;
std::string value;
std::string entityName;
if (!sourceInfo.pa.name.empty()) {
entityName = sourceInfo.pa.name;
} else if (!sourceInfo.tpg.name.empty()) {
entityName = sourceInfo.tpg.name;
} else {
LOGE("Empty entity name");
return UNKNOWN_ERROR;
}
ret = sensorNode->getValue(GCSS_KEY_EXPOSURE, value);
if (ret == css_err_none) {
addCtlParams(entityName, GCSS_KEY_EXPOSURE, V4L2_CID_EXPOSURE,
value, config);
}
ret = sensorNode->getValue(GCSS_KEY_GAIN, value);
if (ret == css_err_none) {
addCtlParams(entityName, GCSS_KEY_GAIN, V4L2_CID_ANALOGUE_GAIN,
value, config);
}
return OK;
}
/**
* Add video nodes into mediaCtlConfig
*
* \param csiBESocOutput[in] use to determine whether csi be soc is enabled
* \param mediaCtlConfig[out] populate this struct with given values
*/
void GraphConfig::addVideoNodes(const Node* csiBESocOutput,
MediaCtlConfig* config)
{
CheckAndLogError((!config), VOID_VALUE, "@%s null ptr\n", __FUNCTION__);
MediaCtlElement mediaCtlElement;
// Imgu support
mediaCtlElement.isysNodeName = ISYS_NODE_RAW;
mediaCtlElement.name = mCSIBE;
config->mVideoNodes.push_back(mediaCtlElement);
}
void GraphConfig::addImguVideoNode(int nodeType, const string& nodeName, MediaCtlConfig *config)
{
CheckAndLogError((!config), VOID_VALUE, "@%s null ptr\n", __FUNCTION__);
CheckAndLogError((nodeType == IMGU_NODE_NULL), VOID_VALUE,
"@%s null ipu node name\n", __FUNCTION__);
MediaCtlElement mediaCtlElement;
mediaCtlElement.name = nodeName;
mediaCtlElement.isysNodeName = nodeType;
config->mVideoNodes.push_back(mediaCtlElement);
}
/*
* Imgu helper function
*/
status_t GraphConfig::getValue(string &nodeName, uint32_t id, int &value)
{
Node *node;
int ret = mSettings->getDescendantByString(nodeName, &node);
if (ret != css_err_none) {
LOGE("Error: Couldn't get %s node", nodeName.c_str());
return UNKNOWN_ERROR;
}
GCSS::GraphConfigAttribute *attr;
ret = node->getAttribute(id, &attr);
if (ret != css_err_none) {
LOGE("Error: Couldn't get attribute '%s' for node: %s", GCSS::ItemUID::key2str(id), NODE_NAME(node));
return UNKNOWN_ERROR;
}
string valueString = "-2";
ret = attr->getValue(valueString);
if (ret != css_err_none) {
LOGE("Error: Couldn't get value of '%s' for node: %s", GCSS::ItemUID::key2str(id), NODE_NAME(node));
return UNKNOWN_ERROR;
}
value = atoi(valueString.c_str());
return OK;
}
/**
* Dump contents of mediaCtlConfig struct
*/
void GraphConfig::dumpMediaCtlConfig(const MediaCtlConfig &config) const {
size_t i = 0;
LOGE("MediaCtl config w=%d ,height=%d"
,config.mCameraProps.outputWidth,
config.mCameraProps.outputHeight);
for (i = 0; i < config.mLinkParams.size() ; i++) {
LOGE("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++) {
LOGE("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++) {
LOGE("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++) {
LOGE("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++) {
LOGE("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());
}
}
/**
* Get binning factor values from the given node
* \param[in] node the node to read the values from
* \param[out] hBin horizontal binning factor
* \param[out] vBin vertical binning factor
*/
status_t GraphConfig::getBinningFactor(const Node *node, int &hBin, int &vBin) const
{
css_err_t ret = css_err_none;
string value;
ret = node->getValue(GCSS_KEY_BINNING_H_FACTOR, hBin);
if (ret != css_err_none) {
LOGE("Error: Couldn't get horizontal binning factor");
return UNKNOWN_ERROR;
}
ret = node->getValue(GCSS_KEY_BINNING_V_FACTOR, vBin);
if (ret != css_err_none) {
LOGE("Error: Couldn't get vertical binning factor");
return UNKNOWN_ERROR;
}
return OK;
}
/**
* Get scaling factor values from the given node
* \param[in] node the node to read the values from
* \param[out] scalingNum scaling ratio
* \param[out] scalingDenom scaling ratio
*/
status_t GraphConfig::getScalingFactor(const Node *node,
int32_t &scalingNum,
int32_t &scalingDenom) const
{
css_err_t ret = css_err_none;
string value;
ret = node->getValue(GCSS_KEY_SCALING_FACTOR_NUM, scalingNum);
if (ret != css_err_none) {
LOGE("Error: Couldn't get width scaling num ratio");
return UNKNOWN_ERROR;
}
ret = node->getValue(GCSS_KEY_SCALING_FACTOR_DENOM, scalingDenom);
if (ret != css_err_none) {
LOGE("Error: Couldn't get width scaling num ratio");
return UNKNOWN_ERROR;
}
return OK;
}
/**
* Get width and height values from the given node
* \param[in] node the node to read the values from
* \param[out] w width
* \param[out] h height
*/
status_t GraphConfig::getDimensions(const Node *node, int &w, int &h) const
{
css_err_t ret = css_err_none;
ret = node->getValue(GCSS_KEY_WIDTH, w);
if (ret != css_err_none) {
LOGE("Error: Couldn't get width");
return UNKNOWN_ERROR;
}
ret = node->getValue(GCSS_KEY_HEIGHT, h);
if (ret != css_err_none) {
LOGE("Error: Couldn't get height");
return UNKNOWN_ERROR;
}
return OK;
}
/**
* Get width, height and cropping values from the given node
* \param[in] node the node to read the values from
* \param[out] w width
* \param[out] h height
* \param[out] l left crop
* \param[out] t top crop
*/
status_t GraphConfig::getDimensions(const Node *node, int32_t &w, int32_t &h,
int32_t &l, int32_t &t) const
{
status_t retErr = getDimensions(node, w, h);
if (retErr != OK)
return UNKNOWN_ERROR;
css_err_t ret = node->getValue(GCSS_KEY_LEFT, l);
if (ret != css_err_none) {
LOGE("Error: Couldn't get left crop");
return UNKNOWN_ERROR;
}
ret = node->getValue(GCSS_KEY_TOP, t);
if (ret != css_err_none) {
LOGE("Error: Couldn't get top crop");
return UNKNOWN_ERROR;
}
return OK;
}
/**
* Add format params to config
*
* \param[in] entityName
* \param[in] width
* \param[in] height
* \param[in] pad
* \param[in] format
* \param[in] filed
* \param[in] quantization
* \param[out] config populate this struct with given values
*/
void GraphConfig::addFormatParams(const string &entityName,
int width,
int height,
int pad,
int format,
int field,
int quantization,
MediaCtlConfig *config)
{
if (!entityName.empty() && config) {
MediaCtlFormatParams mediaCtlFormatParams;
mediaCtlFormatParams.entityName = entityName;
mediaCtlFormatParams.width = width;
mediaCtlFormatParams.height = height;
mediaCtlFormatParams.pad = pad;
mediaCtlFormatParams.formatCode = format;
mediaCtlFormatParams.stride = 0;
mediaCtlFormatParams.field = field;
mediaCtlFormatParams.quantization= quantization;
config->mFormatParams.push_back(mediaCtlFormatParams);
LOG2("@%s, entityName:%s, width:%d, height:%d, pad:%d, format:%d, format:%s, field:%d",
__FUNCTION__, entityName.c_str(), width, height, pad, format, v4l2Fmt2Str(format), field);
}
}
/**
* Add control params into config
* \param[in] entityName
* \param[in] controlName
* \param[in] controlId
* \param[in] strValue
* \param[out] config populate this struct with given values
*/
void GraphConfig::addCtlParams(const string &entityName,
uint32_t controlName,
int controlId,
const string &strValue,
MediaCtlConfig *config)
{
if (!entityName.empty() && config) {
int value = atoi(strValue.c_str());
string controlNameStr = GCSS::ItemUID::key2str(controlName);
MediaCtlControlParams mediaCtlControlParams;
mediaCtlControlParams.entityName = entityName;
mediaCtlControlParams.controlName = controlNameStr;
mediaCtlControlParams.controlId = controlId;
mediaCtlControlParams.value = value;
config->mControlParams.push_back(mediaCtlControlParams);
LOG2("@%s, entityName:%s, controlNameStr:%s, controlId:%d, value:%d",
__FUNCTION__, entityName.c_str(), controlNameStr.c_str(), controlId, value);
}
}
/**
* Add selection params into config
*
* \param[in] entityName
* \param[in] width
* \param[in] height
* \param[in] left
* \param[in] top
* \param[in] target
* \param[in] pad
* \param[out] config populate this struct with given values
*/
void GraphConfig::addSelectionParams(const string &entityName,
int width,
int height,
int left,
int top,
int target,
int pad,
MediaCtlConfig *config)
{
if (!entityName.empty() && config) {
MediaCtlSelectionParams mediaCtlSelectionParams;
mediaCtlSelectionParams.width = width;
mediaCtlSelectionParams.height = height;
mediaCtlSelectionParams.left = left;
mediaCtlSelectionParams.top = top;
mediaCtlSelectionParams.target = target;
mediaCtlSelectionParams.pad = pad;
mediaCtlSelectionParams.entityName = entityName;
config->mSelectionParams.push_back(mediaCtlSelectionParams);
LOG2("@%s, width:%d, height:%d, left:%d, top:%d, target:%d, pad:%d, entityName:%s",
__FUNCTION__, width, height, left, top, target, pad, entityName.c_str());
}
}
void GraphConfig::addSelectionVideoParams(const string &entityName,
const struct v4l2_selection &select,
MediaCtlConfig* config)
{
if (entityName.empty() || !config) {
LOGE("The config or entity <%s> is empty!", entityName.c_str());
return;
}
MediaCtlSelectionVideoParams mediaCtlSelectionVideoParams;
mediaCtlSelectionVideoParams.entityName = entityName;
mediaCtlSelectionVideoParams.select = select;
config->mSelectionVideoParams.push_back(mediaCtlSelectionVideoParams);
LOG2("@%s, width:%d, height:%d, left:%d, top:%d, target:%d, type:%d, flags:%d entityName:%s",
__FUNCTION__, select.r.width, select.r.height, select.r.left, select.r.top,
select.target, select.type, select.flags, entityName.c_str());
}
/**
* Add link params into config
*
* \param[in] srcName
* \param[in] srcPad
* \param[in] sinkName
* \param[in] sinkPad
* \param[in] enable
* \param[in] flags
* \param[out] config populate this struct with given values
*/
void GraphConfig::addLinkParams(const string &srcName,
int srcPad,
const string &sinkName,
int sinkPad,
int enable,
int flags,
MediaCtlConfig *config)
{
if (!srcName.empty() && !sinkName.empty() && config) {
MediaCtlLinkParams mediaCtlLinkParams;
mediaCtlLinkParams.srcName = srcName;
mediaCtlLinkParams.srcPad = srcPad;
mediaCtlLinkParams.sinkName = sinkName;
mediaCtlLinkParams.sinkPad = sinkPad;
mediaCtlLinkParams.enable = enable;
mediaCtlLinkParams.flags = flags;
config->mLinkParams.push_back(mediaCtlLinkParams);
LOG2("@%s, srcName:%s, srcPad:%d, sinkName:%s, sinkPad:%d, enable:%d, flags:%d",
__FUNCTION__, srcName.c_str(), srcPad, sinkName.c_str(), sinkPad, enable, flags);
}
}
/**
* Gets all stream id's and generates kernel list for each of those.
* Generated kernel lists are stored inside a mKernels map, from where
* they can be retrieved with streamId.
*
*/
status_t GraphConfig::generateKernelListsForStreams()
{
return OK;
}
/**
* query source frame parameters according to the different input
* device: sensor or tpg
*/
status_t
GraphConfig::getSourceFrameParams(rk_aiq_frame_params &frameParams)
{
status_t status = UNKNOWN_ERROR;
if (mSourceType == SRC_SENSOR) {
status = getSensorFrameParams(frameParams);
} else if (mSourceType == SRC_TPG) {
status = getTPGFrameParams(frameParams);
} else {
LOGE("wrong source");
}
return status;
}
/**
* Retrieve the resolution of the tpg in use.
*/
status_t
GraphConfig::getTPGFrameParams(rk_aiq_frame_params &tpgFrameParams)
{
css_err_t ret = css_err_none;
if (mSourceType == SRC_TPG) {
int32_t w,h;
Node *tpgPortNode = nullptr;
ret = mSettings->getDescendantByString(TPG_PORT_NAME, &tpgPortNode);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Error: Couldn't get tpg port_0 node from the graph");
return UNKNOWN_ERROR;
}
ret = getDimensions(tpgPortNode, w, h);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to get dimension for tpg port_0 Node");
return UNKNOWN_ERROR;
}
tpgFrameParams.cropped_image_width = w;
tpgFrameParams.cropped_image_height = h;
tpgFrameParams.horizontal_crop_offset = 0;
tpgFrameParams.vertical_crop_offset = 0;
tpgFrameParams.horizontal_scaling_numerator = 1;
tpgFrameParams.horizontal_scaling_denominator = 1;
tpgFrameParams.vertical_scaling_numerator = 1;
tpgFrameParams.vertical_scaling_denominator = 1;
return OK;
}
return UNKNOWN_ERROR;
}
/**
* Retrieve the resolution of the sensor mode in use.
* sensor frame params is used to inform 3A what is the size of the
* image that arrives to the ISP, in this case the ISA PG.
* We pick it up from the sensor node of the graph.
* In the settings we have had only width and height. We do not have
* attributes for the cropping or scaling factor.
* For that reason the dimensions set is the settings of the node should
* be the final size produced by the sensor, not the one of the pixel array.
*/
status_t
GraphConfig::getSensorFrameParams(rk_aiq_frame_params &sensorFrameParams)
{
Node *sensorNode = nullptr;
Node *pixelArrayNode = nullptr;
Node *binnerNode = nullptr;
Node *scalerNode = nullptr;
css_err_t ret = css_err_none;
int32_t w,h;
int32_t wPixArray,hPixArray;
int32_t lPixArray,tPixArray;
int32_t lFinalCrop = 0;
int32_t tFinalCrop = 0;
if (mSourceType != SRC_SENSOR) {
LOGE("wrong source type");
return UNKNOWN_ERROR;
}
// calculate the frame params when source is sensor
ret = mSettings->getDescendant(GCSS_KEY_SENSOR, &sensorNode);
if (ret != css_err_none) {
LOGE("Error: Couldn't get sensor mode node from the graph");
return UNKNOWN_ERROR;
}
ret = getDimensions(sensorNode, w, h);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to get dimension for sensor Node");
return UNKNOWN_ERROR;
}
ret = sensorNode->getDescendantByString("pixel_array:output", &pixelArrayNode);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to get pixel_array:output");
return UNKNOWN_ERROR;
}
ret = getDimensions(pixelArrayNode, wPixArray, hPixArray, lPixArray, tPixArray);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to get pixel array output dimension and crop");
return UNKNOWN_ERROR;
}
// start to accumulate cropping.
lFinalCrop = lPixArray;
tFinalCrop = tPixArray;
LOG1("%s: PixelArray output: w: %d, h: %d, crop l: %d, crop t: %d",
__FUNCTION__, wPixArray, hPixArray, lPixArray, tPixArray);
int32_t hBinning = 1;
int32_t vBinning = 1;
ret = sensorNode->getDescendant(GCSS_KEY_BINNER, &binnerNode);
if (ret != css_err_none) {
LOGW("Warning, no binner found, make sure sensor has no binner");
} else {
int32_t lBinner, tBinner; // binner left and top crop
int32_t wBinner, hBinner; // width and height of binner output
ret = getBinningFactor(binnerNode, hBinning, vBinning);
if (ret != css_err_none) {
LOGE("Error: Couldn't get binning factor");
return UNKNOWN_ERROR;
}
ret = sensorNode->getDescendantByString("binner:output", &binnerNode);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to get binner:output");
return UNKNOWN_ERROR;
}
ret = getDimensions(binnerNode, wBinner, hBinner, lBinner, tBinner);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to get binner output dimensions and crop");
return UNKNOWN_ERROR;
}
LOG1("%s: binner output w: %d, %d,"
" binning: w: %d, h: %d, crop w: %d, crop h: %d", __FUNCTION__, wBinner, hBinner,
hBinning, vBinning,
lBinner, tBinner);
// accumulate binner cropping.
lFinalCrop += (lBinner * hBinning);
tFinalCrop += (tBinner * vBinning);
}
int32_t scalingNum = 1; // avoid possible division by 0
int32_t scalingDenom = 1; // avoid possible division by 0
int32_t lScaler = 0; // left scaler crop
int32_t tScaler = 0; // top scaler crop
hBinning = (hBinning <= 0) ? 1 : hBinning;
vBinning = (vBinning <= 0) ? 1 : vBinning;
int32_t wScaler = wPixArray / hBinning;
int32_t hScaler = hPixArray / vBinning;
ret = sensorNode->getDescendant(GCSS_KEY_SCALER, &scalerNode);
if (ret != css_err_none) {
LOGW("Warning, no scaler found, make sure sensor has no scaler");
} else {
ret = getScalingFactor(scalerNode, scalingNum, scalingDenom);
if (ret != css_err_none) {
LOGE("Error: Couldn't get scaling factor");
return UNKNOWN_ERROR;
}
if (scalingDenom == 0) {
LOGE("Scaling Denominator is 0! Wrong setting! Set to 16");
scalingNum = 16;
}
if (scalingNum == 0) {
LOGE("Scaling Numerator is 0! Wrong setting! Set to 16");
scalingDenom = 16;
}
ret = sensorNode->getDescendantByString("scaler:output", &scalerNode);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to get scaler:output");
return UNKNOWN_ERROR;
}
ret = getDimensions(scalerNode, wScaler, hScaler, lScaler, tScaler);
if (CC_UNLIKELY(ret != css_err_none)) {
LOGE("Failed to get scaler output dimensions and crop");
return UNKNOWN_ERROR;
}
LOG1("%s: scaler output w: %d, h: %d, crop w: %d, crop h: %d",
__FUNCTION__,
wScaler, hScaler,
lScaler, tScaler);
}
int32_t wPixFormatIn = wScaler;
int32_t hPixFormatIn = hScaler;
int32_t wPixFormatOut = wPixFormatIn;
int32_t hPixFormatOut = hPixFormatIn;
int32_t lPixFormat = 0;
int32_t tPixFormat = 0;
int32_t lLastStep; // all horizontal croppings after last scaling
int32_t tLastStep; // all vertical croppings after last scaling
/*pixel formatter crop and scaler crop are be handled at the same time
* since they appear after the scaling
*/
lLastStep = lPixFormat + lScaler;
tLastStep = tPixFormat + tScaler;
lFinalCrop += ((lLastStep * scalingDenom) / scalingNum) * hBinning;
tFinalCrop += ((tLastStep * scalingDenom) / scalingNum) * vBinning;
int32_t wCroppedImage = ((wPixFormatOut * scalingDenom) / scalingNum * hBinning);
int32_t hCroppedImage = ((hPixFormatOut * scalingDenom) / scalingNum * vBinning);
LOG1("------------------- sensorFrameParams ---------------------------");
LOG1("%s: Final cropped Image w = %d, Final cropped Image h = %d",
__FUNCTION__, wCroppedImage, hCroppedImage);
LOG1("%s: Horizontal_crop_offset = %d, Vertical_crop_offset = %d",
__FUNCTION__, lFinalCrop, tFinalCrop);
LOG1("-----------------------------------------------------------------");
sensorFrameParams.cropped_image_width = wCroppedImage;
sensorFrameParams.cropped_image_height = hCroppedImage;
sensorFrameParams.horizontal_crop_offset = lFinalCrop;
sensorFrameParams.vertical_crop_offset = tFinalCrop;
sensorFrameParams.horizontal_scaling_numerator = SCALING_FACTOR;
sensorFrameParams.horizontal_scaling_denominator = SCALING_FACTOR;
sensorFrameParams.vertical_scaling_numerator = SCALING_FACTOR;
sensorFrameParams.vertical_scaling_denominator = SCALING_FACTOR;
return OK;
}
/**
* Retrieve the resolution of the first port for a given stream id
*
* Fill the resolution inside the a frame params struct for convenience
*
* \param[in] streamId Id of the stream to use.
* \param[out] frameParams
*
* \return OK
* \return UNKNOWN_ERROR if it could not find the stream id or the port
*/
status_t
GraphConfig::streamGetFrameParams(rk_aiq_frame_params &frameParams,
int32_t streamId)
{
Node *portNode = nullptr;
status_t status = OK;
PortFormatSettings format;
status = streamGetInputPort(streamId, &portNode);
status |= portGetFormat(portNode, format);
if (CC_UNLIKELY(status != OK)) {
LOGE("Failed to get input port format from stream %d", streamId);
return UNKNOWN_ERROR;
}
frameParams.cropped_image_width = format.width;
frameParams.cropped_image_height = format.height;
frameParams.horizontal_crop_offset = 0;
frameParams.vertical_crop_offset = 0;
frameParams.horizontal_scaling_numerator = 1;
frameParams.horizontal_scaling_denominator = 1;
frameParams.vertical_scaling_numerator = 1;
frameParams.vertical_scaling_denominator = 1;
return OK;
}
void GraphConfig::dumpSettings()
{
mSettings->dumpNodeTree(mSettings, 2);
}
void GraphConfig::dumpKernels(int32_t streamId)
{
}
GraphConfig::Rectangle::Rectangle(): w(0),h(0),t(0),l(0) {}
GraphConfig::SubdevPad::SubdevPad(): Rectangle(), mbusFormat(0){}
GraphConfig::SourceNodeInfo::SourceNodeInfo() : metadataEnabled(false),
interlaced(0) {}
} // namespace camera2
} // namespace android