| 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..876b38ae0f385c114ccfdd70c1163cf8db295b7e 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() {}
|
| @@ -179,6 +177,7 @@ V4L2VideoDecodeAccelerator::V4L2VideoDecodeAccelerator(
|
| output_buffer_queued_count_(0),
|
| output_buffer_pixelformat_(0),
|
| output_dpb_size_(0),
|
| + output_planes_count_(0),
|
| picture_clearing_count_(0),
|
| pictures_assigned_(false, false),
|
| device_poll_thread_("V4L2DevicePollThread"),
|
| @@ -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,11 @@ 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,
|
| + output_planes_count_);
|
| 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 +358,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 +988,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.";
|
| @@ -1021,10 +1004,10 @@ void V4L2VideoDecodeAccelerator::Dequeue() {
|
|
|
| // Dequeue completed input (VIDEO_OUTPUT) buffers, and recycle to the free
|
| // list.
|
| - struct v4l2_buffer dqbuf;
|
| - struct v4l2_plane planes[2];
|
| while (input_buffer_queued_count_ > 0) {
|
| DCHECK(input_streamon_);
|
| + struct v4l2_buffer dqbuf;
|
| + struct v4l2_plane planes[1];
|
| memset(&dqbuf, 0, sizeof(dqbuf));
|
| memset(planes, 0, sizeof(planes));
|
| dqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
|
| @@ -1053,12 +1036,15 @@ void V4L2VideoDecodeAccelerator::Dequeue() {
|
| // completed queue.
|
| while (output_buffer_queued_count_ > 0) {
|
| DCHECK(output_streamon_);
|
| + struct v4l2_buffer dqbuf;
|
| + scoped_ptr<struct v4l2_plane[]> planes(
|
| + new v4l2_plane[output_planes_count_]);
|
| memset(&dqbuf, 0, sizeof(dqbuf));
|
| - memset(planes, 0, sizeof(planes));
|
| + memset(planes.get(), 0, sizeof(struct v4l2_plane) * output_planes_count_);
|
| dqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
|
| dqbuf.memory = V4L2_MEMORY_MMAP;
|
| - dqbuf.m.planes = planes;
|
| - dqbuf.length = 2;
|
| + dqbuf.m.planes = planes.get();
|
| + dqbuf.length = output_planes_count_;
|
| if (device_->Ioctl(VIDIOC_DQBUF, &dqbuf) != 0) {
|
| if (errno == EAGAIN) {
|
| // EAGAIN if we're just out of buffers to dequeue.
|
| @@ -1154,14 +1140,16 @@ bool V4L2VideoDecodeAccelerator::EnqueueOutputRecord() {
|
| output_record.egl_sync = EGL_NO_SYNC_KHR;
|
| }
|
| struct v4l2_buffer qbuf;
|
| - struct v4l2_plane qbuf_planes[arraysize(output_record.fds)];
|
| + scoped_ptr<struct v4l2_plane[]> qbuf_planes(
|
| + new v4l2_plane[output_planes_count_]);
|
| memset(&qbuf, 0, sizeof(qbuf));
|
| - memset(qbuf_planes, 0, sizeof(qbuf_planes));
|
| + memset(
|
| + qbuf_planes.get(), 0, sizeof(struct v4l2_plane) * output_planes_count_);
|
| 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.m.planes = qbuf_planes.get();
|
| + qbuf.length = output_planes_count_;
|
| IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF, &qbuf);
|
| free_output_buffers_.pop();
|
| output_record.at_device = true;
|
| @@ -1632,11 +1620,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 +1721,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 +1732,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,18 +1785,11 @@ 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.";
|
| + DVLOG(1) << __func__ << " DestroyEGLImage failed.";
|
| success = false;
|
| }
|
| }
|
| @@ -1926,4 +1892,32 @@ void V4L2VideoDecodeAccelerator::PictureCleared() {
|
| SendPictureReady();
|
| }
|
|
|
| +bool V4L2VideoDecodeAccelerator::IsResolutionChangeNecessary() {
|
| + DVLOG(3) << "IsResolutionChangeNecessary() ";
|
| +
|
| + 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
|
|
|