强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

RTMP 协议精讲 / 08 - 音频编解码

音频编解码(Audio Codecs)

8.1 RTMP 音频架构

RTMP 中的音频数据以 FLV Audio Tag 格式封装,通过 Type 8 消息传输。音频在直播中的重要性不亚于视频——观众对音频卡顿的容忍度远低于视频。

音频数据在 RTMP 中的位置:

RTMP Connection
├── Message Type 8 (Audio)
│   ┌────────────────────────────────────────────┐
│   │  FLV Audio Tag Body                        │
│   │  ┌──────────┬──────────┬─────────────────┐│
│   │  │ Audio    │ AAC      │  Audio Data     ││
│   │  │ Header   │ Packet   │  (Raw/ADTS)     ││
│   │  │ (1 byte) │ Type     │                 ││
│   │  │          │ (1 byte) │                 ││
│   │  └──────────┴──────────┴─────────────────┘│
│   └────────────────────────────────────────────┘
│
└── Message Type 9 (Video) ...

8.2 FLV Audio Tag 结构

音频头(第一个字节)

 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
│Sound  │Sound  │
│Format │Rate   │
│(4b)   │(2b)   │
+-+-+-+-+-+-+-+-+
│Sound  │Sound  │
│Size   │Type   │
│(1b)   │(1b)   │
+-+-+-+-+-+-+-+-+

字段详解:
┌──────────────┬────────┬──────────────────────────────────────┐
│  字段        │  位数  │  取值                                 │
├──────────────┼────────┼──────────────────────────────────────┤
│ Sound Format │ 4 bits │ 音频编码格式                          │
│              │        │ 0=PCM/PCM_LE                         │
│              │        │ 1=ADPCM                              │
│              │        │ 2=MP3                                │
│              │        │ 3=PCM_LE (Little Endian)             │
│              │        │ 4=Nellymoser 16kHz Mono              │
│              │        │ 5=Nellymoser 8kHz Mono               │
│              │        │ 6=Nellymoser                         │
│              │        │ 7=G.711 A-law                        │
│              │        │ 8=G.711 mu-law                       │
│              │        │ 9=reserved                           │
│              │        │ 10=AAC                               │
│              │        │ 11=Speex                             │
│              │        │ 14=MP3 8kHz                          │
│              │        │ 15=Device-specific                   │
├──────────────┼────────┼──────────────────────────────────────┤
│ Sound Rate   │ 2 bits │ 采样率索引                            │
│              │        │ 0=5.5kHz (仅 Nellymoser/Speex)       │
│              │        │ 1=11kHz (22.05kHz for MP3/AAC)       │
│              │        │ 2=22kHz (44.1kHz for MP3/AAC)        │
│              │        │ 3=44kHz                              │
├──────────────┼────────┼──────────────────────────────────────┤
│ Sound Size   │ 1 bit  │ 采样位深                              │
│              │        │ 0=8-bit                              │
│              │        │ 1=16-bit                             │
├──────────────┼────────┼──────────────────────────────────────┤
│ Sound Type   │ 1 bit  │ 声道数                                │
│              │        │ 0=mono (单声道)                       │
│              │        │ 1=stereo (立体声)                     │
└──────────────┴────────┴──────────────────────────────────────┘

常见音频头值

十六进制Sound FormatRateSizeType典型配置
0xAFAAC (10)3 (44kHz)1 (16bit)1 (stereo)AAC 44.1kHz 立体声
0x2FMP3 (2)3 (44kHz)1 (16bit)1 (stereo)MP3 44.1kHz 立体声
0xAEAAC (10)3 (44kHz)1 (16bit)0 (mono)AAC 44.1kHz 单声道

8.3 AAC 编码详解

AAC(Advanced Audio Coding)是 RTMP 直播中最常用的音频编码格式。

8.3.1 AAC Audio Tag 结构

当 Sound Format = 10 (AAC) 时:

AAC Audio Tag Body:
┌────────────────┬─────────────────┬──────────────────────────┐
│ Audio Header   │ AAC Packet Type │      AAC Data             │
│ (1 byte)       │ (1 byte)        │                           │
│ 0xAF           │                 │                           │
│                │ 0=Sequence Header│  AudioSpecificConfig      │
│                │ 1=AAC Raw       │  AAC 压缩数据              │
└────────────────┴─────────────────┴──────────────────────────┘

