【RV1106】rkipc:分析(一)

文章目录

说明文档

  • rkipc/docs/Rockchip_Developer_Guide_Linux_RKIPC_CN.md

框架图

介绍

bash 复制代码
| 源码目录    |    依赖外部库 | 功能                                                         |
| ---------- | ------------- | ------------------------------------------------------------ |
| rv1106_ipc | rockit、rkaiq | 针对RV1106平台的IPC产品,支持网页和rtsp/rtmp预览,参数动态修改,关闭卷绕。 |

RV1106 IPC 流程图

bind
get
send
bind
get and sned
bind
AI
AENC
MUXER
MP4
VI_0
VENC_0_and_OSD_draw_NN_result
TDE_process
VENC_JPEG
RTSP_RTMP_0
VI_1
VENC_1
RTSP_RTMP_1
VI_2
NPU
IVS_for_OD_MD

编译 rkipc (由 RK_APP_TYPE=RKIPC_RV1106 决定)

  • 仅编译app: ./build.sh app
  • 仅清空app: ./build.sh clean app 会删除掉两处 app_out 文件夹。
bash 复制代码
function build_app() {
	check_config RK_APP_TYPE || return 0

	echo "============Start building app============"
	echo "TARGET_APP_CONFIG=$RK_APP_DEFCONFIG $RK_APP_DEFCONFIG_FRAGMENT $RK_APP_TYPE"
	echo "========================================="

	build_meta --export --media_dir $RK_PROJECT_PATH_MEDIA
	test -d ${SDK_APP_DIR} && make -C ${SDK_APP_DIR}

	finish_build
}

RK_APP_TYPE 决定了编译的APP,这个在 BoardConfig.mk文件里面有定义

bash 复制代码
#################################################
# 	Board Config
#################################################
export LF_ORIGIN_BOARD_CONFIG=BoardConfig-EMMC-Buildroot-RV1106_Luckfox_Pico_Ultra_W-IPC.mk
# Target CHIP
export RK_CHIP=rv1106

# app config
export RK_APP_TYPE=RKIPC_RV1106

rkipc 上电时的参数(功能动态)

参数系统函数分析

  • 参数系统初始化:int rk_param_init(char *ini_path);
  • 获取参数:rk_param_get_int()
c 复制代码
int rk_param_init(char *ini_path) {
  LOG_INFO("%s\n", __func__);
  pthread_mutex_lock(&g_param_mutex);
  g_ini_d_ = NULL;
  if (ini_path)
    memcpy(g_ini_path_, ini_path, strlen(ini_path));
  else
    memcpy(g_ini_path_, "/usr/share/rkuvc.ini", strlen("/usr/share/rkuvc.ini"));
  LOG_INFO("g_ini_path_ is %s\n", g_ini_path_);

  g_ini_d_ = iniparser_load(g_ini_path_);
  if (g_ini_d_ == NULL) {
    LOG_ERROR("iniparser_load error!\n");
    return -1;
  }
  rk_param_dump();
  pthread_mutex_unlock(&g_param_mutex);

  return 0;
}
c 复制代码
const char * dictionary_get(const dictionary * d, const char * key, const char * def)
{
    unsigned    hash ;
    ssize_t      i ;

    hash = dictionary_hash(key);
    for (i=0 ; i<d->size ; i++) {
        if (d->key[i]==NULL)
            continue ;
        /* Compare hash */
        if (hash==d->hash[i]) {
            /* Compare string, to avoid hash collisions */
            if (!strcmp(key, d->key[i])) {
                return d->val[i] ;
            }
        }
    }
    return def ;
}
...
static const char * strlwc(const char * in, char *out, unsigned len)
{
    unsigned i ;

    if (in==NULL || out == NULL || len==0) return NULL ;
    i=0 ;
    while (in[i] != '\0' && i < len-1) {
        out[i] = (char)tolower((int)in[i]);
        i++ ;
    }
    out[i] = '\0';
    return out ;
}

