Amazon Kinesis Video Streams WebRTC集成AAC编解码器完整指南

核心摘要

  • AAC编解码器相比G.711可节省约50%带宽,在低码率场景下音质表现优于传统方案
  • 集成方案仅需修改设备端SDK和客户端NDK,无需改动KVS服务端架构
  • 完整覆盖SDP协商、RTP封包解包、平台特定编解码实现等关键技术环节
  • 支持iOS、Android及主流IoT设备的硬件AAC编解码能力

Amazon Kinesis Video Streams WebRTC集成AAC编解码器完整指南

Amazon Kinesis Video Streams WebRTC技术概览

Amazon Kinesis Video Streams提供了符合标准的WebRTC实现,作为一项完全托管的云服务运行。借助这项能力,开发者可以在摄像头、IoT设备与符合WebRTC标准的移动端或Web播放器之间建立安全的实时媒体流传输通道,实现双向音视频交互。这种托管模式的核心价值在于:无需自行构建、运维或扩展信令服务器、媒体中继服务器等WebRTC基础设施组件。

从架构层面来看,Amazon KVS WebRTC采用了典型的信令与媒体分离设计。信令通道负责处理SDP交换、ICE候选协商等控制面消息,而媒体流则通过P2P直连或TURN中继进行传输。这种设计既保证了低延迟的媒体传输体验,又提供了穿越NAT和防火墙的可靠性保障。

现有编解码器支持与AAC的技术优势

当前支持的编解码器清单

Amazon KVS WebRTC目前原生支持以下编解码器:

  • 音频编解码器:G.711 A-Law、G.711 U-Law、Opus
  • 视频编解码器:H.264、VP8

AAC编解码器的核心优势

AAC(Advanced Audio Coding)作为一种高效的音频压缩标准,在实时通信场景中展现出多项显著优势:

  • 更高的压缩效率:在相同音质条件下,AAC相比MP3可节省约30%的带宽开销
  • 低码率音质保持:即使在较低的比特率下,AAC仍能维持令人满意的音频质量
  • 广泛的设备兼容性:几乎所有现代移动设备、浏览器和IoT芯片都内置AAC硬件解码支持
  • 低延迟特性:编解码延迟可控,适合对实时性要求较高的音频传输场景
  • 多声道能力:原生支持立体声及多声道音频配置

各平台AAC支持情况

AAC在主流平台上的支持相当成熟:

  • iOS:从iOS 3.0版本起即提供原生硬件AAC编解码支持
  • Android:Android 3.0(API Level 11)及以上版本支持AAC编解码
  • IoT设备:大多数音频处理芯片方案都集成了AAC硬件编码能力

为何需要在KVS WebRTC中引入AAC

尽管KVS WebRTC已支持多种音频格式,引入AAC仍具有明确的业务价值:

  • 带宽成本优化:在保持音质的前提下显著减少网络带宽消耗
  • 用户体验提升:更优的音频质量配合更低的传输延迟
  • 运营成本降低:减少数据传输产生的费用支出
  • 设备适配增强:更好地兼容移动终端和IoT设备的硬件编解码能力

整体技术架构设计

AAC编解码器的集成方案遵循最小化改动原则,整体架构调整范围如下:

  • 设备端SDK修改:需要在amazon-kinesis-video-streams-webrtc-sdk-c中添加AAC相关处理逻辑
  • 客户端NDK修改:需要在WebRTC原生库中集成AAC编解码器支持
  • 服务端KVS:无需任何修改,信令服务透传SDP内容

这种设计的优势在于:服务端作为透明的信令中继,不参与编解码器协商的具体逻辑,因此新增编解码器支持仅需在端侧完成。对于正在评估多云账单代付解决方案的团队而言,这种架构也意味着可以灵活选择不同云服务商的资源进行部署测试。

关键技术实现详解

SDP中添加AAC编解码格式

SDP(Session Description Protocol)协商是WebRTC建立连接的核心环节。需要在以下位置添加AAC格式声明:

  • 设备端代码amazon-kinesis-video-streams-webrtc-sdk-c/src/source/PeerConnection/SessionDescription.c
  • iOS客户端webrtc/sdk/objc/components/audio_codec/RTCDefaultAudioDecoderFactory.m
  • Android客户端webrtc/sdk/android/src/java/org/webrtc/MediaCodecAudioDecoderFactory.java

RTP载荷的封包与解包规范

