【RTSP从零实践】5、实现最简单的传输AAC的RTSP服务器

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍怎么实现最简单的传输H264的RTSP服务器🍭
😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你——泰戈尔🍭
⏰发布时间⏰: 2025-06-30

本文未经允许,不得转发!!!


在这里插入图片描述

前面系列文章回顾:
【RTSP从零实践】1、根据RTSP协议实现一个RTSP服务
【RTSP从零实践】2、使用RTP协议封装并传输H264
【RTSP从零实践】3、实现最简单的传输H264的RTSP服务器
【RTSP从零实践】4、使用RTP协议封装并传输AAC

在这里插入图片描述

🎄一、概述

这篇文章主要就是介绍怎么通过RTSP协议通信,将AAC音频数据封装成RTP包并发送的,目的是写一个最简单的RTSP服务器,熟悉RTSP协议处理、AAC数据的RTP封包。

包含了下面知识点:
1、RTSP协议的内容;
2、实现TCP协议实现RTSP;
3、RTP协议;
4、怎么实现RTP协议;
5、AAC音频数据格式;
6、AAC的RTP封包格式。

这些知识点都可以从上面 【RTSP从零实践】系列文章学习到,这篇文章不再赘述,只介绍步骤和代码。


在这里插入图片描述

🎄二、实现步骤、实现细节

这个小写介绍怎么实现的步骤、细节,下个小节会提供源码,可以结合着源码看帮助理解消化。

✨2.1、使用RTSP服务的TCP套接字并监听

RTSP协议是基于TCP作为传输层协议去实现的。

// 创建套接字
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;
}

✨2.2、接收客户端请求并处理RTSP命令

RTSP通信不直接传输音视频码流,会先进行一些命令通信,所以第二个步骤就是处理这些命令:

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, rtpPort);
}
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);
}

✨2.3、读取AAC音频帧并发送

处理完RTSP命令后,就需要读取并发送RTP包了。

// 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);
		}
	}
   }

在这里插入图片描述

🎄三、传输AAC的RTSP服务器的实现源码

总共有5个源代码文件,具体如下:

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、rtsp_aac_main.c

/**
 * @file rtsp_aac_main.c
 * @author : https://blog.csdn.net/wkd_007
 * @brief 
 * @version 0.1
 * @date 2025-07-01
 * 
 * @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 RTP_PORT        55666
#define MAX_CLIENTS     5
#define SESSION_ID      10086001
#define SESSION_TIMEOUT 60

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描述
const char *generate_sdp()
{
    return "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"
           "m=audio 0 RTP/AVP 97\r\n"
           "a=rtpmap:97 mpeg4-generic/48000/2\r\n"
           "a=fmtp:97 SizeLength=13;\r\n"
           "a=control:streamid=0\r\n";
}

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, int rtpPort)
{
    sprintf(response,
            "RTSP/1.0 200 OK\r\n"
            "CSeq: %d\r\n"
            "Session: %u; timeout=%d\r\n"
            "Transport: RTP/AVP;unicast;client_port=%d-%d;server_port=%d-%d\r\n\r\n",
            cseq, SESSION_ID, SESSION_TIMEOUT, rtpPort, rtpPort + 1, RTP_PORT, RTP_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 [%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, rtpPort);
        }
        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 [%s]\n\n", response);

        if (bSendFlag == RTP_PLAY) // PLAY
        {
            rtpSend.rtpSendFd = rtp_send_fd;
            rtpSend.rtpPort = rtpPort;
            rtpSend.cliIp = cli_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;
    }
    address.sin_port = htons(RTP_PORT);
    if (bind(rtp_send_fd, (struct sockaddr *)&address, sizeof(address)) < 0)
    {
        perror("rtp_send_fd bind failed");
        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运行程序,下面是我运行的过程:
在这里插入图片描述


在这里插入图片描述

🎄四、总结

本文介绍了实现介绍怎么通过RTSP协议通信,将aac音频数据封装成RTP包并发送的,也提供了实现源码和运行结果,可以帮助读者快速了解怎样实现一个最简单的RTSP服务器。

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

参考:
https://blog.csdn.net/huabiaochen/article/details/104557971

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wkd_007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值