| .. 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. |