简介

本文针对官方例程中的:02-multiple-acquisition-signal做简单的讲解。并简单介绍其中调用的g_main_loop_newg_main_loop_rung_main_loop_quitg_signal_connectarv_stream_set_emit_signals

aravis版本:0.8.31

操作系统:ubuntu-20.04

gcc版本:9.4.0

例程代码

这段代码使用Aravis的API,控制相机连续采集,并通过GLib的事件循环机制和GObject的信号系统异步地获取10个图像,主要操作步骤如下:

  • 连接相机
  • 设置采集模式为连续采集
  • 创建流对象,并向流对象的buffer池中添加buffer
  • 设置流对象信号回调函数,并使能流对象信号发射
  • 开始采集
  • 启动事件循环
  • 获取10张图像后关闭事件循环
  • 关闭流对象信号发射,释放资源

连续采集multiple-acquisition-main-thread不同的是,本例中使用GMainLoop(GLib的事件循环)来处理异步事件,图像获取过程是异步进行的。

/* SPDX-License-Identifier:Unlicense */

/* Aravis header */
#include <arv.h>
/* Standard headers */
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include "LogManager.h" typedef struct {
GMainLoop *main_loop;
guint32 counter;
} AppData; void new_buffer_cb (ArvStream *stream, void *user_data)
{
ArvBuffer *buffer;
AppData *app_data = static_cast<AppData*>(user_data); buffer = arv_stream_pop_buffer (stream);
PAW_INFO("Acquired"<<arv_buffer_get_image_width(buffer)<<"x"<<arv_buffer_get_image_height(buffer)<< " buffer"); arv_stream_push_buffer (stream, buffer); app_data->counter++;
if (app_data->counter == 10)
g_main_loop_quit (app_data->main_loop);
} int main (int argc, char **argv)
{
ArvCamera *camera;
AppData app_data;
GError *error = NULL; app_data.main_loop = g_main_loop_new (NULL, FALSE);
app_data.counter = 0; //连接相机
camera = arv_camera_new (NULL, &error); if (ARV_IS_CAMERA (camera)) {
ArvStream *stream = NULL; printf ("Found camera '%s'\n", arv_camera_get_model_name (camera, NULL));
//设置采集模式
arv_camera_set_acquisition_mode (camera, ARV_ACQUISITION_MODE_CONTINUOUS, &error);
//创建流对象
if (error == NULL)
stream = arv_camera_create_stream (camera, NULL, NULL, &error); if (ARV_IS_STREAM (stream)) {
int i;
size_t payload; //获取有效负载大小
payload = arv_camera_get_payload (camera, &error);
if (error == NULL) {
//设置流对象的缓冲区数量
for (i = 0; i < 5; i++)
arv_stream_push_buffer (stream, arv_buffer_new (payload, NULL));
} //设置流对象信号回调函数
g_signal_connect (stream, "new-buffer", G_CALLBACK (new_buffer_cb), &app_data);
//设置流对象发射信号
//当流对象接收到新的缓冲区时,发射new-buffer信号
arv_stream_set_emit_signals (stream, TRUE); //开始采集
if (error == NULL)
arv_camera_start_acquisition (camera, &error); //启动主循环
PAW_INFO("start main loop");
if (error == NULL)
g_main_loop_run (app_data.main_loop);
PAW_INFO("start main loop end"); if (error == NULL)
//停止采集
arv_camera_stop_acquisition (camera, &error); arv_stream_set_emit_signals (stream, FALSE);
g_clear_object (&stream);
} g_clear_object (&camera);
} g_main_loop_unref (app_data.main_loop); if (error != NULL) {
/* En error happened, display the correspdonding message */
printf ("Error: %s\n", error->message);
return EXIT_FAILURE;
} return EXIT_SUCCESS;
}

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

运行结果:

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

函数说明

g_main_loop_new

简介:GLib的API,构造GMainLoop对象

GMainLoop* g_main_loop_new(GMainContext* context, gboolean is_running)

其中:

[in]context:一个GMainContext,如果为NULL,将使用全局默认的main上下文

[in]is_running:设置为TRUE表示循环正在运行。这不是很重要,因为只要后面调用g_main_loop_run()就会将其设置为TRUE。

g_main_loop_run

简介:GLib的API,运行一个主循环,直到在循环中调用g_main_loop_quit()

void g_main_loop_run(GMainLoop* loop)

g_main_loop_quit

简介:GLib的API,停止GMainLoop的运行。任何使用g_main_loop_run()开启的循环都将返回。

void g_main_loop_quit(GMainLoop* loop)

g_signal_connect

简介:GObject的宏,用于将信号处理器连接到特定对象的某个信号上。当一个信号被发出时,处理器将被同步调用。

#define g_signal_connect(instance, detailed_signal, c_handler, data)

arv_stream_set_emit_signals

简介:控制流对象信号发射。默认情况下流对象发射信号是禁用的,因为信号发射在性能上有一定开销而且在某些应用场景下是不需要的。

