blob: 77d7ddba172818e8a03fb07b9fb10ec24ecd4755 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// 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 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 <optional>
#include <vector>
#include <C2Config.h>
#include <base/containers/flat_map.h>
#include <base/files/scoped_file.h>
#include <base/memory/ref_counted.h>
#include <ui/Size.h>
#include <v4l2_codec2/common/Common.h>
#include <v4l2_codec2/common/V4L2DevicePoller.h>
#include <v4l2_codec2/common/VideoTypes.h>
namespace android {
class V4L2Queue;
class V4L2BufferRefBase;
class V4L2BuffersList;
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;
};
// 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.
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<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.
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.
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.
//
// 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).
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()).
// |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 ui::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 ui::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:
// Specification of an encoding profile supported by an encoder.
struct SupportedEncodeProfile {
C2Config::profile_t profile = C2Config::PROFILE_UNUSED;
ui::Size min_resolution;
ui::Size max_resolution;
uint32_t max_framerate_numerator = 0;
uint32_t max_framerate_denominator = 0;
};
using SupportedEncodeProfiles = std::vector<SupportedEncodeProfile>;
// Specification of a decoding profile supported by an decoder.
// |max_resolution| and |min_resolution| are inclusive.
struct SupportedDecodeProfile {
C2Config::profile_t profile = C2Config::PROFILE_UNUSED;
ui::Size max_resolution;
ui::Size min_resolution;
bool encrypted_only = false;
};
using SupportedDecodeProfiles = std::vector<SupportedDecodeProfile>;
// Utility format conversion functions
// If there is no corresponding single- or multi-planar format, returns 0.
static uint32_t C2ProfileToV4L2PixFmt(C2Config::profile_t profile, bool sliceBased);
static C2Config::profile_t v4L2ProfileToC2Profile(VideoCodec codec, uint32_t profile);
std::vector<C2Config::profile_t> v4L2PixFmtToC2Profiles(uint32_t pixFmt, bool isEncoder);
// Calculates the largest plane's allocation size requested by a V4L2 device.
static ui::Size allocatedSizeFromV4L2Format(const struct v4l2_format& format);
// Convert required H264 profile and level to V4L2 enums.
static int32_t c2ProfileToV4L2H264Profile(C2Config::profile_t profile);
static int32_t h264LevelIdcToV4L2H264Level(uint8_t levelIdc);
static v4l2_mpeg_video_bitrate_mode C2BitrateModeToV4L2BitrateMode(
C2Config::bitrate_mode_t bitrateMode);
// 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<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, ui::Size* minResolution,
ui::Size* maxResolution);
std::vector<uint32_t> enumerateSupportedPixelformats(v4l2_buf_type bufType);
// Return supported profiles for decoder, including only profiles for given fourcc
// |pixelFormats|.
SupportedDecodeProfiles getSupportedDecodeProfiles(const size_t numFormats,
const uint32_t pixelFormats[]);
// Return supported profiles for encoder.
SupportedEncodeProfiles 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;
SupportedDecodeProfiles enumerateSupportedDecodeProfiles(const size_t numFormats,
const uint32_t pixelFormats[]);
SupportedEncodeProfiles 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 android
#endif // ANDROID_V4L2_CODEC2_COMMON_V4L2_DEVICE_H