Qt截屏 X265lib编码 rtsp请求 rtp流发送 (rtsp)实现

\color{rgb(125,125,125)}{什么是rtsp协议,作用是什么?}
rtsp跟http一样都是七层应用层协议.在rtsp协议组中的主要作用是:
\color{rgb(255,125,0)}{返回支持的方法}
\color{rgb(255,125,0)}{客户端->发送客户端监听的rtp rtcp端口号}
\color{rgb(255,125,0)}{服务端->发送服务端监听的rtp rtcp端口号}
\color{rgb(255,125,125)}{下面是支持vlc,ffplay的rtsp的请求响应过程:}
1.客户端发送OPTIONS请求:

客户端(ffplay)请求报文:
OPTIONS rtsp://127.0.0.1:1133 RTSP/1.0\r\nCSeq: 1\r\nUser-Agent: Lavf57.83.100\r\n\r\n}$
服务端响应报文:
RTSP/1.0 200 OK\r\nCSeq:"+QString::number(cseq)+"\r\nPublic:DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE\r\n\r\n
//服务器返回了服务器支持的方法;
//注意请求和返回都是\r\n\r\n结尾。

2.客户端发送DESCRIBE请求

客户端发送DESCRIBE请求
DESCRIBE rtsp://127.0.0.1:1133 RTSP/1.0\r\nAccept: application/sdp\r\nCSeq: 2\r\nUser-Agent: Lavf57.83.100\r\n\r\n"
服务端响应客户端请求.
"c=IN IP4 127.0.0.1\r\nm=video 0 RTP/AVP 96\r\na=rtpmap:96 H265/90000\r\na=fmtp:96 profile-space=0;profile-id=0;tier-flag=0;level-id=0\r\n\r\n"
//注释:a=rtpmap:96 H265/90000  96表示媒体类型是视频,H265/90000表示h265编码波特率是90000

3.客户端SETUP请求:

客户端setup请求报文
"SETUP rtsp://127.0.0.1:1133 RTSP/1.0\r\nTransport: RTP/AVP/UDP;unicast;client_port=\color{rgb(255,0,0)}{20260-20261}\r\nCSeq: 3\r\nUser-Agent: Lavf57.83.100\r\n\r\n"
//红色为客户端监听的端口号.
服务端响应报文
"RTSP/1.0 200 OK\r\nCSeq:0\r\nTransport: RTP/AVP;unicast;client_port=18174-0;server_port=\color{rgb(255,0,0)}{1130-1131};ssrc=\color{rgb(0,0,255)}{Wang_qin_feng18174_0}\r\nSession:\color{rgb(0,255,0)}{Wang_qin_feng18174_0}\r\n\r\n"
//红色部分为server rtp-rtcp端口号,蓝色部分和绿色部分为会话唯一标识,类似于http的会话保持.

4.客户端发送PLAY请求

客户端PLAY请求报文
"PLAY rtsp://127.0.0.1:1133 RTSP/1.0\r\nRange: npt=0.000-\r\nCSeq: 4\r\nUser-Agent: Lavf57.83.100\r\nSession: Wang_qin_feng18174_0\r\n\r\n"
服务端返回报文
"RTSP/1.0 200 OK\r\nCSeq: 0\r\nSession:Wang_qin_feng10540_0\r\nRTP-Info: url=rtsp://127.0.0.1:1133;seq=9810092;rtptime=0\r\n\r\n"

5.此时ffplay等待server rtp端口号(1130)发送来的数据.
rtsp的实现:
cache.h全局缓存

#ifndef CACHE_H
#define CACHE_H
#include <QList>
#include <QHostAddress>
#include "rtp.h"
struct rtp_rtcp_struct{
  int rtp; //客户端端口
  int rtcp; //客户端端口
  QHostAddress ip; //客户端地址
};
extern rtp_rtcp_struct s_s_p;
extern QList<rtp_rtcp_struct> client_rtp_rtcp_list;
extern rtp rtp_server;
class cache
{
public:
    cache();
};

#endif // CACHE_H

cache.cpp

#include "cache.h"
#include <QList>

rtp_rtcp_struct s_s_p;
QList<rtp_rtcp_struct> client_rtp_rtcp_list;
rtp rtp_server;
cache::cache()
{

}

rtsp.h

#define RTSP_H
#include <QObject>
#include <QTcpServer>
#include <QTcpSocket>
class rtsp : public QTcpServer
{
    Q_OBJECT
public:
    explicit rtsp(int rtp,int rtcp,QTcpServer *parent = nullptr);
    void incomingConnection(qintptr socket_number);
signals:
public slots:
private:
    int server_rtp;
    int server_rtcp;
};
//客户端连接
class connect_socket :public QTcpSocket{
    Q_OBJECT
public:
    explicit connect_socket(int rtp,int rtcp,qintptr   socket_number,QTcpSocket *parent=nullptr);
    enum return_error{
        no_error=0,
        no_vision= -1,
        no_rtp_rtcp_port=-2,
    };
signals:
private:
    int router(QStringList head); //请求方法路由
    void send_methon();    //返回支持的方法
    void send_rtp_rtcp_session(); //返回会话端口号等信息
    void send_sdp();    //发送sdp文件
    int verison;
    int client_rtp_port; //client 监听rtp的端口号
    int client_rtcp_port; //client 监听的rtcp的端口号
    int server_rtp_port; 
    int server_rtcp_port;
    int cseq=0;
    QByteArray SSRC="Wang_qin_feng";
public slots:
    void read_data();
};
#endif // RTSP_H

rtsp.cpp