void arv_stream_set_emit_signals(ArvStream* stream, gboolean emit_signals)

Available since: 0.2.0

Q&A

回调函数的同步调用与异步调用

观察程序运行时的日志,可以发现new_buffer_cb的运行并不是在主线程中。

但是按照g_signal_connect的描述,回调函数应该是被同步调用,也就是说new_buffer_cb理论上应该在主线程被调用。

后来查看文档发现,在GObject的信号系统中,处理器的调用是同步的。当信号发射时,其关联的所有处理器会都会在发射信号的线程中按照它们被连接的顺序依次执行。

所以正确的应该是:处理器是在信号发射的线程被调用,而不是在处理器被注册时的线程。

在本例中,预定义的信号new-buffer的处理器new_buffer_cb被绑定在流对象上,这意味着每当流对象有一个新的buffer可用时,这个信号就会被发射,随后new_buffer_cb就被调用。而官方文档钟提到,流对象内部是使用一个单独的线程来监听数据的到达,因此信号是在这个单独的线程被发射的,也就是说回调函数也是在这个单独的线程被调用的,而不是在主线程中。

帧丢失问题

官方给出的例程中,先启动的相机采集,然后才开始事件循环。我认为这样的话会存在丢帧的问题,因为在事件循环启动并准备好处理接收到的图像之前,相机可能已经开始发送数据,如果数据流的缓冲不足或处理不及时,新的图像数据可能会覆盖还未处理的旧数据,或者直接被丢弃。

所以我对代码做了一些改动,改变调用顺序为先开启事件循环,然后再启动相机的采集,代码如下:

/* SPDX-License-Identifier:Unlicense */
/* Aravis header */
#include <arv.h>
/* Standard headers */
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include "LogManager.h" typedef struct {
GMainLoop *main_loop;
guint32 counter;
ArvCamera *camera;
} AppData; gboolean start_acquisition_cb(gpointer user_data)
{
AppData *app_data = static_cast<AppData*>(user_data);
GError *error = NULL; arv_camera_start_acquisition(app_data->camera, &error); if (error != NULL) {
printf("Error: %s\n", error->message);
g_main_loop_quit(app_data->main_loop);
}
//只调用一次
return FALSE;
} ... int main (int argc, char **argv)
{
AppData app_data;
GError *error = NULL; app_data.main_loop = g_main_loop_new (NULL, FALSE);
app_data.counter = 0; app_data.camera = arv_camera_new (NULL, &error); if (ARV_IS_CAMERA (app_data.camera)) {
ArvStream *stream = NULL; printf ("Found camera '%s'\n", arv_camera_get_model_name (app_data.camera, NULL)); arv_camera_set_acquisition_mode (app_data.camera, ARV_ACQUISITION_MODE_CONTINUOUS, &error); if (error == NULL)
stream = arv_camera_create_stream (app_data.camera, NULL, NULL, &error); if (ARV_IS_STREAM (stream)) {
int i;
size_t payload; payload = arv_camera_get_payload (app_data.camera, &error);
if (error == NULL) {
for (i = 0; i < 5; i++)
arv_stream_push_buffer (stream, arv_buffer_new (payload, NULL));
} g_signal_connect (stream, "new-buffer", G_CALLBACK (new_buffer_cb), &app_data); PAW_INFO("emit signals");
arv_stream_set_emit_signals (stream, TRUE);
PAW_INFO("emit signals end"); /* if (error == NULL)
arv_camera_start_acquisition (camera, &error); */ //在主循环开始后尽快执行一次start_acquisition_cb
g_idle_add(start_acquisition_cb, &app_data); PAW_INFO("start main loop");
if (error == NULL)
g_main_loop_run (app_data.main_loop);
PAW_INFO("start main loop end");
if (error == NULL)
arv_camera_stop_acquisition (app_data.camera, &error); arv_stream_set_emit_signals (stream, FALSE); g_clear_object (&stream);
} g_clear_object (&app_data.camera);
} g_main_loop_unref (app_data.main_loop); if (error != NULL) {
printf ("Error: %s\n", error->message);
return EXIT_FAILURE;
} return EXIT_SUCCESS;
}

