准备资料
一、硬件资源
可以看到x2000是拥有2个ISP模块的。
选择通过ISP模块时,camera 设备节点名为 mscaler。
- 开启单摄时为 mscaler0 或 mscaler1,对应的是 sensor0 或 sensor1;
- 开启双摄时为 mscaler0 和 mscaler1,分别对应 sensor0 和 sensor1;
- 每个mscaler支持3个 channel,故完整的设备名为 mscalerx-chx.
mscaler0-ch0 表示isp0,通道0的设备节点。
从框图看,sensor0和sensor1都有对应的独立ISP,以我的开发板为例,RGB摄像头接的是MIPI-CSI 1,即对应的设备节点为mscaler1,可以利用mscaler1-ch0和mscaler1-ch1实现一个双通道编码应用。
二、编码节点
X2000将vpu分为felix和helix两部分:
- felix是h264解码,包括:流解析器、运动补偿、反量化、IDCT和De-block engines的功能。
- helix是h264编码、JPEG压缩和解压缩。
三、设计需求
- 设计两个通道,分别为主码流和子码流。
- 主码流参数:1080p@30fps,H264编码,以文件形式保存;
- 子码流参数:720p@30fps,H264编码,以文件形式保存;
四、设计思路
再回到数据流框图
也就是说,sensor的mipi数据通过,VIC单元控制送如ISP模块,再通过Mscaler节点输出,因此只需要从对应的Mscaler节点拿到sensor数据,然后送给/dev/video1编码器节点进行编码即可。
五、代码实现
参考君正给的SDK内的demo加以修改,路径:ingenic/x2000/libisp/src/demo/
ISP格式配置——
/* isp格式配置 */
static struct frame_image_format output_fmt_main = {
.width = 1920,
.height = 1080,
.pixel_format = CAMERA_PIX_FMT_NV12,
.scaler.enable = 0,
.scaler.width = 0,
.scaler.height = 0,
.frame_nums = 2,
};
static struct frame_image_format output_fmt_sub = {
.width = 1280,
.height = 720,
.pixel_format = CAMERA_PIX_FMT_NV12,
.scaler.enable = 0,
.scaler.width = 0,
.scaler.height = 0,
.frame_nums = 2,
};
ISP初始化——
参考这个流程图
static int init_isp_camera(char * isp_cam_path, struct frame_image_format * pformat, struct camera_info * info)
{
int ret;
int fd;
fd = isp_open(isp_cam_path);
if (fd < 0) {
fprintf(stderr, "isp open failed\n");
return -1;
}
ret = isp_get_sensor_info(fd, info);
if (ret < 0) {
fprintf(stderr, "isp get sensor info failed\n");
goto close_isp;
}
if (pformat->width != 0 || pformat->height != 0)
pformat->scaler.enable = 1;
pformat->scaler.width = pformat->width ? pformat->width : info->width;
pformat->scaler.height = pformat->height ? pformat->height : info->height;
pformat->width = pformat->scaler.width;
pformat->height = pformat->scaler.height;
ret = isp_set_format(fd, pformat);
if (ret < 0) {
fprintf(stderr, "isp set format failed\n");
goto close_isp;
}
ret = isp_requset_buffer(fd, pformat);
if (ret < 0) {
fprintf(stderr, "isp set request buffer failed\n");
goto close_isp;
}
isp_get_info(fd, info);
ret = isp_mmap(fd, info);
if (ret < 0)
goto free_isp_buffer;
ret = isp_power_on(fd);
if (ret < 0)
goto free_isp_buffer;
ret = isp_stream_on(fd);
if (ret < 0)
goto power_off_isp;
return fd;
power_off_isp:
isp_power_off(fd);
free_isp_buffer:
isp_free_buffer(fd);
close_isp:
isp_close(fd, info);
return -1;
}
主码流实现——
void * main_stream_thread(void * arg)
{
struct v4l2_h264_encoder_config config;
struct frame_info frame_info;
int res = 0;
int frame_cnt = 0;
int output_size = 0;
menc_cfg_main.isp_path = malloc(20);
menc_cfg_main.output_file_path = malloc(20);
menc_cfg_main.encoder_path = malloc(20);
memmove(menc_cfg_main.output_file_path, "output00", 9);
memmove(menc_cfg_main.isp_path, "/dev/mscaler1-ch0", 18);
memmove( menc_cfg_main.encoder_path, "/dev/video1", 12);
menc_cfg_main.camera_fd = -1;
menc_cfg_main.output_file_fd = -1;
memset(&config, 0, sizeof(struct v4l2_h264_encoder_config));
menc_cfg_main.camera_fd = init_isp_camera(menc_cfg_main.isp_path, &output_fmt_main, &menc_cfg_main.camera_info);
if (menc_cfg_main.camera_fd < 0) {
fprintf(stderr, "Init isp camera failed\n");
goto exit;
}
config.video_path = menc_cfg_main.encoder_path;
config.width = output_fmt_main.width;
config.height = output_fmt_main.height;
config.line_length = menc_cfg_main.camera_info.line_length;
config.input_fmt = menc_cfg_main.camera_info.data_fmt;
config.gop_size = 10;
config.bitrate = 400000;
menc_cfg_main.encoder = v4l2_h264_encoder_open(&config);
if (!menc_cfg_main.encoder) {
fprintf(stderr, "Uanble to open v4l2 h264 encoder\n");
res = -1;
goto close_isp_cam;
}
menc_cfg_main.output_file_fd = open(menc_cfg_main.output_file_path, O_RDWR | O_CREAT | O_TRUNC);
if ( menc_cfg_main.output_file_fd < 0) {
fprintf(stderr, "open file %s error.\n", menc_cfg_main.output_file_path);
goto close_encoder;
}
while (1)
{
res = isp_dqbuf_wait(menc_cfg_main.camera_fd, &frame_info);
if (res){
fprintf(stderr, "isp_dqbuf_waitopen file %s error.\n");
goto close_file;
}
if (frame_cnt == 5){
v4l2_h264_encoder_set_keyframe(menc_cfg_main.encoder);
}
void *mem = v4l2_h264_encoder_work_by_phy_mem(menc_cfg_main.encoder, frame_info.vaddr, frame_info.paddr, &output_size);
if (!mem) {
fprintf(stderr, "V4l2 h264 encode work failed, ret : %d, %p\n", res, mem);
res = -1;
goto close_file;
}
isp_put_frame(menc_cfg_main.camera_fd, frame_info.vaddr);
write(menc_cfg_main.output_file_fd, mem, output_size);
if (frame_cnt++ >= 120){
break;
}
}
fprintf(stderr, "frame_cnt:%d\r\n",frame_cnt);
close_file:
close(menc_cfg_main.output_file_fd);
close_encoder:
v4l2_h264_encoder_close(menc_cfg_main.encoder);
close_isp_cam:
deinit_isp_camera(menc_cfg_main.camera_fd, &menc_cfg_main.camera_info);
exit:
menc_cfg_main.camera_fd = -1;
fprintf(stderr, "main_stream_thread exit\r\n");
}
子码流实现——
void * sub_stream_thread(void * arg)
{
struct v4l2_h264_encoder_config config;
struct frame_info frame_info;
int res = 0;
int frame_cnt = 0;
int output_size = 0;
menc_cfg_sub.isp_path = malloc(20);
menc_cfg_sub.output_file_path = malloc(20);
menc_cfg_sub.encoder_path = malloc(20);
memmove(menc_cfg_sub.output_file_path, "output01", 9);
memmove(menc_cfg_sub.isp_path, "/dev/mscaler1-ch1", 18);
memmove( menc_cfg_sub.encoder_path, "/dev/video1", 12);
menc_cfg_sub.camera_fd = -1;
menc_cfg_sub.output_file_fd = -1;
memset(&config, 0, sizeof(struct v4l2_h264_encoder_config));
menc_cfg_sub.camera_fd = init_isp_camera(menc_cfg_sub.isp_path, &output_fmt_sub, &menc_cfg_sub.camera_info);
if (menc_cfg_sub.camera_fd < 0) {
fprintf(stderr, "Init isp camera failed\n");
goto exit;
}
config.video_path = menc_cfg_sub.encoder_path;
config.width = output_fmt_sub.width;
config.height = output_fmt_sub.height;
config.line_length = menc_cfg_sub.camera_info.line_length;
config.input_fmt = menc_cfg_sub.camera_info.data_fmt;
config.gop_size = 10;
config.bitrate = 400000;
menc_cfg_sub.encoder = v4l2_h264_encoder_open(&config);
if (!menc_cfg_sub.encoder) {
fprintf(stderr, "Uanble to open v4l2 h264 encoder\n");
res = -1;
goto close_isp_cam;
}
menc_cfg_sub.output_file_fd = open(menc_cfg_sub.output_file_path, O_RDWR | O_CREAT | O_TRUNC);
if ( menc_cfg_sub.output_file_fd < 0) {
fprintf(stderr, "open file %s error.\n", menc_cfg_sub.output_file_path);
goto close_encoder;
}
while (1)
{
res = isp_dqbuf_wait(menc_cfg_sub.camera_fd, &frame_info);
if (res){
fprintf(stderr, "isp_dqbuf_waitopen file %s error.\n");
goto close_file;
}
if (frame_cnt == 5){
v4l2_h264_encoder_set_keyframe(menc_cfg_sub.encoder);
}
void *mem = v4l2_h264_encoder_work_by_phy_mem(menc_cfg_sub.encoder, frame_info.vaddr, frame_info.paddr, &output_size);
if (!mem) {
fprintf(stderr, "V4l2 h264 encode work failed, ret : %d, %p\n", res, mem);
res = -1;
goto close_file;
}
isp_put_frame(menc_cfg_sub.camera_fd, frame_info.vaddr);
write(menc_cfg_sub.output_file_fd, mem, output_size);
if (frame_cnt++ >= 120){
break;
}
}
fprintf(stderr, "frame_cnt:%d\r\n",frame_cnt);
close_file:
close(menc_cfg_sub.output_file_fd);
close_encoder:
v4l2_h264_encoder_close(menc_cfg_sub.encoder);
close_isp_cam:
deinit_isp_camera(menc_cfg_sub.camera_fd, &menc_cfg_sub.camera_info);
exit:
menc_cfg_sub.camera_fd = -1;
fprintf(stderr, "sub_stream_thread exit\r\n");
}
main函数实现——
int main(void)
{
int res = 0;
signal(SIGINT, signal_handler);
res = pthread_create(&main_stream_thread_id, NULL, main_stream_thread, NULL);
if(res != 0){
printf("main stream pthread create failed.\r\n");
}
res = pthread_create(&sub_stream_thread_id, NULL, sub_stream_thread, NULL);
if(res != 0){
printf("sub stream pthread create failed.\r\n");
}
while (1)
{
sleep(1);
}
fprintf(stderr, "exit.\n");
return 0;
}
板端执行——
输出名为output00和output01的两个h264文件