OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 35131|回复: 14

转 STM32+SD卡+znFAT+TFT液晶+AVI视频解码 实现“简易视频播放器”

[复制链接]

12

主题

216

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
313
金钱
313
注册时间
2011-4-7
在线时间
3 小时
发表于 2012-8-26 23:00:58 | 显示全部楼层 |阅读模式

http://bbs.21ic.com/frame.php?frameon=yes&referer=http%3A//bbs.21ic.com/icnewest.html









  振南znFAT 单片机上的FAT32文件系统

关注znFAT,加入QQ群 106889506

1.JPG 2.JPG 3.JPG 4.JPG 5.JPG 6.JPG
====================================
IMG_0750.gif IMG_0750-[0002].gif
====================================
相关软件:FFDSHOW解码器包:http://58.51.84.4:82/down/ffdshow_beta7_rev3154_20091209.zip
      实验演示视频录像:
====================================
     在我们拿到一个液晶显示模块,尤其彩色液晶屏的时候,你有没有想过用它作一个图片显示,甚至是视频播放的实验呢?也许你在实现的过程中遇到了重重困难。振南在这里所作的“简易视频播放器”实验希望能给你一个参考。
     我们知道要在液晶上显示一些图案,我们就要将像素数据写到液晶的显示缓冲区(CGRAM)之中。比如对于一个16位色(可显示6万5千种颜色)的彩色液晶来说,它的每一个像素都对应于一个16位的RAM单元,通过它来设定这个像素所显示的颜色。(我们在此实验中所使用的TFT液晶就是一个16位色的液晶,颜色表示的形式为RGB565,即在16位的数码中,高5位用于表达红色分量、中间6位用于表达绿色分量、低5位用于表达蓝色分量。)
视频播放实验1-310.png

     只要我们向液晶的每一个像素写入相应的数据,最终我们就可以让液晶显示出相应的图案。比如上图,我们就显示了一幅美女图(其实长得比我老婆差远了)。至于图片上各个像素的RGB565的数值如何获取?我们可以使用Image2LCD这个软件来得到,想必很人都已经用过。通常这些像素数据我们是以数组的形式固化在单片机的ROM中的。

  1. const unsigned char pimage[IMAGE_SIZE] =
  2. {
  3. 0X10,0XF8,0X10,0XF8,0X10,0XF8,0X10,0XF8,0X10,0XF8,0X10,0XF8,0X10,0XF8,0X10,0XF8,
  4. 0X10,0XF8,0X10,0XF8,0X10,0XF8,0X10,0XF8,0X10,0XF8,0X10,0XF8,0X10,0XF8,0X10,0XF8,
  5.   ......//很多的像素数据
  6. 0X10,0XF8,0X10,0XF8,0X10,0XF8,0X10,0XF8,0X10,0XF8,0X10,0XF8,0X10,0XF8,0X10,0XF8,
  7. 0X10,0XF8,0X10,0XF8,0X10,0XF8,0X10,0XF8,0X10,0XF8,0X10,0XF8,0X10,0XF8,0X10,0XF8,
  8. };
