RTSP協議的一些分析(七)——傳輸AAC的RTSP服務器

直接上代碼。

參考:https://blog.csdn.net/weixin_42462202/category_9293806.html

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <time.h>

#include <fcntl.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "rtp.h"

#define AAC_FILE_NAME  "test.aac"
#define CLIENT_IP       "10.14.33.103"
#define CLIENT_PORT     9832

#define BUF_MAX_SIZE    (1024*1024)
#define SERVER_RTP_PORT  55532
#define SERVER_RTCP_PORT 55533
#define SERVER_PORT     8554

#define HDR_CSEQ 			"CSeq"
#define HDR_RANGE 			"Range"
#define HDR_SESSION 		"Session"
#define HDR_TRANSPORT 		"Transport"

#define RTSP_METHOD_OPTIONS  "OPTIONS"
#define RTSP_METHOD_DESCRIBE "DESCRIBE"
#define RTSP_METHOD_SETUP 	 "SETUP"
#define RTSP_METHOD_PLAY 	 "PLAY"

typedef struct _RTSP_HDR_PARAM_
{
	unsigned int rtsp_cseq;
	unsigned int rtsp_session;
	unsigned int rtsp_clientRtpPort;
	unsigned int rtsp_clientRtcpPort;
	unsigned char rtsp_method[20];
	char rtsp_InBuffer[BUF_MAX_SIZE]; /*接收緩衝區*/
	char rtsp_OutBuffer[BUF_MAX_SIZE];/*發送緩衝區*/
}RTSP_HDR_PARAM;


static int createTcpSocket()
{
    int sockfd;
    int on = 1;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0)
        return -1;

    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));

    return sockfd;
}

static int createUdpSocket()
{
    int sockfd;
    int on = 1;

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd < 0)
        return -1;

    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));

    return sockfd;
}

static int bindSocketAddr(int sockfd, const char* ip, int port)
{
    struct sockaddr_in addr;

    addr.sin_family 		= AF_INET;
    addr.sin_port 			= htons(port);
    addr.sin_addr.s_addr 	= inet_addr(ip);

    if(bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) < 0)
        return -1;

    return 0;
}

static int acceptClient(int sockfd, char* ip, int* port)
{
    int clientfd;
    socklen_t len = 0;
    struct sockaddr_in addr;

    memset(&addr, 0, sizeof(addr));
    len = sizeof(addr);

    clientfd = accept(sockfd, (struct sockaddr *)&addr, &len);
    if(clientfd < 0)
        return -1;
    
    strcpy(ip, inet_ntoa(addr.sin_addr));
    *port = ntohs(addr.sin_port);

    return clientfd;
}
struct AdtsHeader
{
    unsigned int syncword;  //12 bit 同步字 '1111 1111 1111',說明一個ADTS幀的開始
    unsigned int id;        //1 bit MPEG 標示符, 0 for MPEG-4,1 for MPEG-2
    unsigned int layer;     //2 bit 總是'00'
    unsigned int protectionAbsent;  //1 bit 1表示沒有crc,0表示有crc
    unsigned int profile;           //1 bit 表示使用哪個級別的AAC
    unsigned int samplingFreqIndex; //4 bit 表示使用的採樣頻率
    unsigned int privateBit;        //1 bit
    unsigned int channelCfg; //3 bit 表示聲道數
    unsigned int originalCopy;         //1 bit 
    unsigned int home;                  //1 bit 

    /*下面的爲改變的參數即每一幀都不同*/
    unsigned int copyrightIdentificationBit;   //1 bit
    unsigned int copyrightIdentificationStart; //1 bit
    unsigned int aacFrameLength;               //13 bit 一個ADTS幀的長度包括ADTS頭和AAC原始流
    unsigned int adtsBufferFullness;           //11 bit 0x7FF 說明是碼率可變的碼流

