OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2013 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 "content/common/gpu/media/exynos_video_encode_accelerator.h" | |
6 | |
7 #include <dlfcn.h> | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
copy/pasta?
(please make a pass on #includes and
| |
8 #include <fcntl.h> | |
9 #include <libdrm/drm_fourcc.h> | |
10 #include <linux/videodev2.h> | |
11 #include <poll.h> | |
12 #include <sys/eventfd.h> | |
13 #include <sys/ioctl.h> | |
14 #include <sys/mman.h> | |
15 #include "base/callback.h" | |
16 #include "base/debug/trace_event.h" | |
17 #include "base/file_util.h" | |
18 #include "base/message_loop/message_loop_proxy.h" | |
19 #include "base/posix/eintr_wrapper.h" | |
20 #include "media/base/bitstream_buffer.h" | |
21 #include "ui/gl/scoped_binders.h" | |
22 | |
23 #define NOTIFY_ERROR(x) \ | |
24 do { \ | |
25 SetEncoderState(kError); \ | |
26 DLOG(ERROR) << "calling NotifyError(): " << x; \ | |
27 NotifyError(x); \ | |
28 } while (0) | |
29 | |
30 #define IOCTL_OR_ERROR_RETURN(fd, type, arg) \ | |
31 do { \ | |
32 if (HANDLE_EINTR(ioctl(fd, type, arg) != 0)) { \ | |
33 DPLOG(ERROR) << __func__ << "(): ioctl() failed: " << #type; \ | |
34 NOTIFY_ERROR(kPlatformFailureError); \ | |
35 return; \ | |
36 } \ | |
37 } while (0) | |
38 | |
39 #define IOCTL_OR_ERROR_RETURN_FALSE(fd, type, arg) \ | |
40 do { \ | |
41 if (HANDLE_EINTR(ioctl(fd, type, arg) != 0)) { \ | |
42 DPLOG(ERROR) << __func__ << "(): ioctl() failed: " << #type; \ | |
43 NOTIFY_ERROR(kPlatformFailureError); \ | |
44 return false; \ | |
45 } \ | |
46 } while (0) | |
47 | |
48 namespace content { | |
49 | |
50 namespace { | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
anon namespace buys you nothing and costs you:
- 4
| |
51 | |
52 const char kExynosGscDevice[] = "/dev/gsc1"; | |
53 const char kExynosMfcDevice[] = "/dev/mfc-enc"; | |
54 | |
55 } // anonymous namespace | |
56 | |
57 struct ExynosVideoEncodeAccelerator::BitstreamBufferRef { | |
58 BitstreamBufferRef(int32 id, | |
59 scoped_ptr<base::SharedMemory> shm, | |
60 size_t size) | |
61 : id(id), | |
62 shm(shm.Pass()), | |
63 size(size) {} | |
64 const int32 id; | |
65 const scoped_ptr<base::SharedMemory> shm; | |
66 const size_t size; | |
67 }; | |
68 | |
69 ExynosVideoEncodeAccelerator::GscInputRecord::GscInputRecord() | |
70 : at_device(false), | |
71 frame_id(-1), | |
72 egl_sync(EGL_NO_SYNC_KHR), | |
73 egl_image(EGL_NO_IMAGE_KHR), | |
74 texture_id(0) {} | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
address & length are uninitialized.
please make a
| |
75 | |
76 ExynosVideoEncodeAccelerator::GscOutputRecord::GscOutputRecord() | |
77 : at_device(false), | |
78 mfc_input(-1) { | |
79 address[0] = address[1] = address[2] = NULL; | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
I am surprised to find out that the style guide do
| |
80 length[0] = length[1] = length[2] = 0; | |
81 bytes_used[0] = bytes_used[1] = bytes_used[2] = 0; | |
82 } | |
83 | |
84 ExynosVideoEncodeAccelerator::MfcInputRecord::MfcInputRecord() | |
85 : at_device(false) { | |
86 fd[0] = fd[1] = -1; | |
87 } | |
88 | |
89 ExynosVideoEncodeAccelerator::MfcOutputRecord::MfcOutputRecord() | |
90 : at_device(false), | |
91 address(NULL), | |
92 length(0), | |
93 bytes_used(0) {} | |
94 | |
95 ExynosVideoEncodeAccelerator::ExynosVideoEncodeAccelerator( | |
96 EGLDisplay egl_display, | |
97 media::VideoEncodeAccelerator::Client* client, | |
98 const base::Callback<bool(void)>& make_context_current, | |
99 bool encode_from_backbuffer) | |
100 : child_message_loop_proxy_(base::MessageLoopProxy::current()), | |
101 weak_this_ptr_factory_(this), | |
102 weak_this_(weak_this_ptr_factory_.GetWeakPtr()), | |
103 client_ptr_factory_(client), | |
104 client_(client_ptr_factory_.GetWeakPtr()), | |
105 encoder_thread_("ExynosEncoderThread"), | |
106 encoder_state_(kUninitialized), | |
107 output_buffer_byte_size_(0), | |
108 do_output_encoding_(false), | |
109 do_encode_from_backbuffer_(encode_from_backbuffer), | |
110 gsc_fd_(-1), | |
111 gsc_input_streamon_(false), | |
112 gsc_input_buffer_queued_count_(0), | |
113 gsc_output_streamon_(false), | |
114 gsc_output_buffer_queued_count_(0), | |
115 mfc_fd_(-1), | |
116 mfc_input_streamon_(false), | |
117 mfc_input_buffer_queued_count_(0), | |
118 mfc_output_streamon_(false), | |
119 mfc_output_buffer_queued_count_(0), | |
120 device_poll_thread_("ExynosDevicePollThread"), | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
This is the same name used by EVDA. IWBN to have
| |
121 device_poll_interrupt_fd_(-1), | |
122 make_context_current_(make_context_current), | |
123 egl_display_(egl_display), | |
124 video_profile_(media::VIDEO_CODEC_PROFILE_UNKNOWN) { | |
125 DCHECK(client_); | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
DCHECK_EQ(base::MessageLoopProxy::current(), Child
| |
126 } | |
127 | |
128 ExynosVideoEncodeAccelerator::~ExynosVideoEncodeAccelerator() { | |
129 DCHECK(!encoder_thread_.IsRunning()); | |
130 DCHECK(!device_poll_thread_.IsRunning()); | |
131 | |
132 if (device_poll_interrupt_fd_ != -1) { | |
133 HANDLE_EINTR(close(device_poll_interrupt_fd_)); | |
134 device_poll_interrupt_fd_ = -1; | |
135 } | |
136 if (mfc_fd_ != -1) { | |
137 DestroyMfcInputBuffers(); | |
138 DestroyMfcOutputBuffers(); | |
139 HANDLE_EINTR(close(mfc_fd_)); | |
140 mfc_fd_ = -1; | |
141 } | |
142 if (gsc_fd_ != -1) { | |
143 DestroyGscInputBuffers(); | |
144 DestroyGscOutputBuffers(); | |
145 HANDLE_EINTR(close(gsc_fd_)); | |
146 gsc_fd_ = -1; | |
147 } | |
148 } | |
149 | |
150 void ExynosVideoEncodeAccelerator::Initialize( | |
151 media::VideoCodecProfile profile, | |
152 const gfx::Size& input_resolution, | |
153 const gfx::Size& output_resolution, | |
154 int32 initial_bitrate) { | |
155 DVLOG(3) << "Initialize()"; | |
156 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread()); | |
157 DCHECK_EQ(encoder_state_, kUninitialized); | |
158 | |
159 video_profile_ = profile; | |
160 input_visible_size_ = input_resolution; | |
161 output_visible_size_ = output_resolution; | |
162 | |
163 switch (video_profile_) { | |
164 case media::RAWPROFILE_I420: { | |
165 do_output_encoding_ = false; | |
166 break; | |
167 } | |
168 case media::H264PROFILE_MAIN: { | |
169 do_output_encoding_ = true; | |
170 break; | |
171 } | |
172 default: { | |
173 DLOG(ERROR) << "Initialize(): invalid profile=" << video_profile_; | |
174 NOTIFY_ERROR(kInvalidArgumentError); | |
175 return; | |
176 } | |
177 } | |
178 | |
179 input_allocated_size_.SetSize( | |
180 (input_visible_size_.width() + 0xF) & ~0xF, | |
181 (input_visible_size_.height() + 0xF) & ~0xF); | |
182 converted_visible_size_.SetSize( | |
183 (output_visible_size_.width() + 0x1) & ~0x1, | |
184 (output_visible_size_.height() + 0x1) & ~0x1); | |
185 converted_allocated_size_.SetSize( | |
186 (converted_visible_size_.width() + 0xF) & ~0xF, | |
187 (converted_visible_size_.height() + 0xF) & ~0xF); | |
188 | |
189 // Output size may be modified by the constraints of the format (e.g. | |
190 // multiple-of-two for YUV formats) or the hardware. | |
191 output_visible_size_ = converted_visible_size_; | |
192 | |
193 if (!make_context_current_.Run()) { | |
194 DLOG(ERROR) << "Initialize(): could not make context current"; | |
195 NOTIFY_ERROR(kPlatformFailureError); | |
196 return; | |
197 } | |
198 | |
199 if (!gfx::g_driver_egl.ext.b_EGL_KHR_fence_sync) { | |
200 DLOG(ERROR) << "Initialize(): context does not have " | |
201 << "EGL_KHR_fence_sync"; | |
202 NOTIFY_ERROR(kPlatformFailureError); | |
203 return; | |
204 } | |
205 | |
206 // Open the video devices. | |
207 DVLOG(2) << "Initialize(): opening GSC device: " << kExynosGscDevice; | |
208 gsc_fd_ = HANDLE_EINTR(open(kExynosGscDevice, | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
worth pulling out open+capscheck into a helper fun
| |
209 O_RDWR | O_NONBLOCK | O_CLOEXEC)); | |
210 if (gsc_fd_ == -1) { | |
211 DPLOG(ERROR) << "Initialize(): could not open GSC device: " | |
212 << kExynosGscDevice; | |
213 NOTIFY_ERROR(kPlatformFailureError); | |
214 return; | |
215 } | |
216 | |
217 // Capabilities check. | |
218 struct v4l2_capability caps; | |
219 const __u32 kCapsRequired = | |
220 V4L2_CAP_VIDEO_CAPTURE_MPLANE | | |
221 V4L2_CAP_VIDEO_OUTPUT_MPLANE | | |
222 V4L2_CAP_STREAMING; | |
223 IOCTL_OR_ERROR_RETURN(gsc_fd_, VIDIOC_QUERYCAP, &caps); | |
224 if ((caps.capabilities & kCapsRequired) != kCapsRequired) { | |
225 DLOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP" | |
226 ", caps check failed: 0x" << std::hex << caps.capabilities; | |
227 NOTIFY_ERROR(kPlatformFailureError); | |
228 return; | |
229 } | |
230 | |
231 if (do_output_encoding_) { | |
232 // Open the video encoder device. | |
233 mfc_fd_ = HANDLE_EINTR(open(kExynosMfcDevice, | |
234 O_RDWR | O_NONBLOCK | O_CLOEXEC)); | |
235 if (mfc_fd_ == -1) { | |
236 DPLOG(ERROR) << "Initialize(): could not open MFC device: " | |
237 << kExynosMfcDevice; | |
238 NOTIFY_ERROR(kPlatformFailureError); | |
239 return; | |
240 } | |
241 | |
242 IOCTL_OR_ERROR_RETURN(mfc_fd_, VIDIOC_QUERYCAP, &caps); | |
243 if ((caps.capabilities & kCapsRequired) != kCapsRequired) { | |
244 DLOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP" | |
245 ", caps check failed: 0x" << std::hex << caps.capabilities; | |
246 NOTIFY_ERROR(kPlatformFailureError); | |
247 return; | |
248 } | |
249 } | |
250 | |
251 // Create the interrupt fd. | |
252 DCHECK_EQ(device_poll_interrupt_fd_, -1); | |
253 device_poll_interrupt_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); | |
254 if (device_poll_interrupt_fd_ == -1) { | |
255 DPLOG(ERROR) << "Initialize(): eventfd() failed"; | |
256 NOTIFY_ERROR(kPlatformFailureError); | |
257 return; | |
258 } | |
259 | |
260 DLOG(ERROR) | |
261 << "Initialize(): input_visible_size_=" | |
262 << input_visible_size_.width() << "x" | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
input_visible_size_.ToString() and ditto for the r
| |
263 << input_visible_size_.height() << ", input_allocated_size_=" | |
264 << input_allocated_size_.width() << "x" | |
265 << input_allocated_size_.height() << ", converted_visible_size_=" | |
266 << converted_visible_size_.width() << "x" | |
267 << converted_visible_size_.height() << ", converted_allocated_size_=" | |
268 << converted_allocated_size_.width() << "x" | |
269 << converted_allocated_size_.height() << ", output_visible_size_=" | |
270 << output_visible_size_.width() << "x" | |
271 << output_visible_size_.height(); | |
272 | |
273 if (!CreateGscInputBuffers() || !CreateGscOutputBuffers()) | |
274 return; | |
275 | |
276 if (do_output_encoding_) { | |
277 // MFC setup for encoding is rather particular in ordering. | |
278 // 1. Format (VIDIOC_S_FMT) set first on OUTPUT and CPATURE queues. | |
Pawel Osciak
2013/06/17 23:21:56
s/CPATURE/CAPTURE/
| |
279 // 2. VIDIOC_REQBUFS, VIDIOC_QBUF, and VIDIOC_STREAMON on CAPTURE queue. | |
280 // 3. VIDIOC_REQBUFS (and later VIDIOC_QBUF and VIDIOC_STREAMON) on OUTPUT | |
281 // queue. | |
282 | |
283 if (!SetMfcFormats()) | |
284 return; | |
285 | |
286 // VIDIOC_REQBUFS on CAPTURE queue. | |
287 if (!CreateMfcOutputBuffers()) | |
288 return; | |
289 | |
290 // VIDIOC_QBUF and VIDIOC_STREAMON on CAPTURE queue. | |
291 EnqueueMfc(); | |
292 | |
293 // VIDIOC_REQBUFS on OUTPUT queue. | |
294 if (!CreateMfcInputBuffers()) | |
295 return; | |
296 | |
297 SetBitrate(initial_bitrate); | |
298 } | |
299 | |
300 if (!encoder_thread_.Start()) { | |
301 DLOG(ERROR) << "Initialize(): encoder thread failed to start"; | |
302 NOTIFY_ERROR(kPlatformFailureError); | |
303 return; | |
304 } | |
305 | |
306 SetEncoderState(kInitialized); | |
307 | |
308 child_message_loop_proxy_->PostTask(FROM_HERE, base::Bind( | |
309 &Client::NotifyInitializeDone, | |
310 client_)); | |
311 | |
312 child_message_loop_proxy_->PostTask(FROM_HERE, base::Bind( | |
313 &Client::RequireBitstreamBuffers, | |
314 client_, | |
315 output_visible_size_, | |
316 output_buffer_byte_size_)); | |
317 } | |
318 | |
319 void ExynosVideoEncodeAccelerator::ReturnFreeGscInputBuffer(int index) { | |
320 DVLOG(3) << "ReturnFreeGscInputBuffer(): index=" << index; | |
321 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread()); | |
322 gsc_free_input_buffers_.push_back(index); | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
I was going to observe that
ReturnFreeGscInputBuf
| |
323 } | |
324 | |
325 void ExynosVideoEncodeAccelerator::Encode( | |
326 const scoped_refptr<media::VideoFrame>& frame, | |
327 int32 frame_id, | |
328 bool force_keyframe) { | |
329 DVLOG(3) << "Encode(): frame=" << frame.get() << ", frame_id=" << frame_id; | |
330 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread()); | |
331 | |
332 if (gsc_free_input_buffers_.empty()) { | |
333 child_message_loop_proxy_->PostTask(FROM_HERE, base::Bind( | |
334 &media::VideoEncodeAccelerator::Client::NotifyEndOfVideoFrame, | |
335 client_, | |
336 frame_id)); | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
Silently drop the frame?
At least worthy of a comm
| |
337 return; | |
338 } | |
339 | |
340 if (!make_context_current_.Run()) { | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
Comment this is needed for the eglsync call?
| |
341 DLOG(ERROR) << "Encode(): could not make context current"; | |
342 NOTIFY_ERROR(kPlatformFailureError); | |
343 return; | |
344 } | |
345 | |
346 const int gsc_buffer = gsc_free_input_buffers_.back(); | |
347 gsc_free_input_buffers_.pop_back(); | |
348 GscInputRecord& input_record = gsc_input_buffer_map_[gsc_buffer]; | |
349 DCHECK(!input_record.at_device); | |
350 DCHECK_EQ(input_record.frame_id, -1); | |
351 DCHECK_EQ(input_record.egl_sync, EGL_NO_SYNC_KHR); | |
352 input_record.frame_id = frame_id; | |
353 | |
354 if (frame->format() == media::VideoFrame::NATIVE_EGLSURFACE) { | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
WDYT of putting at the top of this method:
if ((f
| |
355 DCHECK_NE(input_record.texture_id, 0U); | |
356 gfx::ScopedTextureBinder binder(GL_TEXTURE_2D, input_record.texture_id); | |
357 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, | |
358 input_visible_size_.width(), | |
359 input_visible_size_.height()); | |
360 } | |
361 | |
362 input_record.egl_sync = | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
Why does this happen after the glCopyTexSubImage2D
| |
363 eglCreateSyncKHR(egl_display_, EGL_SYNC_FENCE_KHR, NULL); | |
364 if (input_record.egl_sync == EGL_NO_SYNC_KHR) { | |
365 DLOG(ERROR) << "Encode(): eglCreateSyncKHR() failed"; | |
366 NOTIFY_ERROR(kPlatformFailureError); | |
367 return; | |
368 } | |
369 | |
370 if (frame->format() == media::VideoFrame::NATIVE_EGLSURFACE) { | |
371 encoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( | |
372 &ExynosVideoEncodeAccelerator::EncodeTask, | |
373 base::Unretained(this), | |
374 gsc_buffer)); | |
375 } else { | |
376 encoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( | |
377 &ExynosVideoEncodeAccelerator::CopyFrameAndEncodeTask, | |
378 base::Unretained(this), | |
379 frame, | |
380 gsc_buffer)); | |
381 } | |
382 } | |
383 | |
384 void ExynosVideoEncodeAccelerator::UseBitstreamBuffer( | |
385 const media::BitstreamBuffer& buffer) { | |
386 DVLOG(3) << "UseBitstreamBuffer(): id=" << buffer.id(); | |
387 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread()); | |
388 | |
389 scoped_ptr<BitstreamBufferRef> buffer_ref(new BitstreamBufferRef( | |
390 buffer.id(), | |
391 scoped_ptr<base::SharedMemory>( | |
392 new base::SharedMemory(buffer.handle(), false)).Pass(), | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
wouldn't it be simpler to pass the handle to the B
| |
393 buffer.size())); | |
394 if (!buffer_ref->shm->Map(buffer_ref->size)) { | |
395 DLOG(ERROR) << "UseBitstreamBuffer(): could not map bitstream_buffer"; | |
396 NOTIFY_ERROR(kPlatformFailureError); | |
397 return; | |
398 } | |
399 | |
400 encoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( | |
401 &ExynosVideoEncodeAccelerator::UseBitstreamBufferTask, | |
402 base::Unretained(this), | |
403 base::Passed(&buffer_ref))); | |
404 } | |
405 | |
406 void ExynosVideoEncodeAccelerator::RequestEncodingParameterChange( | |
407 int32 bitrate) { | |
408 encoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( | |
409 &ExynosVideoEncodeAccelerator::SetBitrate, | |
410 base::Unretained(this), | |
411 bitrate)); | |
412 } | |
413 | |
414 void ExynosVideoEncodeAccelerator::Destroy() { | |
415 DVLOG(3) << "Destroy()"; | |
416 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread()); | |
417 | |
418 // We're destroying; cancel all callbacks. | |
419 client_ptr_factory_.InvalidateWeakPtrs(); | |
420 | |
421 // If the encoder thread is running, destroy using posted task. | |
422 if (encoder_thread_.IsRunning()) { | |
423 encoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( | |
424 &ExynosVideoEncodeAccelerator::DestroyTask, base::Unretained(this))); | |
425 // DestroyTask() will cause the encoder_thread_ to flush all tasks. | |
426 encoder_thread_.Stop(); | |
427 } else { | |
428 // Otherwise, call the destroy task directly. | |
429 DestroyTask(); | |
430 } | |
431 | |
432 // Set to kError state just in case. | |
433 SetEncoderState(kError); | |
434 | |
435 delete this; | |
436 } | |
437 | |
438 void ExynosVideoEncodeAccelerator::EncodeTask(int gsc_input_index) { | |
439 DVLOG(3) << "EncodeTask()"; | |
440 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current()); | |
441 DCHECK_NE(encoder_state_, kUninitialized); | |
442 const GscInputRecord& input_record = gsc_input_buffer_map_[gsc_input_index]; | |
443 TRACE_EVENT1("Video encoder", "EVEA::EncodeTask", | |
444 "frame_id", input_record.frame_id); | |
445 | |
446 if (encoder_state_ == kError) { | |
447 DVLOG(2) << "EncodeTask(): early out: kError state"; | |
448 return; | |
449 } | |
450 | |
451 if (encoder_state_ == kInitialized) { | |
452 if (!StartDevicePoll()) | |
453 return; | |
454 encoder_state_ = kEncoding; | |
455 } | |
456 | |
457 encoder_input_queue_.push_back(gsc_input_index); | |
458 EnqueueGsc(); | |
459 | |
460 child_message_loop_proxy_->PostTask(FROM_HERE, base::Bind( | |
461 &media::VideoEncodeAccelerator::Client::NotifyEndOfVideoFrame, | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
In the case of encoding video frames backed by tex
| |
462 client_, | |
463 input_record.frame_id)); | |
464 } | |
465 | |
466 void ExynosVideoEncodeAccelerator::CopyFrameAndEncodeTask( | |
467 const scoped_refptr<media::VideoFrame>& frame, | |
468 int gsc_input_index) { | |
469 DVLOG(3) << "CopyFrameAndEncodeTask()"; | |
470 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current()); | |
471 | |
472 if (encoder_state_ == kError) { | |
473 DVLOG(2) << "CopyFrameAndEncodeTask(): early out: kError state"; | |
474 return; | |
475 } | |
476 | |
477 if (frame->coded_size() != input_visible_size_) { | |
Pawel Osciak
2013/06/17 23:21:56
This is confusing... why not visible_rect() (needs
| |
478 DLOG(ERROR) << "EncodeFrameTask(): input size change not supported: " | |
479 << input_visible_size_.width() << "x" | |
480 << input_visible_size_.height() << " -> " | |
481 << frame->coded_size().width() << "x" | |
482 << frame->coded_size().height(); | |
483 NOTIFY_ERROR(kInvalidArgumentError); | |
484 return; | |
485 } | |
486 | |
487 NOTIMPLEMENTED(); | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
TODOify the bits that aren't (namely, copying |fra
| |
488 EncodeTask(gsc_input_index); | |
489 } | |
490 | |
491 void ExynosVideoEncodeAccelerator::UseBitstreamBufferTask( | |
492 scoped_ptr<BitstreamBufferRef> buffer_ref) { | |
493 DVLOG(3) << "UseBitstreamBufferTask(): id=" << buffer_ref->id; | |
494 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current()); | |
495 TRACE_EVENT1("Video Encoder", "EVEA::UseBitstreamBufferTask", | |
496 "buffer", buffer_ref->id); | |
497 | |
498 encoder_bitstream_buffers_.push_back(linked_ptr<BitstreamBufferRef>( | |
499 buffer_ref.release())); | |
500 | |
501 ReturnCompleteBuffers(); | |
502 EnqueueGsc(); | |
503 EnqueueMfc(); | |
504 } | |
505 | |
506 void ExynosVideoEncodeAccelerator::DestroyTask() { | |
507 DVLOG(3) << "DestroyTask()"; | |
508 TRACE_EVENT0("Video Encoder", "EVEA::DestroyTask"); | |
509 | |
510 // DestroyTask() should run regardless of encoder_state_. | |
Pawel Osciak
2013/06/17 23:21:56
Perhaps a DCHECK(!decoder_thread_.IsRunning() || d
| |
511 | |
512 // Stop streaming and the device_poll_thread_. | |
513 StopDevicePoll(); | |
514 | |
515 // Set our state to kError. Just in case. | |
516 encoder_state_ = kError; | |
517 } | |
518 | |
519 void ExynosVideoEncodeAccelerator::ServiceDeviceTask() { | |
520 DVLOG(3) << "ServiceDeviceTask()"; | |
521 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current()); | |
522 DCHECK_NE(encoder_state_, kUninitialized); | |
523 DCHECK_NE(encoder_state_, kInitialized); | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
Could be more robust to future change by dropping
| |
524 TRACE_EVENT0("Video Encoder", "EVEA::ServiceDeviceTask"); | |
525 | |
526 if (encoder_state_ == kError) { | |
527 DVLOG(2) << "ServiceDeviceTask(): early out: kError state"; | |
528 return; | |
529 } | |
530 | |
531 DequeueGsc(); | |
532 DequeueMfc(); | |
533 EnqueueGsc(); | |
534 EnqueueMfc(); | |
535 | |
536 // Clear the interrupt fd. | |
537 if (!ClearDevicePollInterrupt()) | |
538 return; | |
539 | |
540 unsigned int poll_fds = 0; | |
541 // Add GSC fd, if we should poll on it. | |
542 // GSC has to wait until both input and output buffers are queued. | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
This logic makes me think it's never correct to ha
| |
543 if (gsc_input_buffer_queued_count_ > 0 && gsc_output_buffer_queued_count_ > 0) | |
544 poll_fds |= kPollGsc; | |
545 // Add MFC fd, if we should poll on it. | |
546 // MFC can be polled as soon as either input or output buffers are queued. | |
547 if (mfc_input_buffer_queued_count_ + mfc_output_buffer_queued_count_ > 0) | |
548 poll_fds |= kPollMfc; | |
549 | |
550 // ServiceDeviceTask() should only ever be scheduled from DevicePollTask(), | |
551 // so either: | |
552 // * device_poll_thread_ is running normally | |
553 // * device_poll_thread_ scheduled us, but then a ResetTask() or DestroyTask() | |
554 // shut it down, in which case we're either in kError state, and we should | |
555 // have early-outed already. | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
either... is missing "or" clause.
| |
556 DCHECK(device_poll_thread_.message_loop()); | |
557 // Queue the DevicePollTask() now. | |
558 device_poll_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( | |
559 &ExynosVideoEncodeAccelerator::DevicePollTask, | |
560 base::Unretained(this), | |
561 poll_fds)); | |
562 | |
563 DVLOG(1) << "ServiceDeviceTask(): buffer counts: ENC[" | |
564 << encoder_input_queue_.size() << "] => GSC[" | |
565 << gsc_free_input_buffers_.size() << "+" | |
566 << gsc_input_buffer_queued_count_ << "/" | |
567 << gsc_input_buffer_map_.size() << "->" | |
568 << gsc_free_output_buffers_.size() << "+" | |
569 << gsc_output_buffer_queued_count_ << "/" | |
570 << gsc_output_buffer_map_.size() << "] => " | |
571 << gsc_output_mfc_input_queue_.size() << " => MFC[" | |
572 << mfc_free_input_buffers_.size() << "+" | |
573 << mfc_input_buffer_queued_count_ << "/" | |
574 << mfc_input_buffer_map_.size() << "->" | |
575 << mfc_free_output_buffers_.size() << "+" | |
576 << mfc_output_buffer_queued_count_ << "/" | |
577 << mfc_output_buffer_map_.size() << "] => OUT[" | |
578 << encoder_output_queue_.size() << "]"; | |
579 } | |
580 | |
581 void ExynosVideoEncodeAccelerator::EnqueueGsc() { | |
582 DVLOG(3) << "EnqueueGsc()"; | |
583 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current()); | |
584 TRACE_EVENT0("Video Encoder", "EVEA::EnqueueGsc"); | |
585 | |
586 const int old_gsc_inputs_queued = gsc_input_buffer_queued_count_; | |
587 while (!encoder_input_queue_.empty()) { | |
588 if (!EnqueueGscInputRecord()) | |
589 return; | |
590 } | |
591 if (old_gsc_inputs_queued == 0 && gsc_input_buffer_queued_count_ != 0) { | |
592 // We started up a previously empty queue. | |
593 // Queue state changed; signal interrupt. | |
594 if (!SetDevicePollInterrupt()) | |
595 return; | |
596 // Start VIDIOC_STREAMON if we haven't yet. | |
597 if (!gsc_input_streamon_) { | |
598 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
599 IOCTL_OR_ERROR_RETURN(gsc_fd_, VIDIOC_STREAMON, &type); | |
600 gsc_input_streamon_ = true; | |
601 } | |
602 } | |
603 | |
604 // Enqueue a GSC output, only if we need one | |
605 if (gsc_input_buffer_queued_count_ != 0 && | |
606 gsc_output_buffer_queued_count_ == 0 && | |
607 !gsc_free_output_buffers_.empty() && | |
608 (!do_output_encoding_ || !mfc_free_input_buffers_.empty())) { | |
609 const int old_gsc_outputs_queued = gsc_output_buffer_queued_count_; | |
610 if (!EnqueueGscOutputRecord()) | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
Why not loop until gsc_free_output_buffers_.empty(
| |
611 return; | |
612 if (old_gsc_outputs_queued == 0 && gsc_output_buffer_queued_count_ != 0) { | |
613 // We just started up a previously empty queue. | |
614 // Queue state changed; signal interrupt. | |
615 if (!SetDevicePollInterrupt()) | |
616 return; | |
617 // Start VIDIOC_STREAMON if we haven't yet. | |
618 if (!gsc_output_streamon_) { | |
619 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
620 IOCTL_OR_ERROR_RETURN(gsc_fd_, VIDIOC_STREAMON, &type); | |
621 gsc_output_streamon_ = true; | |
622 } | |
623 } | |
624 } | |
625 // Bug check: GSC is liable to race conditions if more than one buffer is | |
626 // simultaneously queued. | |
627 DCHECK_GE(1, gsc_output_buffer_queued_count_); | |
628 } | |
629 | |
630 void ExynosVideoEncodeAccelerator::DequeueGsc() { | |
631 DVLOG(3) << "DequeueGsc()"; | |
632 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current()); | |
633 TRACE_EVENT0("Video Encoder", "EVEA::DequeueGsc"); | |
634 | |
635 // Dequeue completed GSC input (VIDEO_OUTPUT) buffers, and recycle to the free | |
636 // list. | |
637 struct v4l2_buffer dqbuf; | |
638 struct v4l2_plane planes[3]; | |
639 while (gsc_input_buffer_queued_count_ > 0) { | |
640 DCHECK(gsc_input_streamon_); | |
641 memset(&dqbuf, 0, sizeof(dqbuf)); | |
642 dqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
643 dqbuf.memory = V4L2_MEMORY_MMAP; | |
644 if (ioctl(gsc_fd_, VIDIOC_DQBUF, &dqbuf) != 0) { | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
HANDLE_EINTR?
(here and elsewhere)
| |
645 if (errno == EAGAIN) { | |
646 // EAGAIN if we're just out of buffers to dequeue. | |
647 break; | |
648 } | |
649 DPLOG(ERROR) << "DequeueGsc(): ioctl() failed: VIDIOC_DQBUF"; | |
650 NOTIFY_ERROR(kPlatformFailureError); | |
651 return; | |
652 } | |
653 GscInputRecord& input_record = gsc_input_buffer_map_[dqbuf.index]; | |
654 DCHECK(input_record.at_device); | |
655 gsc_free_input_buffers_.push_back(dqbuf.index); | |
656 const int32 frame_id = input_record.frame_id; | |
657 input_record.at_device = false; | |
658 input_record.frame_id = -1; | |
659 gsc_input_buffer_queued_count_--; | |
660 child_message_loop_proxy_->PostTask(FROM_HERE, base::Bind( | |
661 &Client::NotifyEndOfVideoFrame, client_, frame_id)); | |
662 } | |
663 | |
664 // Dequeue completed GSC output (VIDEO_CAPTURE) buffers, and queue to the | |
665 // completed queue. | |
666 while (gsc_output_buffer_queued_count_ > 0) { | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
Note this only goes around at most once because of
| |
667 DCHECK(gsc_output_streamon_); | |
668 memset(&dqbuf, 0, sizeof(dqbuf)); | |
669 memset(&planes, 0, sizeof(planes)); | |
670 dqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
671 dqbuf.memory = V4L2_MEMORY_MMAP; | |
672 dqbuf.m.planes = planes; | |
673 dqbuf.length = 3; | |
Pawel Osciak
2013/06/17 23:21:56
3 planes for GSC CAPTURE, but always 2 into MFC OU
| |
674 if (ioctl(gsc_fd_, VIDIOC_DQBUF, &dqbuf) != 0) { | |
675 if (errno == EAGAIN) { | |
676 // EAGAIN if we're just out of buffers to dequeue. | |
677 break; | |
678 } | |
679 DPLOG(ERROR) << "DequeueGsc(): ioctl() failed: VIDIOC_DQBUF"; | |
680 NOTIFY_ERROR(kPlatformFailureError); | |
681 return; | |
682 } | |
683 GscOutputRecord& output_record = gsc_output_buffer_map_[dqbuf.index]; | |
684 DCHECK(output_record.at_device); | |
685 output_record.at_device = false; | |
686 if (do_output_encoding_) { | |
687 gsc_output_mfc_input_queue_.push_back(output_record.mfc_input); | |
688 output_record.mfc_input = -1; | |
689 gsc_free_output_buffers_.push_back(dqbuf.index); | |
690 } else { | |
691 // Don't recycle to its free list yet -- we can't do that until | |
692 // ReturnCompleteBuffers() finishes copying the output out. | |
693 output_record.bytes_used[0] = dqbuf.m.planes[0].bytesused; | |
694 output_record.bytes_used[1] = dqbuf.m.planes[1].bytesused; | |
695 output_record.bytes_used[2] = dqbuf.m.planes[2].bytesused; | |
696 encoder_output_queue_.push_back(dqbuf.index); | |
697 } | |
698 gsc_output_buffer_queued_count_--; | |
699 } | |
700 | |
701 ReturnCompleteBuffers(); | |
702 } | |
703 | |
704 void ExynosVideoEncodeAccelerator::EnqueueMfc() { | |
705 DVLOG(3) << "EnqueueMfc()"; | |
706 TRACE_EVENT0("Video Encoder", "EVEA::EnqueueMfc"); | |
707 | |
708 if (!do_output_encoding_) | |
709 return; | |
710 | |
711 // Drain the pipe of completed GSC output buffers. | |
712 const int old_mfc_inputs_queued = mfc_input_buffer_queued_count_; | |
713 while (!gsc_output_mfc_input_queue_.empty()) { | |
714 if (!EnqueueMfcInputRecord()) | |
715 return; | |
716 } | |
717 if (old_mfc_inputs_queued == 0 && mfc_input_buffer_queued_count_ != 0) { | |
718 // We just started up a previously empty queue. | |
719 // Queue state changed; signal interrupt. | |
720 if (!SetDevicePollInterrupt()) | |
721 return; | |
722 // Start VIDIOC_STREAMON if we haven't yet. | |
723 if (!mfc_input_streamon_) { | |
724 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
725 IOCTL_OR_ERROR_RETURN(mfc_fd_, VIDIOC_STREAMON, &type); | |
726 mfc_input_streamon_ = true; | |
727 } | |
728 } | |
729 | |
730 // Enqueue all the MFC outputs we can. | |
731 const int old_mfc_outputs_queued = mfc_output_buffer_queued_count_; | |
732 while (!mfc_free_output_buffers_.empty()) { | |
733 if (!EnqueueMfcOutputRecord()) | |
734 return; | |
735 } | |
736 if (old_mfc_outputs_queued == 0 && mfc_output_buffer_queued_count_ != 0) { | |
737 // We just started up a previously empty queue. | |
738 // Queue state changed; signal interrupt. | |
739 if (!SetDevicePollInterrupt()) | |
740 return; | |
741 // Start VIDIOC_STREAMON if we haven't yet. | |
742 if (!mfc_output_streamon_) { | |
743 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
744 IOCTL_OR_ERROR_RETURN(mfc_fd_, VIDIOC_STREAMON, &type); | |
745 mfc_output_streamon_ = true; | |
746 } | |
747 } | |
748 } | |
749 | |
750 void ExynosVideoEncodeAccelerator::DequeueMfc() { | |
751 DVLOG(3) << "DequeueMfc"; | |
752 TRACE_EVENT0("Video Encoder", "EVEA::DequeueMfc"); | |
753 | |
754 // Dequeue completed MFC input (VIDEO_OUTPUT) buffers, and recycle to the free | |
755 // list. | |
756 struct v4l2_buffer dqbuf; | |
757 struct v4l2_plane planes[2]; | |
758 while (mfc_input_buffer_queued_count_ > 0) { | |
759 DCHECK(mfc_input_streamon_); | |
760 memset(&dqbuf, 0, sizeof(dqbuf)); | |
761 dqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
762 dqbuf.memory = V4L2_MEMORY_MMAP; | |
763 dqbuf.m.planes = planes; | |
764 dqbuf.length = 2; | |
765 if (ioctl(mfc_fd_, VIDIOC_DQBUF, &dqbuf) != 0) { | |
766 if (errno == EAGAIN) { | |
767 // EAGAIN if we're just out of buffers to dequeue. | |
768 break; | |
769 } | |
770 DPLOG(ERROR) << "DequeueMfc(): ioctl() failed: VIDIOC_DQBUF"; | |
771 NOTIFY_ERROR(kPlatformFailureError); | |
772 return; | |
773 } | |
774 MfcInputRecord& input_record = mfc_input_buffer_map_[dqbuf.index]; | |
775 DCHECK(input_record.at_device); | |
776 input_record.at_device = false; | |
777 mfc_free_input_buffers_.push_back(dqbuf.index); | |
778 mfc_input_buffer_queued_count_--; | |
779 } | |
780 | |
781 // Dequeue completed MFC output (VIDEO_CAPTURE) buffers, and queue to the | |
782 // completed queue. Don't recycle to its free list yet -- we can't do that | |
783 // until ReturnCompleteBuffers() finishes copying the output out. | |
784 while (mfc_output_buffer_queued_count_ > 0) { | |
785 DCHECK(mfc_output_streamon_); | |
786 memset(&dqbuf, 0, sizeof(dqbuf)); | |
787 memset(planes, 0, sizeof(planes)); | |
788 dqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
789 dqbuf.memory = V4L2_MEMORY_MMAP; | |
790 dqbuf.m.planes = planes; | |
791 dqbuf.length = 1; | |
792 if (ioctl(mfc_fd_, VIDIOC_DQBUF, &dqbuf) != 0) { | |
793 if (errno == EAGAIN) { | |
794 // EAGAIN if we're just out of buffers to dequeue. | |
795 break; | |
796 } | |
797 DPLOG(ERROR) << "DequeueMfc(): ioctl() failed: VIDIOC_DQBUF"; | |
798 NOTIFY_ERROR(kPlatformFailureError); | |
799 return; | |
800 } | |
801 // Don't recycle to its free list yet -- we can't do that until | |
802 // ReturnCompleteBuffers() finishes copying the output out. | |
803 MfcOutputRecord& output_record = mfc_output_buffer_map_[dqbuf.index]; | |
804 DCHECK(output_record.at_device); | |
805 output_record.at_device = false; | |
806 output_record.bytes_used = dqbuf.m.planes[0].bytesused; | |
807 encoder_output_queue_.push_back(dqbuf.index); | |
808 mfc_output_buffer_queued_count_--; | |
809 } | |
810 } | |
811 | |
812 bool ExynosVideoEncodeAccelerator::EnqueueGscInputRecord() { | |
813 DVLOG(3) << "EnqueueGscInputRecord()"; | |
814 DCHECK(!encoder_input_queue_.empty()); | |
815 | |
816 // Enqueue a GSC input (VIDEO_OUTPUT) buffer for an input video frame | |
817 const int gsc_buffer = encoder_input_queue_.front(); | |
818 GscInputRecord& input_record = gsc_input_buffer_map_[gsc_buffer]; | |
819 DCHECK(!input_record.at_device); | |
820 if (input_record.egl_sync != EGL_NO_SYNC_KHR) { | |
821 TRACE_EVENT0( | |
822 "Video Encoder", | |
823 "EVEA::EnqueueGscInputRecord: eglClientWaitSyncKHR"); | |
824 // If we have to wait for completion, wait. | |
825 eglClientWaitSyncKHR(egl_display_, input_record.egl_sync, 0, | |
Pawel Osciak
2013/06/17 23:21:56
egl calls from encoder_thread_?
Also, needs MCC?
| |
826 EGL_FOREVER_KHR); | |
827 eglDestroySyncKHR(egl_display_, input_record.egl_sync); | |
828 input_record.egl_sync = EGL_NO_SYNC_KHR; | |
829 } | |
830 struct v4l2_buffer qbuf; | |
831 struct v4l2_plane qbuf_planes[1]; | |
832 memset(&qbuf, 0, sizeof(qbuf)); | |
833 memset(qbuf_planes, 0, sizeof(qbuf_planes)); | |
834 qbuf.index = gsc_buffer; | |
835 qbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
836 qbuf.timestamp.tv_sec = input_record.frame_id; | |
837 qbuf.memory = V4L2_MEMORY_MMAP; | |
838 qbuf.m.planes = qbuf_planes; | |
839 qbuf.length = 1; | |
840 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_QBUF, &qbuf); | |
841 encoder_input_queue_.pop_front(); | |
842 input_record.at_device = true; | |
843 gsc_input_buffer_queued_count_++; | |
844 DVLOG(3) << "EnqueueGscInputRecord(): enqueued buffer=" << gsc_buffer | |
845 << ", frame_id=" << input_record.frame_id; | |
846 return true; | |
847 } | |
848 | |
849 bool ExynosVideoEncodeAccelerator::EnqueueGscOutputRecord() { | |
850 DVLOG(3) << "EnqueueGscOutputRecord()"; | |
851 DCHECK(!gsc_free_output_buffers_.empty()); | |
852 | |
853 // Enqueue a GSC output (VIDEO_CAPTURE) buffer. | |
854 const int gsc_buffer = gsc_free_output_buffers_.back(); | |
855 GscOutputRecord& output_record = gsc_output_buffer_map_[gsc_buffer]; | |
856 DCHECK(!output_record.at_device); | |
857 DCHECK_EQ(output_record.mfc_input, -1); | |
858 struct v4l2_buffer qbuf; | |
859 struct v4l2_plane qbuf_planes[3]; | |
860 memset(&qbuf, 0, sizeof(qbuf)); | |
861 memset(qbuf_planes, 0, sizeof(qbuf_planes)); | |
862 qbuf.index = gsc_buffer; | |
863 qbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
864 qbuf.m.planes = qbuf_planes; | |
865 if (do_output_encoding_) { | |
866 DCHECK(!mfc_free_input_buffers_.empty()); | |
867 qbuf.memory = V4L2_MEMORY_DMABUF; | |
868 const int mfc_buffer = mfc_free_input_buffers_.back(); | |
869 mfc_free_input_buffers_.pop_back(); | |
870 MfcInputRecord& input_record = mfc_input_buffer_map_[mfc_buffer]; | |
871 DCHECK(!input_record.at_device); | |
872 output_record.mfc_input = mfc_buffer; | |
873 qbuf.m.planes[0].m.fd = input_record.fd[0]; | |
874 qbuf.m.planes[1].m.fd = input_record.fd[1]; | |
875 qbuf.length = 2; | |
876 } else { | |
877 qbuf.memory = V4L2_MEMORY_MMAP; | |
878 qbuf.length = 3; | |
879 } | |
880 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_QBUF, &qbuf); | |
881 gsc_free_output_buffers_.pop_back(); | |
882 output_record.at_device = true; | |
883 gsc_output_buffer_queued_count_++; | |
884 return true; | |
885 } | |
886 | |
887 bool ExynosVideoEncodeAccelerator::EnqueueMfcInputRecord() { | |
888 DVLOG(3) << "EnqueueMfcInputRecord()"; | |
889 DCHECK(do_output_encoding_); | |
890 DCHECK(!gsc_output_mfc_input_queue_.empty()); | |
891 | |
892 // Enqueue a MFC input (VIDEO_OUTPUT) buffer. | |
893 const int mfc_buffer = gsc_output_mfc_input_queue_.front(); | |
894 MfcInputRecord& input_record = mfc_input_buffer_map_[mfc_buffer]; | |
895 DCHECK(!input_record.at_device); | |
896 struct v4l2_buffer qbuf; | |
897 struct v4l2_plane qbuf_planes[2]; | |
898 memset(&qbuf, 0, sizeof(qbuf)); | |
899 memset(qbuf_planes, 0, sizeof(qbuf_planes)); | |
900 qbuf.index = mfc_buffer; | |
901 qbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
902 qbuf.memory = V4L2_MEMORY_MMAP; | |
903 qbuf.m.planes = qbuf_planes; | |
904 qbuf.length = 2; | |
905 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_QBUF, &qbuf); | |
906 gsc_output_mfc_input_queue_.pop_front(); | |
907 input_record.at_device = true; | |
908 mfc_input_buffer_queued_count_++; | |
909 return true; | |
910 } | |
911 | |
912 bool ExynosVideoEncodeAccelerator::EnqueueMfcOutputRecord() { | |
913 DVLOG(3) << "EnqueueMfcOutputRecord()"; | |
914 DCHECK(do_output_encoding_); | |
915 DCHECK(!mfc_free_output_buffers_.empty()); | |
916 | |
917 // Enqueue a MFC output (VIDEO_CAPTURE) buffer. | |
918 const int mfc_buffer = mfc_free_output_buffers_.back(); | |
919 MfcOutputRecord& output_record = mfc_output_buffer_map_[mfc_buffer]; | |
920 DCHECK(!output_record.at_device); | |
921 DCHECK_EQ(output_record.bytes_used, 0U); | |
922 struct v4l2_buffer qbuf; | |
923 struct v4l2_plane qbuf_planes[1]; | |
924 memset(&qbuf, 0, sizeof(qbuf)); | |
925 memset(qbuf_planes, 0, sizeof(qbuf_planes)); | |
926 qbuf.index = mfc_buffer; | |
927 qbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
928 qbuf.memory = V4L2_MEMORY_MMAP; | |
929 qbuf.m.planes = qbuf_planes; | |
930 qbuf.length = 1; | |
931 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_QBUF, &qbuf); | |
932 mfc_free_output_buffers_.pop_back(); | |
933 output_record.at_device = true; | |
934 mfc_output_buffer_queued_count_++; | |
935 return true; | |
936 } | |
937 | |
938 void ExynosVideoEncodeAccelerator::ReturnCompleteBuffers() { | |
939 DVLOG(3) << "ReturnCompleteBuffers()"; | |
940 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current()); | |
941 DCHECK_NE(encoder_state_, kUninitialized); | |
942 | |
943 while (!encoder_output_queue_.empty() && | |
944 !encoder_bitstream_buffers_.empty()) { | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
indent to opening parens
(what happened to [git cl
| |
945 const int output_index = encoder_output_queue_.front(); | |
946 encoder_output_queue_.pop_front(); | |
947 scoped_ptr<BitstreamBufferRef> buffer_ref( | |
948 encoder_bitstream_buffers_.back().release()); | |
949 encoder_bitstream_buffers_.pop_back(); | |
950 uint8* data = reinterpret_cast<uint8*>(buffer_ref->shm->memory()); | |
951 size_t offset = 0; | |
952 if (do_output_encoding_) { | |
953 MfcOutputRecord& output_record = mfc_output_buffer_map_[output_index]; | |
954 CHECK_GE(buffer_ref->size, output_record.bytes_used); | |
955 memcpy(data, output_record.address, output_record.bytes_used); | |
956 offset += output_record.bytes_used; | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
s/+=/=/ ?
| |
957 output_record.bytes_used = 0; | |
958 mfc_free_output_buffers_.push_back(output_index); | |
959 } else { | |
960 GscOutputRecord& output_record = gsc_output_buffer_map_[output_index]; | |
961 // GSC output is 16 pixel-aligned; we may have to trim down to our actual | |
962 // output size. | |
963 // Copy the Y plane. | |
964 const uint8* y_plane = reinterpret_cast<uint8*>(output_record.address[0]); | |
965 for (int i = 0; i < output_visible_size_.height(); ++i) { | |
966 memcpy(data, y_plane, output_visible_size_.width()); | |
967 data += output_visible_size_.width(); | |
968 y_plane += converted_allocated_size_.width(); | |
969 } | |
970 // Copy the U plane. | |
971 const uint8* u_plane = reinterpret_cast<uint8*>(output_record.address[1]); | |
972 for (int i = 0; i < output_visible_size_.height() / 2; ++i) { | |
973 memcpy(data, u_plane, output_visible_size_.width() / 2); | |
974 data += output_visible_size_.width() / 2; | |
975 u_plane += converted_allocated_size_.width() / 2; | |
976 } | |
977 // Copy the V plane. | |
978 const uint8* v_plane = reinterpret_cast<uint8*>(output_record.address[2]); | |
979 for (int i = 0; i < output_visible_size_.height() / 2; ++i) { | |
980 memcpy(data, v_plane, output_visible_size_.width() / 2); | |
981 data += output_visible_size_.width() / 2; | |
982 v_plane += converted_allocated_size_.width() / 2; | |
983 } | |
984 offset = output_visible_size_.GetArea() * 3 / 2; | |
985 gsc_free_output_buffers_.push_back(output_index); | |
986 } | |
987 DLOG(ERROR) << "ReturnCompleteBuffers(): BitstreamBufferReady(): " | |
988 << "bitstream_buffer_id=" << buffer_ref->id | |
989 << ", size=" << offset; | |
990 child_message_loop_proxy_->PostTask(FROM_HERE, base::Bind( | |
991 &Client::BitstreamBufferReady, | |
992 client_, | |
993 buffer_ref->id, | |
994 offset)); | |
995 } | |
996 } | |
997 | |
998 bool ExynosVideoEncodeAccelerator::StartDevicePoll() { | |
999 DVLOG(3) << "StartDevicePoll()"; | |
1000 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current()); | |
1001 DCHECK(!device_poll_thread_.IsRunning()); | |
1002 | |
1003 // Start up the device poll thread and schedule its first DevicePollTask(). | |
1004 if (!device_poll_thread_.Start()) { | |
1005 DLOG(ERROR) << "StartDevicePoll(): Device thread failed to start"; | |
1006 NOTIFY_ERROR(kPlatformFailureError); | |
1007 return false; | |
1008 } | |
1009 device_poll_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( | |
1010 &ExynosVideoEncodeAccelerator::DevicePollTask, | |
1011 base::Unretained(this), | |
1012 0)); | |
1013 | |
1014 return true; | |
1015 } | |
1016 | |
1017 bool ExynosVideoEncodeAccelerator::StopDevicePoll() { | |
1018 DVLOG(3) << "StopDevicePoll()"; | |
1019 | |
1020 // Signal the DevicePollTask() to stop, and stop the device poll thread. | |
1021 if (!SetDevicePollInterrupt()) | |
1022 return false; | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
NOTIFY_ERROR?
| |
1023 device_poll_thread_.Stop(); | |
1024 // Clear the interrupt now, to be sure. | |
1025 if (!ClearDevicePollInterrupt()) | |
1026 return false; | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
NOTIFY_ERROR?
| |
1027 | |
1028 // Stop streaming. | |
1029 if (gsc_input_streamon_) { | |
1030 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
1031 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_STREAMOFF, &type); | |
1032 } | |
1033 gsc_input_streamon_ = false; | |
1034 if (gsc_output_streamon_) { | |
1035 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
1036 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_STREAMOFF, &type); | |
1037 } | |
1038 gsc_output_streamon_ = false; | |
1039 if (mfc_input_streamon_) { | |
1040 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
1041 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_STREAMOFF, &type); | |
1042 } | |
1043 mfc_input_streamon_ = false; | |
1044 if (mfc_output_streamon_) { | |
1045 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
1046 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_STREAMOFF, &type); | |
1047 } | |
1048 mfc_output_streamon_ = false; | |
1049 | |
1050 // Reset all our accounting info. | |
1051 encoder_input_queue_.clear(); | |
1052 gsc_free_input_buffers_.clear(); | |
1053 for (size_t i = 0; i < gsc_input_buffer_map_.size(); ++i) { | |
1054 gsc_free_input_buffers_.push_back(i); | |
1055 GscInputRecord& input_record = gsc_input_buffer_map_[i]; | |
1056 input_record.at_device = false; | |
1057 input_record.frame_id = -1; | |
1058 if (input_record.egl_sync) { | |
1059 eglDestroySyncKHR(egl_display_, input_record.egl_sync); | |
1060 input_record.egl_sync = EGL_NO_SYNC_KHR; | |
1061 } | |
1062 } | |
1063 gsc_input_buffer_queued_count_ = 0; | |
1064 gsc_free_output_buffers_.clear(); | |
1065 for (size_t i = 0; i < gsc_output_buffer_map_.size(); ++i) { | |
1066 gsc_free_output_buffers_.push_back(i); | |
1067 GscOutputRecord& output_record = gsc_output_buffer_map_[i]; | |
1068 output_record.at_device = false; | |
1069 output_record.mfc_input = -1; | |
1070 output_record.bytes_used[0] = output_record.bytes_used[1] | |
1071 = output_record.bytes_used[2] = 0; | |
1072 } | |
1073 gsc_output_buffer_queued_count_ = 0; | |
1074 gsc_output_mfc_input_queue_.clear(); | |
1075 mfc_free_input_buffers_.clear(); | |
1076 for (size_t i = 0; i < mfc_input_buffer_map_.size(); ++i) { | |
1077 mfc_free_input_buffers_.push_back(i); | |
1078 MfcInputRecord& input_record = mfc_input_buffer_map_[i]; | |
1079 input_record.at_device = false; | |
1080 } | |
1081 mfc_input_buffer_queued_count_ = 0; | |
1082 mfc_free_output_buffers_.clear(); | |
1083 for (size_t i = 0; i < mfc_output_buffer_map_.size(); ++i) { | |
1084 mfc_free_output_buffers_.push_back(i); | |
1085 MfcOutputRecord& output_record = mfc_output_buffer_map_[i]; | |
1086 output_record.at_device = false; | |
1087 output_record.bytes_used = 0; | |
1088 } | |
1089 encoder_output_queue_.clear(); | |
1090 | |
1091 DVLOG(3) << "StopDevicePoll(): device poll stopped"; | |
1092 return true; | |
1093 } | |
1094 | |
1095 bool ExynosVideoEncodeAccelerator::SetDevicePollInterrupt() { | |
1096 DVLOG(3) << "SetDevicePollInterrupt()"; | |
1097 | |
1098 const uint64 buf = 1; | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
Why uint64 and thus more than a byte and thus the
| |
1099 if (HANDLE_EINTR(write(device_poll_interrupt_fd_, &buf, sizeof(buf))) == -1) { | |
1100 DPLOG(ERROR) << "SetDevicePollInterrupt(): write() failed"; | |
1101 NOTIFY_ERROR(kPlatformFailureError); | |
1102 return false; | |
1103 } | |
1104 return true; | |
1105 } | |
1106 | |
1107 bool ExynosVideoEncodeAccelerator::ClearDevicePollInterrupt() { | |
1108 DVLOG(3) << "ClearDevicePollInterrupt()"; | |
1109 | |
1110 uint64 buf; | |
1111 if (HANDLE_EINTR(read(device_poll_interrupt_fd_, &buf, sizeof(buf))) == -1) { | |
1112 if (errno == EAGAIN) { | |
1113 // No interrupt flag set, and we're reading nonblocking. Not an error. | |
1114 return true; | |
1115 } else { | |
1116 DPLOG(ERROR) << "ClearDevicePollInterrupt(): read() failed"; | |
1117 NOTIFY_ERROR(kPlatformFailureError); | |
1118 return false; | |
1119 } | |
1120 } | |
1121 return true; | |
1122 } | |
1123 | |
1124 void ExynosVideoEncodeAccelerator::DevicePollTask(unsigned int poll_fds) { | |
1125 DVLOG(3) << "DevicePollTask()"; | |
1126 DCHECK_EQ(device_poll_thread_.message_loop(), base::MessageLoop::current()); | |
1127 TRACE_EVENT0("Video Encoder", "EVEA::DevicePollTask"); | |
1128 | |
1129 // This routine just polls the set of device fds, and schedules a | |
1130 // ServiceDeviceTask() on encoder_thread_ when processing needs to occur. | |
1131 // Other threads may notify this task to return early by writing to | |
1132 // device_poll_interrupt_fd_. | |
1133 struct pollfd pollfds[3]; | |
1134 nfds_t nfds; | |
1135 | |
1136 // Add device_poll_interrupt_fd_; | |
1137 pollfds[0].fd = device_poll_interrupt_fd_; | |
1138 pollfds[0].events = POLLIN | POLLERR; | |
1139 nfds = 1; | |
1140 | |
1141 // Add GSC fd, if we should poll on it. | |
1142 // GSC has to wait until both input and output buffers are queued. | |
1143 if (poll_fds & kPollGsc) { | |
1144 DVLOG(3) << "DevicePollTask(): adding GSC to poll() set"; | |
1145 pollfds[nfds].fd = gsc_fd_; | |
1146 pollfds[nfds].events = POLLIN | POLLOUT | POLLERR; | |
1147 nfds++; | |
1148 } | |
1149 if (poll_fds & kPollMfc) { | |
1150 DVLOG(3) << "DevicePollTask(): adding MFC to poll() set"; | |
1151 pollfds[nfds].fd = mfc_fd_; | |
1152 pollfds[nfds].events = POLLIN | POLLOUT | POLLERR; | |
1153 nfds++; | |
1154 } | |
1155 | |
1156 // Poll it! | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
Add a trace event to make obvious to about:tracing
| |
1157 if (HANDLE_EINTR(poll(pollfds, nfds, -1)) == -1) { | |
1158 DPLOG(ERROR) << "DevicePollTask(): poll() failed"; | |
1159 NOTIFY_ERROR(kPlatformFailureError); | |
1160 return; | |
1161 } | |
1162 | |
1163 // All processing should happen on ServiceDeviceTask(), since we shouldn't | |
1164 // touch encoder state from this thread. | |
1165 encoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( | |
1166 &ExynosVideoEncodeAccelerator::ServiceDeviceTask, | |
1167 base::Unretained(this))); | |
1168 } | |
1169 | |
1170 void ExynosVideoEncodeAccelerator::NotifyError(Error error) { | |
1171 DVLOG(2) << "NotifyError()"; | |
1172 | |
1173 if (!child_message_loop_proxy_->BelongsToCurrentThread()) { | |
1174 child_message_loop_proxy_->PostTask(FROM_HERE, base::Bind( | |
1175 &ExynosVideoEncodeAccelerator::NotifyError, weak_this_, error)); | |
1176 return; | |
1177 } | |
1178 | |
1179 if (client_) { | |
1180 client_->NotifyError(error); | |
1181 client_ptr_factory_.InvalidateWeakPtrs(); | |
1182 } | |
1183 } | |
1184 | |
1185 void ExynosVideoEncodeAccelerator::SetEncoderState(State state) { | |
1186 DVLOG(3) << "SetEncoderState(): state=" << state; | |
1187 | |
1188 // We can touch encoder_state_ only if this is the encoder thread or the | |
1189 // encoder thread isn't running. | |
1190 if (encoder_thread_.message_loop() != NULL && | |
1191 encoder_thread_.message_loop() != base::MessageLoop::current()) { | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
indent
| |
1192 encoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( | |
1193 &ExynosVideoEncodeAccelerator::SetEncoderState, | |
1194 base::Unretained(this), state)); | |
1195 } else { | |
1196 encoder_state_ = state; | |
1197 } | |
1198 } | |
1199 | |
1200 bool ExynosVideoEncodeAccelerator::CreateGscInputBuffers() { | |
1201 DVLOG(3) << "CreateGscInputBuffers()"; | |
1202 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread()); | |
1203 DCHECK_EQ(encoder_state_, kUninitialized); | |
1204 DCHECK(!gsc_input_streamon_); | |
1205 | |
1206 struct v4l2_control control; | |
1207 memset(&control, 0, sizeof(control)); | |
1208 control.id = V4L2_CID_ROTATE; | |
1209 control.value = 0; | |
1210 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_S_CTRL, &control); | |
1211 | |
1212 // HFLIP actually seems to control vertical mirroring for GSC, and vice-versa. | |
1213 memset(&control, 0, sizeof(control)); | |
1214 control.id = V4L2_CID_HFLIP; | |
1215 control.value = 1; | |
Pawel Osciak
2013/06/17 23:21:56
Is this because our textures are flipped?
| |
1216 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_S_CTRL, &control); | |
1217 | |
1218 memset(&control, 0, sizeof(control)); | |
1219 control.id = V4L2_CID_VFLIP; | |
1220 control.value = 0; | |
1221 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_S_CTRL, &control); | |
1222 | |
1223 memset(&control, 0, sizeof(control)); | |
1224 control.id = V4L2_CID_GLOBAL_ALPHA; | |
1225 control.value = 255; | |
1226 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_S_CTRL, &control); | |
1227 | |
1228 struct v4l2_format format; | |
1229 memset(&format, 0, sizeof(format)); | |
1230 format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
1231 format.fmt.pix_mp.width = input_allocated_size_.width(); | |
1232 format.fmt.pix_mp.height = input_allocated_size_.height(); | |
1233 format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_RGB32; | |
1234 format.fmt.pix_mp.plane_fmt[0].sizeimage = | |
1235 input_allocated_size_.GetArea() * 4; | |
1236 format.fmt.pix_mp.plane_fmt[0].bytesperline = | |
1237 input_allocated_size_.width() * 4; | |
1238 format.fmt.pix_mp.num_planes = 1; | |
1239 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_S_FMT, &format); | |
1240 | |
1241 struct v4l2_crop crop; | |
1242 memset(&crop, 0, sizeof(crop)); | |
1243 crop.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
1244 crop.c.left = 0; | |
1245 crop.c.top = 0; | |
1246 crop.c.width = input_visible_size_.width(); | |
1247 crop.c.height = input_visible_size_.height(); | |
1248 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_S_CROP, &crop); | |
1249 | |
1250 struct v4l2_requestbuffers reqbufs; | |
1251 memset(&reqbufs, 0, sizeof(reqbufs)); | |
1252 reqbufs.count = kGscInputBufferCount; | |
1253 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
1254 reqbufs.memory = V4L2_MEMORY_MMAP; | |
1255 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_REQBUFS, &reqbufs); | |
1256 | |
1257 DCHECK(gsc_input_buffer_map_.empty()); | |
1258 gsc_input_buffer_map_.resize(reqbufs.count); | |
1259 for (size_t i = 0; i < gsc_input_buffer_map_.size(); ++i) { | |
1260 GscInputRecord& input_record = gsc_input_buffer_map_[i]; | |
1261 if (do_encode_from_backbuffer_) { | |
1262 // We have to export textures from the GSC input buffers so we can | |
1263 // glCopyTexSubImage2D() to them. | |
1264 struct v4l2_exportbuffer expbuf; | |
1265 memset(&expbuf, 0, sizeof(expbuf)); | |
1266 expbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
1267 expbuf.index = i; | |
1268 expbuf.plane = 0; | |
1269 expbuf.flags = O_CLOEXEC; | |
1270 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_EXPBUF, &expbuf); | |
1271 file_util::ScopedFD autofd(&expbuf.fd); | |
1272 | |
1273 EGLint attrs[13]; | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
Why not
EGLint attrs[] = {
EGL_WIDTH, i_a_s_.wid
| |
1274 { | |
1275 size_t j = 0; | |
1276 attrs[j++] = EGL_WIDTH; | |
1277 attrs[j++] = input_allocated_size_.width(); | |
1278 attrs[j++] = EGL_HEIGHT; | |
1279 attrs[j++] = input_allocated_size_.height(); | |
1280 attrs[j++] = EGL_LINUX_DRM_FOURCC_EXT; | |
1281 attrs[j++] = DRM_FORMAT_XRGB8888; | |
1282 attrs[j++] = EGL_DMA_BUF_PLANE0_FD_EXT; | |
1283 attrs[j++] = expbuf.fd; | |
1284 attrs[j++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; | |
1285 attrs[j++] = 0; | |
1286 attrs[j++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; | |
1287 attrs[j++] = input_allocated_size_.width() * 4; | |
1288 attrs[j++] = EGL_NONE; | |
1289 DCHECK_EQ(j, arraysize(attrs)); | |
1290 } | |
1291 input_record.egl_image = eglCreateImageKHR( | |
1292 egl_display_, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, attrs); | |
1293 if (input_record.egl_image == EGL_NO_IMAGE_KHR) { | |
1294 DLOG(ERROR) << "CreateGscInputBuffers(): could not create EGLImageKHR"; | |
1295 NOTIFY_ERROR(kPlatformFailureError); | |
1296 return false; | |
1297 } | |
1298 glGenTextures(1, &input_record.texture_id); | |
1299 if (input_record.texture_id == 0) { | |
1300 DLOG(ERROR) << "CreateGscInputBuffers(): glGenTextures() failed"; | |
1301 NOTIFY_ERROR(kPlatformFailureError); | |
1302 return false; | |
1303 } | |
1304 gfx::ScopedTextureBinder binder(GL_TEXTURE_2D, input_record.texture_id); | |
1305 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, input_record.egl_image); | |
1306 } | |
1307 gsc_free_input_buffers_.push_back(i); | |
1308 } | |
1309 | |
1310 return true; | |
1311 } | |
1312 | |
1313 bool ExynosVideoEncodeAccelerator::CreateGscOutputBuffers() { | |
1314 DVLOG(3) << "CreateGscOutputBuffers()"; | |
1315 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread()); | |
1316 DCHECK_EQ(encoder_state_, kUninitialized); | |
1317 DCHECK(!gsc_output_streamon_); | |
1318 | |
1319 struct v4l2_format format; | |
1320 memset(&format, 0, sizeof(format)); | |
1321 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
1322 format.fmt.pix_mp.width = converted_allocated_size_.width(); | |
1323 format.fmt.pix_mp.height = converted_allocated_size_.height(); | |
1324 if (do_output_encoding_) { | |
1325 format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M; | |
1326 format.fmt.pix_mp.plane_fmt[0].sizeimage = | |
1327 converted_allocated_size_.GetArea(); | |
1328 format.fmt.pix_mp.plane_fmt[1].sizeimage = | |
1329 converted_allocated_size_.GetArea() / 2; | |
1330 format.fmt.pix_mp.plane_fmt[0].bytesperline = | |
1331 converted_allocated_size_.width(); | |
1332 format.fmt.pix_mp.plane_fmt[1].bytesperline = | |
1333 converted_allocated_size_.width(); | |
1334 format.fmt.pix_mp.num_planes = 2; | |
1335 } else { | |
1336 format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420M; | |
1337 format.fmt.pix_mp.plane_fmt[0].sizeimage = | |
1338 converted_allocated_size_.GetArea(); | |
1339 format.fmt.pix_mp.plane_fmt[1].sizeimage = | |
1340 converted_allocated_size_.GetArea() / 4; | |
1341 format.fmt.pix_mp.plane_fmt[2].sizeimage = | |
1342 converted_allocated_size_.GetArea() / 4; | |
1343 format.fmt.pix_mp.plane_fmt[0].bytesperline = | |
1344 converted_allocated_size_.width(); | |
1345 format.fmt.pix_mp.plane_fmt[1].bytesperline = | |
1346 converted_allocated_size_.width() / 2; | |
1347 format.fmt.pix_mp.plane_fmt[2].bytesperline = | |
1348 converted_allocated_size_.width() / 2; | |
1349 format.fmt.pix_mp.num_planes = 3; | |
1350 output_buffer_byte_size_ = output_visible_size_.GetArea() * 3 / 2; | |
1351 } | |
1352 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_S_FMT, &format); | |
1353 | |
1354 struct v4l2_crop crop; | |
1355 memset(&crop, 0, sizeof(crop)); | |
1356 crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
1357 crop.c.left = 0; | |
1358 crop.c.top = 0; | |
1359 crop.c.width = output_visible_size_.width(); | |
1360 crop.c.height = output_visible_size_.height(); | |
1361 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_S_CROP, &crop); | |
1362 | |
1363 struct v4l2_requestbuffers reqbufs; | |
1364 memset(&reqbufs, 0, sizeof(reqbufs)); | |
1365 reqbufs.count = kGscOutputBufferCount; | |
1366 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
1367 reqbufs.memory = (do_output_encoding_ ? | |
1368 V4L2_MEMORY_DMABUF : V4L2_MEMORY_MMAP); | |
1369 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_REQBUFS, &reqbufs); | |
1370 | |
1371 DCHECK(gsc_output_buffer_map_.empty()); | |
1372 gsc_output_buffer_map_.resize(reqbufs.count); | |
1373 for (size_t i = 0; i < gsc_output_buffer_map_.size(); ++i) { | |
1374 gsc_free_output_buffers_.push_back(i); | |
1375 GscOutputRecord& output_record = gsc_output_buffer_map_[i]; | |
1376 if (!do_output_encoding_) { | |
1377 // Query for the MEMORY_MMAP pointer. | |
1378 struct v4l2_plane planes[arraysize(output_record.address)]; | |
1379 struct v4l2_buffer buffer; | |
1380 memset(&buffer, 0, sizeof(buffer)); | |
1381 memset(planes, 0, sizeof(planes)); | |
1382 buffer.index = i; | |
1383 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
1384 buffer.memory = V4L2_MEMORY_MMAP; | |
1385 buffer.m.planes = planes; | |
1386 buffer.length = arraysize(output_record.address); | |
Pawel Osciak
2013/06/17 23:21:56
s/arraysize(output_record.address)/arraysize(plane
| |
1387 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_QUERYBUF, &buffer); | |
1388 for (size_t j = 0; j < arraysize(output_record.address); ++j) { | |
1389 void* address = mmap(NULL, buffer.m.planes[j].length, | |
1390 PROT_READ | PROT_WRITE, MAP_SHARED, gsc_fd_, | |
1391 buffer.m.planes[j].m.mem_offset); | |
1392 if (address == MAP_FAILED) { | |
1393 DPLOG(ERROR) << "CreateGscOutputBuffers(): mmap() failed"; | |
1394 return false; | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
NOTIFY_ERROR?
| |
1395 } | |
1396 output_record.address[j] = address; | |
1397 output_record.length[j] = buffer.m.planes[j].length; | |
1398 } | |
1399 } | |
1400 } | |
1401 return true; | |
1402 } | |
1403 | |
1404 bool ExynosVideoEncodeAccelerator::SetMfcFormats() { | |
1405 DVLOG(3) << "SetMfcFormats()"; | |
1406 DCHECK(!mfc_input_streamon_); | |
1407 DCHECK(!mfc_output_streamon_); | |
1408 DCHECK(do_output_encoding_); | |
1409 | |
1410 // VIDIOC_S_FMT on OUTPUT queue. | |
1411 struct v4l2_format format; | |
1412 memset(&format, 0, sizeof(format)); | |
1413 format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
1414 format.fmt.pix_mp.width = converted_visible_size_.width(); | |
Pawel Osciak
2013/06/17 23:21:56
All those sizes confuse me to no end, unfortunatel
| |
1415 format.fmt.pix_mp.height = converted_visible_size_.height(); | |
1416 format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M; | |
1417 format.fmt.pix_mp.num_planes = 2; | |
1418 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_S_FMT, &format); | |
1419 // We read direct from GSC, so we rely on the HW not changing our set | |
1420 // size/stride. | |
1421 DCHECK_EQ(format.fmt.pix_mp.plane_fmt[0].sizeimage, | |
1422 static_cast<__u32>(converted_allocated_size_.GetArea())); | |
1423 DCHECK_EQ(format.fmt.pix_mp.plane_fmt[0].bytesperline, | |
1424 static_cast<__u32>(converted_allocated_size_.width())); | |
1425 DCHECK_EQ(format.fmt.pix_mp.plane_fmt[1].sizeimage, | |
1426 static_cast<__u32>(converted_allocated_size_.GetArea() / 2)); | |
1427 DCHECK_EQ(format.fmt.pix_mp.plane_fmt[1].bytesperline, | |
1428 static_cast<__u32>(converted_allocated_size_.width())); | |
1429 | |
1430 struct v4l2_crop crop; | |
1431 memset(&crop, 0, sizeof(crop)); | |
1432 crop.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; | |
1433 crop.c.left = 0; | |
1434 crop.c.top = 0; | |
1435 crop.c.width = converted_visible_size_.width(); | |
1436 crop.c.height = converted_visible_size_.height(); | |
1437 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_S_CROP, &crop); | |
1438 | |
1439 // VIDIOC_S_FMT on CAPTURE queue. | |
1440 output_buffer_byte_size_ = kMfcOutputBufferSize; | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
Move to l.1335 to mirror the else clause up there?
| |
1441 __u32 pixelformat = 0; | |
1442 if (video_profile_ >= media::H264PROFILE_MIN && | |
1443 video_profile_ <= media::H264PROFILE_MAX) { | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
Elsewhere you only accept Main for h264, so this i
| |
1444 pixelformat = V4L2_PIX_FMT_H264; | |
1445 } | |
1446 memset(&format, 0, sizeof(format)); | |
1447 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
1448 format.fmt.pix_mp.width = output_visible_size_.width(); | |
1449 format.fmt.pix_mp.height = output_visible_size_.height(); | |
1450 format.fmt.pix_mp.pixelformat = pixelformat; | |
1451 format.fmt.pix_mp.plane_fmt[0].sizeimage = output_buffer_byte_size_; | |
1452 format.fmt.pix_mp.num_planes = 1; | |
1453 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_S_FMT, &format); | |
1454 | |
1455 struct v4l2_ext_control ctrls[3]; | |
1456 struct v4l2_ext_controls control; | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
the type+name combos of the previous two lines are
| |
1457 memset(&ctrls, 0, sizeof(ctrls)); | |
1458 memset(&control, 0, sizeof(control)); | |
1459 ctrls[0].id = V4L2_CID_MPEG_VIDEO_B_FRAMES; | |
1460 ctrls[0].value = 0; | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
does this mean no B frames? (why do that?)
| |
1461 ctrls[1].id = V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE; | |
1462 ctrls[1].value = 1; | |
1463 ctrls[2].id = V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF; | |
1464 ctrls[2].value = 9; | |
1465 control.ctrl_class = V4L2_CTRL_CLASS_MPEG; | |
1466 control.count = 3; | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
I guess doco all these encoding params?
| |
1467 control.controls = ctrls; | |
1468 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_S_EXT_CTRLS, &control); | |
1469 | |
1470 struct v4l2_streamparm parms; | |
1471 memset(&parms, 0, sizeof(parms)); | |
1472 parms.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
1473 parms.parm.output.timeperframe.numerator = 1000; | |
Pawel Osciak
2013/06/17 23:21:56
I'm surprised this is required. Why?
| |
1474 parms.parm.output.timeperframe.denominator = 25; | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
Is this hard-coding 40fps framerate? Frame durati
| |
1475 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_S_PARM, &parms); | |
1476 | |
1477 return true; | |
1478 } | |
1479 | |
1480 bool ExynosVideoEncodeAccelerator::CreateMfcInputBuffers() { | |
1481 DVLOG(3) << "CreateMfcInputBuffers()"; | |
1482 DCHECK(!mfc_input_streamon_); | |
1483 DCHECK(do_output_encoding_); | |
1484 | |
1485 struct v4l2_requestbuffers reqbufs; | |
1486 memset(&reqbufs, 0, sizeof(reqbufs)); | |
1487 reqbufs.count = 1; | |
1488 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
1489 reqbufs.memory = V4L2_MEMORY_MMAP; | |
1490 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_REQBUFS, &reqbufs); | |
1491 | |
1492 DCHECK(mfc_input_buffer_map_.empty()); | |
1493 mfc_input_buffer_map_.resize(reqbufs.count); | |
1494 for (size_t i = 0; i < mfc_input_buffer_map_.size(); ++i) { | |
1495 mfc_free_input_buffers_.push_back(i); | |
1496 MfcInputRecord& input_record = mfc_input_buffer_map_[i]; | |
1497 for (size_t j = 0; j < 2; ++j) { | |
1498 struct v4l2_exportbuffer expbuf; | |
1499 memset(&expbuf, 0, sizeof(expbuf)); | |
1500 expbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
1501 expbuf.index = i; | |
1502 expbuf.plane = j; | |
1503 expbuf.flags = O_CLOEXEC; | |
1504 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_EXPBUF, &expbuf); | |
1505 input_record.fd[j] = expbuf.fd; | |
1506 } | |
1507 } | |
1508 return true; | |
1509 } | |
1510 | |
1511 bool ExynosVideoEncodeAccelerator::CreateMfcOutputBuffers() { | |
1512 DVLOG(3) << "CreateMfcOutputBuffers()"; | |
1513 DCHECK(!mfc_output_streamon_); | |
1514 DCHECK(do_output_encoding_); | |
1515 | |
1516 struct v4l2_requestbuffers reqbufs; | |
1517 memset(&reqbufs, 0, sizeof(reqbufs)); | |
1518 reqbufs.count = kMfcOutputBufferCount; | |
1519 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
1520 reqbufs.memory = V4L2_MEMORY_MMAP; | |
1521 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_REQBUFS, &reqbufs); | |
1522 | |
1523 DCHECK(mfc_output_buffer_map_.empty()); | |
1524 mfc_output_buffer_map_.resize(reqbufs.count); | |
1525 for (size_t i = 0; i < mfc_output_buffer_map_.size(); ++i) { | |
1526 mfc_free_output_buffers_.push_back(i); | |
1527 MfcOutputRecord& output_record = mfc_output_buffer_map_[i]; | |
1528 // Query for the MEMORY_MMAP pointer. | |
1529 struct v4l2_plane planes[1]; | |
1530 struct v4l2_buffer buffer; | |
1531 memset(&buffer, 0, sizeof(buffer)); | |
1532 memset(planes, 0, sizeof(planes)); | |
1533 buffer.index = i; | |
1534 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
1535 buffer.memory = V4L2_MEMORY_MMAP; | |
1536 buffer.m.planes = planes; | |
1537 buffer.length = 1; | |
1538 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_QUERYBUF, &buffer); | |
1539 void* address = mmap(NULL, buffer.m.planes[0].length, | |
1540 PROT_READ | PROT_WRITE, MAP_SHARED, mfc_fd_, | |
1541 buffer.m.planes[0].m.mem_offset); | |
1542 if (address == MAP_FAILED) { | |
1543 DPLOG(ERROR) << "CreateMfcOutputBuffers(): mmap() failed"; | |
1544 return false; | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
NOTIFY_ERROR?
| |
1545 } | |
1546 output_record.address = address; | |
1547 output_record.length = buffer.m.planes[0].length; | |
1548 } | |
1549 return true; | |
1550 } | |
1551 | |
1552 void ExynosVideoEncodeAccelerator::SetBitrate(int32 bitrate) { | |
1553 DVLOG(3) << "SetBitrate(): bitrate=" << bitrate; | |
1554 DCHECK(do_output_encoding_); | |
1555 | |
1556 struct v4l2_ext_control ctrls[1]; | |
1557 struct v4l2_ext_controls control; | |
1558 memset(&ctrls, 0, sizeof(ctrls)); | |
1559 memset(&control, 0, sizeof(control)); | |
1560 ctrls[0].id = V4L2_CID_MPEG_VIDEO_BITRATE; | |
1561 ctrls[0].value = bitrate; | |
1562 control.ctrl_class = V4L2_CTRL_CLASS_MPEG; | |
1563 control.count = 1; | |
1564 control.controls = ctrls; | |
1565 IOCTL_OR_ERROR_RETURN(mfc_fd_, VIDIOC_S_EXT_CTRLS, &control); | |
Pawel Osciak
2013/06/17 23:21:56
Is not changing the bitrate a noncritical error th
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
v4l2 is thread safe - it's legit to ioctl mfc_fd_
| |
1566 } | |
1567 | |
1568 void ExynosVideoEncodeAccelerator::DestroyGscInputBuffers() { | |
1569 DVLOG(3) << "DestroyGscInputBuffers()"; | |
1570 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread()); | |
1571 DCHECK(!gsc_input_streamon_); | |
1572 | |
1573 for (size_t i = 0; i < gsc_input_buffer_map_.size(); ++i) { | |
1574 GscInputRecord& input_record = gsc_input_buffer_map_[i]; | |
1575 if (input_record.egl_sync != EGL_NO_SYNC_KHR) | |
1576 eglDestroySyncKHR(egl_display_, input_record.egl_sync); | |
1577 if (input_record.egl_image != EGL_NO_IMAGE_KHR) | |
1578 eglDestroyImageKHR(egl_display_, input_record.egl_image); | |
1579 if (input_record.texture_id != 0) | |
1580 glDeleteTextures(1, &input_record.texture_id); | |
1581 } | |
1582 | |
1583 struct v4l2_requestbuffers reqbufs; | |
1584 memset(&reqbufs, 0, sizeof(reqbufs)); | |
1585 reqbufs.count = 0; | |
1586 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
1587 reqbufs.memory = V4L2_MEMORY_MMAP; | |
1588 if (ioctl(gsc_fd_, VIDIOC_REQBUFS, &reqbufs) != 0) | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
HANDLE_EINTR?
| |
1589 DPLOG(ERROR) << "DestroyGscInputBuffers(): ioctl() failed: VIDIOC_REQBUFS"; | |
1590 | |
1591 gsc_input_buffer_map_.clear(); | |
1592 gsc_free_input_buffers_.clear(); | |
1593 } | |
1594 | |
1595 void ExynosVideoEncodeAccelerator::DestroyGscOutputBuffers() { | |
1596 DVLOG(3) << "DestroyGscOutputBuffers()"; | |
1597 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread()); | |
1598 DCHECK(!gsc_output_streamon_); | |
1599 | |
1600 for (size_t i = 0; i < gsc_output_buffer_map_.size(); ++i) { | |
1601 GscOutputRecord& output_record = gsc_output_buffer_map_[i]; | |
1602 for (size_t j = 0; j < arraysize(output_record.address); ++j) { | |
1603 if (output_record.address[j] != NULL) { | |
1604 HANDLE_EINTR(munmap(output_record.address[j], output_record.length[j])); | |
1605 } | |
1606 } | |
1607 } | |
1608 | |
1609 struct v4l2_requestbuffers reqbufs; | |
1610 memset(&reqbufs, 0, sizeof(reqbufs)); | |
1611 reqbufs.count = 0; | |
1612 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
1613 reqbufs.memory = do_output_encoding_ ? V4L2_MEMORY_DMABUF : V4L2_MEMORY_MMAP; | |
1614 if (ioctl(gsc_fd_, VIDIOC_REQBUFS, &reqbufs) != 0) | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
HANDLE_EINTR?
| |
1615 DPLOG(ERROR) << "DestroyGscOutputBuffers(): ioctl() failed: VIDIOC_REQBUFS"; | |
1616 | |
1617 gsc_output_buffer_map_.clear(); | |
1618 gsc_free_output_buffers_.clear(); | |
1619 } | |
1620 | |
1621 void ExynosVideoEncodeAccelerator::DestroyMfcInputBuffers() { | |
1622 DVLOG(3) << "DestroyMfcInputBuffers()"; | |
1623 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread()); | |
1624 DCHECK(do_output_encoding_); | |
1625 DCHECK(!mfc_input_streamon_); | |
1626 | |
1627 for (size_t i = 0; i < mfc_input_buffer_map_.size(); ++i) { | |
1628 MfcInputRecord& input_record = mfc_input_buffer_map_[i]; | |
1629 for (size_t j = 0; j < arraysize(input_record.fd); ++j) { | |
1630 if (input_record.fd[j] != -1) { | |
1631 HANDLE_EINTR(close(input_record.fd[j])); | |
1632 } | |
1633 } | |
1634 } | |
1635 | |
1636 struct v4l2_requestbuffers reqbufs; | |
1637 memset(&reqbufs, 0, sizeof(reqbufs)); | |
1638 reqbufs.count = 0; | |
1639 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
1640 reqbufs.memory = V4L2_MEMORY_MMAP; | |
1641 if (ioctl(mfc_fd_, VIDIOC_REQBUFS, &reqbufs) != 0) | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
HANDLE_EINTR?
| |
1642 DPLOG(ERROR) << "DestroyMfcInputBuffers(): ioctl() failed: VIDIOC_REQBUFS"; | |
1643 | |
1644 mfc_input_buffer_map_.clear(); | |
1645 mfc_free_input_buffers_.clear(); | |
1646 } | |
1647 | |
1648 void ExynosVideoEncodeAccelerator::DestroyMfcOutputBuffers() { | |
1649 DVLOG(3) << "DestroyMfcOutputBuffers()"; | |
1650 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread()); | |
1651 DCHECK(do_output_encoding_); | |
1652 DCHECK(!mfc_output_streamon_); | |
1653 | |
1654 for (size_t i = 0; i < mfc_output_buffer_map_.size(); ++i) { | |
1655 MfcOutputRecord& output_record = mfc_output_buffer_map_[i]; | |
1656 if (output_record.address != NULL) | |
1657 HANDLE_EINTR(munmap(output_record.address, output_record.length)); | |
1658 } | |
1659 | |
1660 struct v4l2_requestbuffers reqbufs; | |
1661 memset(&reqbufs, 0, sizeof(reqbufs)); | |
1662 reqbufs.count = 0; | |
1663 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
1664 reqbufs.memory = V4L2_MEMORY_MMAP; | |
1665 if (ioctl(mfc_fd_, VIDIOC_REQBUFS, &reqbufs) != 0) | |
Ami GONE FROM CHROMIUM
2013/06/18 01:09:25
HANDLE_EINTR?
| |
1666 DPLOG(ERROR) << "DestroyMfcOutputBuffers(): ioctl() failed: VIDIOC_REQBUFS"; | |
1667 | |
1668 mfc_output_buffer_map_.clear(); | |
1669 mfc_free_output_buffers_.clear(); | |
1670 } | |
1671 | |
1672 } // namespace content | |
OLD | NEW |