...
const char * iniparser_getstring(const dictionary * d, const char * key, const char * def)
{
    const char * lc_key ;
    const char * sval ;
    char tmp_str[ASCIILINESZ+1];

    if (d==NULL || key==NULL)
        return def ;

    lc_key = strlwc(key, tmp_str, sizeof(tmp_str));
    sval = dictionary_get(d, lc_key, def);
    return sval ;
}

...
long int iniparser_getlongint(const dictionary * d, const char * key, long int notfound)
{
    const char * str ;

    str = iniparser_getstring(d, key, INI_INVALID_KEY);
    if (str==INI_INVALID_KEY) return notfound ;
    return strtol(str, NULL, 0);
}
...
int iniparser_getint(const dictionary * d, const char * key, int notfound)
{
    return (int)iniparser_getlongint(d, key, notfound);
}
...

int rk_param_get_int(const char *entry, int default_val) {
  int ret;
  pthread_mutex_lock(&g_param_mutex);
  ret = iniparser_getint(g_ini_d_, entry, default_val);
  pthread_mutex_unlock(&g_param_mutex);

  return ret;
}

实例:rk_param_get_int("video.source:enable_ivs", 0);

xxx.ini文件
当程序读取时,你的代码就会从 [video.source] 段中找到 enable_ivs,并返回其值 。如果找不到,则返回默认值 0。

上电时的打印系统:

c 复制代码
[rkipc.c][main]:rkipc_ini_path_ is (null), rkipc_iq_file_path_ is (null), rkipc_log_level is 3
[param.c][rk_param_init]:rk_param_init
[param.c][rk_param_init]:g_ini_path_ is /userdata/rkipc.ini

源码:

c 复制代码
char *rkipc_ini_path_ = NULL;

...
	LOG_INFO("rkipc_ini_path_ is %s, rkipc_iq_file_path_ is %s, rkipc_log_level "
	         "is %d\n",
	         rkipc_ini_path_, rkipc_iq_file_path_, rkipc_log_level);

	// init
	rk_param_init(rkipc_ini_path_);
...

确定了上电时加载的参数系统ini文件为:/userdata/rkipc.ini

代码结构

bash 复制代码
├── CMakeLists.txt
├── common # 通用模块
│   ├── common.h # 一些通用函数功能
│   ├── event # 事件处理模块
│   ├── isp # 图像处理模块
│   │   ├── rk3588
│   │   ├── rv1106
│   │   └── rv1126
│   ├── log.h # 日志管理
│   ├── network # 网络模块
│   ├── osd # OSD模块
│   │   ├── image.bmp # logo图片
│   │   └── simsun_en.ttf # 字体库
│   ├── param # 参数管理模块
│   ├── rkbar # 二维码识别模块
│   ├── rockiva # 周界算法模块,人脸人形识别
│   ├── rtmp # rtmp推流模块
│   ├── rtsp # rtsp推流模块
│   ├── storage # 存储模块
│   └── system # 系统管理模块
│   └── tuya_ipc # 涂鸦IPC模块
├── format.sh # 格式化脚本
├── lib # 存放32/64位版本,不同工具链的预编译库
│   ├── aarch64-rockchip1031-linux-gnu
│   └── arm-rockchip830-linux-gnueabihf
├── LICENSE # 版权声明
└── src
    ├── low_memory_ipc
    ├── rk3588_ipc
    │   ├── audio # 音频业务逻辑
    │   ├── CMakeLists.txt
    │   ├── main.c
    │   ├── rkipc.ini # 参数文件
    │   ├── server # socket服务端
    │   └── video # 视频业务逻辑
    │       ├── video.c
    │       └── video.h
    ├── rk3588_muliti_ipc
    ├── rv1126_battery_ipc
    ├── rv1126_ipc_rkmedia
    ├── rv1126_ipc_rockit
    └── rv1126_snapshot

rv1106_ipc的主程序分析注释