    /* 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個採樣及相關數據)
     */
    unsigned int numberOfRawDataBlockInFrame; //2 bit
};


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 = ((unsigned int) in[1] & 0x08) >> 3;
        res->layer = ((unsigned int) in[1] & 0x06) >> 1;
        res->protectionAbsent = (unsigned int) in[1] & 0x01;
        res->profile = ((unsigned int) in[2] & 0xc0) >> 6;
        res->samplingFreqIndex = ((unsigned int) in[2] & 0x3c) >> 2;
        res->privateBit = ((unsigned int) in[2] & 0x02) >> 1;
        res->channelCfg = ((((unsigned int) in[2] & 0x01) << 2) | (((unsigned int) in[3] & 0xc0) >> 6));
        res->originalCopy = ((unsigned int) in[3] & 0x20) >> 5;
        res->home = ((unsigned int) in[3] & 0x10) >> 4;
        res->copyrightIdentificationBit = ((unsigned int) in[3] & 0x08) >> 3;
        res->copyrightIdentificationStart = (unsigned int) 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 = ((unsigned int) in[6] & 0x03);

        return 0;
    }
    else
    {
        printf("failed to parse adts header\n");
        return -1;
    }
}

static int rtpSendAACFrame(int socket, const 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++;

    /*
     * 如果採樣頻率是44100
     * 一般AAC每個1024個採樣爲一幀
     * 所以一秒就有 44100 / 1024 = 43幀
     * 時間增量就是 44100 / 43 = 1025
     * 一幀的時間爲 1 / 43 = 23ms
     */
    rtpPacket->rtpHeader.timestamp += 1025;

    return 0;
}

static int RTSP_HandleMethodOPTIONS(RTSP_HDR_PARAM* pstRtspHdrParam)
{
    sprintf(pstRtspHdrParam->rtsp_OutBuffer,
					"RTSP/1.0 200 OK\r\n"
                    "CSeq: %d\r\n"
                    "Public: OPTIONS, DESCRIBE, SETUP, PLAY\r\n"
                    "\r\n",
                    pstRtspHdrParam->rtsp_cseq);
                
    return 0;

}

static int RTSP_HandleMethodDESCRIBE(RTSP_HDR_PARAM* pstRtspHdrParam)
{
	char sdp[500];
    char localIp[100];
	char url[255];
    sscanf(pstRtspHdrParam->rtsp_InBuffer, "%*s %256s",url);
    sscanf(pstRtspHdrParam->rtsp_InBuffer, "DESCRIBE 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 SizeLength=13;\r\n"
                 "a=control:track0\r\n",
                 time(NULL), localIp);

    
    sprintf(pstRtspHdrParam->rtsp_OutBuffer, 
					"RTSP/1.0 200 OK\r\n"
					"CSeq: %d\r\n"
                    "Content-Base: %s\r\n"
                    "Content-type: application/sdp\r\n"
                    "Content-length: %d\r\n\r\n"
                    "%s",
                    pstRtspHdrParam->rtsp_cseq,
                    url,
                    strlen(sdp),
                    sdp);
    return 0;

}

static int RTSP_HandleMethodSETUP(RTSP_HDR_PARAM* pstRtspHdrParam)
{
	char *p = NULL;
	if((p = strstr(pstRtspHdrParam->rtsp_InBuffer,HDR_TRANSPORT)))
	{
		sscanf(p, "Transport: RTP/AVP;unicast;client_port=%d-%d\r\n",
			 &pstRtspHdrParam->rtsp_clientRtpPort, &pstRtspHdrParam->rtsp_clientRtcpPort);
	}

    sprintf(pstRtspHdrParam->rtsp_OutBuffer, 
    				"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",
                    pstRtspHdrParam->rtsp_cseq,
                    pstRtspHdrParam->rtsp_clientRtpPort,
                    pstRtspHdrParam->rtsp_clientRtcpPort,
                    SERVER_RTP_PORT,
                    SERVER_RTCP_PORT);
    return 0;
}

