【RTSP】客户端(三) 音频相关
ADTS头生成
根据给定的频率返回采样率索引
该索引信息主要是在ADTS头中进行使用
int GetSampleRateIndex(int freq){
int i = 0;
int freq_arr[13] = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350};
for(i = 0; i < 13; i++){
if(freq == freq_arr[i]){
return i;
}
}
return 4; // 默认是44100 Hz
}
生成填充ADTS头信息缓冲区
ADTS 头是 AAC 音频流的标识信息,通常用于传输音频流(例如通过 RTP/RTSP 传输),加入该头部信息的作用在于发送RTP包的时候,让对方得知该包是一个音频
void AdtsHeader(char *adts_header_buffer, int data_len, int profile, int sample_rate_index, int channels)
{
int adts_len = data_len + 7; // 总长度 = 数据长度 + 头部长度(7字节)
adts_header_buffer[0] = 0xff; // syncword: 0xfff
adts_header_buffer[1] = 0xf0; // syncword: 0xfff
adts_header_buffer[1] |= (0 << 3); // MPEG Version: 0 for MPEG-4
adts_header_buffer[1] |= (0 << 1); // Layer: 0
adts_header_buffer[1] |= 1; // protection absent: 1
adts_header_buffer[2] = (profile) << 6; // profile
adts_header_buffer[2] |= (sample_rate_index & 0x0f) << 2; // sample rate index
adts_header_buffer[2] |= (0 << 1); // private bit: 0
adts_header_buffer[2] |= (channels & 0x04) >> 2; // channel configuration
adts_header_buffer[3] = (channels & 0x03) << 6; // channel configuration
adts_header_buffer[3] |= (0 << 5); // original: 0
adts_header_buffer[3] |= (0 << 4); // home: 0
adts_header_buffer[3] |= (0 << 3); // copyright id bit: 0
adts_header_buffer[3] |= (0 << 2); // copyright id start: 0
adts_header_buffer[3] |= ((adts_len & 0x1800) >> 11); // frame length: high 2 bits
adts_header_buffer[4] = (uint8_t)((adts_len & 0x7f8) >> 3); // frame length: middle 8 bits
adts_header_buffer[5] = (uint8_t)((adts_len & 0x7) << 5); // frame length: low 3 bits
adts_header_buffer[5] |= 0x1f; // buffer fullness: 0x1f (empty)
adts_header_buffer[6] = 0xfc; // reserved bits
return;
}
RTP音频数据处理
主要处理逻辑
- 接收 RTP 数据包:
InputData
方法接收到一个 RTP 数据包并解析其头部,检查负载类型是否是我们关心的 AAC 格式 - 提取有效负载数据:从 RTP 包中提取出音频数据(即去除 RTP 头和扩展头后的部分)
- 回调音频数据:通过回调接口将提取到的音频数据传递给外部处理。回调函数接收时间戳和有效负载数据
- 处理扩展头:如果 RTP 包包含扩展头,跳过扩展头并更新有效负载的指针和长度
代码实现
// 接收RTP包数据以及数据大小
void AACDemuxer::InputData(const uint8_t* data, size_t size)
{
// 1. 解析RTP头部
struct RtpHeader* header = (struct RtpHeader*)data;
int payload_type = header->payloadType;
// 如果设置的payload_type和RTP包的payload_type不一致,则直接返回
if(payload_type != payload_){
return;
}
// 2. 提取有效的负载数据
const uint8_t* payload = data + sizeof(struct RtpHeader); //去除负载头部的数据
size_t payload_len = size - sizeof(struct RtpHeader); //负载的长度
// 3. 解析RTP扩展头
if(header->extension){
// 计算拓展头的长度
const uint8_t* extension_data = payload;
size_t extension_length = 4 * (extension_data[2] << 8 | extension_data[3]);
size_t payload_offset = 4 + extension_length;
// 更新负载指针,同时更新负载长度
payload = extension_data + payload_offset;
payload_len -= payload_offset;
}
// 4. 回调函数
if(call_back_){
call_back_->OnAudioData(header->timestamp, payload, payload_len);
}
return;
}
class AACDemuxer : public RTPDemuxer{
public:
void InputData(const uint8_t* data, size_t size) override;
};
音频解复用器
- 接收 RTP 包:
InputData
方法接收一个 RTP 包,并解析包头,获取负载类型(payloadType
)和有效负载(音频数据)。 - 检查负载类型:只有当 RTP 包的负载类型匹配时,才继续处理。
- 处理 RTP 扩展头:如果 RTP 包包含扩展头,跳过扩展头并更新有效负载数据。
- 传递音频数据:通过回调将解析出的音频数据传递给外部应用
头文件
class PCMaDemuxer : public RTPDemuxer {
public:
void InputData(const uint8_t* data, size_t size) override;
};
源文件实现
void PCMADemuxer::InputData(const uint8_t* data, size_t size){
struct RtpHeader *header = (struct RtpHeader *)data;
int payload_type = header->payloadType;
if(payload_type != payload_){
return;
}
const uint8_t* payload = data + sizeof(struct RtpHeader);
size_t payload_len = size - sizeof(struct RtpHeader);
// rtp扩展头
if (header->extension){
const uint8_t *extension_data = payload;
size_t extension_length = 4 * (extension_data[2] << 8 | extension_data[3]);
size_t payload_offset = 4 + extension_length;
payload = payload + payload_offset;
payload_len = payload_len - payload_offset;
}
if(call_back_){
call_back_->OnAudioData(ntohl(header->timestamp), payload, payload_len);
}
return;
}