Rtsp服务器搭建(RTSP协议实现)

Rtsp服务器搭建(RTSP协议实现)

RTSP协议

OPTIONS rtsp://127.0.0.1:8554/live RTSP/1.0\r\n
CSeq: 2\r\n
\r\n
DESCRIBE rtsp://127.0.0.1:8554/live RTSP/1.0\r\n
CSeq: 3\r\n
Accept: application/sdp\r\n
\r\n
SETUP rtsp://127.0.0.1:8554/live/track0 RTSP/1.0\r\n
CSeq: 4\r\n
Transport: RTP/AVP;unicast;client_port=54492-54493\r\n
\r\n
PLAY rtsp://127.0.0.1:8554/live RTSP/1.0\r\n
CSeq: 5\r\n
Session: 66334873\r\n
Range: npt=0.000-\r\n
\r\n

头文件(函数实现)

导入库

//"main.h"
#include<iostream>
#define _WINSOCK_DEPRECATED_NO_WARNINGS 1
#include<string>
#include<WinSock2.h>

#pragma comment(lib,"ws2_32.lib")
using namespace std;

extern "C"
{
    #include<stdio.h>
    #include<time.h>
    #include<stdlib.h>
}
#define SERVER_PORT     8554
#define SERVER_RTP_PORT  55532
#define SERVER_RTCP_PORT 55533
#define BUF_MAX_SIZE    (1024*1024)

创建tcpsocket套接字(createTcpSocket)

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

创建udpsocket套接字(createUdpSocket)

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

