当前位置:   article > 正文

imx6 vpu程序分析_libimxvpuapi

libimxvpuapi

imx6 vpu程序分析

背景

最近公司需要将产品与外界的设备进行流媒体通信,经过一系列的方案研究确立,最终把功能完成,目前能够顺利的播放基于h264的流媒体文件,趁着闲暇时间对相关的东西做一些笔记记录,方便以后追溯。
这里主要分析imx6 的vpu 测试程序,有一部分功能是基于这个来实现的。考虑到 imx6 的 vpu 固件代码不开源,相关的vpu 操作代码根据文档来执行,其接口函数看文档就行,本篇就不过多阐述,驱动也不加以分析。简单来说,就是vpu 的应用以及注意点。

版本

linux内核版本:3.10.17
vpu 测试程序版本:imx-test-3.10.17

程序功能说明
  • 处理的任务:-D 解码 -E 编码 -L Loopback模式 -C 解析文件,从文件中获取参数的设置
  • 如果有多个任务,会为每个任务开启一个线程来进行处理(解码任务、编码任务)
  • 输入方式为:文件 和 网络(udp),文件:对应格式的文件(h264、MP4等)
    输出方式为:文件 和 网络,文件:普通文件(存储)、网络文件(发送出去),IPU(imx6x不适用),video17(调用v4l2架构直接显示)

关键源码注释

main.c main() 函数

