开源相机管理库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
函数,在那个例子中callback
和user_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_BUFFER
和ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE
。最后在线程退出时最后调用一次stream_callback
,并向type
传入ARV_STREAM_CALLBACK_TYPE_EXIT
。
回调函数中使用switch结构是为了根据不同的type
参数值执行不同的操作,以实现在流线程的不同时间点完成用户自定义的相关操作。