4. gstimxv4l2src.c源码分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yanbixing123/article/details/52970843

由于这个文件中会调用到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的:
<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;
      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;
  }
<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;
    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; 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_QBUFVIDIOC_STREAMON这两个ioctl调用。

 

 

小总结:

gst_imx_v4l2_queue_gstbuffer函数中主要的工作是做了V4L2框架中的VIDIOC_QBUFVIDIOC_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;
    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这个库文件。同时对于现在这些函数接口很不熟悉。


























猜你喜欢

转载自blog.csdn.net/yanbixing123/article/details/52970843