RealSense SDK源码分析(1) rs2::context类

RealSense SDK源码分析(1) rs2::context类

High level API rs2::context中,核心的数据为rs2_context结构体。rs2::context中最为核心的query_devices()、query_all_sensors()等函数,均为调用了次级API(rs_xxx_xxx()一类的函数)。对这些函数来说,其输入是对应的次级结构体或类rs2_xxx。rs2_context即为这些次级结构的一员。

与rs2_context相关的次级API在rs_context.h和rs.cpp中实现。这些次级API,从本质上说,均调用了rs_context结构体中层级更低的librealsense::context类中的方法。librealsense::context类即是rs2::context对外API操作中,最低层的一级封装。

rs2_context包含了librealsense::context类的共享指针:

1
2
3
4
5
struct rs2_context
{
~rs2_context() { ctx->stop(); }
std::shared_ptr<librealsense::context> ctx;
};

因此,rs2::context所有的具体操作,均调用了librealsense::context类中所对应的方法。

rs::context与librealsense::context之间的关系如下所示:

librealsense::context类

该类如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class context : public std::enable_shared_from_this<context>
{
public:
explicit context( backend_type type );

void stop() { _device_watcher->stop(); }
~context();
std::vector<std::shared_ptr<device_info>> query_devices(int mask) const;
const platform::backend& get_backend() const { return *_backend; }

uint64_t register_internal_device_callback(devices_changed_callback_ptr callback);
void unregister_internal_device_callback(uint64_t cb_id);
void set_devices_changed_callback(devices_changed_callback_ptr callback);

std::vector<std::shared_ptr<device_info>> create_devices(platform::backend_device_group devices,
const std::map<std::string, std::weak_ptr<device_info>>& playback_devices, int mask) const;

std::shared_ptr<playback_device_info> add_device(const std::string& file);
void remove_device(const std::string& file);

void add_software_device(std::shared_ptr<device_info> software_device);

private:
void on_device_changed(platform::backend_device_group old,
platform::backend_device_group curr,
const std::map<std::string, std::weak_ptr<device_info>>& old_playback_devices,
const std::map<std::string, std::weak_ptr<device_info>>& new_playback_devices);
void raise_devices_changed(const std::vector<rs2_device_info>& removed, const std::vector<rs2_device_info>& added);
void start_device_watcher();

std::shared_ptr<platform::backend> _backend;
std::shared_ptr<platform::device_watcher> _device_watcher;

std::map<std::string, std::weak_ptr<device_info>> _playback_devices;
std::map<uint64_t, devices_changed_callback_ptr> _devices_changed_callbacks;

devices_changed_callback_ptr _devices_changed_callback;
std::map<int, std::weak_ptr<const stream_interface>> _streams;
std::map<int, std::map<int, std::weak_ptr<lazy<rs2_extrinsics>>>> _extrinsics;
std::mutex _streams_mutex, _devices_changed_callbacks_mtx;
};

该类的有8个类变量:

1
2
3
4
5
6
7
8
9
10
std::shared_ptr<platform::backend> _backend;
std::shared_ptr<platform::device_watcher> _device_watcher;

std::map<std::string, std::weak_ptr<device_info>> _playback_devices;
std::map<uint64_t, devices_changed_callback_ptr> _devices_changed_callbacks;

devices_changed_callback_ptr _devices_changed_callback;
std::map<int, std::weak_ptr<const stream_interface>> _streams;
std::map<int, std::map<int, std::weak_ptr<lazy<rs2_extrinsics>>>> _extrinsics;
std::mutex _streams_mutex, _devices_changed_callbacks_mtx;

该类的构造函数如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
context::context( backend_type type )
: _devices_changed_callback(nullptr, [](rs2_devices_changed_callback*){})
{
static bool version_logged=false;
if (!version_logged)
{
version_logged = true;
LOG_DEBUG( "Librealsense VERSION: " << RS2_API_VERSION_STR );
}

_backend = platform::create_backend();

environment::get_instance().set_time_service(_backend->create_time_service());

_device_watcher = _backend->create_device_watcher();
assert(_device_watcher->is_stopped());
}

输入参数为backend_type,但该参数貌似并没有用。构造函数首先初始化类成员变量_devices_changed_callback(TODO: 搞清楚这个类到底是什么),接着调platform::create_backend()函数来为成员变量_backend赋值,然后调用类函数create_time_service()与create_device_watcher(),前者不知道在干什么(TODO: 弄清楚),后者则为设备守望类型的成员变量_device_watcher赋值。最后,检查一下_device_watcher是否处于stop状态。