复制代码
好,你已经知道如何在液晶上显示一幅图像了。那“视频播放”的原理就不难理解了:把视频的每一帧图像按顺序依次显示在液晶上,只要速度够快,我们就可以看到连贯的动画效果,这就是“视频”了!
     现在的问题是:我们如何获取这一帧又一帧的视频图像数据?也许你会说:可以在视频文件中去获取这些图像数据吧?比如平常我们所熟悉的AVI、MP4、RMVB等等,它们之中肯定存着图像的数据和信息。没错,每一帧的图像数据就在其中,但是却没那么容易得到可以用于显示的RGB565数据。因为这些视频帧经过了某种压缩算法的编码,已经面目全非,而且这种压缩算法可能会极为复杂而强劲,要想得到RGB565的像素数据,必须要把这些数据经过“逆向算法”进行还原,这就是视频的解码。
     其实,我们会发现,很多人在用液晶显示图片的时候,都喜欢用BMP这种文件格式,这是因为BMP中存储的就是现成的RGB数据,而没有经过任何的压缩处理(有些特殊的BMP可能也有压缩,但通常是没有的),我们通常称这种数据为Raw Data,即原始数据。正是因为没有经过压缩处理,所以我们会发现BMP文件的数据量是比较大的。所以,我们只需要直接读取BMP的数据,不经任何处理就可以用于显示了。
     在前面,我发布的“简单数码相机”实验中,我所使用的就是BMP图像格式,因为它简单,直接把从摄像头得到的RGB数据“扔”到文件里就可以了!
     其实,单单就图片文件来说,就有很多种格式,比如JPG、PNG、GIF等等。它们也是经过了压缩算法处理过的,所以很少有人去直接使用这些图片文件。其实我们就是通过借助Image2LCD这个工具来对图片进行了解码,从而获得像素数据的。这个通过软件工具获取像素数据的过程,我们也称为“取模”!(当然我们也经常会看到有一些高手,在一些单片机或处理器上直接对这些图像格式进行解码,这就要考验算法和数字信号处理的一些功底了,同时也需要性能比较高的CPU)。
     要用单片机或者处理器,直接对视频进行解码,难度是很大的。那在电脑上有没有一种软件可以把视频文件转换为像素数据,就像是Image2LCD软件那样将各种图片转为像素数据数组那样。答案是没有!至少我还没有发现有这样的软件。其实就算有,转换后的像素数据量也比单单一幅图像的数据量要大得多,我们的单片机哪有那么大的ROM去固化这些像素数据?为了存储如此大的数据量,所以我们使用SD卡