AAC的RTP封包遵循RFC 3640标准,该标准定义了MPEG-4基本流的RTP传输格式。主要技术特点包括:

  • 支持在单个RTP包中传输多个AAC帧
  • 使用AU-headers结构描述每个音频访问单元
  • 支持对大尺寸AAC帧进行分片传输

相关实现代码位于:

  • 设备端amazon-kinesis-video-streams-webrtc-sdk-c/src/source/Rtp/Codecs/RtpAacPayloader.c
  • iOS/Android NDKwebrtc/modules/audio_coding/aac_config_parser.cc

实际数据的AAC编解码处理

编解码实现需要针对不同平台采用相应的技术方案:

  • 设备端编码:大多数音频芯片支持AAC硬件编码,通过芯片厂商提供的SDK接口获取AAC音频帧
  • iOS客户端解码webrtc/sdk/objc/components/audio_codec/RTCAudioDecoderAAC.m
  • Android客户端解码webrtc/sdk/android/src/java/org/webrtc/HardwareAudioDecoder.java

设备端SDK实现步骤

开发环境准备

AAC集成涉及设备端SDK和客户端NDK的修改,需要准备以下编译环境:

  • 设备端:Linux环境进行代码编辑和编译
  • Android客户端:Linux环境进行NDK编译
  • iOS客户端:macOS环境进行Xcode编译

AAC RTP载荷处理器实现

核心文件结构:

  • src/source/PeerConnection/Rtp/Codecs/RtpAacPayloader.h – 头文件定义
  • src/source/PeerConnection/Rtp/Codecs/RtpAacPayloader.c – 实现文件

以下是RTP载荷处理器的核心实现代码:

#define LOG_CLASS "RtpAacPayloader"
#include "../../Include_i.h"

STATUS createPayloadForAac(UINT32 mtu, PBYTE aacFrame, UINT32 aacFrameLength, 
    PBYTE payloadBuffer, PUINT32 pPayloadLength, PUINT32 pPayloadSubLength,
    PUINT32 pPayloadSubLenSize)
{
    UNUSED_PARAM(mtu);
    ENTERS();
    STATUS retStatus = STATUS_SUCCESS;
    UINT32 payloadLength = 0;
    UINT32 payloadSubLenSize = 0;
    BOOL sizeCalculationOnly = (payloadBuffer == NULL);

    CHK(aacFrame != NULL && pPayloadSubLenSize != NULL && pPayloadLength != NULL && 
        (sizeCalculationOnly || pPayloadSubLength != NULL), STATUS_NULL_ARG);

    payloadLength = aacFrameLength;
    payloadSubLenSize = 1;

    // Only return size if given buffer is NULL
    CHK(!sizeCalculationOnly, retStatus);
    CHK(payloadLength <= *pPayloadLength && payloadSubLenSize <= *pPayloadSubLenSize, 
        STATUS_BUFFER_TOO_SMALL);

    MEMCPY(payloadBuffer, aacFrame, aacFrameLength);
    pPayloadSubLength[0] = aacFrameLength;

CleanUp:
    if (STATUS_FAILED(retStatus) && sizeCalculationOnly) {
        payloadLength = 0;
        payloadSubLenSize = 0;
    }
    if (pPayloadSubLenSize != NULL && pPayloadLength != NULL) {
        *pPayloadLength = payloadLength;
        *pPayloadSubLenSize = payloadSubLenSize;
    }
    LEAVES();
    return retStatus;
}

解包函数实现:

STATUS depayAacFromRtpPayload(PBYTE pRawPacket, UINT32 packetLength, 
    PBYTE pAacData, PUINT32 pAacLength, PBOOL pIsStart)
{
    ENTERS();
    STATUS retStatus = STATUS_SUCCESS;
    UINT32 aacLength = 0;
    BOOL sizeCalculationOnly = (pAacData == NULL);

    CHK(pRawPacket != NULL && pAacLength != NULL, STATUS_NULL_ARG);
    CHK(packetLength > 0, retStatus);

    aacLength = packetLength;
    CHK(!sizeCalculationOnly, retStatus);
    CHK(aacLength <= *pAacLength, STATUS_BUFFER_TOO_SMALL);

    MEMCPY(pAacData, pRawPacket, aacLength);

CleanUp:
    if (STATUS_FAILED(retStatus) && sizeCalculationOnly) {
        aacLength = 0;
    }
    if (pAacLength != NULL) {
        *pAacLength = aacLength;
    }
    if (pIsStart != NULL) {
        *pIsStart = TRUE;
    }
    LEAVES();
    return retStatus;
}

