Android RTMP协议直播之编译适用于Android设备的X264&RTMP库

在之前的文章中对直播项目技术选型有做简单描述,即使用软编码方式进行音视频编码,其中软编码指的是H264视频编码以及AAC音频编码,对于H264视频编码相信读者在之前的文章中已经有了一定的了解,本文将介绍AAC高级音频编码和RTMP协议。在此基础上将会介绍如何在Linux平台上编译出适合Android移动设备的X264以及RTMP库。

AAC高级音频编码

AAC高级音频编码(Advanced Audio Coding),出现于1997年,为一种基于MPEG-2的有损数字音频压缩的专利音频编码标准,由Fraunhofer IIS、杜比实验室、AT&T、Sony、Nokia等公司共同开发。2000年,MPEG-4标准在原本的基础上加上了PNS(Perceptual Noise Substitution)等技术,并提供了多种扩展工具。为了区别于传统的MPEG-2,AAC又称为MPEG-4 AAC。其作为MP3的后继者而被设计出来,在相同的比特率之下,AAC相较于MP3通常可以达到更好的声音质量。以上来自维基百科

AAC音频格式

AAC音频格式有ADIF和ADTS两种:

  • ADIF:Audio Data Interchange Format 音频数据交换格式。这种格式的特征是可以确定的找到这个音频数据的开始,不需进行在音频数据流中间开始的解码,即它的解码必须在明确定义的开始处进行。故这种格式常用在磁盘文件中。
  • ADTS:Audio Data Transport Stream 音频数据传输流。这种格式的特征是它是一个有同步字的比特流,解码可以在这个流中任何位置开始。它的特征类似于mp3数据流格式。

两者的区别:ADTS可以在任意帧解码,也就是说它每一帧都有头信息。ADIF只有一个统一的头,所以必须得到所有的数据后解码。且这两种的header的格式也是不同的,目前一般编码后的和抽取出的都是ADTS格式的音频流

ADTS(Audio Data Transport Stream)

ADTS是帧序列,本身具备流特征,在音频流的传输与处理方面更加合适。ADST帧结构如下

ADTS Header


主要概念说明

  • syncword :同步头 总是0xFFF, all bits must be 1,代表着一个ADTS帧的开始
  • ID:MPEG Version: 0 for MPEG-4, 1 for MPEG-2
  • Layer:always: ‘00’
  • profile:表示使用哪个级别的AAC,有些芯片只支持AAC LC 。在MPEG-2 AAC中定义了3种
  • sampling_frequency_index:表示使用的采样率下标,通过这个下标在 Sampling Frequencies[ ]数组中查找得知采样率的值。
    • 0: 96000 Hz
    • 1: 88200 Hz
    • 2: 64000 Hz
    • 3: 48000 Hz
    • 4: 44100 Hz
    • 5: 32000 Hz
  • channel_configuration: 表示声道数
    • 0: Defined in AOT Specifc Config
    • 1: 1 channel: front-center
    • 2: 2 channels: front-left, front-right
    • 3: 3 channels: front-center, front-left, front-right
    • 4: 4 channels: front-center, front-left, front-right, back-center
    • channels: front-center, front-left, front-right, back-left, back-right
  • frame_length : 一个ADTS帧的长度包括ADTS头和AAC原始流.
  • adts_buffer_fullness:0x7FF 说明是码率可变的码流

RTMP实时消息传送协议

Real Time Messaging Protocol(实时消息传送协议协议)是Adobe Systems公司为Flash播放器和服务器之间音频、视频和数据传输开发的私有协议。RTMP协议是TCP/IP五层体系结构中应用层的协议。
RTMP协议中基本的数据单元称为消息(Message)。当RTMP协议在互联网中传输数据的时候,消息会被拆分成更小的单元,称为消息块(Chunk)。

消息(Message)

是RTMP协议中基本的数据单元。不同种类的消息包含不同的Message Type ID,代表不同的功能。

