diff options
author | David Staessens <dstaessens@google.com> | 2021-03-19 11:44:04 +0900 |
---|---|---|
committer | Chih-Yu Huang <akahuang@google.com> | 2021-05-12 11:37:06 +0900 |
commit | 2a741c3132bc7e2be2fb924a27b42f77edd0f762 (patch) | |
tree | c90d60d48859607df67c686ef171afa9316fe81d /common | |
parent | d8ec69fd1b204dbcebfa50a3c1714d14cc6c0b44 (diff) | |
download | v4l2_codec2-2a741c3132bc7e2be2fb924a27b42f77edd0f762.tar.gz |
v4l2_codec2: Adapt V4L2 device and V4L2 device poller for Android.
This CL reworks the V4L2 device and V4L2 device poller code that was
branched from their Chrome counterparts:
- Adhere to the Android coding style.
- Make use of Android logging mechanisms.
- Make use of Android assert macros.
Bug: 155138142
Test: arc.VideoEncodeAccel.h264_192p_i420_vm
Change-Id: I4842624e7fae9fc3f52d90dc90a5e7c026c63513
Diffstat (limited to 'common')
-rw-r--r-- | common/V4L2Device.cpp | 3128 | ||||
-rw-r--r-- | common/V4L2DevicePoller.cpp | 168 | ||||
-rw-r--r-- | common/include/v4l2_codec2/common/V4L2Device.h | 1009 | ||||
-rw-r--r-- | common/include/v4l2_codec2/common/V4L2DevicePoller.h | 139 |
4 files changed, 2096 insertions, 2348 deletions
diff --git a/common/V4L2Device.cpp b/common/V4L2Device.cpp index 7a0e83e..52ef5ac 100644 --- a/common/V4L2Device.cpp +++ b/common/V4L2Device.cpp @@ -5,7 +5,10 @@ // Note: Added some missing defines that are only defined in newer kernel // versions (e.g. V4L2_PIX_FMT_VP8_FRAME) -#include "v4l2_codec2/common/V4L2Device.h" +//#define LOG_NDEBUG 0 +#define LOG_TAG "V4L2Device" + +#include <v4l2_codec2/common/V4L2Device.h> #include <fcntl.h> #include <inttypes.h> @@ -18,18 +21,18 @@ #include <sys/mman.h> #include <algorithm> +#include <mutex> #include <set> #include <sstream> -#include "base/bind.h" -#include "base/logging.h" -#include "base/numerics/safe_conversions.h" -#include "base/posix/eintr_wrapper.h" -#include "base/thread_annotations.h" +#include <base/bind.h> +#include <base/numerics/safe_conversions.h> +#include <base/posix/eintr_wrapper.h> +#include <base/thread_annotations.h> +#include <utils/Log.h> -#include "color_plane_layout.h" -#include "macros.h" -#include "video_pixel_format.h" +#include <color_plane_layout.h> +#include <video_pixel_format.h> // VP8 parsed frames #ifndef V4L2_PIX_FMT_VP8_FRAME @@ -46,2116 +49,1971 @@ #define V4L2_PIX_FMT_H264_SLICE v4l2_fourcc('S', '2', '6', '4') #endif -namespace media { +namespace android { -struct v4l2_format BuildV4L2Format(const enum v4l2_buf_type type, - uint32_t fourcc, - const Size& size, - size_t buffer_size, - uint32_t stride) { - struct v4l2_format format; - memset(&format, 0, sizeof(format)); - format.type = type; - format.fmt.pix_mp.pixelformat = fourcc; - format.fmt.pix_mp.width = size.width(); - format.fmt.pix_mp.height = size.height(); - format.fmt.pix_mp.num_planes = V4L2Device::GetNumPlanesOfV4L2PixFmt(fourcc); - format.fmt.pix_mp.plane_fmt[0].sizeimage = buffer_size; +struct v4l2_format buildV4L2Format(const enum v4l2_buf_type type, uint32_t fourcc, + const media::Size& size, size_t buffer_size, uint32_t stride) { + struct v4l2_format format; + memset(&format, 0, sizeof(format)); + format.type = type; + format.fmt.pix_mp.pixelformat = fourcc; + format.fmt.pix_mp.width = size.width(); + format.fmt.pix_mp.height = size.height(); + format.fmt.pix_mp.num_planes = V4L2Device::getNumPlanesOfV4L2PixFmt(fourcc); + format.fmt.pix_mp.plane_fmt[0].sizeimage = buffer_size; - // When the image format is planar the bytesperline value applies to the - // first plane and is divided by the same factor as the width field for the - // other planes. - format.fmt.pix_mp.plane_fmt[0].bytesperline = stride; + // When the image format is planar the bytesperline value applies to the first plane and is + // divided by the same factor as the width field for the other planes. + format.fmt.pix_mp.plane_fmt[0].bytesperline = stride; - return format; + return format; } V4L2ExtCtrl::V4L2ExtCtrl(uint32_t id) { - memset(&ctrl, 0, sizeof(ctrl)); - ctrl.id = id; + memset(&ctrl, 0, sizeof(ctrl)); + ctrl.id = id; } V4L2ExtCtrl::V4L2ExtCtrl(uint32_t id, int32_t val) : V4L2ExtCtrl(id) { - ctrl.value = val; + ctrl.value = val; } -// Class used to store the state of a buffer that should persist between -// reference creations. This includes: +// Class used to store the state of a buffer that should persist between reference creations. This +// includes: // * Result of initial VIDIOC_QUERYBUF ioctl, // * Plane mappings. // // Also provides helper functions. class V4L2Buffer { - public: - static std::unique_ptr<V4L2Buffer> Create(scoped_refptr<V4L2Device> device, - enum v4l2_buf_type type, - enum v4l2_memory memory, - const struct v4l2_format& format, - size_t buffer_id); - ~V4L2Buffer(); - - void* GetPlaneMapping(const size_t plane); - size_t GetMemoryUsage() const; - const struct v4l2_buffer& v4l2_buffer() const { return v4l2_buffer_; } - - private: - V4L2Buffer(scoped_refptr<V4L2Device> device, - enum v4l2_buf_type type, - enum v4l2_memory memory, - const struct v4l2_format& format, - size_t buffer_id); - bool Query(); - - scoped_refptr<V4L2Device> device_; - std::vector<void*> plane_mappings_; - - // V4L2 data as queried by QUERYBUF. - struct v4l2_buffer v4l2_buffer_; - // WARNING: do not change this to a vector or something smaller than - // VIDEO_MAX_PLANES, otherwise the Tegra libv4l2 will write data beyond - // the number of allocated planes, resulting in memory corruption. - struct v4l2_plane v4l2_planes_[VIDEO_MAX_PLANES]; - - struct v4l2_format format_ __attribute__((unused)); - - DISALLOW_COPY_AND_ASSIGN(V4L2Buffer); +public: + static std::unique_ptr<V4L2Buffer> create(scoped_refptr<V4L2Device> device, + enum v4l2_buf_type type, enum v4l2_memory memory, + const struct v4l2_format& format, size_t bufferId); + ~V4L2Buffer(); + + V4L2Buffer(const V4L2Buffer&) = delete; + V4L2Buffer& operator=(const V4L2Buffer&) = delete; + + void* getPlaneMapping(const size_t plane); + size_t getMemoryUsage() const; + const struct v4l2_buffer& v4l2_buffer() const { return mV4l2Buffer; } + +private: + V4L2Buffer(scoped_refptr<V4L2Device> device, enum v4l2_buf_type type, enum v4l2_memory memory, + const struct v4l2_format& format, size_t bufferId); + bool query(); + + scoped_refptr<V4L2Device> mDevice; + std::vector<void*> mPlaneMappings; + + // V4L2 data as queried by QUERYBUF. + struct v4l2_buffer mV4l2Buffer; + // WARNING: do not change this to a vector or something smaller than VIDEO_MAX_PLANES, otherwise + // the Tegra libv4l2 will write data beyond the number of allocated planes, resulting in memory + // corruption. + struct v4l2_plane mV4l2Planes[VIDEO_MAX_PLANES]; + + struct v4l2_format mFormat __attribute__((unused)); }; -std::unique_ptr<V4L2Buffer> V4L2Buffer::Create(scoped_refptr<V4L2Device> device, - enum v4l2_buf_type type, - enum v4l2_memory memory, - const struct v4l2_format& format, - size_t buffer_id) { - // Not using std::make_unique because constructor is private. - std::unique_ptr<V4L2Buffer> buffer( - new V4L2Buffer(device, type, memory, format, buffer_id)); - - if (!buffer->Query()) - return nullptr; - - return buffer; -} - -V4L2Buffer::V4L2Buffer(scoped_refptr<V4L2Device> device, - enum v4l2_buf_type type, - enum v4l2_memory memory, - const struct v4l2_format& format, - size_t buffer_id) - : device_(device), format_(format) { - DCHECK(V4L2_TYPE_IS_MULTIPLANAR(type)); - DCHECK_LE(format.fmt.pix_mp.num_planes, base::size(v4l2_planes_)); - - memset(v4l2_planes_, 0, sizeof(v4l2_planes_)); - memset(&v4l2_buffer_, 0, sizeof(v4l2_buffer_)); - v4l2_buffer_.m.planes = v4l2_planes_; - // Just in case we got more planes than we want. - v4l2_buffer_.length = - std::min(static_cast<size_t>(format.fmt.pix_mp.num_planes), - base::size(v4l2_planes_)); - v4l2_buffer_.index = buffer_id; - v4l2_buffer_.type = type; - v4l2_buffer_.memory = memory; - plane_mappings_.resize(v4l2_buffer_.length); +std::unique_ptr<V4L2Buffer> V4L2Buffer::create(scoped_refptr<V4L2Device> device, + enum v4l2_buf_type type, enum v4l2_memory memory, + const struct v4l2_format& format, size_t bufferId) { + // Not using std::make_unique because constructor is private. + std::unique_ptr<V4L2Buffer> buffer(new V4L2Buffer(device, type, memory, format, bufferId)); + + if (!buffer->query()) return nullptr; + + return buffer; +} + +V4L2Buffer::V4L2Buffer(scoped_refptr<V4L2Device> device, enum v4l2_buf_type type, + enum v4l2_memory memory, const struct v4l2_format& format, size_t bufferId) + : mDevice(device), mFormat(format) { + ALOG_ASSERT(V4L2_TYPE_IS_MULTIPLANAR(type)); + ALOG_ASSERT(format.fmt.pix_mp.num_planes <= base::size(mV4l2Planes)); + + memset(mV4l2Planes, 0, sizeof(mV4l2Planes)); + memset(&mV4l2Buffer, 0, sizeof(mV4l2Buffer)); + mV4l2Buffer.m.planes = mV4l2Planes; + // Just in case we got more planes than we want. + mV4l2Buffer.length = + std::min(static_cast<size_t>(format.fmt.pix_mp.num_planes), base::size(mV4l2Planes)); + mV4l2Buffer.index = bufferId; + mV4l2Buffer.type = type; + mV4l2Buffer.memory = memory; + mV4l2Buffer.memory = V4L2_MEMORY_DMABUF; + mPlaneMappings.resize(mV4l2Buffer.length); } V4L2Buffer::~V4L2Buffer() { - if (v4l2_buffer_.memory == V4L2_MEMORY_MMAP) { - for (size_t i = 0; i < plane_mappings_.size(); i++) - if (plane_mappings_[i] != nullptr) - device_->Munmap(plane_mappings_[i], v4l2_buffer_.m.planes[i].length); - } + if (mV4l2Buffer.memory == V4L2_MEMORY_MMAP) { + for (size_t i = 0; i < mPlaneMappings.size(); i++) { + if (mPlaneMappings[i] != nullptr) { + mDevice->munmap(mPlaneMappings[i], mV4l2Buffer.m.planes[i].length); + } + } + } } -bool V4L2Buffer::Query() { - int ret = device_->Ioctl(VIDIOC_QUERYBUF, &v4l2_buffer_); - if (ret) { - VPLOGF(1) << "VIDIOC_QUERYBUF failed: "; - return false; - } +bool V4L2Buffer::query() { + int ret = mDevice->ioctl(VIDIOC_QUERYBUF, &mV4l2Buffer); + if (ret) { + ALOGE("VIDIOC_QUERYBUF failed"); + return false; + } - DCHECK(plane_mappings_.size() == v4l2_buffer_.length); + DCHECK(mPlaneMappings.size() == mV4l2Buffer.length); - return true; + return true; } -void* V4L2Buffer::GetPlaneMapping(const size_t plane) { - if (plane >= plane_mappings_.size()) { - VLOGF(1) << "Invalid plane " << plane << " requested."; - return nullptr; - } +void* V4L2Buffer::getPlaneMapping(const size_t plane) { + if (plane >= mPlaneMappings.size()) { + ALOGE("Invalid plane %zu requested.", plane); + return nullptr; + } + + void* p = mPlaneMappings[plane]; + if (p) { + return p; + } + + // Do this check here to avoid repeating it after a buffer has been successfully mapped (we know + // we are of MMAP type by then). + if (mV4l2Buffer.memory != V4L2_MEMORY_MMAP) { + ALOGE("Cannot create mapping on non-MMAP buffer"); + return nullptr; + } - void* p = plane_mappings_[plane]; - if (p) + p = mDevice->mmap(NULL, mV4l2Buffer.m.planes[plane].length, PROT_READ | PROT_WRITE, MAP_SHARED, + mV4l2Buffer.m.planes[plane].m.mem_offset); + if (p == MAP_FAILED) { + ALOGE("mmap() failed: "); + return nullptr; + } + + mPlaneMappings[plane] = p; return p; +} + +size_t V4L2Buffer::getMemoryUsage() const { + size_t usage = 0; + for (size_t i = 0; i < mV4l2Buffer.length; i++) { + usage += mV4l2Buffer.m.planes[i].length; + } + return usage; +} - // Do this check here to avoid repeating it after a buffer has been - // successfully mapped (we know we are of MMAP type by then). - if (v4l2_buffer_.memory != V4L2_MEMORY_MMAP) { - VLOGF(1) << "Cannot create mapping on non-MMAP buffer"; - return nullptr; - } - - p = device_->Mmap(NULL, v4l2_buffer_.m.planes[plane].length, - PROT_READ | PROT_WRITE, MAP_SHARED, - v4l2_buffer_.m.planes[plane].m.mem_offset); - if (p == MAP_FAILED) { - VPLOGF(1) << "mmap() failed: "; - return nullptr; - } - - plane_mappings_[plane] = p; - return p; -} - -size_t V4L2Buffer::GetMemoryUsage() const { - size_t usage = 0; - for (size_t i = 0; i < v4l2_buffer_.length; i++) { - usage += v4l2_buffer_.m.planes[i].length; - } - return usage; -} - -// A thread-safe pool of buffer indexes, allowing buffers to be obtained and -// returned from different threads. All the methods of this class are -// thread-safe. Users should keep a scoped_refptr to instances of this class -// in order to ensure the list remains alive as long as they need it. +// A thread-safe pool of buffer indexes, allowing buffers to be obtained and returned from different +// threads. All the methods of this class are thread-safe. Users should keep a scoped_refptr to +// instances of this class in order to ensure the list remains alive as long as they need it. class V4L2BuffersList : public base::RefCountedThreadSafe<V4L2BuffersList> { - public: - V4L2BuffersList() = default; - // Return a buffer to this list. Also can be called to set the initial pool - // of buffers. - // Note that it is illegal to return the same buffer twice. - void ReturnBuffer(size_t buffer_id); - // Get any of the buffers in the list. There is no order guarantee whatsoever. - base::Optional<size_t> GetFreeBuffer(); - // Get the buffer with specified index. - base::Optional<size_t> GetFreeBuffer(size_t requested_buffer_id); - // Number of buffers currently in this list. - size_t size() const; - - private: - friend class base::RefCountedThreadSafe<V4L2BuffersList>; - ~V4L2BuffersList() = default; - - mutable base::Lock lock_; - std::set<size_t> free_buffers_ GUARDED_BY(lock_); - DISALLOW_COPY_AND_ASSIGN(V4L2BuffersList); +public: + V4L2BuffersList() = default; + + V4L2BuffersList(const V4L2BuffersList&) = delete; + V4L2BuffersList& operator=(const V4L2BuffersList&) = delete; + + // Return a buffer to this list. Also can be called to set the initial pool of buffers. + // Note that it is illegal to return the same buffer twice. + void returnBuffer(size_t bufferId); + // Get any of the buffers in the list. There is no order guarantee whatsoever. + std::optional<size_t> getFreeBuffer(); + // Get the buffer with specified index. + std::optional<size_t> getFreeBuffer(size_t requestedBufferId); + // Number of buffers currently in this list. + size_t size() const; + +private: + friend class base::RefCountedThreadSafe<V4L2BuffersList>; + ~V4L2BuffersList() = default; + + mutable std::mutex mLock; + std::set<size_t> mFreeBuffers GUARDED_BY(mLock); }; -void V4L2BuffersList::ReturnBuffer(size_t buffer_id) { - base::AutoLock auto_lock(lock_); +void V4L2BuffersList::returnBuffer(size_t bufferId) { + std::lock_guard<std::mutex> lock(mLock); - auto inserted = free_buffers_.emplace(buffer_id); - DCHECK(inserted.second); + auto inserted = mFreeBuffers.emplace(bufferId); + if (!inserted.second) { + ALOGE("Returning buffer failed"); + } } -base::Optional<size_t> V4L2BuffersList::GetFreeBuffer() { - base::AutoLock auto_lock(lock_); +std::optional<size_t> V4L2BuffersList::getFreeBuffer() { + std::lock_guard<std::mutex> lock(mLock); - auto iter = free_buffers_.begin(); - if (iter == free_buffers_.end()) { - DVLOGF(4) << "No free buffer available!"; - return base::nullopt; - } + auto iter = mFreeBuffers.begin(); + if (iter == mFreeBuffers.end()) { + ALOGV("No free buffer available!"); + return std::nullopt; + } - size_t buffer_id = *iter; - free_buffers_.erase(iter); + size_t bufferId = *iter; + mFreeBuffers.erase(iter); - return buffer_id; + return bufferId; } -base::Optional<size_t> V4L2BuffersList::GetFreeBuffer( - size_t requested_buffer_id) { - base::AutoLock auto_lock(lock_); +std::optional<size_t> V4L2BuffersList::getFreeBuffer(size_t requestedBufferId) { + std::lock_guard<std::mutex> lock(mLock); - return (free_buffers_.erase(requested_buffer_id) > 0) - ? base::make_optional(requested_buffer_id) - : base::nullopt; + return (mFreeBuffers.erase(requestedBufferId) > 0) ? std::make_optional(requestedBufferId) + : std::nullopt; } size_t V4L2BuffersList::size() const { - base::AutoLock auto_lock(lock_); + std::lock_guard<std::mutex> lock(mLock); - return free_buffers_.size(); + return mFreeBuffers.size(); } -// Module-private class that let users query/write V4L2 buffer information. -// It also makes some private V4L2Queue methods available to this module only. +// Module-private class that let users query/write V4L2 buffer information. It also makes some +// private V4L2Queue methods available to this module only. class V4L2BufferRefBase { - public: - V4L2BufferRefBase(const struct v4l2_buffer& v4l2_buffer, - base::WeakPtr<V4L2Queue> queue); - ~V4L2BufferRefBase(); - - bool QueueBuffer(); - void* GetPlaneMapping(const size_t plane); - - // Checks that the number of passed FDs is adequate for the current format - // and buffer configuration. Only useful for DMABUF buffers. - bool CheckNumFDsForFormat(const size_t num_fds) const; - - // Data from the buffer, that users can query and/or write. - struct v4l2_buffer v4l2_buffer_; - // WARNING: do not change this to a vector or something smaller than - // VIDEO_MAX_PLANES, otherwise the Tegra libv4l2 will write data beyond - // the number of allocated planes, resulting in memory corruption. - struct v4l2_plane v4l2_planes_[VIDEO_MAX_PLANES]; - - private: - size_t BufferId() const { return v4l2_buffer_.index; } - - friend class V4L2WritableBufferRef; - // A weak pointer to the queue this buffer belongs to. Will remain valid as - // long as the underlying V4L2 buffer is valid too. - // This can only be accessed from the sequence protected by sequence_checker_. - // Thread-safe methods (like ~V4L2BufferRefBase) must *never* access this. - base::WeakPtr<V4L2Queue> queue_; - // Where to return this buffer if it goes out of scope without being queued. - scoped_refptr<V4L2BuffersList> return_to_; - bool queued = false; - - SEQUENCE_CHECKER(sequence_checker_); - DISALLOW_COPY_AND_ASSIGN(V4L2BufferRefBase); +public: + V4L2BufferRefBase(const struct v4l2_buffer& v4l2Buffer, base::WeakPtr<V4L2Queue> queue); + ~V4L2BufferRefBase(); + + V4L2BufferRefBase(const V4L2BufferRefBase&) = delete; + V4L2BufferRefBase& operator=(const V4L2BufferRefBase&) = delete; + + bool queueBuffer(); + void* getPlaneMapping(const size_t plane); + + // Checks that the number of passed FDs is adequate for the current format and buffer + // configuration. Only useful for DMABUF buffers. + bool checkNumFDsForFormat(const size_t numFds) const; + + // Data from the buffer, that users can query and/or write. + struct v4l2_buffer mV4l2Buffer; + // WARNING: do not change this to a vector or something smaller than VIDEO_MAX_PLANES, otherwise + // the Tegra libv4l2 will write data beyond the number of allocated planes, resulting in memory + // corruption. + struct v4l2_plane mV4l2Planes[VIDEO_MAX_PLANES]; + +private: + size_t bufferId() const { return mV4l2Buffer.index; } + + friend class V4L2WritableBufferRef; + // A weak pointer to the queue this buffer belongs to. Will remain valid as long as the + // underlying V4L2 buffer is valid too. This can only be accessed from the sequence protected by + // sequence_checker_. Thread-safe methods (like ~V4L2BufferRefBase) must *never* access this. + base::WeakPtr<V4L2Queue> mQueue; + // Where to return this buffer if it goes out of scope without being queued. + scoped_refptr<V4L2BuffersList> mReturnTo; + bool queued = false; + + SEQUENCE_CHECKER(mSequenceChecker); }; -V4L2BufferRefBase::V4L2BufferRefBase(const struct v4l2_buffer& v4l2_buffer, +V4L2BufferRefBase::V4L2BufferRefBase(const struct v4l2_buffer& v4l2Buffer, base::WeakPtr<V4L2Queue> queue) - : queue_(std::move(queue)), return_to_(queue_->free_buffers_) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(V4L2_TYPE_IS_MULTIPLANAR(v4l2_buffer.type)); - DCHECK_LE(v4l2_buffer.length, base::size(v4l2_planes_)); - DCHECK(return_to_); + : mQueue(std::move(queue)), mReturnTo(mQueue->mFreeBuffers) { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(V4L2_TYPE_IS_MULTIPLANAR(v4l2Buffer.type)); + ALOG_ASSERT(v4l2Buffer.length <= base::size(mV4l2Planes)); + ALOG_ASSERT(mReturnTo); - memcpy(&v4l2_buffer_, &v4l2_buffer, sizeof(v4l2_buffer_)); - memcpy(v4l2_planes_, v4l2_buffer.m.planes, - sizeof(struct v4l2_plane) * v4l2_buffer.length); - v4l2_buffer_.m.planes = v4l2_planes_; + memcpy(&mV4l2Buffer, &v4l2Buffer, sizeof(mV4l2Buffer)); + memcpy(mV4l2Planes, v4l2Buffer.m.planes, sizeof(struct v4l2_plane) * v4l2Buffer.length); + mV4l2Buffer.m.planes = mV4l2Planes; } V4L2BufferRefBase::~V4L2BufferRefBase() { - // We are the last reference and are only accessing the thread-safe - // return_to_, so we are safe to call from any sequence. - // If we have been queued, then the queue is our owner so we don't need to - // return to the free buffers list. - if (!queued) - return_to_->ReturnBuffer(BufferId()); + // We are the last reference and are only accessing the thread-safe mReturnTo, so we are safe + // to call from any sequence. If we have been queued, then the queue is our owner so we don't + // need to return to the free buffers list. + if (!queued) mReturnTo->returnBuffer(bufferId()); } -bool V4L2BufferRefBase::QueueBuffer() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +bool V4L2BufferRefBase::queueBuffer() { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); - if (!queue_) - return false; + if (!mQueue) return false; - queued = queue_->QueueBuffer(&v4l2_buffer_); + queued = mQueue->queueBuffer(&mV4l2Buffer); - return queued; + return queued; } -void* V4L2BufferRefBase::GetPlaneMapping(const size_t plane) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +void* V4L2BufferRefBase::getPlaneMapping(const size_t plane) { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); - if (!queue_) - return nullptr; + if (!mQueue) return nullptr; - return queue_->buffers_[BufferId()]->GetPlaneMapping(plane); + return mQueue->mBuffers[bufferId()]->getPlaneMapping(plane); } -bool V4L2BufferRefBase::CheckNumFDsForFormat(const size_t num_fds) const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +bool V4L2BufferRefBase::checkNumFDsForFormat(const size_t numFds) const { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); - if (!queue_) - return false; + if (!mQueue) return false; - // We have not used SetFormat(), assume this is ok. - // Hopefully we standardize SetFormat() in the future. - if (!queue_->current_format_) - return true; + // We have not used SetFormat(), assume this is ok. + // Hopefully we standardize SetFormat() in the future. + if (!mQueue->mCurrentFormat) return true; - const size_t required_fds = queue_->current_format_->fmt.pix_mp.num_planes; - // Sanity check. - DCHECK_EQ(v4l2_buffer_.length, required_fds); - if (num_fds < required_fds) { - VLOGF(1) << "Insufficient number of FDs given for the current format. " - << num_fds << " provided, " << required_fds << " required."; - return false; - } + const size_t requiredFds = mQueue->mCurrentFormat->fmt.pix_mp.num_planes; + // Sanity check. + ALOG_ASSERT(mV4l2Buffer.length == requiredFds); + if (numFds < requiredFds) { + ALOGE("Insufficient number of FDs given for the current format. " + "%zu provided, %zu required.", + numFds, requiredFds); + return false; + } - const auto* planes = v4l2_buffer_.m.planes; - for (size_t i = v4l2_buffer_.length - 1; i >= num_fds; --i) { - // Assume that an fd is a duplicate of a previous plane's fd if offset != 0. - // Otherwise, if offset == 0, return error as it is likely pointing to - // a new plane. - if (planes[i].data_offset == 0) { - VLOGF(1) << "Additional dmabuf fds point to a new buffer."; - return false; + const auto* planes = mV4l2Buffer.m.planes; + for (size_t i = mV4l2Buffer.length - 1; i >= numFds; --i) { + // Assume that an fd is a duplicate of a previous plane's fd if offset != 0. Otherwise, if + // offset == 0, return error as it is likely pointing to a new plane. + if (planes[i].data_offset == 0) { + ALOGE("Additional dmabuf fds point to a new buffer."); + return false; + } } - } - return true; + return true; } -V4L2WritableBufferRef::V4L2WritableBufferRef( - const struct v4l2_buffer& v4l2_buffer, - base::WeakPtr<V4L2Queue> queue) - : buffer_data_( - std::make_unique<V4L2BufferRefBase>(v4l2_buffer, std::move(queue))) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +V4L2WritableBufferRef::V4L2WritableBufferRef(const struct v4l2_buffer& v4l2Buffer, + base::WeakPtr<V4L2Queue> queue) + : mBufferData(std::make_unique<V4L2BufferRefBase>(v4l2Buffer, std::move(queue))) { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); } V4L2WritableBufferRef::V4L2WritableBufferRef(V4L2WritableBufferRef&& other) - : buffer_data_(std::move(other.buffer_data_)) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK_CALLED_ON_VALID_SEQUENCE(other.sequence_checker_); + : mBufferData(std::move(other.mBufferData)) { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + DCHECK_CALLED_ON_VALID_SEQUENCE(other.mSequenceChecker); } V4L2WritableBufferRef::~V4L2WritableBufferRef() { - // Only valid references should be sequence-checked - if (buffer_data_) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - } + // Only valid references should be sequence-checked + if (mBufferData) { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + } } -V4L2WritableBufferRef& V4L2WritableBufferRef::operator=( - V4L2WritableBufferRef&& other) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK_CALLED_ON_VALID_SEQUENCE(other.sequence_checker_); +V4L2WritableBufferRef& V4L2WritableBufferRef::operator=(V4L2WritableBufferRef&& other) { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + DCHECK_CALLED_ON_VALID_SEQUENCE(other.mSequenceChecker); - if (this == &other) - return *this; + if (this == &other) return *this; - buffer_data_ = std::move(other.buffer_data_); + mBufferData = std::move(other.mBufferData); - return *this; + return *this; } -enum v4l2_memory V4L2WritableBufferRef::Memory() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(buffer_data_); +enum v4l2_memory V4L2WritableBufferRef::memory() const { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(mBufferData); - return static_cast<enum v4l2_memory>(buffer_data_->v4l2_buffer_.memory); + return static_cast<enum v4l2_memory>(mBufferData->mV4l2Buffer.memory); } -bool V4L2WritableBufferRef::DoQueue() && { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(buffer_data_); +bool V4L2WritableBufferRef::doQueue() && { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(mBufferData); - bool queued = buffer_data_->QueueBuffer(); + bool queued = mBufferData->queueBuffer(); - // Clear our own reference. - buffer_data_.reset(); + // Clear our own reference. + mBufferData.reset(); - return queued; + return queued; } -bool V4L2WritableBufferRef::QueueMMap() && { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(buffer_data_); +bool V4L2WritableBufferRef::queueMMap() && { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(mBufferData); - // Move ourselves so our data gets freed no matter when we return - V4L2WritableBufferRef self(std::move(*this)); + // Move ourselves so our data gets freed no matter when we return + V4L2WritableBufferRef self(std::move(*this)); - if (self.Memory() != V4L2_MEMORY_MMAP) { - VLOGF(1) << "Called on invalid buffer type!"; - return false; - } + if (self.memory() != V4L2_MEMORY_MMAP) { + ALOGE("Called on invalid buffer type!"); + return false; + } - return std::move(self).DoQueue(); + return std::move(self).doQueue(); } -bool V4L2WritableBufferRef::QueueUserPtr(const std::vector<void*>& ptrs) && { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(buffer_data_); - - // Move ourselves so our data gets freed no matter when we return - V4L2WritableBufferRef self(std::move(*this)); +bool V4L2WritableBufferRef::queueUserPtr(const std::vector<void*>& ptrs) && { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(mBufferData); - if (self.Memory() != V4L2_MEMORY_USERPTR) { - VLOGF(1) << "Called on invalid buffer type!"; - return false; - } + // Move ourselves so our data gets freed no matter when we return + V4L2WritableBufferRef self(std::move(*this)); - if (ptrs.size() != self.PlanesCount()) { - VLOGF(1) << "Provided " << ptrs.size() << " pointers while we require " - << self.buffer_data_->v4l2_buffer_.length << "."; - return false; - } - - for (size_t i = 0; i < ptrs.size(); i++) - self.buffer_data_->v4l2_buffer_.m.planes[i].m.userptr = - reinterpret_cast<unsigned long>(ptrs[i]); - - return std::move(self).DoQueue(); -} + if (self.memory() != V4L2_MEMORY_USERPTR) { + ALOGE("Called on invalid buffer type!"); + return false; + } -bool V4L2WritableBufferRef::QueueDMABuf(const std::vector<base::ScopedFD>& scoped_fds) && { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (ptrs.size() != self.planesCount()) { + ALOGE("Provided %zu pointers while we require %u.", ptrs.size(), + self.mBufferData->mV4l2Buffer.length); + return false; + } - std::vector<int> fds; - fds.reserve(scoped_fds.size()); - for (const base::ScopedFD& scoped_fd : scoped_fds) - fds.push_back(scoped_fd.get()); + for (size_t i = 0; i < ptrs.size(); i++) { + self.mBufferData->mV4l2Buffer.m.planes[i].m.userptr = + reinterpret_cast<unsigned long>(ptrs[i]); + } - return std::move(*this).QueueDMABuf(fds); + return std::move(self).doQueue(); } -bool V4L2WritableBufferRef::QueueDMABuf(const std::vector<int>& fds) && { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(buffer_data_); +bool V4L2WritableBufferRef::queueDMABuf(const std::vector<int>& fds) && { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(mBufferData); - // Move ourselves so our data gets freed no matter when we return - V4L2WritableBufferRef self(std::move(*this)); + // Move ourselves so our data gets freed no matter when we return + V4L2WritableBufferRef self(std::move(*this)); - if (self.Memory() != V4L2_MEMORY_DMABUF) { - VLOGF(1) << "Called on invalid buffer type!"; - return false; - } + if (self.memory() != V4L2_MEMORY_DMABUF) { + ALOGE("Called on invalid buffer type!"); + return false; + } - if (!self.buffer_data_->CheckNumFDsForFormat(fds.size())) - return false; + if (!self.mBufferData->checkNumFDsForFormat(fds.size())) return false; - size_t num_planes = self.PlanesCount(); - for (size_t i = 0; i < num_planes; i++) - self.buffer_data_->v4l2_buffer_.m.planes[i].m.fd = fds[i]; + size_t numPlanes = self.planesCount(); + for (size_t i = 0; i < numPlanes; i++) self.mBufferData->mV4l2Buffer.m.planes[i].m.fd = fds[i]; - return std::move(self).DoQueue(); + return std::move(self).doQueue(); } -size_t V4L2WritableBufferRef::PlanesCount() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(buffer_data_); +size_t V4L2WritableBufferRef::planesCount() const { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(mBufferData); - return buffer_data_->v4l2_buffer_.length; + return mBufferData->mV4l2Buffer.length; } -size_t V4L2WritableBufferRef::GetPlaneSize(const size_t plane) const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(buffer_data_); +size_t V4L2WritableBufferRef::getPlaneSize(const size_t plane) const { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(mBufferData); - if (plane >= PlanesCount()) { - VLOGF(1) << "Invalid plane " << plane << " requested."; - return 0; - } + if (plane >= planesCount()) { + ALOGE("Invalid plane %zu requested.", plane); + return 0; + } - return buffer_data_->v4l2_buffer_.m.planes[plane].length; + return mBufferData->mV4l2Buffer.m.planes[plane].length; } -void V4L2WritableBufferRef::SetPlaneSize(const size_t plane, - const size_t size) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(buffer_data_); +void V4L2WritableBufferRef::setPlaneSize(const size_t plane, const size_t size) { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(mBufferData); - enum v4l2_memory memory = Memory(); - if (memory == V4L2_MEMORY_MMAP) { - DCHECK_EQ(buffer_data_->v4l2_buffer_.m.planes[plane].length, size); - return; - } - DCHECK(memory == V4L2_MEMORY_USERPTR || memory == V4L2_MEMORY_DMABUF); + enum v4l2_memory mem = memory(); + if (mem == V4L2_MEMORY_MMAP) { + ALOG_ASSERT(mBufferData->mV4l2Buffer.m.planes[plane].length == size); + return; + } + ALOG_ASSERT(mem == V4L2_MEMORY_USERPTR || mem == V4L2_MEMORY_DMABUF); - if (plane >= PlanesCount()) { - VLOGF(1) << "Invalid plane " << plane << " requested."; - return; - } + if (plane >= planesCount()) { + ALOGE("Invalid plane %zu requested.", plane); + return; + } - buffer_data_->v4l2_buffer_.m.planes[plane].length = size; + mBufferData->mV4l2Buffer.m.planes[plane].length = size; } -void* V4L2WritableBufferRef::GetPlaneMapping(const size_t plane) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(buffer_data_); +void* V4L2WritableBufferRef::getPlaneMapping(const size_t plane) { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(mBufferData); - return buffer_data_->GetPlaneMapping(plane); + return mBufferData->getPlaneMapping(plane); } -void V4L2WritableBufferRef::SetTimeStamp(const struct timeval& timestamp) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(buffer_data_); +void V4L2WritableBufferRef::setTimeStamp(const struct timeval& timestamp) { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(mBufferData); - buffer_data_->v4l2_buffer_.timestamp = timestamp; + mBufferData->mV4l2Buffer.timestamp = timestamp; } -const struct timeval& V4L2WritableBufferRef::GetTimeStamp() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(buffer_data_); +const struct timeval& V4L2WritableBufferRef::getTimeStamp() const { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(mBufferData); - return buffer_data_->v4l2_buffer_.timestamp; + return mBufferData->mV4l2Buffer.timestamp; } -void V4L2WritableBufferRef::SetPlaneBytesUsed(const size_t plane, - const size_t bytes_used) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(buffer_data_); +void V4L2WritableBufferRef::setPlaneBytesUsed(const size_t plane, const size_t bytesUsed) { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(mBufferData); - if (plane >= PlanesCount()) { - VLOGF(1) << "Invalid plane " << plane << " requested."; - return; - } + if (plane >= planesCount()) { + ALOGE("Invalid plane %zu requested.", plane); + return; + } - if (bytes_used > GetPlaneSize(plane)) { - VLOGF(1) << "Set bytes used " << bytes_used << " larger than plane size " - << GetPlaneSize(plane) << "."; - return; - } + if (bytesUsed > getPlaneSize(plane)) { + ALOGE("Set bytes used %zu larger than plane size %zu.", bytesUsed, getPlaneSize(plane)); + return; + } - buffer_data_->v4l2_buffer_.m.planes[plane].bytesused = bytes_used; + mBufferData->mV4l2Buffer.m.planes[plane].bytesused = bytesUsed; } -size_t V4L2WritableBufferRef::GetPlaneBytesUsed(const size_t plane) const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(buffer_data_); +size_t V4L2WritableBufferRef::getPlaneBytesUsed(const size_t plane) const { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(mBufferData); - if (plane >= PlanesCount()) { - VLOGF(1) << "Invalid plane " << plane << " requested."; - return 0; - } + if (plane >= planesCount()) { + ALOGE("Invalid plane %zu requested.", plane); + return 0; + } - return buffer_data_->v4l2_buffer_.m.planes[plane].bytesused; + return mBufferData->mV4l2Buffer.m.planes[plane].bytesused; } -void V4L2WritableBufferRef::SetPlaneDataOffset(const size_t plane, - const size_t data_offset) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(buffer_data_); +void V4L2WritableBufferRef::setPlaneDataOffset(const size_t plane, const size_t dataOffset) { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(mBufferData); - if (plane >= PlanesCount()) { - VLOGF(1) << "Invalid plane " << plane << " requested."; - return; - } + if (plane >= planesCount()) { + ALOGE("Invalid plane %zu requested.", plane); + return; + } - buffer_data_->v4l2_buffer_.m.planes[plane].data_offset = data_offset; + mBufferData->mV4l2Buffer.m.planes[plane].data_offset = dataOffset; } -size_t V4L2WritableBufferRef::BufferId() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(buffer_data_); +size_t V4L2WritableBufferRef::bufferId() const { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(mBufferData); - return buffer_data_->v4l2_buffer_.index; + return mBufferData->mV4l2Buffer.index; } -V4L2ReadableBuffer::V4L2ReadableBuffer(const struct v4l2_buffer& v4l2_buffer, +V4L2ReadableBuffer::V4L2ReadableBuffer(const struct v4l2_buffer& v4l2Buffer, base::WeakPtr<V4L2Queue> queue) - : buffer_data_( - std::make_unique<V4L2BufferRefBase>(v4l2_buffer, std::move(queue))) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + : mBufferData(std::make_unique<V4L2BufferRefBase>(v4l2Buffer, std::move(queue))) { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); } V4L2ReadableBuffer::~V4L2ReadableBuffer() { - // This method is thread-safe. Since we are the destructor, we are guaranteed - // to be called from the only remaining reference to us. Also, we are just - // calling the destructor of buffer_data_, which is also thread-safe. - DCHECK(buffer_data_); + // This method is thread-safe. Since we are the destructor, we are guaranteed to be called from + // the only remaining reference to us. Also, we are just calling the destructor of buffer_data_, + // which is also thread-safe. + ALOG_ASSERT(mBufferData); } -bool V4L2ReadableBuffer::IsLast() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(buffer_data_); +bool V4L2ReadableBuffer::isLast() const { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(mBufferData); - return buffer_data_->v4l2_buffer_.flags & V4L2_BUF_FLAG_LAST; + return mBufferData->mV4l2Buffer.flags & V4L2_BUF_FLAG_LAST; } -bool V4L2ReadableBuffer::IsKeyframe() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(buffer_data_); +bool V4L2ReadableBuffer::isKeyframe() const { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(mBufferData); - return buffer_data_->v4l2_buffer_.flags & V4L2_BUF_FLAG_KEYFRAME; + return mBufferData->mV4l2Buffer.flags & V4L2_BUF_FLAG_KEYFRAME; } -struct timeval V4L2ReadableBuffer::GetTimeStamp() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(buffer_data_); +struct timeval V4L2ReadableBuffer::getTimeStamp() const { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(mBufferData); - return buffer_data_->v4l2_buffer_.timestamp; + return mBufferData->mV4l2Buffer.timestamp; } -size_t V4L2ReadableBuffer::PlanesCount() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(buffer_data_); +size_t V4L2ReadableBuffer::planesCount() const { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(mBufferData); - return buffer_data_->v4l2_buffer_.length; + return mBufferData->mV4l2Buffer.length; } -const void* V4L2ReadableBuffer::GetPlaneMapping(const size_t plane) const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(buffer_data_); +const void* V4L2ReadableBuffer::getPlaneMapping(const size_t plane) const { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + DCHECK(mBufferData); - return buffer_data_->GetPlaneMapping(plane); + return mBufferData->getPlaneMapping(plane); } -size_t V4L2ReadableBuffer::GetPlaneBytesUsed(const size_t plane) const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(buffer_data_); +size_t V4L2ReadableBuffer::getPlaneBytesUsed(const size_t plane) const { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(mBufferData); - if (plane >= PlanesCount()) { - VLOGF(1) << "Invalid plane " << plane << " requested."; - return 0; - } + if (plane >= planesCount()) { + ALOGE("Invalid plane %zu requested.", plane); + return 0; + } - return buffer_data_->v4l2_planes_[plane].bytesused; + return mBufferData->mV4l2Planes[plane].bytesused; } -size_t V4L2ReadableBuffer::GetPlaneDataOffset(const size_t plane) const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(buffer_data_); +size_t V4L2ReadableBuffer::getPlaneDataOffset(const size_t plane) const { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(mBufferData); - if (plane >= PlanesCount()) { - VLOGF(1) << "Invalid plane " << plane << " requested."; - return 0; - } + if (plane >= planesCount()) { + ALOGE("Invalid plane %zu requested.", plane); + return 0; + } - return buffer_data_->v4l2_planes_[plane].data_offset; + return mBufferData->mV4l2Planes[plane].data_offset; } -size_t V4L2ReadableBuffer::BufferId() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(buffer_data_); +size_t V4L2ReadableBuffer::bufferId() const { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(mBufferData); - return buffer_data_->v4l2_buffer_.index; + return mBufferData->mV4l2Buffer.index; } -// This class is used to expose buffer reference classes constructors to -// this module. This is to ensure that nobody else can create buffer references. +// This class is used to expose buffer reference classes constructors to this module. This is to +// ensure that nobody else can create buffer references. class V4L2BufferRefFactory { - public: - static V4L2WritableBufferRef CreateWritableRef( - const struct v4l2_buffer& v4l2_buffer, - base::WeakPtr<V4L2Queue> queue) { - return V4L2WritableBufferRef(v4l2_buffer, std::move(queue)); - } - - static V4L2ReadableBufferRef CreateReadableRef( - const struct v4l2_buffer& v4l2_buffer, - base::WeakPtr<V4L2Queue> queue) { - return new V4L2ReadableBuffer(v4l2_buffer, std::move(queue)); - } +public: + static V4L2WritableBufferRef CreateWritableRef(const struct v4l2_buffer& v4l2Buffer, + base::WeakPtr<V4L2Queue> queue) { + return V4L2WritableBufferRef(v4l2Buffer, std::move(queue)); + } + + static V4L2ReadableBufferRef CreateReadableRef(const struct v4l2_buffer& v4l2Buffer, + base::WeakPtr<V4L2Queue> queue) { + return new V4L2ReadableBuffer(v4l2Buffer, std::move(queue)); + } }; -// Helper macros that print the queue type with logs. -#define VPQLOGF(level) \ - VPLOGF(level) << "(" << V4L2Device::V4L2BufferTypeToString(type_) << ") " -#define VQLOGF(level) \ - VLOGF(level) << "(" << V4L2Device::V4L2BufferTypeToString(type_) << ") " -#define DVQLOGF(level) \ - DVLOGF(level) << "(" << V4L2Device::V4L2BufferTypeToString(type_) << ") " +//// Helper macros that print the queue type with logs. +#define ALOGEQ(fmt, ...) ALOGE("(%s)" fmt, V4L2Device::v4L2BufferTypeToString(mType), ##__VA_ARGS__) +#define ALOGVQ(fmt, ...) ALOGD("(%s)" fmt, V4L2Device::v4L2BufferTypeToString(mType), ##__VA_ARGS__) -V4L2Queue::V4L2Queue(scoped_refptr<V4L2Device> dev, - enum v4l2_buf_type type, - base::OnceClosure destroy_cb) - : type_(type), - device_(dev), - destroy_cb_(std::move(destroy_cb)), - weak_this_factory_(this) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +V4L2Queue::V4L2Queue(scoped_refptr<V4L2Device> dev, enum v4l2_buf_type type, + base::OnceClosure destroyCb) + : mType(type), mDevice(dev), mDestroyCb(std::move(destroyCb)) { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); } V4L2Queue::~V4L2Queue() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - if (is_streaming_) { - VQLOGF(1) << "Queue is still streaming, trying to stop it..."; - Streamoff(); - } - - DCHECK(queued_buffers_.empty()); - DCHECK(!free_buffers_); - - if (!buffers_.empty()) { - VQLOGF(1) << "Buffers are still allocated, trying to deallocate them..."; - DeallocateBuffers(); - } - - std::move(destroy_cb_).Run(); -} - -base::Optional<struct v4l2_format> V4L2Queue::SetFormat(uint32_t fourcc, - const Size& size, - size_t buffer_size, - uint32_t stride) { - struct v4l2_format format = BuildV4L2Format(type_, fourcc, size, buffer_size, stride); - if (device_->Ioctl(VIDIOC_S_FMT, &format) != 0 || - format.fmt.pix_mp.pixelformat != fourcc) { - VPQLOGF(2) << "Failed to set format (format_fourcc=0x" << std::hex << fourcc - << ")"; - return base::nullopt; - } - - current_format_ = format; - return current_format_; -} - -base::Optional<struct v4l2_format> V4L2Queue::TryFormat(uint32_t fourcc, - const Size& size, - size_t buffer_size) { - struct v4l2_format format = BuildV4L2Format(type_, fourcc, size, buffer_size, 0); - if (device_->Ioctl(VIDIOC_TRY_FMT, &format) != 0 || - format.fmt.pix_mp.pixelformat != fourcc) { - VPQLOGF(2) << "Tried format not supported (format_fourcc=0x" << std::hex - << fourcc << ")"; - return base::nullopt; - } - - return format; -} - -std::pair<base::Optional<struct v4l2_format>, int> V4L2Queue::GetFormat() { - struct v4l2_format format; - memset(&format, 0, sizeof(format)); - format.type = type_; - if (device_->Ioctl(VIDIOC_G_FMT, &format) != 0) { - VPQLOGF(2) << "Failed to get format"; - return std::make_pair(base::nullopt, errno); - } - - return std::make_pair(format, 0); -} - -size_t V4L2Queue::AllocateBuffers(size_t count, enum v4l2_memory memory) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(!free_buffers_); - DCHECK_EQ(queued_buffers_.size(), 0u); - - if (IsStreaming()) { - VQLOGF(1) << "Cannot allocate buffers while streaming."; - return 0; - } - - if (buffers_.size() != 0) { - VQLOGF(1) - << "Cannot allocate new buffers while others are still allocated."; - return 0; - } - - if (count == 0) { - VQLOGF(1) << "Attempting to allocate 0 buffers."; - return 0; - } - - // First query the number of planes in the buffers we are about to request. - // This should not be required, but Tegra's VIDIOC_QUERYBUF will fail on - // output buffers if the number of specified planes does not exactly match the - // format. - struct v4l2_format format = {.type = type_}; - int ret = device_->Ioctl(VIDIOC_G_FMT, &format); - if (ret) { - VPQLOGF(1) << "VIDIOC_G_FMT failed"; - return 0; - } - planes_count_ = format.fmt.pix_mp.num_planes; - DCHECK_LE(planes_count_, static_cast<size_t>(VIDEO_MAX_PLANES)); - - struct v4l2_requestbuffers reqbufs; - memset(&reqbufs, 0, sizeof(reqbufs)); - reqbufs.count = count; - reqbufs.type = type_; - reqbufs.memory = memory; - DVQLOGF(3) << "Requesting " << count << " buffers."; - - ret = device_->Ioctl(VIDIOC_REQBUFS, &reqbufs); - if (ret) { - VPQLOGF(1) << "VIDIOC_REQBUFS failed"; - return 0; - } - DVQLOGF(3) << "queue " << type_ << ": got " << reqbufs.count << " buffers."; - - memory_ = memory; - - free_buffers_ = new V4L2BuffersList(); - - // Now query all buffer information. - for (size_t i = 0; i < reqbufs.count; i++) { - auto buffer = V4L2Buffer::Create(device_, type_, memory_, format, i); - - if (!buffer) { - DeallocateBuffers(); - - return 0; - } - - buffers_.emplace_back(std::move(buffer)); - free_buffers_->ReturnBuffer(i); - } - - DCHECK(free_buffers_); - DCHECK_EQ(free_buffers_->size(), buffers_.size()); - DCHECK_EQ(queued_buffers_.size(), 0u); + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + + if (mIsStreaming) { + ALOGEQ("Queue is still streaming, trying to stop it..."); + streamoff(); + } + + ALOG_ASSERT(mQueuedBuffers.empty()); + ALOG_ASSERT(!mFreeBuffers); + + if (!mBuffers.empty()) { + ALOGEQ("Buffers are still allocated, trying to deallocate them..."); + deallocateBuffers(); + } - return buffers_.size(); + std::move(mDestroyCb).Run(); } -bool V4L2Queue::DeallocateBuffers() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - if (IsStreaming()) { - VQLOGF(1) << "Cannot deallocate buffers while streaming."; - return false; - } +std::optional<struct v4l2_format> V4L2Queue::setFormat(uint32_t fourcc, const media::Size& size, + size_t bufferSize, uint32_t stride) { + struct v4l2_format format = buildV4L2Format(mType, fourcc, size, bufferSize, stride); + if (mDevice->ioctl(VIDIOC_S_FMT, &format) != 0 || format.fmt.pix_mp.pixelformat != fourcc) { + ALOGEQ("Failed to set format (format_fourcc=0x%" PRIx32 ")", fourcc); + return std::nullopt; + } - if (buffers_.size() == 0) - return true; + mCurrentFormat = format; + return mCurrentFormat; +} + +std::optional<struct v4l2_format> V4L2Queue::tryFormat(uint32_t fourcc, const media::Size& size, + size_t bufferSize) { + struct v4l2_format format = buildV4L2Format(mType, fourcc, size, bufferSize, 0); + if (mDevice->ioctl(VIDIOC_TRY_FMT, &format) != 0 || format.fmt.pix_mp.pixelformat != fourcc) { + ALOGEQ("Tried format not supported (format_fourcc=0x%" PRIx32 ")", fourcc); + return std::nullopt; + } + + return format; +} - weak_this_factory_.InvalidateWeakPtrs(); - buffers_.clear(); - free_buffers_ = nullptr; +std::pair<std::optional<struct v4l2_format>, int> V4L2Queue::getFormat() { + struct v4l2_format format; + memset(&format, 0, sizeof(format)); + format.type = mType; + if (mDevice->ioctl(VIDIOC_G_FMT, &format) != 0) { + ALOGEQ("Failed to get format"); + return std::make_pair(std::nullopt, errno); + } + + return std::make_pair(format, 0); +} + +size_t V4L2Queue::allocateBuffers(size_t count, enum v4l2_memory memory) { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + ALOG_ASSERT(!mFreeBuffers); + ALOG_ASSERT(mQueuedBuffers.size() == 0u); - // Free all buffers. - struct v4l2_requestbuffers reqbufs; - memset(&reqbufs, 0, sizeof(reqbufs)); - reqbufs.count = 0; - reqbufs.type = type_; - reqbufs.memory = memory_; + if (isStreaming()) { + ALOGEQ("Cannot allocate buffers while streaming."); + return 0; + } - int ret = device_->Ioctl(VIDIOC_REQBUFS, &reqbufs); - if (ret) { - VPQLOGF(1) << "VIDIOC_REQBUFS failed"; - return false; - } + if (mBuffers.size() != 0) { + ALOGEQ("Cannot allocate new buffers while others are still allocated."); + return 0; + } - DCHECK(!free_buffers_); - DCHECK_EQ(queued_buffers_.size(), 0u); + if (count == 0) { + ALOGEQ("Attempting to allocate 0 buffers."); + return 0; + } - return true; + // First query the number of planes in the buffers we are about to request. This should not be + // required, but Tegra's VIDIOC_QUERYBUF will fail on output buffers if the number of specified + // planes does not exactly match the format. + struct v4l2_format format = {.type = mType}; + int ret = mDevice->ioctl(VIDIOC_G_FMT, &format); + if (ret) { + ALOGEQ("VIDIOC_G_FMT failed"); + return 0; + } + mPlanesCount = format.fmt.pix_mp.num_planes; + ALOG_ASSERT(mPlanesCount <= static_cast<size_t>(VIDEO_MAX_PLANES)); + + struct v4l2_requestbuffers reqbufs; + memset(&reqbufs, 0, sizeof(reqbufs)); + reqbufs.count = count; + reqbufs.type = mType; + reqbufs.memory = memory; + ALOGVQ("Requesting %zu buffers.", count); + + ret = mDevice->ioctl(VIDIOC_REQBUFS, &reqbufs); + if (ret) { + ALOGEQ("VIDIOC_REQBUFS failed"); + return 0; + } + ALOGVQ("Queue %u: got %u buffers.", mType, reqbufs.count); + + mMemory = memory; + + mFreeBuffers = new V4L2BuffersList(); + + // Now query all buffer information. + for (size_t i = 0; i < reqbufs.count; i++) { + auto buffer = V4L2Buffer::create(mDevice, mType, mMemory, format, i); + + if (!buffer) { + deallocateBuffers(); + + return 0; + } + + mBuffers.emplace_back(std::move(buffer)); + mFreeBuffers->returnBuffer(i); + } + + ALOG_ASSERT(mFreeBuffers); + ALOG_ASSERT(mFreeBuffers->size() == mBuffers.size()); + ALOG_ASSERT(mQueuedBuffers.size() == 0u); + + return mBuffers.size(); } -size_t V4L2Queue::GetMemoryUsage() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - size_t usage = 0; - for (const auto& buf : buffers_) { - usage += buf->GetMemoryUsage(); - } - return usage; +bool V4L2Queue::deallocateBuffers() { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + + if (isStreaming()) { + ALOGEQ("Cannot deallocate buffers while streaming."); + return false; + } + + if (mBuffers.size() == 0) return true; + + mWeakThisFactory.InvalidateWeakPtrs(); + mBuffers.clear(); + mFreeBuffers = nullptr; + + // Free all buffers. + struct v4l2_requestbuffers reqbufs; + memset(&reqbufs, 0, sizeof(reqbufs)); + reqbufs.count = 0; + reqbufs.type = mType; + reqbufs.memory = mMemory; + + int ret = mDevice->ioctl(VIDIOC_REQBUFS, &reqbufs); + if (ret) { + ALOGEQ("VIDIOC_REQBUFS failed"); + return false; + } + + ALOG_ASSERT(!mFreeBuffers); + ALOG_ASSERT(mQueuedBuffers.size() == 0u); + + return true; } -v4l2_memory V4L2Queue::GetMemoryType() const { - return memory_; +size_t V4L2Queue::getMemoryUsage() const { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); + size_t usage = 0; + for (const auto& buf : mBuffers) { + usage += buf->getMemoryUsage(); + } + return usage; +} + +v4l2_memory V4L2Queue::getMemoryType() const { + return mMemory; } -base::Optional<V4L2WritableBufferRef> V4L2Queue::GetFreeBuffer() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +std::optional<V4L2WritableBufferRef> V4L2Queue::getFreeBuffer() { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); - // No buffers allocated at the moment? - if (!free_buffers_) - return base::nullopt; + // No buffers allocated at the moment? + if (!mFreeBuffers) return std::nullopt; - auto buffer_id = free_buffers_->GetFreeBuffer(); - if (!buffer_id.has_value()) - return base::nullopt; + auto bufferId = mFreeBuffers->getFreeBuffer(); + if (!bufferId.has_value()) return std::nullopt; - return V4L2BufferRefFactory::CreateWritableRef( - buffers_[buffer_id.value()]->v4l2_buffer(), - weak_this_factory_.GetWeakPtr()); + return V4L2BufferRefFactory::CreateWritableRef(mBuffers[bufferId.value()]->v4l2_buffer(), + mWeakThisFactory.GetWeakPtr()); } -base::Optional<V4L2WritableBufferRef> V4L2Queue::GetFreeBuffer( - size_t requested_buffer_id) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +std::optional<V4L2WritableBufferRef> V4L2Queue::getFreeBuffer(size_t requestedBufferIid) { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); - // No buffers allocated at the moment? - if (!free_buffers_) - return base::nullopt; + // No buffers allocated at the moment? + if (!mFreeBuffers) return std::nullopt; - auto buffer_id = free_buffers_->GetFreeBuffer(requested_buffer_id); - if (!buffer_id.has_value()) - return base::nullopt; + auto bufferId = mFreeBuffers->getFreeBuffer(requestedBufferIid); + if (!bufferId.has_value()) return std::nullopt; - return V4L2BufferRefFactory::CreateWritableRef( - buffers_[buffer_id.value()]->v4l2_buffer(), - weak_this_factory_.GetWeakPtr()); + return V4L2BufferRefFactory::CreateWritableRef(mBuffers[bufferId.value()]->v4l2_buffer(), + mWeakThisFactory.GetWeakPtr()); } -bool V4L2Queue::QueueBuffer(struct v4l2_buffer* v4l2_buffer) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +bool V4L2Queue::queueBuffer(struct v4l2_buffer* v4l2Buffer) { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); - int ret = device_->Ioctl(VIDIOC_QBUF, v4l2_buffer); - if (ret) { - VPQLOGF(1) << "VIDIOC_QBUF failed"; - return false; - } + int ret = mDevice->ioctl(VIDIOC_QBUF, v4l2Buffer); + if (ret) { + ALOGEQ("VIDIOC_QBUF failed"); + return false; + } - auto inserted = queued_buffers_.emplace(v4l2_buffer->index); - DCHECK_EQ(inserted.second, true); + auto inserted = mQueuedBuffers.emplace(v4l2Buffer->index); + if (!inserted.second) { + ALOGE("Queuing buffer failed"); + return false; + } - device_->SchedulePoll(); + mDevice->schedulePoll(); - return true; + return true; } -std::pair<bool, V4L2ReadableBufferRef> V4L2Queue::DequeueBuffer() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - // No need to dequeue if no buffers queued. - if (QueuedBuffersCount() == 0) - return std::make_pair(true, nullptr); +std::pair<bool, V4L2ReadableBufferRef> V4L2Queue::dequeueBuffer() { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); - if (!IsStreaming()) { - VQLOGF(1) << "Attempting to dequeue a buffer while not streaming."; - return std::make_pair(true, nullptr); - } + // No need to dequeue if no buffers queued. + if (queuedBuffersCount() == 0) return std::make_pair(true, nullptr); - struct v4l2_buffer v4l2_buffer; - memset(&v4l2_buffer, 0, sizeof(v4l2_buffer)); - // WARNING: do not change this to a vector or something smaller than - // VIDEO_MAX_PLANES, otherwise the Tegra libv4l2 will write data beyond - // the number of allocated planes, resulting in memory corruption. - struct v4l2_plane planes[VIDEO_MAX_PLANES]; - memset(planes, 0, sizeof(planes)); - v4l2_buffer.type = type_; - v4l2_buffer.memory = memory_; - v4l2_buffer.m.planes = planes; - v4l2_buffer.length = planes_count_; - int ret = device_->Ioctl(VIDIOC_DQBUF, &v4l2_buffer); - if (ret) { - // TODO(acourbot): we should not have to check for EPIPE as codec clients - // should not call this method after the last buffer is dequeued. - switch (errno) { - case EAGAIN: - case EPIPE: - // This is not an error so we'll need to continue polling but won't - // provide a buffer. - device_->SchedulePoll(); + if (!isStreaming()) { + ALOGEQ("Attempting to dequeue a buffer while not streaming."); return std::make_pair(true, nullptr); - default: - VPQLOGF(1) << "VIDIOC_DQBUF failed"; - return std::make_pair(false, nullptr); } - } - auto it = queued_buffers_.find(v4l2_buffer.index); - DCHECK(it != queued_buffers_.end()); - queued_buffers_.erase(*it); + struct v4l2_buffer v4l2Buffer; + memset(&v4l2Buffer, 0, sizeof(v4l2Buffer)); + // WARNING: do not change this to a vector or something smaller than VIDEO_MAX_PLANES, otherwise + // the Tegra libv4l2 will write data beyond the number of allocated planes, resulting in memory + // corruption. + struct v4l2_plane planes[VIDEO_MAX_PLANES]; + memset(planes, 0, sizeof(planes)); + v4l2Buffer.type = mType; + v4l2Buffer.memory = mMemory; + v4l2Buffer.m.planes = planes; + v4l2Buffer.length = mPlanesCount; + int ret = mDevice->ioctl(VIDIOC_DQBUF, &v4l2Buffer); + if (ret) { + // TODO(acourbot): we should not have to check for EPIPE as codec clients should not call + // this method after the last buffer is dequeued. + switch (errno) { + case EAGAIN: + case EPIPE: + // This is not an error so we'll need to continue polling but won't provide a buffer. + mDevice->schedulePoll(); + return std::make_pair(true, nullptr); + default: + ALOGEQ("VIDIOC_DQBUF failed"); + return std::make_pair(false, nullptr); + } + } + + auto it = mQueuedBuffers.find(v4l2Buffer.index); + ALOG_ASSERT(it != mQueuedBuffers.end()); + mQueuedBuffers.erase(*it); - if (QueuedBuffersCount() > 0) - device_->SchedulePoll(); + if (queuedBuffersCount() > 0) mDevice->schedulePoll(); - DCHECK(free_buffers_); - return std::make_pair(true, - V4L2BufferRefFactory::CreateReadableRef( - v4l2_buffer, weak_this_factory_.GetWeakPtr())); + ALOG_ASSERT(mFreeBuffers); + return std::make_pair(true, V4L2BufferRefFactory::CreateReadableRef( + v4l2Buffer, mWeakThisFactory.GetWeakPtr())); } -bool V4L2Queue::IsStreaming() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +bool V4L2Queue::isStreaming() const { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); - return is_streaming_; + return mIsStreaming; } -bool V4L2Queue::Streamon() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +bool V4L2Queue::streamon() { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); - if (is_streaming_) - return true; + if (mIsStreaming) return true; - int arg = static_cast<int>(type_); - int ret = device_->Ioctl(VIDIOC_STREAMON, &arg); - if (ret) { - VPQLOGF(1) << "VIDIOC_STREAMON failed"; - return false; - } + int arg = static_cast<int>(mType); + int ret = mDevice->ioctl(VIDIOC_STREAMON, &arg); + if (ret) { + ALOGEQ("VIDIOC_STREAMON failed"); + return false; + } - is_streaming_ = true; + mIsStreaming = true; - return true; + return true; } -bool V4L2Queue::Streamoff() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +bool V4L2Queue::streamoff() { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); - // We do not check the value of IsStreaming(), because we may have queued - // buffers to the queue and wish to get them back - in such as case, we may - // need to do a VIDIOC_STREAMOFF on a stopped queue. + // We do not check the value of IsStreaming(), because we may have queued buffers to the queue + // and wish to get them back - in such as case, we may need to do a VIDIOC_STREAMOFF on a + // stopped queue. - int arg = static_cast<int>(type_); - int ret = device_->Ioctl(VIDIOC_STREAMOFF, &arg); - if (ret) { - VPQLOGF(1) << "VIDIOC_STREAMOFF failed"; - return false; - } + int arg = static_cast<int>(mType); + int ret = mDevice->ioctl(VIDIOC_STREAMOFF, &arg); + if (ret) { + ALOGEQ("VIDIOC_STREAMOFF failed"); + return false; + } - for (const auto& buffer_id : queued_buffers_) { - DCHECK(free_buffers_); - free_buffers_->ReturnBuffer(buffer_id); - } + for (const auto& bufferId : mQueuedBuffers) { + ALOG_ASSERT(mFreeBuffers); + mFreeBuffers->returnBuffer(bufferId); + } - queued_buffers_.clear(); + mQueuedBuffers.clear(); - is_streaming_ = false; + mIsStreaming = false; - return true; + return true; } -size_t V4L2Queue::AllocatedBuffersCount() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +size_t V4L2Queue::allocatedBuffersCount() const { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); - return buffers_.size(); + return mBuffers.size(); } -size_t V4L2Queue::FreeBuffersCount() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +size_t V4L2Queue::freeBuffersCount() const { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); - return free_buffers_ ? free_buffers_->size() : 0; + return mFreeBuffers ? mFreeBuffers->size() : 0; } -size_t V4L2Queue::QueuedBuffersCount() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +size_t V4L2Queue::queuedBuffersCount() const { + ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence()); - return queued_buffers_.size(); + return mQueuedBuffers.size(); } -#undef VDQLOGF -#undef VPQLOGF -#undef VQLOGF +#undef ALOGEQ +#undef ALOGVQ -// This class is used to expose V4L2Queue's constructor to this module. This is -// to ensure that nobody else can create instances of it. +// This class is used to expose V4L2Queue's constructor to this module. This is to ensure that +// nobody else can create instances of it. class V4L2QueueFactory { - public: - static scoped_refptr<V4L2Queue> CreateQueue(scoped_refptr<V4L2Device> dev, - enum v4l2_buf_type type, - base::OnceClosure destroy_cb) { - return new V4L2Queue(std::move(dev), type, std::move(destroy_cb)); - } +public: + static scoped_refptr<V4L2Queue> createQueue(scoped_refptr<V4L2Device> dev, + enum v4l2_buf_type type, + base::OnceClosure destroyCb) { + return new V4L2Queue(std::move(dev), type, std::move(destroyCb)); + } }; V4L2Device::V4L2Device() { - DETACH_FROM_SEQUENCE(client_sequence_checker_); + DETACH_FROM_SEQUENCE(mClientSequenceChecker); } -V4L2Device::~V4L2Device() {} +V4L2Device::~V4L2Device() { + closeDevice(); +} -scoped_refptr<V4L2Queue> V4L2Device::GetQueue(enum v4l2_buf_type type) { - DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_); +scoped_refptr<V4L2Queue> V4L2Device::getQueue(enum v4l2_buf_type type) { + DCHECK_CALLED_ON_VALID_SEQUENCE(mClientSequenceChecker); - switch (type) { + switch (type) { // Supported queue types. case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - break; + break; default: - VLOGF(1) << "Unsupported V4L2 queue type: " << type; - return nullptr; - } + ALOGE("Unsupported V4L2 queue type: %u", type); + return nullptr; + } - // TODO(acourbot): we should instead query the device for available queues, - // and allocate them accordingly. This will do for now though. - auto it = queues_.find(type); - if (it != queues_.end()) - return scoped_refptr<V4L2Queue>(it->second); + // TODO(acourbot): we should instead query the device for available queues, and allocate them + // accordingly. This will do for now though. + auto it = mQueues.find(type); + if (it != mQueues.end()) return scoped_refptr<V4L2Queue>(it->second); - scoped_refptr<V4L2Queue> queue = V4L2QueueFactory::CreateQueue( - this, type, base::BindOnce(&V4L2Device::OnQueueDestroyed, this, type)); + scoped_refptr<V4L2Queue> queue = V4L2QueueFactory::createQueue( + this, type, base::BindOnce(&V4L2Device::onQueueDestroyed, this, type)); - queues_[type] = queue.get(); - return queue; + mQueues[type] = queue.get(); + return queue; } -void V4L2Device::OnQueueDestroyed(v4l2_buf_type buf_type) { - DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_); +void V4L2Device::onQueueDestroyed(v4l2_buf_type bufType) { + DCHECK_CALLED_ON_VALID_SEQUENCE(mClientSequenceChecker); - auto it = queues_.find(buf_type); - DCHECK(it != queues_.end()); - queues_.erase(it); + auto it = mQueues.find(bufType); + ALOG_ASSERT(it != mQueues.end()); + mQueues.erase(it); } // static -scoped_refptr<V4L2Device> V4L2Device::Create() { - DVLOGF(3); - return scoped_refptr<V4L2Device>(new V4L2Device()); +scoped_refptr<V4L2Device> V4L2Device::create() { + ALOGV("%s()", __func__); + return scoped_refptr<V4L2Device>(new V4L2Device()); } -bool V4L2Device::Open(Type type, uint32_t v4l2_pixfmt) { - DVLOGF(3); - std::string path = GetDevicePathFor(type, v4l2_pixfmt); +bool V4L2Device::open(Type type, uint32_t v4l2PixFmt) { + ALOGV("%s()", __func__); - if (path.empty()) { - VLOGF(1) << "No devices supporting " << FourccToString(v4l2_pixfmt) - << " for type: " << static_cast<int>(type); - return false; - } + std::string path = getDevicePathFor(type, v4l2PixFmt); - if (!OpenDevicePath(path, type)) { - VLOGF(1) << "Failed opening " << path; - return false; - } - - device_poll_interrupt_fd_.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC)); - if (!device_poll_interrupt_fd_.is_valid()) { - VLOGF(1) << "Failed creating a poll interrupt fd"; - return false; - } + if (path.empty()) { + ALOGE("No devices supporting %s for type: %u", media::FourccToString(v4l2PixFmt).c_str(), + static_cast<uint32_t>(type)); + return false; + } - return true; -} + if (!openDevicePath(path, type)) { + ALOGE("Failed opening %s", path.c_str()); + return false; + } -bool V4L2Device::Initialize() { - DVLOGF(3); - static bool v4l2_functions_initialized = PostSandboxInitialization(); - if (!v4l2_functions_initialized) { - VLOGF(1) << "Failed to initialize LIBV4L2 libs"; - return false; - } + mDevicePollInterruptFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC)); + if (!mDevicePollInterruptFd.is_valid()) { + ALOGE("Failed creating a poll interrupt fd"); + return false; + } - return true; + return true; } -int V4L2Device::Ioctl(int request, void* arg) { - DCHECK(device_fd_.is_valid()); - return HANDLE_EINTR(ioctl(device_fd_.get(), request, arg)); +int V4L2Device::ioctl(int request, void* arg) { + ALOG_ASSERT(mDeviceFd.is_valid()); + return HANDLE_EINTR(::ioctl(mDeviceFd.get(), request, arg)); } -bool V4L2Device::Poll(bool poll_device, bool* event_pending) { - struct pollfd pollfds[2]; - nfds_t nfds; - int pollfd = -1; +bool V4L2Device::poll(bool pollDevice, bool* eventPending) { + struct pollfd pollfds[2]; + nfds_t nfds; + int pollfd = -1; - pollfds[0].fd = device_poll_interrupt_fd_.get(); - pollfds[0].events = POLLIN | POLLERR; - nfds = 1; + pollfds[0].fd = mDevicePollInterruptFd.get(); + pollfds[0].events = POLLIN | POLLERR; + nfds = 1; - if (poll_device) { - DVLOGF(5) << "adding device fd to poll() set"; - pollfds[nfds].fd = device_fd_.get(); - pollfds[nfds].events = POLLIN | POLLOUT | POLLERR | POLLPRI; - pollfd = nfds; - nfds++; - } + if (pollDevice) { + ALOGV("adding device fd to poll() set"); + pollfds[nfds].fd = mDeviceFd.get(); + pollfds[nfds].events = POLLIN | POLLOUT | POLLERR | POLLPRI; + pollfd = nfds; + nfds++; + } - if (HANDLE_EINTR(poll(pollfds, nfds, -1)) == -1) { - VPLOGF(1) << "poll() failed"; - return false; - } - *event_pending = (pollfd != -1 && pollfds[pollfd].revents & POLLPRI); - return true; + if (HANDLE_EINTR(::poll(pollfds, nfds, -1)) == -1) { + ALOGE("poll() failed"); + return false; + } + *eventPending = (pollfd != -1 && pollfds[pollfd].revents & POLLPRI); + return true; } -void* V4L2Device::Mmap(void* addr, - unsigned int len, - int prot, - int flags, - unsigned int offset) { - DCHECK(device_fd_.is_valid()); - return mmap(addr, len, prot, flags, device_fd_.get(), offset); +void* V4L2Device::mmap(void* addr, unsigned int len, int prot, int flags, unsigned int offset) { + DCHECK(mDeviceFd.is_valid()); + return ::mmap(addr, len, prot, flags, mDeviceFd.get(), offset); } -void V4L2Device::Munmap(void* addr, unsigned int len) { - munmap(addr, len); +void V4L2Device::munmap(void* addr, unsigned int len) { + ::munmap(addr, len); } -bool V4L2Device::SetDevicePollInterrupt() { - DVLOGF(4); +bool V4L2Device::setDevicePollInterrupt() { + ALOGV("%s()", __func__); - const uint64_t buf = 1; - if (HANDLE_EINTR(write(device_poll_interrupt_fd_.get(), &buf, sizeof(buf))) == - -1) { - VPLOGF(1) << "write() failed"; - return false; - } - return true; + const uint64_t buf = 1; + if (HANDLE_EINTR(write(mDevicePollInterruptFd.get(), &buf, sizeof(buf))) == -1) { + ALOGE("write() failed"); + return false; + } + return true; } -bool V4L2Device::ClearDevicePollInterrupt() { - DVLOGF(5); +bool V4L2Device::clearDevicePollInterrupt() { + ALOGV("%s()", __func__); - uint64_t buf; - if (HANDLE_EINTR(read(device_poll_interrupt_fd_.get(), &buf, sizeof(buf))) == - -1) { - if (errno == EAGAIN) { - // No interrupt flag set, and we're reading nonblocking. Not an error. - return true; - } else { - VPLOGF(1) << "read() failed"; - return false; + uint64_t buf; + if (HANDLE_EINTR(read(mDevicePollInterruptFd.get(), &buf, sizeof(buf))) == -1) { + if (errno == EAGAIN) { + // No interrupt flag set, and we're reading nonblocking. Not an error. + return true; + } else { + ALOGE("read() failed"); + return false; + } } - } - return true; + return true; } -std::vector<base::ScopedFD> V4L2Device::GetDmabufsForV4L2Buffer( - int index, - size_t num_planes, - enum v4l2_buf_type buf_type) { - DVLOGF(3); - DCHECK(V4L2_TYPE_IS_MULTIPLANAR(buf_type)); +std::vector<base::ScopedFD> V4L2Device::getDmabufsForV4L2Buffer(int index, size_t numPlanes, + enum v4l2_buf_type bufType) { + ALOGV("%s()", __func__); + ALOG_ASSERT(V4L2_TYPE_IS_MULTIPLANAR(bufType)); + + std::vector<base::ScopedFD> dmabufFds; + for (size_t i = 0; i < numPlanes; ++i) { + struct v4l2_exportbuffer expbuf; + memset(&expbuf, 0, sizeof(expbuf)); + expbuf.type = bufType; + expbuf.index = index; + expbuf.plane = i; + expbuf.flags = O_CLOEXEC; + if (ioctl(VIDIOC_EXPBUF, &expbuf) != 0) { + dmabufFds.clear(); + break; + } - std::vector<base::ScopedFD> dmabuf_fds; - for (size_t i = 0; i < num_planes; ++i) { - struct v4l2_exportbuffer expbuf; - memset(&expbuf, 0, sizeof(expbuf)); - expbuf.type = buf_type; - expbuf.index = index; - expbuf.plane = i; - expbuf.flags = O_CLOEXEC; - if (Ioctl(VIDIOC_EXPBUF, &expbuf) != 0) { - dmabuf_fds.clear(); - break; + dmabufFds.push_back(base::ScopedFD(expbuf.fd)); } - dmabuf_fds.push_back(base::ScopedFD(expbuf.fd)); - } - - return dmabuf_fds; + return dmabufFds; } -std::vector<uint32_t> V4L2Device::PreferredInputFormat(Type type) { - if (type == Type::kEncoder) - return {V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_NV12}; +std::vector<uint32_t> V4L2Device::preferredInputFormat(Type type) { + if (type == Type::kEncoder) return {V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_NV12}; - return {}; + return {}; } -VideoDecodeAccelerator::SupportedProfiles -V4L2Device::GetSupportedDecodeProfiles(const size_t num_formats, - const uint32_t pixelformats[]) { - VideoDecodeAccelerator::SupportedProfiles supported_profiles; +media::VideoDecodeAccelerator::SupportedProfiles V4L2Device::getSupportedDecodeProfiles( + const size_t numFormats, const uint32_t pixelFormats[]) { + media::VideoDecodeAccelerator::SupportedProfiles supportedProfiles; - Type type = Type::kDecoder; - const auto& devices = GetDevicesForType(type); - for (const auto& device : devices) { - if (!OpenDevicePath(device.first, type)) { - VLOGF(1) << "Failed opening " << device.first; - continue; - } + Type type = Type::kDecoder; + const auto& devices = getDevicesForType(type); + for (const auto& device : devices) { + if (!openDevicePath(device.first, type)) { + ALOGV("Failed opening %s", device.first.c_str()); + continue; + } - const auto& profiles = - EnumerateSupportedDecodeProfiles(num_formats, pixelformats); - supported_profiles.insert(supported_profiles.end(), profiles.begin(), - profiles.end()); - CloseDevice(); - } + const auto& profiles = enumerateSupportedDecodeProfiles(numFormats, pixelFormats); + supportedProfiles.insert(supportedProfiles.end(), profiles.begin(), profiles.end()); + closeDevice(); + } - return supported_profiles; + return supportedProfiles; } -VideoEncodeAccelerator::SupportedProfiles -V4L2Device::GetSupportedEncodeProfiles() { - VideoEncodeAccelerator::SupportedProfiles supported_profiles; +media::VideoEncodeAccelerator::SupportedProfiles V4L2Device::getSupportedEncodeProfiles() { + media::VideoEncodeAccelerator::SupportedProfiles supportedProfiles; - Type type = Type::kEncoder; - const auto& devices = GetDevicesForType(type); - for (const auto& device : devices) { - if (!OpenDevicePath(device.first, type)) { - VLOGF(1) << "Failed opening " << device.first; - continue; - } + Type type = Type::kEncoder; + const auto& devices = getDevicesForType(type); + for (const auto& device : devices) { + if (!openDevicePath(device.first, type)) { + ALOGV("Failed opening %s", device.first.c_str()); + continue; + } - const auto& profiles = EnumerateSupportedEncodeProfiles(); - supported_profiles.insert(supported_profiles.end(), profiles.begin(), - profiles.end()); - CloseDevice(); - } + const auto& profiles = enumerateSupportedEncodeProfiles(); + supportedProfiles.insert(supportedProfiles.end(), profiles.begin(), profiles.end()); + closeDevice(); + } - return supported_profiles; + return supportedProfiles; } -bool V4L2Device::OpenDevicePath(const std::string& path, Type /*type*/) { - DCHECK(!device_fd_.is_valid()); +bool V4L2Device::openDevicePath(const std::string& path, Type /*type*/) { + ALOG_ASSERT(!mDeviceFd.is_valid()); - device_fd_.reset( - HANDLE_EINTR(open(path.c_str(), O_RDWR | O_NONBLOCK | O_CLOEXEC))); - if (!device_fd_.is_valid()) - return false; + mDeviceFd.reset(HANDLE_EINTR(::open(path.c_str(), O_RDWR | O_NONBLOCK | O_CLOEXEC))); + if (!mDeviceFd.is_valid()) return false; - return true; + return true; } -void V4L2Device::CloseDevice() { - DVLOGF(3); - device_fd_.reset(); -} +void V4L2Device::closeDevice() { + ALOGV("%s()", __func__); -// static -bool V4L2Device::PostSandboxInitialization() { - return true; + mDeviceFd.reset(); } -void V4L2Device::EnumerateDevicesForType(Type type) { - // video input/output devices are registered as /dev/videoX in V4L2. - static const std::string kVideoDevicePattern = "/dev/video"; +void V4L2Device::enumerateDevicesForType(Type type) { + // video input/output devices are registered as /dev/videoX in V4L2. + static const std::string kVideoDevicePattern = "/dev/video"; - std::string device_pattern; - v4l2_buf_type buf_type; - switch (type) { + std::string devicePattern; + v4l2_buf_type bufType; + switch (type) { case Type::kDecoder: - device_pattern = kVideoDevicePattern; - buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - break; + devicePattern = kVideoDevicePattern; + bufType = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + break; case Type::kEncoder: - device_pattern = kVideoDevicePattern; - buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - break; + devicePattern = kVideoDevicePattern; + bufType = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + break; default: - LOG(ERROR) << "Only decoder and encoder types are supported!!"; - return; - } + ALOGE("Only decoder and encoder types are supported!!"); + return; + } - std::vector<std::string> candidate_paths; + std::vector<std::string> candidatePaths; - // TODO(posciak): Remove this legacy unnumbered device once - // all platforms are updated to use numbered devices. - candidate_paths.push_back(device_pattern); + // TODO(posciak): Remove this legacy unnumbered device once all platforms are updated to use + // numbered devices. + candidatePaths.push_back(devicePattern); - // We are sandboxed, so we can't query directory contents to check which - // devices are actually available. Try to open the first 10; if not present, - // we will just fail to open immediately. - for (int i = 0; i < 10; ++i) { - candidate_paths.push_back( - base::StringPrintf("%s%d", device_pattern.c_str(), i)); - } + // We are sandboxed, so we can't query directory contents to check which devices are actually + // available. Try to open the first 10; if not present, we will just fail to open immediately. + for (int i = 0; i < 10; ++i) { + candidatePaths.push_back(base::StringPrintf("%s%d", devicePattern.c_str(), i)); + } - Devices devices; - for (const auto& path : candidate_paths) { - if (!OpenDevicePath(path, type)) - continue; + Devices devices; + for (const auto& path : candidatePaths) { + if (!openDevicePath(path, type)) { + continue; + } - const auto& supported_pixelformats = - EnumerateSupportedPixelformats(buf_type); - if (!supported_pixelformats.empty()) { - DVLOGF(3) << "Found device: " << path; - devices.push_back(std::make_pair(path, supported_pixelformats)); - } + const auto& supportedPixelformats = enumerateSupportedPixelformats(bufType); + if (!supportedPixelformats.empty()) { + ALOGV("Found device: %s", path.c_str()); + devices.push_back(std::make_pair(path, supportedPixelformats)); + } - CloseDevice(); - } + closeDevice(); + } - DCHECK_EQ(devices_by_type_.count(type), 0u); - devices_by_type_[type] = devices; + ALOG_ASSERT(mDevicesByType.count(type) == 0u); + mDevicesByType[type] = devices; } -const V4L2Device::Devices& V4L2Device::GetDevicesForType( - Type type) { - if (devices_by_type_.count(type) == 0) - EnumerateDevicesForType(type); +const V4L2Device::Devices& V4L2Device::getDevicesForType(Type type) { + if (mDevicesByType.count(type) == 0) enumerateDevicesForType(type); - DCHECK_NE(devices_by_type_.count(type), 0u); - return devices_by_type_[type]; + ALOG_ASSERT(mDevicesByType.count(type) != 0u); + return mDevicesByType[type]; } -std::string V4L2Device::GetDevicePathFor(Type type, uint32_t pixfmt) { - const Devices& devices = GetDevicesForType(type); +std::string V4L2Device::getDevicePathFor(Type type, uint32_t pixFmt) { + const Devices& devices = getDevicesForType(type); - for (const auto& device : devices) { - if (std::find(device.second.begin(), device.second.end(), pixfmt) != - device.second.end()) - return device.first; - } + for (const auto& device : devices) { + if (std::find(device.second.begin(), device.second.end(), pixFmt) != device.second.end()) + return device.first; + } - return std::string(); + return std::string(); } // static -uint32_t V4L2Device::VideoCodecProfileToV4L2PixFmt(VideoCodecProfile profile, - bool slice_based) { - if (profile >= H264PROFILE_MIN && profile <= H264PROFILE_MAX) { - if (slice_based) - return V4L2_PIX_FMT_H264_SLICE; - else - return V4L2_PIX_FMT_H264; - } else if (profile >= VP8PROFILE_MIN && profile <= VP8PROFILE_MAX) { - if (slice_based) - return V4L2_PIX_FMT_VP8_FRAME; - else - return V4L2_PIX_FMT_VP8; - } else if (profile >= VP9PROFILE_MIN && profile <= VP9PROFILE_MAX) { - if (slice_based) - return V4L2_PIX_FMT_VP9_FRAME; - else - return V4L2_PIX_FMT_VP9; - } else { - LOG(ERROR) << "Unknown profile: " << GetProfileName(profile); - return 0; - } +uint32_t V4L2Device::videoCodecProfileToV4L2PixFmt(media::VideoCodecProfile profile, + bool sliceBased) { + if (profile >= media::H264PROFILE_MIN && profile <= media::H264PROFILE_MAX) { + if (sliceBased) { + return V4L2_PIX_FMT_H264_SLICE; + } else { + return V4L2_PIX_FMT_H264; + } + } else if (profile >= media::VP8PROFILE_MIN && profile <= media::VP8PROFILE_MAX) { + if (sliceBased) { + return V4L2_PIX_FMT_VP8_FRAME; + } else { + return V4L2_PIX_FMT_VP8; + } + } else if (profile >= media::VP9PROFILE_MIN && profile <= media::VP9PROFILE_MAX) { + if (sliceBased) { + return V4L2_PIX_FMT_VP9_FRAME; + } else { + return V4L2_PIX_FMT_VP9; + } + } else { + ALOGE("Unknown profile: %s", GetProfileName(profile).c_str()); + return 0; + } } // static -VideoCodecProfile V4L2Device::V4L2ProfileToVideoCodecProfile(VideoCodec codec, - uint32_t profile) { - switch (codec) { - case kCodecH264: - switch (profile) { +media::VideoCodecProfile V4L2Device::v4L2ProfileToVideoCodecProfile(media::VideoCodec codec, + uint32_t profile) { + switch (codec) { + case media::kCodecH264: + switch (profile) { case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: - return H264PROFILE_BASELINE; + return media::H264PROFILE_BASELINE; case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: - return H264PROFILE_MAIN; + return media::H264PROFILE_MAIN; case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED: - return H264PROFILE_EXTENDED; + return media::H264PROFILE_EXTENDED; case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: - return H264PROFILE_HIGH; - } - break; - case kCodecVP8: - switch (profile) { + return media::H264PROFILE_HIGH; + } + break; + case media::kCodecVP8: + switch (profile) { case V4L2_MPEG_VIDEO_VP8_PROFILE_0: case V4L2_MPEG_VIDEO_VP8_PROFILE_1: case V4L2_MPEG_VIDEO_VP8_PROFILE_2: case V4L2_MPEG_VIDEO_VP8_PROFILE_3: - return VP8PROFILE_ANY; - } - break; - case kCodecVP9: - switch (profile) { + return media::VP8PROFILE_ANY; + } + break; + case media::kCodecVP9: + switch (profile) { case V4L2_MPEG_VIDEO_VP9_PROFILE_0: - return VP9PROFILE_PROFILE0; + return media::VP9PROFILE_PROFILE0; case V4L2_MPEG_VIDEO_VP9_PROFILE_1: - return VP9PROFILE_PROFILE1; + return media::VP9PROFILE_PROFILE1; case V4L2_MPEG_VIDEO_VP9_PROFILE_2: - return VP9PROFILE_PROFILE2; + return media::VP9PROFILE_PROFILE2; case V4L2_MPEG_VIDEO_VP9_PROFILE_3: - return VP9PROFILE_PROFILE3; - } - break; - default: - VLOGF(2) << "Unknown codec: " << codec; - } - VLOGF(2) << "Unknown profile: " << profile; - return VIDEO_CODEC_PROFILE_UNKNOWN; -} - -std::vector<VideoCodecProfile> V4L2Device::V4L2PixFmtToVideoCodecProfiles( - uint32_t pix_fmt, - bool /*is_encoder*/) { - auto get_supported_profiles = [this]( - VideoCodec codec, - std::vector<VideoCodecProfile>* profiles) { - uint32_t query_id = 0; - switch (codec) { - case kCodecH264: - query_id = V4L2_CID_MPEG_VIDEO_H264_PROFILE; - break; - case kCodecVP8: - query_id = V4L2_CID_MPEG_VIDEO_VP8_PROFILE; - break; - case kCodecVP9: - query_id = V4L2_CID_MPEG_VIDEO_VP9_PROFILE; + return media::VP9PROFILE_PROFILE3; + } break; - default: - return false; + default: + ALOGE("Unknown codec: %u", codec); } + ALOGE("Unknown profile: %u", profile); + return media::VIDEO_CODEC_PROFILE_UNKNOWN; +} + +std::vector<media::VideoCodecProfile> V4L2Device::v4L2PixFmtToVideoCodecProfiles( + uint32_t pixFmt, bool /*isEncoder*/) { + auto getSupportedProfiles = [this](media::VideoCodec codec, + std::vector<media::VideoCodecProfile>* profiles) { + uint32_t queryId = 0; + switch (codec) { + case media::kCodecH264: + queryId = V4L2_CID_MPEG_VIDEO_H264_PROFILE; + break; + case media::kCodecVP8: + queryId = V4L2_CID_MPEG_VIDEO_VP8_PROFILE; + break; + case media::kCodecVP9: + queryId = V4L2_CID_MPEG_VIDEO_VP9_PROFILE; + break; + default: + return false; + } - v4l2_queryctrl query_ctrl = {}; - query_ctrl.id = query_id; - if (Ioctl(VIDIOC_QUERYCTRL, &query_ctrl) != 0) { - return false; - } - v4l2_querymenu query_menu = {}; - query_menu.id = query_ctrl.id; - for (query_menu.index = query_ctrl.minimum; - static_cast<int>(query_menu.index) <= query_ctrl.maximum; - query_menu.index++) { - if (Ioctl(VIDIOC_QUERYMENU, &query_menu) == 0) { - const VideoCodecProfile profile = - V4L2Device::V4L2ProfileToVideoCodecProfile(codec, query_menu.index); - if (profile != VIDEO_CODEC_PROFILE_UNKNOWN) - profiles->push_back(profile); - } - } - return true; - }; + v4l2_queryctrl queryCtrl = {}; + queryCtrl.id = queryId; + if (ioctl(VIDIOC_QUERYCTRL, &queryCtrl) != 0) { + return false; + } + v4l2_querymenu queryMenu = {}; + queryMenu.id = queryCtrl.id; + for (queryMenu.index = queryCtrl.minimum; + static_cast<int>(queryMenu.index) <= queryCtrl.maximum; queryMenu.index++) { + if (ioctl(VIDIOC_QUERYMENU, &queryMenu) == 0) { + const media::VideoCodecProfile profile = + V4L2Device::v4L2ProfileToVideoCodecProfile(codec, queryMenu.index); + if (profile != media::VIDEO_CODEC_PROFILE_UNKNOWN) profiles->push_back(profile); + } + } + return true; + }; - std::vector<VideoCodecProfile> profiles; - switch (pix_fmt) { + std::vector<media::VideoCodecProfile> profiles; + switch (pixFmt) { case V4L2_PIX_FMT_H264: case V4L2_PIX_FMT_H264_SLICE: - if (!get_supported_profiles(kCodecH264, &profiles)) { - DLOG(WARNING) << "Driver doesn't support QUERY H264 profiles, " - << "use default values, Base, Main, High"; - profiles = { - H264PROFILE_BASELINE, - H264PROFILE_MAIN, - H264PROFILE_HIGH, - }; - } - break; + if (!getSupportedProfiles(media::kCodecH264, &profiles)) { + ALOGW("Driver doesn't support QUERY H264 profiles, " + "use default values, Base, Main, High"); + profiles = { + media::H264PROFILE_BASELINE, + media::H264PROFILE_MAIN, + media::H264PROFILE_HIGH, + }; + } + break; case V4L2_PIX_FMT_VP8: case V4L2_PIX_FMT_VP8_FRAME: - profiles = {VP8PROFILE_ANY}; - break; + profiles = {media::VP8PROFILE_ANY}; + break; case V4L2_PIX_FMT_VP9: case V4L2_PIX_FMT_VP9_FRAME: - if (!get_supported_profiles(kCodecVP9, &profiles)) { - DLOG(WARNING) << "Driver doesn't support QUERY VP9 profiles, " - << "use default values, Profile0"; - profiles = {VP9PROFILE_PROFILE0}; - } - break; + if (!getSupportedProfiles(media::kCodecVP9, &profiles)) { + ALOGW("Driver doesn't support QUERY VP9 profiles, " + "use default values, Profile0"); + profiles = {media::VP9PROFILE_PROFILE0}; + } + break; default: - VLOGF(1) << "Unhandled pixelformat " << FourccToString(pix_fmt); - return {}; - } + ALOGE("Unhandled pixelformat %s", media::FourccToString(pixFmt).c_str()); + return {}; + } - // Erase duplicated profiles. - std::sort(profiles.begin(), profiles.end()); - profiles.erase(std::unique(profiles.begin(), profiles.end()), profiles.end()); - return profiles; + // Erase duplicated profiles. + std::sort(profiles.begin(), profiles.end()); + profiles.erase(std::unique(profiles.begin(), profiles.end()), profiles.end()); + return profiles; } // static -int32_t V4L2Device::VideoCodecProfileToV4L2H264Profile( - VideoCodecProfile profile) { - switch (profile) { - case H264PROFILE_BASELINE: - return V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; - case H264PROFILE_MAIN: - return V4L2_MPEG_VIDEO_H264_PROFILE_MAIN; - case H264PROFILE_EXTENDED: - return V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED; - case H264PROFILE_HIGH: - return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH; - case H264PROFILE_HIGH10PROFILE: - return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10; - case H264PROFILE_HIGH422PROFILE: - return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422; - case H264PROFILE_HIGH444PREDICTIVEPROFILE: - return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE; - case H264PROFILE_SCALABLEBASELINE: - return V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_BASELINE; - case H264PROFILE_SCALABLEHIGH: - return V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH; - case H264PROFILE_STEREOHIGH: - return V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH; - case H264PROFILE_MULTIVIEWHIGH: - return V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH; +int32_t V4L2Device::videoCodecProfileToV4L2H264Profile(media::VideoCodecProfile profile) { + switch (profile) { + case media::H264PROFILE_BASELINE: + return V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; + case media::H264PROFILE_MAIN: + return V4L2_MPEG_VIDEO_H264_PROFILE_MAIN; + case media::H264PROFILE_EXTENDED: + return V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED; + case media::H264PROFILE_HIGH: + return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH; + case media::H264PROFILE_HIGH10PROFILE: + return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10; + case media::H264PROFILE_HIGH422PROFILE: + return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422; + case media::H264PROFILE_HIGH444PREDICTIVEPROFILE: + return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE; + case media::H264PROFILE_SCALABLEBASELINE: + return V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_BASELINE; + case media::H264PROFILE_SCALABLEHIGH: + return V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH; + case media::H264PROFILE_STEREOHIGH: + return V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH; + case media::H264PROFILE_MULTIVIEWHIGH: + return V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH; default: - DVLOGF(1) << "Add more cases as needed"; - return -1; - } + ALOGE("Add more cases as needed"); + return -1; + } } // static -int32_t V4L2Device::H264LevelIdcToV4L2H264Level(uint8_t level_idc) { - switch (level_idc) { +int32_t V4L2Device::h264LevelIdcToV4L2H264Level(uint8_t levelIdc) { + switch (levelIdc) { case 10: - return V4L2_MPEG_VIDEO_H264_LEVEL_1_0; + return V4L2_MPEG_VIDEO_H264_LEVEL_1_0; case 9: - return V4L2_MPEG_VIDEO_H264_LEVEL_1B; + return V4L2_MPEG_VIDEO_H264_LEVEL_1B; case 11: - return V4L2_MPEG_VIDEO_H264_LEVEL_1_1; + return V4L2_MPEG_VIDEO_H264_LEVEL_1_1; case 12: - return V4L2_MPEG_VIDEO_H264_LEVEL_1_2; + return V4L2_MPEG_VIDEO_H264_LEVEL_1_2; case 13: - return V4L2_MPEG_VIDEO_H264_LEVEL_1_3; + return V4L2_MPEG_VIDEO_H264_LEVEL_1_3; case 20: - return V4L2_MPEG_VIDEO_H264_LEVEL_2_0; + return V4L2_MPEG_VIDEO_H264_LEVEL_2_0; case 21: - return V4L2_MPEG_VIDEO_H264_LEVEL_2_1; + return V4L2_MPEG_VIDEO_H264_LEVEL_2_1; case 22: - return V4L2_MPEG_VIDEO_H264_LEVEL_2_2; + return V4L2_MPEG_VIDEO_H264_LEVEL_2_2; case 30: - return V4L2_MPEG_VIDEO_H264_LEVEL_3_0; + return V4L2_MPEG_VIDEO_H264_LEVEL_3_0; case 31: - return V4L2_MPEG_VIDEO_H264_LEVEL_3_1; + return V4L2_MPEG_VIDEO_H264_LEVEL_3_1; case 32: - return V4L2_MPEG_VIDEO_H264_LEVEL_3_2; + return V4L2_MPEG_VIDEO_H264_LEVEL_3_2; case 40: - return V4L2_MPEG_VIDEO_H264_LEVEL_4_0; + return V4L2_MPEG_VIDEO_H264_LEVEL_4_0; case 41: - return V4L2_MPEG_VIDEO_H264_LEVEL_4_1; + return V4L2_MPEG_VIDEO_H264_LEVEL_4_1; case 42: - return V4L2_MPEG_VIDEO_H264_LEVEL_4_2; + return V4L2_MPEG_VIDEO_H264_LEVEL_4_2; case 50: - return V4L2_MPEG_VIDEO_H264_LEVEL_5_0; + return V4L2_MPEG_VIDEO_H264_LEVEL_5_0; case 51: - return V4L2_MPEG_VIDEO_H264_LEVEL_5_1; + return V4L2_MPEG_VIDEO_H264_LEVEL_5_1; default: - DVLOGF(1) << "Unrecognized level_idc: " << static_cast<int>(level_idc); - return -1; - } + ALOGE("Unrecognized levelIdc: %u", static_cast<uint32_t>(levelIdc)); + return -1; + } } // static -Size V4L2Device::AllocatedSizeFromV4L2Format(const struct v4l2_format& format) { - Size coded_size; - Size visible_size; - VideoPixelFormat frame_format = PIXEL_FORMAT_UNKNOWN; - size_t bytesperline = 0; - // Total bytes in the frame. - size_t sizeimage = 0; - - if (V4L2_TYPE_IS_MULTIPLANAR(format.type)) { - DCHECK_GT(format.fmt.pix_mp.num_planes, 0); - bytesperline = - base::checked_cast<int>(format.fmt.pix_mp.plane_fmt[0].bytesperline); - for (size_t i = 0; i < format.fmt.pix_mp.num_planes; ++i) { - sizeimage += - base::checked_cast<int>(format.fmt.pix_mp.plane_fmt[i].sizeimage); - } - visible_size.SetSize(base::checked_cast<int>(format.fmt.pix_mp.width), - base::checked_cast<int>(format.fmt.pix_mp.height)); - const uint32_t pix_fmt = format.fmt.pix_mp.pixelformat; - const auto frame_fourcc = Fourcc::FromV4L2PixFmt(pix_fmt); - if (!frame_fourcc) { - VLOGF(1) << "Unsupported format " << FourccToString(pix_fmt); - return coded_size; - } - frame_format = frame_fourcc->ToVideoPixelFormat(); - } else { - bytesperline = base::checked_cast<int>(format.fmt.pix.bytesperline); - sizeimage = base::checked_cast<int>(format.fmt.pix.sizeimage); - visible_size.SetSize(base::checked_cast<int>(format.fmt.pix.width), - base::checked_cast<int>(format.fmt.pix.height)); - const uint32_t fourcc = format.fmt.pix.pixelformat; - const auto frame_fourcc = Fourcc::FromV4L2PixFmt(fourcc); - if (!frame_fourcc) { - VLOGF(1) << "Unsupported format " << FourccToString(fourcc); - return coded_size; - } - frame_format = frame_fourcc ? frame_fourcc->ToVideoPixelFormat() - : PIXEL_FORMAT_UNKNOWN; - } - - // V4L2 does not provide per-plane bytesperline (bpl) when different - // components are sharing one physical plane buffer. In this case, it only - // provides bpl for the first component in the plane. So we can't depend on it - // for calculating height, because bpl may vary within one physical plane - // buffer. For example, YUV420 contains 3 components in one physical plane, - // with Y at 8 bits per pixel, and Cb/Cr at 4 bits per pixel per component, - // but we only get 8 pits per pixel from bytesperline in physical plane 0. - // So we need to get total frame bpp from elsewhere to calculate coded height. - - // We need bits per pixel for one component only to calculate - // coded_width from bytesperline. - int plane_horiz_bits_per_pixel = - VideoFrame::PlaneHorizontalBitsPerPixel(frame_format, 0); - - // Adding up bpp for each component will give us total bpp for all components. - int total_bpp = 0; - for (size_t i = 0; i < VideoFrame::NumPlanes(frame_format); ++i) - total_bpp += VideoFrame::PlaneBitsPerPixel(frame_format, i); - - if (sizeimage == 0 || bytesperline == 0 || plane_horiz_bits_per_pixel == 0 || - total_bpp == 0 || (bytesperline * 8) % plane_horiz_bits_per_pixel != 0) { - VLOGF(1) << "Invalid format provided"; - return coded_size; - } - - // Coded width can be calculated by taking the first component's bytesperline, - // which in V4L2 always applies to the first component in physical plane - // buffer. - int coded_width = bytesperline * 8 / plane_horiz_bits_per_pixel; - // Sizeimage is coded_width * coded_height * total_bpp. - int coded_height = sizeimage * 8 / coded_width / total_bpp; - - coded_size.SetSize(coded_width, coded_height); - DVLOGF(3) << "coded_size=" << coded_size.ToString(); - - // Sanity checks. Calculated coded size has to contain given visible size - // and fulfill buffer byte size requirements. - DCHECK(Rect(coded_size).Contains(Rect(visible_size))); - DCHECK_LE(sizeimage, VideoFrame::AllocationSize(frame_format, coded_size)); - - return coded_size; +media::Size V4L2Device::allocatedSizeFromV4L2Format(const struct v4l2_format& format) { + media::Size codedSize; + media::Size visibleSize; + media::VideoPixelFormat frameFormat = media::PIXEL_FORMAT_UNKNOWN; + size_t bytesPerLine = 0; + // Total bytes in the frame. + size_t sizeimage = 0; + + if (V4L2_TYPE_IS_MULTIPLANAR(format.type)) { + ALOG_ASSERT(format.fmt.pix_mp.num_planes > 0); + bytesPerLine = base::checked_cast<int>(format.fmt.pix_mp.plane_fmt[0].bytesperline); + for (size_t i = 0; i < format.fmt.pix_mp.num_planes; ++i) { + sizeimage += base::checked_cast<int>(format.fmt.pix_mp.plane_fmt[i].sizeimage); + } + visibleSize.SetSize(base::checked_cast<int>(format.fmt.pix_mp.width), + base::checked_cast<int>(format.fmt.pix_mp.height)); + const uint32_t pixFmt = format.fmt.pix_mp.pixelformat; + const auto frameFourcc = media::Fourcc::FromV4L2PixFmt(pixFmt); + if (!frameFourcc) { + ALOGE("Unsupported format %s", media::FourccToString(pixFmt).c_str()); + return codedSize; + } + frameFormat = frameFourcc->ToVideoPixelFormat(); + } else { + bytesPerLine = base::checked_cast<int>(format.fmt.pix.bytesperline); + sizeimage = base::checked_cast<int>(format.fmt.pix.sizeimage); + visibleSize.SetSize(base::checked_cast<int>(format.fmt.pix.width), + base::checked_cast<int>(format.fmt.pix.height)); + const uint32_t fourcc = format.fmt.pix.pixelformat; + const auto frameFourcc = media::Fourcc::FromV4L2PixFmt(fourcc); + if (!frameFourcc) { + ALOGE("Unsupported format %s", media::FourccToString(fourcc).c_str()); + return codedSize; + } + frameFormat = frameFourcc ? frameFourcc->ToVideoPixelFormat() : media::PIXEL_FORMAT_UNKNOWN; + } + + // V4L2 does not provide per-plane bytesperline (bpl) when different components are sharing one + // physical plane buffer. In this case, it only provides bpl for the first component in the + // plane. So we can't depend on it for calculating height, because bpl may vary within one + // physical plane buffer. For example, YUV420 contains 3 components in one physical plane, with + // Y at 8 bits per pixel, and Cb/Cr at 4 bits per pixel per component, but we only get 8 pits + // per pixel from bytesperline in physical plane 0. So we need to get total frame bpp from + // elsewhere to calculate coded height. + + // We need bits per pixel for one component only to calculate the coded width from bytesperline. + int planeHorizBitsPerPixel = media::VideoFrame::PlaneHorizontalBitsPerPixel(frameFormat, 0); + + // Adding up bpp for each component will give us total bpp for all components. + int totalBpp = 0; + for (size_t i = 0; i < media::VideoFrame::NumPlanes(frameFormat); ++i) + totalBpp += media::VideoFrame::PlaneBitsPerPixel(frameFormat, i); + + if (sizeimage == 0 || bytesPerLine == 0 || planeHorizBitsPerPixel == 0 || totalBpp == 0 || + (bytesPerLine * 8) % planeHorizBitsPerPixel != 0) { + ALOGE("Invalid format provided"); + return codedSize; + } + + // Coded width can be calculated by taking the first component's bytesperline, which in V4L2 + // always applies to the first component in physical plane buffer. + int codedWidth = bytesPerLine * 8 / planeHorizBitsPerPixel; + // Sizeimage is codedWidth * codedHeight * totalBpp. + int codedHeight = sizeimage * 8 / codedWidth / totalBpp; + + codedSize.SetSize(codedWidth, codedHeight); + ALOGV("codedSize=%s", codedSize.ToString().c_str()); + + // Sanity checks. Calculated coded size has to contain given visible size and fulfill buffer + // byte size requirements. + ALOG_ASSERT(media::Rect(codedSize).Contains(media::Rect(visibleSize))); + ALOG_ASSERT(sizeimage <= media::VideoFrame::AllocationSize(frameFormat, codedSize)); + + return codedSize; } // static -const char* V4L2Device::V4L2MemoryToString(const v4l2_memory memory) { - switch (memory) { +const char* V4L2Device::v4L2MemoryToString(const v4l2_memory memory) { + switch (memory) { case V4L2_MEMORY_MMAP: - return "V4L2_MEMORY_MMAP"; + return "V4L2_MEMORY_MMAP"; case V4L2_MEMORY_USERPTR: - return "V4L2_MEMORY_USERPTR"; + return "V4L2_MEMORY_USERPTR"; case V4L2_MEMORY_DMABUF: - return "V4L2_MEMORY_DMABUF"; + return "V4L2_MEMORY_DMABUF"; case V4L2_MEMORY_OVERLAY: - return "V4L2_MEMORY_OVERLAY"; + return "V4L2_MEMORY_OVERLAY"; default: - return "UNKNOWN"; - } + return "UNKNOWN"; + } } // static -const char* V4L2Device::V4L2BufferTypeToString( - const enum v4l2_buf_type buf_type) { - switch (buf_type) { +const char* V4L2Device::v4L2BufferTypeToString(const enum v4l2_buf_type bufType) { + switch (bufType) { case V4L2_BUF_TYPE_VIDEO_OUTPUT: - return "OUTPUT"; + return "OUTPUT"; case V4L2_BUF_TYPE_VIDEO_CAPTURE: - return "CAPTURE"; + return "CAPTURE"; case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - return "OUTPUT_MPLANE"; + return "OUTPUT_MPLANE"; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - return "CAPTURE_MPLANE"; + return "CAPTURE_MPLANE"; default: - return "UNKNOWN"; - } + return "UNKNOWN"; + } } // static -std::string V4L2Device::V4L2FormatToString(const struct v4l2_format& format) { - std::ostringstream s; - s << "v4l2_format type: " << format.type; - if (format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE || - format.type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - // single-planar - const struct v4l2_pix_format& pix = format.fmt.pix; - s << ", width_height: " << Size(pix.width, pix.height).ToString() - << ", pixelformat: " << FourccToString(pix.pixelformat) - << ", field: " << pix.field << ", bytesperline: " << pix.bytesperline - << ", sizeimage: " << pix.sizeimage; - } else if (V4L2_TYPE_IS_MULTIPLANAR(format.type)) { - const struct v4l2_pix_format_mplane& pix_mp = format.fmt.pix_mp; - // As long as num_planes's type is uint8_t, ostringstream treats it as a - // char instead of an integer, which is not what we want. Casting - // pix_mp.num_planes unsigned int solves the issue. - s << ", width_height: " << Size(pix_mp.width, pix_mp.height).ToString() - << ", pixelformat: " << FourccToString(pix_mp.pixelformat) - << ", field: " << pix_mp.field - << ", num_planes: " << static_cast<unsigned int>(pix_mp.num_planes); - for (size_t i = 0; i < pix_mp.num_planes; ++i) { - const struct v4l2_plane_pix_format& plane_fmt = pix_mp.plane_fmt[i]; - s << ", plane_fmt[" << i << "].sizeimage: " << plane_fmt.sizeimage - << ", plane_fmt[" << i << "].bytesperline: " << plane_fmt.bytesperline; - } - } else { - s << " unsupported yet."; - } - return s.str(); +std::string V4L2Device::v4L2FormatToString(const struct v4l2_format& format) { + std::ostringstream s; + s << "v4l2_format type: " << format.type; + if (format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE || format.type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + // single-planar + const struct v4l2_pix_format& pix = format.fmt.pix; + s << ", width_height: " << media::Size(pix.width, pix.height).ToString() + << ", pixelformat: " << media::FourccToString(pix.pixelformat) << ", field: " << pix.field + << ", bytesperline: " << pix.bytesperline << ", sizeimage: " << pix.sizeimage; + } else if (V4L2_TYPE_IS_MULTIPLANAR(format.type)) { + const struct v4l2_pix_format_mplane& pixMp = format.fmt.pix_mp; + // As long as num_planes's type is uint8_t, ostringstream treats it as a char instead of an + // integer, which is not what we want. Casting pix_mp.num_planes unsigned int solves the + // issue. + s << ", width_height: " << media::Size(pixMp.width, pixMp.height).ToString() + << ", pixelformat: " << media::FourccToString(pixMp.pixelformat) + << ", field: " << pixMp.field + << ", num_planes: " << static_cast<unsigned int>(pixMp.num_planes); + for (size_t i = 0; i < pixMp.num_planes; ++i) { + const struct v4l2_plane_pix_format& plane_fmt = pixMp.plane_fmt[i]; + s << ", plane_fmt[" << i << "].sizeimage: " << plane_fmt.sizeimage << ", plane_fmt[" + << i << "].bytesperline: " << plane_fmt.bytesperline; + } + } else { + s << " unsupported yet."; + } + return s.str(); } // static -std::string V4L2Device::V4L2BufferToString(const struct v4l2_buffer& buffer) { - std::ostringstream s; - s << "v4l2_buffer type: " << buffer.type << ", memory: " << buffer.memory - << ", index: " << buffer.index << " bytesused: " << buffer.bytesused - << ", length: " << buffer.length; - if (buffer.type == V4L2_BUF_TYPE_VIDEO_CAPTURE || - buffer.type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - // single-planar - if (buffer.memory == V4L2_MEMORY_MMAP) { - s << ", m.offset: " << buffer.m.offset; - } else if (buffer.memory == V4L2_MEMORY_USERPTR) { - s << ", m.userptr: " << buffer.m.userptr; - } else if (buffer.memory == V4L2_MEMORY_DMABUF) { - s << ", m.fd: " << buffer.m.fd; - } - } else if (V4L2_TYPE_IS_MULTIPLANAR(buffer.type)) { - for (size_t i = 0; i < buffer.length; ++i) { - const struct v4l2_plane& plane = buffer.m.planes[i]; - s << ", m.planes[" << i << "](bytesused: " << plane.bytesused - << ", length: " << plane.length - << ", data_offset: " << plane.data_offset; - if (buffer.memory == V4L2_MEMORY_MMAP) { - s << ", m.mem_offset: " << plane.m.mem_offset; - } else if (buffer.memory == V4L2_MEMORY_USERPTR) { - s << ", m.userptr: " << plane.m.userptr; - } else if (buffer.memory == V4L2_MEMORY_DMABUF) { - s << ", m.fd: " << plane.m.fd; - } - s << ")"; - } - } else { - s << " unsupported yet."; - } - return s.str(); +std::string V4L2Device::v4L2BufferToString(const struct v4l2_buffer& buffer) { + std::ostringstream s; + s << "v4l2_buffer type: " << buffer.type << ", memory: " << buffer.memory + << ", index: " << buffer.index << " bytesused: " << buffer.bytesused + << ", length: " << buffer.length; + if (buffer.type == V4L2_BUF_TYPE_VIDEO_CAPTURE || buffer.type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + // single-planar + if (buffer.memory == V4L2_MEMORY_MMAP) { + s << ", m.offset: " << buffer.m.offset; + } else if (buffer.memory == V4L2_MEMORY_USERPTR) { + s << ", m.userptr: " << buffer.m.userptr; + } else if (buffer.memory == V4L2_MEMORY_DMABUF) { + s << ", m.fd: " << buffer.m.fd; + }; + } else if (V4L2_TYPE_IS_MULTIPLANAR(buffer.type)) { + for (size_t i = 0; i < buffer.length; ++i) { + const struct v4l2_plane& plane = buffer.m.planes[i]; + s << ", m.planes[" << i << "](bytesused: " << plane.bytesused + << ", length: " << plane.length << ", data_offset: " << plane.data_offset; + if (buffer.memory == V4L2_MEMORY_MMAP) { + s << ", m.mem_offset: " << plane.m.mem_offset; + } else if (buffer.memory == V4L2_MEMORY_USERPTR) { + s << ", m.userptr: " << plane.m.userptr; + } else if (buffer.memory == V4L2_MEMORY_DMABUF) { + s << ", m.fd: " << plane.m.fd; + } + s << ")"; + } + } else { + s << " unsupported yet."; + } + return s.str(); } // static -base::Optional<VideoFrameLayout> V4L2Device::V4L2FormatToVideoFrameLayout( - const struct v4l2_format& format) { - if (!V4L2_TYPE_IS_MULTIPLANAR(format.type)) { - VLOGF(1) << "v4l2_buf_type is not multiplanar: " << std::hex << "0x" - << format.type; - return base::nullopt; - } - const v4l2_pix_format_mplane& pix_mp = format.fmt.pix_mp; - const uint32_t& pix_fmt = pix_mp.pixelformat; - const auto video_fourcc = Fourcc::FromV4L2PixFmt(pix_fmt); - if (!video_fourcc) { - VLOGF(1) << "Failed to convert pixel format to VideoPixelFormat: " - << FourccToString(pix_fmt); - return base::nullopt; - } - const VideoPixelFormat video_format = video_fourcc->ToVideoPixelFormat(); - const size_t num_buffers = pix_mp.num_planes; - const size_t num_color_planes = VideoFrame::NumPlanes(video_format); - if (num_color_planes == 0) { - VLOGF(1) << "Unsupported video format for NumPlanes(): " - << VideoPixelFormatToString(video_format); - return base::nullopt; - } - if (num_buffers > num_color_planes) { - VLOGF(1) << "pix_mp.num_planes: " << num_buffers - << " should not be larger than NumPlanes(" - << VideoPixelFormatToString(video_format) - << "): " << num_color_planes; - return base::nullopt; - } - // Reserve capacity in advance to prevent unnecessary vector reallocation. - std::vector<ColorPlaneLayout> planes; - planes.reserve(num_color_planes); - for (size_t i = 0; i < num_buffers; ++i) { - const v4l2_plane_pix_format& plane_format = pix_mp.plane_fmt[i]; - planes.emplace_back(static_cast<int32_t>(plane_format.bytesperline), 0u, - plane_format.sizeimage); - } - // For the case that #color planes > #buffers, it fills stride of color - // plane which does not map to buffer. - // Right now only some pixel formats are supported: NV12, YUV420, YVU420. - if (num_color_planes > num_buffers) { - const int32_t y_stride = planes[0].stride; - // Note that y_stride is from v4l2 bytesperline and its type is uint32_t. - // It is safe to cast to size_t. - const size_t y_stride_abs = static_cast<size_t>(y_stride); - switch (pix_fmt) { - case V4L2_PIX_FMT_NV12: - // The stride of UV is the same as Y in NV12. - // The height is half of Y plane. - planes.emplace_back(y_stride, y_stride_abs * pix_mp.height, - y_stride_abs * pix_mp.height / 2); - DCHECK_EQ(2u, planes.size()); - break; - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: { - // The spec claims that two Cx rows (including padding) is exactly as - // long as one Y row (including padding). So stride of Y must be even - // number. - if (y_stride % 2 != 0 || pix_mp.height % 2 != 0) { - VLOGF(1) << "Plane-Y stride and height should be even; stride: " - << y_stride << ", height: " << pix_mp.height; - return base::nullopt; +std::optional<media::VideoFrameLayout> V4L2Device::v4L2FormatToVideoFrameLayout( + const struct v4l2_format& format) { + if (!V4L2_TYPE_IS_MULTIPLANAR(format.type)) { + ALOGE("v4l2_buf_type is not multiplanar: 0x%" PRIx32, format.type); + return std::nullopt; + } + const v4l2_pix_format_mplane& pixMp = format.fmt.pix_mp; + const uint32_t& pixFmt = pixMp.pixelformat; + const auto videoFourcc = media::Fourcc::FromV4L2PixFmt(pixFmt); + if (!videoFourcc) { + ALOGE("Failed to convert pixel format to VideoPixelFormat: %s", + media::FourccToString(pixFmt).c_str()); + return std::nullopt; + } + const media::VideoPixelFormat videoFormat = videoFourcc->ToVideoPixelFormat(); + const size_t numBuffers = pixMp.num_planes; + const size_t numColorPlanes = media::VideoFrame::NumPlanes(videoFormat); + if (numColorPlanes == 0) { + ALOGE("Unsupported video format for NumPlanes(): %s", + VideoPixelFormatToString(videoFormat).c_str()); + return std::nullopt; + } + if (numBuffers > numColorPlanes) { + ALOGE("pix_mp.num_planes: %zu should not be larger than NumPlanes(%s): %zu", numBuffers, + VideoPixelFormatToString(videoFormat).c_str(), numColorPlanes); + return std::nullopt; + } + // Reserve capacity in advance to prevent unnecessary vector reallocation. + std::vector<media::ColorPlaneLayout> planes; + planes.reserve(numColorPlanes); + for (size_t i = 0; i < numBuffers; ++i) { + const v4l2_plane_pix_format& planeFormat = pixMp.plane_fmt[i]; + planes.emplace_back(static_cast<int32_t>(planeFormat.bytesperline), 0u, + planeFormat.sizeimage); + } + // For the case that #color planes > #buffers, it fills stride of color plane which does not map + // to buffer. Right now only some pixel formats are supported: NV12, YUV420, YVU420. + if (numColorPlanes > numBuffers) { + const int32_t yStride = planes[0].stride; + // Note that y_stride is from v4l2 bytesperline and its type is uint32_t. It is safe to cast + // to size_t. + const size_t yStrideAbs = static_cast<size_t>(yStride); + switch (pixFmt) { + case V4L2_PIX_FMT_NV12: + // The stride of UV is the same as Y in NV12. The height is half of Y plane. + planes.emplace_back(yStride, yStrideAbs * pixMp.height, yStrideAbs * pixMp.height / 2); + ALOG_ASSERT(2u == planes.size()); + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: { + // The spec claims that two Cx rows (including padding) is exactly as long as one Y row + // (including padding). So stride of Y must be even number. + if (yStride % 2 != 0 || pixMp.height % 2 != 0) { + ALOGE("Plane-Y stride and height should be even; stride: %i, height: %u", yStride, + pixMp.height); + return std::nullopt; + } + const int32_t halfStride = yStride / 2; + const size_t plane0Area = yStrideAbs * pixMp.height; + const size_t plane1Area = plane0Area / 4; + planes.emplace_back(halfStride, plane0Area, plane1Area); + planes.emplace_back(halfStride, plane0Area + plane1Area, plane1Area); + ALOG_ASSERT(3u == planes.size()); + break; } - const int32_t half_stride = y_stride / 2; - const size_t plane_0_area = y_stride_abs * pix_mp.height; - const size_t plane_1_area = plane_0_area / 4; - planes.emplace_back(half_stride, plane_0_area, plane_1_area); - planes.emplace_back(half_stride, plane_0_area + plane_1_area, - plane_1_area); - DCHECK_EQ(3u, planes.size()); - break; - } - default: - VLOGF(1) << "Cannot derive stride for each plane for pixel format " - << FourccToString(pix_fmt); - return base::nullopt; - } - } - - // Some V4L2 devices expect buffers to be page-aligned. We cannot detect - // such devices individually, so set this as a video frame layout property. - constexpr size_t buffer_alignment = 0x1000; - if (num_buffers == 1) { - return VideoFrameLayout::CreateWithPlanes( - video_format, Size(pix_mp.width, pix_mp.height), std::move(planes), - buffer_alignment); - } else { - return VideoFrameLayout::CreateMultiPlanar( - video_format, Size(pix_mp.width, pix_mp.height), std::move(planes), - buffer_alignment); - } + default: + ALOGE("Cannot derive stride for each plane for pixel format %s", + media::FourccToString(pixFmt).c_str()); + return std::nullopt; + } + } + + // Some V4L2 devices expect buffers to be page-aligned. We cannot detect such devices + // individually, so set this as a video frame layout property. + constexpr size_t bufferAlignment = 0x1000; + if (numBuffers == 1) { + return media::VideoFrameLayout::CreateWithPlanes(videoFormat, + media::Size(pixMp.width, pixMp.height), + std::move(planes), bufferAlignment); + } else { + return media::VideoFrameLayout::CreateMultiPlanar(videoFormat, + media::Size(pixMp.width, pixMp.height), + std::move(planes), bufferAlignment); + } } // static -size_t V4L2Device::GetNumPlanesOfV4L2PixFmt(uint32_t pix_fmt) { - base::Optional<Fourcc> fourcc = Fourcc::FromV4L2PixFmt(pix_fmt); - if (fourcc && fourcc->IsMultiPlanar()) { - return VideoFrame::NumPlanes(fourcc->ToVideoPixelFormat()); - } - return 1u; -} - -void V4L2Device::GetSupportedResolution(uint32_t pixelformat, - Size* min_resolution, - Size* max_resolution) { - max_resolution->SetSize(0, 0); - min_resolution->SetSize(0, 0); - v4l2_frmsizeenum frame_size; - memset(&frame_size, 0, sizeof(frame_size)); - frame_size.pixel_format = pixelformat; - for (; Ioctl(VIDIOC_ENUM_FRAMESIZES, &frame_size) == 0; ++frame_size.index) { - if (frame_size.type == V4L2_FRMSIZE_TYPE_DISCRETE) { - if (frame_size.discrete.width >= - base::checked_cast<uint32_t>(max_resolution->width()) && - frame_size.discrete.height >= - base::checked_cast<uint32_t>(max_resolution->height())) { - max_resolution->SetSize(frame_size.discrete.width, - frame_size.discrete.height); - } - if (min_resolution->IsEmpty() || - (frame_size.discrete.width <= - base::checked_cast<uint32_t>(min_resolution->width()) && - frame_size.discrete.height <= - base::checked_cast<uint32_t>(min_resolution->height()))) { - min_resolution->SetSize(frame_size.discrete.width, - frame_size.discrete.height); - } - } else if (frame_size.type == V4L2_FRMSIZE_TYPE_STEPWISE || - frame_size.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) { - max_resolution->SetSize(frame_size.stepwise.max_width, - frame_size.stepwise.max_height); - min_resolution->SetSize(frame_size.stepwise.min_width, - frame_size.stepwise.min_height); - break; - } - } - if (max_resolution->IsEmpty()) { - max_resolution->SetSize(1920, 1088); - VLOGF(1) << "GetSupportedResolution failed to get maximum resolution for " - << "fourcc " << FourccToString(pixelformat) << ", fall back to " - << max_resolution->ToString(); - } - if (min_resolution->IsEmpty()) { - min_resolution->SetSize(16, 16); - VLOGF(1) << "GetSupportedResolution failed to get minimum resolution for " - << "fourcc " << FourccToString(pixelformat) << ", fall back to " - << min_resolution->ToString(); - } -} - -std::vector<uint32_t> V4L2Device::EnumerateSupportedPixelformats( - v4l2_buf_type buf_type) { - std::vector<uint32_t> pixelformats; - - v4l2_fmtdesc fmtdesc; - memset(&fmtdesc, 0, sizeof(fmtdesc)); - fmtdesc.type = buf_type; - - for (; Ioctl(VIDIOC_ENUM_FMT, &fmtdesc) == 0; ++fmtdesc.index) { - DVLOGF(3) << "Found " << fmtdesc.description << std::hex << " (0x" - << fmtdesc.pixelformat << ")"; - pixelformats.push_back(fmtdesc.pixelformat); - } +size_t V4L2Device::getNumPlanesOfV4L2PixFmt(uint32_t pixFmt) { + std::optional<media::Fourcc> fourcc = media::Fourcc::FromV4L2PixFmt(pixFmt); + if (fourcc && fourcc->IsMultiPlanar()) { + return media::VideoFrame::NumPlanes(fourcc->ToVideoPixelFormat()); + } + return 1u; +} + +void V4L2Device::getSupportedResolution(uint32_t pixelFormat, media::Size* minResolution, + media::Size* maxResolution) { + maxResolution->SetSize(0, 0); + minResolution->SetSize(0, 0); + v4l2_frmsizeenum frameSize; + memset(&frameSize, 0, sizeof(frameSize)); + frameSize.pixel_format = pixelFormat; + for (; ioctl(VIDIOC_ENUM_FRAMESIZES, &frameSize) == 0; ++frameSize.index) { + if (frameSize.type == V4L2_FRMSIZE_TYPE_DISCRETE) { + if (frameSize.discrete.width >= base::checked_cast<uint32_t>(maxResolution->width()) && + frameSize.discrete.height >= + base::checked_cast<uint32_t>(maxResolution->height())) { + maxResolution->SetSize(frameSize.discrete.width, frameSize.discrete.height); + } + if (minResolution->IsEmpty() || + (frameSize.discrete.width <= base::checked_cast<uint32_t>(minResolution->width()) && + frameSize.discrete.height <= + base::checked_cast<uint32_t>(minResolution->height()))) { + minResolution->SetSize(frameSize.discrete.width, frameSize.discrete.height); + } + } else if (frameSize.type == V4L2_FRMSIZE_TYPE_STEPWISE || + frameSize.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) { + maxResolution->SetSize(frameSize.stepwise.max_width, frameSize.stepwise.max_height); + minResolution->SetSize(frameSize.stepwise.min_width, frameSize.stepwise.min_height); + break; + } + } + if (maxResolution->IsEmpty()) { + maxResolution->SetSize(1920, 1088); + ALOGE("GetSupportedResolution failed to get maximum resolution for fourcc %s, " + "fall back to %s", + media::FourccToString(pixelFormat).c_str(), maxResolution->ToString().c_str()); + } + if (minResolution->IsEmpty()) { + minResolution->SetSize(16, 16); + ALOGE("GetSupportedResolution failed to get minimum resolution for fourcc %s, " + "fall back to %s", + media::FourccToString(pixelFormat).c_str(), minResolution->ToString().c_str()); + } +} + +std::vector<uint32_t> V4L2Device::enumerateSupportedPixelformats(v4l2_buf_type bufType) { + std::vector<uint32_t> pixelFormats; - return pixelformats; + v4l2_fmtdesc fmtDesc; + memset(&fmtDesc, 0, sizeof(fmtDesc)); + fmtDesc.type = bufType; + + for (; ioctl(VIDIOC_ENUM_FMT, &fmtDesc) == 0; ++fmtDesc.index) { + ALOGV("Found %s (0x%" PRIx32 ")", fmtDesc.description, fmtDesc.pixelformat); + pixelFormats.push_back(fmtDesc.pixelformat); + } + + return pixelFormats; } -VideoDecodeAccelerator::SupportedProfiles -V4L2Device::EnumerateSupportedDecodeProfiles(const size_t num_formats, - const uint32_t pixelformats[]) { - VideoDecodeAccelerator::SupportedProfiles profiles; +media::VideoDecodeAccelerator::SupportedProfiles V4L2Device::enumerateSupportedDecodeProfiles( + const size_t numFormats, const uint32_t pixelFormats[]) { + media::VideoDecodeAccelerator::SupportedProfiles profiles; - const auto& supported_pixelformats = - EnumerateSupportedPixelformats(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + const auto& supportedPixelformats = + enumerateSupportedPixelformats(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - for (uint32_t pixelformat : supported_pixelformats) { - if (std::find(pixelformats, pixelformats + num_formats, pixelformat) == - pixelformats + num_formats) - continue; + for (uint32_t pixelFormat : supportedPixelformats) { + if (std::find(pixelFormats, pixelFormats + numFormats, pixelFormat) == + pixelFormats + numFormats) + continue; - VideoDecodeAccelerator::SupportedProfile profile; - GetSupportedResolution(pixelformat, &profile.min_resolution, - &profile.max_resolution); + media::VideoDecodeAccelerator::SupportedProfile profile; + getSupportedResolution(pixelFormat, &profile.min_resolution, &profile.max_resolution); - const auto video_codec_profiles = - V4L2PixFmtToVideoCodecProfiles(pixelformat, false); + const auto videoCodecProfiles = v4L2PixFmtToVideoCodecProfiles(pixelFormat, false); - for (const auto& video_codec_profile : video_codec_profiles) { - profile.profile = video_codec_profile; - profiles.push_back(profile); + for (const auto& videoCodecProfile : videoCodecProfiles) { + profile.profile = videoCodecProfile; + profiles.push_back(profile); - DVLOGF(3) << "Found decoder profile " << GetProfileName(profile.profile) - << ", resolutions: " << profile.min_resolution.ToString() << " " - << profile.max_resolution.ToString(); + ALOGV("Found decoder profile %s, resolutions: %s %s", + GetProfileName(profile.profile).c_str(), + profile.min_resolution.ToString().c_str(), + profile.max_resolution.ToString().c_str()); + } } - } - return profiles; + return profiles; } -VideoEncodeAccelerator::SupportedProfiles -V4L2Device::EnumerateSupportedEncodeProfiles() { - VideoEncodeAccelerator::SupportedProfiles profiles; +media::VideoEncodeAccelerator::SupportedProfiles V4L2Device::enumerateSupportedEncodeProfiles() { + media::VideoEncodeAccelerator::SupportedProfiles profiles; - const auto& supported_pixelformats = - EnumerateSupportedPixelformats(V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + const auto& supportedPixelformats = + enumerateSupportedPixelformats(V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - for (const auto& pixelformat : supported_pixelformats) { - VideoEncodeAccelerator::SupportedProfile profile; - profile.max_framerate_numerator = 30; - profile.max_framerate_denominator = 1; - Size min_resolution; - GetSupportedResolution(pixelformat, &min_resolution, - &profile.max_resolution); + for (const auto& pixelformat : supportedPixelformats) { + media::VideoEncodeAccelerator::SupportedProfile profile; + profile.max_framerate_numerator = 30; + profile.max_framerate_denominator = 1; + media::Size minResolution; + getSupportedResolution(pixelformat, &minResolution, &profile.max_resolution); - const auto video_codec_profiles = - V4L2PixFmtToVideoCodecProfiles(pixelformat, true); + const auto videoCodecProfiles = v4L2PixFmtToVideoCodecProfiles(pixelformat, true); - for (const auto& video_codec_profile : video_codec_profiles) { - profile.profile = video_codec_profile; - profiles.push_back(profile); + for (const auto& videoCodecProfile : videoCodecProfiles) { + profile.profile = videoCodecProfile; + profiles.push_back(profile); - DVLOGF(3) << "Found encoder profile " << GetProfileName(profile.profile) - << ", max resolution: " << profile.max_resolution.ToString(); + ALOGV("Found encoder profile %s, max resolution: %s", + GetProfileName(profile.profile).c_str(), + profile.max_resolution.ToString().c_str()); + } } - } - return profiles; + return profiles; } -bool V4L2Device::StartPolling(V4L2DevicePoller::EventCallback event_callback, - base::RepeatingClosure error_callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_); +bool V4L2Device::startPolling(android::V4L2DevicePoller::EventCallback eventCallback, + base::RepeatingClosure errorCallback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(mClientSequenceChecker); - if (!device_poller_) { - device_poller_ = - std::make_unique<V4L2DevicePoller>(this, "V4L2DeviceThreadPoller"); - } + if (!mDevicePoller) { + mDevicePoller = std::make_unique<android::V4L2DevicePoller>(this, "V4L2DeviceThreadPoller"); + } - bool ret = device_poller_->StartPolling(std::move(event_callback), - std::move(error_callback)); + bool ret = mDevicePoller->startPolling(std::move(eventCallback), std::move(errorCallback)); - if (!ret) - device_poller_ = nullptr; + if (!ret) mDevicePoller = nullptr; - return ret; + return ret; } -bool V4L2Device::StopPolling() { - DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_); +bool V4L2Device::stopPolling() { + DCHECK_CALLED_ON_VALID_SEQUENCE(mClientSequenceChecker); - return !device_poller_ || device_poller_->StopPolling(); + return !mDevicePoller || mDevicePoller->stopPolling(); } -void V4L2Device::SchedulePoll() { - DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_); +void V4L2Device::schedulePoll() { + DCHECK_CALLED_ON_VALID_SEQUENCE(mClientSequenceChecker); - if (!device_poller_ || !device_poller_->IsPolling()) - return; + if (!mDevicePoller || !mDevicePoller->isPolling()) return; - device_poller_->SchedulePoll(); + mDevicePoller->schedulePoll(); } -bool V4L2Device::IsCtrlExposed(uint32_t ctrl_id) { - DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_); +bool V4L2Device::isCtrlExposed(uint32_t ctrlId) { + DCHECK_CALLED_ON_VALID_SEQUENCE(mClientSequenceChecker); - struct v4l2_queryctrl query_ctrl; - memset(&query_ctrl, 0, sizeof(query_ctrl)); - query_ctrl.id = ctrl_id; + struct v4l2_queryctrl queryCtrl; + memset(&queryCtrl, 0, sizeof(queryCtrl)); + queryCtrl.id = ctrlId; - return Ioctl(VIDIOC_QUERYCTRL, &query_ctrl) == 0; + return ioctl(VIDIOC_QUERYCTRL, &queryCtrl) == 0; } -bool V4L2Device::SetExtCtrls(uint32_t ctrl_class, - std::vector<V4L2ExtCtrl> ctrls) { - DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_); +bool V4L2Device::setExtCtrls(uint32_t ctrlClass, std::vector<V4L2ExtCtrl> ctrls) { + DCHECK_CALLED_ON_VALID_SEQUENCE(mClientSequenceChecker); - if (ctrls.empty()) - return true; + if (ctrls.empty()) return true; - struct v4l2_ext_controls ext_ctrls; - memset(&ext_ctrls, 0, sizeof(ext_ctrls)); - ext_ctrls.ctrl_class = ctrl_class; - ext_ctrls.count = ctrls.size(); - ext_ctrls.controls = &ctrls[0].ctrl; - return Ioctl(VIDIOC_S_EXT_CTRLS, &ext_ctrls) == 0; + struct v4l2_ext_controls extCtrls; + memset(&extCtrls, 0, sizeof(extCtrls)); + extCtrls.ctrl_class = ctrlClass; + extCtrls.count = ctrls.size(); + extCtrls.controls = &ctrls[0].ctrl; + return ioctl(VIDIOC_S_EXT_CTRLS, &extCtrls) == 0; } -bool V4L2Device::IsCommandSupported(uint32_t command_id) { - DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_); +bool V4L2Device::isCommandSupported(uint32_t commandId) { + DCHECK_CALLED_ON_VALID_SEQUENCE(mClientSequenceChecker); - struct v4l2_encoder_cmd cmd; - memset(&cmd, 0, sizeof(cmd)); - cmd.cmd = command_id; + struct v4l2_encoder_cmd cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd = commandId; - return Ioctl(VIDIOC_TRY_ENCODER_CMD, &cmd) == 0; + return ioctl(VIDIOC_TRY_ENCODER_CMD, &cmd) == 0; } -bool V4L2Device::HasCapabilities(uint32_t capabilities) { - DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_); +bool V4L2Device::hasCapabilities(uint32_t capabilities) { + DCHECK_CALLED_ON_VALID_SEQUENCE(mClientSequenceChecker); - struct v4l2_capability caps; - memset(&caps, 0, sizeof(caps)); - if (Ioctl(VIDIOC_QUERYCAP, &caps) != 0) { - LOG(ERROR) << "Failed to query capabilities"; - return false; - } + struct v4l2_capability caps; + memset(&caps, 0, sizeof(caps)); + if (ioctl(VIDIOC_QUERYCAP, &caps) != 0) { + ALOGE("Failed to query capabilities"); + return false; + } - return (caps.capabilities & capabilities) == capabilities; + return (caps.capabilities & capabilities) == capabilities; } -} // namespace media +} // namespace android diff --git a/common/V4L2DevicePoller.cpp b/common/V4L2DevicePoller.cpp index 618dca3..5f2d0a5 100644 --- a/common/V4L2DevicePoller.cpp +++ b/common/V4L2DevicePoller.cpp @@ -3,138 +3,128 @@ // found in the LICENSE file. // Note: ported from Chromium commit head: 22d34680c8ac -#include "v4l2_codec2/common/V4L2DevicePoller.h" +#include <v4l2_codec2/common/V4L2DevicePoller.h> #include <string> -#include "base/bind.h" -#include "base/threading/sequenced_task_runner_handle.h" -#include "base/threading/thread_checker.h" +#include <base/bind.h> +#include <base/threading/sequenced_task_runner_handle.h> +#include <base/threading/thread_checker.h> +#include <log/log.h> -#include "macros.h" -#include "v4l2_codec2/common/V4L2Device.h" +#include <v4l2_codec2/common/V4L2Device.h> -namespace media { +namespace android { -V4L2DevicePoller::V4L2DevicePoller(V4L2Device* const device, - const std::string& thread_name) - : device_(device), - poll_thread_(std::move(thread_name)), - trigger_poll_(base::WaitableEvent::ResetPolicy::AUTOMATIC, - base::WaitableEvent::InitialState::NOT_SIGNALED), - stop_polling_(false) { - DETACH_FROM_SEQUENCE(client_sequence_checker_); -} +V4L2DevicePoller::V4L2DevicePoller(V4L2Device* const device, const std::string& threadName) + : mDevice(device), + mPollThread(std::move(threadName)), + mTriggerPoll(base::WaitableEvent::ResetPolicy::AUTOMATIC, + base::WaitableEvent::InitialState::NOT_SIGNALED), + mStopPolling(false) {} V4L2DevicePoller::~V4L2DevicePoller() { - DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_); + ALOG_ASSERT(mClientTaskTunner->RunsTasksInCurrentSequence()); - StopPolling(); + stopPolling(); } -bool V4L2DevicePoller::StartPolling(EventCallback event_callback, - base::RepeatingClosure error_callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_); - - if (IsPolling()) - return true; +bool V4L2DevicePoller::startPolling(EventCallback eventCallback, + base::RepeatingClosure errorCallback) { + if (isPolling()) return true; - DVLOGF(4) << "Starting polling"; + ALOGV("Starting polling"); - client_task_runner_ = base::SequencedTaskRunnerHandle::Get(); - error_callback_ = error_callback; + mClientTaskTunner = base::SequencedTaskRunnerHandle::Get(); + mErrorCallback = errorCallback; - if (!poll_thread_.Start()) { - VLOGF(1) << "Failed to start device poll thread"; - return false; - } + if (!mPollThread.Start()) { + ALOGE("Failed to start device poll thread"); + return false; + } - event_callback_ = std::move(event_callback); + mEventCallback = std::move(eventCallback); - stop_polling_.store(false); - poll_thread_.task_runner()->PostTask( - FROM_HERE, base::BindOnce(&V4L2DevicePoller::DevicePollTask, - base::Unretained(this))); + mStopPolling.store(false); + mPollThread.task_runner()->PostTask( + FROM_HERE, base::BindOnce(&V4L2DevicePoller::devicePollTask, base::Unretained(this))); - DVLOGF(3) << "Polling thread started"; + ALOGV("Polling thread started"); - SchedulePoll(); + schedulePoll(); - return true; + return true; } -bool V4L2DevicePoller::StopPolling() { - DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_); +bool V4L2DevicePoller::stopPolling() { + ALOG_ASSERT(mClientTaskTunner->RunsTasksInCurrentSequence()); - if (!IsPolling()) - return true; + if (!isPolling()) return true; - DVLOGF(4) << "Stopping polling"; + ALOGV("Stopping polling"); - stop_polling_.store(true); + mStopPolling.store(true); - trigger_poll_.Signal(); + mTriggerPoll.Signal(); - if (!device_->SetDevicePollInterrupt()) { - VLOGF(1) << "Failed to interrupt device poll."; - return false; - } + if (!mDevice->setDevicePollInterrupt()) { + ALOGE("Failed to interrupt device poll."); + return false; + } - DVLOGF(3) << "Stop device poll thread"; - poll_thread_.Stop(); + ALOGV("Stop device poll thread"); + mPollThread.Stop(); - if (!device_->ClearDevicePollInterrupt()) { - VLOGF(1) << "Failed to clear interrupting device poll."; - return false; - } + if (!mDevice->clearDevicePollInterrupt()) { + ALOGE("Failed to clear interrupting device poll."); + return false; + } - DVLOGF(4) << "Polling thread stopped"; + ALOGV("Polling thread stopped"); - return true; + return true; } -bool V4L2DevicePoller::IsPolling() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_); +bool V4L2DevicePoller::isPolling() const { + ALOG_ASSERT(mClientTaskTunner->RunsTasksInCurrentSequence()); - return poll_thread_.IsRunning(); + return mPollThread.IsRunning(); } -void V4L2DevicePoller::SchedulePoll() { - DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_); +void V4L2DevicePoller::schedulePoll() { + ALOG_ASSERT(mClientTaskTunner->RunsTasksInCurrentSequence()); - // A call to DevicePollTask() will be posted when we actually start polling. - if (!IsPolling()) - return; + // A call to DevicePollTask() will be posted when we actually start polling. + if (!isPolling()) return; - DVLOGF(4) << "Scheduling poll"; + ALOGV("Scheduling poll"); - trigger_poll_.Signal(); + mTriggerPoll.Signal(); } -void V4L2DevicePoller::DevicePollTask() { - DCHECK(poll_thread_.task_runner()->RunsTasksInCurrentSequence()); +void V4L2DevicePoller::devicePollTask() { + ALOG_ASSERT(mClientTaskTunner->RunsTasksInCurrentSequence()); - while (true) { - DVLOGF(4) << "Waiting for poll to be scheduled."; - trigger_poll_.Wait(); + while (true) { + ALOGV("Waiting for poll to be scheduled."); + mTriggerPoll.Wait(); - if (stop_polling_) { - DVLOGF(4) << "Poll stopped, exiting."; - break; - } + if (mStopPolling) { + ALOGV("Poll stopped, exiting."); + break; + } - bool event_pending = false; - DVLOGF(4) << "Polling device."; - if (!device_->Poll(true, &event_pending)) { - VLOGF(1) << "An error occurred while polling, calling error callback"; - client_task_runner_->PostTask(FROM_HERE, error_callback_); - return; - } + bool event_pending = false; + ALOGV("Polling device."); + if (!mDevice->poll(true, &event_pending)) { + ALOGE("An error occurred while polling, calling error callback"); + mClientTaskTunner->PostTask(FROM_HERE, mErrorCallback); + return; + } - DVLOGF(4) << "Poll returned, calling event callback."; - client_task_runner_->PostTask(FROM_HERE, - base::Bind(event_callback_, event_pending)); - } + ALOGV("Poll returned, calling event callback."); + mClientTaskTunner->PostTask(FROM_HERE, base::Bind(mEventCallback, event_pending)); + } } -} // namespace media +} // namespace android diff --git a/common/include/v4l2_codec2/common/V4L2Device.h b/common/include/v4l2_codec2/common/V4L2Device.h index fcd34b8..70a72b7 100644 --- a/common/include/v4l2_codec2/common/V4L2Device.h +++ b/common/include/v4l2_codec2/common/V4L2Device.h @@ -2,49 +2,35 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // -// This file defines the V4L2Device interface which is used by the -// V4L2DecodeAccelerator class to delegate/pass the device specific -// handling of any of the functionalities. -// Note: ported from Chromium commit head: 2f13d62f0c0d -// Note: the complete v4l2 device code is ported from Chromium, but some parts -// have been removed: -// - All V4L2 request functionality has been removed, as it required a newer -// kernel version. -// - void SetConfigStore() has been removed as it depends on a newer kernel -// version. -// - QueueDMABuf() from native pixmap planes has been removed, as -// NativePixmapPlane have not been ported. -// - GetVideoFrame() is removed as it depends on some helper functions that have -// not been ported. -// - GL-related functionality has been removed: canCreateEGLImageFrom(), -// CreateEGLImage(), CreateGLImage() and GetTextureTarget() -// - V4L2PixFmtToDrmFormat() has been removed, as DRM is not supported yet. - -#ifndef V4L2_DEVICE_H_ -#define V4L2_DEVICE_H_ +// This file defines the V4L2Device which is used by the V4L2Decoder and V4L2Encoder classes to +// delegate/pass the device specific handling of any of the functionalities. +// Note: ported from Chromium commit head: 2f13d62f0c0d, but some parts have been removed. + +#ifndef ANDROID_V4L2_CODEC2_COMMON_V4L2_DEVICE_H +#define ANDROID_V4L2_CODEC2_COMMON_V4L2_DEVICE_H #include <linux/videodev2.h> #include <stddef.h> #include <stdint.h> -#include <queue> +#include <optional> #include <vector> -#include "base/containers/flat_map.h" -#include "base/files/scoped_file.h" -#include "base/memory/ref_counted.h" +#include <base/containers/flat_map.h> +#include <base/files/scoped_file.h> +#include <base/memory/ref_counted.h> -#include "fourcc.h" -#include "size.h" -#include "video_codecs.h" -#include "video_decode_accelerator.h" -#include "video_encode_accelerator.h" -#include "video_frame.h" -#include "video_frame_layout.h" -#include "video_pixel_format.h" -#include "v4l2_codec2/common/V4L2DevicePoller.h" +#include <fourcc.h> +#include <size.h> +#include <v4l2_codec2/common/V4L2DevicePoller.h> +#include <video_codecs.h> +#include <video_decode_accelerator.h> +#include <video_encode_accelerator.h> +#include <video_frame.h> +#include <video_frame_layout.h> +#include <video_pixel_format.h> -namespace media { +namespace android { class V4L2Queue; class V4L2BufferRefBase; @@ -53,154 +39,131 @@ class V4L2DecodeSurface; // Wrapper for the 'v4l2_ext_control' structure. struct V4L2ExtCtrl { - V4L2ExtCtrl(uint32_t id); - V4L2ExtCtrl(uint32_t id, int32_t val); - struct v4l2_ext_control ctrl; + V4L2ExtCtrl(uint32_t id); + V4L2ExtCtrl(uint32_t id, int32_t val); + struct v4l2_ext_control ctrl; }; // A unique reference to a buffer for clients to prepare and submit. // -// Clients can prepare a buffer for queuing using the methods of this class, and -// then either queue it using the Queue() method corresponding to the memory -// type of the buffer, or drop the reference to make the buffer available again. +// Clients can prepare a buffer for queuing using the methods of this class, and then either queue +// it using the Queue() method corresponding to the memory type of the buffer, or drop the reference +// to make the buffer available again. class V4L2WritableBufferRef { - public: - V4L2WritableBufferRef(V4L2WritableBufferRef&& other); - V4L2WritableBufferRef() = delete; - V4L2WritableBufferRef& operator=(V4L2WritableBufferRef&& other); - - // Return the memory type of the buffer. Useful to e.g. decide which Queue() - // method to use. - enum v4l2_memory Memory() const; - - // Queue a MMAP buffer. - // If successful, true is returned and the reference to the buffer is dropped - // so this reference becomes invalid. - // In case of error, false is returned and the buffer is returned to the free - // list. - bool QueueMMap() &&; - // Queue a USERPTR buffer, assigning |ptrs| as pointer for each plane. - // The size of |ptrs| must be equal to the number of planes of this buffer. - // If successful, true is returned and the reference to the buffer is dropped - // so this reference becomes invalid. - // In case of error, false is returned and the buffer is returned to the free - // list. - bool QueueUserPtr(const std::vector<void*>& ptrs) &&; - // Queue a DMABUF buffer, assigning |fds| as file descriptors for each plane. - // It is allowed the number of |fds| might be greater than the number of - // planes of this buffer. It happens when the v4l2 pixel format is single - // planar. The fd of the first plane is only used in that case. - // If successful, true is returned and the reference to the buffer is dropped - // so this reference becomes invalid. - // In case of error, false is returned and the buffer is returned to the free - // list. - bool QueueDMABuf(const std::vector<base::ScopedFD>& scoped_fds) &&; - // Queue a DMABUF buffer, assigning |fds| as file descriptors for each plane. - // It is allowed the number of |fds| might be greater than the number of - // planes of this buffer. It happens when the v4l2 pixel format is single - // planar. The fd of the first plane is only used in that case. - // If successful, true is returned and the reference to the buffer is dropped - // so this reference becomes invalid. - // In case of error, false is returned and the buffer is returned to the free - // list. - bool QueueDMABuf(const std::vector<int>& fds) &&; - - // Returns the number of planes in this buffer. - size_t PlanesCount() const; - // Returns the size of the requested |plane|, in bytes. - size_t GetPlaneSize(const size_t plane) const; - // Set the size of the requested |plane|, in bytes. It is only valid for - // USERPTR and DMABUF buffers. When using MMAP buffer, this method triggers a - // DCHECK and is a no-op for release builds. - void SetPlaneSize(const size_t plane, const size_t size); - // This method can only be used with MMAP buffers. - // It will return a pointer to the data of the |plane|th plane. - // In case of error (invalid plane index or mapping failed), a nullptr is - // returned. - void* GetPlaneMapping(const size_t plane); - // Set the timestamp field for this buffer. - void SetTimeStamp(const struct timeval& timestamp); - // Return the previously-set timestamp field for this buffer. - const struct timeval& GetTimeStamp() const; - // Set the number of bytes used for |plane|. - void SetPlaneBytesUsed(const size_t plane, const size_t bytes_used); - // Returns the previously-set number of bytes used for |plane|. - size_t GetPlaneBytesUsed(const size_t plane) const; - // Set the data offset for |plane|, in bytes. - void SetPlaneDataOffset(const size_t plane, const size_t data_offset); - - // Return the V4L2 buffer ID of the underlying buffer. - // TODO(acourbot) This is used for legacy clients but should be ultimately - // removed. See crbug/879971 - size_t BufferId() const; - - ~V4L2WritableBufferRef(); - - private: - // Do the actual queue operation once the v4l2_buffer structure is properly - // filled. - bool DoQueue() &&; - - V4L2WritableBufferRef(const struct v4l2_buffer& v4l2_buffer, - base::WeakPtr<V4L2Queue> queue); - friend class V4L2BufferRefFactory; - - std::unique_ptr<V4L2BufferRefBase> buffer_data_; - - SEQUENCE_CHECKER(sequence_checker_); - DISALLOW_COPY_AND_ASSIGN(V4L2WritableBufferRef); +public: + V4L2WritableBufferRef(V4L2WritableBufferRef&& other); + V4L2WritableBufferRef() = delete; + V4L2WritableBufferRef& operator=(V4L2WritableBufferRef&& other); + + // Return the memory type of the buffer. Useful to e.g. decide which Queue() method to use. + enum v4l2_memory memory() const; + + // Queue a MMAP buffer. If successful, true is returned and the reference to the buffer is + // dropped so this reference becomes invalid. In case of error, false is returned and the buffer + // is returned to the free list. + bool queueMMap() &&; + // Queue a USERPTR buffer, assigning |ptrs| as pointer for each plane. The size of |ptrs| must + // be equal to the number of planes of this buffer. If successful, true is returned and the + // reference to the buffer is dropped so this reference becomes invalid. In case of error, false + // is returned and the buffer is returned to the free list. + bool queueUserPtr(const std::vector<void*>& ptrs) &&; + // Queue a DMABUF buffer, assigning |fds| as file descriptors for each plane. It is allowed the + // number of |fds| might be greater than the number of planes of this buffer. It happens when + // the v4l2 pixel format is single planar. The fd of the first plane is only used in that case. + // If successful, true is returned and the reference to the buffer is dropped so this reference + // becomes invalid. In case of error, false is returned and the buffer is returned to the free + // list. + bool queueDMABuf(const std::vector<int>& fds) &&; + + // Returns the number of planes in this buffer. + size_t planesCount() const; + // Returns the size of the requested |plane|, in bytes. + size_t getPlaneSize(const size_t plane) const; + // Set the size of the requested |plane|, in bytes. It is only valid for USERPTR and DMABUF + // buffers. When using an MMAP buffer, this method triggers an assert and is a no-op for release + // builds. + void setPlaneSize(const size_t plane, const size_t size); + // This method can only be used with MMAP buffers. It will return a pointer to the data of the + // |plane|th plane. In case of error (invalid plane index or mapping failed), a nullptr is + // returned. + void* getPlaneMapping(const size_t plane); + // Set the timestamp field for this buffer. + void setTimeStamp(const struct timeval& timestamp); + // Return the previously-set timestamp field for this buffer. + const struct timeval& getTimeStamp() const; + // Set the number of bytes used for |plane|. + void setPlaneBytesUsed(const size_t plane, const size_t bytesUsed); + // Returns the previously-set number of bytes used for |plane|. + size_t getPlaneBytesUsed(const size_t plane) const; + // Set the data offset for |plane|, in bytes. + void setPlaneDataOffset(const size_t plane, const size_t dataOffset); + + // Return the V4L2 buffer ID of the underlying buffer. + size_t bufferId() const; + + ~V4L2WritableBufferRef(); + +private: + friend class V4L2BufferRefFactory; + + // Do the actual queue operation once the v4l2_buffer structure is properly filled. + bool doQueue() &&; + + V4L2WritableBufferRef(const struct v4l2_buffer& v4l2Buffer, base::WeakPtr<V4L2Queue> queue); + + V4L2WritableBufferRef(const V4L2WritableBufferRef&) = delete; + V4L2WritableBufferRef& operator=(const V4L2WritableBufferRef&) = delete; + + std::unique_ptr<V4L2BufferRefBase> mBufferData; + + SEQUENCE_CHECKER(mSequenceChecker); }; // A reference to a read-only, dequeued buffer. // -// Clients use this class to query the buffer state and content, and are -// guaranteed that the buffer will not be reused until all references are -// destroyed. -// All methods of this class must be called from the same sequence, but -// instances of V4L2ReadableBuffer objects can be destroyed from any sequence. -// They can even outlive the V4L2 buffers they originate from. This flexibility -// is required because V4L2ReadableBufferRefs can be embedded into VideoFrames, -// which are then passed to other threads and not necessarily destroyed before -// the V4L2Queue buffers are freed. -class V4L2ReadableBuffer - : public base::RefCountedThreadSafe<V4L2ReadableBuffer> { - public: - // Returns whether the V4L2_BUF_FLAG_LAST flag is set for this buffer. - bool IsLast() const; - // Returns whether the V4L2_BUF_FLAG_KEYFRAME flag is set for this buffer. - bool IsKeyframe() const; - // Return the timestamp set by the driver on this buffer. - struct timeval GetTimeStamp() const; - // Returns the number of planes in this buffer. - size_t PlanesCount() const; - // Returns the number of bytes used for |plane|. - size_t GetPlaneBytesUsed(size_t plane) const; - // Returns the data offset for |plane|. - size_t GetPlaneDataOffset(size_t plane) const; - // This method can only be used with MMAP buffers. - // It will return a pointer to the data of the |plane|th plane. - // In case of error (invalid plane index or mapping failed), a nullptr is - // returned. - const void* GetPlaneMapping(const size_t plane) const; - - // Return the V4L2 buffer ID of the underlying buffer. - // TODO(acourbot) This is used for legacy clients but should be ultimately - // removed. See crbug/879971 - size_t BufferId() const; - - private: - friend class V4L2BufferRefFactory; - friend class base::RefCountedThreadSafe<V4L2ReadableBuffer>; - - ~V4L2ReadableBuffer(); - - V4L2ReadableBuffer(const struct v4l2_buffer& v4l2_buffer, - base::WeakPtr<V4L2Queue> queue); - - std::unique_ptr<V4L2BufferRefBase> buffer_data_; - - SEQUENCE_CHECKER(sequence_checker_); - DISALLOW_COPY_AND_ASSIGN(V4L2ReadableBuffer); +// Clients use this class to query the buffer state and content, and are guaranteed that the buffer +// will not be reused until all references are destroyed. +// All methods of this class must be called from the same sequence, but instances of +// V4L2ReadableBuffer objects can be destroyed from any sequence. They can even outlive the V4L2 +// buffers they originate from. This flexibility is required because V4L2ReadableBufferRefs can be +// embedded into VideoFrames, which are then passed to other threads and not necessarily destroyed +// before the V4L2Queue buffers are freed. +class V4L2ReadableBuffer : public base::RefCountedThreadSafe<V4L2ReadableBuffer> { +public: + // Returns whether the V4L2_BUF_FLAG_LAST flag is set for this buffer. + bool isLast() const; + // Returns whether the V4L2_BUF_FLAG_KEYFRAME flag is set for this buffer. + bool isKeyframe() const; + // Return the timestamp set by the driver on this buffer. + struct timeval getTimeStamp() const; + // Returns the number of planes in this buffer. + size_t planesCount() const; + // Returns the number of bytes used for |plane|. + size_t getPlaneBytesUsed(size_t plane) const; + // Returns the data offset for |plane|. + size_t getPlaneDataOffset(size_t plane) const; + // This method can only be used with MMAP buffers. It will return a pointer to the data of the + // |plane|th plane. In case of error (invalid plane index or mapping failed), a nullptr is + // returned. + const void* getPlaneMapping(const size_t plane) const; + + // Return the V4L2 buffer ID of the underlying buffer. + size_t bufferId() const; + +private: + friend class V4L2BufferRefFactory; + friend class base::RefCountedThreadSafe<V4L2ReadableBuffer>; + + ~V4L2ReadableBuffer(); + + V4L2ReadableBuffer(const struct v4l2_buffer& v4l2Buffer, base::WeakPtr<V4L2Queue> queue); + + V4L2ReadableBuffer(const V4L2ReadableBuffer&) = delete; + V4L2ReadableBuffer& operator=(const V4L2ReadableBuffer&) = delete; + + std::unique_ptr<V4L2BufferRefBase> mBufferData; + + SEQUENCE_CHECKER(mSequenceChecker); }; // Shortcut for naming consistency. @@ -209,389 +172,335 @@ using V4L2ReadableBufferRef = scoped_refptr<V4L2ReadableBuffer>; class V4L2Device; class V4L2Buffer; -// Interface representing a specific queue of a |V4L2Device|. It provides free -// and queued buffer management that is commonly required by clients. +// Interface representing a specific queue of a |V4L2Device|. It provides free and queued buffer +// management that is commonly required by clients. // // Buffers managed by this class undergo the following cycle: -// 1) Allocated buffers are put into a free buffers pool, indicating that they -// are used neither by the client nor the hardware. -// 2) The client obtains a unique, writable reference to one of the free -// buffers in order to set its content and other parameters. -// 3) The client then queues the buffer obtained in 2), which invalidates its -// reference. The buffer is now prepared to be processed by the hardware. -// 4) Once the hardware is done with the buffer, it is ready to be dequeued by -// the client. The client obtains a read-only, counted reference to the -// buffer and can read its content and metadata, as well as making other -// references to it. The buffer will not be reused until all the references -// are dropped. Once this happens, the buffer goes back to the free list -// described in 1). +// 1) Allocated buffers are put into a free buffers pool, indicating that they are used neither by +// the client nor the hardware. +// 2) The client obtains a unique, writable reference to one of the free buffers in order to set +// its content and other parameters. +// 3) The client then queues the buffer obtained in 2), which invalidates its reference. The buffer +// is now prepared to be processed by the hardware. +// 4) Once the hardware is done with the buffer, it is ready to be dequeued by the client. The +// client obtains a read-only, counted reference to the buffer and can read its content and +// metadata, as well as making other references to it. The buffer will not be reused until all +// the references are dropped. Once this happens, the buffer goes back to the free list described +// in 1). class V4L2Queue : public base::RefCountedThreadSafe<V4L2Queue> { - public: - // Set |fourcc| as the current format on this queue. |size| corresponds to the - // desired buffer's dimensions (i.e. width and height members of - // v4l2_pix_format_mplane (if not applicable, pass Size()). - // |buffer_size| is the desired size in bytes of the buffer for single-planar - // formats (i.e. sizeimage of the first plane). It can be set to 0 if not - // relevant for the desired format. - // |stride| is the desired stride in bytes of the buffer (i.e. bytesperline). - // It can be set to 0 if not relevant or to let the driver decide. - // If the format could be set, then the |v4l2_format| reflecting the actual - // format is returned. It is guaranteed to feature the specified |fourcc|, - // but any other parameter (including |size| and |buffer_size| may have been - // adjusted by the driver, so the caller must check their values. - base::Optional<struct v4l2_format> SetFormat(uint32_t fourcc, - const Size& size, - size_t buffer_size, - uint32_t stride = 0) - WARN_UNUSED_RESULT; - - // Identical to |SetFormat|, but does not actually apply the format, and can - // be called anytime. - // Returns an adjusted V4L2 format if |fourcc| is supported by the queue, or - // |nullopt| if |fourcc| is not supported or an ioctl error happened. - base::Optional<struct v4l2_format> TryFormat(uint32_t fourcc, - const Size& size, - size_t buffer_size) - WARN_UNUSED_RESULT; - - // Returns the currently set format on the queue. The result is returned as - // a std::pair where the first member is the format, or base::nullopt if the - // format could not be obtained due to an ioctl error. The second member is - // only used in case of an error and contains the |errno| set by the failing - // ioctl. If the first member is not base::nullopt, the second member will - // always be zero. - // - // If the second member is 0, then the first member is guaranteed to have - // a valid value. So clients that are not interested in the precise error - // message can just check that the first member is valid and go on. - // - // This pair is used because not all failures to get the format are - // necessarily errors, so we need to way to let the use decide whether it - // is one or not. - std::pair<base::Optional<struct v4l2_format>, int> GetFormat(); - - // Allocate |count| buffers for the current format of this queue, with a - // specific |memory| allocation, and returns the number of buffers allocated - // or zero if an error occurred, or if references to any previously allocated - // buffers are still held by any clients. - // - // The number of allocated buffers may be larger than the number requested, so - // callers must always check the return value. - // - // Calling this method while buffers are still allocated results in an error. - size_t AllocateBuffers(size_t count, - enum v4l2_memory memory) WARN_UNUSED_RESULT; - - // Deallocate all buffers previously allocated by |AllocateBuffers|. Any - // references to buffers previously allocated held by the client must be - // released, or this call will fail. - bool DeallocateBuffers(); - - // Returns the memory usage of v4l2 buffers owned by this V4L2Queue which are - // mapped in user space memory. - size_t GetMemoryUsage() const; - - // Returns |memory_|, memory type of last buffers allocated by this V4L2Queue. - v4l2_memory GetMemoryType() const; - - // Return a reference to a free buffer for the caller to prepare and submit, - // or nullopt if no buffer is currently free. - // - // If the caller discards the returned reference, the underlying buffer is - // made available to clients again. - base::Optional<V4L2WritableBufferRef> GetFreeBuffer(); - base::Optional<V4L2WritableBufferRef> GetFreeBuffer( - size_t requested_buffer_id); - - // Attempt to dequeue a buffer, and return a reference to it if one was - // available. - // - // The first element of the returned pair will be false if an error occurred, - // in which case the second element will be nullptr. If no error occurred, - // then the first element will be true and the second element will contain a - // reference to the dequeued buffer if one was available, or nullptr - // otherwise. - // Dequeued buffers will not be reused by the driver until all references to - // them are dropped. - std::pair<bool, V4L2ReadableBufferRef> DequeueBuffer(); - - // Returns true if this queue is currently streaming. - bool IsStreaming() const; - // If not currently streaming, starts streaming. Returns true if we started - // streaming, or were already streaming, or false if we were not streaming - // and an error occurred when attempting to start the stream. On failure, any - // previously-queued buffers will be dequeued without processing and made - // available to the client, while any buffers held by the client will remain - // unchanged and their ownership will remain with the client. - bool Streamon(); - // If currently streaming, stops streaming. Also make all queued buffers - // available to the client again regardless of the streaming state. - // If an error occurred while attempting to stop streaming, then false is - // returned and queued buffers are left untouched since the V4L2 queue may - // still be using them. - bool Streamoff(); - - // Returns the number of buffers currently allocated for this queue. - size_t AllocatedBuffersCount() const; - // Returns the number of currently free buffers on this queue. - size_t FreeBuffersCount() const; - // Returns the number of buffers currently queued on this queue. - size_t QueuedBuffersCount() const; - - private: - ~V4L2Queue(); - - // Called when clients request a buffer to be queued. - bool QueueBuffer(struct v4l2_buffer* v4l2_buffer); - - const enum v4l2_buf_type type_; - enum v4l2_memory memory_ = V4L2_MEMORY_MMAP; - bool is_streaming_ = false; - size_t planes_count_ = 0; - // Current format as set by SetFormat. - base::Optional<struct v4l2_format> current_format_; - - std::vector<std::unique_ptr<V4L2Buffer>> buffers_; - - // Buffers that are available for client to get and submit. - // Buffers in this list are not referenced by anyone else than ourselves. - scoped_refptr<V4L2BuffersList> free_buffers_; - // Buffers that have been queued by the client, and not dequeued yet. - std::set<size_t> queued_buffers_; - - scoped_refptr<V4L2Device> device_; - // Callback to call in this queue's destructor. - base::OnceClosure destroy_cb_; - - V4L2Queue(scoped_refptr<V4L2Device> dev, - enum v4l2_buf_type type, - base::OnceClosure destroy_cb); - friend class V4L2QueueFactory; - friend class V4L2BufferRefBase; - friend class base::RefCountedThreadSafe<V4L2Queue>; - - SEQUENCE_CHECKER(sequence_checker_); - - base::WeakPtrFactory<V4L2Queue> weak_this_factory_; - - DISALLOW_COPY_AND_ASSIGN(V4L2Queue); +public: + // Set |fourcc| as the current format on this queue. |size| corresponds to the desired buffer's + // dimensions (i.e. width and height members of v4l2_pix_format_mplane (if not applicable, pass + // Size()). + // |bufferSize| is the desired size in bytes of the buffer for single-planar formats (i.e. + // sizeimage of the first plane). It can be set to 0 if not relevant for the desired format. + // |stride| is the desired stride in bytes of the buffer (i.e. bytesperline). It can be set to 0 + // if not relevant or to let the driver decide. If the format could be set, then the + // |v4l2_format| reflecting the actual format is returned. It is guaranteed to feature the + // specified |fourcc|, but any other parameter (including |size| and |bufferSize| may have been + // adjusted by the driver, so the caller must check their values. + std::optional<struct v4l2_format> setFormat(uint32_t fourcc, const media::Size& size, + size_t bufferSize, + uint32_t stride = 0) WARN_UNUSED_RESULT; + + // Identical to |setFormat|, but does not actually apply the format, and can be called anytime. + // Returns an adjusted V4L2 format if |fourcc| is supported by the queue, or |nullopt| if + // |fourcc| is not supported or an ioctl error happened. + std::optional<struct v4l2_format> tryFormat(uint32_t fourcc, const media::Size& size, + size_t bufferSize) WARN_UNUSED_RESULT; + + // Returns the currently set format on the queue. The result is returned as a std::pair where + // the first member is the format, or base::nullopt if the format could not be obtained due to + // an ioctl error. The second member is only used in case of an error and contains the |errno| + // set by the failing ioctl. If the first member is not base::nullopt, the second member will + // always be zero. + // + // If the second member is 0, then the first member is guaranteed to have a valid value. So + // clients that are not interested in the precise error message can just check that the first + // member is valid and go on. + // + // This pair is used because not all failures to get the format are necessarily errors, so we + // need to way to let the use decide whether it is one or not. + std::pair<std::optional<struct v4l2_format>, int> getFormat(); + + // Allocate |count| buffers for the current format of this queue, with a specific |memory| + // allocation, and returns the number of buffers allocated or zero if an error occurred, or if + // references to any previously allocated buffers are still held by any clients. + // + // The number of allocated buffers may be larger than the number requested, so callers must + // always check the return value. + // + // Calling this method while buffers are still allocated results in an error. + size_t allocateBuffers(size_t count, enum v4l2_memory memory) WARN_UNUSED_RESULT; + + // Deallocate all buffers previously allocated by |allocateBuffers|. Any references to buffers + // previously allocated held by the client must be released, or this call will fail. + bool deallocateBuffers(); + + // Returns the memory usage of v4l2 buffers owned by this V4L2Queue which are mapped in user + // space memory. + size_t getMemoryUsage() const; + + // Returns |mMemory|, memory type of last buffers allocated by this V4L2Queue. + v4l2_memory getMemoryType() const; + + // Return a reference to a free buffer for the caller to prepare and submit, or nullopt if no + // buffer is currently free. + // + // If the caller discards the returned reference, the underlying buffer is made available to + // clients again. + std::optional<V4L2WritableBufferRef> getFreeBuffer(); + std::optional<V4L2WritableBufferRef> getFreeBuffer(size_t requestedBufferId); + + // Attempt to dequeue a buffer, and return a reference to it if one was available. + // + // The first element of the returned pair will be false if an error occurred, in which case the + // second element will be nullptr. If no error occurred, then the first element will be true and + // the second element will contain a reference to the dequeued buffer if one was available, or + // nullptr otherwise. Dequeued buffers will not be reused by the driver until all references to + // them are dropped. + std::pair<bool, V4L2ReadableBufferRef> dequeueBuffer(); + + // Returns true if this queue is currently streaming. + bool isStreaming() const; + // If not currently streaming, starts streaming. Returns true if we started streaming, or were + // already streaming, or false if we were not streaming and an error occurred when attempting to + // start the stream. On failure, any previously-queued buffers will be dequeued without + // processing and made available to the client, while any buffers held by the client will remain + // unchanged and their ownership will remain with the client. + bool streamon(); + // If currently streaming, stops streaming. Also make all queued buffers available to the client + // again regardless of the streaming state. If an error occurred while attempting to stop + // streaming, then false is returned and queued buffers are left untouched since the V4L2 queue + // may still be using them. + bool streamoff(); + + // Returns the number of buffers currently allocated for this queue. + size_t allocatedBuffersCount() const; + // Returns the number of currently free buffers on this queue. + size_t freeBuffersCount() const; + // Returns the number of buffers currently queued on this queue. + size_t queuedBuffersCount() const; + +private: + ~V4L2Queue(); + + V4L2Queue(const V4L2Queue&) = delete; + V4L2Queue& operator=(const V4L2Queue&) = delete; + + // Called when clients request a buffer to be queued. + bool queueBuffer(struct v4l2_buffer* v4l2Buffer); + + const enum v4l2_buf_type mType; + enum v4l2_memory mMemory = V4L2_MEMORY_MMAP; + bool mIsStreaming = false; + size_t mPlanesCount = 0; + // Current format as set by SetFormat. + std::optional<struct v4l2_format> mCurrentFormat; + + std::vector<std::unique_ptr<V4L2Buffer>> mBuffers; + + // Buffers that are available for client to get and submit. Buffers in this list are not + // referenced by anyone else than ourselves. + scoped_refptr<V4L2BuffersList> mFreeBuffers; + // Buffers that have been queued by the client, and not dequeued yet. + std::set<size_t> mQueuedBuffers; + + scoped_refptr<V4L2Device> mDevice; + // Callback to call in this queue's destructor. + base::OnceClosure mDestroyCb; + + V4L2Queue(scoped_refptr<V4L2Device> dev, enum v4l2_buf_type type, base::OnceClosure destroyCb); + friend class V4L2QueueFactory; + friend class V4L2BufferRefBase; + friend class base::RefCountedThreadSafe<V4L2Queue>; + + SEQUENCE_CHECKER(mSequenceChecker); + + base::WeakPtrFactory<V4L2Queue> mWeakThisFactory{this}; }; class V4L2Device : public base::RefCountedThreadSafe<V4L2Device> { - public: - // Utility format conversion functions - // If there is no corresponding single- or multi-planar format, returns 0. - static uint32_t VideoCodecProfileToV4L2PixFmt(VideoCodecProfile profile, - bool slice_based); - static VideoCodecProfile V4L2ProfileToVideoCodecProfile(VideoCodec codec, - uint32_t profile); - std::vector<VideoCodecProfile> V4L2PixFmtToVideoCodecProfiles( - uint32_t pix_fmt, - bool is_encoder); - // Calculates the largest plane's allocation size requested by a V4L2 device. - static Size AllocatedSizeFromV4L2Format(const struct v4l2_format& format); - - // Convert required H264 profile and level to V4L2 enums. - static int32_t VideoCodecProfileToV4L2H264Profile(VideoCodecProfile profile); - static int32_t H264LevelIdcToV4L2H264Level(uint8_t level_idc); - - // Converts v4l2_memory to a string. - static const char* V4L2MemoryToString(const v4l2_memory memory); - - // Returns the printable name of a v4l2_buf_type. - static const char* V4L2BufferTypeToString(const enum v4l2_buf_type buf_type); - - // Composes human readable string of v4l2_format. - static std::string V4L2FormatToString(const struct v4l2_format& format); - - // Composes human readable string of v4l2_buffer. - static std::string V4L2BufferToString(const struct v4l2_buffer& buffer); - - // Composes VideoFrameLayout based on v4l2_format. - // If error occurs, it returns base::nullopt. - static base::Optional<VideoFrameLayout> V4L2FormatToVideoFrameLayout( - const struct v4l2_format& format); - - // Returns number of planes of |pix_fmt|. - static size_t GetNumPlanesOfV4L2PixFmt(uint32_t pix_fmt); - - enum class Type { - kDecoder, - kEncoder, - }; - - // Create and initialize an appropriate V4L2Device instance for the current - // platform, or return nullptr if not available. - static scoped_refptr<V4L2Device> Create(); - - // Open a V4L2 device of |type| for use with |v4l2_pixfmt|. - // Return true on success. - // The device will be closed in the destructor. - bool Open(Type type, uint32_t v4l2_pixfmt); - - // Returns the V4L2Queue corresponding to the requested |type|, or nullptr - // if the requested queue type is not supported. - scoped_refptr<V4L2Queue> GetQueue(enum v4l2_buf_type type); - - // Parameters and return value are the same as for the standard ioctl() system - // call. - int Ioctl(int request, void* arg); - - // This method sleeps until either: - // - SetDevicePollInterrupt() is called (on another thread), - // - |poll_device| is true, and there is new data to be read from the device, - // or an event from the device has arrived; in the latter case - // |*event_pending| will be set to true. - // Returns false on error, true otherwise. - // This method should be called from a separate thread. - bool Poll(bool poll_device, bool* event_pending); - - // These methods are used to interrupt the thread sleeping on Poll() and force - // it to return regardless of device state, which is usually when the client - // is no longer interested in what happens with the device (on cleanup, - // client state change, etc.). When SetDevicePollInterrupt() is called, Poll() - // will return immediately, and any subsequent calls to it will also do so - // until ClearDevicePollInterrupt() is called. - bool SetDevicePollInterrupt(); - bool ClearDevicePollInterrupt(); - - // Wrappers for standard mmap/munmap system calls. - void* Mmap(void* addr, - unsigned int len, - int prot, - int flags, - unsigned int offset); - void Munmap(void* addr, unsigned int len); - - // Return a vector of dmabuf file descriptors, exported for V4L2 buffer with - // |index|, assuming the buffer contains |num_planes| V4L2 planes and is of - // |type|. Return an empty vector on failure. - // The caller is responsible for closing the file descriptors after use. - std::vector<base::ScopedFD> GetDmabufsForV4L2Buffer( - int index, - size_t num_planes, - enum v4l2_buf_type type); - - // Returns the preferred V4L2 input formats for |type| or empty if none. - std::vector<uint32_t> PreferredInputFormat(Type type); - - // NOTE: The below methods to query capabilities have a side effect of - // closing the previously-open device, if any, and should not be called after - // Open(). - // TODO(posciak): fix this. - - // Get minimum and maximum resolution for fourcc |pixelformat| and store to - // |min_resolution| and |max_resolution|. - void GetSupportedResolution(uint32_t pixelformat, - Size* min_resolution, - Size* max_resolution); - - std::vector<uint32_t> EnumerateSupportedPixelformats(v4l2_buf_type buf_type); - - // Return supported profiles for decoder, including only profiles for given - // fourcc |pixelformats|. - VideoDecodeAccelerator::SupportedProfiles GetSupportedDecodeProfiles( - const size_t num_formats, - const uint32_t pixelformats[]); - - // Return supported profiles for encoder. - VideoEncodeAccelerator::SupportedProfiles - GetSupportedEncodeProfiles(); - - // Start polling on this V4L2Device. |event_callback| will be posted to - // the caller's sequence if a buffer is ready to be dequeued and/or a V4L2 - // event has been posted. |error_callback| will be posted to the client's - // sequence if a polling error has occurred. - bool StartPolling(V4L2DevicePoller::EventCallback event_callback, - base::RepeatingClosure error_callback); - // Stop polling this V4L2Device if polling was active. No new events will - // be posted after this method has returned. - bool StopPolling(); - // Schedule a polling event if polling is enabled. This method is intended - // to be called from V4L2Queue, clients should not need to call it directly. - void SchedulePoll(); - - // Check whether the V4L2 control with specified |ctrl_id| is supported. - bool IsCtrlExposed(uint32_t ctrl_id); - // Set the specified list of |ctrls| for the specified |ctrl_class|, returns - // whether the operation succeeded. - bool SetExtCtrls(uint32_t ctrl_class, std::vector<V4L2ExtCtrl> ctrls); - - // Check whether the V4L2 command with specified |command_id| is supported. - bool IsCommandSupported(uint32_t command_id); - // Check whether the V4L2 device has the specified |capabilities|. - bool HasCapabilities(uint32_t capabilities); - - private: - // Vector of video device node paths and corresponding pixelformats supported - // by each device node. - using Devices = std::vector<std::pair<std::string, std::vector<uint32_t>>>; - - friend class base::RefCountedThreadSafe<V4L2Device>; - V4L2Device(); - ~V4L2Device(); - - VideoDecodeAccelerator::SupportedProfiles EnumerateSupportedDecodeProfiles( - const size_t num_formats, - const uint32_t pixelformats[]); - - VideoEncodeAccelerator::SupportedProfiles EnumerateSupportedEncodeProfiles(); - - // Perform platform-specific initialization of the device instance. - // Return true on success, false on error or if the particular implementation - // is not available. - bool Initialize(); - - // Open device node for |path| as a device of |type|. - bool OpenDevicePath(const std::string& path, Type type); - - // Close the currently open device. - void CloseDevice(); - - // Enumerate all V4L2 devices on the system for |type| and store the results - // under devices_by_type_[type]. - void EnumerateDevicesForType(V4L2Device::Type type); - - // Return device information for all devices of |type| available in the - // system. Enumerates and queries devices on first run and caches the results - // for subsequent calls. - const Devices& GetDevicesForType(V4L2Device::Type type); - - // Return device node path for device of |type| supporting |pixfmt|, or - // an empty string if the given combination is not supported by the system. - std::string GetDevicePathFor(V4L2Device::Type type, uint32_t pixfmt); - - // Callback that is called upon a queue's destruction, to cleanup its pointer - // in queues_. - void OnQueueDestroyed(v4l2_buf_type buf_type); - - // Lazily initialize static data after sandbox is enabled. Return false on - // init failure. - static bool PostSandboxInitialization(); - - // Stores information for all devices available on the system - // for each device Type. - std::map<V4L2Device::Type, Devices> devices_by_type_; - - // The actual device fd. - base::ScopedFD device_fd_; - - // eventfd fd to signal device poll thread when its poll() should be - // interrupted. - base::ScopedFD device_poll_interrupt_fd_; - - // Associates a v4l2_buf_type to its queue. - base::flat_map<enum v4l2_buf_type, V4L2Queue*> queues_; - - // Used if EnablePolling() is called to signal the user that an event - // happened or a buffer is ready to be dequeued. - std::unique_ptr<V4L2DevicePoller> device_poller_; - - DISALLOW_COPY_AND_ASSIGN(V4L2Device); - - SEQUENCE_CHECKER(client_sequence_checker_); +public: + // Utility format conversion functions + // If there is no corresponding single- or multi-planar format, returns 0. + static uint32_t videoCodecProfileToV4L2PixFmt(media::VideoCodecProfile profile, + bool sliceBased); + static media::VideoCodecProfile v4L2ProfileToVideoCodecProfile(media::VideoCodec codec, + uint32_t profile); + std::vector<media::VideoCodecProfile> v4L2PixFmtToVideoCodecProfiles(uint32_t pixFmt, + bool isEncoder); + // Calculates the largest plane's allocation size requested by a V4L2 device. + static media::Size allocatedSizeFromV4L2Format(const struct v4l2_format& format); + + // Convert required H264 profile and level to V4L2 enums. + static int32_t videoCodecProfileToV4L2H264Profile(media::VideoCodecProfile profile); + static int32_t h264LevelIdcToV4L2H264Level(uint8_t levelIdc); + + // Converts v4l2_memory to a string. + static const char* v4L2MemoryToString(const v4l2_memory memory); + + // Returns the printable name of a v4l2_buf_type. + static const char* v4L2BufferTypeToString(const enum v4l2_buf_type bufType); + + // Composes human readable string of v4l2_format. + static std::string v4L2FormatToString(const struct v4l2_format& format); + + // Composes human readable string of v4l2_buffer. + static std::string v4L2BufferToString(const struct v4l2_buffer& buffer); + + // Composes VideoFrameLayout based on v4l2_format. + // If error occurs, it returns base::nullopt. + static std::optional<media::VideoFrameLayout> v4L2FormatToVideoFrameLayout( + const struct v4l2_format& format); + + // Returns number of planes of |pixFmt|. + static size_t getNumPlanesOfV4L2PixFmt(uint32_t pixFmt); + + enum class Type { kDecoder, kEncoder }; + + // Create and initialize an appropriate V4L2Device instance for the current platform, or return + // nullptr if not available. + static scoped_refptr<V4L2Device> create(); + + // Open a V4L2 device of |type| for use with |v4l2PixFmt|. Return true on success. The device + // will be closed in the destructor. + bool open(Type type, uint32_t v4l2PixFmt); + + // Returns the V4L2Queue corresponding to the requested |type|, or nullptr if the requested + // queue type is not supported. + scoped_refptr<V4L2Queue> getQueue(enum v4l2_buf_type type); + + // Parameters and return value are the same as for the standard ioctl() system call. + int ioctl(int request, void* arg); + + // This method sleeps until either: + // - SetDevicePollInterrupt() is called (on another thread), + // - |pollDevice| is true, and there is new data to be read from the device, + // or an event from the device has arrived; in the latter case + // |*eventPending| will be set to true. + // Returns false on error, true otherwise. This method should be called from a separate thread. + bool poll(bool pollDevice, bool* eventPending); + + // These methods are used to interrupt the thread sleeping on poll() and force it to return + // regardless of device state, which is usually when the client is no longer interested in what + // happens with the device (on cleanup, client state change, etc.). When + // setDevicePollInterrupt() is called, poll() will return immediately, and any subsequent calls + // to it will also do so until clearDevicePollInterrupt() is called. + bool setDevicePollInterrupt(); + bool clearDevicePollInterrupt(); + + // Wrappers for standard mmap/munmap system calls. + void* mmap(void* addr, unsigned int len, int prot, int flags, unsigned int offset); + void munmap(void* addr, unsigned int len); + + // Return a vector of dmabuf file descriptors, exported for V4L2 buffer with |index|, assuming + // the buffer contains |numPlanes| V4L2 planes and is of |bufType|. Return an empty vector on + // failure. The caller is responsible for closing the file descriptors after use. + std::vector<base::ScopedFD> getDmabufsForV4L2Buffer(int index, size_t numPlanes, + enum v4l2_buf_type bufType); + + // Returns the preferred V4L2 input formats for |type| or empty if none. + std::vector<uint32_t> preferredInputFormat(Type type); + + // NOTE: The below methods to query capabilities have a side effect of closing the + // previously-open device, if any, and should not be called after Open(). + + // Get minimum and maximum resolution for fourcc |pixelFormat| and store to |minResolution| and + // |maxResolution|. + void getSupportedResolution(uint32_t pixelFormat, media::Size* minResolution, + media::Size* maxResolution); + + std::vector<uint32_t> enumerateSupportedPixelformats(v4l2_buf_type bufType); + + // Return supported profiles for decoder, including only profiles for given fourcc + // |pixelFormats|. + media::VideoDecodeAccelerator::SupportedProfiles getSupportedDecodeProfiles( + const size_t numFormats, const uint32_t pixelFormats[]); + + // Return supported profiles for encoder. + media::VideoEncodeAccelerator::SupportedProfiles getSupportedEncodeProfiles(); + + // Start polling on this V4L2Device. |eventCallback| will be posted to the caller's sequence if + // a buffer is ready to be dequeued and/or a V4L2 event has been posted. |errorCallback| will + // be posted to the client's + // sequence if a polling error has occurred. + bool startPolling(android::V4L2DevicePoller::EventCallback eventCallback, + base::RepeatingClosure errorCallback); + // Stop polling this V4L2Device if polling was active. No new events will be posted after this + // method has returned. + bool stopPolling(); + // Schedule a polling event if polling is enabled. This method is intended to be called from + // V4L2Queue, clients should not need to call it directly. + void schedulePoll(); + + // Check whether the V4L2 control with specified |ctrlId| is supported. + bool isCtrlExposed(uint32_t ctrlId); + // Set the specified list of |ctrls| for the specified |ctrlClass|, returns whether the + // operation succeeded. + bool setExtCtrls(uint32_t ctrlClass, std::vector<V4L2ExtCtrl> ctrls); + + // Check whether the V4L2 command with specified |commandId| is supported. + bool isCommandSupported(uint32_t commandId); + // Check whether the V4L2 device has the specified |capabilities|. + bool hasCapabilities(uint32_t capabilities); + +private: + // Vector of video device node paths and corresponding pixelformats supported by each device node. + using Devices = std::vector<std::pair<std::string, std::vector<uint32_t>>>; + + friend class base::RefCountedThreadSafe<V4L2Device>; + V4L2Device(); + ~V4L2Device(); + + V4L2Device(const V4L2Device&) = delete; + V4L2Device& operator=(const V4L2Device&) = delete; + + media::VideoDecodeAccelerator::SupportedProfiles enumerateSupportedDecodeProfiles( + const size_t numFormats, const uint32_t pixelFormats[]); + + media::VideoEncodeAccelerator::SupportedProfiles enumerateSupportedEncodeProfiles(); + + // Open device node for |path| as a device of |type|. + bool openDevicePath(const std::string& path, Type type); + + // Close the currently open device. + void closeDevice(); + + // Enumerate all V4L2 devices on the system for |type| and store the results under + // mDevicesByType[type]. + void enumerateDevicesForType(V4L2Device::Type type); + + // Return device information for all devices of |type| available in the system. Enumerates and + // queries devices on first run and caches the results for subsequent calls. + const Devices& getDevicesForType(V4L2Device::Type type); + + // Return device node path for device of |type| supporting |pixFmt|, or an empty string if the + // given combination is not supported by the system. + std::string getDevicePathFor(V4L2Device::Type type, uint32_t pixFmt); + + // Callback that is called upon a queue's destruction, to cleanup its pointer in mQueues. + void onQueueDestroyed(v4l2_buf_type buf_type); + + // Stores information for all devices available on the system for each device Type. + std::map<V4L2Device::Type, Devices> mDevicesByType; + + // The actual device fd. + base::ScopedFD mDeviceFd; + + // eventfd fd to signal device poll thread when its poll() should be interrupted. + base::ScopedFD mDevicePollInterruptFd; + + // Associates a v4l2_buf_type to its queue. + base::flat_map<enum v4l2_buf_type, V4L2Queue*> mQueues; + + // Used if EnablePolling() is called to signal the user that an event happened or a buffer is + // ready to be dequeued. + std::unique_ptr<android::V4L2DevicePoller> mDevicePoller; + + SEQUENCE_CHECKER(mClientSequenceChecker); }; -} // namespace media +} // namespace android -#endif // V4L2_DEVICE_H_ +#endif // ANDROID_V4L2_CODEC2_COMMON_V4L2_DEVICE_H diff --git a/common/include/v4l2_codec2/common/V4L2DevicePoller.h b/common/include/v4l2_codec2/common/V4L2DevicePoller.h index aac3d8c..ad256be 100644 --- a/common/include/v4l2_codec2/common/V4L2DevicePoller.h +++ b/common/include/v4l2_codec2/common/V4L2DevicePoller.h @@ -3,95 +3,86 @@ // found in the LICENSE file. // Note: ported from Chromium commit head: f65c38dcdac2 -#ifndef V4L2_V4L2_DEVICE_POLLER_H_ -#define V4L2_V4L2_DEVICE_POLLER_H_ +#ifndef ANDROID_V4L2_CODEC2_COMMON_V4L2_DEVICE_POLLER_H +#define ANDROID_V4L2_CODEC2_COMMON_V4L2_DEVICE_POLLER_H #include <atomic> -#include "base/callback_forward.h" -#include "base/sequence_checker.h" -#include "base/sequenced_task_runner.h" -#include "base/synchronization/waitable_event.h" -#include "base/threading/thread.h" +#include <base/callback_forward.h> +#include <base/sequence_checker.h> +#include <base/sequenced_task_runner.h> +#include <base/synchronization/waitable_event.h> +#include <base/threading/thread.h> -namespace media { +namespace android { class V4L2Device; -// Allows a client to poll() on a given V4L2Device and be signaled when -// a buffer is ready to be dequeued or a V4L2 event has been received. Polling -// is done on a dedicated thread, and notifications are delivered in the form of -// a callback to the listener's sequence. +// Allows a client to poll() on a given V4L2Device and be signaled when a buffer is ready to be +// dequeued or a V4L2 event has been received. Polling is done on a dedicated thread, and +// notifications are delivered in the form of a callback to the listener's sequence. // -// All the methods of this class (with the exception of the constructor) must be -// called from the same sequence. +// All the methods of this class (with the exception of the constructor) must be called from the +// same sequence. // -// Note that the service callback may also be called when no particular event -// occurred due to the way poll() works. It is the responsibility of the caller -// to call SchedulePoll() again if there may still be pending events. +// Note that the service callback may also be called when no particular event occurred due to the +// way poll() works. It is the responsibility of the caller to call SchedulePoll() again if there +// may still be pending events. class V4L2DevicePoller { - public: - // Callback to be called when buffer ready/V4L2 event has potentially been - // polled. |event| is set if a V4L2 event has been detected. - using EventCallback = base::RepeatingCallback<void(bool event)>; +public: + // Callback to be called when buffer ready/V4L2 event has potentially been polled. |event| is + // set if a V4L2 event has been detected. + using EventCallback = base::RepeatingCallback<void(bool event)>; - // Create a poller for |device|, using a thread named |thread_name|. - // Notification won't start until |StartPolling()| is called. - V4L2DevicePoller(V4L2Device* const device, const std::string& thread_name); - ~V4L2DevicePoller(); + // Create a poller for |device|, using a thread named |threadName|. Notification won't start + // until |startPolling()| is called. + V4L2DevicePoller(V4L2Device* const device, const std::string& threadName); + ~V4L2DevicePoller(); - // Starts polling. |event_callback| will be posted on the caller's sequence - // every time an event occurs. The client is then responsible for consuming - // all pending events in that callback. If new events may still happen after - // the callback has run, the client must call |SchedulePoll()| again in order - // to be notified for them. - // - // If an error occurs during polling, |error_callback| will be posted on the - // caller's sequence. - bool StartPolling(EventCallback event_callback, - base::RepeatingClosure error_callback); - // Stop polling and stop the thread. The poller won't post any new event to - // the caller's sequence after this method has returned. - bool StopPolling(); - // Returns true if currently polling, false otherwise. - bool IsPolling() const; - // Attempts polling the V4L2 device. This method should be called whenever - // doing something that may trigger an event of interest (buffer dequeue or - // V4L2 event), for instance queueing a buffer. In the absence of a pending - // event, poll() will return immediately and the service callback will be - // posted to the caller's sequence. The client is then responsible for calling - // this method again when it is interested in receiving events. - void SchedulePoll(); + // Starts polling. |mEventCallback| will be posted on the caller's sequence every time an event + // occurs. The client is then responsible for consuming all pending events in that callback. If + // new events may still happen after the callback has run, the client must call |schedulePoll()| + // again in order to be notified for them. + // + // If an error occurs during polling, |mErrorCallback| will be posted on the caller's sequence. + bool startPolling(EventCallback eventCallback, base::RepeatingClosure errorCallback); + // Stop polling and stop the thread. The poller won't post any new event to the caller's + // sequence after this method has returned. + bool stopPolling(); + // Returns true if currently polling, false otherwise. + bool isPolling() const; + // Attempts polling the V4L2 device. This method should be called whenever doing something that + // may trigger an event of interest (buffer dequeue or V4L2 event), for instance queueing a + // buffer. In the absence of a pending event, poll() will return immediately and the service + // callback will be posted to the caller's sequence. The client is then responsible for calling + // this method again when it is interested in receiving events. + void schedulePoll(); - private: - // Perform a poll() on |device_| and post either |service_task_| or - // |error_callback_| on the client's sequence when poll() returns. - void DevicePollTask(); +private: + // Perform a poll() on |mDevice| and post either |mEventCallback| or |mErrorCallback| on the + // client's sequence when poll() returns. + void devicePollTask(); - // V4L2 device we are polling. - V4L2Device* const device_; - // Thread on which polling is done. - base::Thread poll_thread_; - // Callback to post to the client's sequence when an event occurs. - EventCallback event_callback_; - // Closure to post to the client's sequence when an error occurs. - base::RepeatingClosure error_callback_; - // Client sequence's task runner, where closures are posted. - scoped_refptr<base::SequencedTaskRunner> client_task_runner_; + // V4L2 device we are polling. + V4L2Device* const mDevice; + // Thread on which polling is done. + base::Thread mPollThread; + // Callback to post to the client's sequence when an event occurs. + EventCallback mEventCallback; + // Closure to post to the client's sequence when an error occurs. + base::RepeatingClosure mErrorCallback; + // Client sequence's task runner, where closures are posted. + scoped_refptr<base::SequencedTaskRunner> mClientTaskTunner; - // Since poll() returns immediately if no buffers have been queued, we cannot - // rely on it to pause the polling thread until an event occurs. Instead, - // the polling thread will wait on this WaitableEvent (signaled by - // |SchedulePoll| before calling poll(), so we only call it when we are - // actually waiting for an event. - base::WaitableEvent trigger_poll_; - // Set to true when we wish to stop polling, instructing the poller thread - // to break its loop. - std::atomic_bool stop_polling_; - - SEQUENCE_CHECKER(client_sequence_checker_); + // Since poll() returns immediately if no buffers have been queued, we cannot rely on it to + // pause the polling thread until an event occurs. Instead, + // the polling thread will wait on this WaitableEvent (signaled by |schedulePoll| before calling + // poll(), so we only call it when we are actually waiting for an event. + base::WaitableEvent mTriggerPoll; + // Set to true when we wish to stop polling, instructing the poller thread to break its loop. + std::atomic_bool mStopPolling; }; -} // namespace media +} // namespace android -#endif // V4L2_V4L2_DEVICE_POLLER_H_ +#endif // ANDROID_V4L2_CODEC2_COMMON_V4L2_DEVICE_POLLER_H |