Contents

Embedded:RV1126 USB_Camera应用代码阅读笔记

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

USB Camera 产品

软件框架

https://cdn.jsdelivr.net/gh/zhangyuhu/share_images/images/202302092105044.png

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 为例)

https://cdn.jsdelivr.net/gh/zhangyuhu/share_images/images/202302092105496.svg

https://cdn.jsdelivr.net/gh/zhangyuhu/share_images/images/202302092105628.svg

https://cdn.jsdelivr.net/gh/zhangyuhu/share_images/images/202302092106230.svg

 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

mediaserver

Mediaserver 介绍

文档路径:rv1126_rv1109_linux_201230/docs/RV1126_RV1109/ApplicationNote/Rockchip_Instructions_Linux_MediaServer_CN.pdf

mediaserver 建立pipe的概念,对单个或者多个通路的media stream进行配置重组,同时提供IPC通信接口,可与web等界面交互。 开发者可通过简单配置,实现下面这些功能的排列组合:

  1. 文本流读取、摄像头设备采集、音频设备采集。
  2. 音频/视频频编码。
  3. rtsp/rtmp/阿里云推流、云对讲功能、图片上传。
  4. 视频文件录制、拍照、音频播放。
  5. 支持rockface、rockx、rga等filter插件。
  6. 可与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使用的处理方法。

https://cdn.jsdelivr.net/gh/zhangyuhu/share_images/images/202302092105264.png

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;
}

rkmedia

文档位置:/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(遮挡侦测)等。

https://cdn.jsdelivr.net/gh/zhangyuhu/share_images/images/202302092105193.png

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 虚拟网口IP地址

    1
    
    ifconfig usb0
    

其他

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 处理流程如图所示:

https://cdn.jsdelivr.net/gh/zhangyuhu/share_images/images/202302092105730.png

工具:

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