828华为云征文|采用Flexus云服务器X实例部署RTSP直播服务器

一、前言

这篇文章讲解: 采用华为云最新推出的Flexus云服务器X实例搭建RTSP服务器,完成视频直播需求。

随着实时视频流传输需求的增长,RTSP(实时流协议)服务器成为了许多视频监控、直播和多媒体应用的核心组件。在当今高度互联的世界中,能够快速部署且稳定运行的RTSP服务器对于确保高质量的视频流体验至关重要。本文将指导如何在华为云Flexus X实例上部署一款轻量级的RTSP服务器------(采用simple-rtsp-server)。

通过本教程,将详细介绍如何在华为云Flexus X实例上安装和配置simple-rtsp-server。simple-rtsp-server以其简单易用的特点而闻名,非常适合那些希望快速搭建起基本RTSP服务的开发者。将涵盖从环境准备到软件安装,再到基本配置的所有步骤,帮助顺利搭建一个可靠的RTSP服务器,为视频流应用提供坚实的基础。

华为云Flexus云服务器X实例是由国家科技进步奖获得者、华为公司Fellow及华为云首席架构师顾炯炯牵头研发的一款创新性云服务器。该实例基于华为的擎天QingTian架构、瑶光云脑和盘古大模型等核心技术,是业界首款应用驱动的柔性算力云服务器,适用于高科技、零售、金融、游戏等多个行业的通用工作负载场景,如网络应用、数据库、虚拟桌面、分析索引、微服务及持续集成/持续部署(CI/CD)等。

传统的云服务器通常只提供固定的CPU和内存规格,无法精准匹配用户的实际资源需求,导致资源利用效率低下。相比之下,华为云Flexus X实例提供了更为灵活的算力配置,支持超过100种不同的CPU与内存配比,最高可达到3:1的比例,从而更好地适应各种业务应用的需求。

Flexus X实例不仅在性能方面表现出色,还内置了智能应用调优算法,结合华为技术专家多年积累的经验,在基础模式下,其GeekBench单核及多核跑分可达业界同规格独享型实例的1.6倍。在性能模式下,Flexus X实例的性能超过了同类C系/G系/R系及S系旗舰型云主机的标准。

此外,Flexus X实例还配备了X-Turbo加速技术和大模型底层智能调度技术,为关键业务应用提供加速功能。例如,在Flexus X实例上部署的MySQL、Redis和Nginx等应用,其性能最高可达业界同规格独享型实例的6倍(MySQL性能),长期运行时也能保持2倍的性能优势。

Flexus X实例在定价策略上定位于经济型级别,但其性能表现却超越了旗舰级云主机。通过动态业务画像规格优化等技术,用户在将业务从本地服务器或其他云服务提供商迁移到Flexus X实例时,可以节省高达30%的算力成本,从而实现业务的全面提速和效能提升,享受到云基础设施的显著改进体验。

二、服务器选购

2.1 登录官网

链接:https://www.huaweicloud.com/

在官网首页的轮播图里可以看到,有Flexus云服务器的宣传。这是华为云匠心打造的下一代跃级产品,面向中低负载场景,性能倍增、体验跃级的服务器。

2.2 选购服务器

在产品页面,也可以看到Flexus云服务的选项,点击进去选购服务器。

链接:https://www.huaweicloud.com/product/flexus.html

在选购页面可以看到服务器推广器件,1年36块钱。 每个月的流量是100G,对于一些访问量不高的服务器或者测试用是非常合适的。

当前我要选择的服务器是:Flexus云服务器X实例 ,点击Flexus系列产品,选择X实例。Flexus云服务器X实例符合:柔性算力,六倍性能,旗舰体验,覆盖高科技、零售、金融、游戏等行业大多数通用工作负载场景。

2.3 选择服务器区域

针对时延敏感型业务请选择靠近您业务的区域,以降低网络时延,提高访问速度;针对和存量云产品有内网互通需求的业务,请选择和存量产品相同的区域。

2.4 选择服务器规格

2.5 选择系统镜像

我这选择ubuntu系统,用来搭建服务器。这个根据自己的情况选择,自己适合那一种就选择哪一种。

2.6 选择存储盘

我选择150G大小。

2.7 配置密码

设置好服务器的名字(如果你有多个服务器,为了自己好区别)和系统的登录密码。

2.8 配置云备份

云备份这个不买。有需要自己可以购买。

2.9 确认配置

2.10 立即购买

购买成功。

创建成功之后,邮箱会收到提示的。

2.10 后台控制台

链接:https://console.huaweicloud.com/ecm

在控制台可以看到服务器的详情。

三、服务器登录

3.1 查看服务器的详情

点击服务器的名称,可以进去到详情页面。

3.2 远程登录

填入设置好的密码。

登录成功。

3.9 采用FinalShell登录

自带的在浏览器里运行,每次需要打开浏览器,文件也不方便上传下载。

所以,这里开发阶段,我采用的 FinalShell登录到服务器。

新建SSH连接,输入连接信息。

登录成功。

接下来就可以进行开发了。

四、搭建RTSP流媒体服务器

4.1 simple-rtsp-server库

simple-rtsp-server依赖ffmpeg,版本要求>=4.x。

GitHub仓库地址:https://github.com/BreakingY/simple-rtsp-server

4.2 安装基础依赖

在命令行运行。