类成员变量_playback_devices、_devices_changed_callbacks、_streams、_extrinsics等在初始化是并未赋值,这些变量在调用到对应的函数时才会对其进行赋值。

query_devices()与create_device()函数

query_devices()是librealsense::context中非常重要的一个函数。该函数被调用时,返回当前连接的设备信息。该函数如下所示:

1
2
3
4
5
std::vector<std::shared_ptr<device_info>> context::query_devices(int mask) const
{
platform::backend_device_group devices(_backend->query_uvc_devices(), _backend->query_usb_devices(), _backend->query_hid_devices());
return create_devices(devices, _playback_devices, mask);
}

首先该函数创建了一个backend_device_group对象,将目前所有发现的设备集合起来。这些设备通过_backend对象的query_uvc/usb/hid_device()函数来获取。在得到设备的集合后,调用create_device()函数来创建对应的device_info对象。create_device()函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
std::vector<std::shared_ptr<device_info>> context::create_devices(
platform::backend_device_group devices,
const std::map<std::string, std::weak_ptr<device_info>>& playback_devices,
int mask) const
{
std::vector<std::shared_ptr<device_info>> list;

LOG_INFO("UVC backend: " << devices.uvc_devices.size());
LOG_INFO("HID backend: " << devices.hid_devices.size());
LOG_INFO("USB backend: " << devices.usb_devices.size());

auto t = const_cast<context*>(this); // While generally a bad idea, we need to provide mutable reference to the devices
// to allow them to modify context later on
auto ctx = t->shared_from_this();
// 若mask为254, 那么下边的每一个循环都会执行一次, 但是pick_xxx_devices()中会对设备进行过滤, 非本系列产品线的设备返回一个长度为0的vector, 因此list中最终只保留对应的device_info
if (mask & RS2_PRODUCT_LINE_D400)
{
LOG_INFO("Create d400 device_info");
auto d400_devices = d400_info::pick_d400_devices(ctx, devices);
std::copy(begin(d400_devices), end(d400_devices), std::back_inserter(list));
}

if( mask & RS2_PRODUCT_LINE_L500 )
{
LOG_INFO("Create l500 device_info");
auto l500_devices = l500_info::pick_l500_devices(ctx, devices);
std::copy(begin(l500_devices), end(l500_devices), std::back_inserter(list));
}

if (mask & RS2_PRODUCT_LINE_SR300)
{
LOG_INFO("Create sr300 device_info");
auto sr300_devices = sr300_info::pick_sr300_devices(ctx, devices.uvc_devices, devices.usb_devices);
std::copy(begin(sr300_devices), end(sr300_devices), std::back_inserter(list));
}

// Supported recovery devices
if (mask & RS2_PRODUCT_LINE_D400 || mask & RS2_PRODUCT_LINE_SR300 || mask & RS2_PRODUCT_LINE_L500)
{
LOG_INFO("Create recovery devices");
auto recovery_devices = fw_update_info::pick_recovery_devices(ctx, devices.usb_devices, mask);
std::copy(begin(recovery_devices), end(recovery_devices), std::back_inserter(list));
}

if (mask & RS2_PRODUCT_LINE_NON_INTEL)
{
LOG_INFO("Create uvc_device_info");
auto uvc_devices = platform_camera_info::pick_uvc_devices(ctx, devices.uvc_devices);
std::copy(begin(uvc_devices), end(uvc_devices), std::back_inserter(list));
}

for (auto&& item : playback_devices)
{
if (auto dev = item.second.lock())
list.push_back(dev);
}

if (list.size())
LOG_INFO( "Found " << list.size() << " RealSense devices (mask 0x" << std::hex << mask << ")" );

return list;
}

create_device()函数的输入参数有三个:

  1. platform::backend_device_group对象,创建device_info对象时使用;
  2. std::map<std::string, std::weak_ptr<device_info>>类型的类内变量,即当前context对象中通过手动方式添加的设备;
  3. int类型变量mask,用来判断输出哪种类型设备的device_info列表。

create_device()函数首先根据mask值,判定创建哪一类型的device_info。在rs2::context中,通常不会指定mask的值,当未指定时,该值会被赋为254,所有的if都会走一变。为了防止创建出并未连接的设备,device_info各种派生类的pick_xxxx_devices()函数中,都会首先通过设备pid来进行筛选,符合系列pid的设备才会被创建。

连接一个D455设备时,将会返回3个device_info组成的队列。

rs2::context::query_devices()的过程

综上,rs2::context对象轮询所有设备的过程如下所示:

RealSense SDK源码分析(1) rs2::context类

http://example.com/2023/11/14/RealSense-SDK-rs2-context/

作者

Lei Kaiyu

发布于

2023-11-14

更新于

2023-11-14

许可协议