8.3.2 AAC Sequence Header (AudioSpecificConfig)

AAC Sequence Header 是 AudioSpecificConfig 数据,用于初始化 AAC 解码器:

AudioSpecificConfig (ISO 14496-3):

 5 bits       4 bits      4 bits       variable
┌──────────┬───────────┬───────────┬──────────────────┐
│ audio    │ sampling  │ channel   │  GASpecificConfig│
│ object   │ freq      │ config    │  (可选)          │
│ type     │ index     │           │                  │
└──────────┴───────────┴───────────┴──────────────────┘

Audio Object Type:
┌──────┬──────────────────────────────────────────────┐
│  值  │  类型                                        │
├──────┼──────────────────────────────────────────────┤
│  1   │  AAC Main                                    │
│  2   │  AAC LC (Low Complexity) — 最常用            │
│  3   │  AAC SSR (Scalable Sample Rate)              │
│  4   │  AAC LTP (Long Term Prediction)              │
│  5   │  SBR (Spectral Band Replication)             │
│  6   │  AAC Scalable                                │
│  7   │  TwinVQ                                      │
│  8   │  CELP                                        │
│  39   │  ER AAC LD                                   │
└──────┴──────────────────────────────────────────────┘

Sampling Frequency Index:
┌──────┬──────────────────────┐
│  值  │  采样率 (Hz)         │
├──────┼──────────────────────┤
│  0   │  96000               │
│  1   │  88200               │
│  2   │  64000               │
│  3   │  48000               │
│  4   │  44100  ← 最常用     │
│  5   │  32000               │
│  6   │  24000               │
│  7   │  22050               │
│  8   │  16000               │
│  9   │  12000               │
│  10  │  11025               │
│  11  │  8000                │
│  12  │  7350                │
└──────┴──────────────────────┘

Channel Configuration:
┌──────┬──────────────────────┐
│  值  │  声道配置             │
├──────┼──────────────────────┤
│  0   │  特定配置 (在 ASC 中) │
│  1   │  中心 (单声道)        │
│  2   │  左+右 (立体声)       │
│  3   │  中心+左+右           │
│  4   │  中心+左+右+后环绕    │
│  5   │  中心+左+右+后左+后右 │
│  6   │  5.1 (6 声道)         │
│  7   │  7.1 (7.1 声道)       │
└──────┴──────────────────────┘

8.3.3 AudioSpecificConfig 解析实现

#!/usr/bin/env python3
"""
AudioSpecificConfig 解析器
用于解析 AAC Sequence Header
"""

import struct
from dataclasses import dataclass


@dataclass
class AudioSpecificConfig:
    """AAC AudioSpecificConfig"""
    audio_object_type: int      # 音频对象类型
    sampling_frequency_index: int  # 采样率索引
    sampling_frequency: int     # 实际采样率 (Hz)
    channel_configuration: int  # 声道配置
    frame_length_flag: int = 0  # 帧长度标志
    depends_on_core_coder: int = 0
    extension_flag: int = 0

    # AAC-LC 特有
    sbr_present_flag: int = 0
    ps_present_flag: int = 0


# 采样率映射表
SAMPLING_FREQ_TABLE = {
    0: 96000, 1: 88200, 2: 64000, 3: 48000,
    4: 44100, 5: 32000, 6: 24000, 7: 22050,
    8: 16000, 9: 12000, 10: 11025, 11: 8000,
    12: 7350
}

# 音频对象类型名称
AUDIO_OBJECT_TYPE_NAMES = {
    0: "Null", 1: "AAC Main", 2: "AAC-LC", 3: "AAC-SSR",
    4: "AAC-LTP", 5: "SBR", 6: "AAC Scalable",
    7: "TwinVQ", 8: "CELP", 9: "HVXC",
    12: "TTSI", 13: "Main Synthesis", 14: "Wavetable Synthesis",
    39: "ER AAC LD", 42: "ER AAC ELD"
}

# 声道配置名称
CHANNEL_CONFIG_NAMES = {
    0: "Defined in AOT", 1: "Center (Mono)", 2: "L+R (Stereo)",
    3: "Center+L+R", 4: "Center+L+R+Rear", 5: "5 Surround",
    6: "5.1 Surround", 7: "7.1 Surround"
}


