stm32 播放高帧率高分辨率视频和照片详细制作过程(播放Bad Apple为例)
Posted 炒饭多加个蛋
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了stm32 播放高帧率高分辨率视频和照片详细制作过程(播放Bad Apple为例)相关的知识,希望对你有一定的参考价值。
stm32 播放高帧率高分辨率视频和照片详细制作过程
下文以播放Bad Apple视频为例。
文章是我边调试边写的,质量不好请见谅。
文章目录
所有资料都会总结好放到 网盘里,提取码:8e1z
更新:
其实格式工厂里就可以提取,真的是眼瞎没看到,浪费时间了。
但是这个生成出来的jpg文件,文件名格式是imgge_x.jpg,没有按照我们所学的0001-xxxx这样的格式,下面写一段python代码来改一下文件名字(太多了,不可能手改的)。
import os
import re
path = "./badapple_800_480/" #图片文件夹的路径
pic_name_length = 13 #不需要修改的文件名长度,包括(.jpg)
pic_rename_format = "Image%04d.jpg" #需要修改的名字格式
def file_rename():
suffix = 0
files = os.listdir(path)
# change current work path
os.chdir(path)
for file in files:
file_name_size = len(file)
if file_name_size < pic_name_length:
# 获取数字
if file.startswith("Image") and file.endswith(".jpg"):
num = int(re.sub(r"Image", '', re.sub(".jpg", '', file)))
#按照格式改名
new_file_name = pic_rename_format % num
os.rename(file, new_file_name)
file_rename()
硬件条件:
- STM32H743IIT6核心板
- TFT-LCD 800*480分辨率
方案一:
采用网上最通用的方法,将badapple视频处理为一帧一帧的图片,然后再将图片转化为一个个的bin文件,再合成为总的一个bin文件放到tf卡中,stm32按帧大小读bin文件,来显示图片。
**优点:**简单,适合小屏幕小性能的情况
**缺点:**显示的帧数低,大一点的屏幕就放不下。我采用的800*480的分辨率,rgb565的颜色格式。那一帧就是768000字节,bapapple视频有3.27分钟,也就是207秒。要实现一秒30帧率的效果的话。总的bin文件都已经超过了FAT32的最大文件限制4GB了(6210 * 76800),而且一秒30帧的话,也超出了tf卡读取的上限。
使用TouchGFX: 再弄TouchGFX的时候发现了它的L8RGB565格式,可以减小66%的bin文件大小,然后用TouchGFX的动画控件,设置固定的50ms切换图片的时间,再用上class10的高速tf卡,可能勉强能行。但是肯定不能做到我想要的800*480 30fps的效果。
方案二:
STM32H7性能强大,可以采用JPEG硬件解码的方法。配合上MDMA,DMA2D等硬件加速下,在RGB565和800*480的情况下跑个30pfs轻轻松松。实测下来,只要优化玩程序可以做到稳定60fps。
综上所述,采用方案二。
实现过程:
1.移植代码
每个人硬件不同,这里就不具体介绍实现过程。
由于stm32h7的特殊性,和f1 f4 f7完全不一样,sd卡的移植坑会比较多。
主要是由于h7内部的架构导致的,具体细节可以参考安富利的介绍。推荐用cubemx来移植jpeg+ltdc+sdmmc+fatfs+dma2d。
注意:
- 我在这浪费了挺多时间,jpg文件不能太多,比如badappale视频3.27秒 30帧,那么就是6510张jpg图片,全部都放在sd卡的话,挂载文件系统,前几张图片读取会非常快,后面会越来越慢,这个问题最严重,导致我用了两天才弄完,本来半天就好了。
- 解决方法:使用文件合并助手将全部jpg图片合成到一个文件里**(下文细讲)**。
- tf卡一定要买class10的高速卡(普通卡读取速度差很多),sdmmc时钟可以调到高一点。
- 移植的时候,要做好调试顺序
- 解决方法:先弄sdmmc + fatfs,确保读文件速度。正常可以跑到10M/s+
- sdram的移植,确保sdram的读和写正常
- ltdc确保屏幕显示
- dma2d,这个问题不大,使能时钟了就能用,正常使用都是直接调用寄存器。
- jpeg问题也不大,使能初始化就行。
2.具体实现
在上面的硬件都移植完了后,就可以开始操作了。
2.1视频素材处理
这里采用的是matlab来处理视频文件,下面是代码:
资源路径自己修改。
% mp4 --> jpg
apple='H:\\PersonalProducts\\badapple\\badapple_800_480.mp4';
obj = VideoReader(apple);
for i = 1 : 6510 %视频3分37秒,每秒30帧
badApple = read(obj,i);
imwrite(badApple,strcat('H:\\PersonalProducts\\badapple\\output_jpg\\badapple_',num2str(i,'%04d'),'.','jpg'));
end
2.2将图片合成到一个文件,并处理。
使用安富利的文件合并助手,非常nice一款软件。
**注意:**生成格式,选格式1,不要加逗号。
将所有的jpg都合成到一个bin文件里了,那么每张图片我们如何知道长度和地址呢。
我们需要做一个存索引的文件,我这里做成Inde.txt,将输出的窗口数据复制到txt,然后保存好放到tf卡中。
坑点:
-
最开始我采用的是格式2,不加逗号的格式输出。然后在stm32读取的时候花费了不少时间,fatfs没有一行一行读取,不像python有readline怎么方便。而且我们不知道每行的长度,类似下图。这就导致不能读一行出来后,取偏移地址,再取长度了。(最后勉强用这个实现了)
-
如果输出格式1,不加逗号,那么格式都是一样的,如下图。每行长度确定,我们就可以一次固定读两行的长度。再取出两个16进制,前面的是起始地址,相减后就是所需读取的长度。
2.3读取文件
将合成的bin文件和索引文件都放到了sd卡后,就可以开始读bin文件了。
读取bin文件,需要一个bug,直接找jpg最大的图片,看它是多少自己就定义多少,一般就几十k。
然后上面说了读取索引文件的方法,就可以获取到索引地址和长度。
2.4JPEG硬件解码
将读取到的数据缓存和数据长度代入jpeg解码api接口,while()等待结束。
2.5DMA2D显示
将得到的YCbCr格式的图片数据,代入dma2d的api接口,即可显示。
最后根据所需的图片大小,循环调用即可显示一个视频。下面是我用格式2输出的代码,后面可以改成格式1的,直接采用我的这个必有问题。
for(i = 0; i < 6509; i++)
iTimeStart = HAL_GetTick();
f_open(&JPG_File,"0:index.txt",FA_OPEN_EXISTING | FA_READ); //打开文件
/* 更改索引,因为我这里采用格式2输出,导致下面的bug,采用格式1后会解决 */
index_read_index = i*36 ;
if(pic_size > 10000)offset += 1;index_read_index += offset;
/* ******************************************************************* */
f_lseek(&JPG_File, index_read_index); //更改索引
f_read(&JPG_File, Index_Buffer, 37, &JPG_FileCount); // 读取文件 算上换行回车,一行37个 从对应格式中获取长度
f_close(&JPG_File); //关闭文件
sscanf(Index_Buffer, "0x%8x // Image%*d.jpg (%d)", &hexADDR, &pic_size); //获取偏移和长度
f_open(&JPG_File, "badapple_jpg.bin", FA_OPEN_EXISTING | FA_READ);
f_lseek(&JPG_File, hexADDR);
f_read(&JPG_File, JPG_FileBuffer, pic_size, &JPG_FileCount); // 读取文件 一行36个 从对应格式中获取长度
f_close(&JPG_File); //关闭文件
JPEG_Decode_DMA((uint32_t)JPG_FileBuffer,pic_size, JPEG_OUTPUT_DATA_BUFFER); // 调用DMA解码
while( JPEG_Decode_WaitingforEnd() != DecodeComplete ); // 等待解码完成
DMA2D_CopyBuffer( 0 , 0, (uint32_t *)JPEG_OUTPUT_DATA_BUFFER, (uint32_t *)LCD_FRAME_BUFFER ); // 使用DMA2D对解码后的图片进行显示
iTimeEnd = HAL_GetTick();
printf("%dmsd\\r\\n", (iTimeEnd - iTimeStart));
3.性能测试
- sd卡挂载fatfs后读取资源(0-2ms一次,具体看sd卡质量,和sdmms时钟)
- jpeg硬件解码时间 800*480的分辨率下,仅需10ms左右,越小的图片越快。480 * 272试了下才3-4ms。
- dma2d将转化完的结果,转化成rgb到屏幕上,大概12ms。
4.后期目标
- 这方面弄好了后,如果后期使用gui,lvgl或者touchgfx的话,当成一个底层驱动移植好,就可以做到sd卡的图片浏览功能,sd卡中的视频播放功能**(注:需要上位机处理好的资源文件)**,gui中图片资源的转化,比bin文件的图片资源节省了数十倍的存储空间(速度上差距很小)。
- 在上位机上做好jpeg解码,解码后的数据可以直接供给dma2d转化**(不是转化成rgb的bin资源)**,节省了转化的时间(小资源文件没必要,本来就1-2ms),这样读1-2ms,显示12ms,理论上来说可以稳定的做到60hz。
注意点:
**2.**本来一帧图片是768000的,但是他这个软件转换不了800 * 480,只有800 * 450那么也就是720000。这里被坑了下。
3.使用网上采用的type会出现bug,和自己算出来的大小不一致,这里采用安富利的文件合并助手。
**4.**视频转图片的是用的matlab,也可以自己改用python等等
以上是关于stm32 播放高帧率高分辨率视频和照片详细制作过程(播放Bad Apple为例)的主要内容,如果未能解决你的问题,请参考以下文章