| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 // | 4 // |
| 5 | 5 |
| 6 #include <fcntl.h> | 6 #include <fcntl.h> |
| 7 #include <libdrm/drm_fourcc.h> | 7 #include <libdrm/drm_fourcc.h> |
| 8 #include <linux/videodev2.h> | 8 #include <linux/videodev2.h> |
| 9 #include <poll.h> | 9 #include <poll.h> |
| 10 #include <sys/eventfd.h> | 10 #include <sys/eventfd.h> |
| 11 #include <sys/ioctl.h> | 11 #include <sys/ioctl.h> |
| 12 #include <sys/mman.h> | 12 #include <sys/mman.h> |
| 13 | 13 |
| 14 #include "base/debug/trace_event.h" | 14 #include "base/debug/trace_event.h" |
| 15 #include "base/files/scoped_file.h" | 15 #include "base/files/scoped_file.h" |
| 16 #include "base/posix/eintr_wrapper.h" | 16 #include "base/posix/eintr_wrapper.h" |
| 17 #include "content/common/gpu/media/generic_v4l2_video_device.h" | 17 #include "content/common/gpu/media/generic_v4l2_video_device.h" |
| 18 #include "ui/gl/egl_util.h" | |
| 19 #include "ui/gl/gl_bindings.h" | 18 #include "ui/gl/gl_bindings.h" |
| 20 | 19 |
| 21 namespace content { | 20 namespace content { |
| 22 | 21 |
| 23 namespace { | 22 namespace { |
| 24 const char kDecoderDevice[] = "/dev/video-dec"; | 23 const char kDecoderDevice[] = "/dev/video-dec"; |
| 25 const char kEncoderDevice[] = "/dev/video-enc"; | 24 const char kEncoderDevice[] = "/dev/video-enc"; |
| 26 const char kImageProcessorDevice[] = "/dev/gsc1"; | 25 const char kImageProcessorDevice[] = "/dev/gsc1"; |
| 27 } | 26 } |
| 28 | 27 |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 131 return false; | 130 return false; |
| 132 } | 131 } |
| 133 | 132 |
| 134 device_poll_interrupt_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); | 133 device_poll_interrupt_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); |
| 135 if (device_poll_interrupt_fd_ == -1) { | 134 if (device_poll_interrupt_fd_ == -1) { |
| 136 return false; | 135 return false; |
| 137 } | 136 } |
| 138 return true; | 137 return true; |
| 139 } | 138 } |
| 140 | 139 |
| 141 bool GenericV4L2Device::CanCreateEGLImageFrom(uint32_t v4l2_pixfmt) { | 140 EGLImageKHR GenericV4L2Device::CreateEGLImage(EGLDisplay egl_display, |
| 142 static uint32_t kEGLImageDrmFmtsSupported[] = { | 141 EGLContext /* egl_context */, |
| 143 DRM_FORMAT_ARGB8888, | 142 GLuint texture_id, |
| 144 #if defined(ARCH_CPU_ARMEL) | 143 gfx::Size frame_buffer_size, |
| 145 DRM_FORMAT_NV12, | 144 unsigned int buffer_index, |
| 146 #endif | 145 size_t planes_count) { |
| 147 }; | 146 DVLOG(3) << "CreateEGLImage()"; |
| 148 | 147 |
| 149 return std::find( | 148 scoped_ptr<base::ScopedFD[]> dmabuf_fds(new base::ScopedFD[planes_count]); |
| 150 kEGLImageDrmFmtsSupported, | 149 for (size_t i = 0; i < planes_count; ++i) { |
| 151 kEGLImageDrmFmtsSupported + arraysize(kEGLImageDrmFmtsSupported), | 150 // Export the DMABUF fd so we can export it as a texture. |
| 152 V4L2PixFmtToDrmFormat(v4l2_pixfmt)) != | |
| 153 kEGLImageDrmFmtsSupported + arraysize(kEGLImageDrmFmtsSupported); | |
| 154 } | |
| 155 | |
| 156 EGLImageKHR GenericV4L2Device::CreateEGLImage(EGLDisplay egl_display, | |
| 157 EGLContext /* egl_context */, | |
| 158 GLuint texture_id, | |
| 159 gfx::Size frame_buffer_size, | |
| 160 unsigned int buffer_index, | |
| 161 uint32_t v4l2_pixfmt, | |
| 162 size_t num_v4l2_planes) { | |
| 163 DVLOG(3) << "CreateEGLImage()"; | |
| 164 if (!CanCreateEGLImageFrom(v4l2_pixfmt)) { | |
| 165 LOG(ERROR) << "Unsupported V4L2 pixel format"; | |
| 166 return EGL_NO_IMAGE_KHR; | |
| 167 } | |
| 168 | |
| 169 media::VideoFrame::Format vf_format = | |
| 170 V4L2PixFmtToVideoFrameFormat(v4l2_pixfmt); | |
| 171 // Number of components, as opposed to the number of V4L2 planes, which is | |
| 172 // just a buffer count. | |
| 173 size_t num_planes = media::VideoFrame::NumPlanes(vf_format); | |
| 174 DCHECK_LE(num_planes, 3u); | |
| 175 if (num_planes < num_v4l2_planes) { | |
| 176 // It's possible for more than one DRM plane to reside in one V4L2 plane, | |
| 177 // but not the other way around. We must use all V4L2 planes. | |
| 178 LOG(ERROR) << "Invalid plane count"; | |
| 179 return EGL_NO_IMAGE_KHR; | |
| 180 } | |
| 181 | |
| 182 scoped_ptr<base::ScopedFD[]> dmabuf_fds(new base::ScopedFD[num_v4l2_planes]); | |
| 183 // Export dmabuf fds so we can create an EGLImage from them. | |
| 184 for (size_t i = 0; i < num_v4l2_planes; ++i) { | |
| 185 struct v4l2_exportbuffer expbuf; | 151 struct v4l2_exportbuffer expbuf; |
| 186 memset(&expbuf, 0, sizeof(expbuf)); | 152 memset(&expbuf, 0, sizeof(expbuf)); |
| 187 expbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | 153 expbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
| 188 expbuf.index = buffer_index; | 154 expbuf.index = buffer_index; |
| 189 expbuf.plane = i; | 155 expbuf.plane = i; |
| 190 expbuf.flags = O_CLOEXEC; | 156 expbuf.flags = O_CLOEXEC; |
| 191 if (Ioctl(VIDIOC_EXPBUF, &expbuf) != 0) { | 157 if (Ioctl(VIDIOC_EXPBUF, &expbuf) != 0) { |
| 192 return EGL_NO_IMAGE_KHR; | 158 return EGL_NO_IMAGE_KHR; |
| 193 } | 159 } |
| 194 dmabuf_fds[i].reset(expbuf.fd); | 160 dmabuf_fds[i].reset(expbuf.fd); |
| 195 } | 161 } |
| 196 | 162 DCHECK_EQ(planes_count, 2u); |
| 197 std::vector<EGLint> attrs; | 163 EGLint attrs[] = { |
| 198 attrs.push_back(EGL_WIDTH); | 164 EGL_WIDTH, 0, EGL_HEIGHT, 0, |
| 199 attrs.push_back(frame_buffer_size.width()); | 165 EGL_LINUX_DRM_FOURCC_EXT, 0, EGL_DMA_BUF_PLANE0_FD_EXT, 0, |
| 200 attrs.push_back(EGL_HEIGHT); | 166 EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0, EGL_DMA_BUF_PLANE0_PITCH_EXT, 0, |
| 201 attrs.push_back(frame_buffer_size.height()); | 167 EGL_DMA_BUF_PLANE1_FD_EXT, 0, EGL_DMA_BUF_PLANE1_OFFSET_EXT, 0, |
| 202 attrs.push_back(EGL_LINUX_DRM_FOURCC_EXT); | 168 EGL_DMA_BUF_PLANE1_PITCH_EXT, 0, EGL_NONE, }; |
| 203 attrs.push_back(V4L2PixFmtToDrmFormat(v4l2_pixfmt)); | 169 attrs[1] = frame_buffer_size.width(); |
| 204 | 170 attrs[3] = frame_buffer_size.height(); |
| 205 // For existing formats, if we have less buffers (V4L2 planes) than | 171 attrs[5] = DRM_FORMAT_NV12; |
| 206 // components (planes), the remaining planes are stored in the last | 172 attrs[7] = dmabuf_fds[0].get(); |
| 207 // V4L2 plane. Use one V4L2 plane per each component until we run out of V4L2 | 173 attrs[9] = 0; |
| 208 // planes, and use the last V4L2 plane for all remaining components, each | 174 attrs[11] = frame_buffer_size.width(); |
| 209 // with an offset equal to the size of the preceding planes in the same | 175 attrs[13] = dmabuf_fds[1].get(); |
| 210 // V4L2 plane. | 176 attrs[15] = 0; |
| 211 size_t v4l2_plane = 0; | 177 attrs[17] = frame_buffer_size.width(); |
| 212 size_t plane_offset = 0; | |
| 213 for (size_t plane = 0; plane < num_planes; ++plane) { | |
| 214 attrs.push_back(EGL_DMA_BUF_PLANE0_FD_EXT + plane * 3); | |
| 215 attrs.push_back(dmabuf_fds[v4l2_plane].get()); | |
| 216 attrs.push_back(EGL_DMA_BUF_PLANE0_OFFSET_EXT + plane * 3); | |
| 217 attrs.push_back(plane_offset); | |
| 218 attrs.push_back(EGL_DMA_BUF_PLANE0_PITCH_EXT + plane * 3); | |
| 219 attrs.push_back(media::VideoFrame::RowBytes(plane, vf_format, | |
| 220 frame_buffer_size.width())); | |
| 221 | |
| 222 if (v4l2_plane + 1 < num_v4l2_planes) { | |
| 223 ++v4l2_plane; | |
| 224 } else { | |
| 225 plane_offset += media::VideoFrame::PlaneAllocationSize( | |
| 226 vf_format, plane, frame_buffer_size); | |
| 227 } | |
| 228 } | |
| 229 | |
| 230 attrs.push_back(EGL_NONE); | |
| 231 | 178 |
| 232 EGLImageKHR egl_image = eglCreateImageKHR( | 179 EGLImageKHR egl_image = eglCreateImageKHR( |
| 233 egl_display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, &attrs[0]); | 180 egl_display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, attrs); |
| 234 if (egl_image == EGL_NO_IMAGE_KHR) { | 181 if (egl_image == EGL_NO_IMAGE_KHR) { |
| 235 LOG(ERROR) << "Failed creating EGL image: " << ui::GetLastEGLErrorString(); | |
| 236 return egl_image; | 182 return egl_image; |
| 237 } | 183 } |
| 238 glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id); | 184 glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id); |
| 239 glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, egl_image); | 185 glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, egl_image); |
| 240 | 186 |
| 241 return egl_image; | 187 return egl_image; |
| 242 } | 188 } |
| 243 | 189 |
| 244 EGLBoolean GenericV4L2Device::DestroyEGLImage(EGLDisplay egl_display, | 190 EGLBoolean GenericV4L2Device::DestroyEGLImage(EGLDisplay egl_display, |
| 245 EGLImageKHR egl_image) { | 191 EGLImageKHR egl_image) { |
| 246 return eglDestroyImageKHR(egl_display, egl_image); | 192 return eglDestroyImageKHR(egl_display, egl_image); |
| 247 } | 193 } |
| 248 | 194 |
| 249 GLenum GenericV4L2Device::GetTextureTarget() { return GL_TEXTURE_EXTERNAL_OES; } | 195 GLenum GenericV4L2Device::GetTextureTarget() { return GL_TEXTURE_EXTERNAL_OES; } |
| 250 | 196 |
| 251 uint32 GenericV4L2Device::PreferredInputFormat() { | 197 uint32 GenericV4L2Device::PreferredInputFormat() { |
| 252 // TODO(posciak): We should support "dontcare" returns here once we | 198 // TODO(posciak): We should support "dontcare" returns here once we |
| 253 // implement proper handling (fallback, negotiation) for this in users. | 199 // implement proper handling (fallback, negotiation) for this in users. |
| 254 CHECK_EQ(type_, kEncoder); | 200 CHECK_EQ(type_, kEncoder); |
| 255 return V4L2_PIX_FMT_NV12M; | 201 return V4L2_PIX_FMT_NV12M; |
| 256 } | 202 } |
| 257 | 203 |
| 204 uint32 GenericV4L2Device::PreferredOutputFormat() { |
| 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_, kDecoder); |
| 208 return V4L2_PIX_FMT_NV12M; |
| 209 } |
| 210 |
| 258 } // namespace content | 211 } // namespace content |
| OLD | NEW |