开源相机管理库Aravis例程学习(四)——multiple-acquisition-signal的更多相关文章

  1. Git---初入开源代码管理库的学习过程003

    Git常用命令总结 上接<Git 初入开源代码管理库的学习过程>学了一周Git,基本有了个认识.每一位比我厉害的,都是大牛,网上找了几篇博客和教材(感谢你们),边学习边实践用了四天,写笔记 ...

  2. Android Fresco (Facebook开源的图片加载管理库)

    Fresco是Facebook开源的一个图片加载和管理库. 这里是Fresco的GitHub网址. 同类型的开源库市面有非常多,比如Picasso, Universal Image Loader, G ...

  3. C++的开源跨平台日志库glog学习研究(二)--宏的使用

    上一篇从整个工程上简单分析了glog,请看C++的开源跨平台日志库glog学习研究(一),这一篇对glog的实现代码入手,比如在其源码中以宏的使用最为广泛,接下来就先对各种宏的使用做一简单分析. 1. ...

  4. 值得学习的C语言开源项目和库

    收集一些C/C++相关的源码,如有更高效的库,请提醒我 补充上去 C/C++相关交流Q群 1414577 - 1. Webbench Webbench是一个在linux下使用的非常简单的网站压测工具. ...

  5. C++的开源跨平台日志库glog学习研究(三)--杂项

    在前面对glog分别做了两次学习,请看C++的开源跨平台日志库glog学习研究(一).C++的开源跨平台日志库glog学习研究(二)--宏的使用,这篇再做个扫尾工作,算是基本完成了. 编译期断言 动态 ...

  6. 微软企业库5.0 学习之路——第四步、使用缓存提高网站的性能(EntLib Caching)

    首先先补习下企业库的Caching Application Block的相关知识: 1.四大缓存方式,在Caching Application Block中,主要提供以下四种保存缓存数据的途径,分别是 ...

  7. 59.Android开源项目及库 (转)

    转载 : https://github.com/Tim9Liu9/TimLiu-Android?hmsr=toutiao.io&utm_medium=toutiao.io&utm_so ...

  8. iOS、mac开源项目及库汇总

    原文地址:http://blog.csdn.net/qq_26359763/article/details/51076499    iOS每日一记------------之 中级完美大整理 iOS.m ...

  9. Android开源项目及库搜集

    TimLiu-Android 自己总结的Android开源项目及库. github排名 https://github.com/trending,github搜索:https://github.com/ ...

  10. iOS -- 开源项目和库

    TimLiu-iOS 目录 UI 下拉刷新 模糊效果 AutoLayout 富文本 图表 表相关与Tabbar 隐藏与显示 HUD与Toast 对话框 其他UI 动画 侧滑与右滑返回手势 gif动画 ...

随机推荐

  1. 记录--三分钟打造自己专属的uni-app工具箱

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 介绍 可曾想过我们每次创建新项目,或者换地方写程序,都要把之前写过的工具类找出来又要复制粘贴一遍有些麻烦,尤其是写uni-app自定义模板 ...

  2. Tableau 绘制圆环图

    一.对应数据如下所示 二.打开tableau连接对应Excel数据源,将记录数字段连续拖动两次到行,显示设置按整个视图显示,标记里面设置按饼图显示 三.设置两个值按度量值平均值显示,并调整第一个图稍微 ...

  3. 生产环境ES的一个持续转换(continuous transform)报错,问题排查

    背景:有一天突然发现,业务统计的一个数据异常,遂立即排查原因,查看后发现一个mode是continuous 的transform是stop状态:日志如下 报错时间:2023-03-26 14:05:2 ...

  4. Linux服务器下启动和关闭node

    首先将node工程的代码和node_modules目录上传到服务器的某一个目录下 1.用forever 进行管理 前提:linux下已经安装了node npm install -g forever / ...

  5. KingbaseES查找慢查询语句和阻塞会话

    在处理数据库性能问题时,识别和分析慢查询及阻塞会话是至关重要的步骤.数据库管理员和开发人员常常需要依赖特定的工具和查询语句来追踪这些性能瓶颈. 当数据库响应变慢或出现处理延迟时,第一步通常是查找那些执 ...

  6. Android弹出UI界面---Toast

    1.默认展示 1 // 第一个参数:当前的上下文环境.可用getApplicationContext()或this 2 // 第二个参数:要显示的字符串.也可是R.string中字符串ID 3 // ...

  7. 强烈推荐:2024 年12款 Visual Studio 亲测、好用、优秀的工具,AI插件等

    工具类扩展 1. ILSpy 2022 (免费) ILSpy 是 ILSpy 开源反编译器的 Visual Studio 扩展. 是一款开源.免费的.且适用于.NET平台反编译[C#语言编写的程序和库 ...

  8. OpenHarmony支持HDMI接口声卡适配说明

      高清多媒体接口(High Definition Multimedia Interface,HDMI  )是一种全数字化视频和声音发送接口,可以发送未压缩的音频及视频信号.HDMI可用于机顶盒.DV ...

  9. 直播预告丨OpenHarmony标准系统多媒体子系统之音频解读

    今晚19点,OpenHarmony开源开发者成长计划知识赋能第五期"掌握OpenHarmony多媒体的框架原理"的第四节直播课,即将开播! 深开鸿资深技术专家苑春鸽老师,将在Ope ...

  10. 轻松上手Jackjson(珍藏版)

    写在前面 虽然现在市面上有很多优秀的json解析库,但 Spring默认采用Jackson解析Json. 本文将通过一系列通俗易懂的代码示例,带你逐步掌握 Jackson 的基础用法.进阶技巧以及在实 ...