* AVI 格式允许视频和音频交错在一起同步播放,支持 256 色和 RLE 压缩,但 AVI 文件并
未限定压缩标准,AVI 仅仅是一个容器,用不同压缩算法生成的 AVI 文件,必须使用相应的解
压缩算法才能播放出来。比如本章,我们使用的 AVI,其音频数据采用 16 位线性 PCM 格式(未
压缩),而视频数据,则采用 MJPG 编码方式。

* 视频文件信息头这里有很多我们要用到的信息,比如 SecPerFrame,通过该参数,我们可以知道每秒钟的帧
率,也就知道了每秒钟需要解码多少帧图片,才能正常播放。TotalFrame 告诉我们整个视频有
多少帧,结合 SecPerFrame 参数,就可以很方便计算整个视频的时间了。

* 而且它告诉我们视频的分辨率(Width 和 Height),以及视频所用的编码器(Compression),因此它
决定了视频的解码。本章例程仅支持解码视频分辨率小于屏幕分辨率,且编解码器必须是 MJPG
的视频格式。

* 本结构体对音频数据解码起决定性的作用,他告诉我们音频信号的编码方式(FormatTag)、
声道数(Channels)和采样率(SampleRate)等重要信息。本章例程仅支持 PCM 格式
(FormatTag=0X0001)的音频数据解码。

也就是说avi视频是由许多帧组成的(每一帧就是一张图片),但是都是avi格式的视频的帧图片和音频却都可以采用不同的编码方式, 比如的A.avi视频的每一帧可能就是用的.png格式编码的,音频采用.wav。B.avi视频的每一帧可能就是用的.jpg格式编码的,音频采用.mp3。但是他们都是.avi格式的视频,所以解码的时候我们读取他的信息头数据,就可以知道应该用什么方式解码了。

由于stm32h743带的是JPG/JPEG图片硬件解码器,所以只能快速解码由 MJPG 编码的 AVI 格式视频播放

那也就是说,为了这个单片机能获得快速的解码,我们播放的视频得提前在电脑上用软件处理为MJPG编码的.avi视频,且音频必须是 PCM 格式(因为阿波罗开发板另外带的是这个解码芯片,我的水星板没有带,那就不播放声音了呗)

程序过程:

最外层函数

//播放一个mjpeg文件
//pname:文件名
//返回值:
//KEY0_PRES:下一曲
//KEY1_PRES:上一曲
//其他:错误
u8 video_play_mjpeg(u8 *pname)

即可,那它里面到底做了啥呢

1.读取这个avi视频文件结构体头信息,得到帧率等参数

//开始avi解析
            res=avi_init(pbuf,AVI_VIDEO_BUF_SIZE);    //avi解析

这里就会把读取到的参数都赋值给全局变量频文件结构体变量avix了

2.显示出这个视频结构体的参数信息,按照帧率开启定时器定时时间,即按照帧率的时间间隔,来读取视频的数据流,从而实现按照视频指定帧率播放该视频

video_info_show(&avix); 
            TIM6_Int_Init(avix.SecPerFrame/100-1,20000-1);//10Khz计数频率,加1是100us

当然还会对视频的音频进行初始化

if(avix.SampleRate)                            //有音频信息,才初始化
            {
                WM8978_I2S_Cfg(2,0);    //飞利浦标准,16位数据长度
                SAIA_Init(0,1,4);        //设置SAI,主发送,16位数据 
                SAIA_SampleRate_Set(avix.SampleRate);    //设置采样率
                SAIA_TX_DMA_Init(saibuf[1],saibuf[2],avix.AudioBufSize/2,1);//配置DMA
                sai_tx_callback=audio_sai_dma_callback;    //回调函数指向SAI_DMA_Callback
                saiplaybuf=0;
                saisavebuf=0; 
                SAI_Play_Start(); //开启sai播放 
            }

3.就是循环读取每一帧数据了,读取到每一帧(就是一张jpg图片)就调用单片机JPEG硬件进行解码,同时就在这个解码函数内显示在液晶屏上了(这个跟前面学习的fpg图片硬件解码过程一模一样的),解码完一帧后,不急着读下一帧,而是延时一个帧率时间间隔while(frameup==0);即这句代码,然后再读取下一帧且解码,如此循环。。。图片帧数读完且解码完之后,就退出就行了,代码如下

