***********《朱有鹏老师嵌入式linux核心课程》 *********** 《6.小项目.图片解码播放器》 -------------------------------------------------------- 本课程由朱老师物联网大讲堂推出并提供技术支持 -------------------------------------------------------- 第一部分、章节目录 6.1.项目展示与整体规划 6.2.环境搭建和基础确认 6.3.开始动手写代码 6.4.framebuffer基本操作代码 6.5.图片显示原理和实践 6.6.图片数据提取和显示 6.7.图片显示的高级话题 6.8.其他显示细节问题 6.9.任意起点位置图片显示1 6.10.任意起点位置图片显示2 6.11.BMP图片的显示1 6.12.BMP图片的显示2 6.13.BMP图片的显示3 6.14.BMP图片的显示4 6.15.BMP图片的显示5 6.16.及时规整才能写出好项目1 6.17.及时规整才能写出好项目2 6.18.及时规整才能写出好项目3 6.19.jpg图片的显示原理分析1 6.20.jpg图片的显示原理分析2 6.21.libjpeg介绍及开源库的使用方法 6.22.libjpeg的移植实战1 6.23.libjpeg的移植实战2 6.24.使用libjpeg解码显示jpg图片1 6.25.使用libjpeg解码显示jpg图片2 6.26.使用libjpeg解码显示jpg图片3 6.27.使用libjpeg解码显示jpg图片4 6.28.使用libjpeg解码显示jpg图片5 6.29.解决解码显示中的问题1 6.30.解决解码显示中的问题2 6.31.结束jpg图片部分 6.32.解码显示png图片1 6.33.解码显示png图片2 6.34.解码显示png图片3 6.35.解码显示png图片4 6.36.解码显示png图片5 6.37.图片文件的管理和检索1 6.38.图片文件的管理和检索2 6.39.图片文件的管理和检索3 6.40.添加触摸翻页功能 6.41.总结与回顾 第二部分、章节介绍 6.1.项目展示与整体规划 本节对项目整个的规划做个预览,并且对已经实现的项目做个展示,先让大家做到心中有数,知道我们要完成的项目什么样子。 6.2.环境搭建和基础确认 本节对项目开发的环境进行搭建,并且确认一些项目开发的基础条件。 6.3.开始动手写代码 本节开始着手建立工程,分析Makefile等,介绍了一个通用型的Makefile。 6.4.framebuffer基本操作代码 本节开始编码,主要是framebuffer的一些基础操作,譬如打开、ioctl、map、填充背景等,由于前面裸机和驱动课程都讲过了所以走的比较快。 6.5.图片显示原理和实践 本节主要强调像素、点阵、分辨率、bpp等显示相关的概念,并对Image2LCD软件做个介绍。 6.6.图片数据提取和显示 本节介绍Image2LCD软件的设置和使用,并且使用该软件提取图像数据。 6.7.图片显示的高级话题 本节主要强调像素数据中RGB和BGR的两种排布,以及排布引起的编程调整。 6.8.其他显示细节问题 本节处理了图片分辨率比屏幕分辨率小时,显示函数的调整问题。 6.9.任意起点位置图片显示1 本节处理了任意起点开始显示小图片且不超过屏幕范围时,显示函数的调整问题。 6.10.任意起点位置图片显示2 本节处理了任意起点开始显示且图片大小超过屏幕范围时,显示函数的调整问题。 6.11.BMP图片的显示1 本节介绍BMP图片的本质,BMP图片的二进制格式及其如何解析。 6.12.BMP图片的显示2 本节结合规范一起来分析BMP文件的头信息,并且讲述了如何通过代码来解析BMP文件。 6.13.BMP图片的显示3 本节进行bmp解析图片的编码实践 6.14.BMP图片的显示4 本节引入使用结构体方式来解析BMP头,这种方式是比较规范的处理技巧,必须掌握。 6.15.BMP图片的显示5 本节使用标准的BMP头封装结构体,对BMP头信息进行分析和编码实践 6.16.及时规整才能写出好项目1 本节对之前写的代码进行规整和梳理,教会大家如何及时规整代码、梳理思路,以写出调理清晰的代码。 6.17.及时规整才能写出好项目2 本节在项目中添加debug宏控制的调试信息输出,教会大家如何在项目中控制调试信息输出的技巧。这些内容以前课程中都学过,这里主要是实践使用。 6.18.及时规整才能写出好项目3 本节自定义图片信息结构体并且使用该结构体进行函数间的信息传递,这种编程方式是linux下程序开发的常用套路,大家一定要掌握。 6.19.jpg图片的显示原理分析1 本节对jpg图片进行本质分析,并且讲述如何去解码jpg图片并显示。先理清思路后面才好下手。 6.20.jpg图片的显示原理分析2 本节主要讲解了图片编解码方面的一些知识。 6.21.libjpeg介绍及开源库的使用方法 本节引入libjpeg,并且讲述了开源库的一般使用流程和方法。 6.22.libjpeg的移植实战1 本节进行libjpeg的移植实战,主要是源码包的下载、解压、配置、编译、安装等。 6.23.libjpeg的移植实战2 本节继续libjpeg的移植实战,主要是库的部署,以及相关编译链接参数的设置。 6.24.使用libjpeg解码显示jpg图片1 本节开始研究libjpeg的使用,告诉大家如何从零开始啃下一个没有使用过的第三方库。 6.25.使用libjpeg解码显示jpg图片2 本节对example.c进行源码解读,帮助大家对陌生代码进行理解,以便后续进行移植。 6.26.使用libjpeg解码显示jpg图片3 本节对example.c进行初步移植,一边移植一边做些更改和理解,这就是做项目。 6.27.使用libjpeg解码显示jpg图片4 本节对初步移植的代码进行编译,并且排除编译中的问题,主要是头文件包含以及编译链接选项的问题。 6.28.使用libjpeg解码显示jpg图片5 本节对共享库进行部署,以让项目可以在开发板中运行起来,并且成功解码出jpg图片的头信息。 6.29.解决解码显示中的问题1 本节解决图片显示不对的问题,主要着眼于思路的分析和验证排除,目的是教会大家如何分析问题并想办法验证思路。 6.30.解决解码显示中的问题2 本节接上节继续解决显示问题,并且重写了fb_draw函数后将jpg图片显示正确。 6.31.结束jpg图片部分 本节对jpg图片显示做个总结,并且对代码做相应封装和规整,结束了jpg显示的部分。 6.32.解码显示png图片1 本节开始png图片显示部分,主要是libpng的移植和libz的移植。 6.33.解码显示png图片2 本节开始png图片解码显示部分,主要是分析各种文档和示例代码,并且根据示例代码开始编程。 6.34.解码显示png图片3 本节主要对检测是否png文件的函数进行调试和分析,保证libpng在开发板上成功运行。 6.35.解码显示png图片4 本节参考网络资料开始编写使用libpng解码显示png图片的代码。 6.36.解码显示png图片5 本节对libpng解码显示png图片的代码进行规整和总结,png显示功能完成。 6.37.图片文件的管理和检索1 本节讲解如何在程序中对图片文件进行管理,并且封装相应的结构体和数组进行实战。 6.38.图片文件的管理和检索2 本节讲解如何在程序中对图片文件进行检索,并且使用opendir和readdir进行实践编程。 6.39.图片文件的管理和检索3 本节主要解决readdir不灵的问题,通过引入lstat函数读取文件属性进行解决。 6.40.添加触摸翻页功能 本节向程序中添加触摸屏翻页功能,其实就是读出触摸坐标并且驱动显示函数进行翻页。 6.41.总结与回顾 本节首先解决上节遗留的bug,然后对整个项目做总结和展望,最后交代了后期的课程计划,整套课程结束,希望对大家有所帮助。 第三部分、随堂记录 6.1.项目展示与整体规划 6.1.1.关于做项目不得不说的事儿 (1)真实项目与实训项目 (2)如何选择项目课题:噱头还是实质 (3)如何完美讲解一个项目过程:正统流程or解剖流程 6.1.2.本课程将如何演绎一个项目 (1)目的:从程序员角度出发,经历一个项目从无到有的过程,同时锻炼编程功底和发现并解决问题的能力,提升实战功力。 (2)做法:实训项目、选题常见、重实质、解剖流程办事 6.1.3.项目展示 (1)项目从哪里来的 (2)项目实现的基本效果浏览 (3)项目规划文档简介 6.1.4.应该如何学做项目 (1)要动手,练而不是只听 (2)学写程序的关键:聚沙成塔集腋成裘 (3)做项目的核心关注点:1、用代码实现自己的构想;2、解决过程中遇到的问题 6.2.环境搭建和基础确认 6.2.1.开发环境 (1)硬件:PC(主机Win7 X64,虚拟机ubuntu14.04) + 开发板(X210) (2)软件:linux(直接基于linux API) 6.2.2.需要用到的基础环境 (1)开发板uboot(uboot可以在iNand中,也可以在外部SD卡中) (2)移植好的内核(zImage在tftp服务器中,或者zImage直接fastboot方式烧录到iNand中) 建议使用九鼎提供的移植好的源码包来自己修改、编译后得到zImage。 (3)自己做的rootfs(其实就是第2部分移植课程中制作rootfs时制作的那个) (4)主机ubuntu中tftp服务器 (5)主机ubuntu中nfs服务器 6.2.3.其他小细节 (1)代码编辑器:SourceInsight (2)代码管理:Makefile (3)调试流程:Windows共享文件夹编辑、虚拟机ubuntu中编译、make cp到nfs格式的rootfs中在开发板上运行 (4)开发板标准:以V3S(1024*600)为准,V3版本的请自行根据原理进行调整 6.3.开始动手写代码 6.3.1.Makefile介绍 (1)这是一个通用的项目管理的Makefile体系,自己写的分子文件夹组织的项目可以直接套用这套Makefile体系 (2)包含三类:顶层Makefile、Makefile.build、子文件夹下面的Makefile (3)可以参考:http://www.cnblogs.com/lidabo/p/4521123.html 6.3.2.SI建立工程 6.4.framebuffer基本操作代码 6.5.图片显示原理和实践 6.5.1.图片显示原理 (1)概念1:像素 (2)概念2:点阵 (3)分辨率(物理分辨率、显示分辨率) (4)清晰度(分辨率和像素间距有关)像素间距相同时,分辨率越大越清晰;分辨率相同时,像素间距越小越清晰。 (4)bpp(RGB565、RGB888)像素深度,每个像素用多少bit数据表示 (5)颜色序(RGB、BGR) 6.5.2.图片点阵数据获取 (1)Image2LCD软件提取 (2)通过图片/视频文件直接代码方式提取 6.6.图片数据提取和显示 6.6.1.Image2LCD提取图片数据 (1)软件下载:http://www.cr173.com/soft/43222.html (2)软件使用 6.6.2.图片显示编码与实践 6.7.图片显示的高级话题 6.7.1.RGB顺序调整 (1)RGB顺序有三个地方都有影响:第一个是fb驱动中的排布,第二个是应用程序中的排布,第三个是图像数据本身排布(Image2LCD中调整RGB顺序) (2)如果三个点中RGB顺序是一致的就会显示正常。如果三个设置不一致就可能会导致显示结果中R和B相反了。 (3)实际写程序时,一般我不去分析这东西,而是根据实际显示效果去调。如果反了就去调正应用程序中的RGB顺序就行了,这样最简单。 6.7.2.显示函数的其他写法 6.8.其他显示细节问题 6.8.1.任意分辨率大小图片显示 (1)图片比屏幕分辨率大,这种情况下多出来的部分肯定是没法显示的。处理方法是直接在显示函数中把多余不能被显示的部分给丢掉。 (2)图片大小比屏幕大小要小。这种情况下图片只是填充屏幕中一部分,剩余部分仍然保持原来的底色。 6.9_10.任意起点位置图片显示1_2 6.9.1.小图片任意起点(整个图片显示没有超出屏幕范围内) 6.9.2.起点导致图片超出屏幕外 6.11.BMP图片的显示1 6.11.1、图片文件的本质 (1)二进制文件(文件分2种:2进制文件和文本文件) (2)不同格式的图片文件的差别 (3)BMP图片的基本特征:未被压缩的元素位图格式 6.11.2、BMP图片详解 (1)BMP文件如何识别:每种图片格式都有定义好的一种识别方法,BMP图片特征是以0x424D开头 (2)BMP文件组成:头信息+有效信息 6.12.BMP图片的显示2 6.12.1.BMP文件头信息 6.12.2.图片有效数据区 6.12.3.写代码解析BMP图片 第一步:打开BMP图片 第二步:判断图片格式是否真是BMP 第三步:解析头信息,得到该BMP图片的详细信息 第四步:根据第三步得到的信息,去合适位置提取真正的有效图片信息 第五步:将得到的有效数据丢到fb中去显示 6.13.BMP图片的显示3 6.14.BMP图片的显示4 6.14.1.用结构体方式解析BMP文件头 6.15.BMP图片的显示5 解析信息头 6.16.及时规整才能写出好项目1 6.16.1.再次强调规范问题 (1)函数、变量起名字要合法合理 (2)要写注释 (3)函数长短要合适 (4)多文件组织,每个东西丢到合理的位置 6.16.2.为什么要规整项目 (1)完全自由写项目时不可能一步到位,只能先重内容和功能,后补条理和规范 (2)规整的过程也是一个梳理逻辑和分析架构的过程 6.16.3.对本项目进行规整 (1)去掉测试显示时头文件形式提供的图片显示相关的东西 6.16.4.一些重构代码的技巧 (1)用#if 0 #endif来屏幕不需要的代码,不要用/* */ (2)暂时不要的代码先不要删除,而是屏幕掉 6.17.及时规整才能写出好项目2 (1)添加DEBUG宏以控制调试信息输出 debug宏添加好后,要使能输出可以有2种方式: 第一种:就是在debug宏定义之前定义DEBUG宏。 第二种:在编译参数中添加-DDEBUG编译选项 (2)图片信息用结构体来封装传递 6.18.及时规整才能写出好项目3 6.19.jpg图片的显示原理分析1 6.19.1.认识jpg图片 (1)二进制文件 (2)有其固定的识别特征 http://www.cnblogs.com/Wendy_Yu/archive/2011/12/27/2303118.html (3)经过压缩的图片格式 6.19.2.jpg图片如何显示 (1)jpg图片中的二进制数并不对应像素数据 (2)LCD显示器的接口仍然是framebuffer (3)要显示jpg图片必须先解码jpg得到相应的位图数据 6.19.3.如何解码jpg图片 (1)图片编码和解码对应着压缩和解压缩过程 (2)编码和解码其实就是一些数学运算(压缩度、算法复杂度、时间、清晰度) (3)软件编解码和硬件编解码 (4)不同的图片格式其实就是编解码的算法不同,结果是图片特征不同 (5)编程实战:使用开源编解码库 6.20.jpg图片的显示原理分析2 6.21.libjpeg介绍及开源库的使用方法 6.21.1.libjpeg介绍 (1)基于linux的开源软件 (2)C语言编写(gcc、Makefile管理) (3)提供JPEG图片的编解码算法实现 6.21.2.libjpeg版本及下载资源 (1)经典版本v6b:https://sourceforge.net/projects/libjpeg/files/libjpeg/6b/ (2)最新版本v9b:http://www.ijg.org/ 6.21.3.开源库的使用方法 (1)移植(源码下载、解压、配置、修改Makefile、编译或交叉编译)。移植的目的是由源码得到三个东西:动态库.so,静态库.a,头文件.h (2)部署(部署动态库so、部署静态库.a和头文件.h) 动态库是运行时环境需要的,编译程序时不需要。 静态库是静态连接时才需要,动态链接时不需要。 头文件.h是在编译程序时使用的,运行时不需要的。 总结:静态库和头文件这两个东西,是在编译链接过程中需要的;而动态库是在运行时需要的。 所以动态库so文件是要放到开发板的文件系统中去的(放的过程就叫部署),把静态库.a文件和头文件.h文件放到ubuntu的文件系统中去。 (3)注意三个编译链接选项:-I -l -L -I是编译选项(准确的是说是预处理选项CFLAGS或者CPPFLAGS中指定),用来指定预处理时查找头文件的范围的。 -l是链接选项(LDFLAGS中指定),用来指定链接额外的库(譬如我们用到了数学函数,就用-lm,链接器就会去链接libm.so;那么我们使用了libjpeg,对应的库名字就叫libjpeg.so,就需要用-ljpeg选项去链接) -L是链接选项(LDFLAGS中指定),用来告诉链接器到哪个路径下面去找动态链接库。 总结:-l是告诉链接器要链接的动态库的名字,而-L是告诉链接器库的路径 6.22.libjpeg的移植实战1 6.22.1.移植 (1)源码下载、解压 (2)配置 ./configure --prefix=/opt/libdecode --exec-prefix=/opt/libdecode --enable-shared --enable-static -build=i386 -host=arm (3)Makefile检查,主要查看交叉编译设置是否正确 CC=gcc 改为 CC=arm-linux-gcc AR=ar rc 改为 AR=arm-linux-ar rc AR2=ranlib 改为  AR2=arm-linux-ranlib (4)编译 make (5)安装 make install-lib 安装就是将编译生成的库文件、头文件、可执行文件分别装载到--prefix --exec-prefix所指定的那些目录中去。 6.22.2.部署 部署动态链接库一般有三个位置可以考虑: 第一个:/lib 第二个:/usr/lib 第三个:任意指定目录 6.23.libjpeg的移植实战2 6.24.使用libjpeg解码显示jpg图片1 6.24.1.如何使用一个新的库 (1)网络上找别人使用过后写的文档、博客等作为参考 (2)看库源码自带的文档(说明文档和示例代码) 6.24.2.libjpeg说明文档和示例代码 (1)说明文档:README和libjpeg.doc (2)示例代码:example.c 6.24.3.结合说明文档和示例代码来一边学习一边编码一边实践 6.25.使用libjpeg解码显示jpg图片2 (1)解读example.c 6.26.使用libjpeg解码显示jpg图片3 (1)参考example.c中的示例代码来移植到我们项目中 6.27.使用libjpeg解码显示jpg图片4 (1)测试代码,先试图读取jpg图片头信息 (2)问题排除: 编译时问题:主要就是头文件包含,除了在代码中包含头文件外,还要注意指明头文件的路径 注意-I、-l、-L三个编译链接选项的使用 6.28.使用libjpeg解码显示jpg图片5 6.28.1.部署动态库以使程序运行起来 (1)第一种情况:放到/lib或者/usr/lib下,这样不需要给系统指定库路径,就能自动找到。(强调一下是开发板根文件系统下的路径,千万不要弄成了ubuntu的根文件系统下的目录) (2)第二种情况:有时候对于一些不常用的库,我们不愿意将他放到lib或usr/lib目录下去,而是找个自定义的第三方的目录,单独将这些不常用的库放置,然后再用一定方法告诉操作系统去到这个路径下加载这个库。将该自定义第三方目录导出到环境变量LD_LIBRARY_PATH下即可。 6.28.2.测试读取头信息 6.29_30.解决解码显示中的问题1_2 问题分析及解决记录: (1)根据LCD上错误的显示状态,分析有可能是显示函数中的图片宽高数据有误导致的,于是在fb_draw函数中添加debug打印出宽和高来,结果发现是对的。 (2)显示函数中的图片宽高和fb宽高都是对的,结果显示时还是只有一溜(其余位置黑屏),可能的一个原因就是:显示数据本身不对,很多都是0.如何验证?只要把显示数据打印出来看一看就知道了。结果发现打印出的待显示数据果然是很多0,说明给显示函数的待显示数据就是错的 (3)这些待显示数据为什么会错?第一种可能性就是libjpeg解码出来就是错的;第二种可能性是解码出来再暂存的时候,或者从暂存的buffer拿出来的时候给搞错了。相对来说第二种很好验证而第一种不好验证。我们只需要在jpeg_read_scanlines函数后面直接打印显示解码出来的一行数据,就可以知道是不是第二种情况了。结果打印出来好多0,说明是第一种情况。 (4)截至目前,已经锁定了问题,就是jpeg_read_scanlines解码出来的数据本身就不对。 (5)可能的问题有:有可能是libjpeg本身就有问题;有可能我们对libjpeg的部署不对导致他工作不对;有可能我们写的代码不对,也就是说我们没用正确的方法来使用libjpeg。 (6)走投无路没有思路,不知道查谁,怎么办? 方法一:去网上找一些别人写的libjpeg解码显示图片的示例代码,多看几个,对着和我们的关键部位对比,寻找思路。 方法二:如果在网上找不到相关资料,这时候就只有硬着头皮去看源码了。譬如我们这里,就要去libjpeg的源码中查看:jpeg_read_scanlines、cinfo.mem->alloc_sarray等 (7)解决了buffer申请导致的问题之后,我们再来解决2个遗留的问题:一个就是RGB顺序问题,另一个是图像转了180度的问题。 (8)添加了fb_draw2函数并且调用后,2个遗留问题彻底解决。至此,jpg图片显示完美实现。 6.31.结束jpg图片部分 (1)加上jpg图片格式识别 (2)对外封装好用的jpg图片显示函数 (3)对外封装好用的bmp图片显示函数 6.32.解码显示png图片1 6.32.1.思路分析 (1)png更像是jpg而不像是bmp (2)png和jpg都是压缩格式的图片,都是二进制文件,不同之处是压缩和解压缩的算法不同。 (3)通过libjpeg来编解码jpg图片,那么同样有一个libpng用来编解码png图片。 (4)工作思路和顺序:找到并移植并部署libpng,然后查readme和其他文档示例代码等来使用libpng提供的API来对png图片进行解码,并将解码出来的数据丢到framebuffer中去显示。 6.32.2.libpng移植 (1)下载源码包: (2)解压、配置、修改Makefile、编译、部署 ./configure --host=arm-linux --enable-shared --enable-static --prefix=/opt/libdecode (3)配置出错,报错信息:configure: error: zlib not installed 分析问题是因为libpng依赖于zlib库,所以要先移植zlib库才可以。 (4)移植了zlib后再过来配置,还是报错,原因是因为没有导出相关环境变量,所以libpng在配置的时候找不到刚才移植的zlib库的库文件和头文件。 (5)解决方案就是使用epport临时性的导出, # export LDFLAGS="-L/opt/libdecode/lib" # export CFLAGS="-I/opt/libdecode/include" # export CPPFLAGS="-I/opt/libdecode/include" (6)导出后再次配置就过了,然后编译和安装 (7)make && make install 6.32.3.zlib移植 (1)下载:http://www.zlib.net/,并解压 (2)配置:export CC=arm-linux-gcc ./configure -shared --prefix=/opt/libdecode (3)make && make install (4)make install后/opt/libdecode目录下的lib和include目录下就有了zlib的静态库动态库和头文件了,然后再回去继续libpng的移植。 6.33_34.解码显示png图片2_3 6.33.1.参考源码包自带的资料 (1)readme (2)libpng-manual.txt (3)example.c 和 pngtest.c 6.33.2.根据示例代码开始编程 6.35.解码显示png图片4 6.36.解码显示png图片5 6.37.图片文件的管理和检索1 6.37.1.图片文件的管理 (1)在物理磁盘存储层次上:用一个文件夹来管理;在程序中,用数据结构来管理。 (2)用数组管理 (3)用链表管理 (4)编程实战 6.37.2.图片信息的自动检索 (1)读取文件类型 (2)普通文件和文件夹分类处理 (3)普通文件区分,将其中的图片按格式存储到图片管理数组/链表中 6.38.图片文件的管理和检索2 实践编程和调试 6.38.图片文件的管理和检索2 6.39.图片文件的管理和检索3 6.40.添加触摸翻页功能 6.40.1.读取触摸坐标数据 6.40.2.使用触摸坐标判断并执行翻页操作 6.41.总结与回顾 6.41.1.bug解决 6.41.2.项目总结 (1)项目描述 (2)重点和难点 6.41.3.项目展望与扩展功能 (1)划屏翻页 (2)图片放大与缩小显示 (3)动画 (4)开机画面 (5)背景音乐 6.41.4.相关课程安排 (1)整个《朱有鹏老师嵌入式linux核心课程》结束,共计7部分408小时。 (2)下一步(2016.12-2017-12)课程录制以单片机系列课程为主,期间夹杂其他事情 (3)嵌入式部分以后以项目课程为主,会源源不断推出各种项目(直播与录播相结合) (4)会逐步推出嵌入式培训班模式,以线上和线下相结合方式来运营(视频课程做产品,培训班做服务)。