Message ID | 20240806090607.43240-2-jonas@kwiboo.se |
---|---|
State | New |
Headers | show |
Series | Add V4L2 Request API hwaccels for MPEG2, H.264 and HEVC | expand |
Context | Check | Description |
---|---|---|
yinshiyou/make_loongarch64 | success | Make finished |
yinshiyou/make_fate_loongarch64 | success | Make fate finished |
On 06/08/2024 11:06, Jonas Karlman wrote: > Add a hwdevice type for V4L2 Request API with transfer_data_from support > for AV_PIX_FMT_DRM_PRIME, based on AV_HWDEVICE_TYPE_DRM. > > AVV4L2RequestDeviceContext.media_fd can be set by the application or a > media device path can be supplied when hwdevice is created. When none > is supplied it default to -1 and hwaccel will auto-detect a media device > with a capable video device. > > Signed-off-by: Jonas Karlman <jonas@kwiboo.se> > --- > configure | 7 + > libavutil/Makefile | 3 + > libavutil/hwcontext.c | 4 + > libavutil/hwcontext.h | 1 + > libavutil/hwcontext_internal.h | 1 + > libavutil/hwcontext_v4l2request.c | 261 ++++++++++++++++++++++++++++++ > libavutil/hwcontext_v4l2request.h | 41 +++++ > 7 files changed, 318 insertions(+) > create mode 100644 libavutil/hwcontext_v4l2request.c > create mode 100644 libavutil/hwcontext_v4l2request.h > > diff --git a/configure b/configure > index 37178d7d81..23d00edc48 100755 > --- a/configure > +++ b/configure > @@ -358,6 +358,7 @@ External library support: > --enable-omx-rpi enable OpenMAX IL code for Raspberry Pi [no] > --enable-rkmpp enable Rockchip Media Process Platform code [no] > --disable-v4l2-m2m disable V4L2 mem2mem code [autodetect] > + --enable-v4l2-request enable V4L2 Request API code [no] > --disable-vaapi disable Video Acceleration API (mainly Unix/Intel) code [autodetect] > --disable-vdpau disable Nvidia Video Decode and Presentation API for Unix code [autodetect] > --disable-videotoolbox disable VideoToolbox code [autodetect] > @@ -2023,6 +2024,7 @@ HWACCEL_LIBRARY_LIST=" > mmal > omx > opencl > + v4l2_request > " > > DOCUMENT_LIST=" > @@ -3148,6 +3150,7 @@ dxva2_deps="dxva2api_h DXVA2_ConfigPictureDecode ole32 user32" > ffnvcodec_deps_any="libdl LoadLibrary" > mediacodec_deps="android mediandk" > nvdec_deps="ffnvcodec" > +v4l2_request_deps="linux_media_h v4l2_timeval_to_ns" > vaapi_x11_deps="xlib_x11" > videotoolbox_hwaccel_deps="videotoolbox pthreads" > videotoolbox_hwaccel_extralibs="-framework QuartzCore" > @@ -7172,6 +7175,10 @@ if enabled v4l2_m2m; then > check_cc vp9_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_VP9;" > fi > > +if enabled v4l2_request; then > + check_func_headers "linux/media.h linux/videodev2.h" v4l2_timeval_to_ns > +fi > + > check_headers sys/videoio.h > test_code cc sys/videoio.h "struct v4l2_frmsizeenum vfse; vfse.discrete.width = 0;" && enable_sanitized struct_v4l2_frmivalenum_discrete > > diff --git a/libavutil/Makefile b/libavutil/Makefile > index 6e6fa8d800..1ce46157dd 100644 > --- a/libavutil/Makefile > +++ b/libavutil/Makefile > @@ -48,6 +48,7 @@ HEADERS = adler32.h \ > hwcontext_qsv.h \ > hwcontext_mediacodec.h \ > hwcontext_opencl.h \ > + hwcontext_v4l2request.h \ > hwcontext_vaapi.h \ > hwcontext_videotoolbox.h \ > hwcontext_vdpau.h \ > @@ -201,6 +202,7 @@ OBJS-$(CONFIG_MACOS_KPERF) += macos_kperf.o > OBJS-$(CONFIG_MEDIACODEC) += hwcontext_mediacodec.o > OBJS-$(CONFIG_OPENCL) += hwcontext_opencl.o > OBJS-$(CONFIG_QSV) += hwcontext_qsv.o > +OBJS-$(CONFIG_V4L2_REQUEST) += hwcontext_v4l2request.o > OBJS-$(CONFIG_VAAPI) += hwcontext_vaapi.o > OBJS-$(CONFIG_VIDEOTOOLBOX) += hwcontext_videotoolbox.o > OBJS-$(CONFIG_VDPAU) += hwcontext_vdpau.o > @@ -222,6 +224,7 @@ SKIPHEADERS-$(CONFIG_D3D12VA) += hwcontext_d3d12va.h > SKIPHEADERS-$(CONFIG_DXVA2) += hwcontext_dxva2.h > SKIPHEADERS-$(CONFIG_QSV) += hwcontext_qsv.h > SKIPHEADERS-$(CONFIG_OPENCL) += hwcontext_opencl.h > +SKIPHEADERS-$(CONFIG_V4L2_REQUEST) += hwcontext_v4l2request.h > SKIPHEADERS-$(CONFIG_VAAPI) += hwcontext_vaapi.h > SKIPHEADERS-$(CONFIG_VIDEOTOOLBOX) += hwcontext_videotoolbox.h > SKIPHEADERS-$(CONFIG_VDPAU) += hwcontext_vdpau.h > diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c > index fa99a0d8a4..7fae9381da 100644 > --- a/libavutil/hwcontext.c > +++ b/libavutil/hwcontext.c > @@ -65,6 +65,9 @@ static const HWContextType * const hw_table[] = { > #endif > #if CONFIG_VULKAN > &ff_hwcontext_type_vulkan, > +#endif > +#if CONFIG_V4L2_REQUEST > + &ff_hwcontext_type_v4l2request, > #endif > NULL, > }; > @@ -77,6 +80,7 @@ static const char *const hw_type_names[] = { > [AV_HWDEVICE_TYPE_D3D12VA] = "d3d12va", > [AV_HWDEVICE_TYPE_OPENCL] = "opencl", > [AV_HWDEVICE_TYPE_QSV] = "qsv", > + [AV_HWDEVICE_TYPE_V4L2REQUEST] = "v4l2request", > [AV_HWDEVICE_TYPE_VAAPI] = "vaapi", > [AV_HWDEVICE_TYPE_VDPAU] = "vdpau", > [AV_HWDEVICE_TYPE_VIDEOTOOLBOX] = "videotoolbox", > diff --git a/libavutil/hwcontext.h b/libavutil/hwcontext.h > index bac30debae..8cf50ddbd0 100644 > --- a/libavutil/hwcontext.h > +++ b/libavutil/hwcontext.h > @@ -38,6 +38,7 @@ enum AVHWDeviceType { > AV_HWDEVICE_TYPE_MEDIACODEC, > AV_HWDEVICE_TYPE_VULKAN, > AV_HWDEVICE_TYPE_D3D12VA, > + AV_HWDEVICE_TYPE_V4L2REQUEST, > }; > > /** > diff --git a/libavutil/hwcontext_internal.h b/libavutil/hwcontext_internal.h > index e32b786238..fd0cf29c6e 100644 > --- a/libavutil/hwcontext_internal.h > +++ b/libavutil/hwcontext_internal.h > @@ -158,6 +158,7 @@ extern const HWContextType ff_hwcontext_type_drm; > extern const HWContextType ff_hwcontext_type_dxva2; > extern const HWContextType ff_hwcontext_type_opencl; > extern const HWContextType ff_hwcontext_type_qsv; > +extern const HWContextType ff_hwcontext_type_v4l2request; > extern const HWContextType ff_hwcontext_type_vaapi; > extern const HWContextType ff_hwcontext_type_vdpau; > extern const HWContextType ff_hwcontext_type_videotoolbox; > diff --git a/libavutil/hwcontext_v4l2request.c b/libavutil/hwcontext_v4l2request.c > new file mode 100644 > index 0000000000..833fbf9f40 > --- /dev/null > +++ b/libavutil/hwcontext_v4l2request.c > @@ -0,0 +1,261 @@ > +/* > + * This file is part of FFmpeg. > + * > + * FFmpeg is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2.1 of the License, or (at your option) any later version. > + * > + * FFmpeg is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with FFmpeg; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA > + */ > + > +#include "config.h" > + > +#include <fcntl.h> > +#include <linux/dma-buf.h> > +#include <linux/media.h> > +#include <sys/ioctl.h> > +#include <sys/mman.h> > +#include <unistd.h> > + > +#include "avassert.h" > +#include "hwcontext_drm.h" > +#include "hwcontext_internal.h" > +#include "hwcontext_v4l2request.h" > +#include "mem.h" > + > +static void v4l2request_device_free(AVHWDeviceContext *hwdev) > +{ > + AVV4L2RequestDeviceContext *hwctx = hwdev->hwctx; > + > + if (hwctx->media_fd >= 0) { > + close(hwctx->media_fd); > + hwctx->media_fd = -1; > + } > +} > + > +static int v4l2request_device_create(AVHWDeviceContext *hwdev, const char *device, > + AVDictionary *opts, int flags) > +{ > + AVV4L2RequestDeviceContext *hwctx = hwdev->hwctx; > + > + hwctx->media_fd = -1; > + hwdev->free = v4l2request_device_free; > + > + // Use auto-detect > + if (!device || !device[0]) > + return 0; > + > + hwctx->media_fd = open(device, O_RDWR); > + if (hwctx->media_fd < 0) > + return AVERROR(errno); > + > + return 0; > +} > + > +static int v4l2request_device_init(AVHWDeviceContext *hwdev) > +{ > + AVV4L2RequestDeviceContext *hwctx = hwdev->hwctx; > + struct media_device_info device_info; > + > + // Use auto-detect > + if (hwctx->media_fd < 0) > + return 0; > + > + if (ioctl(hwctx->media_fd, MEDIA_IOC_DEVICE_INFO, &device_info) < 0) > + return AVERROR(errno); > + > + av_log(hwdev, AV_LOG_VERBOSE, "Using V4L2 media driver %s (%u.%u.%u)\n", > + device_info.driver, > + device_info.driver_version >> 16, > + (device_info.driver_version >> 8) & 0xff, > + device_info.driver_version & 0xff); > + > + return 0; > +} > + > +static int v4l2request_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame) > +{ > + frame->buf[0] = av_buffer_pool_get(hwfc->pool); > + if (!frame->buf[0]) > + return AVERROR(ENOMEM); > + > + frame->data[0] = (uint8_t *)frame->buf[0]->data; > + > + frame->format = AV_PIX_FMT_DRM_PRIME; > + frame->width = hwfc->width; > + frame->height = hwfc->height; > + > + return 0; > +} > + > +typedef struct DRMMapping { > + // Address and length of each mmap()ed region. > + int nb_regions; > + int object[AV_DRM_MAX_PLANES]; > + void *address[AV_DRM_MAX_PLANES]; > + size_t length[AV_DRM_MAX_PLANES]; > +} DRMMapping; > + > +static void v4l2request_unmap_frame(AVHWFramesContext *hwfc, > + HWMapDescriptor *hwmap) > +{ > + DRMMapping *map = hwmap->priv; > + > + for (int i = 0; i < map->nb_regions; i++) { > + struct dma_buf_sync sync = { > + .flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ, > + }; > + ioctl(map->object[i], DMA_BUF_IOCTL_SYNC, &sync); > + munmap(map->address[i], map->length[i]); > + } > + > + av_free(map); > +} > + > +static int v4l2request_map_frame(AVHWFramesContext *hwfc, > + AVFrame *dst, const AVFrame *src) > +{ > + const AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)src->data[0]; > + struct dma_buf_sync sync = { > + .flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_READ, > + }; > + DRMMapping *map; > + int ret, i, p, plane; > + void *addr; > + > + map = av_mallocz(sizeof(*map)); > + if (!map) > + return AVERROR(ENOMEM); > + > + av_assert0(desc->nb_objects <= AV_DRM_MAX_PLANES); > + for (i = 0; i < desc->nb_objects; i++) { > + addr = mmap(NULL, desc->objects[i].size, AV_HWFRAME_MAP_READ, MAP_SHARED, > + desc->objects[i].fd, 0); > + if (addr == MAP_FAILED) { > + av_log(hwfc, AV_LOG_ERROR, "Failed to map DRM object %d to memory: %s (%d)\n", > + desc->objects[i].fd, strerror(errno), errno); > + ret = AVERROR(errno); > + goto fail; > + } > + > + map->address[i] = addr; > + map->length[i] = desc->objects[i].size; > + map->object[i] = desc->objects[i].fd; > + > + /* > + * We're not checking for errors here because the kernel may not > + * support the ioctl, in which case its okay to carry on > + */ > + ioctl(desc->objects[i].fd, DMA_BUF_IOCTL_SYNC, &sync); > + } > + map->nb_regions = i; > + > + plane = 0; > + for (i = 0; i < desc->nb_layers; i++) { > + const AVDRMLayerDescriptor *layer = &desc->layers[i]; > + for (p = 0; p < layer->nb_planes; p++) { > + dst->data[plane] = > + (uint8_t *)map->address[layer->planes[p].object_index] + > + layer->planes[p].offset; > + dst->linesize[plane] = layer->planes[p].pitch; > + ++plane; > + } > + } > + av_assert0(plane <= AV_DRM_MAX_PLANES); > + > + dst->width = src->width; > + dst->height = src->height; > + > + ret = ff_hwframe_map_create(src->hw_frames_ctx, dst, src, > + v4l2request_unmap_frame, map); > + if (ret < 0) > + goto fail; > + > + return 0; > + > +fail: > + for (i = 0; i < desc->nb_objects; i++) { > + if (map->address[i]) > + munmap(map->address[i], map->length[i]); > + } > + av_free(map); > + return ret; > +} > + > +static int v4l2request_transfer_get_formats(AVHWFramesContext *hwfc, > + enum AVHWFrameTransferDirection dir, > + enum AVPixelFormat **formats) > +{ > + enum AVPixelFormat *pix_fmts; > + > + if (dir == AV_HWFRAME_TRANSFER_DIRECTION_TO) > + return AVERROR(ENOSYS); > + > + pix_fmts = av_malloc_array(2, sizeof(*pix_fmts)); > + if (!pix_fmts) > + return AVERROR(ENOMEM); > + > + pix_fmts[0] = hwfc->sw_format; > + pix_fmts[1] = AV_PIX_FMT_NONE; > + > + *formats = pix_fmts; > + return 0; > +} > + > +static int v4l2request_transfer_data_from(AVHWFramesContext *hwfc, > + AVFrame *dst, const AVFrame *src) > +{ > + AVFrame *map; > + int ret; > + > + if (dst->width > hwfc->width || dst->height > hwfc->height) > + return AVERROR(EINVAL); > + > + map = av_frame_alloc(); > + if (!map) > + return AVERROR(ENOMEM); > + map->format = dst->format; > + > + ret = v4l2request_map_frame(hwfc, map, src); > + if (ret) > + goto fail; > + > + map->width = dst->width; > + map->height = dst->height; > + > + ret = av_frame_copy(dst, map); > + if (ret) > + goto fail; > + > + ret = 0; > +fail: > + av_frame_free(&map); > + return ret; > +} > + > +const HWContextType ff_hwcontext_type_v4l2request = { > + .type = AV_HWDEVICE_TYPE_V4L2REQUEST, > + .name = "V4L2 Request API", > + > + .device_hwctx_size = sizeof(AVV4L2RequestDeviceContext), > + .device_create = v4l2request_device_create, > + .device_init = v4l2request_device_init, > + > + .frames_get_buffer = v4l2request_get_buffer, > + > + .transfer_get_formats = v4l2request_transfer_get_formats, > + .transfer_data_from = v4l2request_transfer_data_from, > + > + .pix_fmts = (const enum AVPixelFormat[]) { > + AV_PIX_FMT_DRM_PRIME, > + AV_PIX_FMT_NONE > + }, > +}; > diff --git a/libavutil/hwcontext_v4l2request.h b/libavutil/hwcontext_v4l2request.h > new file mode 100644 > index 0000000000..0fe42f97b4 > --- /dev/null > +++ b/libavutil/hwcontext_v4l2request.h > @@ -0,0 +1,41 @@ > +/* > + * This file is part of FFmpeg. > + * > + * FFmpeg is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2.1 of the License, or (at your option) any later version. > + * > + * FFmpeg is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with FFmpeg; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA > + */ > + > +#ifndef AVUTIL_HWCONTEXT_V4L2REQUEST_H > +#define AVUTIL_HWCONTEXT_V4L2REQUEST_H > + > +/** > + * @file > + * An API-specific header for AV_HWDEVICE_TYPE_V4L2REQUEST. > + */ > + > +/** > + * V4L2 Request API device details. > + * > + * Allocated as AVHWDeviceContext.hwctx > + */ > +typedef struct AVV4L2RequestDeviceContext { > + /** > + * File descriptor of media device. > + * > + * Defaults to -1 for auto-detect. > + */ > + int media_fd; > +} AVV4L2RequestDeviceContext; > + > +#endif /* AVUTIL_HWCONTEXT_V4L2REQUEST_H */ Any reason you're not using hwcontext_drm.h?
Hi Lynne, On 2024-08-06 14:46, Lynne via ffmpeg-devel wrote: > On 06/08/2024 11:06, Jonas Karlman wrote: >> Add a hwdevice type for V4L2 Request API with transfer_data_from support >> for AV_PIX_FMT_DRM_PRIME, based on AV_HWDEVICE_TYPE_DRM. >> >> AVV4L2RequestDeviceContext.media_fd can be set by the application or a >> media device path can be supplied when hwdevice is created. When none >> is supplied it default to -1 and hwaccel will auto-detect a media device >> with a capable video device. >> >> Signed-off-by: Jonas Karlman <jonas@kwiboo.se> >> --- >> configure | 7 + >> libavutil/Makefile | 3 + >> libavutil/hwcontext.c | 4 + >> libavutil/hwcontext.h | 1 + >> libavutil/hwcontext_internal.h | 1 + >> libavutil/hwcontext_v4l2request.c | 261 ++++++++++++++++++++++++++++++ >> libavutil/hwcontext_v4l2request.h | 41 +++++ >> 7 files changed, 318 insertions(+) >> create mode 100644 libavutil/hwcontext_v4l2request.c >> create mode 100644 libavutil/hwcontext_v4l2request.h >> [snip] >> diff --git a/libavutil/hwcontext_v4l2request.h b/libavutil/hwcontext_v4l2request.h >> new file mode 100644 >> index 0000000000..0fe42f97b4 >> --- /dev/null >> +++ b/libavutil/hwcontext_v4l2request.h >> @@ -0,0 +1,41 @@ >> +/* >> + * This file is part of FFmpeg. >> + * >> + * FFmpeg is free software; you can redistribute it and/or >> + * modify it under the terms of the GNU Lesser General Public >> + * License as published by the Free Software Foundation; either >> + * version 2.1 of the License, or (at your option) any later version. >> + * >> + * FFmpeg is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >> + * Lesser General Public License for more details. >> + * >> + * You should have received a copy of the GNU Lesser General Public >> + * License along with FFmpeg; if not, write to the Free Software >> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >> + */ >> + >> +#ifndef AVUTIL_HWCONTEXT_V4L2REQUEST_H >> +#define AVUTIL_HWCONTEXT_V4L2REQUEST_H >> + >> +/** >> + * @file >> + * An API-specific header for AV_HWDEVICE_TYPE_V4L2REQUEST. >> + */ >> + >> +/** >> + * V4L2 Request API device details. >> + * >> + * Allocated as AVHWDeviceContext.hwctx >> + */ >> +typedef struct AVV4L2RequestDeviceContext { >> + /** >> + * File descriptor of media device. >> + * >> + * Defaults to -1 for auto-detect. >> + */ >> + int media_fd; >> +} AVV4L2RequestDeviceContext; >> + >> +#endif /* AVUTIL_HWCONTEXT_V4L2REQUEST_H */ > > Any reason you're not using hwcontext_drm.h? > The DRM hwdevice/hwcontext requires a DRM/DRI device, however V4L2 decoding does not require a DRM/DRI device at all to do decode. This became fully apparent on a Rockchip RK3588 device that at the time did not have gpu or display support in mainline, yet it had a working AV1 decoder. Using a DRM hwdevice/hwcontext required an application to supply a path to a none existing DRI device. Instead a separate new hwdevice type is introduced, for now it can be used to define a specific media device to use /dev/mediaX, in future it could be extended to support options to specify what /dev/videoX device to use for decoding. These hwaccels only want to produce AV_PIX_FMT_DRM_PRIME output buffers and does not want to be tied to any DRM/DRI device, if there is better ways to do this, please advise. Regards, Jonas
diff --git a/configure b/configure index 37178d7d81..23d00edc48 100755 --- a/configure +++ b/configure @@ -358,6 +358,7 @@ External library support: --enable-omx-rpi enable OpenMAX IL code for Raspberry Pi [no] --enable-rkmpp enable Rockchip Media Process Platform code [no] --disable-v4l2-m2m disable V4L2 mem2mem code [autodetect] + --enable-v4l2-request enable V4L2 Request API code [no] --disable-vaapi disable Video Acceleration API (mainly Unix/Intel) code [autodetect] --disable-vdpau disable Nvidia Video Decode and Presentation API for Unix code [autodetect] --disable-videotoolbox disable VideoToolbox code [autodetect] @@ -2023,6 +2024,7 @@ HWACCEL_LIBRARY_LIST=" mmal omx opencl + v4l2_request " DOCUMENT_LIST=" @@ -3148,6 +3150,7 @@ dxva2_deps="dxva2api_h DXVA2_ConfigPictureDecode ole32 user32" ffnvcodec_deps_any="libdl LoadLibrary" mediacodec_deps="android mediandk" nvdec_deps="ffnvcodec" +v4l2_request_deps="linux_media_h v4l2_timeval_to_ns" vaapi_x11_deps="xlib_x11" videotoolbox_hwaccel_deps="videotoolbox pthreads" videotoolbox_hwaccel_extralibs="-framework QuartzCore" @@ -7172,6 +7175,10 @@ if enabled v4l2_m2m; then check_cc vp9_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_VP9;" fi +if enabled v4l2_request; then + check_func_headers "linux/media.h linux/videodev2.h" v4l2_timeval_to_ns +fi + check_headers sys/videoio.h test_code cc sys/videoio.h "struct v4l2_frmsizeenum vfse; vfse.discrete.width = 0;" && enable_sanitized struct_v4l2_frmivalenum_discrete diff --git a/libavutil/Makefile b/libavutil/Makefile index 6e6fa8d800..1ce46157dd 100644 --- a/libavutil/Makefile +++ b/libavutil/Makefile @@ -48,6 +48,7 @@ HEADERS = adler32.h \ hwcontext_qsv.h \ hwcontext_mediacodec.h \ hwcontext_opencl.h \ + hwcontext_v4l2request.h \ hwcontext_vaapi.h \ hwcontext_videotoolbox.h \ hwcontext_vdpau.h \ @@ -201,6 +202,7 @@ OBJS-$(CONFIG_MACOS_KPERF) += macos_kperf.o OBJS-$(CONFIG_MEDIACODEC) += hwcontext_mediacodec.o OBJS-$(CONFIG_OPENCL) += hwcontext_opencl.o OBJS-$(CONFIG_QSV) += hwcontext_qsv.o +OBJS-$(CONFIG_V4L2_REQUEST) += hwcontext_v4l2request.o OBJS-$(CONFIG_VAAPI) += hwcontext_vaapi.o OBJS-$(CONFIG_VIDEOTOOLBOX) += hwcontext_videotoolbox.o OBJS-$(CONFIG_VDPAU) += hwcontext_vdpau.o @@ -222,6 +224,7 @@ SKIPHEADERS-$(CONFIG_D3D12VA) += hwcontext_d3d12va.h SKIPHEADERS-$(CONFIG_DXVA2) += hwcontext_dxva2.h SKIPHEADERS-$(CONFIG_QSV) += hwcontext_qsv.h SKIPHEADERS-$(CONFIG_OPENCL) += hwcontext_opencl.h +SKIPHEADERS-$(CONFIG_V4L2_REQUEST) += hwcontext_v4l2request.h SKIPHEADERS-$(CONFIG_VAAPI) += hwcontext_vaapi.h SKIPHEADERS-$(CONFIG_VIDEOTOOLBOX) += hwcontext_videotoolbox.h SKIPHEADERS-$(CONFIG_VDPAU) += hwcontext_vdpau.h diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c index fa99a0d8a4..7fae9381da 100644 --- a/libavutil/hwcontext.c +++ b/libavutil/hwcontext.c @@ -65,6 +65,9 @@ static const HWContextType * const hw_table[] = { #endif #if CONFIG_VULKAN &ff_hwcontext_type_vulkan, +#endif +#if CONFIG_V4L2_REQUEST + &ff_hwcontext_type_v4l2request, #endif NULL, }; @@ -77,6 +80,7 @@ static const char *const hw_type_names[] = { [AV_HWDEVICE_TYPE_D3D12VA] = "d3d12va", [AV_HWDEVICE_TYPE_OPENCL] = "opencl", [AV_HWDEVICE_TYPE_QSV] = "qsv", + [AV_HWDEVICE_TYPE_V4L2REQUEST] = "v4l2request", [AV_HWDEVICE_TYPE_VAAPI] = "vaapi", [AV_HWDEVICE_TYPE_VDPAU] = "vdpau", [AV_HWDEVICE_TYPE_VIDEOTOOLBOX] = "videotoolbox", diff --git a/libavutil/hwcontext.h b/libavutil/hwcontext.h index bac30debae..8cf50ddbd0 100644 --- a/libavutil/hwcontext.h +++ b/libavutil/hwcontext.h @@ -38,6 +38,7 @@ enum AVHWDeviceType { AV_HWDEVICE_TYPE_MEDIACODEC, AV_HWDEVICE_TYPE_VULKAN, AV_HWDEVICE_TYPE_D3D12VA, + AV_HWDEVICE_TYPE_V4L2REQUEST, }; /** diff --git a/libavutil/hwcontext_internal.h b/libavutil/hwcontext_internal.h index e32b786238..fd0cf29c6e 100644 --- a/libavutil/hwcontext_internal.h +++ b/libavutil/hwcontext_internal.h @@ -158,6 +158,7 @@ extern const HWContextType ff_hwcontext_type_drm; extern const HWContextType ff_hwcontext_type_dxva2; extern const HWContextType ff_hwcontext_type_opencl; extern const HWContextType ff_hwcontext_type_qsv; +extern const HWContextType ff_hwcontext_type_v4l2request; extern const HWContextType ff_hwcontext_type_vaapi; extern const HWContextType ff_hwcontext_type_vdpau; extern const HWContextType ff_hwcontext_type_videotoolbox; diff --git a/libavutil/hwcontext_v4l2request.c b/libavutil/hwcontext_v4l2request.c new file mode 100644 index 0000000000..833fbf9f40 --- /dev/null +++ b/libavutil/hwcontext_v4l2request.c @@ -0,0 +1,261 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include <fcntl.h> +#include <linux/dma-buf.h> +#include <linux/media.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <unistd.h> + +#include "avassert.h" +#include "hwcontext_drm.h" +#include "hwcontext_internal.h" +#include "hwcontext_v4l2request.h" +#include "mem.h" + +static void v4l2request_device_free(AVHWDeviceContext *hwdev) +{ + AVV4L2RequestDeviceContext *hwctx = hwdev->hwctx; + + if (hwctx->media_fd >= 0) { + close(hwctx->media_fd); + hwctx->media_fd = -1; + } +} + +static int v4l2request_device_create(AVHWDeviceContext *hwdev, const char *device, + AVDictionary *opts, int flags) +{ + AVV4L2RequestDeviceContext *hwctx = hwdev->hwctx; + + hwctx->media_fd = -1; + hwdev->free = v4l2request_device_free; + + // Use auto-detect + if (!device || !device[0]) + return 0; + + hwctx->media_fd = open(device, O_RDWR); + if (hwctx->media_fd < 0) + return AVERROR(errno); + + return 0; +} + +static int v4l2request_device_init(AVHWDeviceContext *hwdev) +{ + AVV4L2RequestDeviceContext *hwctx = hwdev->hwctx; + struct media_device_info device_info; + + // Use auto-detect + if (hwctx->media_fd < 0) + return 0; + + if (ioctl(hwctx->media_fd, MEDIA_IOC_DEVICE_INFO, &device_info) < 0) + return AVERROR(errno); + + av_log(hwdev, AV_LOG_VERBOSE, "Using V4L2 media driver %s (%u.%u.%u)\n", + device_info.driver, + device_info.driver_version >> 16, + (device_info.driver_version >> 8) & 0xff, + device_info.driver_version & 0xff); + + return 0; +} + +static int v4l2request_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame) +{ + frame->buf[0] = av_buffer_pool_get(hwfc->pool); + if (!frame->buf[0]) + return AVERROR(ENOMEM); + + frame->data[0] = (uint8_t *)frame->buf[0]->data; + + frame->format = AV_PIX_FMT_DRM_PRIME; + frame->width = hwfc->width; + frame->height = hwfc->height; + + return 0; +} + +typedef struct DRMMapping { + // Address and length of each mmap()ed region. + int nb_regions; + int object[AV_DRM_MAX_PLANES]; + void *address[AV_DRM_MAX_PLANES]; + size_t length[AV_DRM_MAX_PLANES]; +} DRMMapping; + +static void v4l2request_unmap_frame(AVHWFramesContext *hwfc, + HWMapDescriptor *hwmap) +{ + DRMMapping *map = hwmap->priv; + + for (int i = 0; i < map->nb_regions; i++) { + struct dma_buf_sync sync = { + .flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ, + }; + ioctl(map->object[i], DMA_BUF_IOCTL_SYNC, &sync); + munmap(map->address[i], map->length[i]); + } + + av_free(map); +} + +static int v4l2request_map_frame(AVHWFramesContext *hwfc, + AVFrame *dst, const AVFrame *src) +{ + const AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)src->data[0]; + struct dma_buf_sync sync = { + .flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_READ, + }; + DRMMapping *map; + int ret, i, p, plane; + void *addr; + + map = av_mallocz(sizeof(*map)); + if (!map) + return AVERROR(ENOMEM); + + av_assert0(desc->nb_objects <= AV_DRM_MAX_PLANES); + for (i = 0; i < desc->nb_objects; i++) { + addr = mmap(NULL, desc->objects[i].size, AV_HWFRAME_MAP_READ, MAP_SHARED, + desc->objects[i].fd, 0); + if (addr == MAP_FAILED) { + av_log(hwfc, AV_LOG_ERROR, "Failed to map DRM object %d to memory: %s (%d)\n", + desc->objects[i].fd, strerror(errno), errno); + ret = AVERROR(errno); + goto fail; + } + + map->address[i] = addr; + map->length[i] = desc->objects[i].size; + map->object[i] = desc->objects[i].fd; + + /* + * We're not checking for errors here because the kernel may not + * support the ioctl, in which case its okay to carry on + */ + ioctl(desc->objects[i].fd, DMA_BUF_IOCTL_SYNC, &sync); + } + map->nb_regions = i; + + plane = 0; + for (i = 0; i < desc->nb_layers; i++) { + const AVDRMLayerDescriptor *layer = &desc->layers[i]; + for (p = 0; p < layer->nb_planes; p++) { + dst->data[plane] = + (uint8_t *)map->address[layer->planes[p].object_index] + + layer->planes[p].offset; + dst->linesize[plane] = layer->planes[p].pitch; + ++plane; + } + } + av_assert0(plane <= AV_DRM_MAX_PLANES); + + dst->width = src->width; + dst->height = src->height; + + ret = ff_hwframe_map_create(src->hw_frames_ctx, dst, src, + v4l2request_unmap_frame, map); + if (ret < 0) + goto fail; + + return 0; + +fail: + for (i = 0; i < desc->nb_objects; i++) { + if (map->address[i]) + munmap(map->address[i], map->length[i]); + } + av_free(map); + return ret; +} + +static int v4l2request_transfer_get_formats(AVHWFramesContext *hwfc, + enum AVHWFrameTransferDirection dir, + enum AVPixelFormat **formats) +{ + enum AVPixelFormat *pix_fmts; + + if (dir == AV_HWFRAME_TRANSFER_DIRECTION_TO) + return AVERROR(ENOSYS); + + pix_fmts = av_malloc_array(2, sizeof(*pix_fmts)); + if (!pix_fmts) + return AVERROR(ENOMEM); + + pix_fmts[0] = hwfc->sw_format; + pix_fmts[1] = AV_PIX_FMT_NONE; + + *formats = pix_fmts; + return 0; +} + +static int v4l2request_transfer_data_from(AVHWFramesContext *hwfc, + AVFrame *dst, const AVFrame *src) +{ + AVFrame *map; + int ret; + + if (dst->width > hwfc->width || dst->height > hwfc->height) + return AVERROR(EINVAL); + + map = av_frame_alloc(); + if (!map) + return AVERROR(ENOMEM); + map->format = dst->format; + + ret = v4l2request_map_frame(hwfc, map, src); + if (ret) + goto fail; + + map->width = dst->width; + map->height = dst->height; + + ret = av_frame_copy(dst, map); + if (ret) + goto fail; + + ret = 0; +fail: + av_frame_free(&map); + return ret; +} + +const HWContextType ff_hwcontext_type_v4l2request = { + .type = AV_HWDEVICE_TYPE_V4L2REQUEST, + .name = "V4L2 Request API", + + .device_hwctx_size = sizeof(AVV4L2RequestDeviceContext), + .device_create = v4l2request_device_create, + .device_init = v4l2request_device_init, + + .frames_get_buffer = v4l2request_get_buffer, + + .transfer_get_formats = v4l2request_transfer_get_formats, + .transfer_data_from = v4l2request_transfer_data_from, + + .pix_fmts = (const enum AVPixelFormat[]) { + AV_PIX_FMT_DRM_PRIME, + AV_PIX_FMT_NONE + }, +}; diff --git a/libavutil/hwcontext_v4l2request.h b/libavutil/hwcontext_v4l2request.h new file mode 100644 index 0000000000..0fe42f97b4 --- /dev/null +++ b/libavutil/hwcontext_v4l2request.h @@ -0,0 +1,41 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HWCONTEXT_V4L2REQUEST_H +#define AVUTIL_HWCONTEXT_V4L2REQUEST_H + +/** + * @file + * An API-specific header for AV_HWDEVICE_TYPE_V4L2REQUEST. + */ + +/** + * V4L2 Request API device details. + * + * Allocated as AVHWDeviceContext.hwctx + */ +typedef struct AVV4L2RequestDeviceContext { + /** + * File descriptor of media device. + * + * Defaults to -1 for auto-detect. + */ + int media_fd; +} AVV4L2RequestDeviceContext; + +#endif /* AVUTIL_HWCONTEXT_V4L2REQUEST_H */
Add a hwdevice type for V4L2 Request API with transfer_data_from support for AV_PIX_FMT_DRM_PRIME, based on AV_HWDEVICE_TYPE_DRM. AVV4L2RequestDeviceContext.media_fd can be set by the application or a media device path can be supplied when hwdevice is created. When none is supplied it default to -1 and hwaccel will auto-detect a media device with a capable video device. Signed-off-by: Jonas Karlman <jonas@kwiboo.se> --- configure | 7 + libavutil/Makefile | 3 + libavutil/hwcontext.c | 4 + libavutil/hwcontext.h | 1 + libavutil/hwcontext_internal.h | 1 + libavutil/hwcontext_v4l2request.c | 261 ++++++++++++++++++++++++++++++ libavutil/hwcontext_v4l2request.h | 41 +++++ 7 files changed, 318 insertions(+) create mode 100644 libavutil/hwcontext_v4l2request.c create mode 100644 libavutil/hwcontext_v4l2request.h