由于这个文件中会调用到GStreamer 1.0 Core Reference Manual中的很多函数,但是如果将这些函数的分析放在代码分析中的话,就会严重影响可读性,于是将这些函数的讲解都放在《gstreamer插件所用函数整理》这个文件中,这两个文件中的标号都是相同的,如果遇到不理解的函数,可以去那个文件中搜索来看。
(一)属性相关的设置
关于这一节,它对应《插件开发手册:Chapter 9.Adding Properties》这一节,可以去查看。
首先看看有关插件属性的代码:
在gst_imx_v4l2src_class_init函数中,首先重载了GObjectClass的set_property和get_property函数,然后通过gst_imx_v4l2src_install_properties函数来设置相关的插件属性。
在gstimxv4l2src.c中,首先有一个标识所有插件属性的枚举值:
enum {
PROP_0,
PROP_DEVICE,
PROP_USE_V4L2SRC_MEMORY,
PROP_FRAME_PLUS,
};
之后的所有设置就是与这几个枚举值所相关的。
1.1 gst_imx_v4l2src_get_property
static void
gst_imx_v4l2src_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
{
GstImxV4l2Src *v4l2src = GST_IMX_V4L2SRC (object);
switch (prop_id) {
case PROP_DEVICE:
g_value_set_string (value, v4l2src->device);
break;
case PROP_USE_V4L2SRC_MEMORY:
g_value_set_boolean (value, v4l2src->use_v4l2_memory);
break;
case PROP_FRAME_PLUS:
g_value_set_uint (value, v4l2src->frame_plus);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
这个函数中分别使用了g_value_set_string, g_value_set_boolean, g_value_set_uint三个函数,根据传入的prop_id分别将 v4l2src->device, v4l2src->use_v4l2_memory, v4l2src->frame_plus 的值赋给value,而从gstimxv4l2src.h头文件中可以看出来,v4l2src中的device, use_v4l2_memory, freame_plus分别是gchar, gboolean, guint类型的,所以分别使用 g_value_set_boolean, g_value_set_uint这三个函数了来赋值,有关这三个函数的介绍,查看《gstreamer插件所用函数整理》文件。
1.2 gst_imx_v4l2src_set_property
static void
gst_imx_v4l2src_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
{
GstImxV4l2Src *v4l2src = GST_IMX_V4L2SRC (object);
switch (prop_id) {
case PROP_DEVICE:
g_free (v4l2src->device);
v4l2src->device = g_value_dup_string (value);
break;
case PROP_USE_V4L2SRC_MEMORY:
v4l2src->use_v4l2_memory = g_value_get_boolean (value);
break;
case PROP_FRAME_PLUS:
v4l2src->frame_plus = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
这个函数与上一个类似,只不过这个函数的意思是为v4l2src结构体中的device,use_v4l2_memory, frame_plus几个参数赋值,分别使用g_value_dup_string, g_value_get_boolean, g_value_get_uint这三个函数来将value的根据不同的case为不同的变量赋值。
注意上面两个函数只是重载gobject_class中的set_property和get_property函数,这两个函数需要根据这里不同的属性值来进行修改。
1.3 gst_imx_v4l2src_install_properties
static void
gst_imx_v4l2src_install_properties (GObjectClass *gobject_class)
{
g_object_class_install_property (gobject_class, PROP_DEVICE,
g_param_spec_string ("device", "Device", "Device location",
DEFAULT_DEVICE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_USE_V4L2SRC_MEMORY,
g_param_spec_boolean ("use-v4l2src-memory", "Force use V4L2 src memory",
"Force allocate video frame buffer by V4L2 capture",
DEFAULT_USE_V4L2SRC_MEMORY, G_PARAM_READWRITE |G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_FRAME_PLUS,
g_param_spec_uint ("frame-plus", "addtionlal frames",
"set number of addtional frames for smoothly recording",
0, 16, DEFAULT_FRAME_PLUS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
return;
}
这个函数只是进行了一层封装,重点是里面的g_object_class_install_property函数,这个函数同样在《gstreamer插件所用函数整理》文件中介绍了,需要注意的是,需要在_class_init函数中完成属性的注册安装(install):
void g_object_class_install_property (GObjectClass *oclass,
guint property_id, GParamSpec *pspec);
注意这个函数只有三个参数,第一个参数是属性所对应的类,a GObjectClass,所以都是gobject_class。 第二个参数是property_id,在这里就是我们之前创建的插件属性枚举值中的每一项,对于枚举值中的每一项,都需要使用一个g_object_class_install_property函数来注册,从上面我们也可以看出来,使用了三个g_object_class_install_property函数函数来分别注册PROP_DEVICE, PROP_USE_V4L2SRC_MEMORY, PROP_FRAME_PLUS这三个属性。最后一个参数,是GParamSpec类型的,在这里,使用的是g_param_spec_string, g_param_spec_boolean, g_param_spec_uint这三个函数来生成的GParamSpec类型的值。有关这三个函数的详细介绍,同样在《gstreamer插件所用函数整理》文件中。
至此,就分析完插件中有关插件属性的初始化设置过程。
(二)衬垫(Pad)相关的设置
关于这一节,它对应《插件开发手册:Chapter 3.Constructing the Boilerplate》这一节中的3.5GstStaticPadTemplate,可以去查看。
作为一个src插件,它只有src衬垫,所以,这个插件中只需要添加一个src衬垫即可。添加衬垫使用gst_element_class_add_pad_template函数,函数原型如下,函数介绍看《gstreamer插件所用函数整理》:
void
gst_element_class_add_pad_template (GstElementClass *klass,
GstPadTemplate *templ);
第一个参数是对应的element_class,第二个参数是一个GstPadTemplate类型的变量,这个变量可以直接从src_factory/sink_factory复制(gst_static_pad_template_get),或者直接创建一个(gst_pad_template_new)。
GstPadTemplate *
gst_static_pad_template_get (GstStaticPadTemplate *pad_template);
看看插件手册上面的例子:
gst_element_class_add_pad_template(element_class,
gst_static_pad_template_get(&src_factory));
gst_element_class_add_pad_template(element_class,
gst_static_pad_template_get(&sink_factory));
通过这种方法比较简单,但是只能生成默认的pad,对于我们需要自己定制的pad的话,肯定需要使用 gst_pad_template_new函数来自己生成。
函数原型:
GstPadTemplate *
gst_pad_template_new (const gchar *name_template,
GstPadDirection direction,
GstPadPresence presence,
GstCaps *caps);
实际代码:
gst_element_class_add_pad_template (element_class, \
gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, \
gst_imx_v4l2src_get_all_caps ()));
重点看这个gst_pad_template_new函数,根据《gstreamer插件所用函数整理》,可以设置好这个函数的前三个参数,最后一个参数只能确定是(GstCaps *)类型的,而且这个参数最终需要调用GstCaps相关的函数来完成(有关caps协商的知识,在《插件编写手册的 Charpter 14. Caps negotiation》),在这只是简单确定一下它的设置流程:
gst_imx_v4l2src_get_all_caps()--->gst_imx_v4l2_get_device_caps()--->gst_caps_new_empty()。
这三个函数的返回值都是(GstCaps*)类型的,最终的gst_caps_new_empty函数就是GstCaps相关的函数,我们以后再分析它们。
从这里我们也可以看出来,gst_imx_v4l2src_get_all_caps()这个函数只是对GstCaps相关的函数进行了定制封装。
(三)元数据(metadata)相关的设置
在class_init函数中,需要设置插件的元数据,使用gst_element_class_set_static_metadata函数,这个函数比较简单,就直接看《gstreamer插件所用函数整理》中的介绍吧。
(四)GST_DEBUG_FUNCPTR()
这个宏的目的就是就是将函数指针封装一下,为什么这么用?这种用法主要是用于debug模式.具体的分析可以查看《插件编写指南 Chapter 27. Things to check when writing an element》中27.1.Debugging这一节,使用这种方法的目的是为了方便调试。
(五)下一步就是重载GstBaseSrcClass结构体中的方法,有关这方面的介绍在:
https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer-libs/html/GstBaseSrc.html#GstBaseSrc-struct
有关GstBaseSrcClass结构体和其中的相关方法的介绍在《gstreamer插件所用函数整理》中。
下面就来一步一步分析这些方法:
5.1 gst_imx_v4l2src_start()
static gboolean
gst_imx_v4l2src_start (GstBaseSrc * src)
{
GstImxV4l2Src *v4l2src = GST_IMX_V4L2SRC (src);
GST_INFO_OBJECT (v4l2src, "open device: %s", v4l2src->device);
v4l2src->v4l2handle = gst_imx_v4l2_open_device (v4l2src->device, \
V4L2_BUF_TYPE_VIDEO_CAPTURE);
if (!v4l2src->v4l2handle) {
return FALSE;
}
return TRUE;
}
GST_IMX_V4L2SRC是在头文件中定义的,这个宏能从传入的GstBaseSrc类型的结构体转换成相应的GStImxV4l2Src结构体。重点的函数就是gst_imx_v4l2_open_device,这个函数在gst1.0-fsl-plugins-4.0.8/libs/v4l2_core/gstimxv4l2.c文件中定义,如下所示:
gpointer gst_imx_v4l2_open_device (gchar *device, int type)
{
int fd;
struct v4l2_capability cap;
IMXV4l2Handle *handle = NULL;
GST_DEBUG_CATEGORY_INIT (imxv4l2_debug, "imxv4l2", 0, "IMX V4L2 Core");
GST_INFO ("device name: %s", device);
if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
fd = open(device, O_RDWR, 0);
} else {
fd = open(device, O_RDWR | O_NONBLOCK, 0);
}
if (fd < 0) {
GST_DEBUG ("Can't open %s.\n", device);
return NULL;
}
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
GST_ERROR ("VIDIOC_QUERYCAP error.");
close (fd);
return NULL;
}
if (!(cap.capabilities & type)) {
GST_DEBUG ("device can't capture.");
close (fd);
return NULL;
}
handle = (IMXV4l2Handle*) g_slice_alloc (sizeof(IMXV4l2Handle));
if (!handle) {
GST_ERROR ("allocate for IMXV4l2Handle failed.\n");
close (fd);
return NULL;
}
memset (handle, 0, sizeof(IMXV4l2Handle));
handle->v4l2_fd = fd;
handle->device = device;
handle->type = type;
handle->streamon = FALSE;
handle->v4l2_hold_buf_num = V4L2_HOLDED_BUFFERS;
if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
if (HAS_IPU()) {
handle->dev_itf.v4l2out_config_input = (V4l2outConfigInput)imx_ipu_v4l2out_config_input;
handle->dev_itf.v4l2out_config_output = (V4l2outConfigOutput)imx_ipu_v4l2out_config_output;
handle->dev_itf.v4l2out_config_rotate = (V4l2outConfigRotate)imx_ipu_v4l2out_config_rotate;
handle->dev_itf.v4l2out_config_alpha = (V4l2outConfigAlpha) imx_ipu_v4l2_config_alpha;
handle->dev_itf.v4l2out_config_colorkey = (V4l2outConfigColorkey) imx_ipu_v4l2_config_colorkey;
handle->streamon_count = MX6Q_STREAMON_COUNT;
}
else if (HAS_PXP()) {
handle->dev_itf.v4l2out_config_input = (V4l2outConfigInput)imx_pxp_v4l2out_config_input;
handle->dev_itf.v4l2out_config_output = (V4l2outConfigOutput)imx_pxp_v4l2out_config_output;
handle->dev_itf.v4l2out_config_rotate = (V4l2outConfigRotate)imx_pxp_v4l2out_config_rotate;
handle->dev_itf.v4l2out_config_alpha = (V4l2outConfigAlpha) imx_pxp_v4l2_config_alpha;
handle->dev_itf.v4l2out_config_colorkey = (V4l2outConfigColorkey) imx_pxp_v4l2_config_colorkey;
handle->streamon_count = MX60_STREAMON_COUNT;
}
gst_imx_v4l2output_set_default_res (handle);
}
if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
if (gst_imx_v4l2capture_set_function (handle) < 0) {
GST_ERROR ("v4l2 capture set function failed.\n");
close (fd);
return NULL;
}
handle->streamon_count = 2;
}
return (gpointer) handle;
}
首先来看这个函数的参数,第一个参数:v4l2src->device,这个参数是在gst_imx_v4l2src_init函数中,通过
v4l2src->device = g_strdup (DEFAULT_DEVICE);
#define DEFAULT_DEVICE "/dev/video0"
来为这个参数赋值的,它即为/dev/video0。
第二个参数:V4L2_BUF_TYPE_VIDEO_CAPTURE, 是一个type类型。这个函数中会根据这个type类型来选择执行不同的语句。
这个函数中,重点是IMXV4l2Handle类型的结构体。
typedef struct {
gchar *device;
gint type;
int v4l2_fd;
gint disp_w;
gint disp_h;
gint device_map_id;
gboolean streamon;
gint invisible;
gint streamon_count;
gint queued_count;
guint v4l2_hold_buf_num;
guint in_fmt;
gint in_w;
gint in_h;
IMXV4l2Rect in_crop;
gboolean do_deinterlace;
gint buffer_count;
guint memory_mode;
gint allocated;
IMXV4l2BufferPair buffer_pair[MAX_BUFFER];
gint rotate;
guint *support_format_table;
gboolean is_tvin;
IMXV4l2DeviceItf dev_itf;
struct v4l2_buffer * v4lbuf_queued_before_streamon[MAX_BUFFER];
v4l2_std_id id;
gboolean prev_need_crop;
guint alpha;
guint color_key;
IMXV4l2Rect overlay;
gboolean pending_close;
gboolean invalid_paddr;
} IMXV4l2Handle
typedef struct {
V4l2outConfigInput v4l2out_config_input;
V4l2outConfigOutput v4l2out_config_output;
V4l2outConfigRotate v4l2out_config_rotate;
V4l2outConfigAlpha v4l2out_config_alpha;
V4l2outConfigColorkey v4l2out_config_colorkey;
V4l2captureConfig v4l2capture_config;
} IMXV4l2DeviceItf
typedef gint (*V4l2outConfigInput) (void *handle, guint fmt, guint w, guint h, \
IMXV4l2Rect *crop);
typedef gint (*V4l2outConfigOutput) (void *handle, struct v4l2_crop *crop);
typedef gint (*V4l2outConfigRotate) (void *handle, gint rotate);
typedef gint (*V4l2outConfigAlpha) (void *handle, guint alpha);
typedef gint (*V4l2outConfigColorkey) (void *handle, gboolean enable, guint color_key);
typedef gint (*V4l2captureConfig) (void *handle, guint fmt, guint w, guint h, \
guint fps_n, guint fps_d);
依次追踪这几个结构体,会发现最终IMXV4L2DeviceItf里面保存的是6个函数指针。
那么再来看gst_imx_v4l2_open_device这个函数,会根据type类型的不同,使用open函数来打开不同的设备。打开设备后使用ioctl(fd,VIDIOC_QUERYCAP, &cap)来查询这个设备的Capbility。
之后就是上面所说的IMXV4l2Handle类型的结构体指针,首先通过g_slice_alloc函数来为它分配内存(这个函数在《gstreamer插件所用函数整理》中),之后通过memset函数将分配的这一块内存清空。
之后继续初始化这个结构体中的元素。这时候如果type类型是V4L2_BUF_TYPE_VIDEO_OUTPUT的话,就会设置handle->dev_itf里面的几个函数指针。但是,因为我们的type类型为V4L2_BUF_TYPE_VIDEO_CAPTURE,就调用到gst_imx_v4l2capture_set_function函数进行设置。下面就来看gst_imx_v4l2capture_set_function函数(从这个函数的名字上面来看,为v4l2capture设备设置函数指针):
static gint
gst_imx_v4l2capture_set_function (IMXV4l2Handle *handle)
{
struct v4l2_capability cap;
if (ioctl(handle->v4l2_fd, VIDIOC_QUERYCAP, &cap) < 0) {
GST_ERROR ("VIDIOC_QUERYCAP error.");
return -1;
}
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
GST_ERROR ("device can't capture.");
return -1;
}
handle->is_tvin = FALSE; //首先设置这个gboolean变量为FALSE。这个标志位标志这个设备是否是tvin设备。
if (!strcmp (cap.driver, MXC_V4L2_CAPTURE_NAME)) { // MXC_V4L2_CAPTURE_NAME为"mxc_v4l2",会根据名字来选择执行不同的分支,如果名字为"mxc_v4l2"的话,就会执行下面的语句。
struct v4l2_dbg_chip_ident chip;
if (ioctl(handle->v4l2_fd, VIDIOC_DBG_G_CHIP_IDENT, &chip)) {
GST_ERROR ("VIDIOC_DBG_G_CHIP_IDENT failed.\n");
return -1;
}
GST_INFO ("sensor chip is %s\n", chip.match.name);
if (!strncmp (chip.match.name, MXC_V4L2_CAPTURE_CAMERA_NAME, 2)) { //继续判断设备是否是ov设备。
handle->dev_itf.v4l2capture_config = (V4l2captureConfig)gst_imx_v4l2capture_config_camera;
handle->support_format_table = g_camera_format_IPU;
}
else if (!strncmp (chip.match.name, MXC_V4L2_CAPTURE_TVIN_NAME, 3)) {
//如果不为ov设备,继续判断设备是否是adv设备。
handle->dev_itf.v4l2capture_config = (V4l2captureConfig)gst_imx_v4l2capture_config_camera;
handle->support_format_table = g_camera_format_IPU;
handle->is_tvin = TRUE;
if (gst_imx_v4l2capture_config_tvin_std (handle)) {
GST_ERROR ("can't set TV-In STD.\n");
return -1;
}
} else {
GST_ERROR ("can't identify capture sensor type.\n");
return -1;
}
}
//如果名字不为"mxc_v4l2"的话,就会执行下面的语句,继续判断设备是否是csi_v4l2设备。
else if (!strcmp (cap.driver, PXP_V4L2_CAPTURE_NAME)) {
struct v4l2_dbg_chip_ident chip;
if (ioctl(handle->v4l2_fd, VIDIOC_DBG_G_CHIP_IDENT, &chip)) {
GST_ERROR ("VIDIOC_DBG_G_CHIP_IDENT failed.\n");
return -1;
}
GST_INFO ("sensor chip is %s\n", chip.match.name);
if (!strncmp (chip.match.name, MXC_V4L2_CAPTURE_CAMERA_NAME, 2)) {
handle->dev_itf.v4l2capture_config = (V4l2captureConfig)gst_imx_v4l2capture_config_pxp;
handle->support_format_table = g_camera_format_PXP;
} else if (!strncmp (chip.match.name, MXC_V4L2_CAPTURE_TVIN_VADC_NAME, 3)) {
handle->dev_itf.v4l2capture_config = (V4l2captureConfig)gst_imx_v4l2capture_config_camera;
handle->support_format_table = g_camera_format_PXP;
handle->is_tvin = TRUE;
if (gst_imx_v4l2capture_config_tvin_std (handle)) {
GST_ERROR ("can't set TV-In STD.\n");
return -1;
}
} else {
GST_ERROR ("can't identify capture sensor type.\n");
return -1;
}
} else {
handle->dev_itf.v4l2capture_config = (V4l2captureConfig)gst_imx_v4l2capture_config_usb_camera;
handle->support_format_table = NULL;
}
return 0;
}
看这个函数,它的目的很简单,就是根据获取到的不同的设备类型来分别设置IMXV4l2Handle结构体中的dev_itf.v4l2capture_config函数指针和support_format_table函数指针。同时,如果是tvin设备的话,就设置is_tvin这个bool变量。
小总结:
下面再退出到gst_imx_v4l2_open_device函数,这个函数分别完成了打开设备(open),查询能力(ioctl, capability),然后为IMXV4l2Handle结构体指针分配内存,然后初始化IMXV4l2Handle结构体里面的某些值(最重要的是dev_itf的函数指针)。同时,需要注意到,gst_imx_v4l2_open_device函数的返回值,就是将这个函数里面设置的IMXV4l2Handle *handle返回给外层的函数,即gst_imx_v4l2src_start函数。
再退出到gst_imx_v4l2src_start函数中,这个函数的核心就是gst_imx_v4l2_open_device函数,然后在这个函数中,是这样调用gst_imx_v4l2_open_device的:
v4l2src->v4l2handle = gst_imx_v4l2_open_device (v4l2src->device, \
V4L2_BUF_TYPE_VIDEO_CAPTURE);
虽然上面函数中都是设置的IMXV4l2Handle *handle,但是,最外层的结构体仍然是在gstimxv4l2src.h中定义的GstImxV4l2Src结构体。所以,我们分析必须围绕GstImxV4l2Src和GstImxV4l2SrcClass来展开。这两个结构体也是之前的文章中提到的用这两个结构体来模仿C++里面的继承的基础。
5.2 gst_imx_v4l2src_stop函数
这个函数将是最后调用的函数,它会将之前打开设置的东西全部都关闭释放了,但是现在还有很多东西没有设置,所以这个函数在最后分析。
5.3 gst_imx_v4l2src_get_caps函数
static GstCaps *
gst_imx_v4l2src_get_caps (GstBaseSrc * src, GstCaps * filter)
{
GstCaps *caps = NULL;
caps = gst_imx_v4l2src_get_device_caps (src);
if (caps && filter) {
GstCaps *intersection;
intersection =
gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
gst_caps_unref (caps);
caps = intersection;
}
return caps;
}
重点是gst_imx_v4l2src_get_device_caps函数和gst_caps_intersect_full,继续追踪,gst_imx_v4l2src_get_device_caps函数如下:
static GstCaps *
gst_imx_v4l2src_get_device_caps (GstBaseSrc * src)
{
GstImxV4l2Src *v4l2src;
GstCaps *caps = NULL;
v4l2src = GST_IMX_V4L2SRC (src);
if (v4l2src->v4l2handle == NULL) {
return gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (v4l2src));
}
/*如果没有设置 v4l2src->v4l2handle就调用下面的函数,v4l2src->v4l2handle在*gst_imx_v4l2src_start函数中设置的。gst_pad_get_pad_template_caps 函数在《gstreamer *插件所用函数整理》中介绍。
if (v4l2src->probed_caps)
return gst_caps_ref (v4l2src->probed_caps);
//如果已经设置了caps,就直接通过gst_caps_ref增加引用计数。
caps = gst_imx_v4l2_get_caps (v4l2src->v4l2handle);
if(!caps) {
GST_WARNING_OBJECT (v4l2src, "Can't get caps from device.");
}
//这个函数是重点,通过这个函数来创建caps并设置它们。
v4l2src->probed_caps = gst_caps_ref (caps);
GST_INFO_OBJECT (v4l2src, "probed caps: %" GST_PTR_FORMAT, caps);
return caps;
}
gst_imx_v4l2_get_caps函数如下:
GstCaps *
gst_imx_v4l2_get_caps (gpointer v4l2handle)
{
struct v4l2_fmtdesc fmt;
struct v4l2_frmsizeenum frmsize;
struct v4l2_frmivalenum frmival;
gint i, index, vformat;
GstCaps *caps = NULL;
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
if (handle->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
fmt.index = 0;
fmt.type = handle->type;
//FIXME: driver should report v4l2 capture output format. not camera sensor
//support format.
if (handle->support_format_table) {
while (handle->support_format_table[fmt.index]) {
fmt.pixelformat = handle->support_format_table[fmt.index];
vformat = fmt.pixelformat;
GST_INFO ("frame format: %c%c%c%c", vformat & 0xff, (vformat >> 8) & 0xff,
(vformat >> 16) & 0xff, (vformat >> 24) & 0xff);
frmsize.pixel_format = fmt.pixelformat;
frmsize.index = 0;
while (ioctl(handle->v4l2_fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) >= 0) {
GST_INFO ("frame size: %dx%d", frmsize.discrete.width, frmsize.discrete.height);
GST_INFO ("frame size type: %d", frmsize.type);
//FIXME: driver haven't set type.
if (1) {//frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
frmival.index = 0;
frmival.pixel_format = fmt.pixelformat;
frmival.width = frmsize.discrete.width;
frmival.height = frmsize.discrete.height;
while (ioctl(handle->v4l2_fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) >= 0) {
GST_INFO ("frame rate: %d/%d", frmival.discrete.denominator, frmival.discrete.numerator);
// Add hard code format.
index = 0;
while (handle->support_format_table[index]) {
guint map_size;
IMXV4l2FmtMap *fmt_map = imx_v4l2_get_fmt_map(&map_size);
for (i=0; isupport_format_table[index] == fmt_map[i].v4l2fmt) {
if (!caps)
caps = gst_caps_new_empty ();
if (caps) {
GstStructure * structure = gst_structure_from_string( \
fmt_map[i].caps_str, NULL);
gst_structure_set (structure, "width", G_TYPE_INT, frmsize.discrete.width, NULL);
gst_structure_set (structure, "height", G_TYPE_INT, frmsize.discrete.height, NULL);
gst_structure_set (structure, "framerate", GST_TYPE_FRACTION, \
frmival.discrete.denominator, frmival.discrete.numerator, NULL);
if (handle->is_tvin)
gst_structure_set (structure, "interlace-mode", G_TYPE_STRING, "interleaved", NULL);
gst_caps_append_structure (caps, structure);
GST_INFO ("Added one caps\n");
}
}
}
index ++;
}
frmival.index++;
}
}
frmsize.index++;
}
fmt.index++;
}
} else {
while (ioctl(handle->v4l2_fd, VIDIOC_ENUM_FMT, &fmt) >= 0) {
vformat = fmt.pixelformat;
GST_INFO ("frame format: %c%c%c%c", vformat & 0xff, (vformat >> 8) & 0xff,
(vformat >> 16) & 0xff, (vformat >> 24) & 0xff);
frmsize.pixel_format = fmt.pixelformat;
frmsize.index = 0;
while (ioctl(handle->v4l2_fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) >= 0) {
GST_INFO ("frame size: %dx%d", frmsize.discrete.width, frmsize.discrete.height);
GST_INFO ("frame size type: %d", frmsize.type);
if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
frmival.index = 0;
frmival.pixel_format = fmt.pixelformat;
frmival.width = frmsize.discrete.width;
frmival.height = frmsize.discrete.height;
while (ioctl(handle->v4l2_fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) >= 0) {
GST_INFO ("frame rate: %d/%d", frmival.discrete.denominator, frmival.discrete.numerator);
guint map_size;
IMXV4l2FmtMap *fmt_map = imx_v4l2_get_fmt_map(&map_size);
for (i=0; igst_caps_new_empty ();
if (caps) {
GstStructure * structure = gst_structure_from_string( \
fmt_map[i].caps_str, NULL);
gst_structure_set (structure, "width", G_TYPE_INT, frmsize.discrete.width, NULL);
gst_structure_set (structure, "height", G_TYPE_INT, frmsize.discrete.height, NULL);
gst_structure_set (structure, "framerate", GST_TYPE_FRACTION, \
frmival.discrete.denominator, frmival.discrete.numerator, NULL);
gst_caps_append_structure (caps, structure);
GST_INFO ("Added one caps\n");
}
}
}
frmival.index++;
}
}
frmsize.index++;
}
fmt.index++;
}
}
}
if (caps) {
return gst_caps_simplify(caps);
} else {
return NULL;
}
}
上面这个函数比较大,嵌套的也比较多,大致意思是这样的:
1)首先知道插件的caps的作用是什么?这个问题可以看插件开发手册,主要是协商这些caps的格式等问题,所以,首先需要根据handle->support_format_table里面支持的格式来协商这些caps所支持的格式。
2)协商好以后,就通过gst_caps_new_empty ();函数来新建一个cap。这个函数在《gstreamer插件所用函数整理》中介绍。
3)新建好cap以后,肯定需要将协商好的格式写到新建这个cap里面啊,所以首先使用gst_structure_set函数,来将这些格式写到一个结构体里面,然后再通过gst_caps_append_structure函数,来将保存格式的这个结构体附加到cap上面。这两个同样在《gstreamer插件所用函数整理》中介绍。
4)设置好cap后,调用gst_caps_simplify函数来简化这个cap。
小总结:
通过这个gst_imx_v4l2_get_caps函数,就设置好了src插件的caps属性。
继续返回gst_imx_v4l2src_get_device_caps函数中,从新新建设置好caps后,就通过v4l2src->probed_caps = gst_caps_ref (caps);函数,来增加caps的引用计数。最后返回设置好的这个caps结构体。
继续返回到gst_imx_v4l2src_get_caps函数中,继续执行:
if (caps && filter) {
GstCaps *intersection;
intersection =
gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
gst_caps_unref (caps);
caps = intersection;
}
这个filter是函数的行参,static GstCaps *gst_imx_v4l2src_get_caps (GstBaseSrc * src, GstCaps * filter),然后如果caps和filter都存在的话,就会执行下面的语句,重点是gst_caps_intersect_full函数,这个函数在《gstreamer插件所用函数整理》中介绍。意思是创建一个新caps,这个新caps的属性是这两者共有的属性。这也就是第二个参数称为filter的原因,简单来说,这个函数就是求两者的交集。
然后将新产生的intersection赋给caps,减少caps的引用计数。
小总结:
gst_imx_v4l2src_get_caps这个函数的作用就是产生一个新的caps,并设置这个caps。如果这个函数同时传入了filter参数的话,就继续调用gst_caps_intersect_full函数来产生这两者的交集,最终还是返回caps。
5.4 gst_imx_v4l2src_fixate函数
static GstCaps *
gst_imx_v4l2src_fixate (GstBaseSrc * bsrc, GstCaps * caps)
{
caps = GST_BASE_SRC_CLASS (parent_class)->fixate (bsrc, caps);
return caps;
}
这个函数的实现就比较简单了,就是直接继承了它的父类的fixate函数。它的父类是GstBaseSrcClass,在这个带Class的结构体里面,就包含这个类的方法。从《gstreamer插件所用函数整理》中搜索,可以找到这个方法的定义。
5.5 gst_imx_v4l2src_set_caps函数
static gboolean
gst_imx_v4l2src_set_caps (GstBaseSrc * src, GstCaps * caps)
{
GstImxV4l2Src *v4l2src;
GstVideoInfo info;
guint v4l2fmt;
v4l2src = GST_IMX_V4L2SRC (src);
if (v4l2src->old_caps) {
if (gst_caps_is_equal (v4l2src->old_caps, caps))
return TRUE;
}
//如果 v4l2src->old_caps存在的话,同时old_caps与要设置的caps相同,就直接不用设置了,直接返回TRUE即可。这个 v4l2src->old_caps在这个函数的后面会设置。
if (!gst_video_info_from_caps (&info, caps)) {
GST_ERROR_OBJECT (v4l2src, "invalid caps.");
return FALSE;
}
//这个函数用于解析caps并将解析出来的值设置到info中。
GST_DEBUG_OBJECT (v4l2src, "set caps %" GST_PTR_FORMAT, caps);
v4l2fmt = gst_imx_v4l2_fmt_gst2v4l2 (GST_VIDEO_INFO_FORMAT (&info));
if (!v4l2fmt) {
v4l2fmt = gst_imx_v4l2_special_fmt (caps);
}
//将GST类型的format标志符转换成v4l2格式的format标志符。
v4l2src->v4l2fmt = v4l2fmt;
v4l2src->w = GST_VIDEO_INFO_WIDTH (&info);
v4l2src->h = GST_VIDEO_INFO_HEIGHT (&info);
v4l2src->fps_n = GST_VIDEO_INFO_FPS_N (&info);
v4l2src->fps_d = GST_VIDEO_INFO_FPS_D (&info);
//从更新后的info结构体里面获取width,height,fps_n,fps_d等值,保存到v4l2src结构体中。
if (v4l2src->fps_n <= 0 || v4l2src->fps_d <= 0) {
GST_ERROR_OBJECT (v4l2src, "invalid fps.");
return FALSE;
}
v4l2src->duration = gst_util_uint64_scale_int (GST_SECOND, v4l2src->fps_d, \
v4l2src->fps_n);
//根据fps_n,fps_d的值计算出duration时间。
if (!gst_imx_v4l2src_reset(v4l2src)) {
GST_ERROR_OBJECT (v4l2src, "gst_imx_v4l2src_reset failed.");
return FALSE;
}
//根据v4l2src结构体里面保存的值,重置v4l2src插件。因为,重新设置了caps属性,这时候肯定需要重置v4l2src插件,使用新的caps来工作。
if (v4l2src->old_caps) {
gst_caps_unref (v4l2src->old_caps);
v4l2src->old_caps = NULL;
}
v4l2src->old_caps = gst_caps_copy (caps);
//最后,将设置好的caps保存到v4l2src->old_caps里面,作为一个备份,如果下一次设置caps的时候,就能在这个函数上面的部分进行判断了。
return TRUE;
}
这个函数中有两个核心结构体,GstImxV4l2Src和GstVideoInfo。其中GstImxV4l2Src结构体是在gstimxv4l2src.h中定义的核心结构体,GstVideoInfo是glibc提供的结构体,有关这个结构体的详细介绍在《gstreamer插件所用函数整理》中,下面粘贴出这个结构体里面的元素:
struct GstVideoInfo {
const GstVideoFormatInfo *finfo;
GstVideoInterlaceMode interlace_mode;
GstVideoFlags flags;
gint width;
gint height;
gsize size;
gint views;
GstVideoChromaSite chroma_site;
GstVideoColorimetry colorimetry;
gint par_n;
gint par_d;
gint fps_n;
gint fps_d;
gsize offset[GST_VIDEO_MAX_PLANES];
gint stride[GST_VIDEO_MAX_PLANES];
/* Union preserves padded struct size for backwards compat
* Consumer code should use the accessor macros for fields */
union {
struct {
GstVideoMultiviewMode multiview_mode;
GstVideoMultiviewFlags multiview_flags;
} abi;
};
函数流程:
之后就会通过gst_video_info_from_caps函数来解析要设置的caps,将解析出来的参数存放到 GstVideoInfo info结构体中,然后继续解析caps里面获得的format参数,通过gst_imx_v4l2_fmt_gst2v4l2函数来转换成v4l2格式的format格式。
之后继续从info结构体中获取width,height,fps_n, fps_d等参数,需要使用到GST_VIDEO_INFO_WIDTH,GST_VIDEO_INFO_FPS_N等参数,这几个参数同样在《gstreamer插件所用函数整理》中详细介绍。
获取到这些参数后,通过gst_util_uint64_scale_int函数来计算duration参数。
上面这么多步骤的目的就是为了填充v4l2src结构体,因为这个结构体是插件的核心结构体。
最后通过gst_imx_v4l2src_reset函数来重置插件,因为采用了新的caps,所以肯定需要重置一下插件。
将现在所使用的caps作为old_caps通过gst_caps_copy函数保存到v4l2src中,如果需要重新设置caps的时候,用来比较新旧caps是否相同。
下面来详细分析gst_imx_v4l2_fmt_gst2v4l2函数:
v4l2fmt = gst_imx_v4l2_fmt_gst2v4l2 (GST_VIDEO_INFO_FORMAT (&info));
guint
gst_imx_v4l2_fmt_gst2v4l2 (GstVideoFormat gstfmt)
{
guint v4l2fmt = 0;
int i;
guint map_size;
IMXV4l2FmtMap *fmt_map = imx_v4l2_get_fmt_map(&map_size);
for(i=0; iimx_v4l2_get_fmt_map(guint *map_size)
{
IMXV4l2FmtMap *fmt_map = NULL;
*map_size = 0;
if (HAS_IPU()) {
fmt_map = g_imxv4l2fmt_maps_IPU;
*map_size = sizeof(g_imxv4l2fmt_maps_IPU)/sizeof(IMXV4l2FmtMap);
} else if (HAS_PXP()){
fmt_map = g_imxv4l2fmt_maps_PXP;
*map_size = sizeof(g_imxv4l2fmt_maps_PXP)/sizeof(IMXV4l2FmtMap);
}
return fmt_map;
}
static IMXV4l2FmtMap g_imxv4l2fmt_maps_IPU[] = {
{GST_VIDEO_CAPS_MAKE("I420"), V4L2_PIX_FMT_YUV420, GST_VIDEO_FORMAT_I420, 12, 0},
{GST_VIDEO_CAPS_MAKE("YV12"), V4L2_PIX_FMT_YVU420, GST_VIDEO_FORMAT_YV12, 12, 0},
{GST_VIDEO_CAPS_MAKE("NV12"), V4L2_PIX_FMT_NV12, GST_VIDEO_FORMAT_NV12, 12, 0},
{GST_VIDEO_CAPS_MAKE("Y42B"), V4L2_PIX_FMT_YUV422P, GST_VIDEO_FORMAT_Y42B, 16, 0},
{GST_VIDEO_CAPS_MAKE("AYUV"), V4L2_PIX_FMT_YUV32, GST_VIDEO_FORMAT_AYUV, 32, 0},
{GST_VIDEO_CAPS_MAKE("Y444"), IPU_PIX_FMT_YUV444P, GST_VIDEO_FORMAT_Y444, 24, 0},
{GST_VIDEO_CAPS_MAKE("TNVP"), IPU_PIX_FMT_TILED_NV12, GST_VIDEO_FORMAT_UNKNOWN, 12, 0},
{GST_VIDEO_CAPS_MAKE("TNVF"), IPU_PIX_FMT_TILED_NV12F, GST_VIDEO_FORMAT_UNKNOWN, 12, 0},
{GST_VIDEO_CAPS_MAKE("UYVY"), V4L2_PIX_FMT_UYVY, GST_VIDEO_FORMAT_UYVY, 16, 0},
{GST_VIDEO_CAPS_MAKE("YUY2"), V4L2_PIX_FMT_YUYV, GST_VIDEO_FORMAT_YUY2, 16, 0},
{GST_VIDEO_CAPS_MAKE("RGBx"), V4L2_PIX_FMT_RGB32, GST_VIDEO_FORMAT_RGBx, 32, 0},
{GST_VIDEO_CAPS_MAKE("BGRx"), V4L2_PIX_FMT_BGR32, GST_VIDEO_FORMAT_BGRx, 32, 0},
{GST_VIDEO_CAPS_MAKE("RGB"), V4L2_PIX_FMT_RGB24, GST_VIDEO_FORMAT_RGB, 24, 0},
{GST_VIDEO_CAPS_MAKE("BGR"), V4L2_PIX_FMT_BGR24, GST_VIDEO_FORMAT_BGR, 24, 0},
{GST_VIDEO_CAPS_MAKE("RGB16"), V4L2_PIX_FMT_RGB565, GST_VIDEO_FORMAT_RGB16, 16, 0},
};
typedef struct {
const gchar * caps_str;
guint v4l2fmt;
GstVideoFormat gstfmt;
guint bits_per_pixel;
guint flags;
} IMXV4l2FmtMap;
仔细体会上面的代码,为啥要将gst类型的format转换成v4l2类型的呢?从g_imxv4l2fmt_maps_IPU[]数组中可以看出来,以YUV420类型为例:
在gst中,它的名字是:GST_VIDEO_FORMAT_I420
转换成v4l2形式的名字为:V4L2_PIX_FMT_YUV420。这个函数主要完成名字的转换。
下面详细分析gst_imx_v4l2src_reset函数:
static gboolean
gst_imx_v4l2src_reset (GstImxV4l2Src * v4l2src)
{
if (v4l2src->pool) {
gst_object_unref (v4l2src->pool);
v4l2src->pool = NULL;
gst_imx_v4l2_reset_device (v4l2src->v4l2handle);
}
if (v4l2src->gstbuffer_in_v4l2) {
g_list_foreach (v4l2src->gstbuffer_in_v4l2, (GFunc) gst_memory_unref, NULL);
g_list_free (v4l2src->gstbuffer_in_v4l2);
v4l2src->gstbuffer_in_v4l2 = NULL;
}
GST_DEBUG_OBJECT (v4l2src, "gstbuffer_in_v4l2 list free\n");
v4l2src->stream_on = FALSE;
v4l2src->actual_buf_cnt = 0;
v4l2src->use_my_allocator = FALSE;
return TRUE;
}
这个函数首先判断v4l2src->pool是在gst_imx_v4l2src_decide_allocation函数中分配的。大致意思是通过这个函数来分配内存池。如果分配了内存池的话,就说明设备已经开始工作,就需要调用gst_imx_v4l2_reset_device函数来重置摄像头设备。
之后继续判断v4l2src->gstbuffer_in_v4l2,个人感觉这个v4l2src->gstbuffer_in_v4l2是一个链表头,链表中存放的是所使用的buffer,如果这个链表头存在的话,就遍历这个链表,将里面的每一个buffer都通过gst_memory_unref函数来释放。
之后将stream_on标志位置位FALSE,actual_buf_cnt置位0等等重置操作。
5.6 gst_imx_v4l2src_query函数
static gboolean
gst_imx_v4l2src_query (GstBaseSrc * bsrc, GstQuery * query)
{
GstImxV4l2Src *v4l2src;
gboolean res = FALSE;
v4l2src = GST_IMX_V4L2SRC (bsrc);
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_LATENCY:{
GstClockTime min_latency, max_latency;
guint32 fps_n, fps_d;
guint num_buffers = 0;
if (v4l2src->v4l2handle == NULL) {
GST_WARNING_OBJECT (v4l2src,
"Can't give latency since device isn't open !");
goto done;
}
//如果设备没有打开的话,就打印出错误语句。
fps_n = v4l2src->fps_n;
fps_d = v4l2src->fps_d;
if (fps_n <= 0 || fps_d <= 0) {
GST_WARNING_OBJECT (v4l2src,
"Can't give latency since framerate isn't fixated !");
goto done;
}
min_latency = gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
//根据fps_n和fps_d计算出最小传输延时。
num_buffers = v4l2src->actual_buf_cnt;
if (num_buffers == 0)
max_latency = -1;
else
max_latency = num_buffers * min_latency;
//将num_buffers乘以最小传输延时就得到了最大传输延时。
GST_DEBUG_OBJECT (v4l2src,
"report latency min %" GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
gst_query_set_latency (query, TRUE, min_latency, max_latency);
//设置计算出来的最小最大传输延时时间。这个函数在《gstreamer插件所用函数整理》中。
res = TRUE;
break;
}
default:
res = GST_BASE_SRC_CLASS (parent_class)->query (bsrc, query);
break;
//其他的case直接继承它的父类即可。
}
done:
return res;
}
这个函数实现的是查询操作函数,它相应的实现了本插件里面的查询传输延时操作,其他的查询操作继承自它的父类。
5.7 gst_imx_v4l2src_decide_allocation函数
//关于这个函数,需要理解allocator,pool到底什么含义,分别有什么作用,现在还解释不太清楚,做个标记,以后完善这里。!!!!!在GStreamer 1.0 Core Reference Manual中的小章节,如GstBufferPool,在这里面的Description里面,有函数的初始化,如何使用等等的介绍,所以这些概念应该就是在那些里面介绍了,仔细找找分析。
同时,以后应该对那些小章节的Description部分进行简单的翻译,整理出来一个文件。
static gboolean
gst_imx_v4l2src_decide_allocation (GstBaseSrc * bsrc, GstQuery * query)
{
GstImxV4l2Src *v4l2src = GST_IMX_V4L2SRC (bsrc);
IMXV4l2AllocatorContext context;
GstCaps *outcaps;
GstBufferPool *pool = NULL;
guint size, min, max;
GstAllocator *allocator = NULL;
GstAllocationParams params;
GstStructure *config;
gboolean update_pool, update_allocator;
GstVideoInfo vinfo;
const GstStructure *structure;
if (v4l2src->pool){
gst_query_parse_allocation (query, &outcaps, NULL);
gst_video_info_init (&vinfo);
gst_video_info_from_caps (&vinfo, outcaps);
/*如果有pool的话,首先通过gst_query_parse_allocation函数来解析query,将解析出来的数据写到outcaps里面,之后通过 gst_video_info_init函数用默认的值来
初始化vinfo,继续调用 gst_video_info_from_caps函数来解析outcaps,然后用解析出来的值更新vinfo。这三个函数在《gstreamer插件所用函数整理》中。关于这
个v4l2src->pool标志位,就在本函数的后面设置。也就是说如果第一次掉用这个函数,不会执行这个if语句,但是会在后面设置这个v4l2src->pool标志位,如果再
次调用这个函数的话,就会执行这个if语句。 */
if (gst_query_get_n_allocation_pools (query) > 0) {
gst_query_set_nth_allocation_pool (query, 0, v4l2src->pool, vinfo.size, v4l2src->actual_buf_cnt, v4l2src->actual_buf_cnt);
} else {
gst_query_add_allocation_pool (query, v4l2src->pool, vinfo.size, v4l2src->actual_buf_cnt, v4l2src->actual_buf_cnt);
}
/* 首先根据 gst_query_get_n_allocation_pools函数来从query中解析出来有几个allocation_pools,如果解析出来的这个数大于0的话,就调用
gst_query_set_nth_allocation_pool函数来将query中的参数设置到 v4l2src->pool中。
如果解析出来的这个数小于等于0的话,就调用 gst_query_add_allocation_pool函数来将query中的参数设置到v4l2src->pool中。
后面两个函数很相似,差别是 gst_query_set_nth_allocation_pool函数多一个参数---第二个参数index,是指allocator array的序号。*/
return TRUE;
}
v4l2src->use_my_allocator = FALSE;
gst_query_parse_allocation (query, &outcaps, NULL);
gst_video_info_init (&vinfo);
gst_video_info_from_caps (&vinfo, outcaps);
//这三个函数在上面解释了。但是如果没有pool的话,就不会执行上面的语句。通过这几个语句来设置好vinfo。
/* we got configuration from our peer or the decide_allocation method,
* parse them */
if (gst_query_get_n_allocation_params (query) > 0) {
/* try the allocator */
gst_query_parse_nth_allocation_param (query, 0, &allocator, ¶ms);
update_allocator = TRUE;
//首先解析query中的参数,如果解析出来的值大于0,就说明存在allocator,然后尝试从allocator array数组中根据index下标取出对应的 allocator和
allocator对应的params参数。
} else {
allocator = NULL;
gst_allocation_params_init (¶ms);
update_allocator = FALSE;
}
/*
如果解析出的值小于等于0,就说明不存在allocator,设置allocator = NULL ,同时调用 gst_allocation_params_init函数,来设置params为默认值。
在本函数的后面会检测allocator这个标志位,如果为NULL的话,就说明没有allocator,就会创建一个allocator。在本函数后面介绍。 */
if (gst_query_get_n_allocation_pools (query) > 0) {
gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
size = MAX (size, vinfo.size);
update_pool = TRUE;
//如果存在pool的话,就通过 gst_query_parse_nth_allocation_pool函数从query中解析出pool对应的参数。
} else {
pool = NULL;
size = vinfo.size;
min = max = 0;
update_pool = FALSE;
}
//如果不存在poll的话,就将pool设置成NULL,然后在后面会检测这个标志位,为NULL的话,就重新创建一个pool,在这个函数的后面介绍。
if (allocator == NULL \
|| !GST_IS_ALLOCATOR_PHYMEM (allocator) \
|| v4l2src->use_v4l2_memory == TRUE) {
/* no allocator or isn't physical memory allocator. VPU need continus
* physical memory. use VPU memory allocator. */
if (allocator) {
GST_DEBUG_OBJECT (v4l2src, "unref proposaled allocator.\n");
gst_object_unref (allocator);
}
GST_INFO_OBJECT (v4l2src, "using v4l2 source allocator.\n");
context.v4l2_handle = v4l2src->v4l2handle;
context.user_data = (gpointer) v4l2src;
context.callback = gst_imx_v4l2_allocator_cb;
allocator = v4l2src->allocator = gst_imx_v4l2_allocator_new (&context);
if (!v4l2src->allocator) {
GST_ERROR_OBJECT (v4l2src, "New v4l2 allocator failed.\n");
return FALSE;
}
v4l2src->use_my_allocator = TRUE;
}
/* 这一段代码就是上面提到的创建allocator的代码,可以看出来判断allocator == NULL这个标志位,重要的函数是gst_imx_v4l2_allocator_new 函数,会通过这个函数
来创建allocator,gst_imx_v4l2_allocator_new这个函数在gstimxv4l2allocator.c文件中,这个函数的核心就是g_object_new函数,具体在那个文件中再分析。
同时gst_imx_v4l2_allocator_new这个函数根据IMXV4l2AllocatorContext类型的context来创建allocator,所以这个IMXV4l2AllocatorContext类型对应是
在gstimxv4l2allocator.h文件中声明。 */
if (pool == NULL ||v4l2src->use_v4l2_memory == TRUE) {
if (pool) {
gst_object_unref (pool);
}
/* no pool, we can make our own */
GST_DEBUG_OBJECT (v4l2src, "no pool,making new pool");
structure = gst_caps_get_structure(v4l2src->old_caps, 0);
if (gst_structure_has_name (structure,"video/x-bayer")) {
size = GST_ROUND_UP_4 (v4l2src->w) *v4l2src->h;
pool = gst_buffer_pool_new ();
} else
pool = gst_video_buffer_pool_new ();
}
/* 这一段代码是上面提到的创建pool的代码。首先从 v4l2src->old_caps里面获取到 structure,然后根据 structure里面的名字来选择使用 gst_buffer_pool_new函数
还是gst_video_buffer_pool_new函数来创建一个新pool。这两个函数都在《gstreamer插件所用函数整理》中介绍。 */
v4l2src->pool = gst_object_ref (pool);
//上面提到的 v4l2src->pool标志位就是在这里设置的。
max = min += DEFAULT_FRAMES_IN_V4L2_CAPTURE \
+ v4l2src->frame_plus;
if (min > 10)
max = min = 10;
v4l2src->actual_buf_cnt = min;
//设置max和min的值。
/* now configure */
config = gst_buffer_pool_get_config (pool);
if (!gst_buffer_pool_config_has_option(config, \
GST_BUFFER_POOL_OPTION_VIDEO_META)) {
gst_buffer_pool_config_add_option (config,
GST_BUFFER_POOL_OPTION_VIDEO_META);
}
gst_buffer_pool_config_set_params (config,outcaps, size, min, max);
gst_buffer_pool_config_set_allocator (config,allocator, ¶ms);
gst_buffer_pool_set_config (pool, config);
/* 上面这段代码是GstBufferPool相关的API,gst_buffer_pool_get_config 函数是获取当前的配置(config),之后通过 gst_buffer_pool_config_set_params函数,
来将outcaps, size, min, max的值设置到config中,继续通过 gst_buffer_pool_config_set_allocator函数来设置config里面与 allocator, ¶ms相关的参数,
最后通过gst_buffer_pool_set_config函数,来将新设置好的config写到poll中。这些函数都在《gstreamer插件所用函数整理》中介绍。 */
if (update_allocator)
gst_query_set_nth_allocation_param (query,0, allocator, ¶ms);
else
gst_query_add_allocation_param (query,allocator, ¶ms);
if (allocator)
gst_object_unref (allocator);
if (update_pool)
gst_query_set_nth_allocation_pool (query,0, pool, size, min, max);
else
gst_query_add_allocation_pool (query, pool,size, min, max);
if (pool)
gst_object_unref (pool);
return TRUE;
}
有关这一节的知识,可以查看《插件编写手册》中的Chapter 15. Memory allocation 的15.4
GstBufferPool和15.5 GST_QUERY_ALLOCATION这两节,里面有比较详细的流程介绍。
5.8 gst_imx_v4l2src_create函数
这个gst_imx_v4l2src_create函数里面包含很多函数,想要分析清楚的话需要先分析里面的小函数,于是先一步一步分析。
5.8.1 gst_imx_v4l2_allocator_cb函数
static gint
gst_imx_v4l2_allocator_cb (gpointer user_data, gint *count)
{
GstImxV4l2Src *v4l2src = GST_IMX_V4L2SRC (user_data); //从传入的user_data中解析出来GstImxV4l2Src.
guint min, max;
if (!v4l2src->pool)
v4l2src->pool = gst_base_src_get_buffer_pool (GST_BASE_SRC (v4l2src));
/* 如果 v4l2src->pool没有值,就通过 gst_base_src_get_buffer_pool函数从 v4l2src中读取,这时候v4l2src->pool应该有值的,如果没有值,也通过这个函数
读取出来值了,如果还没有值的话,就是出错了。这个函数在《gstreamer插件所用函数整理》中。 */
if (v4l2src->pool) { //上面说的v4l2src->pool这个值无论如何都应该有了,没有的话就跳到else语句,返回错误。
GstStructure *config;
config = gst_buffer_pool_get_config (v4l2src->pool); //解析当前pool的配置(config)。
// check if has alignment option setted.
// if yes, need to recheck the pool params for reconfigure v4l2 devicec.
memset (&v4l2src->video_align, 0, sizeof(GstVideoAlignment));
if (gst_buffer_pool_config_has_option (config, GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT)) {
gst_buffer_pool_config_get_video_alignment (config, &v4l2src->video_align);
GST_DEBUG_OBJECT (v4l2src, "pool has alignment (%d, %d) , (%d, %d)",
v4l2src->video_align.padding_left, v4l2src->video_align.padding_top,
v4l2src->video_align.padding_right, v4l2src->video_align.padding_bottom);
}
/* 上面这段代码是有关alignment的代码。核心是 GstVideoAlignment类型的结构体,GstVideoAlignment类型在《gstreamer插件所用函数整理》中介绍。首先
通过 gst_buffer_pool_config_has_option函数来获取config中是否有GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT选项。如果有这个选项的话,就调用
gst_buffer_pool_config_get_video_alignment函数来从config中读取出来,保存到 v4l2src->video_align中,然后通过 GST_DEBUG_OBJECT来将这些对齐信息打印出来。*/
gst_buffer_pool_config_get_params (config, NULL, NULL, &min, &max);
GST_DEBUG_OBJECT (v4l2src, "need allocate %d buffers.\n", max);
gst_structure_free(config);
/* 通过 gst_buffer_pool_config_get_params函数从config中读取min,max参数,这两个参数是在gst_imx_v4l2src_decide_allocation函数中配置的。表示需要分配
的buffer的最大值最小值。 */
if (gst_imx_v4l2src_config (v4l2src) < 0) {
GST_ERROR_OBJECT (v4l2src, "camera configuration failed.\n");
g_printf ("capture device: %s probed caps: %" GST_PTR_FORMAT, v4l2src->device, \
v4l2src->probed_caps);
g_printf ("Please config accepted caps!\n");
return -1;
}
/* 这里面有个gst_imx_v4l2src_config函数,这个函数的作用是用来配置摄像头等设备的,在这个函数后面分析。 */
if (v4l2src->use_my_allocator) {
if (gst_imx_v4l2_set_buffer_count (v4l2src->v4l2handle, max, V4L2_MEMORY_MMAP) < 0)
return -1;
} else {
if (gst_imx_v4l2_set_buffer_count (v4l2src->v4l2handle, max, V4L2_MEMORY_USERPTR) < 0)
return -1;
}
/* 这里面有个gst_imx_v4l2_set_buffer_count函数,同样在这个函数后面分析,这个函数的作用是来完成VIDIOC_REQBUFS ioctl。 */
*count = max;
}
else {
GST_ERROR_OBJECT (v4l2src, "no pool to get buffer count.\n");
return -1;
}
return 0;
}
下面看gst_imx_v4l2src_config函数,这个函数很深,嵌套了很多层:
static gint
gst_imx_v4l2src_config (GstImxV4l2Src *v4l2src)
{
guint w,h;
w = v4l2src->w + v4l2src->video_align.padding_left + v4l2src->video_align.padding_right;
h = v4l2src->h + v4l2src->video_align.padding_top + v4l2src->video_align.padding_bottom;
GST_DEBUG_OBJECT (v4l2src, "padding: (%d,%d), (%d, %d)",
v4l2src->video_align.padding_left, v4l2src->video_align.padding_top,
v4l2src->video_align.padding_right, v4l2src->video_align.padding_bottom);
//设置w,h的值,这两个参数用于下一个函数。
return gst_imx_v4l2capture_config (v4l2src->v4l2handle, v4l2src->v4l2fmt, w, h, \
v4l2src->fps_n, v4l2src->fps_d);
}
gst_imx_v4l2capture_config函数是在/libs/v4l2-core/gstimxv4l2.c文件中提供的函数接口:
gint gst_imx_v4l2capture_config (gpointer v4l2handle, guint fmt, guint w, guint h, guint fps_n, guint fps_d)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
return (*handle->dev_itf.v4l2capture_config) (v4l2handle, fmt, w, h, fps_n, fps_d);
}
可以看到这个函数最后返回的是一个函数指针,那么来看看这个函数指针,是保存在IMXV4l2Handle结构体中,如下所示:
typedef struct {
。。。。。。
gint allocated;
IMXV4l2BufferPair buffer_pair[MAX_BUFFER];
gint rotate;
guint *support_format_table;
gboolean is_tvin;
IMXV4l2DeviceItf dev_itf;
struct v4l2_buffer * v4lbuf_queued_before_streamon[MAX_BUFFER];
。。。。。
} IMXV4l2Handle
继续看里面的IMXV4l2DeviceItfdev_itf:
typedef struct {
V4l2outConfigInput v4l2out_config_input;
V4l2outConfigOutput v4l2out_config_output;
V4l2outConfigRotate v4l2out_config_rotate;
V4l2outConfigAlpha v4l2out_config_alpha;
V4l2outConfigColorkey v4l2out_config_colorkey;
V4l2captureConfig v4l2capture_config;
} IMXV4l2DeviceItf;
typedef gint (*V4l2captureConfig) (void *handle, guint fmt, guint w, guint h, \
guint fps_n, guint fps_d);
最终找到这个函数指针的原型,但是这个函数指针是在哪里初始化的?可以在源码中搜索,是在之前介绍过的gst_imx_v4l2capture_set_function函数中设置的:
if (!strncmp (chip.match.name, MXC_V4L2_CAPTURE_CAMERA_NAME, 2)) {
handle->dev_itf.v4l2capture_config = (V4l2captureConfig)gst_imx_v4l2capture_config_camera;
handle->support_format_table = g_camera_format_IPU;
} else if (!strncmp (chip.match.name, MXC_V4L2_CAPTURE_TVIN_NAME, 3)) {
handle->dev_itf.v4l2capture_config = (V4l2captureConfig)gst_imx_v4l2capture_config_camera;
handle->support_format_table = g_camera_format_IPU;
handle->is_tvin = TRUE;
if (gst_imx_v4l2capture_config_tvin_std (handle)) {
GST_ERROR ("can't set TV-In STD.\n");
return -1;
}
} else {
GST_ERROR ("can't identify capture sensor type.\n");
return -1;
}
下面跳转到gst_imx_v4l2capture_config_camera函数中看看:
static gint
gst_imx_v4l2capture_config_camera (IMXV4l2Handle *handle, guint fmt, guint w, guint h, guint fps_n, guint fps_d)
{
gint input = 1;
if (ioctl (handle->v4l2_fd, VIDIOC_S_INPUT, &input) < 0) {
GST_ERROR ("VIDIOC_S_INPUT failed");
return -1;
}
return gst_imx_v4l2capture_config_pxp (handle, fmt, w, h, fps_n, fps_d);
}
发现,这个函数的核心是VIDIOC_S_INPUT ioctl调用和gst_imx_v4l2capture_config_pxp函数。默认的设置的输入是1,在之前分析IPU的时候,可以看看input=1是哪条channel?印象中是CSI--->IC--->IDMAC channel。
继续看gst_imx_v4l2capture_config_pxp函数:
static gint
gst_imx_v4l2capture_config_pxp (IMXV4l2Handle *handle, guint fmt, guint w, guint h, guint fps_n, guint fps_d)
{
// can add crop process if needed.
if (gst_imx_v4l2capture_config_usb_camera (handle, fmt, w, h, fps_n, fps_d) < 0) {
GST_ERROR ("camera config failed\n");
return -1;
}
return 0;
}
继续追踪:
static gint
gst_imx_v4l2capture_config_usb_camera (IMXV4l2Handle *handle, guint fmt, guint w, guint h, guint fps_n, guint fps_d)
{
struct v4l2_format v4l2_fmt = {0};
struct v4l2_frmsizeenum fszenum = {0};
struct v4l2_streamparm parm = {0};
gint capture_mode = -1;
fszenum.index = 0;
fszenum.pixel_format = fmt;
while (ioctl (handle->v4l2_fd, VIDIOC_ENUM_FRAMESIZES, &fszenum) >= 0){
if (fszenum.discrete.width == w && fszenum.discrete.height == h) {
capture_mode = fszenum.index;
break;
}
fszenum.index ++;
}
if (capture_mode < 0) {
GST_ERROR ("can't support resolution.");
return -1;
}
GST_INFO ("capture mode %d: %dx%d", capture_mode, w, h);
parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
parm.parm.capture.timeperframe.numerator = fps_d;
parm.parm.capture.timeperframe.denominator = fps_n;
parm.parm.capture.capturemode = capture_mode;
if (ioctl (handle->v4l2_fd, VIDIOC_S_PARM, &parm) < 0) {
GST_ERROR ("VIDIOC_S_PARM failed");
return -1;
}
GST_INFO ("frame format: %c%c%c%c", fmt & 0xff, (fmt >> 8) & 0xff,
(fmt >> 16) & 0xff, (fmt >> 24) & 0xff);
v4l2_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_fmt.fmt.pix.pixelformat = fmt;
v4l2_fmt.fmt.pix.width = w;
v4l2_fmt.fmt.pix.height = h;
if (ioctl (handle->v4l2_fd, VIDIOC_S_FMT, &v4l2_fmt) < 0) {
GST_ERROR ("VIDIOC_S_FMT failed");
return -1;
}
return 0;
}
发现这个函数的核心是三个ioctl调用,整体上来说,无论怎么变化,都是围绕V4L2框架来完成这些的。
在分析完gst_imx_v4l2src_config函数后,再来看看gst_imx_v4l2_set_buffer_count函数:
gint gst_imx_v4l2_set_buffer_count (gpointer v4l2handle, guint count, guint memory_mode)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
struct v4l2_requestbuffers buf_req;
GST_DEBUG ("requeset for (%d) buffers.", count);
memset(&buf_req, 0, sizeof(buf_req));
buf_req.type = handle->type;
/* 从这里应该可以看出,v4l2src->use_my_allocator应该为TRUE,因为为TRUE的话,type类型为V4L2_MEMORY_MMAP,否则的话为V4L2_MEMORY_USERPTR。 */
buf_req.count = count;
handle->memory_mode = buf_req.memory = memory_mode;
if (ioctl(handle->v4l2_fd, VIDIOC_REQBUFS, &buf_req) < 0) {
GST_ERROR("Request %d buffers failed\n", count);
return -1;
}
handle->buffer_count = count;
return 0;
}
在这个函数里面完成了VIDIOC_REQBUFS ioctl调用。
至此,gst_imx_v4l2_allocator_cb函数分析完毕。
5.8.2 gst_imx_v4l2src_register_buffer函数
static GstFlowReturn
gst_imx_v4l2src_register_buffer (GstImxV4l2Src * v4l2src)
{
GstFlowReturn ret = GST_FLOW_OK;
PhyMemBlock *memblk;
GstBuffer * buffer;
gint i;
for (i = 0; i < v4l2src->actual_buf_cnt; i++) {
ret = gst_buffer_pool_acquire_buffer (v4l2src->pool, &buffer, NULL);
if (ret != GST_FLOW_OK) {
GST_ERROR_OBJECT (v4l2src, "gst_buffer_pool_acquire_buffer failed.");
return ret;
}
//从对应的pool里面分配buffer,一共分配v4l2src->actual_buf_cnt个。
memblk = gst_buffer_query_phymem_block(buffer);
if (!memblk) {
GST_ERROR_OBJECT (v4l2src, "Can't get physical memory block from gstbuffer.\n");
return GST_FLOW_ERROR;
}
/* 这个函数是在/libs/allocator/gstallocatorphymem.c中定义的,大致意思是获取buffer的物理地址,虚拟地址等信息,函数的返回值是 PhyMemBlock类型的,如下所示:
typedef struct {
guint8 *vaddr;
guint8 *paddr;
guint8 *caddr;
gsize size;
gpointer *user_data;
} PhyMemBlock;
可以看到这个结构体里面包含了很多地址。以后再具体分析这个函数。*/
if (gst_imx_v4l2_register_buffer (v4l2src->v4l2handle, memblk) < 0) {
GST_ERROR_OBJECT (v4l2src, "register buffer failed.");
return GST_FLOW_ERROR;
}
//这个函数在下面分析。
gst_buffer_unref (buffer);
}
return ret;
}
gst_imx_v4l2_register_buffer函数:
gint gst_imx_v4l2_register_buffer (gpointer v4l2handle, PhyMemBlock *memblk)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
struct v4l2_buffer *v4l2buf;
if (handle->allocated >= handle->buffer_count) {
GST_ERROR ("No more v4l2 buffer for allocating.\n");
return -1;
}
v4l2buf = &handle->buffer_pair[handle->allocated].v4l2buffer;
memset (v4l2buf, 0, sizeof(struct v4l2_buffer));
v4l2buf->type = handle->type;
v4l2buf->memory = handle->memory_mode;
v4l2buf->index = handle->allocated;
v4l2buf->m.userptr = memblk->paddr;
v4l2buf->length = memblk->size;
handle->buffer_pair[handle->allocated].vaddr = memblk->vaddr;
if (ioctl(handle->v4l2_fd, VIDIOC_QUERYBUF, v4l2buf) < 0) {
GST_ERROR ("VIDIOC_QUERYBUF error.");
return -1;
}
handle->allocated ++;
GST_DEBUG ("Allocated v4l2buffer(%p), memblk(%p), paddr(%p), index(%d).",
v4l2buf, memblk, memblk->paddr, handle->allocated - 1);
return 0;
}
这个函数的核心是VIDIOC_QUERYBUF ioctl调用。
(5.8.3) gst_imx_v4l2_queue_gstbuffer函数
gint gst_imx_v4l2_queue_gstbuffer (gpointer v4l2handle, GstBuffer *buffer, GstVideoFrameFlags flags)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
struct v4l2_buffer *v4l2buf;
PhyMemBlock *memblk;
if (handle->invisible) {
gst_buffer_unref (buffer);
return 0;
}
memblk = gst_buffer_query_phymem_block(buffer);
if (!memblk) {
GST_ERROR ("Can't get physical memory block from gstbuffer.\n");
return -1;
}
//这个函数在上面分析了,返回的是一个PhyMemBlock类型的memblk,里面包含了很多地址信息物理地址,虚拟地址等。
GST_DEBUG ("queue gstbuffer(%p).", buffer);
v4l2buf = (struct v4l2_buffer *) gst_imx_v4l2_find_buffer(v4l2handle, memblk);
if (!v4l2buf)
return -1;
//gst_imx_v4l2_find_buffer 这个函数在/libs/v4l2_core/gstimxv4l2.c文件中定义,大致意思是根据memblk->vaddr的值来从handle->buffer_pair[i]找到
这个buffer的地址,然后返回handle->buffer_pair[i].v4l2buffer。
if (handle->buffer_pair[v4l2buf->index].gstbuffer) {
if (handle->buffer_pair[v4l2buf->index].gstbuffer != buffer) {
GST_WARNING ("new buffer (%p) use the same memblk(%p) with queued buffer(%p)",
buffer, memblk, handle->buffer_pair[v4l2buf->index].gstbuffer);
}
GST_WARNING ("gstbuffer(%p) for (%p) not dequeued yet but queued again, index(%d).",
handle->buffer_pair[v4l2buf->index].gstbuffer, index);
}
if (gst_imx_v4l2_queue_v4l2memblk (v4l2handle, memblk, flags) < 0) {
GST_ERROR ("queue gstbuffer (%p) failed.", buffer);
return 0;
}
//核心就是这个gst_imx_v4l2_queue_v4l2memblk函数了,在下面分析。
handle->buffer_pair[v4l2buf->index].gstbuffer = buffer;
return 0;
}
gst_imx_v4l2_queue_v4l2memblk函数:
gint gst_imx_v4l2_queue_v4l2memblk (gpointer v4l2handle, PhyMemBlock *memblk, GstVideoFrameFlags flags)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
struct v4l2_buffer *v4l2buf;
gint index;
v4l2buf = (struct v4l2_buffer *)gst_imx_v4l2_find_buffer(v4l2handle, memblk);
if (!v4l2buf)
return -1;
//还是这个函数,上面分析了。
index = v4l2buf->index;
GST_DEBUG ("queue v4lbuffer memblk (%p), paddr(%p), index(%d), flags(%x).",
memblk, memblk->paddr, index, flags);
v4l2buf->field = V4L2_FIELD_NONE;
if ((flags & GST_VIDEO_FRAME_FLAG_INTERLACED) && handle->do_deinterlace) {
if (flags & GST_VIDEO_FRAME_FLAG_TFF)
v4l2buf->field = V4L2_FIELD_INTERLACED_TB;
else
v4l2buf->field = V4L2_FIELD_INTERLACED_BT;
}
if (flags & GST_VIDEO_FRAME_FLAG_ONEFIELD) {
if (flags & GST_VIDEO_FRAME_FLAG_TFF)
v4l2buf->field = V4L2_FIELD_TOP;
else
v4l2buf->field = V4L2_FIELD_BOTTOM;
}
//上面的代码是设置一些标志位等信息。
handle->buffer_pair[v4l2buf->index].v4l2memblk = memblk;
//这个handle->buffer_pair[]数组里面放置的应该是buffer,最终将memblk的值赋进去。
if (!handle->streamon) {
int i;
GST_DEBUG ("streamon count (%d), queue count (%d)", handle->streamon_count, handle->queued_count);
handle->v4lbuf_queued_before_streamon[handle->queued_count] = v4l2buf;
handle->queued_count ++;
if (handle->queued_count < handle->streamon_count)
return 0;
for (i=0; istreamon_count; i++) {
if (imx_v4l2_do_queue_buffer (handle, handle->v4lbuf_queued_before_streamon[i]) < 0) {
handle->buffer_pair[handle->v4lbuf_queued_before_streamon[i]->index].v4l2memblk = NULL;
GST_ERROR ("queue buffers before streamon failed.");
return -1;
}
}
//在imx_v4l2_do_queue_buffer函数中,核心就是VIDIOC_QBUF ioctl调用。
if (ioctl (handle->v4l2_fd, VIDIOC_STREAMON, &handle->type) < 0) {
GST_ERROR ("Stream on V4L2 device failed.\n");
return -1;
}
handle->streamon = TRUE;
GST_DEBUG ("V4L2 device is STREAMON.");
return 0;
}
if (imx_v4l2_do_queue_buffer (handle, v4l2buf) < 0) {
handle->buffer_pair[v4l2buf->index].v4l2memblk = NULL;
return -1;
}
handle->queued_count ++;
GST_DEBUG ("queued (%d)\n", handle->queued_count);
return 0;
}
这个函数中做的是V4L2框架中的VIDIOC_QBUF和 VIDIOC_STREAMON这两个ioctl调用。
小总结:
gst_imx_v4l2_queue_gstbuffer函数中主要的工作是做了V4L2框架中的VIDIOC_QBUF和 VIDIOC_STREAMON这两个ioctl调用。
(5.8.4) gst_imx_v4l2_dequeue_gstbuffer函数
在分析这个函数之前,对比上一个gst_imx_v4l2_queue_gstbuffer函数,理性分析一下:
在这个函数里面肯定做的是V4L2框架中的VIDIOC_DQBUF ioctl的操作。gint gst_imx_v4l2_dequeue_gstbuffer (gpointer v4l2handle, GstBuffer **buffer,
GstVideoFrameFlags * flags)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
PhyMemBlock *memblk = NULL;
struct v4l2_buffer *v4l2buf;
if (handle->invisible) {
return 0;
}
if (gst_imx_v4l2_dequeue_v4l2memblk (handle, &memblk, flags) < 0) {
GST_ERROR ("dequeue memblk failed.");
return -1;
}
if (!memblk)
return 0;
v4l2buf = (struct v4l2_buffer *) gst_imx_v4l2_find_buffer(v4l2handle, memblk);
if (!v4l2buf)
return -1;
*buffer = handle->buffer_pair[v4l2buf->index].gstbuffer;
handle->buffer_pair[v4l2buf->index].gstbuffer = NULL;
GST_DEBUG ("dequeue gstbuffer(%p), v4l2buffer index(%d).", *buffer, v4l2buf->index);
return 0;
}
gst_imx_v4l2_dequeue_v4l2memblk函数:
gint gst_imx_v4l2_dequeue_v4l2memblk (gpointer v4l2handle, PhyMemBlock **memblk,
GstVideoFrameFlags * flags)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
struct v4l2_buffer v4l2buf;
gint trycnt = 0;
if (handle->queued_count <= MAX(handle->v4l2_hold_buf_num, handle->streamon_count)) {
GST_DEBUG ("current queued %d", handle->queued_count);
*memblk = NULL;
return 0;
}
memset (&v4l2buf, 0, sizeof(v4l2buf));
v4l2buf.type = handle->type;
v4l2buf.memory = handle->memory_mode;
while (ioctl (handle->v4l2_fd, VIDIOC_DQBUF, &v4l2buf) < 0) {
trycnt ++;
if(trycnt >= MAX_TRY_CNT) {
GST_ERROR ("Dequeue buffer from v4l2 device failed.");
return -1;
}
usleep (TRY_INTERVAL);
}
if (v4l2buf.field == V4L2_FIELD_INTERLACED) {
if (handle->id == V4L2_STD_NTSC) {
v4l2buf.field = V4L2_FIELD_INTERLACED_BT;
} else {
v4l2buf.field = V4L2_FIELD_INTERLACED_TB;
}
}
/* set field info */
switch (v4l2buf.field) {
case V4L2_FIELD_NONE: *flags = GST_VIDEO_FRAME_FLAG_NONE; break;
case V4L2_FIELD_TOP: *flags =
GST_VIDEO_FRAME_FLAG_ONEFIELD | GST_VIDEO_FRAME_FLAG_TFF; break;
case V4L2_FIELD_BOTTOM: *flags = GST_VIDEO_FRAME_FLAG_ONEFIELD; break;
case V4L2_FIELD_INTERLACED_TB: *flags =
GST_VIDEO_FRAME_FLAG_INTERLACED | GST_VIDEO_FRAME_FLAG_TFF; break;
case V4L2_FIELD_INTERLACED_BT: *flags = GST_VIDEO_FRAME_FLAG_INTERLACED; break;
default: GST_WARNING("unknown field type"); break;
}
*memblk = handle->buffer_pair[v4l2buf.index].v4l2memblk;
GST_DEBUG ("deque v4l2buffer memblk (%p), paddr(%p), index (%d)",
*memblk, (*memblk)->paddr, v4l2buf.index);
handle->buffer_pair[v4l2buf.index].v4l2memblk = NULL;
handle->queued_count--;
GST_DEBUG ("deque v4l2buffer memblk (%p), index (%d), flags (%d)",
v4l2buf.index, handle->buffer_pair[v4l2buf.index].v4l2memblk, *flags);
return 0;
}
(5.8.5) gst_imx_v4l2src_acquire_buffer函数
static GstFlowReturn
gst_imx_v4l2src_acquire_buffer (GstImxV4l2Src * v4l2src, GstBuffer ** buf)
{
GstFlowReturn ret = GST_FLOW_OK; //返回值类型
GstVideoFrameFlags flags = GST_VIDEO_FRAME_FLAG_NONE; //frame标志位
GstVideoMeta *vmeta; //元数据
gint buffer_count; //buffer计数
if (v4l2src->stream_on == FALSE) {
if (v4l2src->use_my_allocator == FALSE) {
if (gst_imx_v4l2_allocator_cb (v4l2src, &buffer_count) < 0) {
GST_ERROR_OBJECT (v4l2src, "gst_imx_v4l2_allocator_cb failed.");
return GST_FLOW_ERROR;
}
//之前已经分析过了,在 gst_imx_v4l2_allocator_cb中,会执行VIDIOC_REQBUFS 这个ioctl调用。
ret = gst_imx_v4l2src_register_buffer (v4l2src);
if (ret != GST_FLOW_OK) {
GST_ERROR_OBJECT (v4l2src, "gst_imx_v4l2_register_buffer failed.");
return ret;
}
//在gst_imx_v4l2src_register_buffer 函数中会执行VIDIOC_QUERYBUF 这个ioctl调用。
} else {
}
v4l2src->stream_on = TRUE;
}
while (g_list_length (v4l2src->gstbuffer_in_v4l2) \
< DEFAULT_FRAMES_IN_V4L2_CAPTURE) {
GstBuffer * buffer;
ret = gst_buffer_pool_acquire_buffer (v4l2src->pool, &buffer, NULL);
if (ret != GST_FLOW_OK) {
GST_ERROR_OBJECT (v4l2src, "gst_buffer_pool_acquire_buffer failed.");
return ret;
}
if (gst_imx_v4l2_queue_gstbuffer (v4l2src->v4l2handle, buffer, flags) < 0) {
GST_ERROR_OBJECT (v4l2src, "Queue buffer %p failed.", buffer);
return GST_FLOW_ERROR;
}
v4l2src->gstbuffer_in_v4l2 = g_list_append ( \
v4l2src->gstbuffer_in_v4l2, buffer);
}
//在gst_imx_v4l2_queue_gstbuffer函数中,会执行VIDIOC_QBUF和VIDIOC_STREAMON这两个 ioctl调用。
if (gst_imx_v4l2_dequeue_gstbuffer (v4l2src->v4l2handle, buf, &flags) < 0) {
GST_ERROR_OBJECT (v4l2src, "Dequeue buffer failed.");
return GST_FLOW_ERROR;
}
//在gst_imx_v4l2_dequeue_gstbuffer函数中,会执行VIDIOC_DQBUF这个ioctl调用。
v4l2src->gstbuffer_in_v4l2 = g_list_remove ( \
v4l2src->gstbuffer_in_v4l2, *buf);
vmeta = gst_buffer_get_video_meta (*buf);
/* If the buffer pool didn't add the meta already
* we add it ourselves here */
if (!vmeta) {
GstVideoInfo info;
if (!gst_video_info_from_caps (&info, v4l2src->old_caps)) {
GST_ERROR_OBJECT (v4l2src, "invalid caps.");
return GST_FLOW_ERROR;
}
vmeta = gst_buffer_add_video_meta (*buf, \
GST_VIDEO_FRAME_FLAG_NONE, \
GST_VIDEO_INFO_FORMAT (&info), \
v4l2src->w, \
v4l2src->h);
}
vmeta->flags = flags;
GST_DEBUG_OBJECT(v4l2src, "field type: %d\n", flags);
//上面这段代码就是尝试从buf中获取元数据,如果没有获取到的话,就调用 gst_buffer_add_video_meta来设置元数据。
return ret;
}
小总结:
在gst_imx_v4l2src_acquire_buffer函数中,会完成V4L2框架的VIDIOC_REQBUFS, VIDIOC_QUERYBUF, VIDIOC_QBUF和VIDIOC_STREAMON,以及VIDIOC_DQBUF这些ioctl调用。
(5.8.6) 至此,可以来分析gst_imx_v4l2src_create函数了:
static GstFlowReturn
gst_imx_v4l2src_create (GstPushSrc * src, GstBuffer ** buf)
{
GstImxV4l2Src *v4l2src = GST_IMX_V4L2SRC (src);
GstFlowReturn ret;
GstClock *clock;
GstClockTime abs_time, base_time, timestamp, duration;
GstClockTime delay;
GstBuffer *buffer;
ret = gst_imx_v4l2src_acquire_buffer (v4l2src, buf);
if (G_UNLIKELY (ret != GST_FLOW_OK)) {
GST_DEBUG_OBJECT (v4l2src, "error processing buffer %d (%s)", ret,
gst_flow_get_name (ret));
return ret;
}
buffer = *buf;
timestamp = GST_BUFFER_TIMESTAMP (buffer);
duration = v4l2src->duration;
GST_OBJECT_LOCK (v4l2src);
if ((clock = GST_ELEMENT_CLOCK (v4l2src))) {
base_time = GST_ELEMENT (v4l2src)->base_time;
gst_object_ref (clock);
} else {
base_time = GST_CLOCK_TIME_NONE;
}
GST_OBJECT_UNLOCK (v4l2src);
if (clock) {
abs_time = gst_clock_get_time (clock);
gst_object_unref (clock);
} else {
abs_time = GST_CLOCK_TIME_NONE;
}
if (!GST_CLOCK_TIME_IS_VALID (v4l2src->base_time_org)) {
v4l2src->base_time_org = base_time;
}
GST_DEBUG_OBJECT (v4l2src, "base_time: %" GST_TIME_FORMAT " abs_time: %"
GST_TIME_FORMAT, GST_TIME_ARGS (base_time), GST_TIME_ARGS (abs_time));
if (timestamp != GST_CLOCK_TIME_NONE) {
struct timespec now;
GstClockTime gstnow;
clock_gettime (CLOCK_MONOTONIC, &now);
gstnow = GST_TIMESPEC_TO_TIME (now);
if (gstnow < timestamp && (timestamp - gstnow) > (10 * GST_SECOND)) {
GTimeVal now;
g_get_current_time (&now);
gstnow = GST_TIMEVAL_TO_TIME (now);
}
if (gstnow > timestamp) {
delay = gstnow - timestamp;
} else {
delay = 0;
}
GST_DEBUG_OBJECT (v4l2src, "ts: %" GST_TIME_FORMAT " now %" GST_TIME_FORMAT
" delay %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp),
GST_TIME_ARGS (gstnow), GST_TIME_ARGS (delay));
} else {
if (GST_CLOCK_TIME_IS_VALID (duration))
delay = duration;
else
delay = 0;
}
if (G_LIKELY (abs_time != GST_CLOCK_TIME_NONE)) {
/* workaround for base time will change when image capture. */
timestamp = abs_time - v4l2src->base_time_org;
if (timestamp > delay)
timestamp -= delay;
else
timestamp = 0;
} else {
timestamp = GST_CLOCK_TIME_NONE;
}
GST_DEBUG_OBJECT (v4l2src, "timestamp: %" GST_TIME_FORMAT " duration: %" GST_TIME_FORMAT
, GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration));
GST_BUFFER_TIMESTAMP (buffer) = timestamp;
GST_BUFFER_PTS (buffer) = timestamp;
GST_BUFFER_DTS (buffer) = timestamp;
GST_BUFFER_DURATION (buffer) = duration;
return ret;
}
如果上面的代码都分析懂的话,就会发现这个函数其实挺简单的。它的核心函数就是gst_imx_v4l2src_acquire_buffer。在gst_imx_v4l2src_acquire_buffer函数中,会完成V4L2框架的VIDIOC_REQBUFS, VIDIOC_QUERYBUF, VIDIOC_QBUF和VIDIOC_STREAMON,以及VIDIOC_DQBUF这些ioctl调用。
剩下的代码就是来设置时间戳,延迟时间等知识,有关base_time,stream time和running time等知识,可以查看《GStreamer应用开发手册 Chapter 14. Clocks and sunchronization in GStreamer》这一节。
至此,gstimxv4l2src.c文件就算分析完了,核心就是gst_imx_v4l2src_class_init函数,其他的函数都是来实现这个初始化函数中的方法,同时围绕V4L2编程框架。但是感觉还有很多东西不够清楚,下面继续分析gstimxv4l2allocator.c文件或者gstimxv4l2sink.c文件或者gstimxv4l2.c这个库文件。同时对于现在这些函数接口很不熟悉。