文章目录
一、AAC
AAC介绍
AAC(Advanced Audio Coding),中文名:高级音频编码。出现于1997年,基于MPEG-2的音频编码技术。由Fraunhofer IIS、杜比实验室、AT&T、索尼等公司共同开发,目的是取代MP3格式。
2000年,MPEG-4标准出现后,AAC重新集成了其特性,加入了SBR技术和PS技术,为了区别于传统的MPEG-2 AAC又称为MPEG-4 AAC。
AAC音视频格式
AAC音视频格式有两种:
ADIF:音频数据交换格式,只有一个统一的头,必须得到所有数据后解码,适用于本地文件。
ADTS:音视数据传输流,每一帧都有头信息,任意帧解码,适用于传输流。
通常情况下,1024个采样点作为音频的一帧
二、ADTS
1.AAC-ADTS结构
adts的结构是每一帧都带adts header,头部后面跟着aac的原始流(aac es)
2.ADTS的头结构
adts_fixed_header:每一帧的内容是不变的。
adts_variable_header:每一帧的内容是存在变化的。
crc:16bits,protection_absent字段为0时存在。
ADTS头信息的长度是7个字节或9字节(有CRC的情况)。
adts的头部一共有15个字段:
字段 | 长度 | 内容 |
---|---|---|
adts_fixed_header | ||
syncword | 12 | 所有位必须为1,即0xFFF,代表着一个ADTS帧的开始 |
id | 1 | 设置MPEG标识符,0代表MPEG-4, 1代表MPEG-2 |
layer | 2 | 总是00 |
protection_absent | 1 | 误码效验,1代表没有CRC,0代表有CRC |
profile | 2 | AAC级别(MPEG-2 AAC中定义了3种profile,MPEG-4 AAC中定义了6种profile) |
sampling_frequency_index | 4 | 采样率,常见:44100HZ(0x4) |
private_bit | 1 | 编码时设置为0,解码时忽略 |
cannel_configuration | 3 | 声道数量 |
orininal_copy | 1 | 编码时设置为0,解码时忽略 |
home | 1 | 编码时设置为0,解码时忽略 |
adts_variable_heade | ||
copyrigth_identification_bit | 1 | 编码时设置为0,解码时忽略 |
copyrigth_identification_stat | 1 | 编码时设置为0,解码时忽略 |
aac_frame_length | 13 | 一个ADTS帧的长度包括ADTS头和AAC原始流 |
adts_bufferfullness | 11 | 缓冲区充满度,0x7FF说明是码率可变的码流,不需要此字段。CBR可能需要此字段,不同编码器使用情况不同。 |
number_of_raw_data_blocks_in_frame | 2 | 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧 |
结构体如下:
struct AdtsHeader {
unsigned int syncword; //12 bit 同步字 '1111 1111 1111',一个ADTS帧的开始
uint8_t id; //1 bit 0代表MPEG-4, 1代表MPEG-2。
uint8_t layer; //2 bit 必须为0
uint8_t protectionAbsent; //1 bit 1代表没有CRC,0代表有CRC
uint8_t profile; //2 bit AAC级别(MPEG-2 AAC中定义了3种profile,MPEG-4 AAC中定义了6种profile)
uint8_t samplingFreqIndex; //4 bit 采样率
uint8_t privateBit; //1bit 编码时设置为0,解码时忽略
uint8_t channelCfg; //3 bit 声道数量
uint8_t originalCopy; //1bit 编码时设置为0,解码时忽略
uint8_t home; //1 bit 编码时设置为0,解码时忽略
uint8_t copyrightIdentificationBit; //1 bit 编码时设置为0,解码时忽略
uint8_t copyrightIdentificationStart; //1 bit 编码时设置为0,解码时忽略
unsigned int aacFrameLength; //13 bit 一个ADTS帧的长度包括ADTS头和AAC原始流
unsigned int adtsBufferFullness; //11 bit 缓冲区充满度,0x7FF说明是码率可变的码流,不需要此字段。CBR可能需要此字段,不同编码器使用情况不同。这个在使用音频编码的时候需要注意。
uint8_t numberOfRawDataBlockInFrame; //2 bit
};
2.profile
AAC共有9种规格,以适应不同的场合的需要:
MPEG -2 AAC Main 主规格
MPEG -2 AAC LC (Low Complexity)
MPEG -2 AAC SSR 可变采样率规格( Scaleable Sample Rate )
MPEG -4 AAC LC 低复杂度规格( Low Complexity )–﹣现在的手机比较常见的mP4文件中的音频部份就包括了该规格音频文件
MPEG -4 AAC Main 主规格注:包含了除增益控制之外的全部功能,其音质最好
MPEG -4 AAC SSR 可变采样率规格( Scaleable Sample Rate )
MPEG -4 AAC LTP 长时期预测规格( Long Term Predicition )
MPEG -4 AAC LD 低延迟规格( Low Delay )
MPEG -4AAC HE高效率规格( HighEfficiency )–﹣这种规格适合用于低码率编码,有 Nero ACC 编码器支持
3.AAC ES
一个frame的原始数据包含1024个样本时间段的音频数据。
AAC的RTP打包
关于RTSP,PTP等内容在我另一篇文章中有说明:
RTSP和RTP说明
AAC的RTP打包
AAC的RTP打包方式是将ADTS帧去除ADTS头部,取出AAC数据,每帧数据封装成一个RTP包。但并不是将AAC数据直接拷贝到RTP的载荷中。AAC封装成RTP包,在RTP载荷中的前四个字节是有特殊含义的,然后再是AAC数据。
其中RTP载荷
第一个字节为0x00
第二个字节为0x10
第三个字节和第四个字节保存AAC Data的大小(最多只能保存13bit,第三个字节保存数据大小的高八位,第四个字节的高5位保存数据大小的低5位)
rtpPacket->payload[0] = 0x00;
rtpPacket->payload[1] = 0x10;
rtpPacket->payload[2] = (frameSize & 0x1FE0) >> 5; //高8位
rtpPacket->payload[3] = (frameSize & 0x1F) << 3; //低5位
AAC的时间戳计算
例如:
采样频率是44100
一般AAC每个1024个采样为一帧
所以一秒就有 44100 / 1024 = 43帧
时间增量就是 44100 / 43 = 1025
一帧的时间为 1 / 43 = 23ms
三、传输音频aac的RTSP服务器
准备aac音频文件
ffmpeg -i test.mp4 -vn -acodec aac test.aac
-i 表示输入文件
-vm disable video 丢掉视频
-acodec 设置音频编码格式
源码
#include<iostream>
#include<stdlib.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <windows.h>
//#include "rtp.h"
using namespace std;
#pragma comment(lib, "ws2_32.lib")
#pragma warning( disable : 4996 )
#define H264_FILE_NAME "D:/rtspceshiship/baozha.h264"
#define AAC_FILE_NAME "D:/rtspceshiship/baozha.aac"
#define SERVER_PORT 8888
#define SERVER_RTP_PORT 55532
#define SERVER_RTCP_PORT 55533
#define BUF_MAX_SIZE (1024*1024)
#define RTP_VESION 2
#define RTP_PAYLOAD_TYPE_H264 96
#define RTP_PAYLOAD_TYPE_AAC 97
#define RTP_HEADER_SIZE 12
#define RTP_MAX_PKT_SIZE 1400
struct RtpHeader
{
uint8_t csrcLen : 4;//CSRC计数器,占4位,指示CSRC 标识符的个数。
uint8_t extension : 1;//占1位,如果X=1,则在RTP报头后跟有一个扩展报头。
uint8_t padding : 1;//填充标志,占1位,如果P=1,则在该报文的尾部填充一个或多个额外的八位组,它们不是有效载荷的一部分。
uint8_t version : 2;//RTP协议的版本号,占2位,当前协议版本号为2。
uint8_t payloadType : 7;//有效载荷类型,占7位,用于说明RTP报文中有效载荷的类型,如GSM音频、JPEM图像等。
uint8_t marker : 1;//标记,占1位,不同的有效载荷有不同的含义,对于视频,标记一帧的结束;对于音频,标记会话的开始。
uint16_t seq;//占16位,用于标识发送者所发送的RTP报文的序列号,每发送一个报文,序列号增1。接收者通过序列号来检测报文丢失情况,重新排序报文,恢复数据。
uint32_t timestamp;//占32位,时戳反映了该RTP报文的第一个八位组的采样时刻。接收者使用时戳来计算延迟和延迟抖动,并进行同步控制。
uint32_t ssrc;//占32位,用于标识同步信源。该标识符是随机选择的,参加同一视频会议的两个同步信源不能有相同的SSRC。
};
struct RtpPacket
{
struct RtpHeader rtpHeader;
uint8_t payload[0];
};
struct AdtsHeader { //头部信息的长度是七个或九个字节(有CRC的情况)
//adts_fixed_header
unsigned int syncword; //12 bit 同步字 '1111 1111 1111',一个ADTS帧的开始
uint8_t id; //1 bit 0代表MPEG-4, 1代表MPEG-2。
uint8_t layer; //2 bit 必须为0
uint8_t protectionAbsent; //1 bit 1代表没有CRC,0代表有CRC
uint8_t profile; //2 bit AAC级别(MPEG-2 AAC中定义了3种profile,MPEG-4 AAC中定义了6种profile)
uint8_t samplingFreqIndex; //4 bit 采样率
uint8_t privateBit; //1bit 编码时设置为0,解码时忽略
uint8_t channelCfg; //3 bit 声道数量
uint8_t originalCopy; //1bit 编码时设置为0,解码时忽略
uint8_t home; //1 bit 编码时设置为0,解码时忽略
//adts_variable_header
uint8_t copyrightIdentificationBit; //1 bit 编码时设置为0,解码时忽略
uint8_t copyrightIdentificationStart; //1 bit 编码时设置为0,解码时忽略
unsigned int aacFrameLength; //13 bit 一个ADTS帧的长度包括ADTS头和AAC原始流
unsigned int adtsBufferFullness; //11 bit 缓冲区充满度,0x7FF说明是码率可变的码流,不需要此字段。CBR可能需要此字段,不同编码器使用情况不同。这个在使用音频编码的时候需要注意。
uint8_t numberOfRawDataBlockInFrame; //2 bit
};
int parseAdtsHeader(uint8_t* in, struct AdtsHeader* res)//解析adtsheader in码流
{
int frame_number = 0;
memset(res, 0, sizeof(*res));
if ((in[0] == 0xFF) && ((in[1] & 0xF0) == 0xF0))//ADTS帧的开始 必须以0xFFF开头
{
res->id = ((uint8_t)in[1] & 0x08) >> 3;//第二个字节与0x08与运算之后,获得第13位bit对应的值
res->layer = ((uint8_t)in[1] & 0x06) >> 1;//第二个字节与0x06与运算之后,右移1位,获得第14,15位两个bit对应的值
res->protectionAbsent = (uint8_t)in[1] & 0x01;
res->profile = ((uint8_t)in[2] & 0xc0) >> 6;
res->samplingFreqIndex = ((uint8_t)in[2] & 0x3c) >> 2;
res->privateBit = ((uint8_t)in[2] & 0x02) >> 1;
res->channelCfg = ((((uint8_t)in[2] & 0x01) << 2) | (((unsigned int)in[3] & 0xc0) >> 6));
res->originalCopy = ((uint8_t)in[3] & 0x20) >> 5;
res->home = ((uint8_t)in[3] & 0x10) >> 4;
res->copyrightIdentificationBit = ((uint8_t)in[3] & 0x08) >> 3;
res->copyrightIdentificationStart = (uint8_t)in[3] & 0x04 >> 2;
res->aacFrameLength = (((((unsigned int)in[3]) & 0x03) << 11) |
(((unsigned int)in[4] & 0xFF) << 3) |
((unsigned int)in[5] & 0xE0) >> 5);
res->adtsBufferFullness = (((unsigned int)in[5] & 0x1f) << 6 |
((unsigned int)in[6] & 0xfc) >> 2);
res->numberOfRawDataBlockInFrame = ((uint8_t)in[6] & 0x03);
return 0;
}
else
{
printf("failed to parse adts header\n");
return -1;
}
}
void rtpHeaderInit(struct RtpPacket* rtpPacket, uint8_t csrcLen, uint8_t extension,
uint8_t padding, uint8_t version, uint8_t payloadType, uint8_t marker,
uint16_t seq, uint32_t timestamp, uint32_t ssrc)
{
rtpPacket->rtpHeader.csrcLen = csrcLen;
rtpPacket->rtpHeader.extension = extension;
rtpPacket->rtpHeader.padding = padding;
rtpPacket->rtpHeader.version = version;
rtpPacket->rtpHeader.payloadType = payloadType;
rtpPacket->rtpHeader.marker = marker;
rtpPacket->rtpHeader.seq = seq;
rtpPacket->rtpHeader.timestamp = timestamp;
rtpPacket->rtpHeader.ssrc = ssrc;
}
int rtpSendPacketOverUdp(int serverRtpSockfd, const char* ip, int16_t port, struct RtpPacket* rtpPacket, uint32_t dataSize)
{
struct sockaddr_in addr;
int ret;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip);
rtpPacket->rtpHeader.seq = htons(rtpPacket->rtpHeader.seq);//从主机字节顺序转变成网络字节顺序
rtpPacket->rtpHeader.timestamp = htonl(rtpPacket->rtpHeader.timestamp);
rtpPacket->rtpHeader.ssrc = htonl(rtpPacket->rtpHeader.ssrc);
ret = sendto(serverRtpSockfd, (char*)rtpPacket, dataSize + RTP_HEADER_SIZE, 0,
(struct sockaddr*)&addr, sizeof(addr));
rtpPacket->rtpHeader.seq = ntohs(rtpPacket->rtpHeader.seq);
rtpPacket->rtpHeader.timestamp = ntohl(rtpPacket->rtpHeader.timestamp);
rtpPacket->rtpHeader.ssrc = ntohl(rtpPacket->rtpHeader.ssrc);
return ret;
}
static int rtpSendAACFrame(int socket, const char* ip, int16_t port,
struct RtpPacket* rtpPacket, uint8_t* frame, uint32_t frameSize) {
//打包文档:https://blog.csdn.net/yangguoyu8023/article/details/106517251/
int ret;
rtpPacket->payload[0] = 0x00;
rtpPacket->payload[1] = 0x10;
rtpPacket->payload[2] = (frameSize & 0x1FE0) >> 5; //高8位
rtpPacket->payload[3] = (frameSize & 0x1F) << 3; //低5位
memcpy(rtpPacket->payload + 4, frame, frameSize);
ret = rtpSendPacketOverUdp(socket, ip, port, rtpPacket, frameSize + 4);
if (ret < 0)
{
printf("failed to send rtp packet\n");
return -1;
}
rtpPacket->rtpHeader.seq++;
/*
* 如果采样频率是44100
* 一般AAC每个1024个采样为一帧
* 所以一秒就有 44100 / 1024 = 43帧
* 时间增量就是 44100 / 43 = 1025
* 一帧的时间为 1 / 43 = 23ms
*/
rtpPacket->rtpHeader.timestamp += 1025;
return 0;
}
int handleCmd_OPTIONS(char* result, int cseq)
{
sprintf(result, "RTSP/1.0 200 OK\r\n"
"CSeq: %d\r\n"
"Public: OPTIONS, DESCRIBE, SETUP, PLAY\r\n"
"\r\n",
cseq);
return 0;
}
static int handleCmd_DESCRIBE(char* result, int cseq, char* url)
{
char sdp[500];
char localIp[100];
sscanf(url, "rtsp://%[^:]:", localIp);
sprintf(sdp, "v=0\r\n"
"o=- 9%ld 1 IN IP4 %s\r\n"
"t=0 0\r\n"
"a=control:*\r\n"
"m=audio 0 RTP/AVP 97\r\n"
"a=rtpmap:97 mpeg4-generic/44100/2\r\n"
"a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config=1210;\r\n"
//"a=fmtp:97 SizeLength=13;\r\n"
"a=control:track0\r\n",
time(NULL), localIp);
sprintf(result, "RTSP/1.0 200 OK\r\nCSeq: %d\r\n"
"Content-Base: %s\r\n"
"Content-type: application/sdp\r\n"
"Content-length: %d\r\n\r\n"
"%s",
cseq,
url,
strlen(sdp),
sdp);
return 0;
}
int handleCmd_SETUP(char* result, int cseq, int clientRtpPort)
{
sprintf(result, "RTSP/1.0 200 OK\r\n"
"CSeq: %d\r\n"
"Transport: RTP/AVP;unicast;client_port=%d-%d;server_port=%d-%d\r\n"
"Session: 66334873\r\n"
"\r\n",
cseq,
clientRtpPort,
clientRtpPort + 1,
SERVER_RTP_PORT,
SERVER_RTCP_PORT
);
return 0;
}
int handleCmd_PLAY(char* result, int cseq)
{
sprintf(result, "RTSP/1.0 200 OK\r\n"
"CSeq: %d\r\n"
"Range: npt=0.000-\r\n"
"Session: 66334873; timeout=10\r\n\r\n",
cseq);
return 0;
}
int Rtsp_Aac_Replay(int clientSockfd, const char* clientIP, int clientPort)
{
char method[100];
char url[100];
char version[100];
int CSeq;
int Rtp_Serv_Sockfd = -1, Rtcp_Serv_Sockfd = -1;
int Rtp_Clnt_Port, Rtcp_Clnt_Port;
char* rBuf = (char*)malloc(BUF_MAX_SIZE);
char* sBuf = (char*)malloc(BUF_MAX_SIZE);
while (1)
{
int recvLen;
recvLen = recv(clientSockfd, rBuf, BUF_MAX_SIZE, 0);
rBuf[recvLen] = '\0';
cout << "c--->s" << endl;
printf("rBuf = %s \n", rBuf);
const char* sep = "\n";
char* line = strtok(rBuf, sep);//将rbuf进行拆分
while (line)
{
if (strstr(line, "OPTIONS") || strstr(line, "DESCRIBE") || strstr(line, "SETUP") || strstr(line, "PLAY"))
{
if (sscanf(line, "%s %s %s\r\n", method, url, version) != 3) {
cout << "解析出错" << endl;
}
}
else if (strstr(line, "CSeq")) {
if (sscanf(line, "CSeq: %d\r\n", &CSeq) != 1) {
cout << "Cseq 解析出错" << endl;
}
}
else if (!strncmp(line, "Transport:", strlen("Transport:"))) {
// Transport: RTP/AVP/UDP;unicast;client_port=13358-13359
// Transport: RTP/AVP;unicast;client_port=13358-13359
if (sscanf(line, "Transport: RTP/AVP/UDP;unicast;client_port=%d-%d\r\n",
&Rtp_Clnt_Port, &Rtcp_Clnt_Port) != 2) {
cout << "Transport 解析出错" << endl;
}
}
line = strtok(NULL, sep);
}
if (!strcmp(method, "OPTIONS"))
{
if (handleCmd_OPTIONS(sBuf, CSeq))
{
printf("failed to handle options\n");
break;
}
}
else if (!strcmp(method, "DESCRIBE"))
{
if (handleCmd_DESCRIBE(sBuf, CSeq, url))
{
printf("failed to handle describe\n");
break;
}
}
else if (!strcmp(method, "SETUP"))
{
if (handleCmd_SETUP(sBuf, CSeq, Rtp_Clnt_Port))
{
printf("failed to handle setup\n");
break;
}
int on = 1;
if ((Rtp_Serv_Sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ||
(Rtcp_Serv_Sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
cout << "Rtp_Serv_Sockfd || Rtcp_Serv_Sockfd create error" << endl;
}
setsockopt(Rtp_Serv_Sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
setsockopt(Rtcp_Serv_Sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
struct sockaddr_in Rtp_Serv_Sockfd_Addr, Rtcp_Serv_Sockfd_Addr;
Rtp_Serv_Sockfd_Addr.sin_family = AF_INET;
Rtp_Serv_Sockfd_Addr.sin_port = htons(SERVER_PORT);
Rtp_Serv_Sockfd_Addr.sin_addr.s_addr = htonl(INADDR_ANY);
Rtcp_Serv_Sockfd_Addr.sin_family = AF_INET;
Rtcp_Serv_Sockfd_Addr.sin_port = htons(SERVER_PORT);
Rtcp_Serv_Sockfd_Addr.sin_addr.s_addr = htonl(INADDR_ANY);
if ((bind(Rtp_Serv_Sockfd, (const sockaddr*)&Rtp_Serv_Sockfd_Addr, sizeof(Rtp_Serv_Sockfd_Addr))) < 0 ||
(bind(Rtcp_Serv_Sockfd, (const sockaddr*)&Rtcp_Serv_Sockfd_Addr, sizeof(Rtcp_Serv_Sockfd_Addr))) < 0)
{
cout << "fail to bind Rtp_Serv_Sockfd || Rtcp_Serv_Sockfd" << endl;
}
}
else if (!strcmp(method, "PLAY"))
{
if (handleCmd_PLAY(sBuf, CSeq))
{
printf("failed to handle play\n");
break;
}
}
else
{
printf("未定义的method = %s \n", method);
break;
}
cout << "s--->c" << endl;
cout << sBuf << endl;
send(clientSockfd, sBuf, strlen(sBuf), 0);
if (!strcmp(method, "PLAY"))
{
printf("start play\n");
printf("client ip:%s\n", clientIP);
printf("client port:%d\n", Rtp_Clnt_Port);
struct AdtsHeader adtsHeader;
struct RtpPacket* rtpPacket;
uint8_t* frame;
int ret;
FILE* fp = fopen(AAC_FILE_NAME, "rb");
if (!fp) {
printf("读取 %s 失败\n", AAC_FILE_NAME);
break;
}
frame = (uint8_t*)malloc(5000);
rtpPacket = (struct RtpPacket*)malloc(5000);
rtpHeaderInit(rtpPacket, 0, 0, 0, RTP_VESION, RTP_PAYLOAD_TYPE_AAC, 1, 0, 0, 0x32411);
while (true)
{
ret = fread(frame, 1, 7, fp);
if (ret <= 0)
{
printf("fread err\n");
break;
}
printf("fread ret=%d \n", ret);
if (parseAdtsHeader(frame, &adtsHeader) < 0)
{
printf("parseAdtsHeader err\n");
break;
}
ret = fread(frame, 1, adtsHeader.aacFrameLength - 7, fp);//aacFrameLength记录了一个ADTS frame的大小
if (ret <= 0)
{
printf("fread err\n");
break;
}
rtpSendAACFrame(Rtp_Serv_Sockfd, clientIP, Rtp_Clnt_Port,
rtpPacket, frame, adtsHeader.aacFrameLength - 7);
Sleep(1);
//usleep(23223);//1000/43.06 * 1000
}
free(frame);
free(rtpPacket);
break;
}
memset(method, 0, sizeof(method) / sizeof(char));
memset(url, 0, sizeof(url) / sizeof(char));
CSeq = 0;
}
closesocket(clientSockfd);
if (Rtp_Serv_Sockfd) {
closesocket(Rtp_Serv_Sockfd);
}
if (Rtcp_Serv_Sockfd > 0) {
closesocket(Rtcp_Serv_Sockfd);
}
free(rBuf);
free(sBuf);
return 0;
}
int main()
{
WSADATA wsaData;//启动windows socket
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
printf("PC Server Socket Start Up Error \n");
return -1;
}
int Rtsp_Serv_Sock, Client_Sock;;
Rtsp_Serv_Sock = socket(AF_INET, SOCK_STREAM, 0);
if (Rtsp_Serv_Sock < 0)
{
cout << "Rtsp_Serv_Sock init error" << endl;
return -1;
}
int on = 1;
if ((setsockopt(Rtsp_Serv_Sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on))) == -1)//设置TCP允许重用本地地址
{
cout << "fail to setsockopt()" << endl;
return -1;
}
struct sockaddr_in Serv_Addr, Client_Addr;
Serv_Addr.sin_family = AF_INET;
Serv_Addr.sin_port = htons(SERVER_PORT);
Serv_Addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(Rtsp_Serv_Sock, (const sockaddr*)&Serv_Addr, sizeof(Serv_Addr)) == SOCKET_ERROR)
{
cout << "fail to bind Rtsp_Serv_Sock" << endl;
return -1;
}
if (listen(Rtsp_Serv_Sock, 10) < 0)
{
cout << "failed to listen Rtsp_Serv_Sock" << endl;
return -1;
}
printf("%s rtsp://127.0.0.1:%d\n", __FILE__, SERVER_PORT);
while (1)
{
char clientIp[40];
int clientPort;
int len = 0;
memset(&Client_Addr, 0, sizeof(Client_Addr));
len = sizeof(Client_Addr);
if ((Client_Sock = accept(Rtsp_Serv_Sock, (struct sockaddr*)&Client_Addr, &len)) < 0)
{
cout << "fail to accept Client_Sock" << endl;
}
strcpy(clientIp, inet_ntoa(Client_Addr.sin_addr));
clientPort = ntohs(Client_Addr.sin_port);
Rtsp_Aac_Replay(Client_Sock, clientIp, clientPort);
}
closesocket(Rtsp_Serv_Sock);
return 0;
}