/* * Copyright (C) 2016 The Android Open Source Project * * 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_NDEBUG 0 #define LOG_TAG "StaticProperties" #include "static_properties.h" #include #include #include #include "metadata/metadata_reader.h" namespace default_camera_hal { // Build stream capabilities from configs + stall durations. static bool ConstructStreamCapabilities( const std::vector& configs, const std::vector& stalls, StaticProperties::CapabilitiesMap* capabilities) { // Extract directional capabilities from the configs. for (const auto& config : configs) { switch (config.direction) { case ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT: (*capabilities)[config.spec].output_supported = true; break; case ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT: (*capabilities)[config.spec].input_supported = true; break; default: // Should never happen when using the MetadataReader; // it should validate directions. ALOGE("%s: Unrecognized stream config direction %d.", __func__, config.direction); return false; } } // Extract stall durations from the stalls. for (const auto& stall : stalls) { (*capabilities)[stall.spec].stall_duration = stall.duration; } return true; } // Check that each output config has a valid corresponding stall duration // (extra durations not matching any output config are ignored). static bool ValidateStreamCapabilities( StaticProperties::CapabilitiesMap capabilities) { for (const auto& spec_capabilities : capabilities) { // Only non-negative stall durations are valid. This should only happen // due to output streams without an associated stall duration, as // MetadataReader validates the metadata stall durations. if (spec_capabilities.second.output_supported && spec_capabilities.second.stall_duration < 0) { ALOGE( "%s: Static metadata does not have a stall duration for " "each output configuration. ", __func__); return false; } } return true; } // Validate that the input/output formats map matches up with // the capabilities listed for all formats. bool ValidateReprocessFormats( const StaticProperties::CapabilitiesMap& capabilities, const ReprocessFormatMap& reprocess_map) { // Get input formats. std::set all_input_formats; std::set all_output_formats; for (const auto& spec_capabilities : capabilities) { if (spec_capabilities.second.input_supported) { all_input_formats.insert(spec_capabilities.first.format); } if (spec_capabilities.second.output_supported) { all_output_formats.insert(spec_capabilities.first.format); } } // Must be at least one input format. if (all_input_formats.size() < 1) { ALOGE("%s: No input formats, reprocessing can't be supported.", __func__); return false; } // Check that the reprocess map input formats are exactly all available // input formats (check size here, then checking for actual value // matches will happen as part of the loop below). if (all_input_formats.size() != reprocess_map.size()) { ALOGE( "%s: Stream configuration input formats do not match " "input/output format map input formats.", __func__); return false; } // Check that each input format has at least one matching output format. for (const auto& input_format : all_input_formats) { const auto input_outputs_iterator = reprocess_map.find(input_format); if (input_outputs_iterator == reprocess_map.end()) { ALOGE( "%s: No output formats for input format %d.", __func__, input_format); return false; } // No need to check that the output formats vector is non-empty; // MetadataReader validates this. Instead just check that // all outputs are actually output formats. for (const auto& output_format : input_outputs_iterator->second) { if (all_output_formats.count(output_format) < 1) { ALOGE( "%s: Output format %d for input format %d " "is not a supported output format.", __func__, input_format, output_format); return false; } } } return true; } StaticProperties* StaticProperties::NewStaticProperties( std::unique_ptr metadata_reader) { int facing = 0; int orientation = 0; int32_t max_input_streams = 0; int32_t max_raw_output_streams = 0; int32_t max_non_stalling_output_streams = 0; int32_t max_stalling_output_streams = 0; std::set request_capabilities; std::vector configs; std::vector stalls; CapabilitiesMap stream_capabilities; ReprocessFormatMap reprocess_map; // If reading any data returns an error, something is wrong. if (metadata_reader->Facing(&facing) || metadata_reader->Orientation(&orientation) || metadata_reader->MaxInputStreams(&max_input_streams) || metadata_reader->MaxOutputStreams(&max_raw_output_streams, &max_non_stalling_output_streams, &max_stalling_output_streams) || metadata_reader->RequestCapabilities(&request_capabilities) || metadata_reader->StreamConfigurations(&configs) || metadata_reader->StreamStallDurations(&stalls) || !ConstructStreamCapabilities(configs, stalls, &stream_capabilities) || // MetadataReader validates configs and stall seperately, // but not that they match. !ValidateStreamCapabilities(stream_capabilities) || // Reprocessing metadata only necessary if input streams are allowed. (max_input_streams > 0 && (metadata_reader->ReprocessFormats(&reprocess_map) || // MetadataReader validates configs and the reprocess map seperately, // but not that they match. !ValidateReprocessFormats(stream_capabilities, reprocess_map)))) { return nullptr; } return new StaticProperties(std::move(metadata_reader), facing, orientation, max_input_streams, max_raw_output_streams, max_non_stalling_output_streams, max_stalling_output_streams, std::move(request_capabilities), std::move(stream_capabilities), std::move(reprocess_map)); } StaticProperties::StaticProperties( std::unique_ptr metadata_reader, int facing, int orientation, int32_t max_input_streams, int32_t max_raw_output_streams, int32_t max_non_stalling_output_streams, int32_t max_stalling_output_streams, std::set request_capabilities, CapabilitiesMap stream_capabilities, ReprocessFormatMap supported_reprocess_outputs) : metadata_reader_(std::move(metadata_reader)), facing_(facing), orientation_(orientation), max_input_streams_(max_input_streams), max_raw_output_streams_(max_raw_output_streams), max_non_stalling_output_streams_(max_non_stalling_output_streams), max_stalling_output_streams_(max_stalling_output_streams), request_capabilities_(std::move(request_capabilities)), stream_capabilities_(std::move(stream_capabilities)), supported_reprocess_outputs_(std::move(supported_reprocess_outputs)) {} bool StaticProperties::TemplateSupported(int type) { uint8_t required_capability = 0; switch (type) { case CAMERA3_TEMPLATE_PREVIEW: // Preview is always supported. return true; case CAMERA3_TEMPLATE_MANUAL: required_capability = ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR; break; case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG: required_capability = ANDROID_REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING; break; default: required_capability = ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE; return true; } return request_capabilities_.count(required_capability) > 0; } // Helper functions for checking stream properties when verifying support. static bool IsInputType(int stream_type) { return stream_type == CAMERA3_STREAM_INPUT || stream_type == CAMERA3_STREAM_BIDIRECTIONAL; } static bool IsOutputType(int stream_type) { return stream_type == CAMERA3_STREAM_OUTPUT || stream_type == CAMERA3_STREAM_BIDIRECTIONAL; } static bool IsRawFormat(int format) { return format == HAL_PIXEL_FORMAT_RAW10 || format == HAL_PIXEL_FORMAT_RAW12 || format == HAL_PIXEL_FORMAT_RAW16 || format == HAL_PIXEL_FORMAT_RAW_OPAQUE; } bool StaticProperties::StreamConfigurationSupported( const camera3_stream_configuration_t* stream_config) { return SanityCheckStreamConfiguration(stream_config) && InputStreamsSupported(stream_config) && OutputStreamsSupported(stream_config) && OperationModeSupported(stream_config); } bool StaticProperties::SanityCheckStreamConfiguration( const camera3_stream_configuration_t* stream_config) { // Check for null/empty values. if (stream_config == nullptr) { ALOGE("%s: NULL stream configuration array", __func__); return false; } else if (stream_config->num_streams == 0) { ALOGE("%s: Empty stream configuration array", __func__); return false; } else if (stream_config->streams == nullptr) { ALOGE("%s: NULL stream configuration streams", __func__); return false; } // Check that all streams are either inputs or outputs (or both). for (size_t i = 0; i < stream_config->num_streams; ++i) { const camera3_stream_t* stream = stream_config->streams[i]; if (stream == nullptr) { ALOGE("%s: Stream %zu is null", __func__, i); return false; } else if (!IsInputType(stream->stream_type) && !IsOutputType(stream->stream_type)) { ALOGE("%s: Stream %zu type %d is neither an input nor an output type", __func__, i, stream->stream_type); return false; } } return true; } bool StaticProperties::InputStreamsSupported( const camera3_stream_configuration_t* stream_config) { // Find the input stream(s). int32_t num_input_streams = 0; int input_format = -1; for (size_t i = 0; i < stream_config->num_streams; ++i) { const camera3_stream_t* stream = stream_config->streams[i]; if (IsInputType(stream->stream_type)) { // Check that this stream is valid as an input. const auto capabilities_iterator = stream_capabilities_.find(stream); if (capabilities_iterator == stream_capabilities_.end() || !capabilities_iterator->second.input_supported) { ALOGE("%s: %d x %d stream of format %d is not a supported input setup.", __func__, stream->width, stream->height, stream->format); return false; } // Valid input stream; count it. ++num_input_streams; input_format = stream->format; } } // Check the count. if (num_input_streams > max_input_streams_) { ALOGE( "%s: Requested number of input streams %d is greater than " "the maximum number supported by the device (%d).", __func__, num_input_streams, max_input_streams_); return false; } if (num_input_streams > 1) { ALOGE("%s: Camera HAL 3.4 only supports 1 input stream max.", __func__); return false; } // If there's an input stream, the configuration must have at least one // supported output format for reprocessing that input. if (num_input_streams > 0) { const auto input_output_formats_iterator = supported_reprocess_outputs_.find(input_format); if (input_output_formats_iterator == supported_reprocess_outputs_.end()) { // Should never happen; factory should verify that all valid inputs // have one or more valid outputs. ALOGE("%s: No valid output formats for input format %d.", __func__, input_format); return false; } bool match_found = false; // Go through outputs looking for a supported one. for (size_t i = 0; i < stream_config->num_streams; ++i) { const camera3_stream_t* stream = stream_config->streams[i]; if (IsOutputType(stream->stream_type)) { if (input_output_formats_iterator->second.count(stream->format) > 0) { match_found = true; break; } } } if (!match_found) { ALOGE("%s: No supported output format provided for input format %d.", __func__, input_format); return false; } } return true; } bool StaticProperties::OutputStreamsSupported( const camera3_stream_configuration_t* stream_config) { // Find and count output streams. int32_t num_raw = 0; int32_t num_stalling = 0; int32_t num_non_stalling = 0; for (size_t i = 0; i < stream_config->num_streams; ++i) { const camera3_stream_t* stream = stream_config->streams[i]; if (IsOutputType(stream->stream_type)) { // Check that this stream is valid as an output. const auto capabilities_iterator = stream_capabilities_.find(stream); if (capabilities_iterator == stream_capabilities_.end() || !capabilities_iterator->second.output_supported) { ALOGE( "%s: %d x %d stream of format %d " "is not a supported output setup.", __func__, stream->width, stream->height, stream->format); return false; } // Valid output; count it. if (IsRawFormat(stream->format)) { ++num_raw; } else if (capabilities_iterator->second.stall_duration > 0) { ++num_stalling; } else { ++num_non_stalling; } } } // Check that the counts are within bounds. if (num_raw > max_raw_output_streams_) { ALOGE( "%s: Requested stream configuration exceeds maximum supported " "raw output streams %d (requested %d).", __func__, max_raw_output_streams_, num_raw); return false; } else if (num_stalling > max_stalling_output_streams_) { ALOGE( "%s: Requested stream configuration exceeds maximum supported " "stalling output streams %d (requested %u).", __func__, max_stalling_output_streams_, num_stalling); return false; } else if (num_non_stalling > max_non_stalling_output_streams_) { ALOGE( "%s: Requested stream configuration exceeds maximum supported " "non-stalling output streams %d (requested %d).", __func__, max_non_stalling_output_streams_, num_non_stalling); return false; } return true; } bool StaticProperties::OperationModeSupported( const camera3_stream_configuration_t* stream_config) { switch (stream_config->operation_mode) { case CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE: return true; case CAMERA3_STREAM_CONFIGURATION_CONSTRAINED_HIGH_SPEED_MODE: // TODO(b/31370792): Check metadata for high speed support, // check that requested streams have support for high speed. ALOGE("%s: Support for CONSTRAINED_HIGH_SPEED not implemented", __func__); return false; default: ALOGE("%s: Unrecognized stream configuration mode: %d", __func__, stream_config->operation_mode); return false; } } bool StaticProperties::ReprocessingSupported( const camera3_stream_t* input_stream, const std::set& output_streams) { // There must be an input. if (!input_stream) { ALOGE("%s: No input stream.", __func__); return false; } // There must be an output. if (output_streams.size() < 1) { ALOGE("%s: No output stream.", __func__); return false; } const auto input_output_formats = supported_reprocess_outputs_.find(input_stream->format); if (input_output_formats == supported_reprocess_outputs_.end()) { // Should never happen for a valid input stream. ALOGE("%s: Input format %d does not support any output formats.", __func__, input_stream->format); return false; } // Check that all output streams can be outputs for the input stream. const std::set& supported_output_formats = input_output_formats->second; for (const auto output_stream : output_streams) { if (supported_output_formats.count(output_stream->format) < 1) { ALOGE( "%s: Output format %d is not a supported output " "for request input format %d.", __func__, output_stream->format, input_stream->format); return false; } } return true; } } // namespace default_camera_hal