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