Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5caea02d authored by Ján Sebechlebský's avatar Ján Sebechlebský Committed by Android (Google) Code Review
Browse files

Merge "Allow to specify list of supported input configurations." into main

parents 61d504d3 3b478c49
Loading
Loading
Loading
Loading
+162 −41
Original line number Diff line number Diff line
@@ -18,13 +18,19 @@
#define LOG_TAG "VirtualCameraDevice"
#include "VirtualCameraDevice.h"

#include <algorithm>
#include <array>
#include <chrono>
#include <cstdint>
#include <iterator>
#include <optional>
#include <string>

#include "VirtualCameraSession.h"
#include "aidl/android/companion/virtualcamera/SupportedStreamConfiguration.h"
#include "aidl/android/hardware/camera/common/Status.h"
#include "aidl/android/hardware/camera/device/CameraMetadata.h"
#include "aidl/android/hardware/camera/device/StreamConfiguration.h"
#include "android/binder_auto_utils.h"
#include "android/binder_status.h"
#include "log/log.h"
@@ -36,13 +42,16 @@ namespace android {
namespace companion {
namespace virtualcamera {

using ::aidl::android::companion::virtualcamera::Format;
using ::aidl::android::companion::virtualcamera::IVirtualCameraCallback;
using ::aidl::android::companion::virtualcamera::SupportedStreamConfiguration;
using ::aidl::android::hardware::camera::common::CameraResourceCost;
using ::aidl::android::hardware::camera::common::Status;
using ::aidl::android::hardware::camera::device::CameraMetadata;
using ::aidl::android::hardware::camera::device::ICameraDeviceCallback;
using ::aidl::android::hardware::camera::device::ICameraDeviceSession;
using ::aidl::android::hardware::camera::device::ICameraInjectionSession;
using ::aidl::android::hardware::camera::device::Stream;
using ::aidl::android::hardware::camera::device::StreamConfiguration;
using ::aidl::android::hardware::camera::device::StreamRotation;
using ::aidl::android::hardware::camera::device::StreamType;
@@ -55,16 +64,68 @@ using namespace std::chrono_literals;
// Prefix of camera name - "device@1.1/virtual/{numerical_id}"
const char* kDevicePathPrefix = "device@1.1/virtual/";

constexpr int32_t kVgaWidth = 640;
constexpr int32_t kVgaHeight = 480;
constexpr std::chrono::nanoseconds kMinFrameDuration30Fps = 1s / 30;
constexpr int32_t kMaxJpegSize = 3 * 1024 * 1024 /*3MiB*/;

constexpr MetadataBuilder::ControlRegion kDefaultEmptyControlRegion{};

struct Resolution {
  Resolution(const int w, const int h) : width(w), height(h) {
  }

  bool operator<(const Resolution& other) const {
    return width * height < other.width * other.height;
  }

  bool operator==(const Resolution& other) const {
    return width == other.width && height == other.height;
  }

  const int width;
  const int height;
};

std::optional<Resolution> getMaxResolution(
    const std::vector<SupportedStreamConfiguration>& configs) {
  auto itMax = std::max_element(configs.begin(), configs.end(),
                                [](const SupportedStreamConfiguration& a,
                                   const SupportedStreamConfiguration& b) {
                                  return a.width * b.height < a.width * b.height;
                                });
  if (itMax == configs.end()) {
    ALOGE(
        "%s: empty vector of supported configurations, cannot find largest "
        "resolution.",
        __func__);
    return std::nullopt;
  }

  return Resolution(itMax->width, itMax->height);
}

std::set<Resolution> getUniqueResolutions(
    const std::vector<SupportedStreamConfiguration>& configs) {
  std::set<Resolution> uniqueResolutions;
  std::transform(configs.begin(), configs.end(),
                 std::inserter(uniqueResolutions, uniqueResolutions.begin()),
                 [](const SupportedStreamConfiguration& config) {
                   return Resolution(config.width, config.height);
                 });
  return uniqueResolutions;
}

// TODO(b/301023410) - Populate camera characteristics according to camera configuration.
CameraMetadata initCameraCharacteristics() {
  auto metadata =
std::optional<CameraMetadata> initCameraCharacteristics(
    const std::vector<SupportedStreamConfiguration>& supportedInputConfig) {
  if (!std::all_of(supportedInputConfig.begin(), supportedInputConfig.end(),
                   [](const SupportedStreamConfiguration& config) {
                     return config.pixelFormat == Format::YUV_420_888;
                   })) {
    ALOGE("%s: input configuration contains unsupported pixel format", __func__);
    return std::nullopt;
  }

  MetadataBuilder builder =
      MetadataBuilder()
          .setSupportedHardwareLevel(
              ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL)
@@ -73,29 +134,8 @@ CameraMetadata initCameraCharacteristics() {
          .setSensorOrientation(0)
          .setAvailableFaceDetectModes({ANDROID_STATISTICS_FACE_DETECT_MODE_OFF})
          .setControlAfAvailableModes({ANDROID_CONTROL_AF_MODE_OFF})
          .setAvailableOutputStreamConfigurations(
              {MetadataBuilder::StreamConfiguration{
                   .width = kVgaWidth,
                   .height = kVgaHeight,
                   .format =
                       ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED,
                   .minFrameDuration = kMinFrameDuration30Fps,
                   .minStallDuration = 0s},
               MetadataBuilder::StreamConfiguration{
                   .width = kVgaWidth,
                   .height = kVgaHeight,
                   .format = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888,
                   .minFrameDuration = kMinFrameDuration30Fps,
                   .minStallDuration = 0s},
               {MetadataBuilder::StreamConfiguration{
                   .width = kVgaWidth,
                   .height = kVgaHeight,
                   .format = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB,
                   .minFrameDuration = kMinFrameDuration30Fps,
                   .minStallDuration = 0s}}})
          .setControlAeAvailableFpsRange(10, 30)
          .setControlMaxRegions(0, 0, 0)
          .setSensorActiveArraySize(0, 0, kVgaWidth, kVgaHeight)
          .setControlAfRegions({kDefaultEmptyControlRegion})
          .setControlAeRegions({kDefaultEmptyControlRegion})
          .setControlAwbRegions({kDefaultEmptyControlRegion})
@@ -106,9 +146,66 @@ CameraMetadata initCameraCharacteristics() {
          .setAvailableResultKeys({ANDROID_CONTROL_AF_MODE})
          .setAvailableCapabilities(
              {ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE})
          .setAvailableCharacteristicKeys()
          .build();
          .setAvailableCharacteristicKeys();

  // Active array size must correspond to largest supported input resolution.
  std::optional<Resolution> maxResolution =
      getMaxResolution(supportedInputConfig);
  if (!maxResolution.has_value()) {
    return std::nullopt;
  }
  builder.setSensorActiveArraySize(0, 0, maxResolution->width,
                                   maxResolution->height);

  std::vector<MetadataBuilder::StreamConfiguration> outputConfigurations;

  // TODO(b/301023410) Add also all "standard" resolutions we can rescale the
  // streams to (all standard resolutions with same aspect ratio).

  // Add IMPLEMENTATION_DEFINED format for all supported input resolutions.
  std::set<Resolution> uniqueResolutions =
      getUniqueResolutions(supportedInputConfig);
  std::transform(
      uniqueResolutions.begin(), uniqueResolutions.end(),
      std::back_inserter(outputConfigurations),
      [](const Resolution& resolution) {
        return MetadataBuilder::StreamConfiguration{
            .width = resolution.width,
            .height = resolution.height,
            .format = ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED,
            .minFrameDuration = kMinFrameDuration30Fps,
            .minStallDuration = 0s};
      });

  // Add all supported configuration with explicit pixel format.
  std::transform(supportedInputConfig.begin(), supportedInputConfig.end(),
                 std::back_inserter(outputConfigurations),
                 [](const SupportedStreamConfiguration& config) {
                   return MetadataBuilder::StreamConfiguration{
                       .width = config.width,
                       .height = config.height,
                       .format = static_cast<int>(config.pixelFormat),
                       .minFrameDuration = kMinFrameDuration30Fps,
                       .minStallDuration = 0s};
                 });

  // TODO(b/301023410) We currently don't support rescaling for still capture,
  // so only announce BLOB support for formats exactly matching the input.
  std::transform(uniqueResolutions.begin(), uniqueResolutions.end(),
                 std::back_inserter(outputConfigurations),
                 [](const Resolution& resolution) {
                   return MetadataBuilder::StreamConfiguration{
                       .width = resolution.width,
                       .height = resolution.height,
                       .format = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB,
                       .minFrameDuration = kMinFrameDuration30Fps,
                       .minStallDuration = 0s};
                 });

  ALOGV("Adding %zu output configurations", outputConfigurations.size());
  builder.setAvailableOutputStreamConfigurations(outputConfigurations);

  auto metadata = builder.build();
  if (metadata == nullptr) {
    ALOGE("Failed to build metadata!");
    return CameraMetadata();
@@ -121,10 +218,21 @@ CameraMetadata initCameraCharacteristics() {

VirtualCameraDevice::VirtualCameraDevice(
    const uint32_t cameraId,
    const std::vector<SupportedStreamConfiguration>& supportedInputConfig,
    std::shared_ptr<IVirtualCameraCallback> virtualCameraClientCallback)
    : mCameraId(cameraId),
      mVirtualCameraClientCallback(virtualCameraClientCallback) {
  mCameraCharacteristics = initCameraCharacteristics();
      mVirtualCameraClientCallback(virtualCameraClientCallback),
      mSupportedInputConfigurations(supportedInputConfig) {
  std::optional<CameraMetadata> metadata =
      initCameraCharacteristics(mSupportedInputConfigurations);
  if (metadata.has_value()) {
    mCameraCharacteristics = *metadata;
  } else {
    ALOGE(
        "%s: Failed to initialize camera characteristic based on provided "
        "configuration.",
        __func__);
  }
}

ndk::ScopedAStatus VirtualCameraDevice::getCameraCharacteristics(
@@ -168,29 +276,42 @@ ndk::ScopedAStatus VirtualCameraDevice::isStreamCombinationSupported(
    return cameraStatus(Status::ILLEGAL_ARGUMENT);
  }

  for (const auto& stream : in_streams.streams) {
  *_aidl_return = isStreamCombinationSupported(in_streams);
  return ndk::ScopedAStatus::ok();
};

bool VirtualCameraDevice::isStreamCombinationSupported(
    const StreamConfiguration& streamConfiguration) const {
  for (const Stream& stream : streamConfiguration.streams) {
    ALOGV("%s: Configuration queried: %s", __func__, stream.toString().c_str());

    if (stream.streamType == StreamType::INPUT) {
      ALOGW("%s: Input stream type is not supported", __func__);
      *_aidl_return = false;
      return ndk::ScopedAStatus::ok();
      return false;
    }

    // TODO(b/301023410) remove hardcoded format checks, verify against configuration.
    if (stream.width != 640 || stream.height != 480 ||
        stream.rotation != StreamRotation::ROTATION_0 ||
    if (stream.rotation != StreamRotation::ROTATION_0 ||
        (stream.format != PixelFormat::IMPLEMENTATION_DEFINED &&
         stream.format != PixelFormat::YCBCR_420_888 &&
         stream.format != PixelFormat::BLOB)) {
      *_aidl_return = false;
      return ndk::ScopedAStatus::ok();
    }
      ALOGV("Unsupported output stream type");
      return false;
    }

  *_aidl_return = true;
  return ndk::ScopedAStatus::ok();
    auto matchesSupportedInputConfig =
        [&stream](const SupportedStreamConfiguration& config) {
          return stream.width == config.width && stream.height == config.height;
        };
    if (std::none_of(mSupportedInputConfigurations.begin(),
                     mSupportedInputConfigurations.end(),
                     matchesSupportedInputConfig)) {
      ALOGV("Requested config doesn't match any supported input config");
      return false;
    }
  }
  return true;
}

ndk::ScopedAStatus VirtualCameraDevice::open(
    const std::shared_ptr<ICameraDeviceCallback>& in_callback,
@@ -198,7 +319,7 @@ ndk::ScopedAStatus VirtualCameraDevice::open(
  ALOGV("%s", __func__);

  *_aidl_return = ndk::SharedRefBase::make<VirtualCameraSession>(
      std::to_string(mCameraId), in_callback, mVirtualCameraClientCallback);
      *this, in_callback, mVirtualCameraClientCallback);

  return ndk::ScopedAStatus::ok();
};
+12 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <memory>

#include "aidl/android/companion/virtualcamera/IVirtualCameraCallback.h"
#include "aidl/android/companion/virtualcamera/SupportedStreamConfiguration.h"
#include "aidl/android/hardware/camera/device/BnCameraDevice.h"

namespace android {
@@ -34,6 +35,9 @@ class VirtualCameraDevice
 public:
  explicit VirtualCameraDevice(
      uint32_t cameraId,
      const std::vector<
          aidl::android::companion::virtualcamera::SupportedStreamConfiguration>&
          supportedInputConfig,
      std::shared_ptr<
          ::aidl::android::companion::virtualcamera::IVirtualCameraCallback>
          virtualCameraClientCallback = nullptr);
@@ -58,6 +62,10 @@ class VirtualCameraDevice
          in_streams,
      bool* _aidl_return) override;

  bool isStreamCombinationSupported(
      const ::aidl::android::hardware::camera::device::StreamConfiguration&
          in_streams) const;

  ndk::ScopedAStatus open(
      const std::shared_ptr<
          ::aidl::android::hardware::camera::device::ICameraDeviceCallback>&
@@ -94,6 +102,10 @@ class VirtualCameraDevice
      mVirtualCameraClientCallback;

  ::aidl::android::hardware::camera::device::CameraMetadata mCameraCharacteristics;

  const std::vector<
      aidl::android::companion::virtualcamera::SupportedStreamConfiguration>
      mSupportedInputConfigurations;
};

}  // namespace virtualcamera
+3 −1
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ namespace companion {
namespace virtualcamera {

using ::aidl::android::companion::virtualcamera::IVirtualCameraCallback;
using ::aidl::android::companion::virtualcamera::SupportedStreamConfiguration;
using ::aidl::android::hardware::camera::common::CameraDeviceStatus;
using ::aidl::android::hardware::camera::common::Status;
using ::aidl::android::hardware::camera::common::VendorTagSection;
@@ -154,9 +155,10 @@ ndk::ScopedAStatus VirtualCameraProvider::isConcurrentStreamCombinationSupported
}

std::shared_ptr<VirtualCameraDevice> VirtualCameraProvider::createCamera(
    const std::vector<SupportedStreamConfiguration>& supportedInputConfig,
    std::shared_ptr<IVirtualCameraCallback> virtualCameraClientCallback) {
  auto camera = ndk::SharedRefBase::make<VirtualCameraDevice>(
      sNextId++, virtualCameraClientCallback);
      sNextId++, supportedInputConfig, virtualCameraClientCallback);
  std::shared_ptr<ICameraProviderCallback> callback;
  {
    const std::lock_guard<std::mutex> lock(mLock);
+3 −0
Original line number Diff line number Diff line
@@ -77,6 +77,9 @@ class VirtualCameraProvider
  //
  // TODO(b/301023410) - Add camera configuration.
  std::shared_ptr<VirtualCameraDevice> createCamera(
      const std::vector<
          aidl::android::companion::virtualcamera::SupportedStreamConfiguration>&
          supportedInputConfig,
      std::shared_ptr<aidl::android::companion::virtualcamera::IVirtualCameraCallback>
          virtualCameraClientCallback = nullptr);

+41 −4
Original line number Diff line number Diff line
@@ -15,7 +15,6 @@
 */

// #define LOG_NDEBUG 0
#include "android/binder_status.h"
#define LOG_TAG "VirtualCameraService"
#include "VirtualCameraService.h"

@@ -27,9 +26,12 @@

#include "VirtualCameraDevice.h"
#include "VirtualCameraProvider.h"
#include "aidl/android/companion/virtualcamera/Format.h"
#include "aidl/android/companion/virtualcamera/VirtualCameraConfiguration.h"
#include "android/binder_auto_utils.h"
#include "android/binder_libbinder.h"
#include "binder/Status.h"
#include "util/Util.h"

using ::android::binder::Status;

@@ -37,10 +39,14 @@ namespace android {
namespace companion {
namespace virtualcamera {

using ::aidl::android::companion::virtualcamera::Format;
using ::aidl::android::companion::virtualcamera::SupportedStreamConfiguration;
using ::aidl::android::companion::virtualcamera::VirtualCameraConfiguration;

namespace {

constexpr int kVgaWidth = 640;
constexpr int kVgaHeight = 480;
constexpr char kEnableTestCameraCmd[] = "enable_test_camera";
constexpr char kDisableTestCameraCmd[] = "disable_test_camera";
constexpr char kShellCmdHelp[] = R"(
@@ -49,6 +55,27 @@ Available commands:
 * disable_test_camera
)";

ndk::ScopedAStatus validateConfiguration(
    const VirtualCameraConfiguration& configuration) {
  if (configuration.supportedStreamConfigs.empty()) {
    ALOGE("%s: No supported input configuration specified", __func__);
    return ndk::ScopedAStatus::fromServiceSpecificError(
        Status::EX_ILLEGAL_ARGUMENT);
  }

  for (const SupportedStreamConfiguration& config :
       configuration.supportedStreamConfigs) {
    if (!isFormatSupportedForInput(config.width, config.height,
                                   config.pixelFormat)) {
      ALOGE("%s: Requested unsupported input format: %d x %d (%d)", __func__,
            config.width, config.height, static_cast<int>(config.pixelFormat));
      return ndk::ScopedAStatus::fromServiceSpecificError(
          Status::EX_ILLEGAL_ARGUMENT);
    }
  }
  return ndk::ScopedAStatus::ok();
}

}  // namespace

VirtualCameraService::VirtualCameraService(
@@ -59,13 +86,18 @@ VirtualCameraService::VirtualCameraService(
ndk::ScopedAStatus VirtualCameraService::registerCamera(
    const ::ndk::SpAIBinder& token,
    const VirtualCameraConfiguration& configuration, bool* _aidl_return) {
  (void)configuration;
  if (_aidl_return == nullptr) {
    return ndk::ScopedAStatus::fromServiceSpecificError(
        Status::EX_ILLEGAL_ARGUMENT);
  }
  *_aidl_return = true;

  auto status = validateConfiguration(configuration);
  if (!status.isOk()) {
    *_aidl_return = false;
    return status;
  }

  std::lock_guard lock(mLock);
  if (mTokenToCameraName.find(token) != mTokenToCameraName.end()) {
    ALOGE(
@@ -74,11 +106,13 @@ ndk::ScopedAStatus VirtualCameraService::registerCamera(
        "0x%" PRIxPTR,
        reinterpret_cast<uintptr_t>(token.get()));
    *_aidl_return = false;
    return ndk::ScopedAStatus::ok();
  }

  // TODO(b/301023410) Validate configuration and pass it to the camera.
  std::shared_ptr<VirtualCameraDevice> camera =
      mVirtualCameraProvider->createCamera(configuration.virtualCameraCallback);
      mVirtualCameraProvider->createCamera(configuration.supportedStreamConfigs,
                                           configuration.virtualCameraCallback);
  if (camera == nullptr) {
    ALOGE("Failed to create camera for binder token 0x%" PRIxPTR,
          reinterpret_cast<uintptr_t>(token.get()));
@@ -159,7 +193,10 @@ void VirtualCameraService::enableTestCameraCmd(const int out, const int err) {
  mTestCameraToken.set(AIBinder_fromPlatformBinder(token));

  bool ret;
  registerCamera(mTestCameraToken, VirtualCameraConfiguration(), &ret);
  VirtualCameraConfiguration configuration;
  configuration.supportedStreamConfigs.push_back(
      {.width = kVgaWidth, .height = kVgaHeight, Format::YUV_420_888});
  registerCamera(mTestCameraToken, configuration, &ret);
  if (ret) {
    dprintf(out, "Successfully registered test camera %s",
            getCamera(mTestCameraToken)->getCameraName().c_str());
Loading