v4l2codecs: h264: Add API checks

Check that the V4L2 H264 controls' sizes match
our expectation. If not, then probably there's an API
mismatch which will cause errors or decoding corruption.

Also, print a warning if the kernel version is too old.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1624>
This commit is contained in:
Ezequiel Garcia 2020-09-30 14:14:41 -03:00 committed by GStreamer Merge Bot
parent 7d6b06ca1b
commit 010565eb7f
3 changed files with 110 additions and 0 deletions

View file

@ -26,6 +26,12 @@
#include "gstv4l2codecpool.h"
#include "linux/v4l2-controls.h"
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
#define V4L2_MIN_KERNEL_VER_MAJOR 5
#define V4L2_MIN_KERNEL_VER_MINOR 11
#define V4L2_MIN_KERNEL_VERSION KERNEL_VERSION(V4L2_MIN_KERNEL_VER_MAJOR, V4L2_MIN_KERNEL_VER_MINOR, 0)
GST_DEBUG_CATEGORY_STATIC (v4l2_h264dec_debug);
#define GST_CAT_DEFAULT v4l2_h264dec_debug
@ -109,10 +115,62 @@ needs_start_codes (GstV4l2CodecH264Dec * self)
}
static gboolean
gst_v4l2_decoder_h264_api_check (GstV4l2CodecH264Dec * self)
{
guint i, ret_size;
/* *INDENT-OFF* */
struct
{
unsigned int id;
unsigned int size;
} controls[] = {
{
.id = V4L2_CID_MPEG_VIDEO_H264_SPS,
.size = sizeof(struct v4l2_ctrl_h264_sps),
}, {
.id = V4L2_CID_MPEG_VIDEO_H264_PPS,
.size = sizeof(struct v4l2_ctrl_h264_pps),
}, {
.id = V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX,
.size = sizeof(struct v4l2_ctrl_h264_scaling_matrix),
}, {
.id = V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS,
.size = sizeof(struct v4l2_ctrl_h264_decode_params),
}, {
.id = V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAMS,
.size = sizeof(struct v4l2_ctrl_h264_slice_params),
}, {
.id = V4L2_CID_MPEG_VIDEO_H264_PRED_WEIGHTS,
.size = sizeof(struct v4l2_ctrl_h264_pred_weights),
}
};
/* *INDENT-ON* */
/*
* Compatibility check: make sure the pointer controls are
* the right size.
*/
for (i = 0; i < G_N_ELEMENTS (controls); i++) {
if (gst_v4l2_decoder_query_control_size (self->decoder, controls[i].id,
&ret_size) && ret_size != controls[i].size) {
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE,
("H264 API mismatch!"),
("%d control size mismatch: got %d bytes but %d expected.",
controls[i].id, ret_size, controls[i].size));
return FALSE;
}
}
return TRUE;
}
static gboolean
gst_v4l2_codec_h264_dec_open (GstVideoDecoder * decoder)
{
GstV4l2CodecH264Dec *self = GST_V4L2_CODEC_H264_DEC (decoder);
guint version;
/* *INDENT-OFF* */
struct v4l2_ext_control control[] = {
{
@ -131,6 +189,20 @@ gst_v4l2_codec_h264_dec_open (GstVideoDecoder * decoder)
return FALSE;
}
version = gst_v4l2_decoder_get_version (self->decoder);
if (version < V4L2_MIN_KERNEL_VERSION)
GST_WARNING_OBJECT (self,
"V4L2 API v%u.%u too old, at least v%u.%u required",
(version >> 16) & 0xff, (version >> 8) & 0xff,
V4L2_MIN_KERNEL_VER_MAJOR, V4L2_MIN_KERNEL_VER_MINOR);
if (!gst_v4l2_decoder_h264_api_check (self)) {
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE,
("Failed to open H264 decoder"),
("gst_v4l2_decoder_h264_api_check() failed"));
return FALSE;
}
if (!gst_v4l2_decoder_get_controls (self->decoder, control,
G_N_ELEMENTS (control))) {
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE,

View file

@ -74,6 +74,7 @@ struct _GstV4l2Decoder
gint video_fd;
GstQueueArray *request_pool;
GstQueueArray *pending_requests;
guint version;
enum v4l2_buf_type src_buf_type;
enum v4l2_buf_type sink_buf_type;
@ -148,6 +149,12 @@ gst_v4l2_decoder_new (GstV4l2CodecDevice * device)
return gst_object_ref_sink (decoder);
}
guint
gst_v4l2_decoder_get_version (GstV4l2Decoder * self)
{
return self->version;
}
gboolean
gst_v4l2_decoder_open (GstV4l2Decoder * self)
{
@ -176,6 +183,8 @@ gst_v4l2_decoder_open (GstV4l2Decoder * self)
return FALSE;
}
self->version = querycap.version;
if (querycap.capabilities & V4L2_CAP_DEVICE_CAPS)
capabilities = querycap.device_caps;
else
@ -717,6 +726,29 @@ gst_v4l2_decoder_get_controls (GstV4l2Decoder * self,
return TRUE;
}
gboolean
gst_v4l2_decoder_query_control_size (GstV4l2Decoder * self,
unsigned int control_id, unsigned int *control_size)
{
gint ret;
struct v4l2_query_ext_ctrl control = {
.id = control_id,
};
*control_size = 0;
ret = ioctl (self->video_fd, VIDIOC_QUERY_EXT_CTRL, &control);
if (ret < 0)
/*
* It's not an error if a control is not supported by this driver.
* Return false but don't print any error.
*/
return FALSE;
*control_size = control.elem_size;
return TRUE;
}
void
gst_v4l2_decoder_install_properties (GObjectClass * gobject_class,
gint prop_offset, GstV4l2CodecDevice * device)

View file

@ -35,6 +35,8 @@ typedef struct _GstV4l2Request GstV4l2Request;
GstV4l2Decoder * gst_v4l2_decoder_new (GstV4l2CodecDevice * device);
guint gst_v4l2_decoder_get_version (GstV4l2Decoder * self);
gboolean gst_v4l2_decoder_open (GstV4l2Decoder * decoder);
gboolean gst_v4l2_decoder_close (GstV4l2Decoder * decoder);
@ -81,6 +83,10 @@ gboolean gst_v4l2_decoder_get_controls (GstV4l2Decoder * self,
struct v4l2_ext_control * control,
guint count);
gboolean gst_v4l2_decoder_query_control_size (GstV4l2Decoder * self,
unsigned int control_id,
unsigned int *control_size);
void gst_v4l2_decoder_install_properties (GObjectClass * gobject_class,
gint prop_offset,
GstV4l2CodecDevice * device);