def parse_audio_specific_config(data: bytes) -> AudioSpecificConfig:
    """
    解析 AudioSpecificConfig
    
    参数:
        data: AAC Sequence Header 的数据部分(不含 Audio Header 和 AAC Packet Type)
    
    返回:
        AudioSpecificConfig 对象
    """
    if len(data) < 2:
        raise ValueError("AudioSpecificConfig 至少需要 2 字节")

    # 位读取器
    bits = BitReader(data)

    # Audio Object Type (5 bits)
    audio_object_type = bits.read_bits(5)
    if audio_object_type == 31:
        # 扩展类型:再读 6 位 + 32
        audio_object_type = 32 + bits.read_bits(6)

    # Sampling Frequency Index (4 bits)
    sampling_freq_index = bits.read_bits(4)
    if sampling_freq_index == 15:
        # 自定义采样率:读 24 位
        sampling_freq = bits.read_bits(24)
    else:
        sampling_freq = SAMPLING_FREQ_TABLE.get(sampling_freq_index, 0)

    # Channel Configuration (4 bits)
    channel_config = bits.read_bits(4)

    config = AudioSpecificConfig(
        audio_object_type=audio_object_type,
        sampling_frequency_index=sampling_freq_index,
        sampling_frequency=sampling_freq,
        channel_configuration=channel_config,
    )

    # GASpecificConfig (如果 AOT 是 1-7 等)
    if audio_object_type in (1, 2, 3, 4, 6, 7):
        config.frame_length_flag = bits.read_bits(1)
        config.depends_on_core_coder = bits.read_bits(1)
        config.extension_flag = bits.read_bits(1)

    return config


def parse_aac_sequence_header(body: bytes) -> AudioSpecificConfig:
    """
    解析 AAC Sequence Header(完整的 Audio Tag Body)
    
    参数:
        body: Audio Tag Body(包含 1 byte Audio Header + 1 byte AAC Packet Type + ASC)
    """
    if len(body) < 4:
        raise ValueError("AAC Sequence Header 至少需要 4 字节")

    audio_header = body[0]  # 0xAF
    aac_packet_type = body[1]  # 0 = Sequence Header

    if aac_packet_type != 0:
        raise ValueError(f"不是 AAC Sequence Header,type={aac_packet_type}")

    asc_data = body[2:]
    return parse_audio_specific_config(asc_data)


class BitReader:
    """位读取器"""

    def __init__(self, data: bytes):
        self.data = data
        self.byte_pos = 0
        self.bit_pos = 0

    def read_bits(self, n: int) -> int:
        """读取 n 位"""
        result = 0
        for _ in range(n):
            if self.byte_pos >= len(self.data):
                break
            bit = (self.data[self.byte_pos] >> (7 - self.bit_pos)) & 1
            result = (result << 1) | bit
            self.bit_pos += 1
            if self.bit_pos >= 8:
                self.bit_pos = 0
                self.byte_pos += 1
        return result


def create_audio_specific_config(
    object_type: int = 2,
    sample_rate: int = 44100,
    channels: int = 2
) -> bytes:
    """
    创建 AudioSpecificConfig
    
    参数:
        object_type: 音频对象类型 (2=AAC-LC)
        sample_rate: 采样率
        channels: 声道数
    """
    # 查找采样率索引
    freq_index = None
    for idx, freq in SAMPLING_FREQ_TABLE.items():
        if freq == sample_rate:
            freq_index = idx
            break
    if freq_index is None:
        raise ValueError(f"不支持的采样率: {sample_rate}")

    # 编码 AudioSpecificConfig
    bits = 0
    bits |= (object_type & 0x1F) << (32 - 5)
    bits |= (freq_index & 0x0F) << (32 - 5 - 4)
    bits |= (channels & 0x0F) << (32 - 5 - 4 - 4)

    result = bits.to_bytes(2, 'big')
    return result


