rtsp跟http一樣都是七層應(yīng)用層協(xié)議.在rtsp協(xié)議組中的主要作用是:
1.客戶(hù)端發(fā)送OPTIONS請(qǐng)求:
客戶(hù)端(ffplay)請(qǐng)求報(bào)文:
OPTIONS rtsp://127.0.0.1:1133 RTSP/1.0\r\nCSeq: 1\r\nUser-Agent: Lavf57.83.100\r\n\r\n}$
服務(wù)端響應(yīng)報(bào)文:
RTSP/1.0 200 OK\r\nCSeq:"+QString::number(cseq)+"\r\nPublic:DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE\r\n\r\n
//服務(wù)器返回了服務(wù)器支持的方法;
//注意請(qǐng)求和返回都是\r\n\r\n結(jié)尾。
2.客戶(hù)端發(fā)送DESCRIBE請(qǐng)求
客戶(hù)端發(fā)送DESCRIBE請(qǐng)求
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"
服務(wù)端響應(yīng)客戶(hù)端請(qǐng)求.
"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"
//注釋?zhuān)篴=rtpmap:96 H265/90000 96表示媒體類(lèi)型是視頻预吆,H265/90000表示h265編碼波特率是90000
3.客戶(hù)端SETUP請(qǐng)求:
客戶(hù)端setup請(qǐng)求報(bào)文
"SETUP rtsp://127.0.0.1:1133 RTSP/1.0\r\nTransport: RTP/AVP/UDP;unicast;client_port=\r\nCSeq: 3\r\nUser-Agent: Lavf57.83.100\r\n\r\n"
//紅色為客戶(hù)端監(jiān)聽(tīng)的端口號(hào).
服務(wù)端響應(yīng)報(bào)文
"RTSP/1.0 200 OK\r\nCSeq:0\r\nTransport: RTP/AVP;unicast;client_port=18174-0;server_port=;ssrc=
\r\nSession:
\r\n\r\n"
//紅色部分為server rtp-rtcp端口號(hào)乏梁,藍(lán)色部分和綠色部分為會(huì)話(huà)唯一標(biāo)識(shí),類(lèi)似于http的會(huì)話(huà)保持.
4.客戶(hù)端發(fā)送PLAY請(qǐng)求
客戶(hù)端PLAY請(qǐng)求報(bào)文
"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"
服務(wù)端返回報(bào)文
"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.此時(shí)ffplay等待server rtp端口號(hào)(1130)發(fā)送來(lái)的數(shù)據(jù).
rtsp的實(shí)現(xiàn):
cache.h全局緩存
#ifndef CACHE_H
#define CACHE_H
#include <QList>
#include <QHostAddress>
#include "rtp.h"
struct rtp_rtcp_struct{
int rtp; //客戶(hù)端端口
int rtcp; //客戶(hù)端端口
QHostAddress ip; //客戶(hù)端地址
};
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;
};
//客戶(hù)端連接
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); //請(qǐng)求方法路由
void send_methon(); //返回支持的方法
void send_rtp_rtcp_session(); //返回會(huì)話(huà)端口號(hào)等信息
void send_sdp(); //發(fā)送sdp文件
int verison;
int client_rtp_port; //client 監(jiān)聽(tīng)rtp的端口號(hào)
int client_rtcp_port; //client 監(jiān)聽(tīng)的rtcp的端口號(hào)
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連接時(shí)創(chuàng)建一個(gè)新的連接.
void rtsp::incomingConnection(qintptr socket_number){
//new一個(gè)新的socket連接
connect_socket *client_socket=new connect_socket(server_rtp,server_rtcp,socket_number);
//創(chuàng)建一個(gè)線(xiàn)程.
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
//有數(shù)據(jù)傳入是調(diào)用槽
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){ //因?yàn)関lc有傳空請(qǐng)求來(lái)!所有判定下長(zhǎng)度
QStringList request_body=tmp_data.split(" ");
// qDebug()<<tmp_data;
//按不同的請(qǐng)求路由到不同的處理方法.
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獲取版本號(hào)
if(verison_list[0].contains("/")){verison=verison_list[0].split("/")[0].toInt();}else {
return error_return;
};
send_methon(); //發(fā)送支持的方法給客戶(hù)端;
}else {
//無(wú)版本號(hào)返回-1;
error_return=no_vision;
return error_return;
}
}else if (methon=="SETUP") { //setup請(qǐng)求
error_return=no_rtp_rtcp_port;
//從請(qǐng)求中取出rtp rtcp端口號(hào)
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();
//添加客戶(hù)端端口號(hào)到列表
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);
//返回會(huì)話(huà)數(shù)據(jù)
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(){ //發(fā)送支持的方法
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(){ //發(fā)送會(huì)話(huà)數(shù)據(jù)
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);
}