本文采用知识共享署名 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