# 测试
def test_audio_specific_config():
    """测试 AudioSpecificConfig 解析"""
    # AAC-LC, 44100Hz, Stereo
    # Object Type=2, Freq Index=4, Channel=2
    # Binary: 00010 0100 0010 0000
    #         00010010 00010000 = 0x12 0x10
    data = bytes([0x12, 0x10])

    config = parse_audio_specific_config(data)
    print(f"Audio Object Type: {config.audio_object_type} ({AUDIO_OBJECT_TYPE_NAMES.get(config.audio_object_type, 'Unknown')})")
    print(f"Sampling Frequency: {config.sampling_frequency} Hz (index={config.sampling_frequency_index})")
    print(f"Channel Configuration: {config.channel_configuration} ({CHANNEL_CONFIG_NAMES.get(config.channel_configuration, 'Unknown')})")

    # 验证
    assert config.audio_object_type == 2  # AAC-LC
    assert config.sampling_frequency == 44100
    assert config.channel_configuration == 2  # Stereo

    # 编码验证
    encoded = create_audio_specific_config(2, 44100, 2)
    assert encoded == data

    print("✅ AudioSpecificConfig 测试通过")


if __name__ == '__main__':
    test_audio_specific_config()

8.4 AAC Raw Data

当 AAC Packet Type = 1 时,Body 中包含的是 AAC 压缩音频帧

AAC Raw Frame:
┌─────────────────────────────────────────────────────┐
│  AAC Frame (variable length)                         │
│  通常是 ADTS 或 RAW AAC 数据                         │
└─────────────────────────────────────────────────────┘

注意:
- RTMP 中的 AAC Raw 不含 ADTS 头
- 直接是 AAC 编码器输出的原始数据
- 需要配合之前收到的 AudioSpecificConfig 解码

8.5 ADTS 格式

ADTS(Audio Data Transport Stream)是 AAC 数据的传输格式,常用于 HLS/文件存储:

ADTS Header (7/9 bytes):
┌──────────────────────────────────────────────────────────────┐
│  syncword (12 bits)         = 0xFFF                           │
│  ID (1 bit)                 = 0 (MPEG-4)                     │
│  layer (2 bits)             = 00                              │
│  protection_absent (1 bit)  = 1 (无 CRC)                     │
│  profile (2 bits)           = AAC-LC 对应的 profile           │
│  sampling_freq_index (4 bits)                                │
│  private_bit (1 bit)                                         │
│  channel_config (3 bits)                                     │
│  original_copy (1 bit)                                       │
│  home (1 bit)                                                │
│  copyright_id (1 bit)                                        │
│  copyright_start (1 bit)                                     │
│  frame_length (13 bits)     = ADTS header + AAC data          │
│  buffer_fullness (11 bits)  = 0x7FF (VBR)                    │
│  num_frames_minus_1 (2 bits) = 0 (1 frame per ADTS)          │
│  CRC (16 bits, 可选)                                         │
└──────────────────────────────────────────────────────────────┘
def create_adts_header(
    object_type: int,
    sample_rate: int,
    channels: int,
    data_length: int
) -> bytes:
    """
    创建 ADTS Header
    
    参数:
        object_type: AAC Profile (0=Main, 1=LC, 2=SSR)
        sample_rate: 采样率
        channels: 声道数
        data_length: AAC 原始数据长度
    """
    # 采样率索引
    freq_table = {
        96000: 0, 88200: 1, 64000: 2, 48000: 3,
        44100: 4, 32000: 5, 24000: 6, 22050: 7,
        16000: 8, 12000: 9, 11025: 10, 8000: 11,
        7350: 12
    }
    freq_index = freq_table.get(sample_rate, 4)
    profile = object_type - 1  # AAC-LC=2, profile=1
    frame_length = 7 + data_length  # 7 bytes ADTS header + data

    adts = bytearray(7)
    adts[0] = 0xFF                        # syncword high
    adts[1] = 0xF0                        # syncword low + ID=0 + layer=00 + protection=1
    adts[1] |= (profile & 0x03) << 2     # profile
    adts[1] |= (freq_index >> 2) & 0x01  # sampling_freq_index bit 3
    adts[2] = (freq_index & 0x03) << 6   # sampling_freq_index bits 2-1
    adts[2] |= (channels & 0x07) << 2    # channel_config
    adts[3] = 0                           # 其他标志
    adts[4] = (frame_length >> 5) & 0xFF # frame_length 高 8 位
    adts[5] = (frame_length & 0x1F) << 3 # frame_length 低 5 位
    adts[6] = 0xFC                        # buffer_fullness + num_frames

    return bytes(adts)


