 Chromium Code Reviews
 Chromium Code Reviews Issue 137023008:
  Add support for Tegra V4L2 VDA  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master
    
  
    Issue 137023008:
  Add support for Tegra V4L2 VDA  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master| Index: content/common/gpu/media/v4l2_video_decode_accelerator.cc | 
| diff --git a/content/common/gpu/media/v4l2_video_decode_accelerator.cc b/content/common/gpu/media/v4l2_video_decode_accelerator.cc | 
| index 587da6bbc04ed5449ccadf7bf00f693f9273d3e4..aec28770495d866fbda6ced843467b02cedc9491 100644 | 
| --- a/content/common/gpu/media/v4l2_video_decode_accelerator.cc | 
| +++ b/content/common/gpu/media/v4l2_video_decode_accelerator.cc | 
| @@ -5,7 +5,6 @@ | 
| #include <dlfcn.h> | 
| #include <errno.h> | 
| #include <fcntl.h> | 
| -#include <libdrm/drm_fourcc.h> | 
| #include <linux/videodev2.h> | 
| #include <poll.h> | 
| #include <sys/eventfd.h> | 
| @@ -17,6 +16,7 @@ | 
| #include "base/memory/shared_memory.h" | 
| #include "base/message_loop/message_loop.h" | 
| #include "base/message_loop/message_loop_proxy.h" | 
| +#include "base/numerics/safe_conversions.h" | 
| #include "base/posix/eintr_wrapper.h" | 
| #include "content/common/gpu/media/v4l2_video_decode_accelerator.h" | 
| #include "media/filters/h264_parser.h" | 
| @@ -139,8 +139,6 @@ V4L2VideoDecodeAccelerator::OutputRecord::OutputRecord() | 
| egl_sync(EGL_NO_SYNC_KHR), | 
| picture_id(-1), | 
| cleared(false) { | 
| - for (size_t i = 0; i < arraysize(fds); ++i) | 
| - fds[i] = -1; | 
| } | 
| V4L2VideoDecodeAccelerator::OutputRecord::~OutputRecord() {} | 
| @@ -184,7 +182,8 @@ V4L2VideoDecodeAccelerator::V4L2VideoDecodeAccelerator( | 
| device_poll_thread_("V4L2DevicePollThread"), | 
| make_context_current_(make_context_current), | 
| egl_display_(egl_display), | 
| - video_profile_(media::VIDEO_CODEC_PROFILE_UNKNOWN) {} | 
| + video_profile_(media::VIDEO_CODEC_PROFILE_UNKNOWN), | 
| + output_planes_count_(0) {} | 
| V4L2VideoDecodeAccelerator::~V4L2VideoDecodeAccelerator() { | 
| DCHECK(!decoder_thread_.IsRunning()); | 
| @@ -267,7 +266,7 @@ bool V4L2VideoDecodeAccelerator::Initialize(media::VideoCodecProfile profile, | 
| struct v4l2_format format; | 
| memset(&format, 0, sizeof(format)); | 
| format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | 
| - format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M; | 
| + format.fmt.pix_mp.pixelformat = device_->PreferredOutputFormat(); | 
| IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT, &format); | 
| // Subscribe to the resolution change event. | 
| @@ -330,15 +329,6 @@ void V4L2VideoDecodeAccelerator::AssignPictureBuffers( | 
| } | 
| gfx::ScopedTextureBinder bind_restore(GL_TEXTURE_EXTERNAL_OES, 0); | 
| - EGLint attrs[] = { | 
| - EGL_WIDTH, 0, EGL_HEIGHT, 0, | 
| - EGL_LINUX_DRM_FOURCC_EXT, 0, EGL_DMA_BUF_PLANE0_FD_EXT, 0, | 
| - EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0, EGL_DMA_BUF_PLANE0_PITCH_EXT, 0, | 
| - EGL_DMA_BUF_PLANE1_FD_EXT, 0, EGL_DMA_BUF_PLANE1_OFFSET_EXT, 0, | 
| - EGL_DMA_BUF_PLANE1_PITCH_EXT, 0, EGL_NONE, }; | 
| - attrs[1] = frame_buffer_size_.width(); | 
| - attrs[3] = frame_buffer_size_.height(); | 
| - attrs[5] = DRM_FORMAT_NV12; | 
| // It's safe to manipulate all the buffer state here, because the decoder | 
| // thread is waiting on pictures_assigned_. | 
| @@ -354,15 +344,8 @@ void V4L2VideoDecodeAccelerator::AssignPictureBuffers( | 
| DCHECK_EQ(output_record.picture_id, -1); | 
| DCHECK_EQ(output_record.cleared, false); | 
| - attrs[7] = output_record.fds[0]; | 
| - attrs[9] = 0; | 
| - attrs[11] = frame_buffer_size_.width(); | 
| - attrs[13] = output_record.fds[1]; | 
| - attrs[15] = 0; | 
| - attrs[17] = frame_buffer_size_.width(); | 
| - | 
| - EGLImageKHR egl_image = eglCreateImageKHR( | 
| - egl_display_, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, attrs); | 
| + EGLImageKHR egl_image = device_->CreateEGLImage( | 
| + egl_display_, buffers[i].texture_id(), frame_buffer_size_, i); | 
| if (egl_image == EGL_NO_IMAGE_KHR) { | 
| DLOG(ERROR) << "AssignPictureBuffers(): could not create EGLImageKHR"; | 
| // Ownership of EGLImages allocated in previous iterations of this loop | 
| @@ -372,9 +355,6 @@ void V4L2VideoDecodeAccelerator::AssignPictureBuffers( | 
| return; | 
| } | 
| - glBindTexture(GL_TEXTURE_EXTERNAL_OES, buffers[i].texture_id()); | 
| - glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, egl_image); | 
| - | 
| output_record.egl_image = egl_image; | 
| output_record.picture_id = buffers[i].id(); | 
| free_output_buffers_.push(i); | 
| @@ -1005,7 +985,7 @@ void V4L2VideoDecodeAccelerator::DequeueEvents() { | 
| if (ev.type == V4L2_EVENT_RESOLUTION_CHANGE) { | 
| DVLOG(3) << "DequeueEvents(): got resolution change event."; | 
| DCHECK(!resolution_change_pending_); | 
| - resolution_change_pending_ = true; | 
| + resolution_change_pending_ = IsResolutionChangeNecessary(); | 
| } else { | 
| DLOG(FATAL) << "DequeueEvents(): got an event (" << ev.type | 
| << ") we haven't subscribed to."; | 
| @@ -1154,14 +1134,14 @@ bool V4L2VideoDecodeAccelerator::EnqueueOutputRecord() { | 
| output_record.egl_sync = EGL_NO_SYNC_KHR; | 
| } | 
| struct v4l2_buffer qbuf; | 
| - struct v4l2_plane qbuf_planes[arraysize(output_record.fds)]; | 
| + struct v4l2_plane qbuf_planes[output_planes_count_]; | 
| 
Pawel Osciak
2014/03/28 06:21:46
I don't think this is valid C++, it's an extension
 
shivdasp
2014/03/28 06:40:43
Will fix this in next patchset.
On 2014/03/28 06:2
 | 
| memset(&qbuf, 0, sizeof(qbuf)); | 
| memset(qbuf_planes, 0, sizeof(qbuf_planes)); | 
| qbuf.index = buffer; | 
| qbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | 
| qbuf.memory = V4L2_MEMORY_MMAP; | 
| qbuf.m.planes = qbuf_planes; | 
| - qbuf.length = arraysize(output_record.fds); | 
| + qbuf.length = output_planes_count_; | 
| IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF, &qbuf); | 
| free_output_buffers_.pop(); | 
| output_record.at_device = true; | 
| @@ -1536,7 +1516,6 @@ void V4L2VideoDecodeAccelerator::FinishResolutionChange() { | 
| NOTIFY_ERROR(PLATFORM_FAILURE); | 
| return; | 
| } | 
| - | 
| 
Pawel Osciak
2014/03/28 06:21:46
Let's not remove this please.
 
shivdasp
2014/03/28 06:40:43
Done.
 | 
| if (!CreateBuffersForFormat(format)) { | 
| DVLOG(3) << "Couldn't reallocate buffers after resolution change"; | 
| NOTIFY_ERROR(PLATFORM_FAILURE); | 
| @@ -1632,11 +1611,11 @@ bool V4L2VideoDecodeAccelerator::GetFormatInfo(struct v4l2_format* format, | 
| bool V4L2VideoDecodeAccelerator::CreateBuffersForFormat( | 
| const struct v4l2_format& format) { | 
| DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current()); | 
| - CHECK_EQ(format.fmt.pix_mp.num_planes, 2); | 
| + output_planes_count_ = format.fmt.pix_mp.num_planes; | 
| frame_buffer_size_.SetSize( | 
| format.fmt.pix_mp.width, format.fmt.pix_mp.height); | 
| output_buffer_pixelformat_ = format.fmt.pix_mp.pixelformat; | 
| - DCHECK_EQ(output_buffer_pixelformat_, V4L2_PIX_FMT_NV12M); | 
| + DCHECK_EQ(output_buffer_pixelformat_, device_->PreferredOutputFormat()); | 
| DVLOG(3) << "CreateBuffersForFormat(): new resolution: " | 
| << frame_buffer_size_.ToString(); | 
| @@ -1733,22 +1712,7 @@ bool V4L2VideoDecodeAccelerator::CreateOutputBuffers() { | 
| reqbufs.memory = V4L2_MEMORY_MMAP; | 
| IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS, &reqbufs); | 
| - // Create DMABUFs from output buffers. | 
| output_buffer_map_.resize(reqbufs.count); | 
| - for (size_t i = 0; i < output_buffer_map_.size(); ++i) { | 
| - OutputRecord& output_record = output_buffer_map_[i]; | 
| - for (size_t j = 0; j < arraysize(output_record.fds); ++j) { | 
| - // Export the DMABUF fd so we can export it as a texture. | 
| - struct v4l2_exportbuffer expbuf; | 
| - memset(&expbuf, 0, sizeof(expbuf)); | 
| - expbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | 
| - expbuf.index = i; | 
| - expbuf.plane = j; | 
| - expbuf.flags = O_CLOEXEC; | 
| - IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_EXPBUF, &expbuf); | 
| - output_record.fds[j] = expbuf.fd; | 
| - } | 
| - } | 
| DVLOG(3) << "CreateOutputBuffers(): ProvidePictureBuffers(): " | 
| << "buffer_count=" << output_buffer_map_.size() | 
| @@ -1759,7 +1723,7 @@ bool V4L2VideoDecodeAccelerator::CreateOutputBuffers() { | 
| client_, | 
| output_buffer_map_.size(), | 
| frame_buffer_size_, | 
| - GL_TEXTURE_EXTERNAL_OES)); | 
| + device_->GetTextureTarget())); | 
| // Wait for the client to call AssignPictureBuffers() on the Child thread. | 
| // We do this, because if we continue decoding without finishing buffer | 
| @@ -1812,16 +1776,9 @@ bool V4L2VideoDecodeAccelerator::DestroyOutputBuffers() { | 
| for (size_t i = 0; i < output_buffer_map_.size(); ++i) { | 
| OutputRecord& output_record = output_buffer_map_[i]; | 
| - for (size_t j = 0; j < arraysize(output_record.fds); ++j) { | 
| - if (output_record.fds[j] != -1) { | 
| - if (close(output_record.fds[j])) { | 
| - DVPLOG(1) << __func__ << " close() on a dmabuf fd failed."; | 
| - success = false; | 
| - } | 
| - } | 
| - } | 
| + | 
| if (output_record.egl_image != EGL_NO_IMAGE_KHR) { | 
| - if (eglDestroyImageKHR(egl_display_, output_record.egl_image) != | 
| + if (device_->DestroyEGLImage(egl_display_, output_record.egl_image) != | 
| EGL_TRUE) { | 
| DVLOG(1) << __func__ << " eglDestroyImageKHR failed."; | 
| 
Pawel Osciak
2014/03/28 06:21:46
Please update the comment.
 
shivdasp
2014/03/28 06:40:43
Will fix in next patchset.
 | 
| success = false; | 
| @@ -1926,4 +1883,33 @@ void V4L2VideoDecodeAccelerator::PictureCleared() { | 
| SendPictureReady(); | 
| } | 
| +bool V4L2VideoDecodeAccelerator::IsResolutionChangeNecessary() { | 
| + if (frame_buffer_size_.IsEmpty()) | 
| 
Pawel Osciak
2014/03/28 06:21:46
We need to verify that GetFormatInfo doesn't fail
 
shivdasp
2014/03/28 06:40:43
This basically is used to trigger Decoder initiali
 
Pawel Osciak
2014/03/28 06:52:34
Yes.
 
shivdasp
2014/03/28 07:00:19
GetFormatInfo() can fail if the asynchronos decode
 
Pawel Osciak
2014/03/28 07:25:53
But then the driver should not send the event if i
 
shivdasp
2014/03/28 08:00:34
Yes you are right, this if frame_buffer_size_.IsEm
 | 
| + return true; | 
| + | 
| + struct v4l2_control ctrl; | 
| + memset(&ctrl, 0, sizeof(ctrl)); | 
| + ctrl.id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE; | 
| + IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_G_CTRL, &ctrl); | 
| + if (ctrl.value != output_dpb_size_) { | 
| + DVLOG(3) | 
| + << "IsResolutionChangeNecessary(): Returning true since DPB mismatch "; | 
| + return true; | 
| + } | 
| + struct v4l2_format format; | 
| + bool again = false; | 
| + bool ret = GetFormatInfo(&format, &again); | 
| + if (!ret || again) { | 
| + DVLOG(3) << "IsResolutionChangeNecessary(): GetFormatInfo() failed"; | 
| + return false; | 
| + } | 
| + gfx::Size new_size(base::checked_cast<int>(format.fmt.pix_mp.width), | 
| + base::checked_cast<int>(format.fmt.pix_mp.height)); | 
| + if (frame_buffer_size_ != new_size) { | 
| + DVLOG(3) << "IsResolutionChangeNecessary(): Resolution change detected"; | 
| + return true; | 
| + } | 
| + return false; | 
| +} | 
| + | 
| } // namespace content |