MJPEG视频
从4.18版本开始,TouchGFX支持使用MJPEG视频。 视频可以用于创建更生动的用户界面或显示简短说明或用户指南。
视频通过“视频(Video)”控件包含在用户界面中。 该控件在TouchGFX设计器中可用,可以作为任意其他控件添加到用户界面。
在STM32微控制器上解码视频需要额外的支持软件包。 该软件通过在TouchGFX Generator中启用视频支持,从而可以方便的包含在项目中。 在已使能video功能的TouchGFX工程开发板上(见下面的列表),可以轻松运行视频,方法
是像平常一样按下“运行目标(F6)”。
如果你的目标代码中没有视频支持,会得到编译或链接错误。
MJPEG视频
MJPEG视频由打包在容器文件(.avi)中的大量JPEG图像(帧)组成。 压缩后的JPEG帧不能直接复制到帧缓存区。 每一帧必须被解压为RGB格式(16或24位),然后才能在显示屏上显示。
与使用RGB位图相比,这种解压缩方式的计算成本较高,并且大大降低了性能(即每秒帧数)。
JPEG压缩的优点是大大减少了数据尺寸。
上面截图中使用的视频像素为240 x 135。 这意味着每个16位RGB格式的帧将占用240 x 135 x 2字节= 64,800字节。 该视频含有178帧(时长大约7秒) 因此,以位图格式存储的视频总大小为178 x 64,800字节= 11,534,400字节。 MJPEG AVI文件只有1,242,282字节,为位图大小的10.7%。
尺寸的缩减使得MJPEG视频文件对于小段视频非常有用。
尺寸的缩减会伴随一些压缩伪影(差错)。 这些对于真实画面通常是可以接受的,但对于高对比度图形是不可接受的。
部分STM32微控制器(如STM32F769或STM32H750)支持JPEG图像的硬件加速解码。 这加快了JPEG解码,并提高了视频的可能帧速率。
JPEG帧的解码很容易占用16ms以上的时间(取决于MCU的速度和视频分辨率)。 这意味着在大多数情况下,MJPEG视频的解码速率低于用户界面的正常帧速率。 对于某些应用,将整体帧速率降低到解码速率是可以接受的。 对于其他应用,即使视频以20 fps的速率运行,也需要保持用户界面60 fps的帧率。 有一个应用可作为示例:在视频旁边有一个具有动画效果的进度圆环。 60 fps的动画速率让进度圆环的效果达到最佳,即使视频只显示20 fps帧率的新帧。
上面关于STM32F746的示例在解码单个JPEG帧时耗费18-20 ms。
通过TouchGFX使用视频
有了TouchGFX,可以轻松将视频变为您用户界面的一部分。 需要三样东西:一个视频控件、一个视频控制器、当然还有MJPEG视频文件。
视频控件在用户界面中使用,与所有其他控件一样。 视频控制器是构成完整TouchGFX环境(HAL、操作系统、驱动程序等)的底层软件的一部分。
视频控制器由控制MJPEG文件解码和缓冲区管理的软件组成。
TouchGFX设计器自动将视频控制器包含到所有模拟器项目中。 这样就可以在模拟器原型中轻松使用视频:只需添加一个视频控件,选择一个视频文件,然后按下 “Run Simulator(运行模拟器)”( F5
).
如要在硬件上使用视频,还需目标项目(IAR、Keil、arm-gcc、CubeIDE)中有视频控制器。 这已经添加到一些TouchGFX板件规范包(见下面的列表),但您可以通过TouchGFX Generator向任意项目添加视频支持。 参见用户指南。
当您拥有支持视频的平台时,可以轻松在TouchGFX设计器中添加和配置视频控件。 此处详细介绍了如何在TouchGFX设计器中使用视频控件。
TouchGFX项目中的视频文件
当在TouchGFX设计器中包含视频文件后,它将.avi文件复制到assets/vidoes
文件夹。 在代码生成期间,视频以.bin文件格式被复制到generated/videos/bin
,以.o文件格式被复制到generated/videos/obj
。 .O和.bin文件包含相同的数据,但是.O文件是ELF格式(这是一些编译器和IDE的首选格式)。
在生成代码时执行的项目更新程序将视频文件包含在目标项目中。 这意味着视频文件被链接到可执行文件中,并且在应用程序中可用。
应用编程器可以添加其他视频到assets/videos
文件夹。 这些也将包括在项目中。
文件generated/videos/include/videos/VideoDatabase.hpp
包含与编译到应用程序中的视频有关的符号性信息:
const uint32_t video_SampleVideo1_240x135_bin_length = 1242282;
#ifdef SIMULATOR
extern const uint8_t* video_SampleVideo1_240x135_bin_start;
#else
extern const uint8_t video_SampleVideo1_240x135_bin_start[];
#endif
可在用户代码中通过这些声明将视频分配给视频控件。
从用户代码使用视频文件
在一些项目中,仅从TouchGFX设计器中选择一个视频是不够的。 例如, 您想在启动时选择不同的视频。 首先,必须添加视频文件到assets/videos
:
生成代码(或运行make assets)时,assets/videos
文件夹中的视频文件将包含在VideoDatabase.hpp
中:
const uint32_t video_myVideo_bin_length = 1242282;
#ifdef SIMULATOR
extern const uint8_t* video_myVideo_bin_start;
#else
extern const uint8_t video_myVideo_bin_start[];
#endif
我们现在可以在用户代码中使用这些声明,让视频控件播放我们的电影:
Screen1View.cpp
#include <gui/screen1_screen/Screen1View.hpp>
#include <videos/VideoDatabase.hpp>
Screen1View::Screen1View()
{
}
void Screen1View::setupScreen()
{
Screen1ViewBase::setupScreen();
video.setVideoData(video_myVideo_bin_start, video_myVideo_bin_length);
video.setWidthHeight(240, 136);
video.play();
}
注意!视频数据现在链接到应用程序中。 可以避免这种情况,方法是不将任何视频放在assets/videos
中,且手动将视频刷写到外部flash存储的专用区域。 然后使用flash地址传递地址和长度:
void Screen1View::setupScreen()
{
...
video.setVideoData((const uint8_t*)0xA0200000, 1242282);
...
}
支持视频功能的开发套件列表
TouchGFX设计器中的TouchGFX板件设置包默认为这些开发套件启用了视频功能:
- STM32F769Discovery(硬件加速解码)
- STM32H750BDiscovery(硬件加速解码)
- STM32F746Discovery(基于软件的解码)
如果您正在使用其他开发套件或定制硬件,请记住在TouchGFX Generator中启用视频支持。
创建MJPEG AVI文件
大多数视频不以MJPEG AVI文件格式存储,因为这不是常见的视频格式。 因此,在TouchGFX项目中使用视频文件之前,通常需要将其转换为MJPEG格式。
可以完成转换,例如使用FFMPEG。 还提供其他应用和在线服务。
使用FFMPEG
此处可以找到用于FFMPEG的Windows二进制文件。
要将视频文件input.mov转换为MJPEG格式,可使用该命令:
ffmpeg -i input.mov -s 480x272 -vcodec mjpeg -qscale 1 -an output.avi
MJPEG视频在output.avi文件中。 该文件可以复制到assets/videos
中。
为了视频能保持正确的长宽比,可以以像素为单位指定宽度(这里是480),高度“-1”(-1):
ffmpeg -i input.mov -vf scale=480:-1 -vcodec mjpeg -qscale 1 -an output.avi
可以剪切视频:使用-ss指定开始时间,使用-t或-to指定持续时间或停止时间:
ffmpeg -ss 3 -i input.mov -t 3 -s 480x272 -vcodec mjpeg -qscale 1 -an output_section.avi
或:
ffmpeg -ss 3 -i input.mov -to 5 -s 480x272 -vcodec mjpeg -qscale 1 -an output_section.avi
选项 | 说明 |
---|---|
-s | 输出视频分辨率 |
-qscale | 质量等级范围1 - 31(从优到差) |
-an | 去除音频 |
-vf | 设置过滤器图 |
-ss | 开始时间(以秒为单位) |
-t | 持续时间 |
-to | 停止时间(以秒为单位) |
解码策略
如上所述,TouchGFX需要将单独的MJPEG帧从JPEG转换为RGB,然后才在帧缓存区显示。 这种解码可以在需要时实时完成,也可以异步完成 - 提前将下一帧解码到视频缓冲区。
异步解码是由第二个任务(而不是UI任务)完成的。 这意味着在某些情况下,解码可以与UI任务的绘制并行进行。
TouchGFX有3种策略,每种策略都有优点和缺点:
策略 | 说明 |
---|---|
直接解码帧到缓冲区 | 在需要时,将当前视频帧直接解码到帧缓存区 |
专用缓冲区 | 将下一个视频帧解码到视频缓冲区, 从视频缓冲区复制到帧缓存区。 |
双缓冲区 | 将下一个视频帧解码到第二视频缓冲区, 解码完成后,交换视频缓冲区。 |
Designer总是为模拟器项目选择“直接解码到帧缓存区”策略。 可以在Generator中配置对目标使用什么样的策略。
下文将详细讲解这些策略。
直接解码帧到缓冲区
“直接解码到帧缓存区”策略在TouchGFX引擎的渲染阶段解码JPEG帧(参见‘渲染’章节)
在更新阶段(参见‘更新’章节),视频控件决定是否应该将电影提前到下一帧。 在接下来的渲染阶段,JPEG帧被逐行解码到一个“行缓冲区”。 然后对像素进行转换以匹配帧缓存区格式,并复制到帧缓存区。
如果视频控件被其他控件覆盖,它会被重绘成多个块(可见部分被分割成矩形)。 绘制每一个这样的块需要反复进行解压缩工作。 这使得该策略不适合用户界面,因为随着解码时间的增加,那里的其他UI元素(如按钮)会被置于视频的顶部。
该解决方案的优点是只使用了很少额外内存。
专用缓冲区
专用或单缓冲区解码策略首先将JPEG帧解码到专用RGB缓冲区,然后再从该缓冲区复制到帧缓存区。
与直接策略相反,现在解码工作在一个单独任务(而不是通常的TouchGFX任务)中运行。 视频控件计算一个新的电影帧是否应该在下一个用户界面tick中显示,并通过信号告知解码任务开始解码下一帧。 在解码过程中,视频缓冲区不能被绘制到帧缓存区(它被部分更新)。 当解码任务正在运行时,用户界面不得重绘视频控件。 绘制完成后,它将继续。 如果用户界面不需要重绘视频,则用户界面可以照常运行。
新视频被完全解码后,将视频渲染到帧缓存区的成本与绘制位图的成本(低)相同。 通过该策略,在视频上放置按钮或文本不成问题。
该策略的缺点是任务和视频缓冲区占用内存。
双缓冲区
双缓冲区解码策略使用两个RGB缓冲区。 解码在这两个缓冲区中完成,而渲染到帧缓存区则发生于另一个RGB缓冲区中。
当一个帧被解码后,解码任务等待UI显示该视频缓冲区并释放前一个缓冲区。 一旦用户界面改变了缓冲区,就可以开始解码下一帧。
该策略的一个明显缺点是内存使用是前一种策略的两倍。
视频帧速率和用户界面帧速率
不同的解码策略对用户界面帧速率的影响也不同。 用户界面帧速率是每秒在帧缓存区中产生的(不同)帧数量。
如果采用“直接解码到帧缓存区”策略,视频解码速度容易影响用户界面的有效帧速率。 假设解码一个JPEG帧需要28 ms,并且我们希望每秒显示20帧视频(20 fps)。 这是比较实际的,因为总解码时间是:28 ms x 20/s = 560 ms/s。 20 fps对应于每3帧(60 fps)有一个新视频帧。 所以在每三个UI帧中,我们需要一个新的视频帧,但是由于解码时间是渲染阶段的一部分,渲染一帧需要28 ms,加上渲染用户界面的其他部分需要(比如)2 ms。 总的帧渲染时间是30 ms,我们失去了一个tick,但我们准备为下一个tick生成一个新帧。 在下一帧中,我们没有解码视频,所以渲染时间低于16 ms,符合限制要求。 然后,我们可以在第四个tick中开始解码第二个视频帧。
因此,视频帧速率是20 fps,用户界面帧速率是40 fps(共计60 fps)。
结果是我们不能让UI的其他元素具有60 fps的动画效果,因为视频解码限制了帧速率。
如果采用“双缓冲区”策略,这种问题得到缓解。 用户界面总是有一个可用的视频缓冲区用于最新的帧。 解码任务可以将该缓冲区与另一个缓冲区(包含下一帧)交换(当它可用时),而渲染线程没有主动绘制。 交换之后,解码任务可以立即开始解码下一帧:
在UI帧1和UI帧2中,UI显示的是第一个解码的视频帧。 同时,解码器在生成帧2。 在UI帧3中,该帧已经准备就绪并将被使用。 解码器处于空闲状态,可以开始解码下一帧(图中没有显示)。
因此,即使视频解码只能每2个ticks中生成一个新帧,也可以在每帧中更新用户界面的其他元素。
相关文章
如上所述,对目标项目的视频支持是在TouchGFX生成器中配置的。 可参见生成器用户指南获取说明。
视频控件在TouchGFX设计器中可用。 此处详细介绍了如何在TouchGFX设计器中使用视频控件。