mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-27 00:16:37 +00:00
When the driver prefered format is not picked by downstream, the decoders needs to select another format from the list. The selection was currently unsorted, resulting in 10bit data often being stripped to 8bit. To solve this, reorder the formats in an HW preference order. This order deviates slightly from the preferred order in libgstvideo. This is to prefer bandwidth saving over better CPU alignment. As an example NV15 is prefered over P010. We also prefer tiled over linear. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8522>
383 lines
12 KiB
C
383 lines
12 KiB
C
/* GStreamer
|
|
* Copyright (C) 2020 Nicolas Dufresne <nicolas.dufresne@collabora.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <gst/allocators/allocators.h>
|
|
|
|
#include "gstv4l2format.h"
|
|
|
|
#include "linux/drm_fourcc.h"
|
|
|
|
#define GST_CAT_DEFAULT gstv4l2codecs_debug
|
|
GST_DEBUG_CATEGORY_EXTERN (gstv4l2codecs_debug);
|
|
|
|
#ifndef V4L2_PIX_FMT_NC12
|
|
#define V4L2_PIX_FMT_NC12 v4l2_fourcc('N', 'C', '1', '2') /* Y/CbCr 4:2:0 (128b cols) */
|
|
#endif
|
|
|
|
#ifndef V4L2_PIX_FMT_NV15
|
|
#define V4L2_PIX_FMT_NV15 v4l2_fourcc('N', 'V', '1', '5') /* 15 Y/CbCr 4:2:0 10-bit packed */
|
|
#endif
|
|
|
|
typedef struct
|
|
{
|
|
guint32 v4l2_pix_fmt;
|
|
GstVideoFormat gst_fmt;
|
|
guint32 drm_fourcc;
|
|
guint64 drm_modifier;
|
|
gint num_planes;
|
|
} GstV4l2FormatDesc;
|
|
|
|
/* *INDENT-OFF* */
|
|
/* Keep the same order as GST_V4L2_DEFAULT_VIDEO_FORMATS */
|
|
static const GstV4l2FormatDesc gst_v4l2_descriptions[] = {
|
|
{V4L2_PIX_FMT_MT2110R, GST_VIDEO_FORMAT_MT2110R, DRM_FORMAT_INVALID, DRM_FORMAT_MOD_INVALID, 0},
|
|
{V4L2_PIX_FMT_MT2110T, GST_VIDEO_FORMAT_MT2110T, DRM_FORMAT_INVALID, DRM_FORMAT_MOD_INVALID, 0},
|
|
{V4L2_PIX_FMT_NV15_4L4, GST_VIDEO_FORMAT_NV12_10LE40_4L4, DRM_FORMAT_INVALID, DRM_FORMAT_MOD_INVALID, 0},
|
|
{V4L2_PIX_FMT_NV15, GST_VIDEO_FORMAT_NV12_10LE40, DRM_FORMAT_INVALID, DRM_FORMAT_MOD_INVALID, 0},
|
|
{V4L2_PIX_FMT_P010, GST_VIDEO_FORMAT_P010_10LE, DRM_FORMAT_INVALID, DRM_FORMAT_MOD_INVALID, 0},
|
|
{V4L2_PIX_FMT_YUYV, GST_VIDEO_FORMAT_YUY2, DRM_FORMAT_INVALID, DRM_FORMAT_MOD_INVALID, 0},
|
|
{V4L2_PIX_FMT_MM21, GST_VIDEO_FORMAT_NV12_16L32S, DRM_FORMAT_INVALID, DRM_FORMAT_MOD_INVALID, 0},
|
|
{V4L2_PIX_FMT_SUNXI_TILED_NV12, GST_VIDEO_FORMAT_NV12_32L32, DRM_FORMAT_INVALID, DRM_FORMAT_MOD_INVALID, 0},
|
|
{V4L2_PIX_FMT_NV12_4L4, GST_VIDEO_FORMAT_NV12_4L4, DRM_FORMAT_INVALID, DRM_FORMAT_MOD_INVALID, 0},
|
|
{V4L2_PIX_FMT_NC12, GST_VIDEO_FORMAT_UNKNOWN, DRM_FORMAT_NV12, DRM_FORMAT_MOD_BROADCOM_SAND128, 2},
|
|
{V4L2_PIX_FMT_NV12, GST_VIDEO_FORMAT_NV12, DRM_FORMAT_INVALID, DRM_FORMAT_MOD_INVALID, 0},
|
|
{V4L2_PIX_FMT_YUV420M, GST_VIDEO_FORMAT_I420, DRM_FORMAT_INVALID, DRM_FORMAT_MOD_INVALID, 0},
|
|
};
|
|
/* *INDENT-ON* */
|
|
#define GST_V4L2_FORMAT_DESC_COUNT (G_N_ELEMENTS (gst_v4l2_descriptions))
|
|
|
|
static const GstV4l2FormatDesc *
|
|
gst_v4l2_format_get_descriptions (void)
|
|
{
|
|
static GstV4l2FormatDesc v4l2_descs[GST_V4L2_FORMAT_DESC_COUNT];
|
|
static gsize once = 0;
|
|
|
|
if (g_once_init_enter (&once)) {
|
|
for (int i = 0; i < GST_V4L2_FORMAT_DESC_COUNT; i++) {
|
|
v4l2_descs[i].v4l2_pix_fmt = gst_v4l2_descriptions[i].v4l2_pix_fmt;
|
|
if (gst_v4l2_descriptions[i].gst_fmt != GST_VIDEO_FORMAT_UNKNOWN) {
|
|
const GstVideoFormatInfo *info;
|
|
guint64 drm_modifier;
|
|
|
|
v4l2_descs[i].gst_fmt = gst_v4l2_descriptions[i].gst_fmt;
|
|
v4l2_descs[i].drm_fourcc =
|
|
gst_video_dma_drm_format_from_gst_format (gst_v4l2_descriptions
|
|
[i].gst_fmt, &drm_modifier);
|
|
v4l2_descs[i].drm_modifier = drm_modifier;
|
|
|
|
info = gst_video_format_get_info (gst_v4l2_descriptions[i].gst_fmt);
|
|
v4l2_descs[i].num_planes = GST_VIDEO_FORMAT_INFO_N_PLANES (info);
|
|
} else if (gst_v4l2_descriptions[i].drm_fourcc != DRM_FORMAT_INVALID &&
|
|
gst_v4l2_descriptions[i].num_planes > 0) {
|
|
v4l2_descs[i].gst_fmt = GST_VIDEO_FORMAT_DMA_DRM;
|
|
v4l2_descs[i].drm_fourcc = gst_v4l2_descriptions[i].drm_fourcc;
|
|
v4l2_descs[i].drm_modifier = gst_v4l2_descriptions[i].drm_modifier;
|
|
v4l2_descs[i].num_planes = gst_v4l2_descriptions[i].num_planes;
|
|
} else {
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
g_once_init_leave (&once, 1);
|
|
}
|
|
|
|
return v4l2_descs;
|
|
}
|
|
|
|
static const GstV4l2FormatDesc *
|
|
gst_v4l2_lookup_pix_format (guint32 pix_format)
|
|
{
|
|
const GstV4l2FormatDesc *fmt_descs = gst_v4l2_format_get_descriptions ();
|
|
|
|
for (int i = 0; i < GST_V4L2_FORMAT_DESC_COUNT; i++) {
|
|
if (fmt_descs[i].v4l2_pix_fmt == pix_format)
|
|
return &fmt_descs[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static const GstV4l2FormatDesc *
|
|
gst_v4l2_lookup_drm_format (guint32 drm_fourcc, guint64 drm_modifier)
|
|
{
|
|
const GstV4l2FormatDesc *fmt_descs = gst_v4l2_format_get_descriptions ();
|
|
|
|
if (drm_fourcc == DRM_FORMAT_INVALID)
|
|
return NULL;
|
|
|
|
for (int i = 0; i < GST_V4L2_FORMAT_DESC_COUNT; i++) {
|
|
if (fmt_descs[i].drm_fourcc == drm_fourcc &&
|
|
fmt_descs[i].drm_modifier == drm_modifier)
|
|
return &fmt_descs[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static const GstV4l2FormatDesc *
|
|
gst_v4l2_loopup_video_format (GstVideoFormat gst_format)
|
|
{
|
|
const GstV4l2FormatDesc *fmt_descs = gst_v4l2_format_get_descriptions ();
|
|
|
|
if (gst_format == GST_VIDEO_FORMAT_UNKNOWN ||
|
|
gst_format == GST_VIDEO_FORMAT_DMA_DRM)
|
|
return NULL;
|
|
|
|
for (int i = 0; i < GST_V4L2_FORMAT_DESC_COUNT; i++) {
|
|
if (fmt_descs[i].gst_fmt == gst_format)
|
|
return &fmt_descs[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
set_stride (GstVideoInfoDmaDrm * info, gint plane, gint stride)
|
|
{
|
|
const GstVideoFormatInfo *finfo = info->vinfo.finfo;
|
|
|
|
if (GST_VIDEO_FORMAT_INFO_IS_TILED (finfo)) {
|
|
guint x_tiles, y_tiles, tile_height, padded_height;
|
|
|
|
tile_height = GST_VIDEO_FORMAT_INFO_TILE_HEIGHT (finfo, plane);
|
|
|
|
padded_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (finfo, plane,
|
|
info->vinfo.height);
|
|
|
|
x_tiles = stride / GST_VIDEO_FORMAT_INFO_TILE_STRIDE (finfo, plane);
|
|
y_tiles = (padded_height + tile_height - 1) / tile_height;
|
|
info->vinfo.stride[plane] = GST_VIDEO_TILE_MAKE_STRIDE (x_tiles, y_tiles);
|
|
} else {
|
|
info->vinfo.stride[plane] = stride;
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
gst_v4l2_format_to_dma_drm_info (struct v4l2_format *fmt,
|
|
GstVideoInfoDmaDrm * out_drm_info)
|
|
{
|
|
struct v4l2_pix_format_mplane *pix_mp = &fmt->fmt.pix_mp;
|
|
struct v4l2_pix_format *pix = &fmt->fmt.pix;
|
|
gint n_planes;
|
|
gint plane;
|
|
gsize offset = 0;
|
|
gboolean extrapolate = FALSE;
|
|
GstVideoFormat format;
|
|
guint32 drm_fourcc;
|
|
guint64 drm_mod;
|
|
|
|
if (!gst_v4l2_format_to_video_format (pix_mp->pixelformat, &format) ||
|
|
!gst_v4l2_format_to_drm_format (pix_mp->pixelformat, &drm_fourcc,
|
|
&drm_mod))
|
|
return FALSE;
|
|
|
|
g_return_val_if_fail (format != GST_VIDEO_FORMAT_DMA_DRM
|
|
|| drm_fourcc != DRM_FORMAT_INVALID, FALSE);
|
|
|
|
gst_video_info_dma_drm_init (out_drm_info);
|
|
out_drm_info->vinfo.finfo = gst_video_format_get_info (format);
|
|
|
|
out_drm_info->vinfo.width = pix_mp->width;
|
|
out_drm_info->vinfo.height = pix_mp->height;
|
|
out_drm_info->drm_fourcc = drm_fourcc;
|
|
out_drm_info->drm_modifier = drm_mod;
|
|
|
|
if (V4L2_TYPE_IS_MULTIPLANAR (fmt->type)) {
|
|
out_drm_info->vinfo.size = 0;
|
|
for (plane = 0; plane < pix_mp->num_planes; plane++)
|
|
out_drm_info->vinfo.size += pix_mp->plane_fmt[plane].sizeimage;
|
|
n_planes = pix_mp->num_planes;
|
|
} else {
|
|
out_drm_info->vinfo.size = pix->sizeimage;
|
|
n_planes = 1;
|
|
}
|
|
|
|
if (drm_fourcc == DRM_FORMAT_NV12
|
|
&& drm_mod == DRM_FORMAT_MOD_BROADCOM_SAND128) {
|
|
out_drm_info->vinfo.offset[1] = pix_mp->height * 128;
|
|
out_drm_info->vinfo.stride[0] = pix_mp->plane_fmt[0].bytesperline;
|
|
out_drm_info->vinfo.stride[1] = pix_mp->plane_fmt[0].bytesperline;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* When single allocation formats are used for planar formats we need to
|
|
* extrapolate the per-plane stride. Do this check once to prevent
|
|
* complex inner loop.
|
|
*/
|
|
if (n_planes == 1 && gst_v4l2_format_get_n_planes (out_drm_info) != n_planes)
|
|
extrapolate = TRUE;
|
|
|
|
g_return_val_if_fail (format != GST_VIDEO_FORMAT_DMA_DRM
|
|
|| drm_mod == DRM_FORMAT_MOD_LINEAR || !extrapolate, FALSE);
|
|
|
|
for (plane = 0; plane < gst_v4l2_format_get_n_planes (out_drm_info); plane++) {
|
|
gint stride;
|
|
|
|
if (V4L2_TYPE_IS_MULTIPLANAR (fmt->type)) {
|
|
if (extrapolate)
|
|
stride =
|
|
gst_video_format_info_extrapolate_stride (out_drm_info->vinfo.finfo,
|
|
plane, pix_mp->plane_fmt[0].bytesperline);
|
|
else
|
|
stride = pix_mp->plane_fmt[plane].bytesperline;
|
|
} else {
|
|
if (extrapolate)
|
|
stride =
|
|
gst_video_format_info_extrapolate_stride (out_drm_info->vinfo.finfo,
|
|
plane, pix->bytesperline);
|
|
else
|
|
stride = pix->bytesperline;
|
|
}
|
|
|
|
set_stride (out_drm_info, plane, stride);
|
|
out_drm_info->vinfo.offset[plane] = offset;
|
|
|
|
if ((V4L2_TYPE_IS_MULTIPLANAR (fmt->type) && !extrapolate))
|
|
offset += pix_mp->plane_fmt[plane].sizeimage;
|
|
else
|
|
offset +=
|
|
stride *
|
|
GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (out_drm_info->vinfo.finfo, plane,
|
|
pix_mp->height);
|
|
}
|
|
|
|
/* Check that the extrapolation didn't overflow the reported sizeimage */
|
|
if (extrapolate && offset > out_drm_info->vinfo.size) {
|
|
GST_ERROR ("Extrapolated plane offset overflow the image size.");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_v4l2_format_to_video_format (guint32 pix_fmt, GstVideoFormat * out_gst_fmt)
|
|
{
|
|
const GstV4l2FormatDesc *fmt_desc;
|
|
|
|
fmt_desc = gst_v4l2_lookup_pix_format (pix_fmt);
|
|
if (!fmt_desc)
|
|
return FALSE;
|
|
|
|
if (out_gst_fmt)
|
|
*out_gst_fmt = fmt_desc->gst_fmt;
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_v4l2_format_to_drm_format (guint32 pix_fmt, guint32 * out_drm_fourcc,
|
|
guint64 * out_drm_mod)
|
|
{
|
|
const GstV4l2FormatDesc *fmt_desc;
|
|
|
|
fmt_desc = gst_v4l2_lookup_pix_format (pix_fmt);
|
|
if (!fmt_desc)
|
|
return FALSE;
|
|
|
|
if (out_drm_fourcc)
|
|
*out_drm_fourcc = fmt_desc->drm_fourcc;
|
|
if (out_drm_mod)
|
|
*out_drm_mod = fmt_desc->drm_modifier;
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_v4l2_format_from_video_format (GstVideoFormat format, guint32 * out_pix_fmt)
|
|
{
|
|
const GstV4l2FormatDesc *fmt_desc;
|
|
|
|
fmt_desc = gst_v4l2_loopup_video_format (format);
|
|
if (!fmt_desc)
|
|
return FALSE;
|
|
|
|
if (out_pix_fmt)
|
|
*out_pix_fmt = fmt_desc->v4l2_pix_fmt;
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_v4l2_format_from_drm_format (guint32 drm_fourcc, guint64 drm_mod,
|
|
guint32 * out_pix_fmt)
|
|
{
|
|
const GstV4l2FormatDesc *fmt_desc;
|
|
|
|
fmt_desc = gst_v4l2_lookup_drm_format (drm_fourcc, drm_mod);
|
|
if (!fmt_desc)
|
|
return FALSE;
|
|
|
|
if (out_pix_fmt)
|
|
*out_pix_fmt = fmt_desc->v4l2_pix_fmt;
|
|
return TRUE;
|
|
}
|
|
|
|
guint
|
|
gst_v4l2_format_get_n_planes (GstVideoInfoDmaDrm * info)
|
|
{
|
|
const GstV4l2FormatDesc *fmt_desc;
|
|
|
|
fmt_desc = gst_v4l2_loopup_video_format (info->vinfo.finfo->format);
|
|
if (fmt_desc)
|
|
return fmt_desc->num_planes;
|
|
|
|
fmt_desc = gst_v4l2_lookup_drm_format (info->drm_fourcc, info->drm_modifier);
|
|
if (fmt_desc)
|
|
return fmt_desc->num_planes;
|
|
|
|
g_warn_if_reached ();
|
|
return 0;
|
|
}
|
|
|
|
|
|
GstCaps *
|
|
gst_v4l2_format_sort_caps (GstCaps * caps)
|
|
{
|
|
const GstV4l2FormatDesc *fmt_descs = gst_v4l2_format_get_descriptions ();
|
|
GstCaps *sorted_caps = gst_caps_new_empty ();
|
|
|
|
for (int i = 0; i < GST_V4L2_FORMAT_DESC_COUNT; i++) {
|
|
if (fmt_descs[i].drm_fourcc != DRM_FORMAT_INVALID) {
|
|
guint32 drm_fourcc = fmt_descs[i].drm_fourcc;
|
|
guint64 drm_modifier = fmt_descs[i].drm_modifier;
|
|
GValue fmt = G_VALUE_INIT;
|
|
g_value_init (&fmt, G_TYPE_STRING);
|
|
g_value_take_string (&fmt,
|
|
gst_video_dma_drm_fourcc_to_string (drm_fourcc, drm_modifier));
|
|
|
|
GstStructure *dma_s = gst_structure_new ("video/x-raw",
|
|
"format", G_TYPE_STRING, "DMA_DRM", NULL);
|
|
gst_structure_take_value (dma_s, "drm-format", &fmt);
|
|
gst_caps_append_structure_full (sorted_caps, dma_s,
|
|
gst_caps_features_new_static_str (GST_CAPS_FEATURE_MEMORY_DMABUF,
|
|
NULL));
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < GST_V4L2_FORMAT_DESC_COUNT; i++) {
|
|
if (fmt_descs[i].gst_fmt != GST_VIDEO_FORMAT_UNKNOWN) {
|
|
GstStructure *s = gst_structure_new ("video/x-raw",
|
|
"format", G_TYPE_STRING,
|
|
gst_video_format_to_string (fmt_descs[i].gst_fmt), NULL);
|
|
gst_caps_append_structure (sorted_caps, s);
|
|
}
|
|
}
|
|
|
|
GstCaps *ret =
|
|
gst_caps_intersect_full (sorted_caps, caps, GST_CAPS_INTERSECT_FIRST);
|
|
gst_caps_unref (sorted_caps);
|
|
|
|
return ret;
|
|
}
|