由于这个文件中会调用到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;
} IMXV4l2Handletypedef struct {V4l2outConfigInput v4l2out_config_input;V4l2outConfigOutput v4l2out_config_output;V4l2outConfigRotate v4l2out_config_rotate;V4l2outConfigAlpha v4l2out_config_alpha;V4l2outConfigColorkey v4l2out_config_colorkey;V4l2captureConfig v4l2capture_config;
} IMXV4l2DeviceItftypedef 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的:

<span style="color:#FF0000;">v4l2src->v4l2handle</span> = 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 = <span style="color:#FF0000;">gst_imx_v4l2src_get_device_caps</span> (src);if (caps && filter) {GstCaps *intersection;intersection =<span style="color:#FF0000;">gst_caps_intersect_full</span> (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 = <span style="color:#FF0000;">gst_imx_v4l2_get_caps</span> (v4l2src->v4l2handle);if(!caps) {GST_WARNING_OBJECT (v4l2src, "Can't get caps from device.");}
<span style="color:#FF0000;">//这个函数是重点,通过这个函数来创建caps并设置它们。</span>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; i<map_size; i++) {if (handle->support_format_table[index] == fmt_map[i].v4l2fmt) {if (!caps)caps = <span style="color:#FF0000;">gst_caps_new_empty ()</span>;if (caps) {GstStructure * structure = gst_structure_from_string( \fmt_map[i].caps_str, NULL);<span style="color:#FF0000;">gst_structure_set</span> (structure, "width", G_TYPE_INT, frmsize.discrete.width, NULL);<span style="color:#FF0000;"> gst_structure_set</span> (structure, "height", G_TYPE_INT, frmsize.discrete.height, NULL);<span style="color:#FF0000;"> gst_structure_set</span> (structure, "framerate", GST_TYPE_FRACTION, \frmival.discrete.denominator, frmival.discrete.numerator, NULL);if (handle->is_tvin)<span style="color:#FF0000;"> gst_structure_set </span>(structure, "interlace-mode", G_TYPE_STRING, "interleaved", NULL);<span style="color:#FF0000;">gst_caps_append_structure (caps, structure);</span>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; i<map_size; i++) {if (fmt.pixelformat == fmt_map[i].v4l2fmt) {if (!caps)caps = <span style="color:#000099;">gst_caps_new_empty ();</span>if (caps) {GstStructure * structure = gst_structure_from_string( \fmt_map[i].caps_str, NULL);<span style="color:#000099;"> </span><span style="color:#330099;"><span style="color:#000099;"> gst_structure_set</span> </span>(structure, "width", G_TYPE_INT, frmsize.discrete.width, NULL);<span style="color:#000099;"> gst_structure_set</span> (structure, "height", G_TYPE_INT, frmsize.discrete.height, NULL);<span style="color:#000099;"> gst_structure_set </span>(structure, "framerate", GST_TYPE_FRACTION, \frmival.discrete.denominator, frmival.discrete.numerator, NULL);<span style="color:#000099;"> gst_caps_append_structure (caps, structure);</span>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 = <span style="color:#FF0000;">gst_imx_v4l2_fmt_gst2v4l2</span> (GST_VIDEO_INFO_FORMAT (&info));guint
<span style="color:#FF0000;">gst_imx_v4l2_fmt_gst2v4l2 </span>(GstVideoFormat gstfmt)
{guint v4l2fmt = 0;int i;guint map_size;IMXV4l2FmtMap *fmt_map = <span style="color:#FF0000;">imx_v4l2_get_fmt_map</span>(&map_size);for(i=0; i<map_size; i++) {if (gstfmt == fmt_map[i].gstfmt) {v4l2fmt = fmt_map[i].v4l2fmt;break;}}return v4l2fmt;
}static IMXV4l2FmtMap * <span style="color:#FF0000;">imx_v4l2_get_fmt_map</span>(guint *map_size)
{IMXV4l2FmtMap *fmt_map = NULL;*map_size = 0;if (HAS_IPU()) {fmt_map = g_imxv4l2fmt_maps_IPU;*map_size = sizeof(<span style="color:#FF0000;">g_imxv4l2fmt_maps_IPU</span>)/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 <span style="color:#FF0000;">g_imxv4l2fmt_maps_IPU[]</span> = {{GST_VIDEO_CAPS_MAKE("I420"),<span style="color:#FF0000;"> V4L2_PIX_FMT_YUV420, GST_VIDEO_FORMAT_I420</span>, 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;elsemax_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;}
<p>/* 这一段代码就是上面提到的创建allocator的代码,可以看出来判断allocator == NULL这个标志位,重要的函数是<span style="color:#FF3333;">gst_imx_v4l2_allocator_new </span><span style="color:black;">函数,会通过这个函数</span></p><p><span style="color:black;">来创建allocator</span>,gst_imx_v4l2_allocator_new这个函数在gstimxv4l2allocator.c文件中,这个函数的核心就是g_object_new函数,具体在那个文件中再分析。</p><p><span style="color:black;">同时gst_imx_v4l2_allocator_new</span>这个函数根据IMXV4l2AllocatorContext类型的context来创建allocator,所以这个IMXV4l2AllocatorContext类型对应是</p><p>在gstimxv4l2allocator.h文件中声明。 */</p><p><span style="background:#DDDDDD;"> </span></p><p><span style="background:#DDDDDD;">  </span>if (pool == NULL ||v4l2src->use_v4l2_memory == TRUE) {</p><p><span style="background:#DDDDDD;">    </span>if (pool) {</p><p><span style="background:#DDDDDD;">      </span>gst_object_unref (pool);</p><p><span style="background:#DDDDDD;">    </span>}</p><p><span style="background:#DDDDDD;">    </span>/* no pool, we can make our own */</p><p><span style="background:#DDDDDD;">    </span>GST_DEBUG_OBJECT (v4l2src, "no pool,making new pool");</p><p><span style="background:#DDDDDD;"> </span></p><p><span style="background:#DDDDDD;">    </span>structure = gst_caps_get_structure(v4l2src->old_caps, 0);</p><p><span style="background:#DDDDDD;"> </span></p><p><span style="background:#DDDDDD;">    </span>if (gst_structure_has_name (structure,"video/x-bayer")) {</p><p><span style="background:#DDDDDD;">      </span>size = GST_ROUND_UP_4 (v4l2src->w) *v4l2src->h;</p><p><span style="background:#DDDDDD;">      </span>pool = gst_buffer_pool_new ();</p><p><span style="background:#DDDDDD;">    </span>} else</p><p><span style="background:#DDDDDD;">      </span>pool = gst_video_buffer_pool_new ();</p><p><span style="background:#DDDDDD;"> </span>}</p><p>/* 这一段代码是上面提到的创建pool的代码。首先从 v4l2src->old_caps里面获取到 structure,然后根据 structure里面的名字来选择使用 gst_buffer_pool_new函数</p><p>还是gst_video_buffer_pool_new函数来创建一个新pool。这两个函数都在《gstreamer插件所用函数整理》中介绍。 */</p><p><span style="background:#DDDDDD;"> </span></p><p><span style="background:#DDDDDD;"> </span></p><p><span style="background:#DDDDDD;"> </span>v4l2src->pool = gst_object_ref (pool);</p><p>//上面提到的 v4l2src->pool标志位就是在这里设置的。</p><p><span style="background:#DDDDDD;"> </span></p><p><span style="background:#DDDDDD;">  </span>max = min += DEFAULT_FRAMES_IN_V4L2_CAPTURE \</p><p><span style="background:#DDDDDD;">        </span>+ v4l2src->frame_plus;</p><p><span style="background:#DDDDDD;">  </span>if (min > 10)</p><p><span style="background:#DDDDDD;">    </span>max = min = 10;</p><p><span style="background:#DDDDDD;">  </span>v4l2src->actual_buf_cnt = min;</p><p>//设置max和min的值。</p><p><span style="background:#DDDDDD;"> </span></p><p><span style="background:#DDDDDD;">  </span>/* now configure */</p><p><span style="background:#DDDDDD;">  </span>config = gst_buffer_pool_get_config (pool);</p><p> </p><p><span style="background:#DDDDDD;">  </span>if (!gst_buffer_pool_config_has_option(config, \</p><p><span style="background:#DDDDDD;">        </span>GST_BUFFER_POOL_OPTION_VIDEO_META)) {</p><p><span style="background:#DDDDDD;">    </span>gst_buffer_pool_config_add_option (config,</p><p><span style="background:#DDDDDD;">        </span>GST_BUFFER_POOL_OPTION_VIDEO_META);</p><p><span style="background:#DDDDDD;">  </span>}</p><p><span style="background:#DDDDDD;"> </span></p><p><span style="background:#DDDDDD;">  </span>gst_buffer_pool_config_set_params (config,outcaps, size, min, max);</p><p><span style="background:#DDDDDD;">  </span>gst_buffer_pool_config_set_allocator (config,allocator, &params);</p><p><span style="background:#DDDDDD;"> </span>gst_buffer_pool_set_config (pool, config);</p><p>/* 上面这段代码是GstBufferPool相关的API,gst_buffer_pool_get_config 函数是获取当前的配置(config),之后通过 gst_buffer_pool_config_set_params函数,</p><p>来将outcaps, size, min, max的值设置到config中,继续通过 gst_buffer_pool_config_set_allocator函数来设置config里面与 allocator, &params相关的参数,</p><p>最后通过gst_buffer_pool_set_config函数,来将新设置好的config写到poll中。这些函数都在《gstreamer插件所用函数整理》中介绍。 */</p><p><span style="background:#DDDDDD;"> </span></p><p><span style="background:#DDDDDD;">  </span>if (update_allocator)</p><p><span style="background:#DDDDDD;">    </span>gst_query_set_nth_allocation_param (query,0, allocator, &params);</p><p><span style="background:#DDDDDD;">  </span>else</p><p><span style="background:#DDDDDD;">    </span>gst_query_add_allocation_param (query,allocator, &params);</p><p><span style="background:#DDDDDD;">  </span>if (allocator)</p><p><span style="background:#DDDDDD;">    </span>gst_object_unref (allocator);</p><p><span style="background:#DDDDDD;"> </span></p><p><span style="background:#DDDDDD;">  </span>if (update_pool)</p><p><span style="background:#DDDDDD;">    </span>gst_query_set_nth_allocation_pool (query,0, pool, size, min, max);</p><p><span style="background:#DDDDDD;">  </span>else</p><p><span style="background:#DDDDDD;">    </span>gst_query_add_allocation_pool (query, pool,size, min, max);</p><p><span style="background:#DDDDDD;"> </span></p><p><span style="background:#DDDDDD;">  </span>if (pool)</p><p><span style="background:#DDDDDD;">    </span>gst_object_unref (pool);</p><p><span style="background:#DDDDDD;"> </span></p><p><span style="background:#DDDDDD;">  </span>return TRUE;</p><p><span style="background:#DDDDDD;">}</span></p> 

有关这一节的知识,可以查看《插件编写手册》中的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 <span style="color:#FF0000;">gst_imx_v4l2capture_config</span> (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;<span style="color:#FF0000;">IMXV4l2DeviceItf dev_itf;</span>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;<span style="color:#FF0000;">V4l2captureConfig v4l2capture_config;</span>
} 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)) {<span style="color:#FF0000;">handle->dev_itf.v4l2capture_config = (V4l2captureConfig)gst_imx_v4l2capture_config_camera;</span>handle->support_format_table = g_camera_format_IPU;} else if (!strncmp (chip.match.name, MXC_V4L2_CAPTURE_TVIN_NAME, 3)) {<span style="color:#FF0000;"> handle->dev_itf.v4l2capture_config = (V4l2captureConfig)gst_imx_v4l2capture_config_camera;</span>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 (<span style="color:#FF0000;">ioctl (handle->v4l2_fd, VIDIOC_S_INPUT, &input)</span> < 0) {GST_ERROR ("VIDIOC_S_INPUT failed");return -1;}return <span style="color:#FF0000;">gst_imx_v4l2capture_config_pxp </span>(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 (<span style="color:#FF0000;">gst_imx_v4l2capture_config_usb_camera</span> (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 (<span style="color:#FF0000;">ioctl (handle->v4l2_fd, VIDIOC_ENUM_FRAMESIZES, &fszenum)</span> >= 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 (<span style="color:#FF0000;">ioctl (handle->v4l2_fd, VIDIOC_S_PARM, &parm)</span> < 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 (<span style="color:#FF0000;">ioctl (handle->v4l2_fd, VIDIOC_S_FMT, &v4l2_fmt)</span> < 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 (<span style="color:#FF0000;">ioctl(handle->v4l2_fd, VIDIOC_REQBUFS, &buf_req)</span> < 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 (<span style="color:#FF0000;">gst_imx_v4l2_register_buffer (v4l2src->v4l2handle, memblk) </span>< 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 (<span style="color:#FF0000;">ioctl(handle->v4l2_fd, VIDIOC_QUERYBUF, v4l2buf) </span>< 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 (<span style="color:#FF0000;">gst_imx_v4l2_queue_v4l2memblk (v4l2handle, memblk, flags)</span> < 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;elsev4l2buf->field = V4L2_FIELD_INTERLACED_BT;}if (flags & GST_VIDEO_FRAME_FLAG_ONEFIELD) {if (flags & GST_VIDEO_FRAME_FLAG_TFF)v4l2buf->field = V4L2_FIELD_TOP;elsev4l2buf->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; i<handle->streamon_count; i++) {if (<span style="color:#FF0000;">imx_v4l2_do_queue_buffer (handle, handle->v4lbuf_queued_before_streamon[i])</span> < 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函数中,核心就是<span style="color:#FF0000;">VIDIOC_QBUF ioctl调用</span>。if (<span style="color:#FF0000;">ioctl (handle->v4l2_fd,  VIDIOC_STREAMON, &handle->type)</span> < 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 (<span style="color:#FF0000;">gst_imx_v4l2_dequeue_v4l2memblk</span> (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 (<span style="color:#FF0000;">ioctl (handle->v4l2_fd, VIDIOC_DQBUF, &v4l2buf)</span> < 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 (<span style="color:#FF0000;">gst_imx_v4l2_allocator_cb (v4l2src, &buffer_count) </span>< 0) {GST_ERROR_OBJECT (v4l2src, "gst_imx_v4l2_allocator_cb failed.");return GST_FLOW_ERROR;}
//之前已经分析过了,在 gst_imx_v4l2_allocator_cb中,会执行VIDIOC_REQBUFS 这个ioctl调用。ret = <span style="color:#FF0000;">gst_imx_v4l2src_register_buffer (v4l2src)</span>;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 (<span style="color:#FF0000;">gst_imx_v4l2_queue_gstbuffer (v4l2src->v4l2handle, buffer, flags)</span> < 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 (<span style="color:#FF0000;">gst_imx_v4l2_dequeue_gstbuffer (v4l2src->v4l2handle, buf, &flags)</span> < 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 = <span style="color:#FF0000;">gst_imx_v4l2src_acquire_buffer </span>(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;elsedelay = 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;elsetimestamp = 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这个库文件。同时对于现在这些函数接口很不熟悉。

4. gstimxv4l2src.c源码分析相关推荐

  1. 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析

    目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...

  2. SpringBoot-web开发(四): SpringMVC的拓展、接管(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) SpringBoot-web开发(二): 页面和图标定制(源码分析) SpringBo ...

  3. SpringBoot-web开发(二): 页面和图标定制(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) 目录 一.首页 1. 源码分析 2. 访问首页测试 二.动态页面 1. 动态资源目录t ...

  4. SpringBoot-web开发(一): 静态资源的导入(源码分析)

    目录 方式一:通过WebJars 1. 什么是webjars? 2. webjars的使用 3. webjars结构 4. 解析源码 5. 测试访问 方式二:放入静态资源目录 1. 源码分析 2. 测 ...

  5. Yolov3Yolov4网络结构与源码分析

    Yolov3&Yolov4网络结构与源码分析 从2018年Yolov3年提出的两年后,在原作者声名放弃更新Yolo算法后,俄罗斯的Alexey大神扛起了Yolov4的大旗. 文章目录 论文汇总 ...

  6. ViewGroup的Touch事件分发(源码分析)

    Android中Touch事件的分发又分为View和ViewGroup的事件分发,View的touch事件分发相对比较简单,可参考 View的Touch事件分发(一.初步了解) View的Touch事 ...

  7. View的Touch事件分发(二.源码分析)

    Android中Touch事件的分发又分为View和ViewGroup的事件分发,先来看简单的View的touch事件分发. 主要分析View的dispatchTouchEvent()方法和onTou ...

  8. MyBatis原理分析之四:一次SQL查询的源码分析

    上回我们讲到Mybatis加载相关的配置文件进行初始化,这回我们讲一下一次SQL查询怎么进行的. 准备工作 Mybatis完成一次SQL查询需要使用的代码如下: Java代码   String res ...

  9. [转]slf4j + log4j原理实现及源码分析

    slf4j + log4j原理实现及源码分析 转载于:https://www.cnblogs.com/jasonzeng888/p/6051080.html

  10. Spark源码分析之七:Task运行(一)

    在Task调度相关的两篇文章<Spark源码分析之五:Task调度(一)>与<Spark源码分析之六:Task调度(二)>中,我们大致了解了Task调度相关的主要逻辑,并且在T ...

最新文章

  1. MapReduce基础开发之五分布式下载ftp文件到本地再迁移到hdfs
  2. delphi接口基本学习摘录
  3. VTK:Parallel之ExodusIIWriter
  4. 将外部准备好的sqlite导入到项目当中
  5. Entity framework WhereInExtension
  6. Qt图形界面编程入门(基本窗口及控件)
  7. android:configchanges的作用,将uiMode附加到android:configChanges实际做什么?
  8. leetcode - 712. 两个字符串的最小ASCII删除和
  9. 用闭包方式实现点击a标签弹也索引值
  10. 喧嚣之后,BAT都在车联网领域落下了哪些棋子?
  11. 厦大计算机学院2018夏令营6,2018年厦门大学建筑与土木工程学院保研夏令营通知...
  12. Vue框架的初识入门
  13. keep alive是什么?
  14. 常见交通工具英语单词
  15. Postgresql垃圾回收Vacuum优化手册
  16. python字符串方法replace_Python字符串的方法
  17. Python解释器安装教程
  18. IIS发布网站进行访问时提示权限不足的简单解决方法
  19. 2022世界人工智能大会 “智慧金融与数字员工”分论坛在沪成功举办
  20. 如何解决VC2019中:error C3861: “xxxx”: 找不到标识符

热门文章

  1. latex不显示doi号
  2. mysql 占比函数_MySQL通过分组计算百分比
  3. 摄影基础知识——焦点
  4. SAP 报表设计器相关TCODE
  5. HDU2825 Wireless Password【AC自动机 + DP】
  6. 流量卡之家:物联网和人工智能如何实现环境可持续性
  7. windows10如何注销
  8. 80端口和443端口的作用
  9. 微软和美国航空航天局(NASA)强强联手,推出Python免费课程
  10. 卢森堡携手欧洲航天局,在大公国建立独一无二的“欧洲太空资源创新中心”