更新时间:2023-08-09 09:25:02下载pdf
本文介绍 IPC(IP Camera)设备的视频采集全流程,包括视频采集(VI,Video Input)、视频编码(VENC,Video Encoding)初始化、视频流的获取。
视频采集,是指采集编码后的视频数据。IPC 设备常用视频规格如下:
如下图,FH8636 平台数据流信息:
T31 平台,传感器竖装(门铃场景),手动旋转 YUV,平台数据流信息如下:
部分平台有传感器驱动。如安凯平台,在文件系统的初始化脚本中,加载传感器驱动。
insmod /drv/ak_ion.ko
insmod /drv/ak_uio.ko
insmod /drv/ak_pcm.ko
insmod /drv/ak_i2c.ko
if [ -e /drv/sensor_sc2336.ko ]; then
insmod /drv/ak_isp.ko
insmod /drv/sensor_sc2336.ko
fi
if [ -e /drv/sensor_sc3336.ko ]; then
insmod /drv/ak_isp.ko internal_pclk=100000000
insmod /drv/sensor_sc3336.ko
fi
安凯的多媒体内存分配,在内核中,由 BSP 分配。
有些平台没有传感器驱动,如富瀚平台。传感器的寄存器序列放置在代码中。
#内存分配
insmod /drv/vmm.ko mmz=anonymous,0,0xA2600000,26624K anony=1
#0xA2600000 = 0xA0000000+0x2600000,其中 0x2600000 = 38M,代表系统内存。26624K=26M,代表 mmz 的长度
#加载驱动
insmod /drv/media_process.ko
insmod /drv/isp.ko
insmod /drv/enc.ko
insmod /drv/jpeg.ko
insmod /drv/bgm.ko
insmod /drv/nna.ko
不同的平台有不同驱动加载方式。只需完成传感器的寄存器序列初始化即可。
配置传感器的基础属性。包括传感器的长宽,传感器名,传感器默认的镜像翻转,ISP 图像风格等。
配置编码器的属性。具体配置,可参考本文的 使用示例。
如 功能说明图 中所示,不同流有一定的耦合关系。例如子码流和 QR 用同一路 VPU,因此,两者的分辨率需要求均为 640*360。
视频采集分为两种方式,主动采集和被动采集。
一个芯片平台仅支持一种采集方式。不做特殊说明时,默认采用主动采集。
被动采集,调用逻辑基本和上图一致。差异点是,tal_venc_init
前,注册回调参数。上层不需要调用 tal_venc_get_frame
取流接口。底层默认开启线程,调用 put_cb
发送视频。
// vi 和 isp
typedef struct{
CHAR_T conf[128]; // isp conf path
CHAR_T conf1[128]; // isp conf1 path
CHAR_T conf2[128]; // isp conf2 path
CHAR_T conf3[128]; // isp conf3 path
CHAR_T version[32]; // version num
CHAR_T name[16]; // isp sensor num
TKL_ISP_DN_SWITCH_CONFIG_T isp_dn_switch_config; // ADN switch config
TKL_VI_SENSOR_CBUS_TYPE_E sensor_type; // sensor control bus type
INT32_T addr; // sensor address
INT32_T width;
INT32_T height;
TKL_ISP_FIG_STYLE_CONFIG_T isp_fig_style_day; // isp fig style in day
TKL_ISP_FIG_STYLE_CONFIG_T isp_fig_style_night; // isp fig style in night
INT32_T fps; // sensor fps
}TKL_VI_ISP_CONFIG_T;
typedef struct {
INT32_T enable; // 1,enable,0,disable
TKL_VI_CHN_E chn; // video input channel
INT32_T mirror; // mirror defaults
INT32_T filp; // filp defaults
TKL_VI_ISP_CONFIG_T isp; // isp config
VOID *pdata; // reserver data
} TKL_VI_CONFIG_T;
// venc
typedef enum
{
TKL_VIDEO_PB_FRAME = 0,
TKL_VIDEO_I_FRAME,
TKL_VIDEO_TS_FRAME,
TKL_AUDIO_FRAME,
TKL_CMD_FRAME,
TKL_MEDIA_FRAME_MAX,
}TKL_MEDIA_FRAME_TYPE_E;
typedef enum
{
TKL_CODEC_VIDEO_MPEG4 = 0,
TKL_CODEC_VIDEO_H263,
TKL_CODEC_VIDEO_H264,
TKL_CODEC_VIDEO_MJPEG,
TKL_CODEC_VIDEO_H265,
TKL_CODEC_VIDEO_YUV420,
TKL_CODEC_VIDEO_YUV422,
TKL_CODEC_VIDEO_MAX = 99,
TKL_CODEC_AUDIO_ADPCM,
TKL_CODEC_AUDIO_PCM,
TKL_CODEC_AUDIO_AAC_RAW,
TKL_CODEC_AUDIO_AAC_ADTS,
TKL_CODEC_AUDIO_AAC_LATM,
TKL_CODEC_AUDIO_G711U,
TKL_CODEC_AUDIO_G711A,
TKL_CODEC_AUDIO_G726,
TKL_CODEC_AUDIO_SPEEX,
TKL_CODEC_AUDIO_MP3,
TKL_CODEC_AUDIO_MAX = 199,
TKL_CODEC_TYPE_MAX
}TKL_MEDIA_CODEC_TYPE_E;
typedef struct
{
UINT_T left; // osd 左上角的位置 x 坐标
UINT_T top; // osd 左上角的位置 y 坐标
UINT_T font_w; // osd 字体大小,字体的宽
UINT_T font_h; // osd 字体大小,字体的高
}TKL_VENC_OSD_CONFIG_T; // osd 属性配置
typedef struct {
UINT_T enable; // 1,enable,0,disable
TKL_VENC_CHN_E chn; // video encode channel
TKL_VENC_TYPE_E type; // stream work type
TKL_MEDIA_CODEC_TYPE_E codectype; // codec type
UINT_T fps; // fps
UINT_T gop; // I Frame interval
UINT_T bitrate; // bitrate,kbps
UINT_T width;
UINT_T height;
UINT_T min_qp;
UINT_T max_qp;
TKL_VENC_DATA_TRANS_MODE_E trans_mode;
TKL_VENC_PUT_CB put_cb;
TKL_VENC_OSD_CONFIG_T osd;
} TKL_VENC_CONFIG_T;
#define TAL_VI_CONFIG_T TKL_VI_CONFIG_T
#define TAL_VI_HARDWARE_SOURCE_T TKL_VI_HARDWARE_SOURCE_T
typedef enum {
TAL_VI_CMD_MIRROR_FLIP,
TAL_VI_CMD_ANTIFLICKER,
TAL_VI_CMD_ILLUMIN,
TAL_VI_CMD_DAYMODE,
TAL_VI_CMD_SENSOR_REG,
} TAL_VI_CMD_E;
typedef enum {
TAL_VENC_CMD_OSD, // osd
TAL_VENC_CMD_IDR, // set IDR
TAL_VENC_CMD_STREAM_BUFF, // Set the circular cache area of the video memory pool. For example, set two 30K memory
// pools for storage
TAL_VENC_CMD_START, // venc start
TAL_VENC_CMD_STOP, // venc stop
} TAL_VENC_CMD_E;
typedef struct {
INT32_T enable;
INT32_T is_dls;
} TKL_VENC_OSD_T;
typedef struct {
TKL_MEDIA_FRAME_TYPE_E frametype; // bitrate,kbps
TKL_MEDIA_CODEC_TYPE_E codectype; // codec type
CHAR_T *pbuf; // frame buffer
UINT_T buf_size; // buffer size
UINT_T used_size; // used buffer size
UINT_T width; // frame width
UINT_T height; // frame height
UINT64_T pts; // sdk pts
UINT64_T timestamp; // system utc time,unit: ms
UINT_T seq; // frame sequence number
UINT_T fragment; // frame sequence's fragment 第几个分包
BYTE_T seq_error; // frame sequence is error 这张图片是否错误
BYTE_T fragment_is_last; // frame sequence's fragment is last fragment
} TKL_VENC_FRAME_T; // video frame
#define TAL_VENC_FRAME_T TKL_VENC_FRAME_T
typedef INT_T (*TKL_VENC_PUT_CB)(TKL_VENC_FRAME_T *pframe);
IPC 设备,暂时只支持 1 路 VI,count
为 1。
/**
* @brief vi init
*
* @param[in] pconfig: vi config
* @param[in] count: count of pconfig
*
* @return OPRT_OK on success. Others on error, please refer to tkl_error_code.h
*/
OPERATE_RET tal_vi_init(TAL_VI_CONFIG_T *pconfig, INT32_T count);
/**
* @brief vi set
*
* @param[in] chn: vi chn
* @param[in] cmd: cmd
* @param[in] parg: parg
*
* @return OPRT_OK on success. Others on error, please refer to tkl_error_code.h
*/
OPERATE_RET tal_vi_set(INT32_T chn, TAL_VI_CMD_E cmd, VOID *parg);
/**
* @brief vi get
*
* @param[in] chn: vi chn
* @param[in] cmd: cmd
* @param[out] parg: parg
*
* @return OPRT_OK on success. Others on error, please refer to tkl_error_code.h
*/
OPERATE_RET tal_vi_get(INT32_T chn, TAL_VI_CMD_E cmd, VOID *parg);
/**
* @brief vi uninit
*
* @return OPRT_OK on success. Others on error, please refer to tkl_error_code.h
*/
OPERATE_RET tal_vi_uninit(VOID);
IPC 设备,暂时只支持 1 路 VI,vi_chn
传 0。
/**
* @brief video encode init
*
* @param[in] vi_chn: vi channel number
* @param[in] pconfig: venc config
* @param[in] count: count of pconfig
*
* @return OPRT_OK on success. Others on error, please refer to tkl_error_code.h
*/
OPERATE_RET tal_venc_init(INT32_T vi_chn, TAL_VENC_CONFIG_T *pconfig, INT32_T count);
/**
* @brief video set
*
* @param[in] vi_chn: vi channel number
* @param[in] venc_chn: venc chn
* @param[in] cmd: cmd
* @param[in] parg: parg
*
* @return OPRT_OK on success. Others on error, please refer to tkl_error_code.h
*/
OPERATE_RET tal_venc_set(INT32_T vi_chn, INT32_T venc_chn, TAL_VENC_CMD_E cmd, VOID *parg);
/**
* @brief video get
*
* @param[in] vi_chn: vi channel number
* @param[in] venc_chn: venc chn
* @param[in] cmd: cmd
* @param[out] parg: parg
*
* @return OPRT_OK on success. Others on error, please refer to tkl_error_code.h
*/
OPERATE_RET tal_venc_get(INT32_T vi_chn, INT32_T venc_chn, TAL_VENC_CMD_E cmd, VOID *parg);
/**
* @brief video encode get frame
*
* @param[in] vi_chn: vi channel number
* @param[in] venc_chn: venc channel number
* @param[out] pframe: output frame
*
* @return OPRT_OK on success. Others on error, please refer to tkl_error_code.h
*/
OPERATE_RET tal_venc_get_frame(INT32_T vi_chn, INT32_T venc_chn, TAL_VENC_FRAME_T *pframe);
/**
* @brief video encode uninit
*
* @param[in] vi_chn: vi channel number
*
* @return OPRT_OK on success. Others on error, please refer to tkl_error_code.h
*/
OPERATE_RET tal_venc_uninit(INT32_T vi_chn);
// VI 参数配置及初始化:
TAL_VI_CONFIG_T *pvi = NULL;
// 当前 ipc 设备只有一路 vi
pvi = &gMediaInfo.vi[0];
pvi->enable = true;
pvi->chn = TKL_VI_0;
pvi->mirror = ISP_SENSOR_MIRROR; // 初始状态的镜像翻转
pvi->filp = ISP_SENSOR_FILP;
memcpy(pvi->isp.conf, ISP_SENSOR_CONF, sizeof(pvi->isp.conf)); // isp 配置文件
memcpy(pvi->isp.version, ISP_FW_VERSION, sizeof(pvi->isp.version));
memcpy(pvi->isp.name, ISP_SENSOR_NAME, sizeof(pvi->isp.name));
pvi->isp.width = ISP_SENSOR_WIDTH;
pvi->isp.height = ISP_SENSOR_HEIGHT;
memcpy(&pvi->isp.isp_dn_switch_config, &g_Tuya_DN_SWITCH_CONFIG_DAY, sizeof(pvi->isp.isp_dn_switch_config));
...
ret = tal_vi_init(pinfo->vi, 1);
...
// Venc 参数配置
S_DSP_MEDIA_INFO gMediaInfo = {
/* 主码流 */
.venc[E_DSP_MEDIA_VIDEO_MAIN].enable = 1,
.venc[E_DSP_MEDIA_VIDEO_MAIN].chn = TKL_VENC_0,
.venc[E_DSP_MEDIA_VIDEO_MAIN].type = TKL_VENC_MAIN,
.venc[E_DSP_MEDIA_VIDEO_MAIN].fps = 20,
.venc[E_DSP_MEDIA_VIDEO_MAIN].gop = 40,
.venc[E_DSP_MEDIA_VIDEO_MAIN].bitrate = 1024,
.venc[E_DSP_MEDIA_VIDEO_MAIN].width = 1920,
.venc[E_DSP_MEDIA_VIDEO_MAIN].height = 1080,
.venc[E_DSP_MEDIA_VIDEO_MAIN].codectype = 4,
.venc[E_DSP_MEDIA_VIDEO_MAIN].min_qp = 26,
.venc[E_DSP_MEDIA_VIDEO_MAIN].max_qp = 48,
/* 子码流 */
.venc[E_DSP_MEDIA_VIDEO_SUB].enable = 1,
.venc[E_DSP_MEDIA_VIDEO_SUB].chn = TKL_VENC_1,
.venc[E_DSP_MEDIA_VIDEO_SUB].type = TKL_VENC_SUB,
.venc[E_DSP_MEDIA_VIDEO_SUB].fps = 20,
.venc[E_DSP_MEDIA_VIDEO_SUB].gop = 40,
.venc[E_DSP_MEDIA_VIDEO_SUB].bitrate = 512,
.venc[E_DSP_MEDIA_VIDEO_SUB].width = 640,
.venc[E_DSP_MEDIA_VIDEO_SUB].height = 360,
.venc[E_DSP_MEDIA_VIDEO_SUB].codectype = 2,
.venc[E_DSP_MEDIA_VIDEO_SUB].min_qp = 26,
.venc[E_DSP_MEDIA_VIDEO_SUB].max_qp = 48,
/* 第三码流,预留 */
.venc[E_DSP_MEDIA_VIDEO_3RD].enable = 0,
.venc[E_DSP_MEDIA_VIDEO_3RD].chn = TKL_VENC_2,
.venc[E_DSP_MEDIA_VIDEO_3RD].type = TKL_VENC_3RD,
.venc[E_DSP_MEDIA_VIDEO_3RD].fps = 0,
.venc[E_DSP_MEDIA_VIDEO_3RD].gop = 0,
.venc[E_DSP_MEDIA_VIDEO_3RD].bitrate = 0,
.venc[E_DSP_MEDIA_VIDEO_3RD].width = 0,
.venc[E_DSP_MEDIA_VIDEO_3RD].height = 0,
.venc[E_DSP_MEDIA_VIDEO_3RD].codectype = 0,
.venc[E_DSP_MEDIA_VIDEO_3RD].min_qp = 0,
.venc[E_DSP_MEDIA_VIDEO_3RD].max_qp = 0,
/* 第四码流,预留 */
.venc[E_DSP_MEDIA_VIDEO_4TH].enable = 0,
.venc[E_DSP_MEDIA_VIDEO_4TH].chn = TKL_VENC_3,
.venc[E_DSP_MEDIA_VIDEO_4TH].type = TKL_VENC_4TH,
.venc[E_DSP_MEDIA_VIDEO_4TH].fps = 0,
.venc[E_DSP_MEDIA_VIDEO_4TH].gop = 0,
.venc[E_DSP_MEDIA_VIDEO_4TH].bitrate = 0,
.venc[E_DSP_MEDIA_VIDEO_4TH].width = 0,
.venc[E_DSP_MEDIA_VIDEO_4TH].height = 0,
.venc[E_DSP_MEDIA_VIDEO_4TH].codectype = 0,
.venc[E_DSP_MEDIA_VIDEO_4TH].min_qp = 0,
.venc[E_DSP_MEDIA_VIDEO_4TH].max_qp = 0,
/* 抓图 */
.venc[E_DSP_MEDIA_VIDEO_SNAP].enable = 1,
.venc[E_DSP_MEDIA_VIDEO_SNAP].chn = TKL_VENC_4,
.venc[E_DSP_MEDIA_VIDEO_SNAP].type = TKL_VENC_SNAP,
.venc[E_DSP_MEDIA_VIDEO_SNAP].fps = 20,
.venc[E_DSP_MEDIA_VIDEO_SNAP].gop = 40,
.venc[E_DSP_MEDIA_VIDEO_SNAP].bitrate = 512,
.venc[E_DSP_MEDIA_VIDEO_SNAP].width = 640,
.venc[E_DSP_MEDIA_VIDEO_SNAP].height = 360,
.venc[E_DSP_MEDIA_VIDEO_SNAP].codectype = 3,
.venc[E_DSP_MEDIA_VIDEO_SNAP].min_qp = 15,
.venc[E_DSP_MEDIA_VIDEO_SNAP].max_qp = 15,
/* 动检 */
.venc[E_DSP_MEDIA_VIDEO_MD].enable = 1,
.venc[E_DSP_MEDIA_VIDEO_MD].chn = TKL_VENC_5,
.venc[E_DSP_MEDIA_VIDEO_MD].type = TKL_VENC_MD,
.venc[E_DSP_MEDIA_VIDEO_MD].fps = 20,
.venc[E_DSP_MEDIA_VIDEO_MD].gop = 40,
.venc[E_DSP_MEDIA_VIDEO_MD].bitrate = 512,
.venc[E_DSP_MEDIA_VIDEO_MD].width = 320,
.venc[E_DSP_MEDIA_VIDEO_MD].height = 180,
.venc[E_DSP_MEDIA_VIDEO_MD].codectype = 5,
/* 人形 */
.venc[E_DSP_MEDIA_VIDEO_HD].enable = 1,
.venc[E_DSP_MEDIA_VIDEO_HD].chn = TKL_VENC_6,
.venc[E_DSP_MEDIA_VIDEO_HD].type = TKL_VENC_HD,
.venc[E_DSP_MEDIA_VIDEO_HD].fps = 20,
.venc[E_DSP_MEDIA_VIDEO_HD].gop = 40,
.venc[E_DSP_MEDIA_VIDEO_HD].bitrate = 512,
.venc[E_DSP_MEDIA_VIDEO_HD].width = 512,
.venc[E_DSP_MEDIA_VIDEO_HD].height = 288,
.venc[E_DSP_MEDIA_VIDEO_HD].codectype = 5,
/* 二维码 */
.venc[E_DSP_MEDIA_VIDEO_QR].enable = 1,
.venc[E_DSP_MEDIA_VIDEO_QR].chn = TKL_VENC_7,
.venc[E_DSP_MEDIA_VIDEO_QR].type = TKL_VENC_QR,
.venc[E_DSP_MEDIA_VIDEO_QR].fps =25,
.venc[E_DSP_MEDIA_VIDEO_QR].gop = 50,
.venc[E_DSP_MEDIA_VIDEO_QR].bitrate = 512,
.venc[E_DSP_MEDIA_VIDEO_QR].width = 640,
.venc[E_DSP_MEDIA_VIDEO_QR].height = 360,
.venc[E_DSP_MEDIA_VIDEO_QR].codectype = 5,
// venc 初始化:
ret = tal_venc_init(0, pinfo->venc, 8);
...
// 主动获取主码流,主码流建议分配 300k:
TAL_VENC_FRAME_T frame = {0};
frame.pbuf = (UCHAR_T *)malloc(300*1024);
frame.buf_size = 300*1024;
ret = tal_venc_get_frame(0, 0, &frame)
...
// 获取子码流:
frame.pbuf = (UCHAR_T *)malloc(100*1024);
frame.buf_size = 100*1024;
ret = tal_venc_get_frame(0, 1, &frame)
// 抓图
frame.pbuf = (UCHAR_T *)malloc(150*1024);
frame.buf_size = 150*1024;
ret = tal_venc_get_frame(0, TKL_VENC_SNAP, &frame)
...
// 设置 osd 开关
TKL_VENC_OSD_T osd = {0};
osd.enable = 1; // 开关
osd.is_dls = 0; // 是否是夏令时
ret = tal_venc_set(0, 0, TAL_VENC_CMD_OSD, &osd);
...
// 设置镜像翻转
TKL_VI_MIRROR_FLIP_E val = TKL_VI_MIRROR_FLIP_NONE;
ret =tal_vi_set(0, TAL_VI_CMD_MIRROR_FLIP, &val);
...
val = TKL_VI_MIRROR_FLIP;
ret = tal_vi_set(0, TAL_VI_CMD_MIRROR_FLIP, &val);
该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