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_INITvpu_versioninfo ver;
#endifint ret_thr;#ifndef COMMON_INITsrand((unsigned)time(0)); /* init seed of rand() */
#endifdbg_env=getenv("VPU_TEST_DBG");if (dbg_env)vpu_test_dbg_level = atoi(dbg_env);elsevpu_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_INITerr = 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 parsingerr = 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 raceespecially vpu_Init/vpu_UnInit */usleep((int)(rand()%ONE_FRAME_INTERV));
#endifif (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_INITvpu_UnInit();    /* vpu操作,uninit,资源的注销等 */
#endifreturn ret;usage:info_msg("\n%s", usage);return -1;
}

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_INITvpu_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);
#endifvpu_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_INITvpu_UnInit();
#endifreturn ret;
}

后记

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

imx6 vpu程序分析相关推荐

  1. [imx6 VPU]硬解码+示例[ffmpeg获取海康rtsp h264流 QT显示]

    0.说明: 1,代码基于imx6q.imx6dl已验证. 2,网上关于imx6 VPU的资料很少,遂从官方例程mxc_vpu_test里面活生生抽出来.主要是dec_test()里面提取,因为我只要解 ...

  2. 程序分析工具gprof介绍

    程序分析是以某种语言书写的程序为对象,对其内部的运作流程进行分析.程序分析的目的主要有三点:一是通过程序内部各个模块之间的调用关系,整体上把握程序的运行流程,从而更好地理解程序,从中汲取有价值的内容. ...

  3. 字节跳动pest分析_字节跳动小程序分析:前景及优势都是什么?

    近几年小程序渐渐成为微信.百度.支付宝等巨头的标配,各大互联网巨头纷纷加码小程序,字节跳动自然也不甘落后.字节跳动小程序前景如何呢?我们来做一个详细的字节跳动小程序分析: 1.平台条件 小程序要想做起 ...

  4. 第四周项目四-程序分析(4)

    /**Copyright(c)2016,烟台大学计算机与控制工程学院*All rights reserved*文件名称:123.cpp*作 者:王蕊*完成日期:2016年3月23日*版 本 号:v1. ...

  5. 静态程序分析chapter3 - 数据流分析详述(Reaching Definitions、Live Variables、Available Expressions Analysis)

    文章目录 二. 数据流分析 introduction1 introduction2 输入和输出状态 转换函数 数据流分析应用 1,Reaching Definitions Analysis 概述 用途 ...

  6. 静态程序分析chapter1 - 概述和两个重要步骤

    文章目录 前言 Static Analysis Rice's Theorem Sound & Complete Sound 示例 小结 抽象和过近似(Abstraction + Over-ap ...

  7. 基于时间片轮转程序分析进程调度

    张雨梅   原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-10000 背景知识 一般程序运行过程中都 ...

  8. c语言报告程序分析报告,2012C语言程序分析报告.doc

    2012C语言程序分析报告 C语言程序设计专周 专 周 报 告 班级:10611 学号:20 姓名: 设计时间:2011-5-30至2011-6-3 一.设计题目:职工工资管理小软件 二.实习目的 1 ...

  9. 微型计算机原理sar,微机原理的题一.程序分析 1.MOV AX,80F0H MOV CL,10H SAR AX,CL ADD AX,80H...

    共回答了15个问题采纳率:86.7% 一.程序分析 1.MOV AX,80F0H;AX=1000 0000 1111 0000B MOV CL,10H;CL=16 SAR AX,CL;对AX算术右移1 ...

最新文章

  1. tensorflow keras numpy 数据 规范化、标准化、归一化
  2. channel rabbitmq 配置_「喵咪MQ(2)」RabbitMQ单机模式使用
  3. 青龙羊毛——顺丰科勒(搬运)
  4. C++ 檔案、資料夾、路徑處理函式庫:boost::filesystem
  5. Apache Qpid:一个AMQP的开源实现
  6. [leetcode]831. 隐藏个人信息
  7. 回天科技工程师房工对十大硬盘数据恢复软件简评!
  8. 写web项目时出现的错误:来自“http://localhost:63342/Demo/test01/day0618/css/a.css”的资源已被阻止,因为 MIME 类型(“text/html”)
  9. 驱动人生安装驱动计算机无法启动,驱动人生怎么安装驱动程序?驱动人生基本功能...
  10. 关于计算机团队名字大全集,有创意的团队名字大全 新颖而有内涵团队名字
  11. EfficientDeRainy:一种高效的图像去雨雾算法
  12. 接口邮件发送平台,定时发送邮件信息
  13. python中倒背如流_倒背如流100首诗词,你眼中的学霸是怎么做到的?
  14. Unity接入Google登录
  15. 1、OpenSearch入门配置
  16. VertiGIS进入下一增长阶段,任命Andy Berry为首席执行官
  17. swfobject.js 详细解说
  18. 航模继电器技术改进点,记录
  19. FPS游戏原理漫谈:玩家延时与服务器同步
  20. 爬取今日头条上的图片

热门文章

  1. css3和html5网站模板
  2. 使用钉钉发送消息(可用于 服务异常通知、定时任务异常通知 等等...)
  3. php引用复制,php引用和拷贝的区别
  4. 手把手教你一小时设计基于matlab的信号发生器GUI界面(1)
  5. linux中安shell怎么传入参数,【linux】linux 下 shell命令 执行结果赋值给变量【两种方式】...
  6. Java8 拉姆达与集合中对象处理方式记录
  7. 移动端自动化测试appium(6)--搭建模拟器和真机环境
  8. OCR目标识别(车辆VIN码识别效果)
  9. 程序员如何通过兼职赚钱?有哪些渠道?
  10. vue 移动端和web端实现文件的点击预览 而非下载