OLD | NEW |
| (Empty) |
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 | |
6 #include <fcntl.h> | |
7 #include <libdrm/drm_fourcc.h> | |
8 #include <linux/videodev2.h> | |
9 #include <poll.h> | |
10 #include <sys/eventfd.h> | |
11 #include <sys/ioctl.h> | |
12 #include <sys/mman.h> | |
13 | |
14 #include "base/debug/trace_event.h" | |
15 #include "base/files/scoped_file.h" | |
16 #include "base/posix/eintr_wrapper.h" | |
17 #include "content/common/gpu/media/exynos_v4l2_video_device.h" | |
18 #include "ui/gl/gl_bindings.h" | |
19 | |
20 namespace content { | |
21 | |
22 namespace { | |
23 const char kDecoderDevice[] = "/dev/mfc-dec"; | |
24 const char kEncoderDevice[] = "/dev/mfc-enc"; | |
25 const char kImageProcessorDevice[] = "/dev/gsc1"; | |
26 } | |
27 | |
28 ExynosV4L2Device::ExynosV4L2Device(Type type) | |
29 : type_(type), | |
30 device_fd_(-1), | |
31 device_poll_interrupt_fd_(-1) {} | |
32 | |
33 ExynosV4L2Device::~ExynosV4L2Device() { | |
34 if (device_poll_interrupt_fd_ != -1) { | |
35 close(device_poll_interrupt_fd_); | |
36 device_poll_interrupt_fd_ = -1; | |
37 } | |
38 if (device_fd_ != -1) { | |
39 close(device_fd_); | |
40 device_fd_ = -1; | |
41 } | |
42 } | |
43 | |
44 int ExynosV4L2Device::Ioctl(int request, void* arg) { | |
45 return HANDLE_EINTR(ioctl(device_fd_, request, arg)); | |
46 } | |
47 | |
48 bool ExynosV4L2Device::Poll(bool poll_device, bool* event_pending) { | |
49 struct pollfd pollfds[2]; | |
50 nfds_t nfds; | |
51 int pollfd = -1; | |
52 | |
53 pollfds[0].fd = device_poll_interrupt_fd_; | |
54 pollfds[0].events = POLLIN | POLLERR; | |
55 nfds = 1; | |
56 | |
57 if (poll_device) { | |
58 DVLOG(3) << "Poll(): adding device fd to poll() set"; | |
59 pollfds[nfds].fd = device_fd_; | |
60 pollfds[nfds].events = POLLIN | POLLOUT | POLLERR | POLLPRI; | |
61 pollfd = nfds; | |
62 nfds++; | |
63 } | |
64 | |
65 if (HANDLE_EINTR(poll(pollfds, nfds, -1)) == -1) { | |
66 DPLOG(ERROR) << "poll() failed"; | |
67 return false; | |
68 } | |
69 *event_pending = (pollfd != -1 && pollfds[pollfd].revents & POLLPRI); | |
70 return true; | |
71 } | |
72 | |
73 void* ExynosV4L2Device::Mmap(void* addr, | |
74 unsigned int len, | |
75 int prot, | |
76 int flags, | |
77 unsigned int offset) { | |
78 return mmap(addr, len, prot, flags, device_fd_, offset); | |
79 } | |
80 | |
81 void ExynosV4L2Device::Munmap(void* addr, unsigned int len) { | |
82 munmap(addr, len); | |
83 } | |
84 | |
85 bool ExynosV4L2Device::SetDevicePollInterrupt() { | |
86 DVLOG(3) << "SetDevicePollInterrupt()"; | |
87 | |
88 const uint64 buf = 1; | |
89 if (HANDLE_EINTR(write(device_poll_interrupt_fd_, &buf, sizeof(buf))) == -1) { | |
90 DPLOG(ERROR) << "SetDevicePollInterrupt(): write() failed"; | |
91 return false; | |
92 } | |
93 return true; | |
94 } | |
95 | |
96 bool ExynosV4L2Device::ClearDevicePollInterrupt() { | |
97 DVLOG(3) << "ClearDevicePollInterrupt()"; | |
98 | |
99 uint64 buf; | |
100 if (HANDLE_EINTR(read(device_poll_interrupt_fd_, &buf, sizeof(buf))) == -1) { | |
101 if (errno == EAGAIN) { | |
102 // No interrupt flag set, and we're reading nonblocking. Not an error. | |
103 return true; | |
104 } else { | |
105 DPLOG(ERROR) << "ClearDevicePollInterrupt(): read() failed"; | |
106 return false; | |
107 } | |
108 } | |
109 return true; | |
110 } | |
111 | |
112 bool ExynosV4L2Device::Initialize() { | |
113 const char* device_path = NULL; | |
114 switch (type_) { | |
115 case kDecoder: | |
116 device_path = kDecoderDevice; | |
117 break; | |
118 case kEncoder: | |
119 device_path = kEncoderDevice; | |
120 break; | |
121 case kImageProcessor: | |
122 device_path = kImageProcessorDevice; | |
123 break; | |
124 } | |
125 | |
126 DVLOG(2) << "Initialize(): opening device: " << device_path; | |
127 // Open the video device. | |
128 device_fd_ = HANDLE_EINTR(open(device_path, O_RDWR | O_NONBLOCK | O_CLOEXEC)); | |
129 if (device_fd_ == -1) { | |
130 return false; | |
131 } | |
132 | |
133 device_poll_interrupt_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); | |
134 if (device_poll_interrupt_fd_ == -1) { | |
135 return false; | |
136 } | |
137 return true; | |
138 } | |
139 | |
140 EGLImageKHR ExynosV4L2Device::CreateEGLImage(EGLDisplay egl_display, | |
141 EGLContext /* egl_context */, | |
142 GLuint texture_id, | |
143 gfx::Size frame_buffer_size, | |
144 unsigned int buffer_index, | |
145 size_t planes_count) { | |
146 DVLOG(3) << "CreateEGLImage()"; | |
147 | |
148 scoped_ptr<base::ScopedFD[]> dmabuf_fds(new base::ScopedFD[planes_count]); | |
149 for (size_t i = 0; i < planes_count; ++i) { | |
150 // Export the DMABUF fd so we can export it as a texture. | |
151 struct v4l2_exportbuffer expbuf; | |
152 memset(&expbuf, 0, sizeof(expbuf)); | |
153 expbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
154 expbuf.index = buffer_index; | |
155 expbuf.plane = i; | |
156 expbuf.flags = O_CLOEXEC; | |
157 if (Ioctl(VIDIOC_EXPBUF, &expbuf) != 0) { | |
158 return EGL_NO_IMAGE_KHR; | |
159 } | |
160 dmabuf_fds[i].reset(expbuf.fd); | |
161 } | |
162 DCHECK_EQ(planes_count, 2u); | |
163 EGLint attrs[] = { | |
164 EGL_WIDTH, 0, EGL_HEIGHT, 0, | |
165 EGL_LINUX_DRM_FOURCC_EXT, 0, EGL_DMA_BUF_PLANE0_FD_EXT, 0, | |
166 EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0, EGL_DMA_BUF_PLANE0_PITCH_EXT, 0, | |
167 EGL_DMA_BUF_PLANE1_FD_EXT, 0, EGL_DMA_BUF_PLANE1_OFFSET_EXT, 0, | |
168 EGL_DMA_BUF_PLANE1_PITCH_EXT, 0, EGL_NONE, }; | |
169 attrs[1] = frame_buffer_size.width(); | |
170 attrs[3] = frame_buffer_size.height(); | |
171 attrs[5] = DRM_FORMAT_NV12; | |
172 attrs[7] = dmabuf_fds[0].get(); | |
173 attrs[9] = 0; | |
174 attrs[11] = frame_buffer_size.width(); | |
175 attrs[13] = dmabuf_fds[1].get(); | |
176 attrs[15] = 0; | |
177 attrs[17] = frame_buffer_size.width(); | |
178 | |
179 EGLImageKHR egl_image = eglCreateImageKHR( | |
180 egl_display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, attrs); | |
181 if (egl_image == EGL_NO_IMAGE_KHR) { | |
182 return egl_image; | |
183 } | |
184 glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id); | |
185 glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, egl_image); | |
186 | |
187 return egl_image; | |
188 } | |
189 | |
190 EGLBoolean ExynosV4L2Device::DestroyEGLImage(EGLDisplay egl_display, | |
191 EGLImageKHR egl_image) { | |
192 return eglDestroyImageKHR(egl_display, egl_image); | |
193 } | |
194 | |
195 GLenum ExynosV4L2Device::GetTextureTarget() { return GL_TEXTURE_EXTERNAL_OES; } | |
196 | |
197 uint32 ExynosV4L2Device::PreferredInputFormat() { | |
198 // TODO(posciak): We should support "dontcare" returns here once we | |
199 // implement proper handling (fallback, negotiation) for this in users. | |
200 CHECK_EQ(type_, kEncoder); | |
201 return V4L2_PIX_FMT_NV12M; | |
202 } | |
203 | |
204 uint32 ExynosV4L2Device::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 | |
211 } // namespace content | |
OLD | NEW |