#include "rtsp.h"
#include <QTcpServer>
#include <QTcpSocket>
#include <QThread>
#include <QDebug>
#include <QDateTime>
#include <QByteArray>
#include "cache.h"
rtsp::rtsp(int rtp,int rtcp,QTcpServer *parent) : QTcpServer(parent)
{
    server_rtp=rtp; 
    server_rtcp=rtcp;
}
//新的tcp socket连接时创建一个新的连接.
void rtsp::incomingConnection(qintptr socket_number){
 //new一个新的socket连接
    connect_socket *client_socket=new connect_socket(server_rtp,server_rtcp,socket_number);
 //创建一个线程.
    QThread *client_socket_thread=new QThread();
    client_socket->moveToThread(client_socket_thread);
    client_socket_thread->start();
}
connect_socket::connect_socket(int rtp,int rtcp,qintptr socket_number,QTcpSocket *parent):QTcpSocket (parent){
    this->setSocketDescriptor(socket_number);
    server_rtp_port=rtp; //server rtp_port
    server_rtcp_port=rtcp; //server rtcp_port
//有数据传入是调用槽
    connect(this,SIGNAL(readyRead()),this,SLOT(read_data()));
};
void connect_socket::read_data(){ 
    QString tmp_data;
    tmp_data=this->readAll();
    if(tmp_data.size()>50){ //因为vlc有传空请求来!所有判定下长度
        QStringList request_body=tmp_data.split(" ");
//        qDebug()<<tmp_data;
      //按不同的请求路由到不同的处理方法.
        router(request_body);
    }
}
int connect_socket::router(QStringList head){
    return_error error_return;
    error_return=no_error; //正常返回0;
    QString methon=head[0];

    cseq=head[3].split("\r\n")[0].toInt();
    if(methon=="OPTIONS"){ //本版获取
        error_return=no_vision;
            if(head[2].contains("\r\n")){
                QStringList verison_list=head[2].split("\r\n");
                //rtsp获取版本号
                if(verison_list[0].contains("/")){verison=verison_list[0].split("/")[0].toInt();}else {
                    return  error_return;
                    };
                send_methon(); //发送支持的方法给客户端;
            }else {
                //无版本号返回-1;
                error_return=no_vision;
                return error_return;
            }
    }else if (methon=="SETUP") { //setup请求
        error_return=no_rtp_rtcp_port;
        //从请求中取出rtp rtcp端口号
        if(head[head.length()-1].contains("\r\n\r\n")){
            int client_port_index=0;
            for(int i=0;i<head.length();i++){
                if(head[i].contains("=") and head[i].contains("-")){
                    client_port_index=i;
                }
            }
            QStringList port_list=head[client_port_index].split("\r\n\r\n")[0].split("=")[1].split("-");
            client_rtp_port=port_list[0].toInt();
            client_rtcp_port=port_list[1].toInt();
                        //添加客户端端口号到列表
            s_s_p.rtp=client_rtp_port;
            s_s_p.rtcp=client_rtcp_port;
            s_s_p.ip= this->peerAddress();
            client_rtp_rtcp_list.append(s_s_p);
                        //返回会话数据
            send_rtp_rtcp_session();
            }else {
                        return error_return;
                    }
    }else if (methon=="PLAY") {
        QString url;
        if(head.size()>=1){
            url=head[1];
        }
        QByteArray data;
        data.append("RTSP/1.0 200 OK\r\nCSeq: "+QString::number(cseq)+"\r\nSession: "+SSRC+"\r\n"+"RTP-Info: url="+url
                    +";seq=9810092;rtptime=0\r\n\r\n");
        this->write(data);
//        qDebug()<<data;
        this->waitForBytesWritten(3000);
    }else if (methon=="DESCRIBE") {
            send_sdp();
    }else{
    }
    return  error_return;
};
void connect_socket::send_methon(){ //发送支持的方法
    QByteArray data;
    data.append("RTSP/1.0 200 OK\r\n CSeq:"+QString::number(cseq)+"\r\nPublic:DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE\r\n\r\n");
    this->write(data);
    this->waitForBytesWritten(3000);
}
void connect_socket::send_rtp_rtcp_session(){ //发送会话数据
    SSRC+=QString::number(client_rtp_port)+"_"+QString::number(client_rtcp_port);
    QByteArray data;
    data.append("RTSP/1.0 200 OK\r\nCSeq:"+QString::number(cseq)+"\r\nTransport: RTP/AVP;unicast;client_port="+
            QString::number(client_rtp_port)+"-"+QString::number(client_rtcp_port)+";"
            +"server_port="+QString::number(server_rtp_port)+"-"+QString::number(server_rtcp_port)+";"
            +"ssrc="+SSRC+"\r\nSession:"+SSRC+"\r\n\r\n");
//    qDebug()<<data;
    this->write(data);
    this->waitForBytesWritten(3000);
}
void connect_socket::send_sdp(){
    QByteArray tmp_data;
    QByteArray data;
    tmp_data.append("c=IN IP4 "+this->peerAddress().toString().split(":")[3]+"\r\nm=video 0 RTP/AVP 96\r\na=rtpmap:96 H265/90000\r\na=fmtp:96 profile-space=0;profile-id=0;tier-flag=0;level-id=0\r\n\r\n");
    data.append( "RTSP/1.0 200 OK\r\nCSeq:"+QString::number(cseq)+"\r\nContent-Type: application/sdp\r\nContent-Length:"
                 +QString::number(tmp_data.size())+"\r\n\r\n");
    data.append(tmp_data);
//    qDebug()<<tmp_data;
    this->write(data);
    this->waitForBytesWritten(3000);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容