| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- .. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later
- .. c:namespace:: MC
- .. _media-request-api:
- Request API
- ===========
- The Request API has been designed to allow V4L2 to deal with requirements of
- modern devices (stateless codecs, complex camera pipelines, ...) and APIs
- (Android Codec v2). One such requirement is the ability for devices belonging to
- the same pipeline to reconfigure and collaborate closely on a per-frame basis.
- Another is support of stateless codecs, which require controls to be applied
- to specific frames (aka 'per-frame controls') in order to be used efficiently.
- While the initial use-case was V4L2, it can be extended to other subsystems
- as well, as long as they use the media controller.
- Supporting these features without the Request API is not always possible and if
- it is, it is terribly inefficient: user-space would have to flush all activity
- on the media pipeline, reconfigure it for the next frame, queue the buffers to
- be processed with that configuration, and wait until they are all available for
- dequeuing before considering the next frame. This defeats the purpose of having
- buffer queues since in practice only one buffer would be queued at a time.
- The Request API allows a specific configuration of the pipeline (media
- controller topology + configuration for each media entity) to be associated with
- specific buffers. This allows user-space to schedule several tasks ("requests")
- with different configurations in advance, knowing that the configuration will be
- applied when needed to get the expected result. Configuration values at the time
- of request completion are also available for reading.
- General Usage
- -------------
- The Request API extends the Media Controller API and cooperates with
- subsystem-specific APIs to support request usage. At the Media Controller
- level, requests are allocated from the supporting Media Controller device
- node. Their life cycle is then managed through the request file descriptors in
- an opaque way. Configuration data, buffer handles and processing results
- stored in requests are accessed through subsystem-specific APIs extended for
- request support, such as V4L2 APIs that take an explicit ``request_fd``
- parameter.
- Request Allocation
- ------------------
- User-space allocates requests using :ref:`MEDIA_IOC_REQUEST_ALLOC`
- for the media device node. This returns a file descriptor representing the
- request. Typically, several such requests will be allocated.
- Request Preparation
- -------------------
- Standard V4L2 ioctls can then receive a request file descriptor to express the
- fact that the ioctl is part of said request, and is not to be applied
- immediately. See :ref:`MEDIA_IOC_REQUEST_ALLOC` for a list of ioctls that
- support this. Configurations set with a ``request_fd`` parameter are stored
- instead of being immediately applied, and buffers queued to a request do not
- enter the regular buffer queue until the request itself is queued.
- Request Submission
- ------------------
- Once the configuration and buffers of the request are specified, it can be
- queued by calling :ref:`MEDIA_REQUEST_IOC_QUEUE` on the request file descriptor.
- A request must contain at least one buffer, otherwise ``ENOENT`` is returned.
- A queued request cannot be modified anymore.
- .. caution::
- For :ref:`memory-to-memory devices <mem2mem>` you can use requests only for
- output buffers, not for capture buffers. Attempting to add a capture buffer
- to a request will result in an ``EBADR`` error.
- If the request contains configurations for multiple entities, individual drivers
- may synchronize so the requested pipeline's topology is applied before the
- buffers are processed. Media controller drivers do a best effort implementation
- since perfect atomicity may not be possible due to hardware limitations.
- .. caution::
- It is not allowed to mix queuing requests with directly queuing buffers:
- whichever method is used first locks this in place until
- :ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` is called or the device is
- :ref:`closed <func-close>`. Attempts to directly queue a buffer when earlier
- a buffer was queued via a request or vice versa will result in an ``EBUSY``
- error.
- Controls can still be set without a request and are applied immediately,
- regardless of whether a request is in use or not.
- .. caution::
- Setting the same control through a request and also directly can lead to
- undefined behavior!
- User-space can :c:func:`poll()` a request file descriptor in
- order to wait until the request completes. A request is considered complete
- once all its associated buffers are available for dequeuing and all the
- associated controls have been updated with the values at the time of completion.
- Note that user-space does not need to wait for the request to complete to
- dequeue its buffers: buffers that are available halfway through a request can
- be dequeued independently of the request's state.
- A completed request contains the state of the device after the request was
- executed. User-space can query that state by calling
- :ref:`ioctl VIDIOC_G_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` with the request file
- descriptor. Calling :ref:`ioctl VIDIOC_G_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` for a
- request that has been queued but not yet completed will return ``EBUSY``
- since the control values might be changed at any time by the driver while the
- request is in flight.
- .. _media-request-life-time:
- Recycling and Destruction
- -------------------------
- Finally, a completed request can either be discarded or be reused. Calling
- :c:func:`close()` on a request file descriptor will make
- that file descriptor unusable and the request will be freed once it is no
- longer in use by the kernel. That is, if the request is queued and then the
- file descriptor is closed, then it won't be freed until the driver completed
- the request.
- The :ref:`MEDIA_REQUEST_IOC_REINIT` will clear a request's state and make it
- available again. No state is retained by this operation: the request is as
- if it had just been allocated.
- Example for a Codec Device
- --------------------------
- For use-cases such as :ref:`codecs <mem2mem>`, the request API can be used
- to associate specific controls to
- be applied by the driver for the OUTPUT buffer, allowing user-space
- to queue many such buffers in advance. It can also take advantage of requests'
- ability to capture the state of controls when the request completes to read back
- information that may be subject to change.
- Put into code, after obtaining a request, user-space can assign controls and one
- OUTPUT buffer to it:
- .. code-block:: c
- struct v4l2_buffer buf;
- struct v4l2_ext_controls ctrls;
- int req_fd;
- ...
- if (ioctl(media_fd, MEDIA_IOC_REQUEST_ALLOC, &req_fd))
- return errno;
- ...
- ctrls.which = V4L2_CTRL_WHICH_REQUEST_VAL;
- ctrls.request_fd = req_fd;
- if (ioctl(codec_fd, VIDIOC_S_EXT_CTRLS, &ctrls))
- return errno;
- ...
- buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- buf.flags |= V4L2_BUF_FLAG_REQUEST_FD;
- buf.request_fd = req_fd;
- if (ioctl(codec_fd, VIDIOC_QBUF, &buf))
- return errno;
- Note that it is not allowed to use the Request API for CAPTURE buffers
- since there are no per-frame settings to report there.
- Once the request is fully prepared, it can be queued to the driver:
- .. code-block:: c
- if (ioctl(req_fd, MEDIA_REQUEST_IOC_QUEUE))
- return errno;
- User-space can then either wait for the request to complete by calling poll() on
- its file descriptor, or start dequeuing CAPTURE buffers. Most likely, it will
- want to get CAPTURE buffers as soon as possible and this can be done using a
- regular :ref:`VIDIOC_DQBUF <VIDIOC_QBUF>`:
- .. code-block:: c
- struct v4l2_buffer buf;
- memset(&buf, 0, sizeof(buf));
- buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- if (ioctl(codec_fd, VIDIOC_DQBUF, &buf))
- return errno;
- Note that this example assumes for simplicity that for every OUTPUT buffer
- there will be one CAPTURE buffer, but this does not have to be the case.
- We can then, after ensuring that the request is completed via polling the
- request file descriptor, query control values at the time of its completion via
- a call to :ref:`VIDIOC_G_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>`.
- This is particularly useful for volatile controls for which we want to
- query values as soon as the capture buffer is produced.
- .. code-block:: c
- struct pollfd pfd = { .events = POLLPRI, .fd = req_fd };
- poll(&pfd, 1, -1);
- ...
- ctrls.which = V4L2_CTRL_WHICH_REQUEST_VAL;
- ctrls.request_fd = req_fd;
- if (ioctl(codec_fd, VIDIOC_G_EXT_CTRLS, &ctrls))
- return errno;
- Once we don't need the request anymore, we can either recycle it for reuse with
- :ref:`MEDIA_REQUEST_IOC_REINIT`...
- .. code-block:: c
- if (ioctl(req_fd, MEDIA_REQUEST_IOC_REINIT))
- return errno;
- ... or close its file descriptor to completely dispose of it.
- .. code-block:: c
- close(req_fd);
- Example for a Simple Capture Device
- -----------------------------------
- With a simple capture device, requests can be used to specify controls to apply
- for a given CAPTURE buffer.
- .. code-block:: c
- struct v4l2_buffer buf;
- struct v4l2_ext_controls ctrls;
- int req_fd;
- ...
- if (ioctl(media_fd, MEDIA_IOC_REQUEST_ALLOC, &req_fd))
- return errno;
- ...
- ctrls.which = V4L2_CTRL_WHICH_REQUEST_VAL;
- ctrls.request_fd = req_fd;
- if (ioctl(camera_fd, VIDIOC_S_EXT_CTRLS, &ctrls))
- return errno;
- ...
- buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf.flags |= V4L2_BUF_FLAG_REQUEST_FD;
- buf.request_fd = req_fd;
- if (ioctl(camera_fd, VIDIOC_QBUF, &buf))
- return errno;
- Once the request is fully prepared, it can be queued to the driver:
- .. code-block:: c
- if (ioctl(req_fd, MEDIA_REQUEST_IOC_QUEUE))
- return errno;
- User-space can then dequeue buffers, wait for the request completion, query
- controls and recycle the request as in the M2M example above.
|