stm32 播放高帧率高分辨率视频和照片详细制作过程(播放Bad Apple为例)

stm32 播放高帧率高分辨率视频和照片详细制作过程

下文以播放Bad Apple视频为例。

文章是我边调试边写的,质量不好请见谅。


所有资料都会总结好放到 网盘里,提取码:8e1z

更新:

其实格式工厂里就可以提取,真的是眼瞎没看到,浪费时间了。

但是这个生成出来的jpg文件,文件名格式是imgge_x.jpg,没有按照我们所学的0001-xxxx这样的格式,下面写一段python代码来改一下文件名字(太多了,不可能手改的)。

image-20220118235043712

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,不要加逗号。

image-20220120135236723

将所有的jpg都合成到一个bin文件里了,那么每张图片我们如何知道长度和地址呢。

我们需要做一个存索引的文件,我这里做成Inde.txt,将输出的窗口数据复制到txt,然后保存好放到tf卡中。

坑点:
  • 最开始我采用的是格式2,不加逗号的格式输出。然后在stm32读取的时候花费了不少时间,fatfs没有一行一行读取,不像python有readline怎么方便。而且我们不知道每行的长度,类似下图。这就导致不能读一行出来后,取偏移地址,再取长度了。(最后勉强用这个实现了)

  • 如果输出格式1,不加逗号,那么格式都是一样的,如下图。每行长度确定,我们就可以一次固定读两行的长度。再取出两个16进制,前面的是起始地址,相减后就是所需读取的长度。

image-20220120140204068

image-20220120140430046

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。这里被坑了下。

image-20220116112238980

3.使用网上采用的type会出现bug,和自己算出来的大小不一致,这里采用安富利的文件合并助手。

image-20220116141828259

**4.**视频转图片的是用的matlab,也可以自己改用python等等

猜你喜欢

转载自blog.csdn.net/a200327/article/details/122600874