视频播放应用使用说明

概述

本文基于 RTL8773EWE-VS 的 Watch NAND Flash SDK,描述了如何播放存储在文件系统内 AVI 格式的视频。AVI 视频包含 YUYV422 格式的视频流和 MP3 编码的音频流。

FFmpeg

FFmpeg 是一个功能强大的多媒体处理工具,可以用于录制、转换和流式传输音视频内容。它支持几乎所有常见的音视频格式和编解码器。

官网: FFmpeg 官网

可执行文件下载: FFmpeg 可执行文件下载链接

../../../_images/FFmpeg_download_address.png

FFmpeg 可执行文件下载

FFmpeg 指令参考 Code

如上所述,转码的 AVI 格式文件为 YUYV 格式视频帧和 MP3 编码音频帧。可供参考的转码代码如下:

ffmpeg -i input.mp4 -ss 00:09:30 -t 00:00:11 -vf "scale=-1:454, crop=454:454:176.5:0" -c:v rawvideo -r 20 -pix_fmt yuyv422 -c:a libmp3lame -b:a 192k -ar 44100 -ac 2 output.avi

参数解释:

  • -i input file :输入文件。

  • -ss 00:09:30 :开始时间。

  • -t 00:00:11 :持续时间。

  • -vf “ scale=-1:454, crop=454:454:176.5:0” :视频尺寸和裁剪范围,格式为 scale=w:h, crop=w:h:x:y。-1 表示宽度随高度等比例缩放。

  • -c:v rawvideo :指定视频流编码为 rawvideo。

  • -r 20 :转出视频为 20 帧。

  • -pix_fmt yuyv422 :转出视频流格式为 yuyv422。

  • -c:a libmp3lame :指定音频流编码为 libmp3lame。

  • -b:a 192k MP3 :bit 率为 192k。

  • -ar 44100 MP3 :采样频率为 44100。

  • -ac 2 :MP3 双声道。

  • output.avi :输出文件。

其他更多指令格式请参考 FFmpeg 文档

AVI 封装格式

AVI 文件目前有两种版本:

  • AVI 1.0: 原始的 AVI 文件封装格式。最大支持 4GB ,为了安全限制一般为 2GB。

  • Open-DML:扩展的 AVI 文件封装格式,主要改动是解除了文件大小的限制并减小了开销,修订了索引的数据结构。

本文暂不考虑 Open-DML 格式的支持,后续解析说明按照 AVI 1.0 格式。

AVI 基本数据结构

AVI 文件的两种基本数据结构为 CHUNKLIST

typedef struct
{
   DWORD dwFourCC;
   DWORD dwSize;
   BYTE data[dwSize];
}  CHUNK;

typedef struct
{
   DWORD dwList;
   DWORD dwSize;
   DWORD dwFourCC;
   BYTE data[dwSize - 4];
}  LIST;

一个 chunk 包含一个 chunk 的视频、音频或字幕数据,这些数据可以是 header,也可以是数据帧。 dwFourCC 表示该 chunk 的类型,由 4 个字符表示。 例如, strh 表示一个 chunk stream header, 00dc 表示一个 chunk 非压缩的视频帧, 01wb 表示一个 chunk 非压缩的音频帧等。 dwSize 表示从 data 起算的实际 chunk 大小。

一个 list 通常包含和 AVI 文件有关的信息。 dwList 会有两种值: RIFFLISTRIFF 表示 AVI 文件头, LIST 表示其他 list 头。 dwFourCC 表示该 list 的类型,由 4 个字符表示。例如, hdrl 表示 header list, movi 表示数据帧 list 等。 dwSize 表示从 dwFourCC 起算的实际 list 大小。

AVI 文件结构

一个 AVI 文件通常由以下几个部分组成:

  1. “RIFF” list

“RIFF” 为整个 AVI 文件的起始,它的 dwFourCC 为 “AVI” (注意有一个空格)。

  1. “hdrl” list

“hdrl” list 通常包含一个 “avih” chunk,表示 AVI 文件的 main header。 “avih” 的数据结构定义:

typedef struct
{
   DWORD  dwMicroSecPerFrame;
   DWORD  dwMaxBytesPerSec;
   DWORD  dwPaddingGranularity;
   DWORD  dwFlags;
   DWORD  dwTotalFrames;
   DWORD  dwInitialFrames;
   DWORD  dwStreams;
   DWORD  dwSuggestedBufferSize;
   DWORD  dwWidth;
   DWORD  dwHeight;
   DWORD  dwReserved[4];
} MainAVIHeader;
  1. “strl” list

一个 AVI 文件中会有和音频流、视频流、字幕流对应个数的 “strl” list。一个 “strl” list 至少会包含一个 “strh” chunk 和一个 “strf” chunk,分别表示 stream header 和 stream format。 可选的 chunk 还有 “strd” 和 “strn”,分别表示额外的流信息和流名称。一般 “strl” 不包含 “strd” 和 “strn”。

  1. “strh” 数据结构定义:

typedef struct
{
   FOURCC fccType;
   FOURCC fccHandler;
   DWORD  dwFlags;
   WORD   wPriority;
   WORD   wLanguage;
   DWORD  dwInitialFrames;
   DWORD  dwTimeScale;
   DWORD  dwRate;
   DWORD  dwStartTime;
   DWORD  dwLength;
   DWORD  dwSuggestedBufferSize;
   DWORD  dwQuality;
   DWORD  dwSampleSize;
   RECT Frame;
}  AVIStreamHeader;
  1. “strf” 根据视频流或音频流,定义为不同的数据结构:

