Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 #include "content/common/gpu/media/exynos_video_encode_accelerator.h" | 5 #include "content/common/gpu/media/exynos_video_encode_accelerator.h" |
| 6 | 6 |
| 7 #include <fcntl.h> | 7 #include <fcntl.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/callback.h" | 14 #include "base/callback.h" |
| 15 #include "base/command_line.h" | 15 #include "base/command_line.h" |
| 16 #include "base/debug/trace_event.h" | 16 #include "base/debug/trace_event.h" |
| 17 #include "base/message_loop/message_loop_proxy.h" | 17 #include "base/message_loop/message_loop_proxy.h" |
| 18 #include "base/posix/eintr_wrapper.h" | 18 #include "base/posix/eintr_wrapper.h" |
| 19 #include "content/public/common/content_switches.h" | 19 #include "content/public/common/content_switches.h" |
| 20 #include "media/base/bitstream_buffer.h" | 20 #include "media/base/bitstream_buffer.h" |
| 21 | 21 |
| 22 #define NOTIFY_ERROR(x) \ | 22 #define NOTIFY_ERROR(x) \ |
| 23 do { \ | 23 do { \ |
| 24 SetEncoderState(kError); \ | 24 SetEncoderState(kError); \ |
| 25 DLOG(ERROR) << "calling NotifyError(): " << x; \ | 25 DLOG(ERROR) << "calling NotifyError(): " << x; \ |
| 26 NotifyError(x); \ | 26 NotifyError(x); \ |
| 27 } while (0) | 27 } while (0) |
| 28 | 28 |
| 29 #define IOCTL_OR_ERROR_RETURN(fd, type, arg) \ | 29 #define IOCTL_OR_ERROR_RETURN_VALUE(fd, type, arg, value) \ |
| 30 do { \ | 30 do { \ |
| 31 if (HANDLE_EINTR(ioctl(fd, type, arg) != 0)) { \ | 31 if (HANDLE_EINTR(ioctl(fd, type, arg) != 0)) { \ |
| 32 DPLOG(ERROR) << __func__ << "(): ioctl() failed: " << #type; \ | 32 DPLOG(ERROR) << __func__ << "(): ioctl() failed: " << #type; \ |
| 33 NOTIFY_ERROR(kPlatformFailureError); \ | 33 NOTIFY_ERROR(kPlatformFailureError); \ |
| 34 return; \ | 34 return value; \ |
| 35 } \ | 35 } \ |
| 36 } while (0) | 36 } while (0) |
| 37 | 37 |
| 38 #define IOCTL_OR_ERROR_RETURN_FALSE(fd, type, arg) \ | 38 #define IOCTL_OR_ERROR_RETURN(fd, type, arg) \ |
| 39 do { \ | 39 IOCTL_OR_ERROR_RETURN_VALUE(fd, type, arg, ((void)0)) |
|
Pawel Osciak
2014/03/13 06:18:13
You could also just s/((void)0)// I think?
sheu
2014/03/13 22:39:52
I think that's a gcc-specific extension.
| |
| 40 if (HANDLE_EINTR(ioctl(fd, type, arg) != 0)) { \ | 40 |
| 41 DPLOG(ERROR) << __func__ << "(): ioctl() failed: " << #type; \ | 41 #define IOCTL_OR_ERROR_RETURN_FALSE(fd, type, arg) \ |
| 42 NOTIFY_ERROR(kPlatformFailureError); \ | 42 IOCTL_OR_ERROR_RETURN_VALUE(fd, type, arg, false) |
| 43 return false; \ | |
| 44 } \ | |
| 45 } while (0) | |
| 46 | 43 |
| 47 namespace content { | 44 namespace content { |
| 48 | 45 |
| 49 namespace { | 46 namespace { |
| 50 | 47 |
| 51 const char kExynosGscDevice[] = "/dev/gsc1"; | 48 const char kExynosGscDevice[] = "/dev/gsc1"; |
| 52 const char kExynosMfcDevice[] = "/dev/mfc-enc"; | 49 const char kExynosMfcDevice[] = "/dev/mfc-enc"; |
| 53 | 50 |
| 54 // File descriptors we need to poll, one-bit flag for each. | 51 // File descriptors we need to poll, one-bit flag for each. |
| 55 enum PollFds { | 52 enum PollFds { |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 120 gsc_fd_ = -1; | 117 gsc_fd_ = -1; |
| 121 } | 118 } |
| 122 if (mfc_fd_ != -1) { | 119 if (mfc_fd_ != -1) { |
| 123 DestroyMfcInputBuffers(); | 120 DestroyMfcInputBuffers(); |
| 124 DestroyMfcOutputBuffers(); | 121 DestroyMfcOutputBuffers(); |
| 125 close(mfc_fd_); | 122 close(mfc_fd_); |
| 126 mfc_fd_ = -1; | 123 mfc_fd_ = -1; |
| 127 } | 124 } |
| 128 } | 125 } |
| 129 | 126 |
| 130 void ExynosVideoEncodeAccelerator::Initialize( | 127 bool ExynosVideoEncodeAccelerator::Initialize( |
| 131 media::VideoFrame::Format input_format, | 128 media::VideoFrame::Format input_format, |
| 132 const gfx::Size& input_visible_size, | 129 const gfx::Size& input_visible_size, |
| 133 media::VideoCodecProfile output_profile, | 130 media::VideoCodecProfile output_profile, |
| 134 uint32 initial_bitrate, | 131 uint32 initial_bitrate, |
| 135 Client* client) { | 132 Client* client) { |
| 136 DVLOG(3) << "Initialize(): input_format=" << input_format | 133 DVLOG(3) << "Initialize(): input_format=" << input_format |
| 137 << ", input_visible_size=" << input_visible_size.ToString() | 134 << ", input_visible_size=" << input_visible_size.ToString() |
| 138 << ", output_profile=" << output_profile | 135 << ", output_profile=" << output_profile |
| 139 << ", initial_bitrate=" << initial_bitrate; | 136 << ", initial_bitrate=" << initial_bitrate; |
| 140 | 137 |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 152 converted_allocated_size_.SetSize( | 149 converted_allocated_size_.SetSize( |
| 153 (converted_visible_size_.width() + 0xF) & ~0xF, | 150 (converted_visible_size_.width() + 0xF) & ~0xF, |
| 154 (converted_visible_size_.height() + 0xF) & ~0xF); | 151 (converted_visible_size_.height() + 0xF) & ~0xF); |
| 155 output_visible_size_ = converted_visible_size_; | 152 output_visible_size_ = converted_visible_size_; |
| 156 | 153 |
| 157 switch (input_format) { | 154 switch (input_format) { |
| 158 case media::VideoFrame::I420: | 155 case media::VideoFrame::I420: |
| 159 input_format_fourcc_ = V4L2_PIX_FMT_YUV420M; | 156 input_format_fourcc_ = V4L2_PIX_FMT_YUV420M; |
| 160 break; | 157 break; |
| 161 default: | 158 default: |
| 162 NOTIFY_ERROR(kInvalidArgumentError); | 159 DLOG(ERROR) << "Initialize(): invalid input_format=" << input_format; |
| 163 return; | 160 return false; |
| 164 } | 161 } |
| 165 | 162 |
| 166 if (output_profile >= media::H264PROFILE_MIN && | 163 if (output_profile >= media::H264PROFILE_MIN && |
| 167 output_profile <= media::H264PROFILE_MAX) { | 164 output_profile <= media::H264PROFILE_MAX) { |
| 168 output_format_fourcc_ = V4L2_PIX_FMT_H264; | 165 output_format_fourcc_ = V4L2_PIX_FMT_H264; |
| 169 } else if (output_profile >= media::VP8PROFILE_MIN && | 166 } else if (output_profile >= media::VP8PROFILE_MIN && |
| 170 output_profile <= media::VP8PROFILE_MAX) { | 167 output_profile <= media::VP8PROFILE_MAX) { |
| 171 output_format_fourcc_ = V4L2_PIX_FMT_VP8; | 168 output_format_fourcc_ = V4L2_PIX_FMT_VP8; |
| 172 } else { | 169 } else { |
| 173 NOTIFY_ERROR(kInvalidArgumentError); | 170 DLOG(ERROR) << "Initialize(): invalid output_profile=" << output_profile; |
| 174 return; | 171 return false; |
| 175 } | 172 } |
| 176 | 173 |
| 177 // Open the color conversion device. | 174 // Open the color conversion device. |
| 178 DVLOG(2) << "Initialize(): opening GSC device: " << kExynosGscDevice; | 175 DVLOG(2) << "Initialize(): opening GSC device: " << kExynosGscDevice; |
| 179 gsc_fd_ = | 176 gsc_fd_ = |
| 180 HANDLE_EINTR(open(kExynosGscDevice, O_RDWR | O_NONBLOCK | O_CLOEXEC)); | 177 HANDLE_EINTR(open(kExynosGscDevice, O_RDWR | O_NONBLOCK | O_CLOEXEC)); |
| 181 if (gsc_fd_ == -1) { | 178 if (gsc_fd_ == -1) { |
| 182 DPLOG(ERROR) << "Initialize(): could not open GSC device: " | 179 DPLOG(ERROR) << "Initialize(): could not open GSC device: " |
| 183 << kExynosGscDevice; | 180 << kExynosGscDevice; |
| 184 NOTIFY_ERROR(kPlatformFailureError); | 181 return false; |
| 185 return; | |
| 186 } | 182 } |
| 187 | 183 |
| 188 // Capabilities check. | 184 // Capabilities check. |
| 189 struct v4l2_capability caps; | 185 struct v4l2_capability caps; |
| 190 memset(&caps, 0, sizeof(caps)); | 186 memset(&caps, 0, sizeof(caps)); |
| 191 const __u32 kCapsRequired = V4L2_CAP_VIDEO_CAPTURE_MPLANE | | 187 const __u32 kCapsRequired = V4L2_CAP_VIDEO_CAPTURE_MPLANE | |
| 192 V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING; | 188 V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING; |
| 193 IOCTL_OR_ERROR_RETURN(gsc_fd_, VIDIOC_QUERYCAP, &caps); | 189 if (HANDLE_EINTR(ioctl(gsc_fd_, VIDIOC_QUERYCAP, &caps))) { |
| 190 DPLOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP"; | |
| 191 return false; | |
| 192 } | |
| 194 if ((caps.capabilities & kCapsRequired) != kCapsRequired) { | 193 if ((caps.capabilities & kCapsRequired) != kCapsRequired) { |
| 195 DLOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP: " | 194 DLOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP: " |
| 196 "caps check failed: 0x" << std::hex << caps.capabilities; | 195 "caps check failed: 0x" << std::hex << caps.capabilities; |
| 197 NOTIFY_ERROR(kPlatformFailureError); | 196 return false; |
| 198 return; | |
| 199 } | 197 } |
| 200 | 198 |
| 201 // Open the video encoder device. | 199 // Open the video encoder device. |
| 202 DVLOG(2) << "Initialize(): opening MFC device: " << kExynosMfcDevice; | 200 DVLOG(2) << "Initialize(): opening MFC device: " << kExynosMfcDevice; |
| 203 mfc_fd_ = | 201 mfc_fd_ = |
| 204 HANDLE_EINTR(open(kExynosMfcDevice, O_RDWR | O_NONBLOCK | O_CLOEXEC)); | 202 HANDLE_EINTR(open(kExynosMfcDevice, O_RDWR | O_NONBLOCK | O_CLOEXEC)); |
| 205 if (mfc_fd_ == -1) { | 203 if (mfc_fd_ == -1) { |
| 206 DPLOG(ERROR) << "Initialize(): could not open MFC device: " | 204 DPLOG(ERROR) << "Initialize(): could not open MFC device: " |
| 207 << kExynosMfcDevice; | 205 << kExynosMfcDevice; |
| 208 NOTIFY_ERROR(kPlatformFailureError); | 206 return false; |
| 209 return; | |
| 210 } | 207 } |
| 211 | 208 |
| 212 memset(&caps, 0, sizeof(caps)); | 209 memset(&caps, 0, sizeof(caps)); |
| 213 IOCTL_OR_ERROR_RETURN(mfc_fd_, VIDIOC_QUERYCAP, &caps); | 210 if (HANDLE_EINTR(ioctl(mfc_fd_, VIDIOC_QUERYCAP, &caps))) { |
| 211 DPLOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP"; | |
| 212 return false; | |
| 213 } | |
| 214 if ((caps.capabilities & kCapsRequired) != kCapsRequired) { | 214 if ((caps.capabilities & kCapsRequired) != kCapsRequired) { |
| 215 DLOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP: " | 215 DLOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP: " |
| 216 "caps check failed: 0x" << std::hex << caps.capabilities; | 216 "caps check failed: 0x" << std::hex << caps.capabilities; |
| 217 NOTIFY_ERROR(kPlatformFailureError); | 217 return false; |
| 218 return; | |
| 219 } | 218 } |
| 220 | 219 |
| 221 // Create the interrupt fd. | 220 // Create the interrupt fd. |
| 222 DCHECK_EQ(device_poll_interrupt_fd_, -1); | 221 DCHECK_EQ(device_poll_interrupt_fd_, -1); |
| 223 device_poll_interrupt_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); | 222 device_poll_interrupt_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); |
| 224 if (device_poll_interrupt_fd_ == -1) { | 223 if (device_poll_interrupt_fd_ == -1) { |
| 225 DPLOG(ERROR) << "Initialize(): eventfd() failed"; | 224 DPLOG(ERROR) << "Initialize(): eventfd() failed"; |
| 226 NOTIFY_ERROR(kPlatformFailureError); | 225 return false; |
| 227 return; | |
| 228 } | 226 } |
| 229 | 227 |
| 230 DVLOG(3) | 228 DVLOG(3) |
| 231 << "Initialize(): input_visible_size_=" << input_visible_size_.ToString() | 229 << "Initialize(): input_visible_size_=" << input_visible_size_.ToString() |
| 232 << ", input_allocated_size_=" << input_allocated_size_.ToString() | 230 << ", input_allocated_size_=" << input_allocated_size_.ToString() |
| 233 << ", converted_visible_size_=" << converted_visible_size_.ToString() | 231 << ", converted_visible_size_=" << converted_visible_size_.ToString() |
| 234 << ", converted_allocated_size_=" << converted_allocated_size_.ToString() | 232 << ", converted_allocated_size_=" << converted_allocated_size_.ToString() |
| 235 << ", output_visible_size_=" << output_visible_size_.ToString(); | 233 << ", output_visible_size_=" << output_visible_size_.ToString(); |
| 236 | 234 |
| 237 if (!CreateGscInputBuffers() || !CreateGscOutputBuffers()) | 235 if (!CreateGscInputBuffers() || !CreateGscOutputBuffers()) |
| 238 return; | 236 return false; |
| 239 | 237 |
| 240 // MFC setup for encoding is rather particular in ordering: | 238 // MFC setup for encoding is rather particular in ordering: |
| 241 // | 239 // |
| 242 // 1. Format (VIDIOC_S_FMT) set first on OUTPUT and CAPTURE queues. | 240 // 1. Format (VIDIOC_S_FMT) set first on OUTPUT and CAPTURE queues. |
| 243 // 2. VIDIOC_REQBUFS, VIDIOC_QBUF, and VIDIOC_STREAMON on CAPTURE queue. | 241 // 2. VIDIOC_REQBUFS, VIDIOC_QBUF, and VIDIOC_STREAMON on CAPTURE queue. |
| 244 // 3. VIDIOC_REQBUFS (and later VIDIOC_QBUF and VIDIOC_STREAMON) on OUTPUT | 242 // 3. VIDIOC_REQBUFS (and later VIDIOC_QBUF and VIDIOC_STREAMON) on OUTPUT |
| 245 // queue. | 243 // queue. |
| 246 // | 244 // |
| 247 // Unfortunately, we cannot do (3) in Initialize() here since we have no | 245 // Unfortunately, we cannot do (3) in Initialize() here since we have no |
| 248 // buffers to QBUF in step (2) until the client has provided output buffers | 246 // buffers to QBUF in step (2) until the client has provided output buffers |
| 249 // through UseOutputBitstreamBuffer(). So, we just do (1), and the | 247 // through UseOutputBitstreamBuffer(). So, we just do (1), and the |
| 250 // VIDIOC_REQBUFS part of (2) here. The rest is done the first time we get | 248 // VIDIOC_REQBUFS part of (2) here. The rest is done the first time we get |
| 251 // a UseOutputBitstreamBuffer() callback. | 249 // a UseOutputBitstreamBuffer() callback. |
| 252 | 250 |
| 253 if (!SetMfcFormats()) | 251 if (!SetMfcFormats()) |
| 254 return; | 252 return false; |
| 255 | 253 |
| 256 if (!InitMfcControls()) | 254 if (!InitMfcControls()) |
| 257 return; | 255 return false; |
| 258 | 256 |
| 259 // VIDIOC_REQBUFS on CAPTURE queue. | 257 // VIDIOC_REQBUFS on CAPTURE queue. |
| 260 if (!CreateMfcOutputBuffers()) | 258 if (!CreateMfcOutputBuffers()) |
| 261 return; | 259 return false; |
| 262 | |
| 263 | 260 |
| 264 if (!encoder_thread_.Start()) { | 261 if (!encoder_thread_.Start()) { |
| 265 DLOG(ERROR) << "Initialize(): encoder thread failed to start"; | 262 DLOG(ERROR) << "Initialize(): encoder thread failed to start"; |
| 266 NOTIFY_ERROR(kPlatformFailureError); | 263 return false; |
| 267 return; | |
| 268 } | 264 } |
| 269 | 265 |
| 270 RequestEncodingParametersChange(initial_bitrate, kInitialFramerate); | 266 RequestEncodingParametersChange(initial_bitrate, kInitialFramerate); |
| 271 | 267 |
| 272 SetEncoderState(kInitialized); | 268 SetEncoderState(kInitialized); |
| 273 | 269 |
| 274 child_message_loop_proxy_->PostTask( | 270 child_message_loop_proxy_->PostTask( |
| 275 FROM_HERE, base::Bind(&Client::NotifyInitializeDone, client_)); | |
| 276 | |
| 277 child_message_loop_proxy_->PostTask( | |
| 278 FROM_HERE, | 271 FROM_HERE, |
| 279 base::Bind(&Client::RequireBitstreamBuffers, | 272 base::Bind(&Client::RequireBitstreamBuffers, |
| 280 client_, | 273 client_, |
| 281 gsc_input_buffer_map_.size(), | 274 gsc_input_buffer_map_.size(), |
| 282 input_allocated_size_, | 275 input_allocated_size_, |
| 283 output_buffer_byte_size_)); | 276 output_buffer_byte_size_)); |
| 277 return true; | |
| 284 } | 278 } |
| 285 | 279 |
| 286 void ExynosVideoEncodeAccelerator::Encode( | 280 void ExynosVideoEncodeAccelerator::Encode( |
| 287 const scoped_refptr<media::VideoFrame>& frame, | 281 const scoped_refptr<media::VideoFrame>& frame, |
| 288 bool force_keyframe) { | 282 bool force_keyframe) { |
| 289 DVLOG(3) << "Encode(): force_keyframe=" << force_keyframe; | 283 DVLOG(3) << "Encode(): force_keyframe=" << force_keyframe; |
| 290 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread()); | 284 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread()); |
| 291 | 285 |
| 292 encoder_thread_.message_loop()->PostTask( | 286 encoder_thread_.message_loop()->PostTask( |
| 293 FROM_HERE, | 287 FROM_HERE, |
| (...skipping 1243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1537 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | 1531 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
| 1538 reqbufs.memory = V4L2_MEMORY_MMAP; | 1532 reqbufs.memory = V4L2_MEMORY_MMAP; |
| 1539 if (HANDLE_EINTR(ioctl(mfc_fd_, VIDIOC_REQBUFS, &reqbufs)) != 0) | 1533 if (HANDLE_EINTR(ioctl(mfc_fd_, VIDIOC_REQBUFS, &reqbufs)) != 0) |
| 1540 DPLOG(ERROR) << "DestroyMfcOutputBuffers(): ioctl() failed: VIDIOC_REQBUFS"; | 1534 DPLOG(ERROR) << "DestroyMfcOutputBuffers(): ioctl() failed: VIDIOC_REQBUFS"; |
| 1541 | 1535 |
| 1542 mfc_output_buffer_map_.clear(); | 1536 mfc_output_buffer_map_.clear(); |
| 1543 mfc_free_output_buffers_.clear(); | 1537 mfc_free_output_buffers_.clear(); |
| 1544 } | 1538 } |
| 1545 | 1539 |
| 1546 } // namespace content | 1540 } // namespace content |
| OLD | NEW |