在之前的文章中对直播项目技术选型有做简单描述,即使用软编码方式进行音视频编码,其中软编码指的是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文件进行copy1
2
3mkdir jni
mkdir src
mkdir include
将rtmpdump文件夹中的所有.c和.h进行拷贝1
2
3
4cp 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.mk1
APP_ABI:=all
Android.mk1
2
3
4
5
6
7
8
9
10
11
12
13
14
15LOCAL_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库