Nginx RTMP MP4 模块分析

ngx_rtmp_mp4.c 实现了 Nginx RTMP 模块中的 MP4 支持,主要用于将音视频流保存为 MP4 文件格式。MP4 文件格式被广泛应用于视频流的存储与播放,它包括了多个结构化的数据块(boxes),如 ftypmoovmdat 等。Nginx RTMP 使用该模块将实时的 RTMP 流转换为 MP4 文件,以便可以进行进一步的处理和播放。

1. 核心数据结构与常量

1.1 ngx_rtmp_mp4_sample_t

这个结构体用于表示一个 MP4 样本(sample),它包含:

  • size:样本的大小。

  • duration:样本的时长。

  • delay:样本的延迟时间。

  • timestamp:样本的时间戳。

  • key:标记该样本是否是关键帧(keyframe)。

1.2 ngx_rtmp_mp4_file_type_t

这个枚举类型用于表示 MP4 文件的类型:

  • NGX_RTMP_MP4_FILETYPE_INIT:初始化文件(通常是 init.mp4)。

  • NGX_RTMP_MP4_FILETYPE_SEG:片段文件(通常是流的实际数据片段)。

1.3 ngx_rtmp_mp4_track_type_t

这个枚举类型表示 MP4 中的轨道类型:

  • NGX_RTMP_MP4_VIDEO_TRACK:视频轨道。

  • NGX_RTMP_MP4_AUDIO_TRACK:音频轨道。

2. MP4 文件结构与写入

MP4 文件是由多个盒子(boxes)组成的,每个盒子包含不同的元数据或媒体数据。以下是一些常见的盒子类型和它们的功能:

2.1 ngx_rtmp_mp4_field_* 系列函数

这些函数用于将不同长度的数据写入到缓冲区 ngx_buf_t *b 中。它们按照大端格式将数据拆解为字节并写入:

  • ngx_rtmp_mp4_field_32:写入 4 字节的数据。

  • ngx_rtmp_mp4_field_24:写入 3 字节的数据。

  • ngx_rtmp_mp4_field_16:写入 2 字节的数据。

  • ngx_rtmp_mp4_field_8:写入 1 字节的数据。

这些函数确保数据按照指定格式被正确地写入 MP4 文件的缓冲区中。

2.2 ngx_rtmp_mp4_start_boxngx_rtmp_mp4_update_box_size
  • ngx_rtmp_mp4_start_box:用于开始一个新的 MP4 盒子。它首先写入盒子的大小(初始为 0),然后写入盒子的类型(如 ftypmoov 等)。返回指向缓冲区当前位置的指针,方便后续填充内容。

  • ngx_rtmp_mp4_update_box_size:用于更新盒子的大小。每个盒子的大小是动态计算的,因此在写入完成后,调用此函数来修正盒子的大小。

2.3 ngx_rtmp_mp4_write_ftypngx_rtmp_mp4_write_styp
  • ngx_rtmp_mp4_write_ftyp:写入 ftyp 盒子,它是 MP4 文件的第一个盒子,包含文件类型和兼容的品牌信息。

  • ngx_rtmp_mp4_write_styp:写入 styp 盒子,类似于 ftyp,用于指示文件的类型和兼容性。

2.4 ngx_rtmp_mp4_write_mvhdngx_rtmp_mp4_write_tkhd
  • ngx_rtmp_mp4_write_mvhd:写入 mvhd 盒子,它是 MP4 文件的“视频文件头”,包括时间轴、时长、视频的编码方式等元数据。

  • ngx_rtmp_mp4_write_tkhd:写入 tkhd 盒子,表示一个轨道(track)的基本信息。每个轨道都有一个 tkhd 盒子,用于定义该轨道的 ID、时长、类型等信息。

2.5 ngx_rtmp_mp4_write_mdhdngx_rtmp_mp4_write_hdlr
  • ngx_rtmp_mp4_write_mdhd:写入 mdhd 盒子,表示媒体头部信息,包含该媒体的时间尺度和持续时间。

  • ngx_rtmp_mp4_write_hdlr:写入 hdlr 盒子,表示该轨道的处理器类型(如视频处理器或音频处理器)。

2.6 ngx_rtmp_mp4_write_minf, ngx_rtmp_mp4_write_mdia, ngx_rtmp_mp4_write_trak
  • ngx_rtmp_mp4_write_minf:写入 minf 盒子,包含媒体数据的信息,如视频或音频的编解码器、压缩格式等。

  • ngx_rtmp_mp4_write_mdia:写入 mdia 盒子,包含关于轨道(如视频或音频)的完整描述。

  • ngx_rtmp_mp4_write_trak:写入 trak 盒子,表示一个轨道的完整信息,包括 tkhdmdia 等。

3. 视频和音频轨道

3.1 ngx_rtmp_mp4_write_videongx_rtmp_mp4_write_audio
  • ngx_rtmp_mp4_write_video:用于写入视频轨道的相关信息。包括视频的编码信息、宽高、分辨率等。

  • ngx_rtmp_mp4_write_audio:用于写入音频轨道的相关信息。包括音频的编码信息、采样率、声道数等。

3.2 ngx_rtmp_mp4_write_esds

esds 盒子是音频流的一个描述符,包含了音频的配置信息,尤其是 AAC 音频流的相关参数。ngx_rtmp_mp4_write_esds 会解析并写入 esds 盒子。

4. DASH 和 HLS 相关操作

MP4 模块也涉及到生成与 DASH(动态自适应流媒体)和 HLS(HTTP Live Streaming)兼容的文件。这些文件通常包含音频和视频轨道的描述,并被切割为多个片段(segment)以适应不同网络条件下的流媒体播放。

5. 总结

这段代码实现了 Nginx RTMP 模块中的 MP4 文件生成支持。MP4 文件格式由多个盒子(boxes)组成,每个盒子保存不同类型的元数据或媒体数据。Nginx 使用这个模块将实时的 RTMP 流转换为 MP4 格式,这对于流媒体分发和存储非常有用。初学者需要重点理解 MP4 文件的盒子结构以及如何将音视频数据写入到这些盒子中。

关键要点:
  1. MP4 文件盒子结构:了解 ftypmoovmdatstsd 等盒子的作用。

  2. 视频和音频轨道描述:每个轨道(视频或音频)都有详细的描述信息,如 tkhd(轨道头)、mdhd(媒体头)等。

  3. 数据写入与更新:通过 ngx_rtmp_mp4_field_* 系列函数将数据写入到缓冲区,并更新盒子的大小。

这段代码对于实现 MP4 文件格式的流媒体存储与播放至关重要,适用于与 DASH 或其他流媒体协议兼容的使用场景。