字段 大小 备注
Message Type 1字节 0x04表示Ping包,0x08为audio,0x09为video
Payload Length 3字节 这三个字节表述了tag中数据段的大小。
Time Stamp 4字节 记录了每一个tag相对于第一个tag(File Header)的相对时间。以毫秒为单位。而File Header的time stamp永远为0。
Stream ID 3字节 标识消息所属媒体流,永远为0。
Message Body datasize(内容大小) 存储音视频信息。

消息块(Chunk Message)

RTMP协议中规定,消息在网络上传输时被拆分成消息块(Chunk)。消息块首部(Chunk Header)有三部分组成:用于标识本块的Chunk Basic Header,用于标识本块负载所属消息的Chunk Message Header,以及当时间戳溢出时才出现的Extended Timestamp。

消息分块(Message Split)

在消息被分割成几个消息块的过程中,消息负载部分(Message Body)被分割成大小固定的数据块(默认是128字节,最后一个数据块可以小于该固定长度),并在其首部加上消息块首部(Chunk Header),就组成了相应的消息块。消息分块过程如下图所示,一个大小为307字节的消息被分割成128字节的消息块(除了最后一个),如图8所示。
RTMP传输媒体数据的过程中,发送端首先把媒体数据封装成消息,然后把消息分割成消息块,最后将分割后的消息块通过TCP协议发送出去。接收端在通过TCP协议收到数据后,首先把消息块重新组合成消息,然后通过对消息进行解封装处理就可以恢复出媒体数据。

编译适用于Android设备的RTMP库

注:本文所需要的编译环境均为Linux或者Mac OS,如果读者的系统是windows,请安装Cygwin后进行编译

step 1.
官网中下载最新版本

step 2.
使用命令解压

1
tar -zxvf rtmpdump-2.3.tgz

step 3.
新建“jni”目录并在jni目录中新增“src”以及“include”目录,将解压目录中的.c以及.h文件进行copy

1
2
3
mkdir jni
mkdir src
mkdir include


将rtmpdump文件夹中的所有.c和.h进行拷贝