核心处理函数说明

  • createPayloadForAac – 创建AAC RTP载荷
  • getNextAacFrameLength – 获取下一个AAC帧的长度信息
  • createPayloadFromAacFrame – 从原始AAC帧创建RTP载荷
  • depayAacFromRtpPayload – 从RTP载荷中解包还原AAC数据

编解码器集成配置

关键配置参数:

  • 编解码器枚举RTC_CODEC_AAC
  • 默认载荷类型DEFAULT_PAYLOAD_AAC (97)
  • SDP配置:”AAC/16000/1″ 用于rtpmap属性

核心枚举和常量定义

src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h 中添加:

  • RTC_CODEC 枚举中添加 RTC_CODEC_AAC = 8
  • AAC成为SDK支持的第8种编解码器

SDP协商相关配置

src/source/PeerConnection/SessionDescription.h 中定义以下常量:

  • AAC的SDP描述值:#define AAC_VALUE "AAC/16000"
  • AAC的默认payload类型:#define DEFAULT_PAYLOAD_AAC (UINT64) 96
  • AAC的时钟频率:#define AAC_CLOCKRATE (UINT64) 16000
  • AAC的格式参数:#define DEFAULT_AAC_FMTP "profile-level-id=1; mode=AAC-hbr; config=F8F1; SizeLength=13; IndexLength=3; IndexDeltaLength=3"

SDP协商处理逻辑

src/source/PeerConnection/SessionDescription.c 中需要完成以下修改:

  • 在codec表初始化时添加AAC支持:hashTableUpsert(codecTable, RTC_CODEC_AAC, DEFAULT_PAYLOAD_AAC)
  • 在SDP解析时识别AAC编解码器:检查AAC_VALUE并设置rtcCodec = RTC_CODEC_AAC
  • 在生成SDP offer/answer时包含AAC的rtpmap和fmtp属性
  • 在音频编解码器判断逻辑中包含AAC:isAudioCodec = (codec == RTC_CODEC_MULAW || codec == RTC_CODEC_ALAW || codec == RTC_CODEC_OPUS || codec == RTC_CODEC_AAC)

RTP打包和解包集成

src/source/PeerConnection/Rtp.c 的发送路径中添加AAC处理:

case RTC_CODEC_AAC:
    rtpPayloadFunc = createPayloadForAac;
    rtpTimestamp = CONVERT_TIMESTAMP_TO_RTP(AAC_CLOCKRATE, pFrame->presentationTs);
    break;

src/source/PeerConnection/PeerConnection.c 的接收路径中添加AAC处理:

case RTC_CODEC_AAC:
    depayFunc = depayAacFromRtpPayload;
    clockRate = AAC_CLOCKRATE;
    break;

同时在编解码器表中注册AAC:hashTablePut(pKvsPeerConnection->pCodecTable, rtcCodec, DEFAULT_PAYLOAD_AAC)

示例应用程序支持

samples/Samples.h 中添加以下定义:

  • AAC样本帧数量:#define NUMBER_OF_AAC_FRAME_FILES 206
  • AAC编解码器名称:#define AUDIO_CODEC_NAME_AAC "aac"
  • AAC帧持续时间:#define SAMPLE_AUDIO_AAC_FRAME_DURATION (64 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND)

samples/kvsWebRTCClientMaster.c 中完成以下集成:

  • 添加命令行参数解析以支持AAC选项
  • 配置AAC的rolling buffer参数(32kbps比特率)
  • 实现AAC样本帧的读取和发送逻辑
  • 设置正确的帧间隔时间(64ms)

样本数据文件位于 samples/aacSampleFrames/ 目录,包含206个预编码的AAC样本帧文件(sample-001.aac 到 sample-206.aac)。

客户端NDK实现步骤

核心配置项

  • 构建开关rtc_use_aacbuild_overrides/build.gni 中定义
  • 平台支持:默认在 Windows、iOS、Android 平台启用AAC
  • 编译宏:使用 DISABLE_AAC 宏控制AAC功能的编译包含

AAC解析和处理模块

common_audio/aac/ 目录下的核心文件:

  • aac_common.h/cc – AAC通用定义和帧处理逻辑
  • aac_config_parser.h/cc – AAC配置解析器
  • aac_decoder_impl.h/cc – AAC解码器实现

RTP打包和解包模块

modules/rtp_rtcp/source/ 目录下的文件:

  • rtp_format_aac.h/cc – AAC RTP打包器
  • audio_rtp_depacketizer_aac.h/cc – AAC RTP解包器

平台特定实现

iOS/macOS平台

  • sdk/objc/components/audio_codec/RTCAudioEncoderAAC.h/mm
  • sdk/objc/components/audio_codec/RTCAudioDecoderAAC.h/mm

