chromium/chromeos/ash/components/kiosk/vision/internals_page_processor.cc

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/ash/components/kiosk/vision/internals_page_processor.h"

#include <cstddef>
#include <utility>
#include <vector>

#include "base/feature_list.h"
#include "chromeos/ash/components/kiosk/vision/webui/kiosk_vision_internals.mojom.h"
#include "media/capture/video/chromeos/mojom/cros_camera_service.mojom-forward.h"
#include "media/capture/video/chromeos/mojom/cros_camera_service.mojom.h"

namespace ash::kiosk_vision {

namespace {

mojom::StatePtr NewState(mojom::Status status,
                         std::vector<mojom::LabelPtr> labels = {},
                         std::vector<mojom::BoxPtr> boxes = {},
                         std::vector<mojom::FacePtr> faces = {}) {
  return mojom::State::New(status, std::move(labels), std::move(boxes),
                           std::move(faces));
}

mojom::LabelPtr ToPageLabel(int person_id,
                            const cros::mojom::KioskVisionBoundingBox& box) {
  return mojom::Label::New(person_id, box.x, box.y);
}

std::vector<mojom::LabelPtr> ToLabels(
    const std::vector<cros::mojom::KioskVisionAppearancePtr>& appearances) {
  std::vector<mojom::LabelPtr> labels;
  for (const auto& appearance : appearances) {
    // An appearance can have two boxes. Add a label for one of the two.
    if (appearance->body) {
      labels.push_back(
          ToPageLabel(appearance->person_id, *appearance->body->box));
    } else if (appearance->face) {
      labels.push_back(
          ToPageLabel(appearance->person_id, *appearance->face->box));
    }
  }
  return labels;
}

mojom::BoxPtr ToPageBox(const cros::mojom::KioskVisionBoundingBox& box) {
  return mojom::Box::New(/*x=*/box.x,
                         /*y=*/box.y,
                         /*width=*/box.width,
                         /*height=*/box.height);
}

std::vector<mojom::BoxPtr> ToBoxes(
    const std::vector<cros::mojom::KioskVisionAppearancePtr>& appearances) {
  std::vector<mojom::BoxPtr> boxes;
  for (const auto& appearance : appearances) {
    if (appearance->face) {
      boxes.push_back(ToPageBox(*appearance->face->box));
    }
    if (appearance->body) {
      boxes.push_back(ToPageBox(*appearance->body->box));
    }
  }
  return boxes;
}

mojom::FacePtr ToPageFace(const cros::mojom::KioskVisionFaceDetection& face) {
  return mojom::Face::New(
      /*roll=*/face.roll,
      /*pan=*/face.pan,
      /*tilt=*/face.tilt,
      /*box=*/ToPageBox(*face.box));
}

std::vector<mojom::FacePtr> ToFaces(
    const std::vector<cros::mojom::KioskVisionAppearancePtr>& appearances) {
  std::vector<mojom::FacePtr> faces;
  for (const auto& appearance : appearances) {
    if (appearance->face) {
      faces.push_back(ToPageFace(*appearance->face));
    }
  }
  return faces;
}

}  // namespace

InternalsPageProcessor::InternalsPageProcessor()
    : last_state_(NewState(mojom::Status::kUnknown)) {}

InternalsPageProcessor::~InternalsPageProcessor() = default;

void InternalsPageProcessor::OnFrameProcessed(
    const cros::mojom::KioskVisionDetection& detection) {
  NotifyStateChange(
      NewState(mojom::Status::kRunning, ToLabels(detection.appearances),
               ToBoxes(detection.appearances), ToFaces(detection.appearances)));
}

void InternalsPageProcessor::OnTrackCompleted(
    const cros::mojom::KioskVisionTrack& track) {
  NotifyStateChange(NewState(mojom::Status::kRunning,
                             ToLabels(track.appearances),
                             ToBoxes(track.appearances)));
}

void InternalsPageProcessor::OnError(cros::mojom::KioskVisionError error) {
  NotifyStateChange(NewState(mojom::Status::kError));
}

void InternalsPageProcessor::NotifyStateChange(mojom::StatePtr new_state) {
  last_state_ = std::move(new_state);
  for (auto& observer : observers_) {
    observer.OnStateChange(*last_state_);
  }
}

void InternalsPageProcessor::AddObserver(Observer* observer) {
  observers_.AddObserver(observer);
  observer->OnStateChange(*last_state_);
}

void InternalsPageProcessor::RemoveObserver(Observer* observer) {
  observers_.RemoveObserver(observer);
}

BASE_FEATURE(kEnableKioskVisionInternalsPage,
             "EnableKioskVisionInternalsPage",
             base::FEATURE_DISABLED_BY_DEFAULT);

bool IsInternalsPageEnabled() {
  return base::FeatureList::IsEnabled(kEnableKioskVisionInternalsPage);
}

}  // namespace ash::kiosk_vision