1
2
3
4
cp rtmpdump-2.3/*.c ./jni/src/
cp rtmpdump-2.3/*.h ./jni/include/
cp rtmpdump-2.3/librtmp/*.c ./jni/src/
cp rtmpdump-2.3/librtmp/*.h ./jni/include/

在我编译过程中如果只是这样copy会出现找不到头文件,为了防止这种情况,将jni/include中的所有头文件再次拷贝到jni目录中,最终结果如下图

step 4.
使用ndk-build进行编译,首先在jni目录中编写Android.mk以及Application.mk文件,我这里已经为NDK配置了环境变量,如果读者不能使用该命令,请配置NDK环境变量
Application.mk

1
APP_ABI:=all

Android.mk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
LOCAL_PATH:=$(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE:=librtmp
LOCAL_SRC_FILES := \
src/amf.c \
src/hashswf.c \
src/log.c \
src/parseurl.c \
src/rtmp.c \
LOCAL_C_INCLUDES:=$(LOCAL_PATH)/include
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog
LOCAL_CFLAGS := -Wall -O2 -DSYS=posix -DNO_CRYPTO
TARGET_PLATFORM := android-23
include $(BUILD_SHARED_LIBRARY)

step 5.
返回jni父目录使用ndk-build进行编译

1
ndk-build

等待编译完成,最终会在jni同级目录中生成相关目录

上列中演示的编译版本已经编译好了一份点我下载提取码ft8m,或者从AndroidRTMPLive中获取rtmp库

编译适用于Android设备的X264库

X264是H264标准下的一个编码库,遵循H264标准。X264编码被广泛运用各个软件中,比如ffmpeg,VLC media player等。使用X264需要从官网中下载源码进行编译
注:若读者因环境原因无法编译x264,这里我已经编译好了一份点我下载提取码czo5,或者从AndroidRTMPLive中获取x264库

  • 从官网中下载源码.
  • 解压

    1
    tar -jxvf last_x264.tar.bz2
  • 修改configure,在android中不支持在linux中的x.so.x加载方式,需要进行修改。否则工程进行编译的时候会提示找不到xxx函数,这是因为编译的so库->.so.v。

    1
    2
    # echo "SONAME=libx264.so.$API" >> config.mak 查找该代码片段,根据当前编译系统修改,修改为以下代码段
    echo "SONAME=libx264.so" >> config.mak
  • 进入解压目录,编写编译脚本“build_android.sh”

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    #!/bin/bash
    export ANDROID_NDK_HOME=/usr/ben/ndk/android-ndk-r14b
    export ANDROID_VERSION=/android-24

    function build_x264
    {
    echo "build_x264 for "$ARCH" start..."
    ./configure --prefix=$OUT_PREFIX \
    --enable-shared \
    --enable-static \
    --host=$HOST \
    --enable-pic \
    --cross-prefix=$CROSS_PREFIX \
    --sysroot=$SYSROOT \
    --disable-asm

    make clean
    make
    make install
    echo "build_x264 for "$ARCH" end..."
    }


    #clear android dir
    rm -rf $(pwd)/android


    #arm
    ARCH=arm
    OUT_PREFIX=$(pwd)/android/$ARCH
    TOOLCHAINS=$ANDROID_NDK_HOME/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64
    CROSS_PREFIX=$TOOLCHAINS/bin/arm-linux-androideabi-
    SYSROOT=$ANDROID_NDK_HOME/platforms$ANDROID_VERSION/arch-$ARCH
    HOST=arm-linux

    build_x264

    #arm64
    ARCH=arm64
    OUT_PREFIX=$(pwd)/android/$ARCH
    TOOLCHAINS=$ANDROID_NDK_HOME/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64
    CROSS_PREFIX=$TOOLCHAINS/bin/aarch64-linux-android-
    SYSROOT=$ANDROID_NDK_HOME/platforms$ANDROID_VERSION/arch-$ARCH
    HOST=arm-linux

    build_x264

    #mips
    ARCH=mips
    OUT_PREFIX=$(pwd)/android/$ARCH
    TOOLCHAINS=$ANDROID_NDK_HOME/toolchains/mipsel-linux-android-4.9/prebuilt/linux-x86_64
    CROSS_PREFIX=$TOOLCHAINS/bin/mipsel-linux-android-
    SYSROOT=$ANDROID_NDK_HOME/platforms$ANDROID_VERSION/arch-$ARCH
    HOST=arm-linux

    build_x264

    #mips64

    ARCH=mips64
    OUT_PREFIX=$(pwd)/android/$ARCH
    TOOLCHAINS=$ANDROID_NDK_HOME/toolchains/mips64el-linux-android-4.9/prebuilt/linux-x86_64
    CROSS_PREFIX=$TOOLCHAINS/bin/mips64el-linux-android-
    SYSROOT=$ANDROID_NDK_HOME/platforms$ANDROID_VERSION/arch-$ARCH
    HOST=arm-linux

    build_x264

    #x86

    ARCH=x86
    OUT_PREFIX=$(pwd)/android/$ARCH
    TOOLCHAINS=$ANDROID_NDK_HOME/toolchains/x86-4.9/prebuilt/linux-x86_64
    CROSS_PREFIX=$TOOLCHAINS/bin/i686-linux-android-
    SYSROOT=$ANDROID_NDK_HOME/platforms$ANDROID_VERSION/arch-$ARCH
    HOST=i686-linux

    build_x264

    #x86_64

    ARCH=x86_64
    OUT_PREFIX=$(pwd)/android/$ARCH
    TOOLCHAINS=$ANDROID_NDK_HOME/toolchains/x86_64-4.9/prebuilt/linux-x86_64
    CROSS_PREFIX=$TOOLCHAINS/bin/x86_64-linux-android-
    SYSROOT=$ANDROID_NDK_HOME/platforms$ANDROID_VERSION/arch-$ARCH
    HOST=x86_64-linux

    build_x264
  • 使用chmod更改权限,运行shell脚本

    1
    ./build_android

等待编译脚本编译完成,shell脚本执行完后会在当前目录生成“Android”目录,如下

在Android目录中已经编译好了多个ABI版本的so库

随意分享,您的支持将鼓励我继续创作!