cpp 复制代码
sudo apt-get -y install autoconf automake build-essential libass-dev libfreetype6-dev libsdl2-dev libtheora-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev pkg-config texinfo zlib1g-dev

安装过程中:

安装完成。

4.3 安装依赖库

在系统命令行分别运行以下命令,安装依赖库。

cpp 复制代码
汇编库:
sudo apt-get install yasm
sudo apt-get install nasm
 
视频库:
sudo apt-get install libx264-dev
sudo apt-get install libx265-dev
 
音频库:
sudo apt-get install libfdk-aac-dev
sudo apt-get install libmp3lame-dev
sudo apt-get install libopus-dev

4.4 安装ffmpeg

下载ffmpeg的库源码。

cpp 复制代码
wget https://ffmpeg.org//releases/ffmpeg-4.0.5.tar.bz2
 
tar xjvf ffmpeg-4.0.5.tar.bz2
 
cd ffmpeg-4.0.5

编译ffmpeg库源码。

cpp 复制代码
./configure --prefix=/usr/local --enable-libx264 --disable-x86asm --enable-nonfree --enable-libfdk-aac  --enable-shared --enable-gpl --enable-libmp3lame --enable-libopus  --extra-cflags=-I/usr/local/include --extra-ldflags=-L/usr/local/lib
 
make
 
make install

4.5 simple-rtsp-server下载编译

cpp 复制代码
git clone https://github.com/BreakingY/simple-rtsp-server.git
 
cd simple-rtsp-server
 
mkdir build
 
cd build
 
cmake ..
 
make -j4

4.6 运行服务器

cpp 复制代码
cp -r ../mp4path .
./rtsp_server loop 0

运行效果:

4.7 开放规则

要记得把服务器的 端口开放出来, 不然无法访问。

4.8 播放测试(RTSP协议播放云端视频文件)

自带了4个测试文件。 可以直接播放。

这是自带的4个测试文件的路径:

cpp 复制代码
root@flexusx-1a58:~/work/simple-rtsp-server# ls mp4path/
test_1280x720_h264_aac.mp4  test_h264_aac.mp4  test_h264_pcma.mkv  test_h265_aac.mp4

下面是播放格式:

cpp 复制代码
rtsp://116.205.107.156:8554/test_1280x720_h264_aac.mp4

其中:

cpp 复制代码
rtsp://<自己的公网IP>:8554/<mp4path目录下的视频文件名称>

下面是potplayer播放器播放的效果:

播放过程中,在服务器上会打印出每个过程的详情。

4.9 rtsp_server.c代码

cpp 复制代码
#include "common.h"
#include "session.h"
#include <arpa/inet.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define BUF_MAX_SIZE (1024 * 1024)
char *mp4Dir = "mp4path/\0"; // MP4文件存放位置
int auth = 1;
#define USER "admin"
#define PASSWORD "123456"
/*doClientThd线程参数*/
struct thd_arg_st
{
    int client_sock_fd;
    char client_ip[30];
    int client_port;
};