c 复制代码
// 包含头文件
#include <getopt.h>           // 命令行参数解析
#include "audio.h"           // 音频处理模块
#include "common.h"          // 公共定义
#include "isp.h"             // 图像信号处理模块(Image Signal Processor)
#include "log.h"             // 日志模块
#include "network.h"         // 网络模块
#include "param.h"           // 参数管理模块
#include "rockiva.h"         // 瑞芯微AI视觉分析模块
#include "server.h"          // 服务器/服务模块
#include "storage.h"         // 存储模块(SD卡/NAS等)
#include "system.h"          // 系统管理模块
#include "video.h"           // 视频处理模块
#include <linux/input.h>     // Linux输入子系统(用于按键检测)

// 重新定义日志标签
#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "rkipc.c"   // 标识这是IPC主程序的日志

// 定义日志级别枚举
enum { LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG };

// 全局变量定义
int enable_minilog = 0;                 // 迷你日志使能标志(未使用)
int rkipc_log_level = LOG_INFO;        // 全局日志级别,默认INFO
static int g_main_run_ = 1;            // 主循环运行标志,控制程序退出
char *rkipc_ini_path_ = NULL;          // 配置文件路径指针
char *rkipc_iq_file_path_ = NULL;      // IQ调校文件路径指针

// 信号处理函数 - 用于优雅退出
static void sig_proc(int signo) {
    LOG_INFO("received signo %d \n", signo);  // 打印收到的信号
    g_main_run_ = 0;  // 设置退出标志,通知主循环结束
}

// 命令行参数定义
static const char short_options[] = "c:a:l:";  // 短参数:-c -a -l
static const struct option long_options[] = {  // 长参数定义
    {"config", required_argument, NULL, 'c'},      // 配置文件路径
    {"aiq_file", no_argument, NULL, 'a'},          // AIQ文件目录
    {"log_level", no_argument, NULL, 'l'},         // 日志级别
    {"help", no_argument, NULL, 'h'},              // 帮助信息
    {0, 0, 0, 0}                                   // 结束标记
};

// 使用说明提示函数
static void usage_tip(FILE *fp, int argc, char **argv) {
    fprintf(fp,
            "Usage: %s [options]\n"                     // 用法
            "Version %s\n"                             // 版本号
            "Options:\n"
            "-c | --config      rkipc ini file, default is "  // 配置文件
            "/userdata/rkipc.ini, need to be writable\n"
            "-a | --aiq_file    aiq file dir path, default is /etc/iqfiles\n" // IQ文件路径
            "-l | --log_level   log_level [0/1/2/3], default is 2\n"  // 日志级别
            "-h | --help        for help \n\n"          // 帮助
            "\n",
            argv[0], "V1.0");                           // 程序名和版本
}

// 命令行参数解析函数
void rkipc_get_opt(int argc, char *argv[]) {
    for (;;) {  // 无限循环,直到所有参数解析完毕
        int idx;
        int c;
        c = getopt_long(argc, argv, short_options, long_options, &idx);
        if (-1 == c)  // getopt_long返回-1表示解析完成
            break;
        switch (c) {
        case 0: /* getopt_long() flag */
            break;
        case 'c':  // 处理-c参数:配置文件路径
            rkipc_ini_path_ = optarg;  // optarg指向参数值
            break;
        case 'a':  // 处理-a参数:IQ文件路径
            rkipc_iq_file_path_ = optarg;
            break;
        case 'l':  // 处理-l参数:日志级别
            rkipc_log_level = atoi(optarg);  // 字符串转整数
            break;
        case 'h':  // 处理-h参数:帮助信息
            usage_tip(stdout, argc, argv);
            exit(EXIT_SUCCESS);  // 显示帮助后直接退出
        default:   // 未知参数
            usage_tip(stderr, argc, argv);
            exit(EXIT_FAILURE);  // 显示错误用法后退出
        }
    }
}

// 音频文件读取缓冲区大小(4KB)
#define AO_FREAD_SIZE 1024 * 4

