开源相机管理库Aravis例程学习(三)——注册回调multiple-acquisition-callback

简介

本文针对官方例程中的:02-multiple-acquisition-callback做简单的讲解。

aravis版本:0.8.31
操作系统:ubuntu-20.04
gcc版本:9.4.0

例程代码

这段代码使用Aravis的API,控制相机连续采集,并异步地在回调函数中获取10个有效图像,主要操作步骤如下:

  • 连接相机
  • 设置采集模式为连续采集
  • 创建流对象(同时注册回调),并向流对象的buffer池中添加buffer
  • 开始采集
  • 获取10张有效图像后停止采集
  • 释放资源

连续采集multiple-acquisition-main-thread不同的是,本例中图像获取过程以及停止采集条件的改变都是异步进行的(在回调函数中)。

/* SPDX-License-Identifier:Unlicense */

/* Aravis header */
#include <arv.h>
/* Standard headers */
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include "LogManager.h"

//用于回调函数中传递和储存流的状态和计数器
typedef struct {
	ArvStream *stream;
	int counter;
	gboolean done;
} ArvStreamCallbackData;

//回调函数
//根据不同的回调类型处理视频流事件
static void stream_callback (void *user_data, ArvStreamCallbackType type, ArvBuffer *buffer)
{
	ArvStreamCallbackData *callback_data = (ArvStreamCallbackData *) user_data;

	/* 回调函数内尽量不做非必要的耗时操作 */
	switch (type) {
		case ARV_STREAM_CALLBACK_TYPE_INIT:
			PAW_INFO("ARV_STREAM_CALLBACK_TYPE_INIT");
			break;
		case ARV_STREAM_CALLBACK_TYPE_START_BUFFER:
			PAW_INFO("ARV_STREAM_CALLBACK_TYPE_START_BUFFER");
			break;
		case ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE:
			PAW_INFO("ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE");
			//从buffer池中取出buffer
			g_assert (buffer == arv_stream_pop_buffer(callback_data->stream));
			g_assert (buffer != NULL);

			//检索10个有效buffer
			if (callback_data->counter < 10) {
				if (arv_buffer_get_status(buffer) == ARV_BUFFER_STATUS_SUCCESS)
					PAW_INFO("Acquired"<<arv_buffer_get_image_width(buffer)<<"x"<<arv_buffer_get_image_height(buffer)<< " buffer");
				arv_stream_push_buffer(callback_data->stream, buffer);
				callback_data->counter++;
			} else {
				callback_data->done = TRUE;
			}

			break;
		case ARV_STREAM_CALLBACK_TYPE_EXIT:
			PAW_INFO("ARV_STREAM_CALLBACK_TYPE_EXIT");
			/* Stream thread ended */
			break;
	}
}

/*
 * Connect to the first available camera, then acquire 10 buffers.
 */