int create_rtp_sockets(int *fd1, int *fd2, int *port1, int *port2)
{
    struct sockaddr_in addr;
    int port = 0;

    *fd1 = socket(AF_INET, SOCK_DGRAM, 0);
    if (*fd1 < 0)
    {
        perror("socket");
        return -1;
    }

    *fd2 = socket(AF_INET, SOCK_DGRAM, 0);
    if (*fd2 < 0)
    {
        perror("socket");
        close(*fd1);
        return -1;
    }

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    for (port = 1024; port <= 65535; port += 2)
    {
        addr.sin_port = htons(port);
        if (bind(*fd1, (struct sockaddr *)&addr, sizeof(addr)) == 0)
        {
            addr.sin_port = htons(port + 1);
            if (bind(*fd2, (struct sockaddr *)&addr, sizeof(addr)) == 0)
            {
                *port1 = port;
                *port2 = port + 1;
                return 0;
            }
            close(*fd1);
        }
    }
    close(*fd1);
    close(*fd2);
    return -1;
}
static void generate_session_id(char *session_id, size_t size) {
    if (size < 9) {
        return;
    }
    time_t timestamp = time(NULL);
    srand((unsigned int)timestamp);
    int random_part = rand() % 1000000;
    snprintf(session_id, size, "%02ld%06d", timestamp % 100, random_part);
    return;
}
/*处理客户端rtsp请求*/
void *doClientThd(void *arg)
{
    signal(SIGINT, sig_handler);
    signal(SIGQUIT, sig_handler);
    signal(SIGKILL, sig_handler);
    struct thd_arg_st *arg_thd = (struct thd_arg_st *)arg;

    int client_sock_fd = arg_thd->client_sock_fd;
    char *client_ip = arg_thd->client_ip;
    int client_port = arg_thd->client_port;
    char method[40];
    char url[100];
    char url_tmp[100];
    char filename[100];
    char url_setup[100];
    char track[1024];
    char url_play[1024];
    char local_ip[40];
    char version[40];
    int cseq;
    char *buf_ptr;
    char *buf_tmp;
    char *recv_buf = malloc(BUF_MAX_SIZE);
    char *send_buf = malloc(BUF_MAX_SIZE);
    char line[400];
    // rtp_over_tcp
    int sig_0 = -1;
    int sig_1 = -1;
    int sig_2 = -1;
    int sig_3 = -1;
    int ture_of_rtp_tcp = 0;
    // rtp_over_udp
    int client_rtp_port = -1;
    int client_rtcp_port = -1;
    int client_rtp_port_1 = -1;
    int client_rtcp_port_1 = -1;
    int server_rtp_port = -1;
    int server_rtcp_port = -1;
    int server_rtp_port_1 = -1;
    int server_rtcp_port_1 = -1;
    int server_udp_socket_rtp_fd = -1;
    int server_udp_socket_rtcp_fd = -1;
    int server_udp_socket_rtp_1_fd = -1;
    int server_udp_socket_rtcp_1_fd = -1;

    char ch = '/';
    int findflag = 0;

    char path[100];
    memcpy(path, mp4Dir, strlen(mp4Dir));
    path[strlen(mp4Dir)] = '\0';

    char path_tmp[100];
    memcpy(path_tmp, mp4Dir, strlen(mp4Dir));
    path_tmp[strlen(mp4Dir)] = '\0';

    int fd;
    char *realm = "simple-rtsp-server";
    char nonce[33] = {0};
    generate_nonce(nonce, sizeof(nonce));
    char session_id[512];
    generate_session_id(session_id, sizeof(session_id));
    while (1)
    {
        int recv_len;

        recv_len = recv(client_sock_fd, recv_buf, BUF_MAX_SIZE, 0);
        if (recv_len <= 0)
            goto out;

        recv_buf[recv_len] = '\0';

        printf("---------------C->S--------------\n");
        printf("%s", recv_buf);

        buf_ptr = getLineFromBuf(recv_buf, line);
        buf_tmp = buf_ptr;

        if (sscanf(line, "%s %s %s\r\n", method, url, version) != 3)
        {
            printf("parse err\n");
            goto out;
        }

        /*解析序列号*/
        while (1)
        {
            buf_ptr = getLineFromBuf(buf_ptr, line);
            if (!strncmp(line, "CSeq:", strlen("CSeq:")))
            {
                if (sscanf(line, "CSeq: %d\r\n", &cseq) != 1)
                {
                    printf("parse err\n");
                    goto out;
                }
                break;
            }
        }
        if(auth == 1){
            // authorization
            if(!strcmp(method, "SETUP") || !strcmp(method, "DESCRIBE") || !strcmp(method, "PLAY")){
                AuthorizationInfo *auth_info = find_authorization(recv_buf);
                if(auth_info == NULL){
                    handleCmd_Unauthorized(send_buf, cseq, realm, nonce);
                    printf("---------------S->C--------------\n");
                    printf("%s", send_buf);
                    send(client_sock_fd, send_buf, strlen(send_buf), 0);
                    continue;
                }
                else{
                    // printf("nonce:%s\n", auth_info->nonce);
                    // printf("realm:%s\n", auth_info->realm);
                    // printf("response:%s\n", auth_info->response);
                    // printf("uri:%s\n", auth_info->uri);
                    // printf("username:%s\n", auth_info->username);
                    // 鉴权校验
                    int ret = authorization_verify(USER, PASSWORD, realm, nonce, auth_info->uri, method, auth_info->response);
                    free_authorization_info(auth_info);
                    if(ret < 0){ // 鉴权失败
                        goto out;
                    }
                }
            }
        }

        /* 如果是SETUP,需要解析是RTP_OVER_TCP还是RTP_OVER_UDP模式 */
        if (!strcmp(method, "SETUP"))
        {
            memset(url_setup, 0, sizeof(url_setup));
            memset(track, 0, sizeof(track));
            strcpy(url_setup, url);
            char *p = strrchr(url_setup, ch);
            memcpy(track, p + 1, strlen(p)); // video-track0 audio -track1
            while (1)
            {
                buf_tmp = getLineFromBuf(buf_tmp, line);

                if (!buf_tmp)
                {
                    break;
                }

                if (!strncmp(line, "Transport: RTP/AVP/TCP", strlen("Transport: RTP/AVP/TCP")))
                {

                    if (memcmp(track, "track0", 6) == 0)
                    {
                        sscanf(line, "Transport: RTP/AVP/TCP;unicast;interleaved=%d-%d\r\n", &sig_0, &sig_1);
                    }
                    else
                    {
                        sscanf(line, "Transport: RTP/AVP/TCP;unicast;interleaved=%d-%d\r\n", &sig_2, &sig_3);
                    }

                    ture_of_rtp_tcp = 1;
                    break;
                }
                if (!strncmp(line, "Transport: RTP/AVP/UDP", strlen("Transport: RTP/AVP/UDP")))
                {
                    if (memcmp(track, "track0", 6) == 0)
                    {
                        sscanf(line, "Transport: RTP/AVP/UDP;unicast;client_port=%d-%d\r\n", &client_rtp_port, &client_rtcp_port);
                    }
                    else
                    {
                        sscanf(line, "Transport: RTP/AVP/UDP;unicast;client_port=%d-%d\r\n", &client_rtp_port_1, &client_rtcp_port_1);
                    }
                    break;
                }
                if (!strncmp(line, "Transport: RTP/AVP", strlen("Transport: RTP/AVP")))
                {

                    if (memcmp(track, "track0", 6) == 0)
                    {
                        sscanf(line, "Transport: RTP/AVP;unicast;client_port=%d-%d\r\n", &client_rtp_port, &client_rtcp_port);
                    }
                    else
                    {
                        sscanf(line, "Transport: RTP/AVP;unicast;client_port=%d-%d\r\n", &client_rtp_port_1, &client_rtcp_port_1);
                    }
                    break;
                }
            }
        }
        if (!strcmp(method, "OPTIONS"))
        {
            char *p = strrchr(url, ch);
            memcpy(filename, p + 1, strlen(p));

            char *tmp = strcat(path_tmp, filename);
            findflag = 1;
            fd = open(tmp, O_RDONLY);
            if (fd < 0) // 请求的资源不存在返回404并关闭客户端文件描述符
            {
                perror("failed");
                handleCmd_404(send_buf, cseq);
                send(client_sock_fd, send_buf, strlen(send_buf), 0);
                goto out;
            }
            else
            {
                close(fd);
                if (handleCmd_OPTIONS(send_buf, cseq))
                {
                    printf("failed to handle options\n");
                    goto out;
                }
            }
        }
        else if (!strcmp(method, "DESCRIBE"))
        {
            if (findflag == 0)
            {
                char *p = strrchr(url, ch);
                memcpy(filename, p + 1, strlen(p));

                char *tmp = strcat(path_tmp, filename);
                fd = open(tmp, O_RDONLY);
                if (fd < 0) // 请求的资源不存在返回404并关闭客户端文件描述符
                {
                    perror("failed");
                    handleCmd_404(send_buf, cseq);
                    send(client_sock_fd, send_buf, strlen(send_buf), 0);
                    goto out;
                }
                close(fd);
                findflag = 1;
            }
            char sdp[1024];
            char localIp[100];
            sscanf(url, "rtsp://%[^:]:", localIp);
            int ret = generateSDP(path_tmp, localIp, sdp, sizeof(sdp));
            if (ret < 0)
            { // mp4文件有问题,或者视频不是H264/H265,音频不是AAC/PCMA
                handleCmd_500(send_buf, cseq);
                send(client_sock_fd, send_buf, strlen(send_buf), 0);
                goto out;
            }
            if (handleCmd_DESCRIBE(send_buf, cseq, url, sdp))
            {
                printf("failed to handle describe\n");
                goto out;
            }
        }
        else if (!strcmp(method, "SETUP") && ture_of_rtp_tcp == 0) // RTP_OVER_UDP
        {
            sscanf(url, "rtsp://%[^:]:", local_ip);
            if (memcmp(track, "track0", 6) == 0)
            {
                create_rtp_sockets(&server_udp_socket_rtp_fd, &server_udp_socket_rtcp_fd, &server_rtp_port, &server_rtp_port);
                handleCmd_SETUP_UDP(send_buf, cseq, client_rtp_port, server_rtp_port, session_id);
            }
            else
            {
                create_rtp_sockets(&server_udp_socket_rtp_1_fd, &server_udp_socket_rtcp_1_fd, &server_rtp_port_1, &server_rtp_port_1);
                handleCmd_SETUP_UDP(send_buf, cseq, client_rtp_port_1, server_rtp_port_1, session_id);
            }
        }
        else if (!strcmp(method, "SETUP") && ture_of_rtp_tcp == 1) // RTP_OVER_TCP
        {
            sscanf(url, "rtsp://%[^:]:", local_ip);
            if (memcmp(track, "track0", 6) == 0)
            {
                handleCmd_SETUP_TCP(send_buf, cseq, local_ip, client_ip, sig_0, session_id);
            }
            else
            {
                handleCmd_SETUP_TCP(send_buf, cseq, local_ip, client_ip, sig_2, session_id);
            }
        }
        else if (!strcmp(method, "PLAY"))
        {
            memset(url_play, 0, sizeof(url_play));
            memset(track, 0, sizeof(track));
            strcpy(url_play, url);
            if (handleCmd_PLAY(send_buf, cseq, url_play, session_id))
            {
                printf("failed to handle play\n");
                goto out;
            }
        }
        else
        {
            goto out;
        }

        printf("---------------S->C--------------\n");
        printf("%s", send_buf);

        send(client_sock_fd, send_buf, strlen(send_buf), 0);

        if (!strcmp(method, "PLAY"))
        {
            struct timeval time_pre, time_now;
            gettimeofday(&time_pre, NULL);

            char *tmp = strcat(path, filename);
            int ret = addClient(tmp, client_sock_fd, sig_0, sig_2, ture_of_rtp_tcp, client_ip, client_rtp_port, client_rtp_port_1,
                                server_udp_socket_rtp_fd, server_udp_socket_rtcp_fd, server_udp_socket_rtp_1_fd, server_udp_socket_rtcp_1_fd);
            if (ret < 0)
                goto out;
            int sum = getClientNum();

            gettimeofday(&time_now, NULL);
            int time_handle = 1000 * (time_now.tv_sec - time_pre.tv_sec) + (time_now.tv_usec - time_pre.tv_usec) / 1000;
            printf("timeuse:%dms sum_client:%d\n\n", time_handle, sum);
            goto over;
        }
    }
out:
    close(client_sock_fd);
    free(recv_buf);
    free(send_buf);
    free(arg);
    return NULL;
over:

    free(recv_buf);
    free(send_buf);
    free(arg);
    return NULL;
}