// 按键事件监听线程函数
static void *wait_key_event(void *arg) {
    int key_fd;
    // 打开输入设备(通常是硬件按键)
    key_fd = open("/dev/input/event0", O_RDONLY);
    if (key_fd < 0) {
        LOG_ERROR("can't open /dev/input/event0\n");
        return NULL;
    }
    
    // 文件描述符集合和超时设置
    fd_set rfds;
    int nfds = key_fd + 1;
    struct timeval timeout;
    struct input_event key_event;  // 输入事件结构体
    
    // 主监听循环
    while (g_main_run_) {
        // rfds集合必须每次清空,否则无法检测描述符变化
        timeout.tv_sec = 1;  // 1秒超时
        FD_ZERO(&rfds);      // 清空文件描述符集合
        FD_SET(key_fd, &rfds);  // 将按键设备描述符加入集合
        
        // 使用select监听输入事件
        select(nfds, &rfds, NULL, NULL, &timeout);
        
        // 检查是否有按键事件发生
        if (FD_ISSET(key_fd, &rfds)) {
            // 读取按键事件
            read(key_fd, &key_event, sizeof(key_event));
            LOG_INFO("[timeval:sec:%d,usec:%d,type:%d,code:%d,value:%d]\n", 
                    key_event.time.tv_sec, key_event.time.tv_usec, 
                    key_event.type, key_event.code, key_event.value);
            
            // 处理音量减键(硬件测试功能)
            if ((key_event.code == KEY_VOLUMEDOWN) && key_event.value) {
                LOG_INFO("get KEY_VOLUMEDOWN\n");
                
                // 音频系统初始化
                rkipc_ao_init();
                
                // 打开测试音频文件
                FILE *fp = fopen("/oem/usr/share/speaker_test.wav", "rb");
                int size = AO_FREAD_SIZE;
                char *tmp_data;
                tmp_data = malloc(AO_FREAD_SIZE);  // 分配缓冲区
                
                // 读取并播放音频文件
                while (size > 0) {
                    memset((void *)tmp_data, 0, AO_FREAD_SIZE);  // 清空缓冲区
                    size = fread(tmp_data, 1, AO_FREAD_SIZE, fp);  // 读取音频数据
                    rkipc_ao_write(tmp_data, AO_FREAD_SIZE);  // 写入音频输出
                }
                
                // 发送结束标记(0长度)
                rkipc_ao_write(tmp_data, 0);
                
                // 清理资源
                free(tmp_data);
                fclose(fp);
                rkipc_ao_deinit();  // 音频系统反初始化
            }
            
            // 处理音量加键(预留功能)
            if ((key_event.code == KEY_VOLUMEUP) && key_event.value) {
                LOG_INFO("get KEY_VOLUMEUP\n");
                // 这里可以添加其他功能,如拍照、录像等
            }
        }
    }
    
    // 清理资源
    if (key_fd) {
        close(key_fd);
        key_fd = 0;
    }
    LOG_DEBUG("wait key event out\n");
    return NULL;
}