while(1)//播放循环
            {                    
                if(avix.StreamID==AVI_VIDS_FLAG)        //视频流
                {
                    pbuf=framebuf; 
                    f_read(favi,pbuf,avix.StreamSize+8,&nr);        //读入整帧+下一数据流ID信息  
                    res=mjpeg_decode(pbuf,avix.StreamSize);
                    if(res)
                    {
                        printf("decode error!\r\n");
                    } 
                    while(frameup==0);    //等待时间到达(在TIM6的中断里面设置为1),即按照帧率速度
                    frameup=0;            //标志清零
                    frame++; 
。。。读完了所有帧,这里就退出这个死循环了,完成这个视频播放

我们来看看上面这个底层解码函数res=mjpeg_decode(pbuf,avix.StreamSize);关键性的做了啥

JPEG_Decode_Init(&mjpeg);                        //初始化硬件JPEG解码器

JPEG_IN_DMA_Init((u32)mjpeg.inbuf[0].buf,mjpeg.inbuf[0].size);    //配置输入DMA

if(mjpeg.state==JPEG_STATE_FINISHED)    //解码完成了
    {
        mjpeg_fill_color(imgoffx,imgoffy,mjpeg.Conf.ImageWidth,mjpeg.Conf.ImageHeight,rgb565buf);  
    } 
可以看到里面是进行调用JPEG硬件解码,然后配合DMA进行数据高速传输到JPEG硬件和从JPEG硬件直接传输到RGB屏幕的显存区域(必须是横屏模式,但是本例程是竖屏模式,所以DMA无法直接传输到显存)(显存就是sdram的一块内存,480*800的屏幕大概需要2MB显存,这里填啥,屏幕就显示啥了)(横屏这样的DMA方式显示速度是最快的),最后解码出的就是图片的像素点颜色数据了,然后进行调用液晶屏底层的函数显示再屏幕上即可

结果对比:对于 480*272 的视频,本例程最高能播放 30 帧左右的视频,我用了480*800的25帧视频,单片机播放出来大概只有20帧(我的水星开发板液晶屏测试都是480*800的电容屏),但是教材又说了

附 STM32H743 硬件 JPEG 视频解码性能:
对 800*480 分辨率,可达 50 帧
对 1024*600 分辨率,可达 20 帧
对 1280*800 分辨率,可达 10 帧

那为什么我的只有20帧呢,原因应该是因为这个例程是用竖屏模式,DMA无法直接填充颜色数据到显存,还经过了一些转换,所以耗时了,有兴趣的可以直接看看改成横屏模式,搞到50帧(好像综合例程就是横屏模式,可以参考改一下)
如果要想提高帧率,有几个办法:1,降低分辨率;2,降低比特率;3,降低音频采样率。

-------------------------------------------------------------------------------------------------------------------------------------------------------

最后再来分析一下控制帧率的定时器原理:

TIM6_Int_Init(avix.SecPerFrame/100-1,20000-1);//10Khz计数频率,加1是100us,单独开启了定时器6

然后设置了中断方式运行,中断函数:

vu8 frameup; 
//定时器6中断服务程序     
void TIM6_DAC_IRQHandler(void)
{                                       
    if(TIM6->SR&0X0001)//溢出中断
    { 
        frameup=1;
    }                   
    TIM6->SR&=~(1<<0);//清除中断标志位         
}

就是每隔帧率时间就中断了一下(然后定时器会自动重装载定时值),更新了frameup=1;这个变量,然后解码过程函数就会来查询这个变量是否为1,是的话就说明下一帧到了,赶紧开始解码,同时 frameup=0;            //标志清零

那我们来分析一下,如果单片机的一帧解码速度够快,说明解码完上一帧但是下一帧时间还没到,所以就会进行等待,那么就是完全准确的按照帧率播放该视频的。

如果单片机解码一帧速度不够,那么刚解码完一帧,此时一查询frameup是1(新的一帧指示变量早就在定时器中断里面已经更新为1了),就得继续读取且解码下一帧(所以cpu使用率会非常的高),那么这样的话就会出现掉帧的情况:要么是某些帧没有播放到(视频看起来不是连续的变化),要么就是视频时间到了就停止播放所以后面的帧没有播放出来(视频后面部分没有播出来),要么就是播放有卡顿,总播放时间就变长了罢了(这个看程序是怎么写的了,就会有不同的现象)。总体来说,就是感觉卡顿的

while(1)//播放循环
            {                    
                if(avix.StreamID==AVI_VIDS_FLAG)        //视频流
                {
                    pbuf=framebuf; 
                    f_read(favi,pbuf,avix.StreamSize+8,&nr);        //读入整帧+下一数据流ID信息  
                    res=mjpeg_decode(pbuf,avix.StreamSize);
                    if(res)
                    {
                        printf("decode error!\r\n");
                    } 
                    while(frameup==0);    //等待时间到达(在TIM6的中断里面设置为1),即按照帧率速度
                    frameup=0;            //标志清零
                    frame++; 
。。。读完了所有帧,这里就退出这个死循环了,完成这个视频播放

stm32h743单片机嵌入式学习笔记8-avi视频解码相关推荐

  1. stm32h743单片机嵌入式学习笔记6-压缩图片解码原理

    软件解码: JPEG/JPG 的解码过程可以简单的概述为如下几个部分: 1 .从文件头读出文件的相关信息. JPEG 文件数据分为文件头和图像数据两大部分,其中文件头记录了图像的版本. 长宽.采样因子 ...

  2. stm32h743单片机嵌入式学习笔记7-FPU

    * FPU 即浮点运算单元(Float Point Unit).浮点运算,对于定点 CPU(没有 FPU 的 CPU) 来说必须要按照 IEEE-754 标准的算法来完成运算,是相当耗费时间的.而对于 ...

  3. stm32h743单片机嵌入式学习笔记4-文件系统FATFS

    FAT32 文件系统 就是在格式化的时候 选择 FAT32 方式格式化. 然后在 SD 卡存储区会产生一个类似于 目录 的文件系统. 这样存入 SD 卡中的所有 文件 文件夹 都会在这个目录中找到. ...

  4. 嵌入式学习笔记——基于Cortex-M的单片机介绍

    基于Cortex-M的单片机介绍 前言 1生产厂商及其产品线 1.1ARM单片机的产品线 1.2命名规则 作业1 2习单片机的资料准备 2.1STM32开发所需手册 2.1.1芯片的数据手册 芯片基本 ...

  5. 嵌入式学习笔记——ADC模数转换器

    ADC模数转换器 前言 ADC介绍 ADC概述 ADC的数量 ADC的特性 ADC框图 芯片外部框图 芯片内部框图 转换部分框图 状态输出部分 条件触发框图 寄存器介绍 编程思路 模式选择 规则通道的 ...

  6. cortex_m3_stm32嵌入式学习笔记(十五):待机唤醒实验(WK_UP外部中断)

    cortex_m3_stm32嵌入式学习笔记(十五):待机唤醒实验(WK_UP外部中断) https://blog.csdn.net/qq_16255321/article/details/43086 ...

  7. 嵌入式学习笔记——STM32的USART通信概述

    文章目录 前言 常用通信协议分类及其特征介绍 通信协议 通信协议分类 1.同步异步通信 2.全双工/半双工/单工 3.现场总线/板级总线 4. 串行/并行通信 5. 有线通信.无线通信 STM32通信 ...

  8. 嵌入式学习笔记--jlink 工具RTT使用笔记

    嵌入式学习笔记–jlink 工具RTT 使用笔记 最近有调试一个ethcat板卡的需求,板卡设计的很简洁,没有多余的调试串口,正准备飞线到单片机针脚上的时候发现了RTT 的这个工具,记录一下使用的方法 ...

  9. 嵌入式学习笔记——使用寄存器编程操作GPIO

    使用寄存器编程操作GPIO 前言 GPIO相关的寄存器 GPIO 端口模式寄存器 (GPIOx_MODER) (x = A..I) 位操作 GPIO 端口输出类型寄存器 (GPIOx_OTYPER) ...

  10. 嵌入式学习笔记——寄存器实现控制LED小灯

    文章目录 前言 GPIO通用输出模式 初始化LED小灯的GPIO 原理图 初始化代码 初始化的效果 功能函数封装 直接分开宏定义两个 使用条件运算符 封装函数实现简单的功能 KEIL MDK一些技巧 ...

最新文章

  1. WordPress 性能优化:为什么我的博客比你的快
  2. LeetCode-剑指 Offer 04. 二维数组中的查找
  3. 《Java编程思想》《Think in Java》笔记
  4. boost::integer::mod_inverse用法的测试程序
  5. MySql和Oracle数据库的区别?
  6. 前端面试题2021(超详细)
  7. win10浏览器闪退_win10纯净版系统中edge浏览器闪退的解决方法
  8. HTML 界面画图工具
  9. 七夕前夜,地球和月亮搭起“鹊桥”
  10. 什么?四六级及格线不是425分?!
  11. ubuntu - sudo file 编辑报错 sudo: no valid sudoers sources found, quitting
  12. JAVA藏宝阁游戏交易系统计算机毕业设计Mybatis+系统+数据库+调试部署
  13. 南邮——计算机图像学——光照、冯氏光照模型
  14. 离散实验sdut3805双射
  15. win7/win10下KiWi Syslog服务器的安装与配置
  16. 6.1电机,串口控制,PWM调速
  17. ora-00600 [25027]
  18. UE_Visibility Buffer Deferred Material
  19. linux 能打开exe文件,linux能运行exe文件吗
  20. ansys添加力矩_ANSYS转矩施加三种方法

热门文章

  1. OSChina 周日乱弹 —— 我重新说
  2. signature=45f2913b66c5b2ae668a6622be005d65,国开大《商务英语4》自测试题及答案
  3. 英伟达P8显卡_英特尔独显芯片上市 AMD 英伟达 英特尔显卡芯片三方对战_笔记本新闻...
  4. python之while循环、无限循环
  5. linux怎么打开隐藏文件夹,如何在文件管理器中隐藏文件和文件夹
  6. 网站CNZZ数据统计的实现方式
  7. 基于java的企业信息网站发系统
  8. Excel如何动态获取某个产品最新的库存信息
  9. 维护设备的库存信息-SERIAL_EQBS_POST
  10. html5 地铁 代码,基于HTML5WebGL的地铁管理系统