int main(int argc, char *argv[])
{
    if(argc < 3){
        printf("./rtsp_server auth(0-not authentication; 1-authentication) loop(0-not loop 1-loop)\n");
        return -1;
    }
    auth = atoi(argv[1]);
    reloop_flag = atoi(argv[2]);
    int server_sock_fd;
    int ret;
    signal(SIGINT, sig_handler);
    signal(SIGQUIT, sig_handler);
    signal(SIGKILL, sig_handler);

    signal(SIGPIPE, SIG_IGN);
    signal(SIGFPE, SIG_IGN);

    server_sock_fd = createTcpSocket();
    if (server_sock_fd < 0)
    {
        printf("failed to create tcp socket\n");
        return -1;
    }

    ret = bindSocketAddr(server_sock_fd, SERVER_IP, SERVER_PORT);
    if (ret < 0)
    {
        printf("failed to bind addr\n");
        return -1;
    }

    ret = listen(server_sock_fd, 100);
    if (ret < 0)
    {
        printf("failed to listen\n");
        return -1;
    }

    moduleInit();

    printf("rtsp://%s:%d/filename\n", SERVER_IP, SERVER_PORT);
    while (1)
    {
        int client_sock_fd;
        char client_ip[40];
        int client_port;
        pthread_t tid;

        client_sock_fd = acceptClient(server_sock_fd, client_ip, &client_port);
        if (client_sock_fd < 0)
        {
            printf("failed to accept client\n");
            return -1;
        }
        printf("###########accept client --> client_sock_fd:%d client ip:%s,client port:%d###########\n", client_sock_fd, client_ip, client_port);
        struct thd_arg_st *arg;
        arg = malloc(sizeof(struct thd_arg_st));
        memcpy(arg->client_ip, client_ip, strlen(client_ip));
        arg->client_port = client_port;
        arg->client_sock_fd = client_sock_fd;

        ret = pthread_create(&tid, NULL, doClientThd, (void *)arg);
        if (ret < 0)
        {
            perror("doClientThd pthread_create()");
        }
        pthread_detach(tid);
    }
    moduleDel();
    close(server_sock_fd);
    return 0;
}