static int RTSP_HandleMethodPLAY(RTSP_HDR_PARAM* pstRtspHdrParam)
{
    sprintf(pstRtspHdrParam->rtsp_OutBuffer, 
					"RTSP/1.0 200 OK\r\n"
                    "CSeq: %d\r\n"
                    "Range: npt=0.000-\r\n"
                    "Session: 66334873; timeout=60\r\n"
                    "\r\n",
                    pstRtspHdrParam->rtsp_cseq);
    return 0;
}


static int RTSP_HandleMethod(RTSP_HDR_PARAM* pstRtspHdrParam)
{
	if(!strcmp(pstRtspHdrParam->rtsp_method,RTSP_METHOD_OPTIONS))
	{
		if(RTSP_HandleMethodOPTIONS(pstRtspHdrParam))
		{
			printf("failed to handle OPTIONS\n");
			return -1;
		}
	}
	else if(!strcmp(pstRtspHdrParam->rtsp_method,RTSP_METHOD_DESCRIBE))
	{
		if(RTSP_HandleMethodDESCRIBE(pstRtspHdrParam))
		{
			printf("failed to handle DESCRIBE\n");
			return -1;
		}
	}
	else if(!strcmp(pstRtspHdrParam->rtsp_method,RTSP_METHOD_SETUP))
	{
		if(RTSP_HandleMethodSETUP(pstRtspHdrParam))
		{
			printf("failed to handle SETUP\n");
			return -1;
		}
	}
	else if(!strcmp(pstRtspHdrParam->rtsp_method,RTSP_METHOD_PLAY))
	{
		if(RTSP_HandleMethodPLAY(pstRtspHdrParam))
		{
			printf("failed to handle PLAY\n");
			return -1;
		}
	}

	return 0;

}

/*
*OPTIONS rtsp://10.1.74.190:8554 RTSP/1.0
*CSeq: 2
*User-Agent: LibVLC/3.0.4 (LIVE555 Streaming Media v2016.11.28)
*
*DESCRIBE rtsp://10.1.74.190:8554 RTSP/1.0
*CSeq: 3
*User-Agent: LibVLC/3.0.4 (LIVE555 Streaming Media v2016.11.28)
*Accept: application/sdp
*
*
*SETUP rtsp://10.1.74.190:8554/track0 RTSP/1.0
*CSeq: 4
*User-Agent: LibVLC/3.0.4 (LIVE555 Streaming Media v2016.11.28)
*Transport: RTP/AVP;unicast;client_port=63244-63245
*
*PLAY rtsp://10.1.74.190:8554 RTSP/1.0
*CSeq: 5
*User-Agent: LibVLC/3.0.4 (LIVE555 Streaming Media v2016.11.28)
*Session: 66334873
*Range: npt=0.000-
*
*/
static void doClient(int clientSockfd, const char* clientIP, int clientPort,
                        int serverRtpSockfd, int serverRtcpSockfd)
{
	int recvLen;
	int iRet = -1;
	char *p = NULL;
	RTSP_HDR_PARAM stRtspHdrParam;

	while(1)
    {
    	recvLen = recv(clientSockfd, stRtspHdrParam.rtsp_InBuffer, BUF_MAX_SIZE, 0);
		if(recvLen <= 0)
            goto out;
		stRtspHdrParam.rtsp_InBuffer[recvLen] = '\0';
		printf("---------------C->S--------------\n");
		printf("%s\n",stRtspHdrParam.rtsp_InBuffer);

		if (!sscanf(stRtspHdrParam.rtsp_InBuffer, "%s",stRtspHdrParam.rtsp_method))
	    {
	    	printf("prase url failed\n");
			goto out;
	    }

		if((p = strstr(stRtspHdrParam.rtsp_InBuffer,HDR_CSEQ)) != NULL)
		{		
			if(!sscanf(p, "%*s %d",&stRtspHdrParam.rtsp_cseq))
			{
				printf("prase Cseq failed\n");
				goto out;
			}
		}

		iRet = RTSP_HandleMethod(&stRtspHdrParam);
		if(iRet != 0)
		{
			printf("RTSP_HandleMethod failed\n");
			goto out;
		}
		
		printf("---------------S->C--------------\n");
		printf("%s\n",stRtspHdrParam.rtsp_OutBuffer);
		send(clientSockfd, stRtspHdrParam.rtsp_OutBuffer, strlen(stRtspHdrParam.rtsp_OutBuffer), 0);

		/* 開始播放,發送RTP包 */
		if(!strcmp(stRtspHdrParam.rtsp_method,RTSP_METHOD_PLAY))
		{

			struct AdtsHeader adtsHeader;
			struct RtpPacket* rtpPacket;
			uint8_t* frame;
			int ret;
		
			int fd = open(AAC_FILE_NAME, O_RDONLY);
			if(fd < 0)
			{
				printf("failed to open %s\n", AAC_FILE_NAME);
				goto out;
			}
		
			frame = (uint8_t*)malloc(5000);
			rtpPacket = malloc(5000);
		
			rtpHeaderInit(rtpPacket, 0, 0, 0, RTP_VESION, RTP_PAYLOAD_TYPE_AAC, 1, 0, 0, 0x32411);
		
			while(1)
			{
				ret = read(fd, frame, 7);
				if(ret <= 0)
				{
					break;
				}
		
				if(parseAdtsHeader(frame, &adtsHeader) < 0)
				{
					printf("parse err\n");
					break;
				}
		
				ret = read(fd, frame, adtsHeader.aacFrameLength-7);
				if(ret < 0)
				{
					printf("read err\n");
					break;
				}
		
				rtpSendAACFrame(serverRtpSockfd, clientIP, stRtspHdrParam.rtsp_clientRtpPort,
								rtpPacket, frame, adtsHeader.aacFrameLength-7);
		
				usleep(23000);
			}
		
			free(frame);
			free(rtpPacket);
            goto out;
		}
		
	}

out:
	close(clientSockfd);

}

