跳转到主要内容

MJPEG视频

从4.18版本开始,TouchGFX支持使用MJPEG视频。 视频可以用于创建更生动的用户界面或显示简短说明或用户指南。

视频通过Video Widget包含在用户界面中。 该控件在TouchGFX设计器中可用,可以作为任意其他控件添加到用户界面。

在TouchGFX设计器中使用“视频(Video)”控件

在STM32微控制器上解码视频需要额外的支持软件包。 该软件在TouchGFX Generator中启用视频支持,从而轻松包含在项目中。 在已使能video功能的TouchGFX工程开发板上(见下面的列表),可以轻松运行视频,方法是像平常一样按下“运行目标(F6)”。

通过STM32F746Discovery使用“视频(Video)”控件

如果你的目标代码中没有视频支持,会得到编译或链接错误。

MJPEG视频

MJPEG视频由打包在容器文件(.avi)中的大量JPEG图像(帧)组成。 压缩后的JPEG帧不能直接复制到帧缓存区。 每一帧必须被解压为RGB格式(16、24或32位),然后才能在显示器上显示。

与使用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向任意项目添加视频支持。 参见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

添加视频到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设计器中的TouchGFX板件设置包默认为这些开发套件启用了视频功能:

  • STM32F769Discovery(硬件加速解码)
  • STM32H750BDiscovery(硬件加速解码)
  • STM32U5F9Discovery (hardware accelerated decoding)
  • 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视频

因此,视频帧速率是20 fps,用户界面帧速率是40 fps(共计60 fps)。

结果是我们不能让UI的其他元素具有60 fps的动画效果,因为视频解码限制了帧速率。

如果采用“双缓冲区”策略,这种问题得到缓解。 用户界面总是有一个可用的视频缓冲区用于最新的帧。 解码任务可以将该缓冲区与另一个缓冲区(包含下一帧)交换(当它可用时),而渲染线程没有主动绘制。 交换之后,解码任务可以立即开始解码下一帧:

20 fps视频

在UI帧1和UI帧2中,UI显示的是第一个解码的视频帧。 同时,解码器在生成帧2。 在UI帧3中,该帧已经准备就绪并将被使用。 解码器处于空闲状态,可以开始解码下一帧(图中没有显示)。

因此,即使视频解码只能每2个ticks中生成一个新帧,也可以在每帧中更新用户界面的其他元素。

如上所述,对目标项目的视频支持是在TouchGFX生成器中配置的。 参见Generator用户指南。 说明。

视频控件在TouchGFX设计器中可用。 此处详细介绍了如何在TouchGFX设计器中使用视频控件。