您的位置:首页 > 科技 > 能源 > 杀手杰夫链接网站代码_如何自制一个网站_seo一般包括哪些内容_360营销

杀手杰夫链接网站代码_如何自制一个网站_seo一般包括哪些内容_360营销

2025/3/15 19:50:09 来源:https://blog.csdn.net/gma999/article/details/145943096  浏览:    关键词:杀手杰夫链接网站代码_如何自制一个网站_seo一般包括哪些内容_360营销
杀手杰夫链接网站代码_如何自制一个网站_seo一般包括哪些内容_360营销

概述

实现一个简单的RTSP服务器,主要用于从本地AAC文件读取音频数据,然后通过RTP协议实时传输AAC音频流。整体结构和H264视频流服务器结构相似

ADTS头部

结构体分析

该结构体主要用于描述ADTS头部,该头部信息位于每个AAC音频帧之前,其中包含了音频帧的同步信息、长度、采样率等重要参数

  • syncword: 同步字 (0xFFF),标志 ADTS 帧的开始。
  • aacFrameLength: ADTS 帧长度 (包括头部和 AAC 原始数据),这是确定每个 AAC 帧大小的关键信息。
  • samplingFreqIndex: 采样率索引,对应实际的采样率 (如 44100Hz, 48000Hz)。
  • channelCfg: 声道配置,表示音频声道数 (如单声道、立体声)
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 必须为0uint8_t protectionAbsent;  //1 bit 1代表没有CRC,0代表有CRCuint8_t profile;           //1 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可能需要此字段,不同编码器使用情况不同。这个在使用音频编码的时候需要注意。/* number_of_raw_data_blocks_in_frame* 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧* 所以说number_of_raw_data_blocks_in_frame == 0* 表示说ADTS帧中有一个AAC数据块并不是说没有。(一个AAC原始帧包含一段时间内1024个采样及相关数据)*/uint8_t numberOfRawDataBlockInFrame; //2 bit
};

解析函数分析

主要用于解析ADTS头部,从字节流中提取ADTS头部信息,然后填充到AdtsHeader结构体中

  • 同步字校验: 首先检查前两个字节是否为 ADTS 同步字 (0xFFF),这是判断是否为有效 ADTS 头部的首要条件
  • 位域提取: 使用位运算从 ADTS 头部字节中提取各个字段的值,例如 AAC 级别、采样率索引、声道配置、ADTS 帧长度等,并将解析出的值存储到 AdtsHeader 结构体中
  • 错误处理: 如果同步字校验失败,则认为 ADTS 头部解析失败,返回错误代码
static int parseAdtsHeader(uint8_t* in, struct AdtsHeader* res) {static int frame_number = 0;memset(res, 0, sizeof(*res));if ((in[0] == 0xFF) && ((in[1] & 0xF0) == 0xF0)){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;}
}

RTP AAC帧发送函数

负责将 AAC 音频帧封装成 RTP 包并通过 UDP 发送,其中AAC的RTP负载格式使用的是AU头部;整体逻辑是先设置通用的AU头部,然后将负载加入到RTP负载空间中,通过RTP发送,然后更新序列号和时间戳

代码分析

主要流程总结

  • 从AAC音频文件中读取数据,然后解析ADTS的头部
  • RTP数据包封装:将音频数据封装进一个RTP包中,然后通过网络进行发送
  • 控制帧率,确保音频数据按照正确的速度进行播放 
 //开始播放,发送RTP包if (!strcmp(method, "PLAY")) {struct AdtsHeader adtsHeader; // 存储ADTS头部信息struct RtpPacket* rtpPacket;  // RTP包uint8_t* frame; //存储文件中读取AAC音频数据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);// 初始化RTP包头信息(一般是版本负载类型等)rtpHeaderInit(rtpPacket, 0, 0, 0, RTP_VESION, RTP_PAYLOAD_TYPE_AAC, 1, 0, 0, 0x32411);while (true){// 读取前7个字节存储到frame中,也就是ADTS头部数据ret = fread(frame, 1, 7, fp);if (ret <= 0){printf("fread err\n");break;}printf("fread ret=%d \n",ret);// 解析ADTS头部信息if (parseAdtsHeader(frame, &adtsHeader) < 0){printf("parseAdtsHeader err\n");break;}// 读取完剩下的AAC音频数据ret = fread(frame, 1, adtsHeader.aacFrameLength - 7, fp);if (ret <= 0){printf("fread err\n");break;}rtpSendAACFrame(serverRtpSockfd, clientIP, clientRtpPort,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;}

RTSP处理AAC音频SDP

将handleCmd_DESCRIBE修改为生成音频流的SDP描述

  • m=audio 0 RTP/AVP 97: 媒体类型改为 audio,payload type 设置为 97 (动态 payload type)。
  • a=rtpmap:97 mpeg4-generic/44100/2: RTP map 属性设置为 mpeg4-generic 音频,采样率 44100Hz,2 声道 (立体声)。
  • a=fmtp:97 ... config=1210;: 关键的 FMTP 属性,包含 AAC 音频流的格式特定参数,特别是 config=1210,这是 AudioSpecificConfig (音频特定配置),以十六进制表示,解码器需要这个配置信息才能正确解码 AAC 音频。 0x1210 代表 44100Hz 采样率和 2 声道

代码分析

核心流程分为三步

  • 解析RTSP的URL,从URL中提取本地的IP地址
  • 生成SDP数据,构建描述流媒体信息的SDP字符串
  • 构建RTSP响应,将生成的SDP数据与RTSP响应拼接在一起返回给客户端
static int handleCmd_DESCRIBE(char* result, int cseq, char* url) {char sdp[500];char localIp[100];// 从RTSP流地址中获取IP地址sscanf(url, "rtsp://%[^:]:", localIp);// 构建SDP数据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;
}

构建SDP的内容分析

  • v=0: 版本号。
  • o=- 9%ld 1 IN IP4 %s: 发送者和会话标识。time(NULL) 提供当前时间戳,localIp 则是从 URL 中提取的本地 IP 地址。
  • t=0 0: 会话的开始和结束时间,这里设置为 0 0,表示该会话没有特定的开始或结束时间。
  • a=control:: 控制指令,指定 *,表示所有的流都可以被控制。
  • m=audio 0 RTP/AVP 97: 描述音频媒体流,使用 RTP 协议,流的媒体类型是音频,负载类型(Payload Type)为 97。
  • a=rtpmap:97 mpeg4-generic/44100/2: 负载类型 97 的详细信息,表示使用 MPEG4 编码,采样率为 44100 Hz,声道数为 2。
  • a=fmtp:97 ...: 为负载类型 97 提供的格式特定参数,指定编码方式、数据结构等详细信息。
  • a=control:track0: 控制指令,指定音频轨道为 track0

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com