int
#ifdef _FSL_VTS_
vputest_main(int argc, char *argv[])
#else
main(int argc, char *argv[])
#endif
{
	int err, nargc, i, ret = 0;
	char *pargv[32] = {0}, *dbg_env;
	pthread_t sigtid;
#ifdef COMMON_INIT
	vpu_versioninfo ver;
#endif
	int ret_thr;

#ifndef COMMON_INIT
	srand((unsigned)time(0)); /* init seed of rand() */
#endif

	dbg_env=getenv("VPU_TEST_DBG");
	if (dbg_env)
		vpu_test_dbg_level = atoi(dbg_env);
	else
		vpu_test_dbg_level = 0;

    /* 解析主要的参数选项 : -D(vpu解码) -E(vpu编码) -L(Loopback模式) -C(从文件中获取参数)
     *   重点1:当为-C时,会在该函数中解析出文件中的参数,除此之外的,只标记是哪种任务,后面通过  
     *          parse_args 函数进行解析 
     *   重点2: 解析出来的参数放在结构体 input_arg 中:它是一个全局变量,为 
     *          static struct input_argument input_arg[MAX_NUM_INSTANCE] 结构体数组类型*/
	err = parse_main_args(argc, argv); 
	if (err) {
		goto usage;
	}

    /* instance: 用来标记任务实例个数的,例如输入的参数中可能包含 -D -E,即解码、编码混合,那么就是两个
     *          instance, 这个关系到后面的开任务的个数,如果为一个instance的话,后面将只有一个线程来处理,
     *          如果有多个的话,会按照任务个数来开启对应的线程进行处理 */
	if (!instance) {
		goto usage;
	}

	info_msg("VPU test program built on %s %s\n", __DATE__, __TIME__);
#ifndef _FSL_VTS_
	sigemptyset(&sigset);
	sigaddset(&sigset, SIGINT);
	pthread_sigmask(SIG_BLOCK, &sigset, NULL);
	pthread_create(&sigtid, NULL, (void *)&signal_thread, NULL);
#endif

#ifdef COMMON_INIT
	err = vpu_Init(NULL);     /* 有关于vpu的操作,初始化 */
	if (err) {
		err_msg("VPU Init Failure.\n");
		return -1;
	}

	err = vpu_GetVersionInfo(&ver);  /* 有关于vpu的操作,得到版本信息 */
	if (err) {
		err_msg("Cannot get version info, err:%d\n", err);
		vpu_UnInit();
		return -1;
	}

	info_msg("VPU firmware version: %d.%d.%d_r%d\n", ver.fw_major, ver.fw_minor,
						ver.fw_release, ver.fw_code);
	info_msg("VPU library version: %d.%d.%d\n", ver.lib_major, ver.lib_minor,
						ver.lib_release);
#else
	// just to enable cpu_is_xx() to be used in command line parsing
	err = vpu_Init(NULL);
	if (err) {
		err_msg("VPU Init Failure.\n");
		return -1;
	}

	vpu_UnInit();

#endif

    /* 下面的就是根据instance 的个数来进行相应的操作,当个数大于1的时候会开启对应个数线程来
     * 进行相应的任务操作 */
	if (instance > 1) {
		for (i = 0; i < instance; i++) {
#ifndef COMMON_INIT
			/* sleep roughly a frame interval to test multi-thread race
			   especially vpu_Init/vpu_UnInit */
			usleep((int)(rand()%ONE_FRAME_INTERV));
#endif
			if (using_config_file == 0) {  /* 这里重点就是是否从文件中得到相应的参数,如果不是,
			                                * 还需要进行上面提到过的进一步的细化解析命令行参数
			                                *    parse_args() 函数来完成,具体实现见代码 */
				get_arg(input_arg[i].line, &nargc, pargv);
				err = parse_args(nargc, pargv, i);
				if (err) {
					vpu_UnInit();
					goto usage;
				}
			}

            /* check_params(): 在真正使用得到的各种参数之前对参数进行一遍检查,有些默认的东西会在此添加
             * 这里有一点需要注意:在上面的参数赋值中如果参数的来源来自于解析文件,而decode时候的输入方式
             * 又空着没有指定的话,在这里面会把输入方式指定为从网络输入 */
			if (check_params(&input_arg[i].cmd,
						input_arg[i].mode) == 0) {
			    /* open_files(): 把上面参数指定的输入文件、输出文件一次性打开,返回句柄供下面使用
                 * 注意点:输入方式为:文件 和 网络(udp),文件:对应格式的文件(h264、MP4等)
                 *         输出方式为:文件 和 网络,文件:普通文件(存储)、网络文件(发送出去),IPU(imx6x不适用)*/
				if (open_files(&input_arg[i].cmd) == 0) {
					if (input_arg[i].mode == DECODE) {
                        /* 重点: 在这里就是上面说的多个instance的时候了,会通过创建对应的线程来进行任务的操作:
                         *        decode解码任务 和 encode 编码任务*/
					     pthread_create(&input_arg[i].tid,
						   NULL,
						   (void *)&decode_test,   /* 任务开始 */
						   (void *)&input_arg[i].cmd);
					} else if (input_arg[i].mode ==
							ENCODE) {
					     pthread_create(&input_arg[i].tid,
						   NULL,
						   (void *)&encode_test,   /* 任务开始 */
						   (void *)&input_arg[i].cmd);
					}
				}
			}

		}
	} else {
		if (using_config_file == 0) {
			get_arg(input_arg[0].line, &nargc, pargv);
			err = parse_args(nargc, pargv, 0);  /* 解读同上 */
			if (err) {
				vpu_UnInit();
				goto usage;
			}
		}

		if (check_params(&input_arg[0].cmd, input_arg[0].mode) == 0) {  /* 解读同上 */
			if (open_files(&input_arg[0].cmd) == 0) {   /* 解读同上 */
				if (input_arg[0].mode == DECODE) {
					ret = decode_test(&input_arg[0].cmd);   /* 任务开始 */
				} else if (input_arg[0].mode == ENCODE) {
					ret = encode_test(&input_arg[0].cmd);   /* 任务开始 */
                                } else if (input_arg[0].mode == TRANSCODE) {
                                        ret = transcode_test(&input_arg[0].cmd);
				}

				close_files(&input_arg[0].cmd);
			} else {
				ret = -1;
			}
		} else {
			ret = -1;
		}

		if (input_arg[0].mode == LOOPBACK) {
			encdec_test(&input_arg[0].cmd);
		}
	}

    /* 等待对应的线程任务结束 */
	if (instance > 1) {
		for (i = 0; i < instance; i++) {
			if (input_arg[i].tid != 0) {
				pthread_join(input_arg[i].tid, (void *)&ret_thr);
				if (ret_thr)
					ret = -1;
				close_files(&input_arg[i].cmd);
			}
		}
	}

#ifdef COMMON_INIT
	vpu_UnInit();    /* vpu操作,uninit,资源的注销等 */
#endif
	return ret;

usage:
	info_msg("\n%s", usage);
	return -1;
}
  • 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
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181

