😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍 🍭
😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你——泰戈尔🍭
⏰发布时间⏰: 2025-07-09
本文未经允许,不得转发!!!

RTSP系列文章回顾:
【音视频 | RTSP】RTSP协议详解 及 抓包例子解析(详细而不赘述)
【音视频 | RTSP】SDP(会话描述协议)详解 及 抓包例子分析
【音视频 | RTP】RTP协议详解(H.264的RTP封包格式、AAC的RTP封包格式)
【RTSP从零实践】01、根据RTSP协议实现一个RTSP服务
【RTSP从零实践】02、使用RTP协议封装并传输H264
【RTSP从零实践】03、实现最简单的传输H264的RTSP服务器
【RTSP从零实践】04、使用RTP协议封装并传输AAC
【RTSP从零实践】05、实现最简单的传输AAC的RTSP服务器
【RTSP从零实践】06、实现最简单的同时传输H264、AAC的RTSP服务器
【RTSP从零实践】07、多播传输H264格式的RTP包(附带源码)
【RTSP从零实践】08、多播传输H264码流的RTSP服务器——最简单的实现例子(附带源码)
【RTSP从零实践】09、多播传输AAC格式的RTP包(附带源码)
【RTSP从零实践】10、多播传输AAC码流的RTSP服务器——最简单的实现例子(附带源码)
【RTSP从零实践】11、多播同时传输H264、AAC码流的RTSP服务器——最简单的实现例子(附带源码)
【RTSP从零实践】12、TCP传输H264格式RTP包(RTP_over_TCP)的RTSP服务器(附带源码)
【RTSP从零实践】13、TCP传输AAC格式RTP包(RTP_over_TCP)的RTSP服务器(附带源码)
【RTSP从零实践】14、TCP传输H264格式、AAC格式RTP包(RTP_over_TCP)的RTSP服务器(附带源码)
![]()
🎄一、概述
上篇文章介绍了使用多播来传输RTP包,这篇文章介绍使用一个多播的方式来传输AAC码流的RTSP服务器,所有的知识点应该都在前面学习过了,本文会再将必要的知识点介绍一下,最后给出例子代码,对前面内容掌握了的读者可以直接跳转到代码去看就行了。
读者阅读的时候可以将这篇文章的代码与之前的文章 【RTSP从零实践】5、实现最简单的传输AAC的RTSP服务器 进行对比,主要改了两点:
- 1、将UDP单播发送的方式改为UDP多播发送;
- 2、处理RTSP命令时,返回多播的相关信息。
下面章节先是介绍多播的一些简单概念,然后介绍多播的方式处理RTSP命令时的注意点。
![]()
🎄二、多播的概念
关于多播的概念可以参考这篇文章:多播的概念、多播地址、UDP实现多播的C语言例子。下面只简单介绍一下多播。
IP 多播(也称多址广播或组播)技术,是允许一台主机 向 多台主机 发送消息的一种通信方式。单播只向单个IP接口发送数据,广播是向子网内所有IP接口发送数据,多播则介于两者之间,向一组IP接口发送数据。
多播地址:用来标识多播组,IPv4使用D类地址的某一个来表示一个多播组地址。IPv4的D类地址(从224.0.0.0到239.255.255.255)是IPv4多播地址,见下图:

多播发送端:用于发送多播数据报的程序,下面是一个简单的多播发送端例子的代码。基本上就是在发送数据包时,将目的地址设置为 多播地址。
// multicastCli.c
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
int main()
{
// 1、创建UDP套接字socket
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd<0)
perror("socket error" );
// 2、准备多播组地址和端口
struct sockaddr_in servaddr;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons (10086);
if (inet_pton(AF_INET, "239.0.1.1", &servaddr.sin_addr) <= 0)
perror("inet_pton error");
// 4、使用 sendto 发送多播组数据报
if(sendto(sockfd, "Hello,I am udp client", strlen("Hello,I am udp client"), 0, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
perror("sendto error" );
// 5、处理应答
char recvline[256];
int n = 0;
struct sockaddr_in tmpAddr;
bzero(&tmpAddr, sizeof(tmpAddr));
socklen_t addrLen=sizeof(tmpAddr);
while ( (n = recvfrom (sockfd, recvline, sizeof(recvline), 0, (struct sockaddr*)&tmpAddr, &addrLen)) > 0)
{
recvline[n] = 0 ;/*null terminate */
printf("recvfrom ip=[%s], [%s]\n",inet_ntoa(tmpAddr.sin_addr), recvline);
bzero(&tmpAddr, sizeof(tmpAddr));
}
if (n < 0)
perror("read error" );
// 6、关闭
close(sockfd);
return 0;
}
多播接收端:接收端是使用UDP服务端代码修改,需要在交互数据之前,将套接字加入多播组239.0.1.1,让链路层接口接收该多播组的数据报,使用完需要离开多播组。下面是一个多播接收端代码:
// multicastSer.c
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
int main()
{
// 1、创建UDP套接字socket
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd<0)
perror("socket error" );
// 2、准备本地ip接口和多播组端口
struct sockaddr_in servaddr;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons (10086);
servaddr.sin_addr.s_addr = INADDR_ANY; // 指定ip地址为 INADDR_ANY,这样要是服务器主机有多个网络接口,服务器进程就可以在任一网络接口上接受客户端的连接
// 3、绑定多播组端口 bind
if (bind(sockfd,(struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
perror("bind error" );
// 4、加入多播组 239.0.1.1
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr("239.0.1.1"); // 多播组的IP地址
mreq.imr_interface.s_addr = htonl(INADDR_ANY); // 加入的客服端主机IP地址
if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) {
perror("setsockopt");
return -1;
}
// 5、使用 sendto、recvfrom 交互数据
printf("UdpSer sockfd=%d, start \n",sockfd);
char recvline[256];
while(1)
{
struct sockaddr_in cliaddr;
bzero(&cliaddr, sizeof(cliaddr));
socklen_t addrLen=sizeof(cliaddr);
int n = recvfrom(sockfd, recvline, sizeof(recvline), 0, (struct sockaddr*)&cliaddr, &addrLen);
if(n>0)
{
recvline[n] = 0 ;/*null terminate */
printf("recv sockfd=%d %d byte, [%s] addrLen=%d, cliIp=%s, cliPort=%d\n",
sockfd, n, recvline, addrLen, inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port);
sendto(sockfd, "Hello,I am udp server", strlen("Hello,I am udp server"), 0, (struct sockaddr*)&cliaddr, addrLen);
}
}
// 6、离开多播组
setsockopt(sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP,&mreq, sizeof(mreq));
// 7、关闭
close(sockfd);
return 0;
}
![]()
🎄三、实现步骤、实现细节
多播传输AAC格式的RTSP服务器的实现步骤如下:
1、实现 AAC文件读取器。
2、实现 AAC 的 RTP 数据包封装。
3、实现多播发送RTP包。
4、处理RTSP命令并发送码流。
前两点在文章 使用RTP协议封装并传输AAC 介绍得很详细,需要看的童鞋,留言一下,我给你复制过来。😑
实现多播发送RTP包,我们只需要在UDP单播发送的代码里将UDP的目的地址改为我们要使用的多播组地址即可,我们这里要使用的是"239.0.0.1"。
下面内容是多播方式处理RTSP命令的过程:
-
OPTION
处理OPTION消息时,直接返回本服务器支持的方法即可,没什么特别的:

-
DESCRIBE

-
SETUP

-
PLAY

-
TEARDOWN

![]()
🎄四、多播传输AAC格式的RTP包的实现源码
1、aacReader.h
/**
* @file aacReader.h
* @author : https://blog.csdn.net/wkd_007
* @brief
* @version 0.1
* @date 2025-06-30
*
* @copyright Copyright (c) 2025
*
*/
#ifndef __AAC_READER_H__
#define __AAC_READER_H__
#include <stdio.h>
#define ADTS_HEADER_LEN (7)
typedef struct
{
int frame_len; //!
unsigned char *pFrameBuf; //!
} AACFrame_t;
typedef struct AACReaderInfo_s
{
FILE *pFileFd;
}AACReaderInfo_t;
int AAC_FileOpen(char *fileName, AACReaderInfo_t *pAACInfo);
int AAC_FileClose(AACReaderInfo_t *pAACInfo);
int AAC_GetADTSFrame(AACFrame_t *pAACFrame, const AACReaderInfo_t *pAACInfo);
int AAC_IsEndOfFile(const AACReaderInfo_t *pAACInfo);
void AAC_SeekFile(const AACReaderInfo_t *pAACInfo);
#endif // __AAC_READER_H__
2、aacReader.c
/**
* @file aacReader.c
* @author : https://blog.csdn.net/wkd_007
* @brief
* @version 0.1
* @date 2025-06-30
*
* @copyright Copyright (c) 2025
*
*/
#include <stdlib.h>
#include <string.h>
#include "aacReader.h"
#define MAX_FRAME_LEN (1024*1024) // 一帧数据最大字节数
#define MAX_SYNCCODE_LEN (3) // 同步码字节个数 2025-05-21 17:45:06
static int findSyncCode_0xFFF(unsigned char *Buf, int *size)
{
if((Buf[0] == 0xff) && ((Buf[1] & 0xf0) == 0xf0) )//0xFF F,前12bit都为1 2025-05-21 17:46:57
{
*size |= ((Buf[3] & 0x03) <<11); //high 2 bit
*size |= Buf[4]<<3; //middle 8 bit
*size |= ((Buf[5] & 0xe0)>>5); //low 3bit
return 1;
}
return 0;
}
int AAC_FileOpen(char *fileName, AACReaderInfo_t *pAACInfo)
{
pAACInfo->pFileFd = fopen(fileName, "rb+");
if (pAACInfo->pFileFd==NULL){
printf("[%s %d]Open file error\n",__FILE__,__LINE__);
return -1;
}
return 0;
}
int AAC_FileClose(AACReaderInfo_t *pAACInfo)
{
if (pAACInfo->pFileFd != NULL) {
fclose(pAACInfo->pFileFd);
pAACInfo->pFileFd = NULL;
}
return 0;
}
int AAC_IsEndOfFile(const AACReaderInfo_t *pAACInfo)
{
return feof(pAACInfo->pFileFd);
}
void AAC_SeekFile(const AACReaderInfo_t *pAACInfo)
{
fseek(pAACInfo->pFileFd,0,SEEK_SET);
}
/**
* @brief
*
* @param pAACFrame :输出参数,使用后 pAACInfo->pFrameBuf 需要free
* @param pAACInfo
* @return int
*/
int AAC_GetADTSFrame(AACFrame_t *pAACFrame, const AACReaderInfo_t *pAACInfo)
{
int rewind = 0;
if (pAACInfo->pFileFd==NULL){
printf("[%s %d]pFileFd error\n",__FILE__,__LINE__);
return -1;
}
// 1.先读取ADTS帧头(7个字节)
unsigned char* pFrame = (unsigned char*)malloc(MAX_FRAME_LEN);
int readLen = fread(pFrame, 1, ADTS_HEADER_LEN, pAACInfo->pFileFd);
if(readLen <= 0)
{
printf("[%s %d]fread error readLen=%d\n",__FILE__,__LINE__,readLen);
free(pFrame);
return -1;
}
// 2.查找当前帧同步码,获取帧长度
int i=0;
int size = 0;
for(; i<readLen-MAX_SYNCCODE_LEN; i++)
{
if(!findSyncCode_0xFFF(&pFrame[i], &size))
{
continue;
}
else
{
break;
}
}
if(i!=0) // 不是帧开头,偏移到帧开头重新读
{
printf("[%s %d]synccode error, i=%d\n",__FILE__,__LINE__,i);
free(pFrame);
rewind = (-(readLen-i));
fseek (pAACInfo->pFileFd, rewind, SEEK_CUR);
return -1;
}
// 3.读取ADTS帧数据 2025-05-22 21:44:39
readLen = fread(pFrame+ADTS_HEADER_LEN, 1, size-ADTS_HEADER_LEN, pAACInfo->pFileFd);
if(readLen <= 0)
{
printf("[%s %d]fread error\n",__FILE__,__LINE__);
free(pFrame);
return -1;
}
// 4.填数据
pAACFrame->frame_len = size;
pAACFrame->pFrameBuf = pFrame;
return pAACFrame->frame_len;
}
3、rtp.h
#ifndef _RTP_H_
#define _RTP_H_
#include <stdint.h>
#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
/*
*
* 0 1 2 3
* 7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |V=2|P|X| CC |M| PT | sequence number |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | timestamp |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | synchronization source (SSRC) identifier |
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
* | contributing source (CSRC) identifiers |
* : .... :
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
*/
struct RtpHeader
{
/* byte 0 */
uint8_t csrcLen:4;
uint8_t extension:1;
uint8_t padding:1;
uint8_t version:2;
/* byte 1 */
uint8_t payloadType:7;
uint8_t marker:1;
/* bytes 2,3 */
uint16_t seq;
/* bytes 4-7 */
uint32_t timestamp;
/* bytes 8-11 */
uint32_t ssrc;
};
struct RtpPacket
{
struct RtpHeader rtpHeader;
uint8_t payload[0];
};
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);
int rtpSendPacket(int socket, char* ip, int16_t port, struct RtpPacket* rtpPacket, uint32_t dataSize);
#endif //_RTP_H_
4、rtp.c
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "rtp.h"
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 rtpSendPacket(int socket, 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(socket, (void *)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;
}
5、multicast_rtsp_aac_main.c
/**
* @file multicast_rtsp_aac_main.c
* @author : https://blog.csdn.net/wkd_007
* @brief
* @version 0.1
* @date 2025-07-09
*
* @copyright Copyright (c) 2025
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "rtp.h"
#include "aacReader.h"
#define AAC_FILE_NAME "test.aac"
#define RTSP_PORT 8554
#define MAX_CLIENTS 5
#define SESSION_ID 10086001
#define SESSION_TIMEOUT 60
#define MULTICAST_IP "239.0.0.1"
#define MULTICAST_PORT 55666
typedef struct
{
int rtpSendFd;
int rtpPort;
int bPlayFlag; // 播放标志
char *cliIp;
} RTP_Send_t;
typedef enum
{
RTP_NULL,
RTP_PLAY,
RTP_PLAYING,
RTP_STOP,
} RTP_PLAY_STATE;
static int createUdpSocket()
{
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
return -1;
int on = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on));
return fd;
}
static int rtpSendAACFrame(int socket, char *ip, int16_t port,
struct RtpPacket *rtpPacket, uint8_t *frame, uint32_t frameSize)
{
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 = rtpSendPacket(socket, ip, port, rtpPacket, frameSize + 4);
if (ret < 0)
{
printf("failed to send rtp packet\n");
return -1;
}
rtpPacket->rtpHeader.seq++;
return 0;
}
void *sendRtp(void *arg)
{
RTP_Send_t *pRtpSend = (RTP_Send_t *)arg;
int rtp_send_fd = pRtpSend->rtpSendFd;
int rtpPort = pRtpSend->rtpPort;
char *cli_ip = pRtpSend->cliIp;
struct RtpPacket *rtpPacket = (struct RtpPacket *)malloc(sizeof(struct RtpPacket) + 1500);
rtpHeaderInit(rtpPacket, 0, 0, 0, RTP_VESION, RTP_PAYLOAD_TYPE_AAC, 1, 0, 0, 0x32411);
// aac
AACReaderInfo_t aacInfo;
if (AAC_FileOpen(AAC_FILE_NAME, &aacInfo) < 0)
{
printf("failed to open %s\n", AAC_FILE_NAME);
return NULL;
}
while (pRtpSend->bPlayFlag)
{
if (!AAC_IsEndOfFile(&aacInfo))
{
AACFrame_t aacFrame;
memset(&aacFrame, 0, sizeof(aacFrame));
AAC_GetADTSFrame(&aacFrame, &aacInfo);
if (aacFrame.pFrameBuf != NULL)
{
// printf("rtpSendAACFrame\n");
rtpSendAACFrame(rtp_send_fd, cli_ip, rtpPort, rtpPacket,
aacFrame.pFrameBuf + ADTS_HEADER_LEN, aacFrame.frame_len - ADTS_HEADER_LEN);
free(aacFrame.pFrameBuf);
/*
* 如果采样频率是48000
* 一般AAC每个1024个采样为一帧
* 所以一秒就有 48000 / 1024 = 47帧
* 时间增量就是 48000 / 47 = 1021
* 一帧的时间为 1000ms / 47 = 21ms
*/
rtpPacket->rtpHeader.timestamp += 1021;
usleep(21 * 1000);
}
else
{
printf("warning SeekFile\n");
AAC_SeekFile(&aacInfo);
}
}
}
free(rtpPacket);
AAC_FileClose(&aacInfo);
return NULL;
}
// 解析RTSP请求
void rtsp_request_parse(char *buffer, char *method, char *url, int *cseq, int *pRtpPort)
{
char *line = strtok(buffer, "\r\n");
sscanf(line, "%s %s RTSP/1.0", method, url);
while ((line = strtok(NULL, "\r\n")) != NULL)
{
if (strncmp(line, "CSeq:", 5) == 0)
{
sscanf(line, "CSeq: %d", cseq);
}
char *pCliPort = strstr(line, "client_port=");
if (pCliPort != NULL)
{
int rtcpPort = 0;
sscanf(pCliPort, "client_port=%d-%d", pRtpPort, &rtcpPort);
// printf("rtpPort: %d-%d\n",*pRtpPort, rtcpPort);
}
}
}
// 生成SDP描述
char g_sdp[512] = {0,};
const char *generate_sdp()
{
memset(g_sdp, 0, sizeof(g_sdp));
sprintf(g_sdp,
"v=0\r\n"
"o=- 0 0 IN IP4 0.0.0.0\r\n"
"s=Example Stream\r\n"
"t=0 0\r\n"
"a=type:broadcast\r\n"
"a=rtcp-unicast: reflection\r\n"
"m=audio %d RTP/AVP 97\r\n"
"c=IN IP4 %s/255\r\n"
"a=rtpmap:97 mpeg4-generic/48000/2\r\n"
"a=fmtp:97 SizeLength=13;\r\n"
"a=control:streamid=0\r\n",
MULTICAST_PORT, MULTICAST_IP);
return g_sdp;
}
void rtsp_handle_OPTION(char *response, int cseq)
{
sprintf(response,
"RTSP/1.0 200 OK\r\n"
"CSeq: %d\r\n"
"Public: OPTIONS, DESCRIBE, SETUP, PLAY, TEARDOWN\r\n\r\n",
cseq);
}
static void rtsp_handle_DESCRIBE(char *response, int cseq)
{
sprintf(response,
"RTSP/1.0 200 OK\r\n"
"CSeq: %d\r\n"
"Content-Type: application/sdp\r\n"
"Content-Length: %zu\r\n\r\n%s",
cseq, strlen(generate_sdp()), generate_sdp());
}
static void rtsp_handle_SETUP(char *response, int cseq, char *url)
{
char localIp[32];
sscanf(url, "rtsp://%[^:]:", localIp);
sprintf(response,
"RTSP/1.0 200 OK\r\n"
"CSeq: %d\r\n"
"Session: %u; timeout=%d\r\n"
"Transport: RTP/AVP;multicast;destination=%s;source=%s;port=%d-%d;ttl=255\r\n\r\n",
cseq, SESSION_ID, SESSION_TIMEOUT, MULTICAST_IP, localIp, MULTICAST_PORT, MULTICAST_PORT + 1);
}
static void rtsp_handle_PLAY(char *response, int cseq)
{
sprintf(response,
"RTSP/1.0 200 OK\r\n"
"CSeq: %d\r\n"
"Session: %u; timeout=%d\r\n"
"Range: npt=0.000-\r\n\r\n",
cseq, SESSION_ID, SESSION_TIMEOUT);
}
static void rtsp_handle_TEARDOWN(char *response, int cseq)
{
sprintf(response,
"RTSP/1.0 200 OK\r\n"
"CSeq: %d\r\n"
"Session: %d; timeout=%d\r\n\r\n",
cseq, SESSION_ID, SESSION_TIMEOUT);
}
// 处理客户端连接
int handle_client(int cli_fd, int rtp_send_fd, char *cli_ip)
{
int client_sock = cli_fd;
char buffer[1024] = {0};
int cseq = 0;
int rtpPort = 0;
unsigned char bSendFlag = RTP_NULL;
RTP_Send_t rtpSend;
pthread_t thread_id;
while (1)
{
memset(buffer, 0, sizeof(buffer));
int len = read(client_sock, buffer, sizeof(buffer) - 1);
if (len <= 0)
break;
printf("C->S [\n%s]\n\n", buffer);
char method[16] = {0};
char url[128] = {0};
rtsp_request_parse(buffer, method, url, &cseq, &rtpPort);
char response[1024] = {0}; // 构造响应
if (strcmp(method, "OPTIONS") == 0)
{
rtsp_handle_OPTION(response, cseq);
}
else if (strcmp(method, "DESCRIBE") == 0)
{
rtsp_handle_DESCRIBE(response, cseq);
}
else if (strcmp(method, "SETUP") == 0)
{
rtsp_handle_SETUP(response, cseq, url);
}
else if (strcmp(method, "PLAY") == 0)
{
rtsp_handle_PLAY(response, cseq);
bSendFlag = RTP_PLAY;
}
else if (strcmp(method, "TEARDOWN") == 0)
{
rtsp_handle_TEARDOWN(response, cseq);
bSendFlag = RTP_STOP;
}
else
{
snprintf(response, sizeof(response),
"RTSP/1.0 501 Not Implemented\r\nCSeq: %d\r\n\r\n", cseq);
}
write(client_sock, response, strlen(response));
printf("S->C [\n%s]\n\n", response);
if (bSendFlag == RTP_PLAY) // PLAY
{
rtpSend.rtpSendFd = rtp_send_fd;
rtpSend.rtpPort = MULTICAST_PORT;
rtpSend.cliIp = MULTICAST_IP;
rtpSend.bPlayFlag = 1;
// 这里不使用线程的话,会一直无法处理 client_sock 发过来的 OPTION 消息,导致播放出问题
if (pthread_create(&thread_id, NULL, (void *)sendRtp, (void *)&rtpSend) < 0)
{
perror("pthread_create");
}
bSendFlag = RTP_PLAYING;
}
if (bSendFlag == RTP_STOP) // TEARDOWN
{
rtpSend.bPlayFlag = 0;
pthread_join(thread_id); // 等待线程结束
bSendFlag = RTP_NULL;
break;
}
}
printf("close ip=[%s] fd=[%d]\n", cli_ip, client_sock);
close(client_sock);
return 0;
}
int main(int argc, char *argv[])
{
int server_fd, client_fd;
struct sockaddr_in address;
int opt = 1;
socklen_t addrlen = sizeof(address);
// 创建套接字
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
perror("socket failed");
return -1;
}
// 设置套接字选项
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)))
{
perror("setsockopt");
return -1;
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(RTSP_PORT);
// 绑定端口
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0)
{
perror("bind failed");
return -1;
}
// 开始监听
if (listen(server_fd, MAX_CLIENTS) < 0)
{
perror("listen");
return -1;
}
// 用于发送 rtp 包的udp套接字,不需要绑定端口
int rtp_send_fd = createUdpSocket();
if (rtp_send_fd < 0)
{
printf("failed to create socket\n");
return -1;
}
printf("RTSP Server listening on port %d\n", RTSP_PORT);
// 主循环接受连接,目前处理一个客户端
while (1)
{
char cli_ip[40] = {0};
if ((client_fd = accept(server_fd, (struct sockaddr *)&address, &addrlen)) < 0)
{
perror("accept");
return -1;
}
strncpy(cli_ip, inet_ntoa(address.sin_addr), sizeof(cli_ip));
printf("handle cliend [%s]\n", cli_ip);
handle_client(client_fd, rtp_send_fd, cli_ip);
}
return 0;
}
将上面代码保存在同一个目录后,并且在同目录里放一个.aac文件,然后运行 gcc *.c -lpthread 编译,再执行./a.out运行程序,下面是我运行的过程:

![]()
🎄五、总结
本文介绍了多播的一些概念,以及多播传输AAC码流的RTSP服务器实现的步骤和细节,最后提供了实现的源代码,帮助读者学习理解。

如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁
1596

被折叠的 条评论
为什么被折叠?