def add_adts_to_aac_raw(aac_raw: bytes, object_type: int = 2,
                         sample_rate: int = 44100, channels: int = 2) -> bytes:
    """为 AAC Raw 数据添加 ADTS 头"""
    adts = create_adts_header(object_type, sample_rate, channels, len(aac_raw))
    return adts + aac_raw

8.6 MP3 编码

虽然 AAC 是主流,但 RTMP 也支持 MP3 编码(Sound Format = 2):

MP3 Audio Tag

MP3 Audio Tag Body:
┌────────────────┬──────────────────────────────┐
│ Audio Header   │      MP3 Frame Data          │
│ (1 byte)       │  (含 MP3 Header + Data)      │
│ 0x2F (典型)    │                               │
└────────────────┴──────────────────────────────┘

MP3 Frame Header (4 bytes):
┌──────────────────────────────────────────────────┐
│  syncword (11 bits)       = 0x7FF                 │
│  version (2 bits)         = 11 (MPEG1)            │
│  layer (2 bits)           = 01 (Layer III)        │
│  protection (1 bit)                                │
│  bitrate_index (4 bits)   = 码率索引               │
│  sampling_rate (2 bits)   = 采样率索引             │
│  padding (1 bit)                                   │
│  channel_mode (2 bits)    = 声道模式               │
│  ... 其他标志位                                     │
└──────────────────────────────────────────────────┘

8.7 音频配置参数

推流音频参数

参数推荐值说明
编码格式AAC-LC最广泛兼容
采样率44100HzCD 质量
声道立体声 (2ch)或单声道 (1ch)
采样位深16-bit标准位深
码率128-192kbps音乐/语音场景
AAC ProfileLCLow Complexity

FFmpeg 音频编码命令

# AAC 编码(推荐)
ffmpeg -i input.mp4 -c:a aac -b:a 128k -ar 44100 -ac 2 -f flv rtmp://...

# AAC HE v1(低码率优化)
ffmpeg -i input.mp4 -c:a libfdk_aac -profile:a aac_he -b:a 64k -f flv rtmp://...

# AAC HE v2(更低码率)
ffmpeg -i input.mp4 -c:a libfdk_aac -profile:a aac_he_v2 -b:a 32k -f flv rtmp://...

# MP3 编码
ffmpeg -i input.mp4 -c:a libmp3lame -b:a 128k -ar 44100 -f flv rtmp://...

# Opus 编码(Enhanced RTMP,需服务端支持)
ffmpeg -i input.mp4 -c:a libopus -b:a 64k -f flv rtmp://...

AAC Profile 对比

Profile说明适用场景码率范围
AAC-LCLow Complexity通用64-256kbps
HE-AAC v1LC + SBR低码率32-64kbps
HE-AAC v2LC + SBR + PS超低码率16-32kbps
AAC-LDLow Delay实时通信64-128kbps
AAC-ELDEnhanced LD超低延迟32-64kbps

8.8 音频与视频同步

音视频同步是直播系统中的核心挑战:

RTMP 时间戳同步

时间戳(毫秒)    视频帧              音频帧
  0               Video Keyframe     Audio Frame
  33              Video Inter        Audio Frame
  66              Video Inter        Audio Frame
  99              Video Inter        Audio Frame
  100                                  Audio Frame
  133              Video Inter
  ...

同步原则:
- 视频和音频使用同一个时间基(毫秒)
- 播放器根据时间戳对齐音视频
- 时间戳单调递增
- 音频时间戳通常更均匀(固定间隔)

音视频同步实现

class AVMuxer:
    """音视频复用器 — 同步音频和视频"""

    def __init__(self):
        self.video_timestamp = 0  # ms
        self.audio_timestamp = 0  # ms
        self.audio_frame_duration = 23  # ms (44100Hz, 1024 samples/frame)
        self.video_frame_duration = 33  # ms (30fps)

    def get_next_video_timestamp(self) -> int:
        ts = self.video_timestamp
        self.video_timestamp += self.video_frame_duration
        return ts

    def get_next_audio_timestamp(self) -> int:
        ts = self.audio_timestamp
        self.audio_timestamp += self.audio_frame_duration
        return ts

8.9 音频解码器完整实现

