/* * 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 "GCSS" #include #include "LogHelper.h" #include "graph_query_manager.h" using namespace GCSS; using namespace GCSS::RelayControl; using namespace std; using namespace android::camera2; void GraphQueryManager::setGraphSettings(const IGraphConfig *graphSettings) { // \todo cast while the memberi is not IGraphConfig type mGraphSettings = static_cast(graphSettings); } void GraphQueryManager::setGraphDescriptor(const IGraphConfig *graphDescriptor) { // \todo cast while the memberi is not IGraphConfig type mGraphDescriptor = static_cast(graphDescriptor); } css_err_t GraphQueryManager::queryGraphs(const GraphQuery& query, GraphQueryResult& ret_vec, bool strict) { css_err_t ret; strictQuery = strict; // Get all settings level nodes GraphConfigNode::gcss_node_vector settingsVector; ret = mGraphSettings->getAllDescendants(settingsVector, GCSS_KEY_SETTINGS); if (ret != css_err_none) { LOGD("Invalid XML. Settings tag has no children."); return ret; } ret = goThroughSettings(query, settingsVector, ret_vec); return ret; } /** * Searches only from the provided search nodes * \param query std::map of items to search for * \param baseResults search only from inside these settings * \param ret this vector is populated with references to matched settings * \return css_err_t */ css_err_t GraphQueryManager::queryGraphs(const GraphQuery& query, const GraphQueryResult& baseResults, GraphQueryResult& ret_vec, bool strict) { strictQuery = strict; return goThroughSettings(query, baseResults, ret_vec); } /** * Go through every , and look for matches in search criteria */ css_err_t GraphQueryManager::goThroughSettings(const GraphQuery &query, const GraphQueryResult& settingsNodes, GraphQueryResult& ret_vec) { css_err_t ret; for (uint16_t i = 0; i < settingsNodes.size(); i++) { // Search hit counter uint16_t result = 0; // Loop through search items and put hit count to result ret = goThroughSearchItems(query, settingsNodes[i], result); if (ret != css_err_none) { return ret; } if (!strictQuery && result > 0) { ret_vec.push_back(settingsNodes[i]); } else if (result == query.size()) { ret_vec.push_back(settingsNodes[i]); } } if (ret_vec.size() == 0) { LOGE("No settings matching the query found"); return css_err_general; } return css_err_none; } /** * Checks every search item against settings data. If search is strict, we * escape as soon as some condition is not found. * \param query map of search items * \param settingsNode the settings node to search from * \param result count of matches * \return css_err_t */ css_err_t GraphQueryManager::goThroughSearchItems(const GraphQuery &query, GraphConfigNode *settingsNode, uint16_t &result) { css_err_t ret; GraphQuery::const_iterator ite; bool allFound = true; std::string str_val; int int_val = -1; for (ite = query.begin(); ite != query.end(); ++ite) { GraphConfigNode* graphNode = settingsNode; /** * Loop through item uids in the search query. Traverse the tree * node by node. Return back to root node when there's dead end or * match is found. */ for (uint16_t ii = 0; ii < ite->first.size(); ii++) { // If at the last uid in vector, check for attribute if (ii == ite->first.size() - 1) { // Try to get int ret = graphNode->getValue(ite->first[ii], int_val); if (ret == css_err_none) { str_val = std::to_string(int_val); } else { // try to get string ret = graphNode->getValue(ite->first[ii], str_val); if (ret != css_err_none) { if (strictQuery) allFound = false; } } // If searched string matches, increment result counter if (str_val == ite->second) result++; break; } else { // Get next level node ret = graphNode->getDescendant(ite->first[ii], &graphNode); if (ret != css_err_none) { // Dead end if (strictQuery) allFound = false; break; } } } if (allFound != true) break; } return css_err_none; } /** * Builds a graph which is a combination of data in graph descriptor and * graph settings. Resulting graph is based on connections defined in graph * descriptor's graph node. This function then applies settings for those * connections from the given settingsGraph. * * \param settingsGraph graph node containing the settings data * \param results the result graph to return */ css_err_t GraphQueryManager::getGraph(GraphConfigNode* settingsGraph, GraphConfigNode* results) { css_err_t ret; int settingsKey = -1, graphID = -1; if (settingsGraph == nullptr || mGraphDescriptor == nullptr || results == nullptr) return css_err_argument; // Get key from the settings and add that to the result graph ret = settingsGraph->getValue(GCSS_KEY_KEY, settingsKey); if (ret != css_err_none) return ret; ret = results->addValue(GCSS_KEY_KEY, settingsKey); if (ret != css_err_none) return ret; /* Get graph id from the settings and find corresponding graph node from the * graph descriptor */ ret = settingsGraph->getValue(GCSS_KEY_ID, graphID); if (ret != css_err_none) return ret; /* Get "graphs" node from the descriptor. Use graph id, and enumerate through children to find graph that is associated with selected settings.*/ const IGraphConfig *gcDescriptor = static_cast(mGraphDescriptor); IGraphConfig *graphs = gcDescriptor->getDescendant(GCSS_KEY_GRAPHS); if (graphs == nullptr) return css_err_data; uint32_t descCount = graphs->getDescendantCount(); IGraphConfig *graphNode = nullptr; for (uint32_t i = 0; i < descCount && graphNode == nullptr; i++) { graphNode = graphs->iterateDescendantByIndex(GCSS_KEY_ID, ia_uid(graphID), i); } if (graphNode == nullptr) { LOGE("could not find graph with id %d from graph descriptor", graphID); return css_err_data; } // \todo casting needed GraphConfigNode *gc_graph_node = static_cast(graphNode); // Copy graph config graph node GraphConfigNode *gc_graph_node_copy = gc_graph_node->copy(); if (gc_graph_node_copy == nullptr) { LOGE("Error creating GraphConfigNode: No memory"); return css_err_nomemory; } // Add graph node with connections to return tree ret = results->insertDescendant(gc_graph_node_copy, GCSS_KEY_GRAPH); if (ret != css_err_none) return ret; /** * Loop through all connections and add associated nodes from descriptor * * \todo if graph has other children than connections, a type attribute check * is needed using getDescendant iterator */ GraphConfigItem::const_iterator it = gc_graph_node->begin(); for (;it != gc_graph_node->end(); ++it) { if (it->second->type != NODE || it->first != GCSS_KEY_CONNECTION) continue; GraphConfigNode * connection_node = static_cast(it->second); // Insert connected nodes to result graph and add the settings std::string source_connection; std::string sink_connection; ret = connection_node->getValue(GCSS_KEY_SOURCE, source_connection); if (ret != css_err_none) return ret; ret = connection_node->getValue(GCSS_KEY_SINK, sink_connection); if (ret != css_err_none) return ret; string staticConnection; ret = connection_node->getValue(GCSS_KEY_STATIC, staticConnection); if (ret != css_err_none || staticConnection == "false") { ret = getConnectionData(source_connection, sink_connection, settingsGraph, results); if (ret != css_err_none) return ret; } else { ret = getStaticConnectionData(source_connection, sink_connection, results); if (ret != css_err_none) return ret; } } /* apply everything from settings */ ret = addDescendantsFromNode(results, settingsGraph, RELAY_RULE_ADD_NODES | RELAY_RULE_PROPAGATE | RELAY_RULE_OVERWRITE); if (ret != css_err_none) { LOGE("Failed to add settings to the result graph"); return ret; } // Get sensor node from settings and get its id /** \todo get rid of SENSOR specification in getGraph(), nesting * sensor mode data into sensor node based on mode attribute in settings * could re-use the common option-list logic. We just have to solve how * to provide option lists also via the settings file as option lists are * part of descriptor file atm. * Now just considering 'sensor' not mandatory node to allow describing * partial graphs or graphs with different types of sources. */ GraphConfigNode * settingsSensorNode; ret = settingsGraph->getDescendant(GCSS_KEY_SENSOR, &settingsSensorNode); if (ret != css_err_none) { LOGW("getGraph didn't find sensor, ignoring sensor modes."); return css_err_none; } std::string sensorModeID; ret = settingsSensorNode->getValue(GCSS_KEY_MODE_ID, sensorModeID); if (ret != css_err_none) { LOGW("GetGraph failed to set sensor mode. Sensor lacks mode_id"); return ret; } GraphConfigNode * descSensorNode; ret = results->getDescendant(GCSS_KEY_SENSOR, &descSensorNode); if (ret != css_err_none) return ret; /** * Find sensor modes from settings xml and apply contents from matching * sensor mode to the sensor node. */ GraphConfigNode *sensorModesNode; ret = mGraphSettings->getDescendant(GCSS_KEY_SENSOR_MODES, &sensorModesNode); if (ret != css_err_none) { LOGW("Settings file is missing sensor_modes"); return ret; } ret = addSensorModeData(descSensorNode, sensorModesNode, sensorModeID); if (ret != css_err_none) return ret; return css_err_none; } /** * Populate sensor node with data from sensor mode settings based on id. * \param sensorModesNode [in] Node that contains sensor modes * \param sensorModeID [in] id of the sensor mode to get * \return css_err_t */ css_err_t GraphQueryManager::addSensorModeData(GraphConfigNode *sensorNode, GraphConfigNode *sensorModesNode, const std::string &sensorModeID) { css_err_t ret; // Add globals from sensor modes node ret = addDescendantsFromNode(sensorNode, sensorModesNode, RELAY_RULE_OVERWRITE); if (ret != css_err_none) { LOGE("couldn't add settings from sensor modes node"); return ret; } GraphConfigNode * sensorModeNode; GraphConfigItem::const_iterator it = sensorModesNode->begin(); ret = sensorModesNode->getDescendant(GCSS_KEY_NAME, sensorModeID, it, &sensorModeNode); if (ret != css_err_none) return ret; // Add contents from sensor mode to sensor return addDescendantsFromNode(sensorNode, sensorModeNode); } GraphConfigNode* GraphQueryManager::getPortPeer(GraphConfigNode *portNode) { GraphConfigNode *root, *retNode = nullptr; css_err_t ret; /** \todo check the type once type is of integer attribute */ /** \todo peer attribute should be of reference type */ GraphConfigAttribute * peerAttr = nullptr; ret = portNode->getAttribute(GCSS_KEY_PEER, &peerAttr); if (ret != css_err_none) { /* no peer, nowhere to propagate */ return nullptr; } assert(peerAttr); std::string peerPortStr; ret = peerAttr->getValue(peerPortStr); if (ret != css_err_none) return nullptr; root = portNode->getRootNode(); assert(root); size_t del = peerPortStr.find(":"); string peerNode, peerPort; if (del == string::npos) { /* no delimiter -> virtual sink */ peerNode = peerPortStr; ret = root->getDescendant(ItemUID::str2key(peerNode), &retNode); if (ret != css_err_none) return nullptr; } else { peerNode = peerPortStr.substr(0, del); peerPort = peerPortStr.substr(del+1); ret = root->getDescendant(ItemUID::str2key(peerNode), &retNode); if (ret != css_err_none) return nullptr; ret = retNode->getDescendant(ItemUID::str2key(peerPort), &retNode); if (ret != css_err_none) return nullptr; } return retNode; } /** * copy node from descriptor to result node during getConnectionData * * One of the operations done several times by get graph is copying nodes from * the descriptor tree to the new result node tree that we are building * * This operation checks that if the node is already present in the result node * tree we do not add it more than once. * * \param[in] descriptorNodes Root node from the graph descriptor where all the * nodes in the graph are listed. This is the source. * \param[in] nodeId Unique id of the node to to copy * \param[out] resultNode Root node of the result settings being constructed by * getGraph. This is the destination of the copy * \return Pointer to the copied node in the result tree */ IGraphConfig *GraphQueryManager::copyNodeToResult(GraphConfigNode* descriptorNodes, ia_uid nodeId, GraphConfigNode* resultNode) { css_err_t ret = css_err_none; IGraphConfig *descNode = descriptorNodes->getDescendant(nodeId); if (!descNode) { LOGE("Node(%s) not found from descriptor", ItemUID::key2str(nodeId)); return nullptr; } IGraphConfig *outNode = resultNode->getDescendant(nodeId); if (!outNode) { ret = resultNode->insertDescendant( static_cast(descNode)->copy(), nodeId); if (ret != css_err_none) return nullptr; outNode = resultNode->getDescendant(nodeId); assert(outNode); } return outNode; } /** * Takes source and destination strings for a connection and looks for the * named nodes from the graph descriptor, it then copies the nodes to the * result tree and populates the corresponding values from the settings. * * This method is mainly used during get graph to construct the combined * settings + graph information. * * \param[in] source_connection Contains the connection source string * \param[in] sink_connection Contains the connection sink string * \param[in] settings It has the container node for settings * \param[out] ret_node The root node with the results of get graph that we are * building * \return css_err_t */ css_err_t GraphQueryManager::getConnectionData( const std::string& source_connection, const std::string& sink_connection, GraphConfigNode *settings, GraphConfigNode *ret_node) { GraphConfigNode *nodes = nullptr; css_err_t ret = css_err_general; string set_sink_connection = sink_connection; if (ret_node == nullptr) return css_err_argument; ret = mGraphDescriptor->getDescendant(GCSS_KEY_NODES, &nodes); if (ret != css_err_none) { LOGE("Error, graph_descriptor does not have a 'nodes' node"); return ret; } /* * Split sink/source values (node:port) to find the node name and * the port name. * Handle special cases: virtual sinks and sources. * Virtual sink is one end point of the graph. * Virtual source is a buffer source that inject buffers to the graph. * In those cases the port name is left empty and the uid set to * GCSS_KEY_NA */ std::string src_node_name; std::string src_port_name; std::size_t pos = source_connection.find(":"); if (pos == string::npos) { src_node_name = source_connection; } else { src_node_name = source_connection.substr(0, pos); src_port_name = source_connection.substr(pos+1); } pos = sink_connection.find(":"); std::string dst_node_name; std::string dst_port_name; if (pos == string::npos) { dst_node_name = sink_connection; } else { dst_node_name = sink_connection.substr(0, pos); dst_port_name = sink_connection.substr(pos+1); } ia_uid src_node_uid = ItemUID::str2key(src_node_name); ia_uid dst_node_uid = ItemUID::str2key(dst_node_name); ia_uid src_port_uid = ItemUID::str2key(src_port_name); ia_uid dst_port_uid = ItemUID::str2key(dst_port_name); /* Handle source : */ /* source settings missing implicitly mean that source is not part of * the active graph or does not need settings */ IGraphConfig *setSrcNode = settings->getDescendant(src_node_uid); if (!setSrcNode) { LOGW("Node(%s) not found from settings, ignoring connection", src_node_name.c_str()); return css_err_none; } IGraphConfig *setSrcPort = nullptr; if (src_port_uid == GCSS_KEY_NA) { // in case of virtual source the node is treated as a port setSrcPort = setSrcNode; } else { setSrcPort = setSrcNode->getDescendant(src_port_uid); if (!setSrcPort) { LOGW("Node(%s) port %s not found from settings", src_node_name.c_str(), src_port_name.c_str()); return css_err_none; } } /* Copy src node from descriptor nodes to result */ IGraphConfig *outSrcNode = copyNodeToResult(nodes, src_node_uid, ret_node); if (outSrcNode == nullptr) { LOGE("Failed to copy src node(%s) from descriptor to settings", src_node_name.c_str()); return css_err_general; } /* * A src port is disabled only if the attribute enabled is present and set * to 0, if the attribute is not present it is assumed that the port is * enabled. */ int32_t is_src_port_enabled; ret = setSrcPort->getValue(GCSS_KEY_ENABLED, is_src_port_enabled); if (ret == css_err_none && !is_src_port_enabled) { // src port disabled skip connection processing LOGD("Src port %s disabled, skip dst and peer processing", src_node_name.c_str()); return css_err_none; } /* For virtual sinks check if source settings explicitly redefine the * connection to sink. If not just take the sink defined by graph. * If virtual sink is not in settings then ignore. All active virtual * sinks should appear in the settings */ if (dst_port_uid == GCSS_KEY_NA) { ret = setSrcPort->getValue(GCSS_KEY_PEER, set_sink_connection); if (ret != css_err_none) { LOGD("Using default connection %s", source_connection.c_str()); } else { // override the destination node uid dst_node_uid = ItemUID::str2key(set_sink_connection); LOGD("Overriding destination node %s with %s", dst_node_name.c_str(),set_sink_connection.c_str()); } IGraphConfig *setDstNode = settings->getDescendant(dst_node_uid); if (setDstNode == nullptr) { LOGD("Ignoring node %s for not being in settings", dst_node_name.c_str()); return css_err_none; } //add for rockchip, zyc int32_t is_dst_port_enabled; ret = setDstNode->getValue(GCSS_KEY_ENABLED, is_dst_port_enabled); if (ret == css_err_none && !is_dst_port_enabled) { // dst port disabled skip connection processing LOGD("Dst port %s disabled, skip dst and peer processing", dst_node_name.c_str()); return css_err_none; } } /* Handle sink : copy sink node to results */ IGraphConfig *outDstNode = copyNodeToResult(nodes, dst_node_uid, ret_node); if (outDstNode == nullptr) { LOGE("Failed to copy dst node(%s) from descriptor to settings", dst_node_name.c_str()); return css_err_general; } if (dst_port_uid == GCSS_KEY_NA) { /* for virtual sinks we add direction input as if they were ports */ ret = static_cast(outDstNode) ->addValue(GCSS_KEY_DIRECTION, 0); if (ret != css_err_none) return ret; } IGraphConfig *outSrcPort; if (src_port_uid == GCSS_KEY_NA) { // in case of virtual source the node is treated as a port outSrcPort = outSrcNode; } else { outSrcPort = outSrcNode->getDescendant(src_port_uid); if (!outSrcPort) { LOGE("Node(%s) has no port named '%s'", src_node_name.c_str(), src_port_name.c_str()); return css_err_general; } } IGraphConfig *outDstPort; if (dst_port_uid == GCSS_KEY_NA) { /* in case of virtual sink, the node is considered as a port */ outDstPort = outDstNode; string type; ret = outDstPort->getValue(GCSS_KEY_TYPE, type); if (ret != css_err_none) { LOGE("No type for connected peer node"); return ret; } if (type.compare("sink") != 0) { LOGE("sink connection attribute without port '%s' not pointing to virtual sink", type.c_str()); return css_err_general; } /** \todo WORKAROUND[START]: add stream_id's to every 'sink' */ int32_t stream_id; /* check if already set */ ret = outDstPort->getValue(GCSS_KEY_STREAM_ID, stream_id); if (ret != css_err_none) { /* read stream id from ancestor of the port that we are propagating */ ret = outSrcNode->getValue(GCSS_KEY_STREAM_ID, stream_id); if (ret != css_err_none) { LOGE("No stream_id set for connected peer"); return ret; } ret = static_cast(outDstPort) ->addValue(GCSS_KEY_STREAM_ID, stream_id); if (ret != css_err_none) { LOGE("Failed to add stream ID to sink peer"); return ret; } } /* WORKAROUND[END] */ } else { outDstPort = outDstNode->getDescendant(dst_port_uid); if (!outDstPort) { LOGE("Node(%s) has no port named '%s'", dst_node_name.c_str(), dst_port_name.c_str()); return css_err_general; } } /* * Source and destination are valid. * Set the peer attribute for each of the ends of the connection. */ std::string peerStr; GraphConfigStrAttribute * peerAttribute; ret = outSrcPort->getValue(GCSS_KEY_PEER, peerStr); if (ret == css_err_none) { LOGD("Node(%s) port '%s' already connected once to '%s'", src_node_name.c_str(), src_port_name.c_str(), peerStr.c_str()); } // even the src port is already in another connection, // Allow port to have multiple connections. peerAttribute = new GraphConfigStrAttribute; if (peerAttribute == nullptr) return css_err_nomemory; peerAttribute->setValue(set_sink_connection); ret = static_cast(outSrcPort) ->insertDescendant(peerAttribute, GCSS_KEY_PEER); if (ret != css_err_none) { delete peerAttribute; return ret; } ret = outDstPort->getValue(GCSS_KEY_PEER, peerStr); if (ret != css_err_none) { ret = static_cast(outDstPort) ->addValue(GCSS_KEY_PEER, source_connection); if (ret != css_err_none) { return ret; } } else { LOGD("Node(%s) port '%s' already connected once to '%s' adding new peer", dst_node_name.c_str(), dst_port_name.c_str(), peerStr.c_str()); // even the dst port is already in another connection, // Allow port to have multiple connections. peerAttribute = new GraphConfigStrAttribute; if (peerAttribute == nullptr) return css_err_nomemory; peerAttribute->setValue(source_connection); ret = static_cast(outDstPort) ->insertDescendant(peerAttribute, GCSS_KEY_PEER); if (ret != css_err_none) { delete peerAttribute; return ret; } } return css_err_none; } /** * Handler of static connections * * Static connections propagate the nodes to the combined settings (output of * getGraph, here also referred as the result) no matter what is the content of * the settings. * * For connections with sinks as destination, it propagates the following * properties from the source port to the sink: * - EXEC_CTX_ID * - CONTENT_TYPE * * Like regular connections it also sets the PEER attribute correctly in each * end-node of the connection. */ css_err_t GraphQueryManager::getStaticConnectionData( const std::string& source_connection, const std::string& sink_connection, GraphConfigNode *ret_node) { GraphConfigNode *nodes = nullptr; css_err_t ret = css_err_general; string set_sink_connection = sink_connection; if (ret_node == nullptr) return css_err_argument; ret = mGraphDescriptor->getDescendant(GCSS_KEY_NODES, &nodes); if (ret != css_err_none) { LOGE("Error, graph_descriptor does not have a 'nodes' node"); return ret; } /* * Split sink/source values (node:port) to find the node name and * the port name. * Handle special cases: virtual sinks and sources. * Virtual sink is one end point of the graph. * Virtual source is a buffer source that inject buffers to the graph. * In those cases the port name is left empty and the uid set to * GCSS_KEY_NA */ std::string src_node_name; std::string src_port_name; std::size_t pos = source_connection.find(":"); if (pos == string::npos) { src_node_name = source_connection; } else { src_node_name = source_connection.substr(0, pos); src_port_name = source_connection.substr(pos+1); } pos = sink_connection.find(":"); std::string dst_node_name; std::string dst_port_name; if (pos == string::npos) { dst_node_name = sink_connection; } else { dst_node_name = sink_connection.substr(0, pos); dst_port_name = sink_connection.substr(pos+1); } ia_uid src_node_uid = ItemUID::str2key(src_node_name); ia_uid dst_node_uid = ItemUID::str2key(dst_node_name); ia_uid src_port_uid = ItemUID::str2key(src_port_name); ia_uid dst_port_uid = ItemUID::str2key(dst_port_name); /* Handle source : */ /* Copy src node from descriptor nodes to result */ IGraphConfig *outSrcNode = copyNodeToResult(nodes, src_node_uid, ret_node); if (outSrcNode == nullptr) { LOGE("Failed to copy src node(%s) from descriptor to settings", src_node_name.c_str()); return css_err_general; } /* Handle sink : copy sink node to results */ IGraphConfig *outDstNode = copyNodeToResult(nodes, dst_node_uid, ret_node); if (outDstNode == nullptr) { LOGE("Failed to copy dst node(%s) from descriptor to settings", dst_node_name.c_str()); return css_err_general; } if (dst_port_uid == GCSS_KEY_NA) { /* for virtual sinks we add direction input as if they were ports */ ret = static_cast(outDstNode) ->addValue(GCSS_KEY_DIRECTION, 0); if (ret != css_err_none) return ret; } IGraphConfig *outSrcPort; if (src_port_uid == GCSS_KEY_NA) { // in case of virtual source the node is treated as a port outSrcPort = outSrcNode; } else { outSrcPort = outSrcNode->getDescendant(src_port_uid); if (!outSrcPort) { LOGE("Node(%s) has no port named '%s'", src_node_name.c_str(), src_port_name.c_str()); return css_err_general; } } IGraphConfig *outDstPort; if (dst_port_uid == GCSS_KEY_NA) { /* in case of virtual sink, the node is considered as a port */ outDstPort = outDstNode; /* Propagate the execution context of the src node into the sink */ propagateIntAttribute(outSrcNode, outDstNode, GCSS_KEY_EXEC_CTX_ID); /* Propagate the content_type of the src node into the sink */ propagateStrAttribute(outSrcNode, outDstNode, GCSS_KEY_CONTENT_TYPE); } else { outDstPort = outDstNode->getDescendant(dst_port_uid); if (!outDstPort) { LOGE("Node(%s) has no port named '%s'", dst_node_name.c_str(), dst_port_name.c_str()); return css_err_general; } } /* * Source and destination are valid. * Set the peer attribute for each of the ends of the connection. */ std::string peerStr; GraphConfigStrAttribute * peerAttribute; ret = outSrcPort->getValue(GCSS_KEY_PEER, peerStr); if (ret == css_err_none) { LOGD("Node(%s) port '%s' already connected once to '%s'", src_node_name.c_str(), src_port_name.c_str(), peerStr.c_str()); } // even the src port is already in another connection, // Allow port to have multiple connections. peerAttribute = new GraphConfigStrAttribute; if (peerAttribute == nullptr) return css_err_nomemory; peerAttribute->setValue(set_sink_connection); ret = static_cast(outSrcPort) ->insertDescendant(peerAttribute, GCSS_KEY_PEER); if (ret != css_err_none) { delete peerAttribute; return ret; } ret = outDstPort->getValue(GCSS_KEY_PEER, peerStr); if (ret != css_err_none) { ret = static_cast(outDstPort) ->addValue(GCSS_KEY_PEER, source_connection); if (ret != css_err_none) { return ret; } } else { LOGD("Node(%s) port '%s' already connected once to '%s' adding new peer", dst_node_name.c_str(), dst_port_name.c_str(), peerStr.c_str()); // even the dst port is already in another connection, // Allow port to have multiple connections. peerAttribute = new GraphConfigStrAttribute; if (peerAttribute == nullptr) return css_err_nomemory; peerAttribute->setValue(source_connection); ret = static_cast(outDstPort) ->insertDescendant(peerAttribute, GCSS_KEY_PEER); if (ret != css_err_none) { delete peerAttribute; return ret; } } return css_err_none; } css_err_t GraphQueryManager::handleAttributeOptions(GraphConfigNode *node, ia_uid attribute_key, const string &newValue) { css_err_t ret; /* find if ite->second has an option list */ GraphConfigNode *opNode = nullptr; GraphConfigNode::const_iterator op_ite; for (op_ite = node->begin(); op_ite != node->end(); ++op_ite) { /** \todo cannot use generic getDescendant overloads for searh because * need to ensure that returned node is one of options */ if (op_ite->second->type != NODE) continue; if (op_ite->first != GCSS_KEY_OPTIONS) continue; css_err_t ret = node->iterateAttributes(GCSS_KEY_ATTRIBUTE, (int)attribute_key, op_ite); if (ret == css_err_none) { opNode = static_cast(op_ite->second); break; } } if (!opNode) return css_err_noentry; // select option where value matches newValue op_ite = opNode->begin(); ret = opNode->getDescendant(GCSS_KEY_VALUE, newValue, op_ite, &opNode); if (ret != css_err_none) { LOGE("Failed to find attribute value '%s' from its option list", newValue.c_str()); return ret; } ret = opNode->getDescendant(GCSS_KEY_APPLY, &opNode); if (ret != css_err_none) return css_err_none; ret = addDescendantsFromNode(node->getRootNode(), opNode, RELAY_RULE_OVERWRITE); if (ret != css_err_none) { LOGE("Failed to apply option attributes"); return ret; } return css_err_none; } /** * Populate object with children of the param node */ css_err_t GraphQueryManager::addDescendantsFromNode( GraphConfigNode* to, GraphConfigNode* from, Rule rr) { GraphConfigItem::gcss_item_map::const_iterator ite; css_err_t ret; if (!to || ! from) return css_err_argument; for (ite = from->item.begin(); ite != from->item.end(); ++ite) { GraphConfigNode * gc_settings_node = nullptr; // traverse to descendant if it already exists ret = to->getDescendant(ite->first, &gc_settings_node); if (ret == css_err_none) { /* never overwrite options */ if (!(rr & RELAY_RULE_OVERWRITE) || ite->first == GCSS_KEY_OPTIONS) continue; ret = addDescendantsFromNode( gc_settings_node, static_cast(ite->second), rr); if (ret != css_err_none) return ret; continue; } /* If attribute exists, update descriptor value from the settings, * otherwise copy the node from descriptor to the results. */ std::string newValueStr; GraphConfigAttribute * resultsAttr = nullptr; if(ite->second->type == INT_ATTRIBUTE) { int newValue = 0; ret = to->getAttribute(ite->first, &resultsAttr); if (ret == css_err_none) { // Do not overwrite if id if (!(rr & RELAY_RULE_OVERWRITE) || ite->first == GCSS_KEY_ID || ite->first == GCSS_KEY_DIRECTION) continue; ite->second->getValue(newValue); resultsAttr->setValue(newValue); if (rr & RELAY_RULE_HANDLE_OPTIONS) { newValueStr = to_string(newValue); ret = handleAttributeOptions(to, ite->first, newValueStr); if (ret != css_err_none && ret != css_err_noentry) return ret; } continue; } resultsAttr = static_cast(ite->second)->copy(); if (resultsAttr == nullptr) { LOGE("Error creating GraphConfigItem: No memory"); return css_err_nomemory; } if (rr & RELAY_RULE_HANDLE_OPTIONS) { ret = resultsAttr->getValue(newValue); if (ret != css_err_none) { delete resultsAttr; return ret; } newValueStr = to_string(newValue); ret = handleAttributeOptions(to, ite->first, newValueStr); if (ret != css_err_none && ret != css_err_noentry) { delete resultsAttr; return ret; } } to->insertDescendant(resultsAttr, ite->first); } else if (ite->second->type == STR_ATTRIBUTE) { ret = to->getAttribute(ite->first, &resultsAttr); if(ret == css_err_none) { // Do not overwrite if name or type if (!(rr & RELAY_RULE_OVERWRITE) || ite->first == GCSS_KEY_NAME || ite->first == GCSS_KEY_TYPE || ite->first == GCSS_KEY_PEER) continue; ite->second->getValue(newValueStr); resultsAttr->setValue(newValueStr); if (rr & RELAY_RULE_HANDLE_OPTIONS) { ret = handleAttributeOptions(to, ite->first, newValueStr); if (ret != css_err_none && ret != css_err_noentry) return ret; } continue; } resultsAttr = static_cast(ite->second)->copy(); if (resultsAttr == nullptr) { LOGE("Error creating GraphConfigItem: No memory"); return css_err_nomemory; } if (rr & RELAY_RULE_HANDLE_OPTIONS) { ret = resultsAttr->getValue(newValueStr); if (ret != css_err_none) { delete resultsAttr; return ret; } ret = handleAttributeOptions(to, ite->first, newValueStr); if (ret != css_err_none && ret != css_err_noentry) { delete resultsAttr; return ret; } } to->insertDescendant(resultsAttr, ite->first); } else if (rr & RELAY_RULE_ADD_NODES) { GraphConfigNode * results_node = static_cast(ite->second)->copy(); if (results_node == nullptr) { LOGE("Error creating GraphConfigItem: No memory"); return css_err_nomemory; } to->insertDescendant(results_node, ite->first); } } /* propagate between connected ports */ if (rr & RELAY_RULE_PROPAGATE) { /* only propagate to downstream when mandatory direction attribute is * set */ int32_t direction; ret = to->getValue(GCSS_KEY_DIRECTION, direction); if (ret != css_err_none) return css_err_none; else if (direction != 1) return css_err_none; GraphConfigNode* peerNode = GraphQueryManager::getPortPeer(to); if (!peerNode) return css_err_none; /** \todo WORKAROUND: don't overwrite when propagating to virtual sinks * Currently settings are missing explicit way to tell difference * between queried dimension and actual buffer dimenssions. */ string type; Rule rrPropagate = RELAY_RULE_ADD_NODES; ret = peerNode->getValue(GCSS_KEY_TYPE, type); if (ret != css_err_none || type.compare("sink") != 0) rrPropagate = RELAY_RULE_OVERWRITE; /* Note: using 'to' as source instead of 'from' (settings) in order to * propagate also the option lists */ ret = addDescendantsFromNode(peerNode, to, rrPropagate); if (ret != css_err_none) return ret; } return css_err_none; } /** * Propagate an integer attribute from one node to another * * The method checks if the attribute is already present in the destination * node. If it is it just updates the value with the value from the source node. * If it is not present, it inserts a new attribute. * * \param[in] srcNode Source node to take the the attribute value * \param[in] dstNode Destination node that will receive the the attribute value * \param[in] attributeId The id for the attribute (like ex: GCSS_KEY_TYPE) * * \returns css_err_none if everything went fine. * \returns css_err_nomemory if the allocation of the new attribute failed. * \returns css_err_general if the attribute id is not of the expected type. */ css_err_t GraphQueryManager::propagateIntAttribute(IGraphConfig *srcNode, IGraphConfig *dstNode, ia_uid attributeId) { int32_t value = -1; css_err_t ret = css_err_none; bool update = false; /* check first if the value is already present in the destination */ ret = dstNode->getValue(attributeId, value); if (ret == css_err_none) { // we only need to update it update = true; } /* acquire the value from the src */ ret = srcNode->getValue(attributeId, value); if (ret != css_err_none) return ret; /* update or insert depending */ if (!update) { GraphConfigIntAttribute *attr = new GraphConfigIntAttribute; if (attr == nullptr) return css_err_nomemory; ret = attr->setValue(value); if (ret != css_err_none) { delete attr; return ret; } ret = static_cast(dstNode) ->insertDescendant(attr, attributeId); if (ret != css_err_none) delete attr; } else { dstNode->setValue(attributeId, value); } return ret; } /** * Propagate an string attribute from one node to another * * The method checks if the attribute is already present in the destination * node. If it is it just updates the value with the value from the source node. * If it is not present, it inserts a new attribute. * * \param[in] srcNode Source node to take the the attribute value * \param[in] dstNode Destination node that will receive the the attribute value * \param[in] attributeId The id for the attribute (like ex: GCSS_KEY_TYPE) * * \returns css_err_none if everything went fine. * \returns css_err_nomemory if the allocation of the new attribute failed. * \returns css_err_general if the attribute id is not of the expected type. */ css_err_t GraphQueryManager::propagateStrAttribute(IGraphConfig *srcNode, IGraphConfig *dstNode, ia_uid attributeId) { string value; css_err_t ret = css_err_none; bool update = false; /* check first if the value is already present in the destination */ ret = dstNode->getValue(attributeId, value); if (ret == css_err_none) { // we only need to update it update = true; } /* acquire the value from the src */ ret = srcNode->getValue(attributeId, value); if (ret != css_err_none) return ret; /* update or insert depending */ if (!update) { GraphConfigStrAttribute *attr = new GraphConfigStrAttribute; if (attr == nullptr) return css_err_nomemory; ret = attr->setValue(value); if (ret != css_err_none) { delete attr; return ret; } ret = static_cast(dstNode) ->insertDescendant(attr, attributeId); if (ret != css_err_none) delete attr; } else { dstNode->setValue(attributeId, value); } return ret; }