Android平台

  • sdk/android/src/jni/audio_codec_aac.cc

核心AAC编解码器库

新增的第三方AAC库文件:

  • modules/third_party/aac/aac_enc_dec.h – AAC编解码器头文件
  • modules/third_party/aac/aac_encode.c – AAC编码器实现
  • modules/third_party/aac/aac_decode.c – AAC解码器实现

这些文件实现了AAC编解码算法,支持16kHz采样率配置。

WebRTC音频编码模块集成

新增的音频编码模块文件:

  • modules/audio_coding/codecs/aac/aac_interface.h/cc – AAC接口定义与实现
  • modules/audio_coding/codecs/aac/audio_encoder_aac.h/cc – AAC编码器封装
  • modules/audio_coding/codecs/aac/audio_decoder_aac.h/cc – AAC解码器封装
  • modules/audio_coding/codecs/aac/audio_coder_aac_common.h/cc – AAC通用功能

API层面的编解码器支持

AAC编码器API实现示例(api/audio_codecs/aac/audio_encoder_aac.h/cc):

AudioEncoder::EncodedInfo AudioEncoderAACImpl::EncodeImpl(
    uint32_t rtp_timestamp,
    rtc::ArrayView audio,
    rtc::Buffer* encoded) {
    // 缓存音频数据直到有足够的帧
    if (num_10ms_frames_buffered_ == 0)
        first_timestamp_in_buffer_ = rtp_timestamp;

    // 将多声道音频数据分离到各个声道缓冲区
    const size_t start = kSampleRateHz / 100 * num_10ms_frames_buffered_;
    for (size_t i = 0; i < kSampleRateHz / 100; ++i)
        for (size_t j = 0; j < num_channels_; ++j)
            encoders_[j].speech_buffer[start + i] = audio[i * num_channels_ + j];

    // 检查是否有足够的帧进行编码
    if (++num_10ms_frames_buffered_ < num_10ms_frames_per_packet_) {
        return EncodedInfo();
    }

    // 对每个声道分别进行AAC编码
    const size_t samples_per_channel = SamplesPerChannel();
    size_t bytes_encoded = 0;
    for (size_t i = 0; i < num_channels_; ++i) {
        bytes_encoded = WebRtcAAC_Encode(
            encoders_[i].encoder,
            encoders_[i].speech_buffer.get(),
            samples_per_channel,
            encoders_[i].encoded_buffer.data());
        if (bytes_encoded <= 0) {
            return EncodedInfo();
        }
    }

    // 构建编码结果
    EncodedInfo info;
    info.encoded_bytes = bytes_encoded;
    info.encoded_timestamp = first_timestamp_in_buffer_;
    info.payload_type = payload_type_;
    info.encoder_type = CodecType::kAac;
    return info;
}

AAC解码器API实现示例(api/audio_codecs/aac/audio_decoder_aac.h/cc):

int AudioDecoderAACImpl::DecodeInternal(const uint8_t* encoded,
                                        size_t encoded_len,
                                        int sample_rate_hz,
                                        int16_t* decoded,
                                        SpeechType* speech_type) {
    RTC_DCHECK_EQ(SampleRateHz(), sample_rate_hz);
    int16_t temp_type = 1; // 默认为语音类型

    // 调用底层AAC解码接口
    size_t ret = WebRtcAAC_Decode(dec_state_, encoded, encoded_len,
                                   decoded, &temp_type);
    *speech_type = ConvertSpeechType(temp_type);
    return static_cast(ret);
}

// 数据包时长计算
int AudioDecoderAACImpl::PacketDuration(const uint8_t* encoded,
                                        size_t encoded_len) const {
    // AAC: 每个样本每声道占用1/2字节
    return static_cast(2 * encoded_len / Channels());
}

构建系统配置

BUILD.gn文件修改要点:

  • modules/audio_coding/BUILD.gn 中添加AAC相关的构建目标
  • api/audio_codecs/BUILD.gn 中集成AAC编解码器工厂
  • webrtc.gni 中添加 rtc_include_aac = true 配置选项

编解码器工厂集成

工厂类修改:

  • builtin_audio_encoder_factory.cc – 添加AAC编码器到内置工厂
  • builtin_audio_decoder_factory.cc – 添加AAC解码器到内置工厂
  • AudioEncoder::CodecType 枚举中添加 kAac = 7

Android平台特

AWS账单代付

AWS/阿里云/谷歌云官方认证架构师,专注云计算解决方案。