本文采用知识共享署名 4.0 国际许可协议进行许可,转载时请注明原文链接,图片在使用时请保留全部内容,可适当缩放并在引用处附上图片所在的文章链接。
USB Camera 产品
软件框架

1.main app 对应 /app/smart_display_service :负责 RNDIS 服务端功能实现,命令处理, NN 数据转发等操作;
2.AI app 对应 /app/mediaserver :负责将一路 camera 数据送到 NPU 做对应 NN 算法处理,通过共享内存机制传递给 main app ;
3.uvc app 对应 /external/uvc_app: :负责 UVC camera 完整功能的实现和控制。
smart_display_service
代码调用逻辑(以调用mediaserver 为例)



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
|
void aiserver_start_nn(char *mode, int value) {
int retry_cnt = 0;
char *ret;
struct UserData* userdata;
userdata = dbus_connection();
retry:
printf("%s: %s \n", __FUNCTION__, mode);
if (value == 0) {
dbus_method_call(userdata->connection,
MEDIASERVER, MEDIASERVER_PATH,
MEDIASERVER_INTERFACE, "Stop",
populate_set, userdata, append_path, mode);
} else {
dbus_method_call(userdata->connection,
MEDIASERVER, MEDIASERVER_PATH,
MEDIASERVER_INTERFACE, "Start",
populate_set, userdata, append_path, mode);
}
if (dbus_async(userdata) == -1 && retry_cnt++ < 5)
goto retry;
ret = userdata->json_str;smart_display_service
dbus_deconnection(userdata);
return ret;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
|
#ifdef USE_MEDIASERVER
#define MEDIASERVER "rockchip.mediaserver.control"
#define MEDIASERVER_PATH "/rockchip/mediaserver/control/feature"
#define MEDIASERVER_INTERFACE "rockchip.mediaserver.control.feature"
#endif
#define USE_AISERVER "ON"
#ifdef USE_AISERVER
#define MEDIASERVER "rockchip.aiserver.control"
#define MEDIASERVER_PATH "/rockchip/aiserver/control/graph"
#define MEDIASERVER_INTERFACE "rockchip.aiserver.control.graph"
#endif
|
文档路径:rv1126_rv1109_linux_201230/docs/RV1126_RV1109/ApplicationNote/Rockchip_Instructions_Linux_MediaServer_CN.pdf
mediaserver 建立pipe的概念,对单个或者多个通路的media stream进行配置重组,同时提供IPC通信接口,可与web等界面交互。
开发者可通过简单配置,实现下面这些功能的排列组合:
- 文本流读取、摄像头设备采集、音频设备采集。
- 音频/视频频编码。
- rtsp/rtmp/阿里云推流、云对讲功能、图片上传。
- 视频文件录制、拍照、音频播放。
- 支持rockface、rockx、rga等filter插件。
- 可与web交互
使用方法:
1
2
3
4
5
6
|
mediaserver [-c config] [-d / -D] [-s / -S] [-h]
-c:指定通路配置文件路径
-d:表示不使用dbserver
-D:表示使用dbserver里面的默认配置
-s:表示dbus注册在system bus上
-S:表示dbus注册在session bus上
|
示例:
使用IPC产品,带屏显:mediaserver -c /oem/usr/shared/mediaserver/rv1109/ipc-display.conf
使用IPC产品,不带屏显:mediaserver -c /oem/usr/shared/mediaserver/rv1109/ipc.conf
ipc-display.conf
文件路径:rv1126_rv1109_linux_201230/app/mediaserver/src/conf/rv1109/ipc-display.conf
配置文件使用json格式。
- Pipe表示一路独立的多媒体通道。
- Flow表示一路多媒体通道中的一个Source/IO/Sink单元。
- Stream表示FLow使用的处理方法。

Mediaserver 代码分析
USB Camera 中使用相应的config ()文件创建Mediaserver, s卖弄mart_display_service 使用 debus 向 Mediaserver 发送处理指令。
Mediaserver 具有独立的处理能力包含通过V4L2获取图图像,编码和AI处理等。参见上述 ipc-display.conf 中所诉功能。
构建数据处理流
Mediaserver 中配置文件加载路径如下:
1
|
#define FLOWS_CONF "/usr/share/mediaserver/mediaserver.conf"
|
基于配置文件生成数据处理流的逻辑见:
媒体管道构建
SHM_SERVER
使用共享内存方式将结果(face detect result)同步给main app (smart_display_service
)。
在flow 设置回调函数,获取结果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#ifdef ENABLE_SHM_SERVER
auto camera_pipe = GetFlowPipe(0, StreamType::CAMERA);
if (camera_pipe) {
auto link_flow = camera_pipe->GetFlow(StreamType::LINK);
if (link_flow)
link_flow->SetUserCallBack(nullptr, ShmControl::PushUserHandler);
auto rockx_filter =
camera_pipe->GetFlow(StreamType::FILTER, RKMEDIA_FILTER_ROCKX_FILTER);
LOG("RegisterCallBack ***********rockx_filter=%p\n", rockx_filter);
if (rockx_filter)
rockx_filter->Control(easymedia::S_NN_CALLBACK,
ShmControl::PushUserHandler);
}
auto file_pipe = GetFlowPipe(0, StreamType::FILE);
if (file_pipe) {
auto link_flow = file_pipe->GetFlow(StreamType::LINK);
if (link_flow)
link_flow->SetUserCallBack(nullptr, ShmControl::PushUserHandler);
}
#endif
|
**Mediaserver Dbus 通信 **
代码位置:rv1126_rv1109_linux_201230/app/mediaserver/src/dbus
InitDbusServer
mediaserver/src/mediaserver.cpp :
MediaServer::MediaServer() -> InitDbusServer();
DBusServer::RegisteredDBusAdaptor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
int DBusServer::RegisteredDBusAdaptor() {
DbusDispatcher(100);
DBus::Connection conn =
session_ ? DBus::Connection::SessionBus() : DBus::Connection::SystemBus();
conn.request_name(MEDIA_CONTROL_BUS_NAME);
media_control_->ConnectDBusServer(conn);
if (need_dbserver_) {
dbserver_proxy_ =
std::make_shared<DBusDbServer>(conn, DBSERVE_PATH, DBSERVE_BUS_NAME);
dbserver_listen_.reset(new DBusDbListen(conn));
dbevent_proxy_ =
std::make_shared<DBusDbEvent>(conn, DBSERVE_PATH, DBSERVE_BUS_NAME);
dbevent_listen_.reset(new DBusDbEventListen(conn));
strorage_proxy_ = std::make_shared<DBusStorageManager>(
conn, STORAGE_MANAGER_PATH, STORAGE_MANAGER_BUS_NAME);
strorage_listen_.reset(new DBusStorageManagerListen(conn));
ispserver_proxy_ = std::make_shared<DBusIspserver>(
conn, ISPSERVER_PATH, ISPSERVER_BUS_NAME);
}
return 0;
}
|

文档位置:/rv1126_rv1109_linux_201230/docs/RV1126_RV1109/Multimedia/Rockchip_Developer_Guide_Linux_RKMedia_CN.pdf
/rv1126_rv1109_linux_201230/docs/RV1126_RV1109/Multimedia/Rockchip_Instructions_Linux_Rkmedia_CN.pdf
代码位置:/rv1126_rv1109_linux_201230/external/rkmedia
Mediaserver 和 rkmedia 的关系
Mediaserver 使用 flow
创建多媒体数据流,将音视频的采集,编码,处理能功能模块串联在一起形成应用处理的数据流,实际处理模块的实现是在rkmedia中。
rkmedia 提供了音视频处理相关的接口,包含了VI(输入视频捕获)、VENC(H.265/H.264/JPEG/MJPEG 编码)、VDEC(H.265/H.264/JPEG、MJPEG 解码)、VO(视频输出显示)、RGA视频处理(包括旋转、缩放、裁剪)、AI(音频采集)、AO(音频输出)、AENC(音频编码)、ADEC(音频解码)、MD(移动侦测)、OD(遮挡侦测)等。

flow_manager->CreatePipes();
CreateFlows
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
|
int FlowPipe::RegisterCallBack() {
auto encoder_flow =
GetFlow(StreamType::VIDEO_ENCODER, RKMEDIA_STREAM_NAME_RKMPP);
auto draw_flow = GetFlow(StreamType::FILTER, RKMEDIA_FILTER_DRAW_FILTER);
if (encoder_flow && draw_flow)
draw_flow->Control(easymedia::S_NN_DRAW_HANDLER, encoder_flow.get());
auto detect_flow =
GetFlow(StreamType::FILTER, RKMEDIA_FILTER_ROCKFACE_DETECT);
if (detect_flow)
detect_flow->Control(easymedia::S_NN_CALLBACK, FlowCallBack);
auto body_detect_flow =
GetFlow(StreamType::FILTER, RKMEDIA_FILTER_ROCKFACE_BODYDETECT);
if (body_detect_flow)
body_detect_flow->Control(easymedia::S_NN_CALLBACK, FlowCallBack);
auto face_capture_flow =
GetFlow(StreamType::FILTER, RKMEDIA_FILTER_FACE_CAPTURE);
if (face_capture_flow)
face_capture_flow->Control(easymedia::S_NN_CALLBACK, FlowCallBack);
auto face_recognize_flow =
GetFlow(StreamType::FILTER, RKMEDIA_FILTER_ROCKFACE_RECOGNIZE);
if (face_recognize_flow)
face_recognize_flow->Control(easymedia::S_NN_CALLBACK, FlowCallBack);
auto rockx_flow = GetFlow(StreamType::FILTER, RKMEDIA_FILTER_ROCKX_FILTER);
if (rockx_flow)
rockx_flow->Control(easymedia::S_NN_CALLBACK, FlowCallBack);
return 0;
}
|
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
|
void FlowPipe::CreateFlow(std::shared_ptr<FlowUnit> flow_unit) {
std::string param;
std::string flow_name = flow_unit->GetFlowName();
std::string flow_param = flow_unit->FlowParamMapToStr();
std::string stream_param = flow_unit->StreamParamMapToStr();
auto type = flow_unit->GetStreamType();
if (type == StreamType::MUXER) {
std::string vid_param = "";
std::string aud_param = "";
std::string flow_index_name = flow_unit->GetFlowIndexName();
std::string upflow_index_name = flow_unit->GetUpFlowIndexName();
auto v = SplitStringToVector(upflow_index_name);
for (auto name : v) {
int upflow_index = GetFlowIndex(name);
auto flow_unit = GetFlowunit(upflow_index);
StreamType type = flow_unit->GetStreamType();
if (type == StreamType::VIDEO_ENCODER)
vid_param = GetStreamParam(StreamType::VIDEO_ENCODER);
else if (type == StreamType::AUDIO_ENCODER)
aud_param = GetStreamParam(StreamType::AUDIO_ENCODER);
}
param = easymedia::JoinFlowParam(flow_param, 2, aud_param, vid_param);
} else {
param = easymedia::JoinFlowParam(flow_param, 1, stream_param);
}
LOG_DEBUG("flow_name :%s\n", flow_name.c_str());
LOG_DEBUG("flow_param :\n%s\n", param.c_str());
int reopen_pipe_index = flow_unit->GetOpenPipeId();
int reopen_flow_index = flow_unit->GetOpenFlowId();
if (reopen_pipe_index >= 0 && reopen_flow_index >= 0) {
flow_unit->SetFlow(nullptr);
return;
}
auto flow = easymedia::REFLECTOR(Flow)::Create<easymedia::Flow>(
flow_name.c_str(), param.c_str());
if (!flow) {
LOG_ERROR("Create flow %s failed\n", flow_name.c_str());
LOG_ERROR("flow param :\n%s\n", param.c_str());
exit(EXIT_FAILURE);
}
if (type == StreamType::VIDEO_ENCODER && enable_encoder_debug)
easymedia::video_encoder_enable_statistics(flow, 1);
flow->RegisterEventHandler(flow, FlowEventProc);
flow_unit->SetFlow(flow);
}
|
mediaserver/src/flows/flow_pipe.cpp 529:
auto flow = easymedia::REFLECTOR(Flow)::Createeasymedia::Flow(
flow_name.c_str(), param.c_str());
uvc_app
命令:
其他
RNDIS
rndis既是RemoteNDIS,既是远程网络驱动接口规范。
protected: templateprotected: templateRemote Network Driver Interface Specification,既是RemoteNDIS,既是远程网络驱动接口规范。基于USB实现RNDIS实际上就是TCP/IP over USB,就是在USB设备上跑TCP/IP,让USB设备看上去像一块网卡。
ISP
ISP(Image Signal Processor), 即图像信号处理, 主要作用是对前端图像传感器输出的信号做后期处理, 依赖于 ISP 才能在不同的光学条件下都能较好的还原现场细节。
Cmos YUV sensor 的 ISP 处理流程如图所示:

工具:
callgraph -f main -d ./main.cpp
callgraph -f rndis_initialize_tcp -d ./net/tcp_control.cpp 20171214140255352
callgraph -f handle_callback -d ./event/event_handle.cpp
callgraph -f main -d ./mediaserver.cpp
callgraph -f main -d ./mediaserver.cpp
RegisteredDBusAdaptorprotected: template
https://blog.csdn.net/weixin_43503508/category_10257454.html