int main(int argc, char* argv[])
{
    int serverSockfd;
    int serverRtpSockfd, serverRtcpSockfd;
    int ret;

	/* 1. 創建TCP   socket */
    serverSockfd = createTcpSocket();
    if(serverSockfd < 0)
    {
        printf("failed to create tcp socket\n");
        return -1;
    }

	/* 2. 綁定TCP   socket */
	ret = bindSocketAddr(serverSockfd, "0.0.0.0", SERVER_PORT);
    if(ret < 0)
    {
        printf("failed to bind addr\n");
        return -1;
    }

	/* 3. 監聽 */
    ret = listen(serverSockfd, 10);
    if(ret < 0)
    {
        printf("failed to listen\n");
        return -1;
    }


	/* 4. 創建UDP socket */
    serverRtpSockfd = createUdpSocket();
    serverRtcpSockfd = createUdpSocket();
    if(serverRtpSockfd < 0 || serverRtcpSockfd < 0)
    {
        printf("failed to create udp socket\n");
        return -1;
    }

	/* 5. 綁定UDP   socket */
	if(bindSocketAddr(serverRtpSockfd, "0.0.0.0", SERVER_RTP_PORT) < 0 ||
        bindSocketAddr(serverRtcpSockfd, "0.0.0.0", SERVER_RTCP_PORT) < 0)
    {
        printf("failed to bind addr\n");
        return -1;
    }

	while(1)
	{
		int clientSockfd;
		char clientIp[40];
		int clientPort;

		clientSockfd = acceptClient(serverSockfd, clientIp, &clientPort);
		if(clientSockfd < 0)
		{
			printf("failed to accept client\n");
			return -1;
		}

		printf("accept client;client ip:%s,client port:%d\n", clientIp, clientPort);

		doClient(clientSockfd, clientIp, clientPort, serverRtpSockfd, serverRtcpSockfd);

	}
		
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章