// 主函数 - 程序入口点
int main(int argc, char **argv) {
    pthread_t key_chk;  // 按键监听线程句柄
    
    LOG_DEBUG("main begin\n");
    rkipc_version_dump();  // 打印版本信息
    
    // 注册信号处理函数
    signal(SIGINT, sig_proc);   // Ctrl+C中断信号
    signal(SIGTERM, sig_proc);  // 终止信号
    
    // 解析命令行参数
    rkipc_get_opt(argc, argv);
    LOG_INFO("rkipc_ini_path_ is %s, rkipc_iq_file_path_ is %s, rkipc_log_level is %d\n",
             rkipc_ini_path_, rkipc_iq_file_path_, rkipc_log_level);
    
    // ==================== 初始化阶段 ====================
    // 1. 参数系统初始化(读取配置文件)
    rk_param_init(rkipc_ini_path_);
    
    // 2. 网络模块初始化
    rk_network_init(NULL);
    
    // 3. 系统模块初始化
    rk_system_init();
    
    // 4. AI视觉模块初始化(根据配置决定是否启用)
    if (rk_param_get_int("video.source:enable_npu", 0))
        rkipc_rockiva_init();
    
    // 5. ISP图像处理模块初始化(根据配置决定是否启用AIQ)
    if (rk_param_get_int("video.source:enable_aiq", 1)) {
        rk_isp_init(0, rkipc_iq_file_path_);  // 初始化ISP,传入IQ文件路径
        if (rk_param_get_int("isp:init_form_ini", 1))
            rk_isp_set_from_ini(0);  // 从INI文件加载ISP参数
    }
    
    // 6. 瑞芯微MPI系统初始化(媒体处理接口)
    RK_MPI_SYS_Init();
    
    // 7. 视频模块初始化
    rk_video_init();
    
    // 8. 音频模块初始化(根据配置决定是否启用)
    if (rk_param_get_int("audio.0:enable", 0))
        rkipc_audio_init();
    
    // 9. 服务器模块初始化(Web服务/RTSP服务等)
    rkipc_server_init();
    
    // 10. 存储模块初始化(SD卡录像等)
    rk_storage_init();
    
    // 11. 创建按键监听线程
    pthread_create(&key_chk, NULL, wait_key_event, NULL);
    
    // ==================== 主运行循环 ====================
    // 简单的主循环,每秒检查一次退出标志
    while (g_main_run_) {
        usleep(1000 * 1000);  // 睡眠1秒(1000毫秒)
        // 注意:这里可以添加其他周期性任务,如状态检查、心跳包等
    }
    
    // ==================== 反初始化阶段(逆序) ====================
    // 1. 等待按键线程结束
    pthread_join(key_chk, NULL);
    
    // 2. 存储模块反初始化
    rk_storage_deinit();
    
    // 3. 服务器模块反初始化
    rkipc_server_deinit();
    
    // 4. 系统模块反初始化
    rk_system_deinit();
    
    // 5. 视频模块反初始化
    rk_video_deinit();
    
    // 6. ISP模块反初始化(如果之前初始化了)
    if (rk_param_get_int("video.source:enable_aiq", 1))
        rk_isp_deinit(0);
    
    // 7. 音频模块反初始化(如果之前初始化了)
    if (rk_param_get_int("audio.0:enable", 0))
        rkipc_audio_deinit();
    
    // 8. 瑞芯微MPI系统退出
    RK_MPI_SYS_Exit();
    
    // 9. AI视觉模块反初始化(如果之前初始化了)
    if (rk_param_get_int("video.source:enable_npu", 0))
        rkipc_rockiva_deinit();
    
    // 10. 网络模块反初始化
    rk_network_deinit();
    
    // 11. 参数系统反初始化
    rk_param_deinit();
    
    return 0;  // 程序正常退出
}
相关推荐
aqi0010 小时前
FFmpeg开发笔记(九十八)基于FFmpeg的跨平台图形用户界面LosslessCut
android·ffmpeg·kotlin·音视频·直播·流媒体
广州服务器托管11 小时前
比较优秀的视频音频播放器PotPlayer64-v1.7.22764绿色版
运维·windows·计算机网络·电脑·音视频·可信计算技术
jbk331114 小时前
批量给视频添加字幕,并实现多样式可选的功能
音视频
dualven_in_csdn1 天前
【视频优化研究】过程 记录
音视频
纽格立科技1 天前
2025全球DRM数字广播战略实施全景——印尼篇(地缘特征主导下的数字骨干网构建)
网络·科技·音视频·信息与通信·传媒
Black蜡笔小新1 天前
全域互联:EasyCVR如何为多区域视频监控融合治理提供技术支持
音视频
非凡ghost1 天前
MPC-QT视频播放器(基于Qt框架播放器)
开发语言·windows·qt·音视频·软件需求
REDcker1 天前
Android WebView 版本升级方案详解
android·音视频·实时音视频·webview·js·编解码
昨日之日20061 天前
LTX-2 - 一键生成音视频,创作更轻松 支持50系显卡 ComfyUI工作流 一键整合包
人工智能·音视频·视频