Home | History | Annotate | Download | only in media
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <dlfcn.h>
      6 #include <fcntl.h>
      7 #include <linux/videodev2.h>
      8 
      9 #include "base/debug/trace_event.h"
     10 #include "base/lazy_instance.h"
     11 #include "base/posix/eintr_wrapper.h"
     12 #include "content/common/gpu/media/tegra_v4l2_video_device.h"
     13 #include "ui/gl/gl_bindings.h"
     14 
     15 namespace content {
     16 
     17 namespace {
     18 const char kDecoderDevice[] = "/dev/tegra_avpchannel";
     19 const char kEncoderDevice[] = "/dev/nvhost-msenc";
     20 }
     21 
     22 typedef int32 (*TegraV4L2Open)(const char* name, int32 flags);
     23 typedef int32 (*TegraV4L2Close)(int32 fd);
     24 typedef int32 (*TegraV4L2Ioctl)(int32 fd, unsigned long cmd, ...);
     25 typedef int32 (*TegraV4L2Poll)(int32 fd, bool poll_device, bool* event_pending);
     26 typedef int32 (*TegraV4L2SetDevicePollInterrupt)(int32 fd);
     27 typedef int32 (*TegraV4L2ClearDevicePollInterrupt)(int32 fd);
     28 typedef void* (*TegraV4L2Mmap)(void* addr,
     29                                size_t length,
     30                                int prot,
     31                                int flags,
     32                                int fd,
     33                                unsigned int offset);
     34 typedef int32 (*TegraV4L2Munmap)(void* addr, size_t length);
     35 typedef int32 (*TegraV4L2UseEglImage)(int fd,
     36                                       unsigned int buffer_index,
     37                                       void* egl_image);
     38 
     39 #define TEGRAV4L2_SYM(name) TegraV4L2##name TegraV4L2_##name = NULL
     40 
     41 TEGRAV4L2_SYM(Open);
     42 TEGRAV4L2_SYM(Close);
     43 TEGRAV4L2_SYM(Ioctl);
     44 TEGRAV4L2_SYM(Poll);
     45 TEGRAV4L2_SYM(SetDevicePollInterrupt);
     46 TEGRAV4L2_SYM(ClearDevicePollInterrupt);
     47 TEGRAV4L2_SYM(Mmap);
     48 TEGRAV4L2_SYM(Munmap);
     49 TEGRAV4L2_SYM(UseEglImage);
     50 
     51 #undef TEGRAV4L2_SYM
     52 
     53 class TegraFunctionSymbolFinder {
     54  public:
     55   TegraFunctionSymbolFinder() : initialized_(false) {
     56     if (!dlopen("/usr/lib/libtegrav4l2.so",
     57                 RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE)) {
     58       DLOG(ERROR) << "Failed to load libtegrav4l2.so ";
     59       return;
     60     }
     61 #define TEGRAV4L2_DLSYM_OR_RETURN_ON_ERROR(name)          \
     62   do {                                                    \
     63     TegraV4L2_##name = reinterpret_cast<TegraV4L2##name>( \
     64         dlsym(RTLD_DEFAULT, "TegraV4L2_" #name));         \
     65     if (TegraV4L2_##name == NULL) {                       \
     66       LOG(ERROR) << "Failed to dlsym TegraV4L2_" #name;   \
     67       return;                                             \
     68     }                                                     \
     69   } while (0)
     70 
     71     TEGRAV4L2_DLSYM_OR_RETURN_ON_ERROR(Open);
     72     TEGRAV4L2_DLSYM_OR_RETURN_ON_ERROR(Close);
     73     TEGRAV4L2_DLSYM_OR_RETURN_ON_ERROR(Ioctl);
     74     TEGRAV4L2_DLSYM_OR_RETURN_ON_ERROR(Poll);
     75     TEGRAV4L2_DLSYM_OR_RETURN_ON_ERROR(SetDevicePollInterrupt);
     76     TEGRAV4L2_DLSYM_OR_RETURN_ON_ERROR(ClearDevicePollInterrupt);
     77     TEGRAV4L2_DLSYM_OR_RETURN_ON_ERROR(Mmap);
     78     TEGRAV4L2_DLSYM_OR_RETURN_ON_ERROR(Munmap);
     79     TEGRAV4L2_DLSYM_OR_RETURN_ON_ERROR(UseEglImage);
     80 #undef TEGRAV4L2_DLSYM
     81     initialized_ = true;
     82   }
     83 
     84   bool initialized() { return initialized_; }
     85 
     86  private:
     87   bool initialized_;
     88 };
     89 
     90 base::LazyInstance<TegraFunctionSymbolFinder> g_tegra_function_symbol_finder_ =
     91     LAZY_INSTANCE_INITIALIZER;
     92 
     93 TegraV4L2Device::TegraV4L2Device(Type type)
     94     : type_(type),
     95       device_fd_(-1) {
     96 }
     97 
     98 TegraV4L2Device::~TegraV4L2Device() {
     99   if (device_fd_ != -1) {
    100     TegraV4L2_Close(device_fd_);
    101     device_fd_ = -1;
    102   }
    103 }
    104 
    105 int TegraV4L2Device::Ioctl(int flags, void* arg) {
    106   return HANDLE_EINTR(TegraV4L2_Ioctl(device_fd_, flags, arg));
    107 }
    108 
    109 bool TegraV4L2Device::Poll(bool poll_device, bool* event_pending) {
    110   if (HANDLE_EINTR(TegraV4L2_Poll(device_fd_, poll_device, event_pending)) ==
    111       -1) {
    112     DLOG(ERROR) << "TegraV4L2Poll returned -1 ";
    113     return false;
    114   }
    115   return true;
    116 }
    117 
    118 void* TegraV4L2Device::Mmap(void* addr,
    119                             unsigned int len,
    120                             int prot,
    121                             int flags,
    122                             unsigned int offset) {
    123   return TegraV4L2_Mmap(addr, len, prot, flags, device_fd_, offset);
    124 }
    125 
    126 void TegraV4L2Device::Munmap(void* addr, unsigned int len) {
    127   TegraV4L2_Munmap(addr, len);
    128 }
    129 
    130 bool TegraV4L2Device::SetDevicePollInterrupt() {
    131   if (HANDLE_EINTR(TegraV4L2_SetDevicePollInterrupt(device_fd_)) == -1) {
    132     DLOG(ERROR) << "Error in calling TegraV4L2SetDevicePollInterrupt";
    133     return false;
    134   }
    135   return true;
    136 }
    137 
    138 bool TegraV4L2Device::ClearDevicePollInterrupt() {
    139   if (HANDLE_EINTR(TegraV4L2_ClearDevicePollInterrupt(device_fd_)) == -1) {
    140     DLOG(ERROR) << "Error in calling TegraV4L2ClearDevicePollInterrupt";
    141     return false;
    142   }
    143   return true;
    144 }
    145 
    146 bool TegraV4L2Device::Initialize() {
    147   const char* device_path = NULL;
    148   switch (type_) {
    149     case kDecoder:
    150       device_path = kDecoderDevice;
    151       break;
    152     case kEncoder:
    153       device_path = kEncoderDevice;
    154       break;
    155     case kImageProcessor:
    156       DVLOG(1) << "Device type " << type_ << " not supported on this platform";
    157       return false;
    158   }
    159 
    160   if (!g_tegra_function_symbol_finder_.Get().initialized()) {
    161     DLOG(ERROR) << "Unable to initialize functions ";
    162     return false;
    163   }
    164   device_fd_ = HANDLE_EINTR(
    165       TegraV4L2_Open(device_path, O_RDWR | O_NONBLOCK | O_CLOEXEC));
    166   if (device_fd_ == -1) {
    167     DLOG(ERROR) << "Unable to open device " << device_path;
    168     return false;
    169   }
    170   return true;
    171 }
    172 
    173 EGLImageKHR TegraV4L2Device::CreateEGLImage(EGLDisplay egl_display,
    174                                             EGLContext egl_context,
    175                                             GLuint texture_id,
    176                                             gfx::Size /* frame_buffer_size */,
    177                                             unsigned int buffer_index,
    178                                             size_t /* planes_count */) {
    179   DVLOG(3) << "CreateEGLImage()";
    180   EGLint attr = EGL_NONE;
    181   EGLImageKHR egl_image =
    182       eglCreateImageKHR(egl_display,
    183                         egl_context,
    184                         EGL_GL_TEXTURE_2D_KHR,
    185                         reinterpret_cast<EGLClientBuffer>(texture_id),
    186                         &attr);
    187   if (egl_image == EGL_NO_IMAGE_KHR) {
    188     return egl_image;
    189   }
    190   if (TegraV4L2_UseEglImage(device_fd_, buffer_index, egl_image) != 0) {
    191     eglDestroyImageKHR(egl_display, egl_image);
    192     egl_image = EGL_NO_IMAGE_KHR;
    193   }
    194   return egl_image;
    195 }
    196 
    197 EGLBoolean TegraV4L2Device::DestroyEGLImage(EGLDisplay egl_display,
    198                                             EGLImageKHR egl_image) {
    199   return eglDestroyImageKHR(egl_display, egl_image);
    200 }
    201 
    202 GLenum TegraV4L2Device::GetTextureTarget() { return GL_TEXTURE_2D; }
    203 
    204 uint32 TegraV4L2Device::PreferredInputFormat() {
    205   // TODO(posciak): We should support "dontcare" returns here once we
    206   // implement proper handling (fallback, negotiation) for this in users.
    207   CHECK_EQ(type_, kEncoder);
    208   return V4L2_PIX_FMT_YUV420M;
    209 }
    210 
    211 uint32 TegraV4L2Device::PreferredOutputFormat() {
    212   // TODO(posciak): We should support "dontcare" returns here once we
    213   // implement proper handling (fallback, negotiation) for this in users.
    214   CHECK_EQ(type_, kDecoder);
    215   return V4L2_PIX_FMT_NV12M;
    216 }
    217 
    218 }  //  namespace content
    219