blob: 2d28017d393fde4d058caa203326c4f5ccc0a478 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <dlfcn.h>
#include <fcntl.h>
#include <linux/videodev2.h>
#include <errno.h>
#include "base/posix/eintr_wrapper.h"
#include "base/trace_event/trace_event.h"
#include "media/gpu/macros.h"
#include "media/gpu/v4l2/aml_v4l2_device.h"
#include "ui/gl/gl_bindings.h"
namespace media {
namespace {
const char kEncoderDevice[] = "/dev/amvenc_avc";
}
void* libvpcodec;
typedef void* (*AmlV4L2Create)();
typedef void (*AmlV4L2Destroy)(void* context);
typedef int32_t (*AmlV4L2Open)(void* context, const char* name);
typedef int32_t (*AmlV4L2Close)(void* context, int32_t fd);
typedef int32_t (*AmlV4L2Ioctl)(void* context, int32_t fd, int cmd, ...);
typedef int32_t (*AmlV4L2Poll)(void* context,
int32_t fd,
bool poll_device,
bool* event_pending);
typedef int32_t (*AmlV4L2SetDevicePollInterrupt)(void* context, int32_t fd);
typedef int32_t (*AmlV4L2ClearDevicePollInterrupt)(void* context, int32_t fd);
typedef void* (*AmlV4L2Mmap)(void* context,
void* addr,
size_t length,
int prot,
int flags,
int fd,
unsigned int offset);
typedef int32_t (*AmlV4L2Munmap)(void* context, void* addr, size_t length);
#define AMLV4L2_SYM(name) AmlV4L2##name AmlV4L2_##name = nullptr
AMLV4L2_SYM(Create);
AMLV4L2_SYM(Destroy);
AMLV4L2_SYM(Open);
AMLV4L2_SYM(Close);
AMLV4L2_SYM(Ioctl);
AMLV4L2_SYM(Poll);
AMLV4L2_SYM(SetDevicePollInterrupt);
AMLV4L2_SYM(ClearDevicePollInterrupt);
AMLV4L2_SYM(Mmap);
AMLV4L2_SYM(Munmap);
#undef AMLV4L2_SYM
AmlV4L2Device::AmlV4L2Device() {}
AmlV4L2Device::~AmlV4L2Device() {
if (context_) {
CloseDevice();
AmlV4L2_Destroy(context_);
}
}
int AmlV4L2Device::Ioctl(int flags, void* arg) {
if (type_ != Type::kEncoder)
return GenericV4L2Device::Ioctl(flags, arg);
return HANDLE_EINTR(AmlV4L2_Ioctl(context_, device_fd_.get(),
static_cast<unsigned long>(flags), arg));
}
bool AmlV4L2Device::Poll(bool poll_device, bool* event_pending) {
if (type_ != Type::kEncoder)
return GenericV4L2Device::Poll(poll_device, event_pending);
if (HANDLE_EINTR(AmlV4L2_Poll(context_, device_fd_.get(), poll_device,
event_pending)) == -1) {
VLOGF(1) << "AmlV4L2Poll returned -1 ";
return false;
}
return true;
}
void* AmlV4L2Device::Mmap(void* addr,
unsigned int len,
int prot,
int flags,
unsigned int offset) {
if (type_ != Type::kEncoder)
return GenericV4L2Device::Mmap(addr, len, prot, flags, offset);
return AmlV4L2_Mmap(context_, addr, len, prot, flags, device_fd_.get(),
offset);
}
void AmlV4L2Device::Munmap(void* addr, unsigned int len) {
if (type_ != Type::kEncoder)
return GenericV4L2Device::Munmap(addr, len);
AmlV4L2_Munmap(context_, addr, len);
}
bool AmlV4L2Device::SetDevicePollInterrupt() {
if (type_ != Type::kEncoder)
return GenericV4L2Device::SetDevicePollInterrupt();
if (HANDLE_EINTR(
AmlV4L2_SetDevicePollInterrupt(context_, device_fd_.get())) == -1) {
VLOGF(1) << "Error in calling AmlV4L2SetDevicePollInterrupt";
return false;
}
return true;
}
bool AmlV4L2Device::ClearDevicePollInterrupt() {
if (type_ != Type::kEncoder)
return GenericV4L2Device::ClearDevicePollInterrupt();
if (HANDLE_EINTR(
AmlV4L2_ClearDevicePollInterrupt(context_, device_fd_.get())) == -1) {
VLOGF(1) << "Error in calling AmlV4L2ClearDevicePollInterrupt";
return false;
}
return true;
}
bool AmlV4L2Device::Initialize() {
if (!GenericV4L2Device::Initialize())
return false;
static bool initialized = []() {
libvpcodec = dlopen("/system/lib/libvpcodec.so",
RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE | RTLD_LAZY);
if (libvpcodec == nullptr) {
VLOGF(1) << "Failed to dlsym load libvpcodec.so";
return false;
}
#define AMLV4L2_DLSYM_OR_RETURN_ON_ERROR(lib, name) \
do { \
AmlV4L2_##name = \
reinterpret_cast<AmlV4L2##name>(dlsym(lib, "AmlV4L2_" #name)); \
if (AmlV4L2_##name == nullptr) { \
VLOGF(1) << "Failed to dlsym AmlV4L2_" #name; \
VLOGF(1) << "Failed to dlsym AmlV4L2_ == " << strerror(errno); \
VLOGF(1) << "Failed to dlerror == " << dlerror(); \
return false; \
} \
} while (0)
AMLV4L2_DLSYM_OR_RETURN_ON_ERROR(libvpcodec, Create);
AMLV4L2_DLSYM_OR_RETURN_ON_ERROR(libvpcodec, Destroy);
AMLV4L2_DLSYM_OR_RETURN_ON_ERROR(libvpcodec, Open);
AMLV4L2_DLSYM_OR_RETURN_ON_ERROR(libvpcodec, Close);
AMLV4L2_DLSYM_OR_RETURN_ON_ERROR(libvpcodec, Ioctl);
AMLV4L2_DLSYM_OR_RETURN_ON_ERROR(libvpcodec, Poll);
AMLV4L2_DLSYM_OR_RETURN_ON_ERROR(libvpcodec, SetDevicePollInterrupt);
AMLV4L2_DLSYM_OR_RETURN_ON_ERROR(libvpcodec, ClearDevicePollInterrupt);
AMLV4L2_DLSYM_OR_RETURN_ON_ERROR(libvpcodec, Mmap);
AMLV4L2_DLSYM_OR_RETURN_ON_ERROR(libvpcodec, Munmap);
#undef AMLV4L2_DLSYM_OR_RETURN_ON_ERROR
return true;
}();
if (!initialized) {
LOG(ERROR) << "Failed to load AmlV4L2 symbols";
return false;
}
context_ = AmlV4L2_Create();
if (!context_) {
LOG(ERROR) << "Fail to create context";
return false;
}
return initialized;
}
bool AmlV4L2Device::Open(Type type, uint32_t v4l2_pixfmt) {
type_ = type;
if (type != Type::kEncoder)
return GenericV4L2Device::Open(type, v4l2_pixfmt);
return OpenDevice();
}
std::vector<uint32_t> AmlV4L2Device::PreferredInputFormat(Type type) const {
if (type_ != Type::kEncoder)
return GenericV4L2Device::PreferredInputFormat(type);
return {V4L2_PIX_FMT_YUV420M};
}
VideoEncodeAccelerator::SupportedProfiles
AmlV4L2Device::GetSupportedEncodeProfiles() {
if (!OpenDevice())
return VideoEncodeAccelerator::SupportedProfiles();
type_ = Type::kEncoder;
const auto& profiles = EnumerateSupportedEncodeProfiles();
CloseDevice();
return profiles;
}
bool AmlV4L2Device::OpenDevice() {
DCHECK(!device_fd_.is_valid());
device_fd_.reset(HANDLE_EINTR(AmlV4L2_Open(context_, kEncoderDevice)));
if (!device_fd_.is_valid()) {
LOG(ERROR) << "Unable to open device " << kEncoderDevice;
return false;
}
return true;
}
void AmlV4L2Device::CloseDevice() {
// Release the fd as AMLV4L2_Close closes the fd.
if (device_fd_.is_valid()) {
AmlV4L2_Close(context_, device_fd_.release());
}
}
} // namespace media