4.10 common.c 代码

cpp 复制代码
#include "common.h"
#include "md5.h"
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/log.h>
#include <libavutil/time.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
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;
}
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;
}

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;
}
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)
    {
        printf("accept err:%s\n", strerror(errno));
        return -1;
    }
    strcpy(ip, inet_ntoa(addr.sin_addr));
    *port = ntohs(addr.sin_port);

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

char *getLineFromBuf(char *buf, char *line)
{
    while (*buf != '\n')
    {
        *line = *buf;
        line++;
        buf++;
    }

    *line = '\n';
    ++line;
    *line = '\0';

    ++buf;
    return buf;
}
static char *extract_value(const char *source, const char *key) {
    const char *start = strstr(source, key);
    if (!start) {
        return NULL;
    }
    start += strlen(key) + 2; // 跳过 key="

    const char *end = strchr(start, '"');
    if (!end) {
        return NULL;
    }

    size_t len = end - start;
    char *value = (char *)malloc(len + 1);
    if (!value) {
        return NULL;
    }

    strncpy(value, start, len);
    value[len] = '\0';
    return value;
}

AuthorizationInfo *find_authorization(const char *request) {
    const char *auth_start = strstr(request, "Authorization: ");
    if (!auth_start) {
        return NULL; // Authorization字段未找到
    }

    auth_start += strlen("Authorization: ");
    const char *auth_end = strchr(auth_start, '\r');
    if (!auth_end) {
        auth_end = strchr(auth_start, '\n');
    }
    if (!auth_end) {
        return NULL; // 无法找到行尾
    }

    char *auth_value = (char *)malloc(auth_end - auth_start + 1);
    if (!auth_value) {
        return NULL; // 内存分配失败
    }
    strncpy(auth_value, auth_start, auth_end - auth_start);
    auth_value[auth_end - auth_start] = '\0';

    AuthorizationInfo *auth_info = (AuthorizationInfo *)malloc(sizeof(AuthorizationInfo));
    if (!auth_info) {
        free(auth_value);
        return NULL; // 内存分配失败
    }

    auth_info->username = extract_value(auth_value, "username");
    auth_info->realm = extract_value(auth_value, "realm");
    auth_info->nonce = extract_value(auth_value, "nonce");
    auth_info->uri = extract_value(auth_value, "uri");
    auth_info->response = extract_value(auth_value, "response");

    free(auth_value);
    return auth_info;
}

void free_authorization_info(AuthorizationInfo *auth_info) {
    if (auth_info) {
        free(auth_info->username);
        free(auth_info->realm);
        free(auth_info->nonce);
        free(auth_info->uri);
        free(auth_info->response);
        free(auth_info);
    }
    return;
}
// 生成随机字符串
static void generate_random_string(char *buf, int length) {
    static const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    for (size_t i = 0; i < length; i++) {
        buf[i] = charset[rand() % (sizeof(charset) - 1)];
    }
    return;
}