#!/usr/bin/env python3
"""
RTMP Audio Decoder
完整的音频消息解析器
"""

import struct
from dataclasses import dataclass


@dataclass
class AudioFrame:
    """音频帧信息"""
    codec: str
    codec_id: int
    sample_rate: int
    sample_size: int
    channels: int
    is_stereo: bool
    data: bytes
    timestamp: int = 0
    # AAC 特有
    aac_packet_type: int = -1
    aac_sequence_header: object = None


SOUND_FORMAT_NAMES = {
    0: 'PCM', 1: 'ADPCM', 2: 'MP3',
    3: 'PCM_LE', 4: 'Nellymoser 16kHz', 5: 'Nellymoser 8kHz',
    6: 'Nellymoser', 7: 'G.711 A-law', 8: 'G.711 mu-law',
    10: 'AAC', 11: 'Speex', 14: 'MP3 8kHz', 15: 'Device'
}

SAMPLE_RATE_MAP = {
    0: 5512, 1: 11025, 2: 22050, 3: 44100
}


def decode_audio_message(body: bytes, timestamp: int = 0) -> AudioFrame:
    """
    解析 Audio Message Body
    
    参数:
        body: Type 8 消息的 Body
        timestamp: 消息时间戳
    
    返回:
        AudioFrame 对象
    """
    if len(body) < 1:
        raise ValueError("Audio message body too short")

    header = body[0]
    sound_format = (header >> 4) & 0x0F
    sound_rate = (header >> 2) & 0x03
    sound_size = (header >> 1) & 0x01
    sound_type = header & 0x01

    codec_name = SOUND_FORMAT_NAMES.get(sound_format, f'Unknown({sound_format})')
    sample_rate = SAMPLE_RATE_MAP.get(sound_rate, 44100)
    sample_size = 8 if sound_size == 0 else 16
    channels = 1 if sound_type == 0 else 2

    frame = AudioFrame(
        codec=codec_name,
        codec_id=sound_format,
        sample_rate=sample_rate,
        sample_size=sample_size,
        channels=channels,
        is_stereo=(sound_type == 1),
        data=body[1:],
        timestamp=timestamp,
    )

    # AAC 特殊处理
    if sound_format == 10 and len(body) >= 2:
        frame.aac_packet_type = body[1]
        if body[1] == 0:
            # AAC Sequence Header
            try:
                config = parse_audio_specific_config(body[2:])
                frame.aac_sequence_header = config
                frame.sample_rate = config.sampling_frequency
                frame.channels = config.channel_configuration
            except Exception:
                pass
        frame.data = body[2:]

    return frame


# 测试
def test_audio_decoder():
    """测试音频解码"""
    # AAC Sequence Header: 0xAF 0x00 0x12 0x10
    body = bytes([0xAF, 0x00, 0x12, 0x10])
    frame = decode_audio_message(body, timestamp=1000)

    print(f"Codec: {frame.codec}")
    print(f"Sample Rate: {frame.sample_rate} Hz")
    print(f"Channels: {frame.channels}")
    print(f"Is Stereo: {frame.is_stereo}")
    print(f"AAC Packet Type: {'Sequence Header' if frame.aac_packet_type == 0 else 'Raw'}")

    assert frame.codec == 'AAC'
    assert frame.sample_rate == 44100
    assert frame.channels == 2
    print("✅ Audio decoder 测试通过")


if __name__ == '__main__':
    test_audio_decoder()

注意事项

  1. Sequence Header 优先:AAC Sequence Header 必须在任何 AAC Raw 数据之前发送
  2. 采样率映射:Sound Rate 字段对于 AAC/MP3 使用不同的映射(22kHz/44kHz vs 5.5kHz/11kHz)
  3. ADTS 头:RTMP 中的 AAC Raw 不含 ADTS 头,转封装为 HLS/MP4 时需要添加
  4. 音频帧时长:AAC-LC 每帧通常 1024 个采样,44100Hz 下约 23ms
  5. 单声道 vs 立体声:低码率场景建议使用单声道(减半码率),语音场景尤为推荐
  6. G.711 通话场景:WebRTC 关联的 RTMP 流可能使用 G.711 编码,采样率为 8kHz

扩展阅读


上一章07 - 视频编解码 下一章09 - 流媒体服务器 — 了解主流 RTMP 服务器与部署