chromium/third_party/mediapipe/src/mediapipe/framework/formats/hardware_buffer_android.cc

// Copyright 2019 The MediaPipe Authors.
//
// 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.

#if (!defined(MEDIAPIPE_NO_JNI) ||                     \
     defined(MEDIAPIPE_ANDROID_LINK_NATIVE_WINDOW)) && \
    (__ANDROID_API__ >= 26 ||                          \
     defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__))

#include <android/hardware_buffer.h>

#include <cstdint>
#include <memory>
#include <optional>

#include "absl/log/absl_check.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "mediapipe/framework/formats/hardware_buffer.h"
#include "mediapipe/framework/port/ret_check.h"

namespace mediapipe {

HardwareBuffer::HardwareBuffer(HardwareBuffer&& other) {
  spec_ = std::exchange(other.spec_, {});
  ahw_buffer_ = std::exchange(other.ahw_buffer_, nullptr);
  is_locked_ = std::exchange(other.is_locked_, false);
}

HardwareBuffer::HardwareBuffer(const HardwareBufferSpec& spec,
                               AHardwareBuffer* ahwb)
    : spec_(spec), ahw_buffer_(ahwb), is_locked_(false) {}

HardwareBuffer::~HardwareBuffer() { Reset(); }

absl::StatusOr<HardwareBuffer> HardwareBuffer::Create(
    const HardwareBufferSpec& spec) {
  MP_ASSIGN_OR_RETURN(AHardwareBuffer * ahwb, AllocateAHardwareBuffer(spec));
  return HardwareBuffer(spec, ahwb);
}

absl::StatusOr<HardwareBuffer> HardwareBuffer::WrapAndAcquireAHardwareBuffer(
    AHardwareBuffer* ahw_buffer) {
  MP_ASSIGN_OR_RETURN(HardwareBufferSpec spec,
                      AcquireAHardwareBuffer(ahw_buffer));
  return HardwareBuffer(spec, ahw_buffer);
}

bool HardwareBuffer::IsSupported() {
  if (__builtin_available(android 26, *)) {
    return true;
  }
  return false;
}

absl::StatusOr<AHardwareBuffer*> HardwareBuffer::AllocateAHardwareBuffer(
    const HardwareBufferSpec& spec) {
  RET_CHECK(IsSupported()) << "AndroidHWBuffers not supported";

  AHardwareBuffer* output = nullptr;
  int error = 0;
  if (__builtin_available(android 26, *)) {
    AHardwareBuffer_Desc desc = {
        .width = spec.width,
        .height = spec.height,
        .layers = spec.layers,
        .format = spec.format,
        .usage = spec.usage,
    };
    error = AHardwareBuffer_allocate(&desc, &output);
  } else {
    return absl::UnavailableError(
        "NDK's hardware buffer support requires Android API level >= 26");
  }
  RET_CHECK(!error && output != nullptr)
      << "AHardwareBuffer_allocate failed: " << error;
  return output;
}

absl::StatusOr<HardwareBufferSpec> HardwareBuffer::AcquireAHardwareBuffer(
    AHardwareBuffer* ahw_buffer) {
  HardwareBufferSpec spec;
  if (__builtin_available(android 26, *)) {
    AHardwareBuffer_Desc desc = {};
    AHardwareBuffer_describe(ahw_buffer, &desc);
    RET_CHECK_GT(desc.stride, 0) << "AHardwareBuffer_acquire failed";
    spec = {.width = desc.width,
            .height = desc.height,
            .layers = desc.layers,
            .format = desc.format,
            .usage = desc.usage,
            .stride = desc.stride};
    AHardwareBuffer_acquire(ahw_buffer);
  } else {
    return absl::UnavailableError(
        "NDK's hardware buffer support requires Android API level >= 26");
  }
  return spec;
}

absl::Status HardwareBuffer::ReleaseAHardwareBuffer() {
  if (ahw_buffer_ == nullptr) {
    return absl::OkStatus();
  }
  if (is_locked_) {
    MP_RETURN_IF_ERROR(Unlock());
  }
  if (__builtin_available(android 26, *)) {
    AHardwareBuffer_release(ahw_buffer_);
  } else {
    return absl::UnavailableError(
        "NDK's hardware buffer support requires Android API level >= 26");
  }
  spec_ = {};
  ahw_buffer_ = nullptr;
  return absl::OkStatus();
}

absl::StatusOr<void*> HardwareBuffer::Lock(
    uint64_t usage, std::optional<int> fence_file_descriptor) {
  RET_CHECK(ahw_buffer_ != nullptr) << "Hardware Buffer not allocated";
  RET_CHECK(!is_locked_) << "Hardware Buffer already locked";
  void* mem = nullptr;
  if (__builtin_available(android 26, *)) {
    const int error = AHardwareBuffer_lock(
        ahw_buffer_, usage,
        fence_file_descriptor.has_value() ? *fence_file_descriptor : -1,
        nullptr, &mem);
    RET_CHECK(error == 0) << "Hardware Buffer lock failed. Error: " << error;
  } else {
    return absl::UnavailableError(
        "NDK's hardware buffer support requires Android API level >= 26");
  }
  is_locked_ = true;
  return mem;
}

absl::Status HardwareBuffer::Unlock() {
  return UnlockInternal(/*fence_file_descriptor=*/nullptr);
}

absl::StatusOr<int> HardwareBuffer::UnlockAsync() {
  int fence_file_descriptor = -1;
  MP_RETURN_IF_ERROR(UnlockInternal(&fence_file_descriptor));
  return fence_file_descriptor;
}

absl::Status HardwareBuffer::UnlockInternal(int* fence_file_descriptor) {
  RET_CHECK(ahw_buffer_ != nullptr) << "Hardware Buffer not allocated";
  if (!is_locked_) {
    return absl::OkStatus();
  }
  if (__builtin_available(android 26, *)) {
    const int error =
        AHardwareBuffer_unlock(ahw_buffer_, fence_file_descriptor);
    RET_CHECK(error == 0) << "Hardware Buffer unlock failed. error: " << error;
  } else {
    return absl::UnavailableError(
        "NDK's hardware buffer support requires Android API level >= 26");
  }
  is_locked_ = false;
  return absl::OkStatus();
}

absl::StatusOr<uint32_t> HardwareBuffer::GetAlignedWidth() const {
  if (__builtin_available(android 26, *)) {
    RET_CHECK(ahw_buffer_ != nullptr) << "Hardware Buffer not allocated";
    AHardwareBuffer_Desc desc = {};
    AHardwareBuffer_describe(ahw_buffer_, &desc);
    RET_CHECK_GT(desc.stride, 0)
        << "AHardwareBuffer_describe failed, stride should not be zero";
    return desc.stride;
  }
  return absl::UnavailableError(
      "NDK's hardware buffer support requires Android API level >= 26");
}

void HardwareBuffer::Reset() {
  const auto success = ReleaseAHardwareBuffer();
  if (!success.ok()) {
    ABSL_LOG(DFATAL) << "Failed to release AHardwareBuffer: " << success;
  }
}

}  // namespace mediapipe

#endif  // (!defined(MEDIAPIPE_NO_JNI) ||
        // defined(MEDIAPIPE_ANDROID_LINK_NATIVE_WINDOW)) && (__ANDROID_API__>=
        // 26 || defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__))