// 生成nonce
void generate_nonce(char *nonce, int length) {
    if (length < 1) {
        nonce[0] = '\0';
        return;
    }
    memset(nonce, 0, length);
    srand((unsigned int)time(NULL));

    char random_string[128] = {0};
    generate_random_string(random_string, sizeof(random_string));

    char timestamp[32];
    snprintf(timestamp, sizeof(timestamp), "%ld", (long)time(NULL));

    char combined[256] = {0};
    snprintf(combined, sizeof(combined), "%s%s", random_string, timestamp);

    MD5_CTX md5;
    unsigned char decrypt[16];
    MD5Init(&md5);
    MD5Update(&md5, combined, strlen(combined));
    MD5Final(&md5, decrypt);
    for(int i = 0; i < 16; i++) {
        snprintf(&(nonce[i * 2]), 3, "%02x", decrypt[i]);
    }
    return;
}

int authorization_verify(char *username, char *password, char *realm, char *nonce, char *uri, char * method, char *response){
    // md5(username:realm:password)
    unsigned char res1[16];
    char res1_hex[33] = {0};
    char buffer1[256] = {0};
    sprintf(buffer1,"%s:%s:%s", username, realm, password);
    MD5_CTX md5_1;
    MD5Init(&md5_1);
    MD5Update(&md5_1, buffer1, strlen(buffer1));
    MD5Final(&md5_1, res1);
    for(int i = 0; i < 16; i++) {
        snprintf(&(res1_hex[i * 2]), 3, "%02x", res1[i]);
    }
    // md5(public_method:url)
    unsigned char res2[16];
    char res2_hex[33] = {0};
    char buffer2[256] = {0};
    sprintf(buffer2,"%s:%s", method, uri);
    MD5_CTX md5_2;
    MD5Init(&md5_2);
    MD5Update(&md5_2, buffer2, strlen(buffer2));
    MD5Final(&md5_2, res2);
    for(int i = 0; i < 16; i++) {
        snprintf(&(res2_hex[i * 2]), 3, "%02x", res2[i]);
    }
    // md5( md5(username:realm:password):nonce:md5(public_method:url) )
    unsigned char res[16];
    char res_hex[33] = {0};
    char buffer[512] = {0};
    sprintf(buffer,"%s:%s:%s", res1_hex, nonce, res2_hex);
    MD5_CTX md5;
    MD5Init(&md5);
    MD5Update(&md5, buffer, strlen(buffer));
    MD5Final(&md5, res);
    for(int i = 0; i < 16; i++) {
        snprintf(&(res_hex[i * 2]), 3, "%02x", res[i]);
    }
    // printf("res:%s response:%s\n", res_hex, response);
    if(strcmp(res_hex, response) == 0){
        return 0;
    }
    return -1;
}