int main (int argc, char **argv)
{
	CLogManager& p_log_instance = CLogManager::GetInstance();

	ArvCamera *camera;
	GError *error = NULL;

	//连接相机
	camera = arv_camera_new ("192.168.6.63", &error);

	if (ARV_IS_CAMERA (camera)) {
		ArvStreamCallbackData callback_data;

		printf ("Found camera '%s'\n", arv_camera_get_model_name (camera, NULL));
		//设置相机采集模式为连续采集
		arv_camera_set_acquisition_mode (camera, ARV_ACQUISITION_MODE_CONTINUOUS, &error);

        //初始化回调数据
		callback_data.counter = 0;
		callback_data.done = FALSE;
		callback_data.stream = NULL;

		if (error == NULL) {
			//创建流对象,注册回调
			PAW_INFO("create stream");
			callback_data.stream = arv_camera_create_stream (camera, stream_callback, &callback_data, &error);
			PAW_INFO("create stream end");
		}
			
		if (ARV_IS_STREAM (callback_data.stream)) {
			int i;
			size_t payload;

			/* Retrieve the payload size for buffer creation */
			//从相机对象中获取图像负载大小(每个图像的字节大小)
			payload = arv_camera_get_payload (camera, &error);
			PAW_INFO("payload:" << payload);
			if (error == NULL) {
				/* Insert some buffers in the stream buffer pool */
				//双缓冲
				for (i = 0; i < 2; i++)
					arv_stream_push_buffer (callback_data.stream, arv_buffer_new (payload, NULL));
			}

			if (error == NULL)
				/* Start the acquisition */
				arv_camera_start_acquisition (camera, &error);

			if (error == NULL) {
				while (!callback_data.done) {
					usleep (1000);
				}
			}


			if (error == NULL)
				/* Stop the acquisition */
				arv_camera_stop_acquisition (camera, &error);

			/* Destroy the stream object */
			g_clear_object (&callback_data.stream);
		}

		/* Destroy the camera instance */
		PAW_INFO("destroy stream");
		g_clear_object (&camera);
		PAW_INFO("destroy stream end");
	}

	if (error != NULL) {
		/* En error happened, display the correspdonding message */
		printf ("Error: %s\n", error->message);
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}

注:PAW_INFO是我自定义的用于打印日志的宏

运行结果:

其中<>之间的是线程号。

arv_camera_create_stream

连续采集multiple-acquisition-main-thread中我们简单介绍了arv_camera_create_stream函数,在那个例子中callbackuser_data都被设置为NULL,表示不注册回调。而在本例中callback注册了一个我们自定义的函数stream_callback。至于stream_callback中为什么为switch结构我们在后面的讨论中会给出回答。

static void stream_callback (void *user_data, ArvStreamCallbackType type, ArvBuffer *buffer)
{
	ArvStreamCallbackData *callback_data = (ArvStreamCallbackData *) user_data;

	switch (type) {
		case ARV_STREAM_CALLBACK_TYPE_INIT:
			...
			break;
		case ARV_STREAM_CALLBACK_TYPE_START_BUFFER:
			...
			break;
		case ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE:
			...
			break;
		case ARV_STREAM_CALLBACK_TYPE_EXIT:
			...
			break;
	}
}

ArvStreamCallbackType

简介:一个枚举类,描述了流回调函数被调用的时间点。

typedef enum {
	ARV_STREAM_CALLBACK_TYPE_INIT,
	ARV_STREAM_CALLBACK_TYPE_EXIT,
	ARV_STREAM_CALLBACK_TYPE_START_BUFFER,
	ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE
} ArvStreamCallbackType;

ArvStreamCallback

简介:ArvStreamCallback是一个函数指针类型,用于在实例化流对象时注册回调函数。

typedef void(* ArvStreamCallback) (
  void* user_data,
  ArvStreamCallbackType type,
  ArvBuffer* buffer
)

它在四种情况下会被调用:
①流接收线程的初始化时(只会被调用一次,对应type为ARV_STREAM_CALLBACK_TYPE_INIT
②流接收线程的终止时(只会被调用一次,对应type为ARV_STREAM_CALLBACK_TYPE_EXIT
③每一个buffer从缓冲队列被开始取出时(对应type为ARV_STREAM_CALLBACK_TYPE_START_BUFFER
④每一个buffer从缓冲队列中取出完毕时(无论成功与否,对应type为ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE

现在回答关于stream_callback中的switch结构的问题:

在调用arv_camera_create_stream 注册回调完成后,会立即开启一个流接收线程,用于数据接收。

arv_camera_create_stream (camera, stream_callback, &callback_data, &error);

在这个流线程初始化时,会调用stream_callback并向type传入ARV_STREAM_CALLBACK_TYPE_INIT。然后是在开启采集之后,会对每一帧满足上述情况③和情况④的图像,再调用stream_callback,并分别向type传入ARV_STREAM_CALLBACK_TYPE_START_BUFFERARV_STREAM_CALLBACK_TYPE_BUFFER_DONE。最后在线程退出时最后调用一次stream_callback,并向type传入ARV_STREAM_CALLBACK_TYPE_EXIT

回调函数中使用switch结构是为了根据不同的type参数值执行不同的操作,以实现在流线程的不同时间点完成用户自定义的相关操作。

posted @ 2024-04-15 09:29  paw5zx  阅读(122)  评论(0编辑  收藏  举报