视频播放实验1-1853.png

     既然没有现成的视频解码软件来为我们提供像素数据,那我们就自己来写一个软件。使用VC6.0+VFW(VFW是Windows上的一套基于AVI视频的编解码开发包,可以让我们很轻松完成对AVI视频的编解码工作,详细请见http://baike.baidu.com/view/189682.htm)。振南已经实现了这个软件,我们来看一下它的用法(在使用此软件前,请先安装ffdshow解码器包,以保证各种AVI格式都可以正常解码)。
视频播放实验1-2068.png

     我们可以通过这个软件来获取一个AVI视频的某一段视频各帧图像的RGB565数据,同时可以调整视频尺寸,比如缩放系数为2,则一个320X240的视频,将缩小为160X120;此缩放系数也支持小数,比如为2.5,则视频尺寸将缩小为128X96;如果此系数小于1,则是对视频尺寸的放大,比如为0.5,则视频尺寸扩大为640X240。还可以设置帧步进的值来调整视频的帧频,比如一个视频每秒有30帧图像,如果帧步为2,则帧步将降为15。
     为了记录解码之后的每帧图像的RGB565像素数据,也为了方便单片机读取像素数据,振南定义了这样一种文件格式,名为ZNV
视频播放实验1-2345.png

     ZNV文件格式的最开始记录了转换之后的每一幅图像的尺寸,即高与宽,以及此文件中所记录的图像(即视频帧)的总数量。随后就是依次存放各图像的RGB565像素数据。最终得到的znv文件,放到SD卡上,由单片机或处理器读取SD卡上的znv文件中的图像数据,送到TFT液晶显示,从而完成简易的视频播放实验。(其实大家会发现,早期的MP4视频播放器,很多也是需要在PC上进行格式的转换的,而且MP4视频播放器所支持的格式也很少。这主要就是受限于主控芯片的处理能力,达不到一些视频解码所需要的硬件性能。或者是因为一些视频格式的版权问题,比如RMVB。如今,MP4播放器基本上都能支持大部分的视频格式了,而不用再用PC进行转换,主要还是因为主控芯片的性能提升了。)
     现在问题又来了!记录着每一帧图像像素数据的ZNV文件已经放在SD卡上了。单片机要如何来读取它的数据?对,就如上图的图中所示的,要使用znFAT了。SD卡要存储文件,都必须要进行格式化,通常我们使用FAT32文件系统所谓文件系统,其实就是一种数据管理的方式。ZNV文件的数据通过FAT32文件系统存放到了SD卡的某些扇区之中。单片机要想读到文件的数据,就必须要遵循FAT32的一些协议与定义。振南长期研究和编写的znFAT,就是这样一种与FAT32高度兼容的文件系统方案。它可以让单片机和各种处理器,很轻松的完成对文件的操作。【关于振南的znFAT,详细请参见znFAT应用手册】
     为了使视频播放的效果比较流畅,我们使用STM32(具体型号为STM32F103RBT6)作为核心CPU,完成znFAT文件系统的运行、文件数据的读取、TFT液晶的驱动等。
     【同时,文件数据的读取速度也是视频播放是否流畅的重要因素。这对于znFAT的效率性能与数据操作速度是一个挑战。】
视频播放实验1-3049.png

在STM32上使用znFAT读取znv文件数据,送给TFT液晶的主要代码如下:
视频播放实验1-3091.png






实验演示视频录像:
http://v.youku.com/v_show/id_XNDQzNTQ3MTg4.html
http://v.youku.com/v_show/id_XNDQzNTA1MzU2.html
http://v.youku.com/v_show/id_XNDQzMDY3NjI4.html
====================================

                                 

zn's AVI2ZNV.rar

366.51 KB, 下载次数: 2011

Image2Lcd_29.zip

720.59 KB, 下载次数: 1757

znFAT+test.rar

909.04 KB, 下载次数: 3425

znFAT移植与使用手册.rar

271.37 KB, 下载次数: 1880

znFAT.rar

252.23 KB, 下载次数: 1911

振南的AVI解码工具.rar

8.12 KB, 下载次数: 1782

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2012-8-27 08:50:56 | 显示全部楼层
实用级别的代码,是能独立解码sd卡,或者其他存储设备上的.gif文件.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 1 反对 0

使用道具 举报

33

主题

489

帖子

6

精华

金牌会员

Rank: 6Rank: 6

积分
1565
金钱
1565
注册时间
2011-12-10
在线时间
4 小时
发表于 2012-8-27 00:18:49 | 显示全部楼层
znFAT听说效率不错,只是现在用习惯了FATFS,不然我也支持一下国产.
这个所谓的视频播放器就有点忽悠初学者了.不过拿来练练手还是不错的.
学习交流请加Q群:242876057(STM32F4交流群)
回复 支持 反对

使用道具 举报

11

主题

52

帖子

0

精华

初级会员

Rank: 2

积分
135
金钱
135
注册时间
2012-6-1
在线时间
3 小时
发表于 2012-8-27 08:11:29 | 显示全部楼层
為什麼還要轉特殊格調播放,太麻煩了?既然無聲..那麼播放 GIF 檔就可以啦!
而且zn範例格式也類似 gif
http://v.youku.com/v_show/id_XNDAwNjg4NzI4.html




回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2012-8-27 08:48:03 | 显示全部楼层
对的,播放gif即可.
znFAT比本论坛某些人的收费fat来的实在的多.起码人家还真在51上跑的起来,但是这个某人的fat,我十分怀疑能在51跑....
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2012-8-27 08:49:40 | 显示全部楼层
回复【4楼】sky3344:
---------------------------------
你这个解码的gif应该是存在内部flash的,也就是直接通过刷代码的方式下载gif到单片机,估计就是用的ucGUI自带的那个gif解码,还没到实用级别.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

33

主题

489

帖子

6

精华

金牌会员

Rank: 6Rank: 6

积分
1565
金钱
1565
注册时间
2011-12-10
在线时间
4 小时
发表于 2012-8-27 09:02:46 | 显示全部楼层
他是把视频转为RAW,那个数据量之大不可想像.
学习交流请加Q群:242876057(STM32F4交流群)
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2012-8-27 10:31:12 | 显示全部楼层
这个有点像PIC那个gif解码的库代码。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

11

主题

52

帖子

0

精华

初级会员

Rank: 2

积分
135
金钱
135
注册时间
2012-6-1
在线时间
3 小时
发表于 2012-8-27 19:48:03 | 显示全部楼层
回复【10楼】正点原子:
这个有点像PIC那个gif解码的库代码。
---------------------------------
http://read.pudn.com/downloads85/sourcecode/graph/329380/gif/gif.c__.htm
回复 支持 反对

使用道具 举报

26

主题

193

帖子

8

精华

金牌会员

Rank: 6Rank: 6

积分
1800
金钱
1800
注册时间
2012-8-23
在线时间
57 小时
发表于 2012-11-16 13:17:54 | 显示全部楼层
回复【4楼】sky3344:
---------------------------------
不知道你了解过gif没有,完全不同的东西
回复 支持 反对

使用道具 举报

1

主题

3

帖子

0

精华

新手上路

积分
20
金钱
20
注册时间
2017-3-1
在线时间
2 小时
发表于 2017-7-31 21:19:50 | 显示全部楼层
都是大神,,小弟还处于潜水论坛 默默倾听阶段
回复 支持 反对

使用道具 举报

1

主题

3

帖子

0

精华

新手上路

积分
20
金钱
20
注册时间
2017-3-1
在线时间
2 小时
发表于 2017-7-31 21:35:53 | 显示全部楼层
我的回复找不到了
回复 支持 反对

使用道具 举报

2

主题

5

帖子

0

精华

新手上路

积分
27
金钱
27
注册时间
2019-9-1
在线时间
4 小时
发表于 2019-12-7 06:53:25 | 显示全部楼层
感谢分享,学习了!
回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手上路

积分
28
金钱
28
注册时间
2019-10-2
在线时间
6 小时
发表于 2020-11-26 16:51:12 | 显示全部楼层
大家好,我用了楼主的解码器解码生成了一个znv文件,用一个ministm32开发板想显示它,但总是有错位下面是我的代码,希望大家有空可以帮我看一下u8 stdbmp_Znv_decode0(const u8 *filename)
{
        FIL* f_bmp;
    u16 br;

                          
        u8  rgb ,color_byte;
        u16 x ,y,color;          
        u16 countpix=0;//记录像素          

        //x,y的实际坐标       
        u16  realx=0;
        u16 realy=0;
        u8  yok=1;                                    
        u8 res;
       
        u16 count=0;

       


        u8 *databuf;                    //数据读取存放地址
        //读取数量有限制??
        u16 readlen=BMP_DBUF_SIZE;//一次从SD卡读取的字节数长度

        u8 *bmpbuf;                                  //数据解码地址
        u8 biCompression=0;                //记录压缩方式
       
        u16 rowlen;                                   //水平方向字节数  
       
        //BITMAPINFO *pbmp;                //临时指针
       
#if BMP_USE_MALLOC == 1        //使用malloc       
        databuf=(u8*)mymalloc(readlen);                //开辟readlen字节的内存区域
        if(databuf==NULL)return PIC_MEM_ERR;        //内存申请失败.
        f_bmp=(FIL *)mymalloc(sizeof(FIL));        //开辟FIL字节的内存区域
        if(f_bmp==NULL)                                                        //内存申请失败.
        {                 
                myfree(databuf);
                return PIC_MEM_ERR;                               
        }          
#else                                         //不使用malloc
        databuf=bmpreadbuf;
        f_bmp=&f_bfile;
#endif
        res=f_open(f_bmp,(const TCHAR*)filename,FA_READ);//打开文件                                                           
        if(res==0)//打开成功.
        {
                f_read(f_bmp,databuf,readlen,(UINT*)&br);        //读出readlen个字节  
                //pbmp=(BITMAPINFO*)databuf;                                        //得到BMP的头部信息   
               
                color_byte=2;        //彩色位 16/24/32  
                biCompression=4;//压缩方式



               
                picinfo.ImgHeight=175;        //得到图片高度
                picinfo.ImgWidth=146;          //得到图片宽度
                ai_draw_init();//初始化智能画图                       
                //水平像素必须是4的倍数!!
                if((picinfo.ImgWidth*color_byte)%4)rowlen=((picinfo.ImgWidth*color_byte)/4+1)*4;
                else rowlen=picinfo.ImgWidth*color_byte;
                //开始解码BMP   
                color=0;//颜色清空                                                                                                                  
                x=0 ;
                //y=picinfo.ImgHeight;
                y=0;
                rgb=0;      
                //对于尺寸小于等于设定尺寸的图片,进行快速解码
                realy=(y*picinfo.Div_Fac)>>13;
                //bmpbuf=databuf;
                bmpbuf=databuf+1;
                count=1;
                while(1)
                {                                 
                       
                        while(count<readlen)  //读取一簇1024扇区 (SectorsPerClust 每簇扇区数)
                    {
                                if(color_byte==3)   //24位颜色图
                                {
                                        switch (rgb)
                                        {
                                                case 0:                                  
                                                        color=bmpbuf[count]>>3; //B
                                                        break ;          
                                                case 1:          
                                                        color+=((u16)bmpbuf[count]<<3)&0X07E0;//G
                                                        break;          
                                                case 2 :
                                                        color+=((u16)bmpbuf[count]<<8)&0XF800;//R          
                                                        break ;                       
                                        }   
                                }else if(color_byte==2)  //16位颜色图
                                {
                                        switch(rgb)
                                        {
                                                case 0 :
                                                        if(biCompression==BI_RGB)//RGB:5,5,5
                                                        {
                                                                color=((u16)bmpbuf[count]&0X1F);                 //R
                                                                color+=(((u16)bmpbuf[count])&0XE0)<<1; //G
                                                        }else                //RGB:5,6,5
                                                        {
                                                                color=bmpbuf[count];                          //G,B
                                                        }  
                                                        break ;   
                                                case 1 :                                                    
                                                        if(biCompression==BI_RGB)//RGB:5,5,5
                                                        {
                                                                color+=(u16)bmpbuf[count]<<9;  //R,G
                                                        }else                  //RGB:5,6,5
                                                        {
                                                                color+=(u16)bmpbuf[count]<<8;        //R,G
                                                        }                                                                           
                                                        break ;         
                                        }                     
                                }else if(color_byte==4)//32位颜色图
                                {
                                        switch (rgb)
                                        {
                                                case 0:                                  
                                                        color=bmpbuf[count]>>3; //B
                                                        break ;          
                                                case 1:          
                                                        color+=((u16)bmpbuf[count]<<3)&0X07E0;//G
                                                        break;          
                                                case 2 :
                                                        color+=((u16)bmpbuf[count]<<8)&0XF800;//R          
                                                        break ;                       
                                                case 3 :
                                                        //alphabend=bmpbuf[count];//不读取  ALPHA通道
                                                        break ;                             
                                        }       
                                }else if(color_byte==1)//8位色,暂时不支持,需要用到颜色表.
                                {
                                }
                                rgb++;          
                                count++ ;                  
                                if(rgb==color_byte) //水平方向读取到1像素数数据后显示
                                {       
                                        if(x<picinfo.ImgWidth)                                                                            
                                        {       
                                                realx=(x*picinfo.Div_Fac)>>13;//x轴实际值
                                                if(is_element_ok(realx,realy,1)&&yok)//符合条件
                                                {                                                                                                           
                                                        pic_phy.draw_point(realx+picinfo.S_XOFF,realy+picinfo.S_YOFF,color);//显示图片       
                                                        //POINT_COLOR=color;                 
                                                        //LCD_DrawPoint(realx+picinfo.S_XOFF,realy+picinfo.S_YOFF);
                                                        //SRAMLCD.Draw_Point(realx+picinfo.S_XOFF,realy+picinfo.S_YOFF,color);
                                                }                                                                               
                                        }
                                        x++;//x轴增加一个像素
                                       
                                        color=0x00;
                                        rgb=0;                    
                                }
                                countpix++;//像素累加
                                if(countpix>=rowlen)//水平方向像素值到了.换行
                                {                 
                                        y++;
                                       
                                        if(y==picinfo.ImgHeight)
                                        {
                                                y=0;
                                               
                                        }                         
                                        realy=(y*picinfo.Div_Fac)>>13;//实际y值改变         
                                        if(is_element_ok(realx,realy,0))yok=1;//此处不改变picinfo.staticx,y的值         
                                        else yok=0;
                                        x=0;
                                        countpix=0;
                                        color=0x00;
                                        rgb=0;
                                }         
                        }                
                        res=f_read(f_bmp,databuf,readlen,(UINT *)&br);//读出readlen个字节
                        if(br!=readlen)readlen=br;        //最后一批数据                  
                        if(res||br==0)break;                //读取出错
                        bmpbuf=databuf;
                          count=0;
                }  
                f_close(f_bmp);//关闭文件
        }         
#if BMP_USE_MALLOC == 1        //使用malloc       
        myfree(databuf);         
        myfree(f_bmp);                 
#endif       
        return res;                //BMP显示结束.                                             
}               

回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手上路

积分
28
金钱
28
注册时间
2019-10-2
在线时间
6 小时
发表于 2020-11-26 19:58:56 | 显示全部楼层
zsycomeon123 发表于 2020-11-26 16:51
大家好,我用了楼主的解码器解码生成了一个znv文件,用一个ministm32开发板想显示它,但总是有错位下面是我 ...

不知道生成的znv文件的开头是不是有10个字节是长、宽和帧数
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-23 03:03

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表