int handleCmd_Unauthorized(char *result, int cseq, char *realm, char *nonce){
	sprintf(result, "RTSP/1.0 401 Unauthorized\r\n"
			        "CSeq: %d\r\n"
			        "WWW-Authenticate: Digest realm=\"%s\", nonce=\"%s\"\r\n"
			        "\r\n",
                cseq,
                realm,
                nonce);

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

    return 0;
}
int handleCmd_DESCRIBE(char *result, int cseq, char *url, char *sdp)
{
    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;
}
int handleCmd_SETUP_TCP(char *result, int cseq, char *localIp, char *clientIp, int sig_0, char *session)
{
    sprintf(result, "RTSP/1.0 200 OK\r\n"
                    "CSeq: %d\r\n"
                    "Transport: RTP/AVP/TCP;unicast;destination=%s;source=%s;interleaved=%d-%d\r\n"
                    "Session: %s\r\n"
                    "\r\n",
            cseq,
            clientIp,
            localIp,
            sig_0,
            sig_0 + 1,
            session);

    return 0;
}
int handleCmd_SETUP_UDP(char *result, int cseq, int clientRtpPort, int serverRtpPort, char *session)
{
    sprintf(result, "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: %s\r\n"
                    "\r\n",
            cseq,
            clientRtpPort,
            clientRtpPort + 1,
            serverRtpPort,
            serverRtpPort + 1,
            session);

    return 0;
}
int handleCmd_PLAY(char *result, int cseq, char *url_setup, char *session)
{
    sprintf(result, "RTSP/1.0 200 OK\r\n"
                    "CSeq: %d\r\n"
                    "Range: npt=0.000-\r\n"
                    "Session: %s; timeout=60\r\n\r\n",
            cseq,
            session);

    return 0;
}
int handleCmd_404(char *result, int cseq)
{
    sprintf(result, "RTSP/1.0 404 NOT FOUND\r\n"
                    "CSeq: %d\r\n"
                    "\r\n",
            cseq);

    return 0;
}
int handleCmd_500(char *result, int cseq)
{
    sprintf(result, "RTSP/1.0 500 SERVER ERROR\r\n"
                    "CSeq: %d\r\n"
                    "\r\n",
            cseq);

    return 0;
}
int check_media_info(const char *filename, MediaInfo *info)
{
    AVFormatContext *format_ctx = NULL;
    int ret;

    if ((ret = avformat_open_input(&format_ctx, filename, NULL, NULL)) < 0)
    {
        fprintf(stderr, "Could not open source file %s\n", filename);
        return ret;
    }
    if ((ret = avformat_find_stream_info(format_ctx, NULL)) < 0)
    {
        fprintf(stderr, "Could not find stream information\n");
        avformat_close_input(&format_ctx);
        return ret;
    }

    info->has_audio = 0;
    info->has_video = 0;
    info->is_video_h26x = 0;
    info->is_audio_aac_pcma = 0;
    info->audio_sample_rate = 0;
    info->audio_channels = 0;
    // info->vps = NULL;
    // info->sps = NULL;
    // info->pps = NULL;
    // info->vps_size = 0;
    // info->sps_size = 0;
    // info->pps_size = 0;

    for (unsigned int i = 0; i < format_ctx->nb_streams; i++)
    {
        AVStream *stream = format_ctx->streams[i];
        AVCodecParameters *codecpar = stream->codecpar;

        if (codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            info->has_video = 1;
            if (codecpar->codec_id == AV_CODEC_ID_H264 || codecpar->codec_id == AV_CODEC_ID_H265 || codecpar->codec_id == AV_CODEC_ID_HEVC)
            {
                info->is_video_h26x = 1;
                if (codecpar->codec_id == AV_CODEC_ID_H264)
                    info->video_type = H264;
                else
                    info->video_type = H265;
            }
        }
        else if (codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
        {
            info->has_audio = 1;
            if (codecpar->codec_id == AV_CODEC_ID_AAC || codecpar->codec_id == AV_CODEC_ID_PCM_ALAW)
            {
                info->is_audio_aac_pcma = 1;
                info->audio_sample_rate = codecpar->sample_rate;
                info->audio_channels = codecpar->channels;
                info->profile = codecpar->profile;
                if(codecpar->codec_id == AV_CODEC_ID_AAC)
                    info->audio_type = AAC;
                else
                    info->audio_type = PCMA;
            }
        }
    }
    avformat_close_input(&format_ctx);

    return 0;
}
void free_media_info(MediaInfo *info)
{
    // if (info->vps)
    // {
    //     free(info->vps);
    // }
    // if (info->sps)
    // {
    //     free(info->sps);
    // }
    // if (info->pps)
    // {
    //     free(info->pps);
    // }
    return;
}
/*
#define FF_PROFILE_AAC_MAIN 0
#define FF_PROFILE_AAC_LOW  1
#define FF_PROFILE_AAC_SSR  2
#define FF_PROFILE_AAC_LTP  3
#define FF_PROFILE_AAC_HE   4
#define FF_PROFILE_AAC_HE_V2 28
#define FF_PROFILE_AAC_LD   22
#define FF_PROFILE_AAC_ELD  38
#define FF_PROFILE_MPEG2_AAC_LOW 128
#define FF_PROFILE_MPEG2_AAC_HE  131
*/
static int get_audio_obj_type(int aactype){
    //AAC HE V2 = AAC LC + SBR + PS
    //AAV HE = AAC LC + SBR
    //所以无论是 AAC_HEv2 还是 AAC_HE 都是 AAC_LC
    switch(aactype){
        case 0:
        case 2:
        case 3:
            return aactype + 1;
        case 1:
        case 4:
        case 28:
            return 2;
        default:
            return 2;

    }
    return 2;
}

static int get_sample_rate_index(int freq, int aactype){

    int i = 0;
    int freq_arr[13] = {
        96000, 88200, 64000, 48000, 44100, 32000,
        24000, 22050, 16000, 12000, 11025, 8000, 7350
    };

    //如果是 AAC HEv2 或 AAC HE, 则频率减半
    if(aactype == 28 || aactype == 4){
        freq /= 2;
    }

    for(i = 0; i < 13; i++){
        if(freq == freq_arr[i]){
            return i;
        }
    }
    return 4;//默认是44100
}

static int get_channel_config(int channels, int aactype){
    //如果是 AAC HEv2 通道数减半
    if(aactype == 28){
        return (channels / 2);
    }
    return channels;
}
int generateSDP(char *file, char *localIp, char *buffer, int buffer_len)
{
    memset(buffer, 0, buffer_len);
    MediaInfo info;
    if (check_media_info(file, &info) != 0)
    {
        printf("server error\n");
        free_media_info(&info);
        return -1;
    }
    if (info.has_video && !info.is_video_h26x)
    {
        printf("only support h264 h265\n");
        free_media_info(&info);
        return -1;
    }
    if (info.has_audio && !info.is_audio_aac_pcma)
    {
        printf("only support aac pcma\n");
        free_media_info(&info);
        return -1;
    }
    sprintf(buffer, "v=0\r\n"
                    "o=- 9%ld 1 IN IP4 %s\r\n"
                    "c=IN IP4 %s\r\n"
                    "t=0 0\r\n"
                    "a=control:*\r\n",
            time(NULL), localIp, localIp);
    if (info.has_video)
    {
        sprintf(buffer + strlen(buffer), "m=video 0 RTP/AVP %d\r\n"
                                         "a=rtpmap:%d %s/90000\r\n"
                                         //"a=fmtp:%d profile-level-id=42A01E; packetization-mode=1; sprop-parameter-sets=Z0IACpZTBYmI,aMljiA==\r\n"
                                         "a=fmtp:%d packetization-mode=1\r\n"
                                         "a=control:track0\r\n",
                RTP_PAYLOAD_TYPE_H26X, RTP_PAYLOAD_TYPE_H26X, info.video_type == H264 ? "H264" : "H265", RTP_PAYLOAD_TYPE_H26X);
    }
    if (info.has_audio)
    {
        if (info.audio_type == AAC)
        {
            char config[10] = {0};
            int index = get_sample_rate_index(info.audio_sample_rate, info.profile);
            sprintf(config, "%02x%02x", (uint8_t)((get_audio_obj_type(info.profile)) << 3)|(index >> 1), (uint8_t)((index << 7)|(info.audio_channels << 3)));
            sprintf(buffer + strlen(buffer), "m=audio 0 RTP/AVP %d\r\n"
                                             "a=rtpmap:%d MPEG4-GENERIC/%u/%u\r\n"
                                             "a=fmtp:%d streamtype=5;profile-level-id=1;mode=AAC-hbr;config=%04u;sizelength=13;indexlength=3;indexdeltalength=3\r\n"
                                             "a=control:track1\r\n",
                    RTP_PAYLOAD_TYPE_AAC, RTP_PAYLOAD_TYPE_AAC, info.audio_sample_rate, info.audio_channels, RTP_PAYLOAD_TYPE_AAC, atoi(config));
        }
        else
        {
            sprintf(buffer + strlen(buffer), "m=audio 0 RTP/AVP %d\r\n"
                                             "a=rtpmap:%d PCMA/%u/%u\r\n"
                                             "a=control:track1\r\n",
                    RTP_PAYLOAD_TYPE_PCMA, RTP_PAYLOAD_TYPE_PCMA, info.audio_sample_rate, info.audio_channels);
        }
    }
    free_media_info(&info);
    return 0;
}
// aactype = ffmpeg --> AVCodecParameters *codecpar->profile
void adts_header(char *adts_header_buffer, int data_len, int aactype, int frequency, int channels){

    int audio_object_type = get_audio_obj_type(aactype);
    int sampling_frequency_index = get_sample_rate_index(frequency, aactype);
    int channel_config = get_channel_config(channels, aactype);

    int adts_len = data_len + 7;

    adts_header_buffer[0] = 0xff;         //syncword:0xfff                          高8bits
    adts_header_buffer[1] = 0xf0;         //syncword:0xfff                          低4bits
    adts_header_buffer[1] |= (0 << 3);    //MPEG Version:0 for MPEG-4,1 for MPEG-2  1bit
    adts_header_buffer[1] |= (0 << 1);    //Layer:0                                 2bits
    adts_header_buffer[1] |= 1;           //protection absent:1                     1bit

    adts_header_buffer[2] = (audio_object_type - 1)<<6;            //profile:audio_object_type - 1                      2bits
    adts_header_buffer[2] |= (sampling_frequency_index & 0x0f)<<2; //sampling frequency index:sampling_frequency_index  4bits
    adts_header_buffer[2] |= (0 << 1);                             //private bit:0                                      1bit
    adts_header_buffer[2] |= (channel_config & 0x04)>>2;           //channel configuration:channel_config               高1bit

    adts_header_buffer[3] = (channel_config & 0x03)<<6;     //channel configuration:channel_config      低2bits
    adts_header_buffer[3] |= (0 << 5);                      //original:0                               1bit
    adts_header_buffer[3] |= (0 << 4);                      //home:0                                   1bit
    adts_header_buffer[3] |= (0 << 3);                      //copyright id bit:0                       1bit
    adts_header_buffer[3] |= (0 << 2);                      //copyright id start:0                     1bit
    adts_header_buffer[3] |= ((adts_len & 0x1800) >> 11);           //frame length:value   高2bits

    adts_header_buffer[4] = (uint8_t)((adts_len & 0x7f8) >> 3);     //frame length:value    中间8bits
    adts_header_buffer[5] = (uint8_t)((adts_len & 0x7) << 5);       //frame length:value    低3bits
    adts_header_buffer[5] |= 0x1f;                                 //buffer fullness:0x7ff 高5bits
    adts_header_buffer[6] = 0xfc;
    return;
}

五、总结

华为云正在举行其年度828 B2B企业节活动,期间提供了包括Flexus X实例在内的多种产品的优惠。对于那些对计算性能有较高要求,并且需要自行部署MySQL、Redis、Nginx等服务的用户来说,这次促销是一个很好的机会,建议有兴趣的朋友可以前往查看相关的优惠信息。

官网直达:https://activity.huaweicloud.com/828_promotion/index.html

相关推荐
CDERgglUoMg10 小时前
BLDC直流无刷电机FOC控制 在Matlab/Simulink中实现了无刷直流电机的磁场定向...
华为云
七夜zippoe10 小时前
CANN Runtime任务描述序列化与持久化源码深度解码
大数据·运维·服务器·cann
盟接之桥10 小时前
盟接之桥说制造:引流品 × 利润品,全球电商平台高效产品组合策略(供讨论)
大数据·linux·服务器·网络·人工智能·制造
Fcy64812 小时前
Linux下 进程(一)(冯诺依曼体系、操作系统、进程基本概念与基本操作)
linux·运维·服务器·进程
袁袁袁袁满12 小时前
Linux怎么查看最新下载的文件
linux·运维·服务器
代码游侠12 小时前
学习笔记——设备树基础
linux·运维·开发语言·单片机·算法
主机哥哥12 小时前
阿里云OpenClaw部署全攻略,五种方案助你快速部署!
服务器·阿里云·负载均衡
Harvey90312 小时前
通过 Helm 部署 Nginx 应用的完整标准化步骤
linux·运维·nginx·k8s
珠海西格电力科技13 小时前
微电网能量平衡理论的实现条件在不同场景下有哪些差异?
运维·服务器·网络·人工智能·云计算·智慧城市
释怀不想释怀14 小时前
Linux环境变量
linux·运维·服务器