dec.c 中decode_test() 函数

int
decode_test(void *arg)
{
	struct cmd_line *cmdl = (struct cmd_line *)arg;
	vpu_mem_desc mem_desc = {0};
	vpu_mem_desc ps_mem_desc = {0};
	vpu_mem_desc slice_mem_desc = {0};
	vpu_mem_desc vp8_mbparam_mem_desc = {0};
	struct decode *dec;
	int ret, eos = 0, fill_end_bs = 0, fillsize = 0;

#ifndef COMMON_INIT
	vpu_versioninfo ver;
	ret = vpu_Init(NULL);
	if (ret) {
		err_msg("VPU Init Failure.\n");
		return -1;
	}

	ret = vpu_GetVersionInfo(&ver);
	if (ret) {
		err_msg("Cannot get version info, err:%d\n", ret);
		vpu_UnInit();
		return -1;
	}

	info_msg("VPU firmware version: %d.%d.%d_r%d\n", ver.fw_major, ver.fw_minor,
						ver.fw_release, ver.fw_code);
	info_msg("VPU library version: %d.%d.%d\n", ver.lib_major, ver.lib_minor,
						ver.lib_release);
#endif

	vpu_v4l_performance_test = 0;

	dec = (struct decode *)calloc(1, sizeof(struct decode));
	if (dec == NULL) {
		err_msg("Failed to allocate decode structure\n");
		ret = -1;
		goto err;
	}

	mem_desc.size = STREAM_BUF_SIZE;
	ret = IOGetPhyMem(&mem_desc);       /* 得到物理地址 */
	if (ret) {
		err_msg("Unable to obtain physical mem\n");
		goto err;
	}

	if (IOGetVirtMem(&mem_desc) <= 0) { /* 得到虚拟地址 */
		err_msg("Unable to obtain virtual mem\n");
		ret = -1;
		goto err;
	}

    /* decode解码任务的设置阶段,注意重要的数据结构 struct decode *dec,
     *  数据存储于此 */
	dec->phy_bsbuf_addr = mem_desc.phy_addr;
	dec->virt_bsbuf_addr = mem_desc.virt_uaddr;

	dec->reorderEnable = 1;
	dec->tiled2LinearEnable = 0;

	dec->userData.enable = 0;
	dec->mbInfo.enable = 0;
	dec->mvInfo.enable = 0;
	dec->frameBufStat.enable = 0;
	dec->mjpgLineBufferMode = 0;
	dec->mjpegScaleDownRatioWidth = 0;  /* 0,1,2,3 */
	dec->mjpegScaleDownRatioHeight = 0; /* 0,1,2,3 */

	dec->cmdl = cmdl;

	if (cpu_is_mx6x() && (dec->cmdl->format == STD_MJPG)
			&& dec->mjpgLineBufferMode) {
		dec->mjpg_cached_bsbuf = malloc(STREAM_BUF_SIZE);
		if (dec->mjpg_cached_bsbuf == NULL) {
			err_msg("Failed to allocate mjpg_cached_bsbuf\n");
			ret = -1;
			goto err;
		}
	}

	if (cmdl->format == STD_RV)
		dec->userData.enable = 0; /* RV has no user data */

	if (cmdl->format == STD_AVC) {
		ps_mem_desc.size = PS_SAVE_SIZE;
		ret = IOGetPhyMem(&ps_mem_desc);
		if (ret) {
			err_msg("Unable to obtain physical ps save mem\n");
			goto err;
		}
		dec->phy_ps_buf = ps_mem_desc.phy_addr;

	}

	/* open decoder */
	ret = decoder_open(dec);   /* 里面就是根据上面的设置进行vpu的打开操作 */
	if (ret)
		goto err;

	cmdl->complete = 1;
	if (dec->cmdl->src_scheme == PATH_NET)
		fillsize = 1024;

	if (cpu_is_mx6x() && (dec->cmdl->format == STD_MJPG) && dec->mjpgLineBufferMode) {
		ret = mjpg_read_chunk(dec);
		if (ret < 0)
			goto err1;
		else if (ret == 0) {
			err_msg("no pic in the clip\n");
			ret = -1;
			goto err1;
		}
	} else {
		ret = dec_fill_bsbuffer(dec->handle, cmdl,
				dec->virt_bsbuf_addr,
				(dec->virt_bsbuf_addr + STREAM_BUF_SIZE),
				dec->phy_bsbuf_addr, fillsize, &eos, &fill_end_bs);

		if (fill_end_bs)
			err_msg("Update 0 before seqinit, fill_end_bs=%d\n", fill_end_bs);

		if (ret < 0) {
			err_msg("dec_fill_bsbuffer failed\n");
			goto err1;
		}
	}
	cmdl->complete = 0;

	/* parse the bitstream */
	ret = decoder_parse(dec);   /* 解析得到的第一帧数据,其实就是在解析数据头,
	                             * 然后配置接下来的动态性的参数,例如数据的格式(h264、h263或者mpeg等)
	                             * 一帧数据的大小(540 * 480)*/
	if (ret) {
		err_msg("decoder parse failed\n");
		goto err1;
	}

	/* allocate slice buf */
	if (cmdl->format == STD_AVC) {
		slice_mem_desc.size = dec->phy_slicebuf_size;
		ret = IOGetPhyMem(&slice_mem_desc);
		if (ret) {
			err_msg("Unable to obtain physical slice save mem\n");
			goto err1;
		}
		dec->phy_slice_buf = slice_mem_desc.phy_addr;
	}

	if (cmdl->format == STD_VP8) {
		vp8_mbparam_mem_desc.size = 68 * (dec->picwidth * dec->picheight / 256);
		ret = IOGetPhyMem(&vp8_mbparam_mem_desc);
		if (ret) {
			err_msg("Unable to obtain physical vp8 mbparam mem\n");
			goto err1;
		}
		dec->phy_vp8_mbparam_buf = vp8_mbparam_mem_desc.phy_addr;
	}

	/* allocate frame buffers */
	ret = decoder_allocate_framebuffer(dec);     /* 缓存空间的准备 */
	if (ret)
		goto err1;

	/* start decoding */
    /* 里面就是对vpu的一些实质性的操作,然后将vpu处理后的数据经过解析后进行相应的操作:
     *    1. 往ipu里面写?2.通过v4l2往video17 里面写?使之显示
     *      或者 3.仅仅作为文件数据直接写到普通文件中,保存下来
     *    提示,项目中把数据拿出来,放到gpu里面去渲染就是走的往普通文件写的流程,把本应该
     *      往普通文件里面写的数据给导流到gpu 中渲染,最终的呈现在屏幕上
     *          另外,在这里还使用了ipu 对数据进行格式的变换:vpu处理得到的是 yuv420,输入到gpu为rgb
     *          (关于ipu 和 gpu 的东西是另外一个程序中涉及到的,这里是因为在具体的项目中使用到了,
     *            留在这里做以后的提醒使用,在这里不展开阐述其使用)*/
	ret = decoder_start(dec);          
err1:
	decoder_close(dec);
	/* free the frame buffers */
	decoder_free_framebuffer(dec);
err:
	if (cmdl->format == STD_AVC) {
		IOFreePhyMem(&slice_mem_desc);
		IOFreePhyMem(&ps_mem_desc);
	}

	if (cmdl->format == STD_VP8)
		IOFreePhyMem(&vp8_mbparam_mem_desc);

	if (dec->mjpg_cached_bsbuf)
		free(dec->mjpg_cached_bsbuf);
	IOFreeVirtMem(&mem_desc);
	IOFreePhyMem(&mem_desc);
	if (dec)
		free(dec);
#ifndef COMMON_INIT
	vpu_UnInit();
#endif
	return ret;
}
  • 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
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199

后记

浏览了一下,通过对关键点的提取,关键代码的注释,拿到源码后,应该可以很好的切入了,或回忆或熟悉会事半功倍。
里面拓展提到的一些东西是项目中实际中遇到的,有些东西没有说太细,但是基本的东西摸清楚了,后面的东西看是否能熟练使用以及变化了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/395594
推荐阅读
相关标签
  

闽ICP备14008679号