绑定地址和端口(bindSockerAddr()

//绑定地址和端口
static int bindSockerAddr(int sockfd, const char*ip, int port)
{
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.S_un.S_addr = inet_addr(ip);
    if (bind(sockfd, (sockaddr*)&addr, sizeof(sockaddr)) < 0)
        return -1;
    return 0;
}

接收客户端连接请求(acceptClient)

static int acceptClient(int sockfd, string *ip, int *port)
{
    int clientfd;
    int len = 0;
    sockaddr_in addr;
    //将addr初始化为0
    memset(&addr, 0, sizeof(addr));
    len = sizeof(addr);
    clientfd = accept(sockfd, (sockaddr*)&addr, &len);
    //addr.sin_addr.S_un.S_addr = inet_addr("192.168.2.160");
    if (clientfd < 0)
        return -1;
    *ip = inet_ntoa(addr.sin_addr);
    *port = ntohs(addr.sin_port);
    return clientfd;

}

获取报文第一行数据(getlinefrombuf)

static char *getlinefrombuf(char*buf, char*line)
{
    while (*buf != '\n')
    {
        *line = *buf;
        line++;
        buf++;
    }
    *line = '\n';
    line++;
    *line = '\0';
    ++buf;
    return buf;
}

处理OPTIONS方法

static int handleCmd_OPTIONS(char* result, int cseq)
{
    sprintf_s(result, 256, "RTSP/1.0 200 OK\r\n"
              "CSeq: %d\r\n"
              "Public: OPTIONS, DESCRIBE, SETUP, PLAY\r\n"
              "\r\n",
              cseq);

    return 0;
}

处理DESCRIBE方法

static int handleCmd_DESCRIBE(char* result, int cseq, char* url)
{
    char sdp[500];
    char localIp[100];

    sscanf_s(url, "rtsp://%[^:]:", localIp, sizeof(localIp));

    sprintf_s(sdp, 256, "v=0\r\n"
              "o=- 9%ld 1 IN IP4 %s\r\n"
              "t=0 0\r\n"
              "a=control:*\r\n"
              "m=video 0 RTP/AVP 96\r\n"
              "a=rtpmap:96 H264/90000\r\n"
              "a=control:track0\r\n",
              time(NULL), localIp);

    sprintf_s(result, 256, "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;
}

处理SETUP方法

static int handleCmd_SETUP(char* result, int cseq, int clientRtpPort)
{
    sprintf_s(result, 256, "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;
}

处理PLAY方法

static int handleCmd_PLAY(char* result, int cseq)
{
    sprintf_s(result, 256, "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",
              cseq);

    return 0;
}

消息处理函数

static void doClient(int clientSockfd, string clientIP, int clientPort,
                     int serverRtpSockfd, int serverRtcpSockfd)
{
    char method[40];//方法
    char url[100];//url
    char version[40];//版本号
    int cseq;//序列号
    int clientRtpPort;//rtp端口号
    int clientRtcpPort;//rtcp端口号
    char *bufPtr;
    char *rbuf = new char[BUF_MAX_SIZE];
    char *sbuf = new char[BUF_MAX_SIZE];
    char line[100];
    while (true)
    {
        int recvlen;
        //接收客户端请求
        recvlen = recv(clientSockfd, rbuf, BUF_MAX_SIZE, 0);
        if (recvlen <= 0)
            goto out;
        rbuf[recvlen] = '\0';//结尾加'\0'结束符
        //打印报文
        cout << "----------------------C->S----------------------\n" << endl;
        cout << rbuf;
        bufPtr = getlinefrombuf(rbuf, line);
        //解析方法
        if (sscanf_s(line, "%s %s %s\r\n", method, sizeof(method), url, sizeof(url), version, sizeof(version)) != 3)
        {
            cerr << "解析错误!!!" << endl;
            goto out;
        }
        //解析序列号
        bufPtr = getlinefrombuf(bufPtr, line);
        if (sscanf_s(line, "CSeq: %d\r\n", &cseq) != 1)
        {
            cerr << "解析错误!!!" << endl;
            goto out;
        }
        //如果是setup则再解析client_port
        if (!strcmp(method, "SETUP"))
        {
            while (1)
            {
                bufPtr = getlinefrombuf(bufPtr, line);
                if (!strncmp(line, "Transport:", strlen("Transport:")))
                {
                    sscanf_s(line, "Transport: RTP/AVP;unicast;client_port=%d-%d\r\n", &clientRtpPort, &clientRtcpPort);
                    break;
                }
            }
        }
        if (!strcmp(method, "OPTIONS"))
        {
            if (handleCmd_OPTIONS(sbuf, cseq))
            {
                cerr << "无法处理OPTIONS" << endl;
                goto out;
            }
        }
        else if (!strcmp(method, "DESCRIBE"))
        {
            if (handleCmd_DESCRIBE(sbuf, cseq, url))
            {
                cerr << "无法处理DESCRIBE" << endl;
                goto out;
            }
        }
        else if (!strcmp(method, "SETUP"))
        {
            if (handleCmd_SETUP(sbuf, cseq, clientRtpPort))
            {
                cerr << "无法处理SETUP" << endl;
                goto out;
            }
        }
        else if (!strcmp(method, "PLAY"))
        {
            if (handleCmd_PLAY(sbuf, cseq))
            {
                cerr << "无法处理PLAY" << endl;
                goto out;
            }
        }
        else
        {
            goto out;
        }
        cout << "----------------------S->C----------------------\n" << endl;
        cout << sbuf;
        send(clientSockfd, sbuf, strlen(sbuf), 0);
    }
    out:
    delete[]rbuf;
    delete[]sbuf;
    closesocket(clientSockfd);
}

主函数

初始化库

WORD version = MAKEWORD(2, 2);//初始化套接字
WSADATA wsa;//初始化套接字
int is = WSAStartup(version, &wsa);
if (is != 0)
{
    cerr << "初始化失败!!!" << endl;
    WSACleanup();
    return -1;
}

版本校验

if (2 != HIBYTE(wsa.wVersion) || 2 != LOBYTE(wsa.wVersion))
{
    //版本不匹配
    WSACleanup();
    return -1;
}

创建套接字

int serverSockfd;
int serverRtpSockfd, serverRtcpSockfd;
int ret;

创建tcp套接字

serverSockfd = createTcpSocket();
if (serverSockfd < 0)
{
    cerr << "TCP套接字创建失败!!!" << endl;
    return -1;
}

绑定地址和端口

ret = bindSockerAddr(serverSockfd, "0.0.0.0", SERVER_PORT);
if (ret < 0)
{
    cerr << "绑定地址或端口失败!!!" << endl;
    return -1;
}

监听网络端口

ret = listen(serverSockfd, 10);
if (ret < 0)
{
    cerr << "监听端口失败!!!" << endl;
    return -1;
}

创建rtp/rtcp udp套接字

//创建rtp udp套接字
serverRtpSockfd = createUdpSocket();
//创建rtcp udp套接字
serverRtcpSockfd = createUdpSocket();
if (serverRtcpSockfd < 0 || serverRtpSockfd < 0)
{
    cerr << "udp创建失败!!!" << endl;
    return -1;
}
if (bindSockerAddr(serverRtpSockfd, "0.0.0.0", SERVER_RTP_PORT) < 0 ||
    bindSockerAddr(serverRtcpSockfd, "0.0.0.0", SERVER_RTCP_PORT) < 0)
{
    cerr << "UDP套接字创建失败!!!" << endl;
    return -1;
}
cout << "rtsp://127.0.0.1:" << SERVER_PORT << endl;

循环接收并处理消息

while (true)
{
    int clientSockfd;
    string clientIp;
    int clientPort;
    //接收客户端连接请求
    clientSockfd = acceptClient(serverSockfd, &clientIp, &clientPort);
    if (clientSockfd < 0)
    {
        cerr << "客户端连接失败!!!" << endl;
        return -1;
    }
    cout << "accept client;client ip:" << clientIp << ",client port:" << clientPort << endl;
    //处理客户端消息
    doClient(clientSockfd, clientIp, clientPort, serverRtpSockfd, serverRtcpSockfd);
}

源代码

“main.h”

#include<iostream>
#define _WINSOCK_DEPRECATED_NO_WARNINGS 1
#include<string>
#include<WinSock2.h>

#pragma comment(lib,"ws2_32.lib")
using namespace std;

extern "C"
{
    #include<stdio.h>
    #include<time.h>
    #include<stdlib.h>
}
#define SERVER_PORT     8554
#define SERVER_RTP_PORT  55532
#define SERVER_RTCP_PORT 55533
#define BUF_MAX_SIZE    (1024*1024)
//创建tcpsocket套接字
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;
}
//创建udpsocket套接字
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 bindSockerAddr(int sockfd, const char*ip, int port)
{
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.S_un.S_addr = inet_addr(ip);
    if (bind(sockfd, (sockaddr*)&addr, sizeof(sockaddr)) < 0)
        return -1;
    return 0;
}
//接收客户端连接请求
static int acceptClient(int sockfd, string *ip, int *port)
{
    int clientfd;
    int len = 0;
    sockaddr_in addr;
    //将addr初始化为0
    memset(&addr, 0, sizeof(addr));
    len = sizeof(addr);
    clientfd = accept(sockfd, (sockaddr*)&addr, &len);
    //addr.sin_addr.S_un.S_addr = inet_addr("192.168.2.160");
    if (clientfd < 0)
        return -1;
    *ip = inet_ntoa(addr.sin_addr);
    *port = ntohs(addr.sin_port);
    return clientfd;

}
//获取报文第一行数据
static char *getlinefrombuf(char*buf, char*line)
{
    while (*buf != '\n')
    {
        *line = *buf;
        line++;
        buf++;
    }
    *line = '\n';
    line++;
    *line = '\0';
    ++buf;
    return buf;
}
//处理OPTIONS方法
static int handleCmd_OPTIONS(char* result, int cseq)
{
    sprintf_s(result, 256, "RTSP/1.0 200 OK\r\n"
              "CSeq: %d\r\n"
              "Public: OPTIONS, DESCRIBE, SETUP, PLAY\r\n"
              "\r\n",
              cseq);

    return 0;
}
//处理DESCRIBE方法
static int handleCmd_DESCRIBE(char* result, int cseq, char* url)
{
    char sdp[500];
    char localIp[100];

    sscanf_s(url, "rtsp://%[^:]:", localIp, sizeof(localIp));

    sprintf_s(sdp, 256, "v=0\r\n"
              "o=- 9%ld 1 IN IP4 %s\r\n"
              "t=0 0\r\n"
              "a=control:*\r\n"
              "m=video 0 RTP/AVP 96\r\n"
              "a=rtpmap:96 H264/90000\r\n"
              "a=control:track0\r\n",
              time(NULL), localIp);

    sprintf_s(result, 256, "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;
}
//处理SETUP方法
static int handleCmd_SETUP(char* result, int cseq, int clientRtpPort)
{
    sprintf_s(result, 256, "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;
}
//处理PLAY方法
static int handleCmd_PLAY(char* result, int cseq)
{
    sprintf_s(result, 256, "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",
              cseq);

    return 0;
}
//消息处理函数
static void doClient(int clientSockfd, string clientIP, int clientPort,
                     int serverRtpSockfd, int serverRtcpSockfd)
{
    char method[40];//方法
    char url[100];//url
    char version[40];//版本号
    int cseq;//序列号
    int clientRtpRort;//rtp端口号
    int clientRtcpPort;//rtcp端口号
    char *bufPtr;
    char *rbuf = new char[BUF_MAX_SIZE];
    char *sbuf = new char[BUF_MAX_SIZE];
    char line[100];
    while (true)
    {
        int recvlen;
        //接收客户端请求
        recvlen = recv(clientSockfd, rbuf, BUF_MAX_SIZE, 0);
        if (recvlen <= 0)
            goto out;
        rbuf[recvlen] = '\0';//结尾加'\0'结束符
        //打印报文
        cout << "----------------------C->S----------------------\n" << endl;
        cout << rbuf;
        bufPtr = getlinefrombuf(rbuf, line);
        //解析方法
        if (sscanf_s(line, "%s %s %s\r\n", method, sizeof(method), url, sizeof(url), version, sizeof(version)) != 3)
        {
            cerr << "解析错误!!!" << endl;
            goto out;
        }
        //解析序列号
        bufPtr = getlinefrombuf(bufPtr, line);
        if (sscanf_s(line, "CSeq: %d\r\n", &cseq) != 1)
        {
            cerr << "解析错误!!!" << endl;
            goto out;
        }
        //如果是setup则再解析client_port
        if (!strcmp(method, "SETUP"))
        {
            while (1)
            {
                bufPtr = getlinefrombuf(bufPtr, line);
                if (!strncmp(line, "Transport:", strlen("Transport:")))
                {
                    sscanf_s(line, "Transport: RTP/AVP;unicast;client_port=%d-%d\r\n", &clientRtpRort, &clientRtpRort);
                    break;
                }
            }
        }
        if (!strcmp(method, "OPTIONS"))
        {
            if (handleCmd_OPTIONS(sbuf, cseq))
            {
                cerr << "无法处理OPTIONS" << endl;
                goto out;
            }
        }
        else if (!strcmp(method, "DESCRIBE"))
        {
            if (handleCmd_DESCRIBE(sbuf, cseq, url))
            {
                cerr << "无法处理DESCRIBE" << endl;
                goto out;
            }
        }
        else if (!strcmp(method, "SETUP"))
        {
            if (handleCmd_SETUP(sbuf, cseq, clientRtpRort))
            {
                cerr << "无法处理SETUP" << endl;
                goto out;
            }
        }
        else if (!strcmp(method, "PLAY"))
        {
            if (handleCmd_PLAY(sbuf, cseq))
            {
                cerr << "无法处理PLAY" << endl;
                goto out;
            }
        }
        else
        {
            goto out;
        }
        cout << "----------------------S->C----------------------\n" << endl;
        cout << sbuf;
        send(clientSockfd, sbuf, strlen(sbuf), 0);
    }
    out:
    delete[]rbuf;
    delete[]sbuf;
    closesocket(clientSockfd);
}

“main.cpp”

#include "main.h"
int main()
{
    //初始化库
    WORD version = MAKEWORD(2, 2);//初始化套接字
    WSADATA wsa;//初始化套接字
    int is = WSAStartup(version, &wsa);
    if (is != 0)
    {
        cerr << "初始化失败!!!" << endl;
        WSACleanup();
        return -1;
    }
    //版本校验
    if (2 != HIBYTE(wsa.wVersion) || 2 != LOBYTE(wsa.wVersion))
    {
        //版本不匹配
        WSACleanup();
        return -1;
    }
    //创建套接字
    int serverSockfd;
    int serverRtpSockfd, serverRtcpSockfd;
    int ret;
    //创建tcp套接字
    serverSockfd = createTcpSocket();
    if (serverSockfd < 0)
    {
        cerr << "TCP套接字创建失败!!!" << endl;
        return -1;
    }
    //绑定地址和端口
    ret = bindSockerAddr(serverSockfd, "0.0.0.0", SERVER_PORT);
    if (ret < 0)
    {
        cerr << "绑定地址或端口失败!!!" << endl;
        return -1;
    }
    //监听网络端口
    ret = listen(serverSockfd, 10);
    if (ret < 0)
    {
        cerr << "监听端口失败!!!" << endl;
        return -1;
    }
    //创建rtp udp套接字
    serverRtpSockfd = createUdpSocket();
    //创建rtcp udp套接字
    serverRtcpSockfd = createUdpSocket();
    if (serverRtcpSockfd < 0 || serverRtpSockfd < 0)
    {
        cerr << "udp创建失败!!!" << endl;
        return -1;
    }
    if (bindSockerAddr(serverRtpSockfd, "0.0.0.0", SERVER_RTP_PORT) < 0 ||
        bindSockerAddr(serverRtcpSockfd, "0.0.0.0", SERVER_RTCP_PORT) < 0)
    {
        cerr << "UDP套接字创建失败!!!" << endl;
        return -1;
    }
    //cout << "rtsp://192.168.2.160:" << SERVER_PORT << endl;
    cout << "rtsp://127.0.0.1:" << SERVER_PORT << endl;
    while (true)
    {
        int clientSockfd;
        string clientIp;
        int clientPort;
        //接收客户端连接请求
        clientSockfd = acceptClient(serverSockfd, &clientIp, &clientPort);
        if (clientSockfd < 0)
        {
            cerr << "客户端连接失败!!!" << endl;
            return -1;
        }
        cout << "accept client;client ip:" << clientIp << ",client port:" << clientPort << endl;
        //处理客户端消息
        doClient(clientSockfd, clientIp, clientPort, serverRtpSockfd, serverRtcpSockfd);
    }
}

运行

打开vlc输入rtsp://127.0.0.1:8554,这时控制台会输出交互信息,并且在wireshark回环抓包可以抓到交互报文
在这里插入图片描述
在这里插入图片描述

来自(52条消息) 从零开始写一个RTSP服务器(二)RTSP协议的实现_从零开始写一个rtsp服务器(二)_JT同学的博客-CSDN博客

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值