chromium/services/shape_detection/face_detection_provider_win.cc

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

#include "services/shape_detection/face_detection_provider_win.h"

#include <windows.media.faceanalysis.h>

#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/scoped_generic.h"
#include "base/win/core_winrt_util.h"
#include "base/win/post_async_results.h"
#include "base/win/scoped_hstring.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"

namespace shape_detection {

namespace {

using ABI::Windows::Foundation::IAsyncOperation;
using ABI::Windows::Graphics::Imaging::BitmapPixelFormat;
using ABI::Windows::Graphics::Imaging::ISoftwareBitmapStatics;
using ABI::Windows::Media::FaceAnalysis::FaceDetector;
using ABI::Windows::Media::FaceAnalysis::IFaceDetector;
using ABI::Windows::Media::FaceAnalysis::IFaceDetectorStatics;

using base::win::GetActivationFactory;
using base::win::ScopedHString;
using Microsoft::WRL::ComPtr;

BitmapPixelFormat GetPreferredPixelFormat(IFaceDetectorStatics* factory) {
  static constexpr BitmapPixelFormat kFormats[] = {
      ABI::Windows::Graphics::Imaging::BitmapPixelFormat_Gray8,
      ABI::Windows::Graphics::Imaging::BitmapPixelFormat_Nv12};

  for (const auto& format : kFormats) {
    boolean is_supported = false;
    factory->IsBitmapPixelFormatSupported(format, &is_supported);
    if (is_supported)
      return format;
  }
  return ABI::Windows::Graphics::Imaging::BitmapPixelFormat_Unknown;
}

}  // namespace

void FaceDetectionProviderWin::CreateFaceDetection(
    mojo::PendingReceiver<shape_detection::mojom::FaceDetection> receiver,
    shape_detection::mojom::FaceDetectorOptionsPtr options) {
  ComPtr<IFaceDetectorStatics> factory;
  HRESULT hr = GetActivationFactory<
      IFaceDetectorStatics,
      RuntimeClass_Windows_Media_FaceAnalysis_FaceDetector>(&factory);
  if (FAILED(hr)) {
    DLOG(ERROR) << "IFaceDetectorStatics factory failed: "
                << logging::SystemErrorCodeToString(hr);
    return;
  }

  boolean is_supported = false;
  factory->get_IsSupported(&is_supported);
  if (!is_supported)
    return;

  // In the current version, the FaceDetector class only supports images in
  // Gray8 or Nv12. Gray8 should be a good type but verify it against
  // FaceDetector’s supported formats.
  BitmapPixelFormat pixel_format = GetPreferredPixelFormat(factory.Get());
  if (pixel_format ==
      ABI::Windows::Graphics::Imaging::BitmapPixelFormat_Unknown) {
    return;
  }

  // Create an instance of FaceDetector asynchronously.
  ComPtr<IAsyncOperation<FaceDetector*>> async_op;
  hr = factory->CreateAsync(&async_op);
  if (FAILED(hr)) {
    DLOG(ERROR) << "Create FaceDetector failed: "
                << logging::SystemErrorCodeToString(hr);
    return;
  }

  // Use WeakPtr to bind the callback so that the once callback will not be run
  // if this object has been already destroyed.
  hr = base::win::PostAsyncResults(
      std::move(async_op),
      base::BindOnce(&FaceDetectionProviderWin::OnFaceDetectorCreated,
                     weak_factory_.GetWeakPtr(), std::move(receiver),
                     pixel_format));
  if (FAILED(hr)) {
    DLOG(ERROR) << "Begin async operation failed: "
                << logging::SystemErrorCodeToString(hr);
    return;
  }

  // When |provider| goes out of scope it will immediately close its end of
  // the message pipe, then the callback OnFaceDetectorCreated will be not
  // called. This prevents this object from being destroyed before the
  // AsyncOperation completes.
  receiver_->PauseIncomingMethodCallProcessing();
}

FaceDetectionProviderWin::FaceDetectionProviderWin() {}

FaceDetectionProviderWin::~FaceDetectionProviderWin() = default;

void FaceDetectionProviderWin::OnFaceDetectorCreated(
    mojo::PendingReceiver<shape_detection::mojom::FaceDetection> receiver,
    BitmapPixelFormat pixel_format,
    ComPtr<IFaceDetector> face_detector) {
  receiver_->ResumeIncomingMethodCallProcessing();

  if (!face_detector)
    return;

  ComPtr<ISoftwareBitmapStatics> bitmap_factory;
  const HRESULT hr = GetActivationFactory<
      ISoftwareBitmapStatics,
      RuntimeClass_Windows_Graphics_Imaging_SoftwareBitmap>(&bitmap_factory);
  if (FAILED(hr)) {
    DLOG(ERROR) << "ISoftwareBitmapStatics factory failed: "
                << logging::SystemErrorCodeToString(hr);
    return;
  }

  auto impl = std::make_unique<FaceDetectionImplWin>(
      std::move(face_detector), std::move(bitmap_factory), pixel_format);
  auto* impl_ptr = impl.get();
  impl_ptr->SetReceiver(
      mojo::MakeSelfOwnedReceiver(std::move(impl), std::move(receiver)));
}

}  // namespace shape_detection