typedef struct
{
   DWORD  headerSize;
   DWORD  biWidth;
   DWORD  biHeight;
   WORD   biPlanes;
   WORD   bitsPerPixel;
   DWORD  biCompression;
   DWORD  biSizeImage;
   DWORD  biXPelsPerMeter;
   DWORD  biYPelsPerMeter;
   DWORD  biClrUsed;
   DWORD  biClrImportant;
} AVIStreamFormatBitMap;

typedef struct
{
   WORD wFormatTag;
   WORD wChannels;
   DWORD dwSamplesPerSec;
   DWORD dwAvgBytesPerSec;
   WORD wBlockAlign;
   WORD wBitsPerSample;
   WORD wSize;
   BYTE reserved[12];
}  AVIStreamFormatAudioWave;
  1. “INFO” list

“INFO” list 用于描述该 AVI 文件的信息,例如版权,作者等。对于本文解码无用,可以跳过。

  1. “movi” list

“movi” list 包含数据流 chunk: ##dc##wb##tx 表示,分别表示一帧视频流/音频流和字幕流。 “##”为流的编号,和 “hdrl” 的顺序有关。 例如,第一个 “hdrl” 为视频流, “movi” list 就会包含若干个 00dc chunk,第二个 “hdrl” 为音频流, “movi” list 就会包含若干个 01wb chunk。 “movi” list 的 chunk,是需要送到音频解码器、视频解码器的数据。

  1. “idx1” list

“idx1” list 包含了 “movi” list 中所有数据流 chunk 的索引。数据结构如下:

typedef struct
{
   DWORD dwChunkId;
   DWORD dwFlags;
   DWORD dwOffset;
   DWORD dwSize;
} AVIIndexEntry;

需要注意的是,offset 的偏移量,通常是基于 movi 的位置。

  1. “JUNK” chunk

“JUNK” chunk 包含一些用于对齐的填充数据,可以直接跳过。

AVI 文件解析流程

../../../_images/AVI_file_parsing.png

AVI 文件解析流程图

视频播放功能

视频播放流程

播放视频的流程基本可以分为三个步骤:开始播放,更新音视频帧,结束播放。

  1. 开始播放:调用 app_video_start(),可以播放指定文件系统中的 AVI 视频文件。 播放视频流程会开启一条 audio track 用于音频播放,创建一个 realgui img 控件用于视频播放。控件相关操作通过发送消息给 GUI task 进行。

  2. 结束播放:调用 app_video_stop(),结束播放视频流程。

  3. 更新音视频帧:在视频播放流程开始后,需要按照视频播放的每帧时长,从文件系统中读取视频帧,更新给 img 控件;读取音频帧,更新给 audio track。

../../../_images/Video_three-in-one.png

开始播放 - 停止播放 - 更新音 / 视频帧

备注

  • 更新音视频帧的流程,需要在一个控件的动画回调中进行。

  • app_video_start()app_video_stop() 需要在 APP 任务调用。

  • 读取音视频帧需要在 APP 任务调用。

视频播放 Demo 移植

  1. 更新包含 avi_parser 版本的 filesystem.lib

  2. 添加 app_video.c编译。

  3. 移植 APP 消息的新增指令:

  1. IO_MSG_VIDEO_START: 开启指定的视频播放。

  1. 移植 APP MMI 的新增指令:

  1. MMI_VIDEO_STOP: 结束当前视频播放。

  2. MMI_VIDEO_NEXT_AUDIO_FRAME:读取若干音频帧。

  3. MMI_VIDEO_NEXT_VIDEO_FRAME:读取下一帧视频帧。

  1. 实现 GUI 的新增更新事件,这三个事件会在 app_video_start()app_video_stop() 的流程中,发送消息给 gui task 处理:

  1. GUI_EVENT_VIDEO_CREATE:创建显示需要的 img 控件,并设置一个动画,动画持续时间需要和视频帧时长相同。随后关闭动画等待下个更新事件。

  2. GUI_EVENT_VIDEO_START:开启更新动画。

  3. GUI_EVENT_VIDEO_STOP:结束更新动画。

  1. 实现 GUI 的更新动画回调 gui_video_refresh_cb()。动画回调需要:

  1. 调用 app_video_refresh_clock(),更新 video 时钟。

  2. 调用 app_video_is_update_video_frame(), 判断是否需要更新视频帧。若是则发送 MMI_VIDEO_NEXT_VIDEO_FRAME 给 APP 任务。

  3. 在更新视频帧后,调用 app_video_is_update_audio_frame() 判断是否需要更新音频帧。若是则发送 MMI_VIDEO_NEXT_AUDIO_FRAME 给 APP 任务。

  4. 更新控件显示。

  1. VIDEO_H_MAXVIDEO_W_MAX 调整到期望视频最大的尺寸,避免浪费 PSRAM

VIDEO_BUFFER_ADDR_A 的地址注意修改,默认放在 PSRAM 的 PSRAM_APP_DEFINED_SECTION 之后。

  1. 功能宏: F_APP_VIDEO

RAM 和 Flash 使用统计

  1. PSRAM 的使用:

  1. video buffer: pixel + gui Header + imdc Header + imdc addr offset = (W * H * 2) + (8) + (12) + (H + 1) * 4 Byte。以 272 * 272 视频尺寸为例,需要 149060 Byte。

  2. audio buffer: 2 KByte。

  1. RAM 的使用:

    1. T_AV_CONTROLLER + AVIHandle_t + frame index lookup table + FILE = (88) + (116) + (video frames * 8 + audio frames * 8) + (2088)。

    以一段 20 秒,帧率为 20,audio MP3 采样率为 44100 的视频为例,需要 88 + 116 + 3208 + 6136 + 2088 = 11636 Byte。

  2. 在 Demo 中,选择了 272 * 272 的 YUYV422 格式视频帧,视频帧率为 20,音频格式为 MP3,192k 码率,44100 采样率,时长 20 秒的视频,占用空间为 56.9 MByte。