Commit ee2ca4bd by liuziloong

Merge branch 'master' into research

parents d2d84680 cbd6cd4e
ZLToolKit @ be08f018
Subproject commit 72013f5128d8c04bad8b973370da463c311081c0
Subproject commit be08f01869ebf1391245c24c1e328422eb11ab77
......@@ -70,8 +70,10 @@ API_EXPORT void API_CALL mk_stop_all_server(){
CLEAR_ARR(rtsp_server);
CLEAR_ARR(rtmp_server);
CLEAR_ARR(http_server);
#ifdef ENABLE_RTPPROXY
udpRtpServer = nullptr;
tcpRtpServer = nullptr;
#endif
stopAllTcpServer();
}
......
#!/bin/bash
docker build -t gemfield/zlmediakit:20.01-runtime-ubuntu18.04 -f docker/ubuntu18.04/Dockerfile.runtime .
#docker build -t gemfield/zlmediakit:20.01-devel-ubuntu18.04 -f docker/ubuntu18.04/Dockerfile.devel .
......@@ -9,7 +9,9 @@ EXPOSE 443/tcp
EXPOSE 10000/udp
EXPOSE 10000/tcp
RUN apt-get update && apt-get install -y --no-install-recommends \
RUN apt-get update && \
DEBIAN_FRONTEND="noninteractive" \
apt-get install -y --no-install-recommends \
build-essential \
cmake \
git \
......@@ -22,6 +24,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
libx264-dev \
libfaac-dev \
libmp4v2-dev && \
apt autoremove -y && \
apt clean -y && \
rm -rf /var/lib/apt/lists/*
RUN mkdir -p /opt/media
......@@ -36,5 +40,4 @@ RUN cmake -DCMAKE_BUILD_TYPE=Release .. && \
make -j4
ENV PATH /opt/media/ZLMediaKit/release/linux/Release/:$PATH
CMD MediaServer
FROM ubuntu:16.04 AS build
#shell,rtmp,rtsp,rtsps,http,https,rtp
EXPOSE 9000/tcp
EXPOSE 1935/tcp
EXPOSE 554/tcp
EXPOSE 322/tcp
EXPOSE 80/tcp
EXPOSE 443/tcp
EXPOSE 10000/udp
EXPOSE 10000/tcp
RUN apt-get update && \
DEBIAN_FRONTEND="noninteractive" \
apt-get install -y --no-install-recommends \
build-essential \
cmake \
git \
curl \
vim \
ca-certificates \
tzdata \
libssl-dev \
libmysqlclient-dev \
libx264-dev \
libfaac-dev \
libmp4v2-dev && \
apt autoremove -y && \
apt clean -y && \
rm -rf /var/lib/apt/lists/*
RUN mkdir -p /opt/media
WORKDIR /opt/media
RUN git clone --depth=1 https://github.com/xiongziliang/ZLMediaKit && \
cd ZLMediaKit && git submodule update --init --recursive && \
mkdir -p build release/linux/Release/
WORKDIR /opt/media/ZLMediaKit/build
RUN cmake -DCMAKE_BUILD_TYPE=Release .. && \
make -j4
FROM ubuntu:16.04
LABEL maintainer "Gemfield <gemfield@civilnet.cn>"
RUN apt-get update && \
DEBIAN_FRONTEND="noninteractive" \
apt-get install -y --no-install-recommends \
vim \
ca-certificates \
tzdata \
libssl-dev \
libx264-dev \
libfaac-dev \
libmp4v2-dev && \
apt autoremove -y && \
apt clean -y && \
rm -rf /var/lib/apt/lists/*
WORKDIR /opt/media/bin/
COPY --from=build /opt/media/ZLMediaKit/release/linux/Release/MediaServer /opt/media/bin/MediaServer
ENV PATH /opt/media/bin:$PATH
CMD MediaServer
\ No newline at end of file
FROM ubuntu:18.04
LABEL maintainer "Gemfield <gemfield@civilnet.cn>"
#shell,rtmp,rtsp,rtsps,http,https,rtp
EXPOSE 9000/tcp
EXPOSE 1935/tcp
EXPOSE 554/tcp
EXPOSE 322/tcp
EXPOSE 80/tcp
EXPOSE 443/tcp
EXPOSE 10000/udp
EXPOSE 10000/tcp
RUN apt-get update && \
DEBIAN_FRONTEND="noninteractive" \
apt-get install -y --no-install-recommends \
build-essential \
cmake \
git \
curl \
vim \
ca-certificates \
tzdata \
libssl-dev \
libmysqlclient-dev \
libx264-dev \
libfaac-dev \
libmp4v2-dev && \
apt autoremove -y && \
apt clean -y && \
rm -rf /var/lib/apt/lists/*
RUN mkdir -p /opt/media
WORKDIR /opt/media
RUN git clone --depth=1 https://github.com/xiongziliang/ZLMediaKit && \
cd ZLMediaKit && git submodule update --init --recursive && \
mkdir -p build release/linux/Release/
WORKDIR /opt/media/ZLMediaKit/build
RUN cmake -DCMAKE_BUILD_TYPE=Release .. && \
make -j4
ENV PATH /opt/media/ZLMediaKit/release/linux/Release:$PATH
CMD MediaServer
\ No newline at end of file
FROM ubuntu:18.04 AS build
#shell,rtmp,rtsp,rtsps,http,https,rtp
EXPOSE 9000/tcp
EXPOSE 1935/tcp
EXPOSE 554/tcp
EXPOSE 322/tcp
EXPOSE 80/tcp
EXPOSE 443/tcp
EXPOSE 10000/udp
EXPOSE 10000/tcp
RUN apt-get update && \
DEBIAN_FRONTEND="noninteractive" \
apt-get install -y --no-install-recommends \
build-essential \
cmake \
git \
curl \
vim \
ca-certificates \
tzdata \
libssl-dev \
libmysqlclient-dev \
libx264-dev \
libfaac-dev \
libmp4v2-dev && \
apt autoremove -y && \
apt clean -y && \
rm -rf /var/lib/apt/lists/*
RUN mkdir -p /opt/media
WORKDIR /opt/media
RUN git clone --depth=1 https://github.com/xiongziliang/ZLMediaKit && \
cd ZLMediaKit && git submodule update --init --recursive && \
mkdir -p build release/linux/Release/
WORKDIR /opt/media/ZLMediaKit/build
RUN cmake -DCMAKE_BUILD_TYPE=Release .. && \
make -j4
FROM ubuntu:18.04
LABEL maintainer "Gemfield <gemfield@civilnet.cn>"
RUN apt-get update && \
DEBIAN_FRONTEND="noninteractive" \
apt-get install -y --no-install-recommends \
vim \
ca-certificates \
tzdata \
libssl-dev \
libx264-dev \
libfaac-dev \
libmp4v2-dev && \
apt autoremove -y && \
apt clean -y && \
rm -rf /var/lib/apt/lists/*
WORKDIR /opt/media/bin/
COPY --from=build /opt/media/ZLMediaKit/release/linux/Release/MediaServer /opt/media/bin/MediaServer
ENV PATH /opt/media/bin:$PATH
CMD MediaServer
\ No newline at end of file
......@@ -76,17 +76,24 @@ AACRtmpEncoder::AACRtmpEncoder(const Track::Ptr &track) {
_track = dynamic_pointer_cast<AACTrack>(track);
}
void AACRtmpEncoder::makeConfigPacket() {
if (_track && _track->ready()) {
//从track中和获取aac配置信息
_aac_cfg = _track->getAacCfg();
}
if (!_aac_cfg.empty()) {
makeAudioConfigPkt();
}
}
void AACRtmpEncoder::inputFrame(const Frame::Ptr &frame) {
if(_aac_cfg.empty()){
if(frame->prefixSize() >= 7){
if (_aac_cfg.empty()) {
if (frame->prefixSize() >= 7) {
//包含adts头,从adts头获取aac配置信息
_aac_cfg = makeAdtsConfig(reinterpret_cast<const uint8_t *>(frame->data()));
makeAudioConfigPkt();
} else if(_track && _track->ready()){
//从track中和获取aac配置信息
_aac_cfg = _track->getAacCfg();
makeAudioConfigPkt();
}
makeConfigPacket();
}
if(!_aac_cfg.empty()){
......
......@@ -88,6 +88,10 @@ public:
*/
void inputFrame(const Frame::Ptr &frame) override;
/**
* 生成config包
*/
void makeConfigPacket() override;
private:
void makeAudioConfigPkt();
private:
......
......@@ -99,7 +99,20 @@ inline void H264RtmpDecoder::onGetH264(const char* pcData, int iLen, uint32_t dt
H264RtmpEncoder::H264RtmpEncoder(const Track::Ptr &track) {
_track = dynamic_pointer_cast<H264Track>(track);
}
void H264RtmpEncoder::makeConfigPacket(){
if (_track && _track->ready()) {
//尝试从track中获取sps pps信息
_sps = _track->getSps();
_pps = _track->getPps();
}
if (!_sps.empty() && !_pps.empty()) {
//获取到sps/pps
makeVideoConfigPkt();
_gotSpsPps = true;
}
}
void H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
......@@ -107,37 +120,24 @@ void H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
auto iLen = frame->size() - frame->prefixSize();
auto type = H264_TYPE(((uint8_t*)pcData)[0]);
if(!_gotSpsPps){
if (!_gotSpsPps) {
//尝试从frame中获取sps pps
switch (type){
case H264Frame::NAL_SPS:{
switch (type) {
case H264Frame::NAL_SPS: {
//sps
if(_sps.empty()){
_sps = string(pcData,iLen);
}
}
_sps = string(pcData, iLen);
makeConfigPacket();
break;
case H264Frame::NAL_PPS:{
//pps
if(_pps.empty()){
_pps = string(pcData,iLen);
}
}
case H264Frame::NAL_PPS: {
//pps
_pps = string(pcData, iLen);
makeConfigPacket();
break;
}
default:
break;
}
if(_track && _track->ready()){
//尝试从track中获取sps pps信息
_sps = _track->getSps();
_pps = _track->getPps();
}
if(!_sps.empty() && !_pps.empty()){
_gotSpsPps = true;
makeVideoConfigPkt();
}
}
if(type == H264Frame::NAL_SEI){
......
......@@ -90,6 +90,11 @@ public:
* @param frame 帧数据
*/
void inputFrame(const Frame::Ptr &frame) override;
/**
* 生成config包
*/
void makeConfigPacket() override;
private:
void makeVideoConfigPkt();
private:
......
......@@ -189,9 +189,8 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) {
//该帧最后一个rtp包 FU-A end
_h264frame->_buffer.append((char *)frame + 2, length - 2);
_h264frame->_pts = rtppack->timeStamp;
auto key = _h264frame->keyFrame();
onGetH264(_h264frame);
return key;
return false;
}
default:{
......@@ -209,8 +208,15 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) {
}
void H264RtpDecoder::onGetH264(const H264Frame::Ptr &frame) {
//根据pts计算dts
auto flag = _dts_generator.getDts(frame->_pts,frame->_dts);
if(!flag){
if(frame->configFrame() || frame->keyFrame()){
flag = true;
frame->_dts = frame->_pts;
}
}
//根据pts计算dts
if(flag){
//写入环形缓存
RtpCodec::inputFrame(frame);
......
......@@ -127,9 +127,8 @@ bool H265RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) {
//该帧最后一个rtp包
_h265frame->_buffer.append((char *) frame + 3, length - 3);
_h265frame->_pts = rtppack->timeStamp;
auto key = _h265frame->keyFrame();
onGetH265(_h265frame);
return key;
return false;
}
default: // 4.4.1. Single NAL Unit Packets (p24)
......@@ -146,6 +145,12 @@ bool H265RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) {
void H265RtpDecoder::onGetH265(const H265Frame::Ptr &frame) {
//计算dts
auto flag = _dts_generator.getDts(frame->_pts,frame->_dts);
if(!flag){
if(frame->configFrame() || frame->keyFrame()){
flag = true;
frame->_dts = frame->_pts;
}
}
if(flag){
//写入环形缓存
RtpCodec::inputFrame(frame);
......
......@@ -31,15 +31,79 @@
#include "Network/TcpServer.h"
/**
* 数据发送拦截器
*/
class SendInterceptor{
public:
typedef function<int(const Buffer::Ptr &buf)> onBeforeSendCB;
SendInterceptor() = default;
virtual ~SendInterceptor() = default;
virtual void setOnBeforeSendCB(const onBeforeSendCB &cb) = 0;
};
/**
* 该类实现了TcpSession派生类发送数据的截取
* 目的是发送业务数据前进行websocket协议的打包
*/
template <typename TcpSessionType>
class TcpSessionTypeImp : public TcpSessionType, public SendInterceptor{
public:
typedef std::shared_ptr<TcpSessionTypeImp> Ptr;
TcpSessionTypeImp(const Parser &header, const HttpSession &parent, const Socket::Ptr &pSock) :
_identifier(parent.getIdentifier()), TcpSessionType(pSock) {}
~TcpSessionTypeImp() {}
/**
* 设置发送数据截取回调函数
* @param cb 截取回调函数
*/
void setOnBeforeSendCB(const onBeforeSendCB &cb) override {
_beforeSendCB = cb;
}
protected:
/**
* 重载send函数截取数据
* @param buf 需要截取的数据
* @return 数据字节数
*/
int send(const Buffer::Ptr &buf) override {
if (_beforeSendCB) {
return _beforeSendCB(buf);
}
return TcpSessionType::send(buf);
}
string getIdentifier() const override {
return _identifier;
}
private:
onBeforeSendCB _beforeSendCB;
string _identifier;
};
template <typename TcpSessionType>
class TcpSessionCreator {
public:
//返回的TcpSession必须派生于SendInterceptor,可以返回null
TcpSession::Ptr operator()(const Parser &header, const HttpSession &parent, const Socket::Ptr &pSock){
return std::make_shared<TcpSessionTypeImp<TcpSessionType> >(header,parent,pSock);
}
};
/**
* 通过该模板类可以透明化WebSocket协议,
* 用户只要实现WebSock协议下的具体业务协议,譬如基于WebSocket协议的Rtmp协议等
* @tparam SessionType 业务协议的TcpSession类
*/
template <class SessionType,class HttpSessionType = HttpSession,WebSocketHeader::Type DataType = WebSocketHeader::TEXT>
class WebSocketSession : public HttpSessionType {
template<typename Creator, typename HttpSessionType = HttpSession, WebSocketHeader::Type DataType = WebSocketHeader::TEXT>
class WebSocketSessionBase : public HttpSessionType {
public:
WebSocketSession(const Socket::Ptr &pSock) : HttpSessionType(pSock){}
virtual ~WebSocketSession(){}
WebSocketSessionBase(const Socket::Ptr &pSock) : HttpSessionType(pSock){}
virtual ~WebSocketSessionBase(){}
//收到eof或其他导致脱离TcpServer事件的回调
void onError(const SockException &err) override{
......@@ -69,23 +133,27 @@ protected:
*/
bool onWebSocketConnect(const Parser &header) override{
//创建websocket session类
_session = std::make_shared<SessionImp>(HttpSessionType::getIdentifier(),HttpSessionType::_sock);
_session = _creator(header, *this,HttpSessionType::_sock);
if(!_session){
//此url不允许创建websocket连接
return false;
}
auto strongServer = _weakServer.lock();
if(strongServer){
_session->attachServer(*strongServer);
}
//此处截取数据并进行websocket协议打包
weak_ptr<WebSocketSession> weakSelf = dynamic_pointer_cast<WebSocketSession>(HttpSessionType::shared_from_this());
_session->setOnBeforeSendCB([weakSelf](const Buffer::Ptr &buf){
weak_ptr<WebSocketSessionBase> weakSelf = dynamic_pointer_cast<WebSocketSessionBase>(HttpSessionType::shared_from_this());
dynamic_pointer_cast<SendInterceptor>(_session)->setOnBeforeSendCB([weakSelf](const Buffer::Ptr &buf) {
auto strongSelf = weakSelf.lock();
if(strongSelf){
if (strongSelf) {
WebSocketHeader header;
header._fin = true;
header._reserved = 0;
header._opcode = DataType;
header._mask_flag = false;
strongSelf->WebSocketSplitter::encode(header,buf);
strongSelf->WebSocketSplitter::encode(header, buf);
}
return buf->size();
});
......@@ -156,49 +224,18 @@ protected:
SocketHelper::send(buffer);
}
private:
typedef function<int(const Buffer::Ptr &buf)> onBeforeSendCB;
/**
* 该类实现了TcpSession派生类发送数据的截取
* 目的是发送业务数据前进行websocket协议的打包
*/
class SessionImp : public SessionType{
public:
SessionImp(const string &identifier,const Socket::Ptr &pSock) :
_identifier(identifier),SessionType(pSock){}
~SessionImp(){}
/**
* 设置发送数据截取回调函数
* @param cb 截取回调函数
*/
void setOnBeforeSendCB(const onBeforeSendCB &cb){
_beforeSendCB = cb;
}
protected:
/**
* 重载send函数截取数据
* @param buf 需要截取的数据
* @return 数据字节数
*/
int send(const Buffer::Ptr &buf) override {
if(_beforeSendCB){
return _beforeSendCB(buf);
}
return SessionType::send(buf);
}
string getIdentifier() const override{
return _identifier;
}
private:
onBeforeSendCB _beforeSendCB;
string _identifier;
};
private:
string _remian_data;
weak_ptr<TcpServer> _weakServer;
std::shared_ptr<SessionImp> _session;
TcpSession::Ptr _session;
Creator _creator;
};
template<typename TcpSessionType,typename HttpSessionType = HttpSession,WebSocketHeader::Type DataType = WebSocketHeader::TEXT>
class WebSocketSession : public WebSocketSessionBase<TcpSessionCreator<TcpSessionType>,HttpSessionType,DataType>{
public:
WebSocketSession(const Socket::Ptr &pSock) : WebSocketSessionBase<TcpSessionCreator<TcpSessionType>,HttpSessionType,DataType>(pSock){}
virtual ~WebSocketSession(){}
};
#endif //ZLMEDIAKIT_WEBSOCKETSESSION_H
......@@ -80,6 +80,7 @@ public:
typedef std::shared_ptr<RtmpCodec> Ptr;
RtmpCodec(){}
virtual ~RtmpCodec(){}
virtual void makeConfigPacket() {};
};
......
......@@ -142,7 +142,11 @@ public:
}
strongSelf->onReaderChanged(size);
};
_ring = std::make_shared<RingType>(_ring_size, std::move(lam));
//rtmp包缓存最大允许512个,如果是纯视频(25fps)大概为20秒数据
//但是这个是GOP缓存的上限值,真实的GOP缓存大小等于两个I帧之间的包数的两倍
//而且每次遇到I帧,则会清空GOP缓存,所以真实的GOP缓存远小于此值
_ring = std::make_shared<RingType>(_ring_size,512,std::move(lam));
onReaderChanged(0);
if(_metadata){
......
......@@ -58,6 +58,7 @@ public:
}
void onAllTrackReady(){
makeConfigPacket();
_mediaSouce->setMetaData(getMetadata());
}
......
......@@ -78,6 +78,14 @@ void RtmpMuxer::inputFrame(const Frame::Ptr &frame) {
}
}
void RtmpMuxer::makeConfigPacket(){
for(auto &encoder : _encoder){
if(encoder){
encoder->makeConfigPacket();
}
}
}
const AMFValue &RtmpMuxer::getMetadata() const {
return _metadata;
}
......
......@@ -71,6 +71,11 @@ public:
* 重置所有track
*/
void resetTracks() override ;
/**
* 生成config包
*/
void makeConfigPacket();
private:
RtmpRing::RingType::Ptr _rtmpRing;
AMFValue _metadata;
......
......@@ -85,74 +85,84 @@ string SdpTrack::toString() const {
}
return _printer;
}
static TrackType toTrackType(const string &str) {
if (str == "") {
return TrackTitle;
}
if (str == "video") {
return TrackVideo;
}
if (str == "audio") {
return TrackAudio;
}
return TrackInvalid;
}
void SdpParser::load(const string &sdp) {
_track_map.clear();
string key;
SdpTrack::Ptr track = std::make_shared<SdpTrack>();
{
_track_vec.clear();
string key;
SdpTrack::Ptr track = std::make_shared<SdpTrack>();
auto lines = split(sdp,"\n");
for (auto &line : lines){
trim(line);
if(line.size() < 2 || line[1] != '='){
continue;
}
char opt = line[0];
string opt_val = line.substr(2);
switch (opt){
case 'o':
track->_o = opt_val;
break;
case 's':
track->_s = opt_val;
break;
case 'i':
track->_i = opt_val;
break;
case 'c':
track->_c = opt_val;
break;
case 't':
track->_t = opt_val;
break;
case 'b':
track->_b = opt_val;
break;
case 'm':{
_track_map[key] = track;
track = std::make_shared<SdpTrack>();
key = FindField(opt_val.data(), nullptr," ");
track->_m = opt_val;
auto lines = split(sdp, "\n");
for (auto &line : lines) {
trim(line);
if (line.size() < 2 || line[1] != '=') {
continue;
}
break;
case 'a':{
string attr = FindField(opt_val.data(), nullptr,":");
if(attr.empty()){
track->_attr[opt_val] = "";
}else{
track->_attr[attr] = FindField(opt_val.data(),":", nullptr);
char opt = line[0];
string opt_val = line.substr(2);
switch (opt) {
case 'o':
track->_o = opt_val;
break;
case 's':
track->_s = opt_val;
break;
case 'i':
track->_i = opt_val;
break;
case 'c':
track->_c = opt_val;
break;
case 't':
track->_t = opt_val;
break;
case 'b':
track->_b = opt_val;
break;
case 'm': {
track->_type = toTrackType(key);
_track_vec.emplace_back(track);
track = std::make_shared<SdpTrack>();
key = FindField(opt_val.data(), nullptr, " ");
track->_m = opt_val;
}
break;
case 'a': {
string attr = FindField(opt_val.data(), nullptr, ":");
if (attr.empty()) {
track->_attr[opt_val] = "";
} else {
track->_attr[attr] = FindField(opt_val.data(), ":", nullptr);
}
}
break;
default:
track->_other[opt] = opt_val;
break;
}
break;
default:
track->_other[opt] = opt_val;
break;
}
track->_type = toTrackType(key);
_track_vec.emplace_back(track);
}
_track_map[key] = track;
for (auto &pr : _track_map) {
auto &track = *pr.second;
if (pr.first == "") {
track._type = TrackTitle;
} else if (pr.first == "video") {
track._type = TrackVideo;
} else if (pr.first == "audio") {
track._type = TrackAudio;
} else {
track._type = TrackInvalid;
}
for (auto &track_ptr : _track_vec) {
auto &track = *track_ptr;
auto it = track._attr.find("range");
if (it != track._attr.end()) {
char name[16] = {0}, start[16] = {0}, end[16] = {0};
......@@ -198,9 +208,9 @@ bool SdpParser::available() const {
}
SdpTrack::Ptr SdpParser::getTrack(TrackType type) const {
for (auto &pr : _track_map){
if(pr.second->_type == type){
return pr.second;
for (auto &track : _track_vec){
if(track->_type == type){
return track;
}
}
return nullptr;
......@@ -208,31 +218,42 @@ SdpTrack::Ptr SdpParser::getTrack(TrackType type) const {
vector<SdpTrack::Ptr> SdpParser::getAvailableTrack() const {
vector<SdpTrack::Ptr> ret;
auto video = getTrack(TrackVideo);
if(video){
ret.emplace_back(video);
}
auto audio = getTrack(TrackAudio);
if(audio){
ret.emplace_back(audio);
bool audio_added = false;
bool video_added = false;
for (auto &track : _track_vec){
if(track->_type == TrackAudio ){
if(!audio_added){
ret.emplace_back(track);
audio_added = true;
}
continue;
}
if(track->_type == TrackVideo ){
if(!video_added){
ret.emplace_back(track);
video_added = true;
}
continue;
}
}
return ret;
return std::move(ret);
}
string SdpParser::toString() const {
string title,audio,video;
for(auto &pr : _track_map){
switch (pr.second->_type){
for(auto &track : _track_vec){
switch (track->_type){
case TrackTitle:{
title = pr.second->toString();
title = track->toString();
}
break;
case TrackVideo:{
video = pr.second->toString();
video = track->toString();
}
break;
case TrackAudio:{
audio = pr.second->toString();
audio = track->toString();
}
break;
default:
......
......@@ -122,7 +122,7 @@ public:
vector<SdpTrack::Ptr> getAvailableTrack() const;
string toString() const ;
private:
map<string, SdpTrack::Ptr> _track_map;
vector<SdpTrack::Ptr> _track_vec;
};
/**
......
......@@ -179,7 +179,10 @@ public:
}
strongSelf->onReaderChanged(size);
};
_ring = std::make_shared<RingType>(_ring_size, std::move(lam));
//rtp包缓存最大允许2048个,大概最多3MB数据
//但是这个是GOP缓存的上限值,真实的GOP缓存大小等于两个I帧之间的包数的两倍
//而且每次遇到I帧,则会清空GOP缓存,所以真实的GOP缓存远小于此值
_ring = std::make_shared<RingType>(_ring_size,2048, std::move(lam));
onReaderChanged(0);
if (!_sdp.empty()) {
regist();
......
......@@ -51,6 +51,7 @@ public:
}
void onRecv(const Buffer::Ptr &buffer) override {
//回显数据
send("from EchoSession:");
send(buffer);
}
void onError(const SockException &err) override{
......@@ -62,6 +63,48 @@ public:
}
};
class EchoSessionWithUrl : public TcpSession {
public:
EchoSessionWithUrl(const Socket::Ptr &pSock) : TcpSession(pSock){
DebugL;
}
virtual ~EchoSessionWithUrl(){
DebugL;
}
void attachServer(const TcpServer &server) override{
DebugL << getIdentifier() << " " << TcpSession::getIdentifier();
}
void onRecv(const Buffer::Ptr &buffer) override {
//回显数据
send("from EchoSessionWithUrl:");
send(buffer);
}
void onError(const SockException &err) override{
WarnL << err.what();
}
//每隔一段时间触发,用来做超时管理
void onManager() override{
DebugL;
}
};
/**
* 此对象可以根据websocket 客户端访问的url选择创建不同的对象
*/
struct EchoSessionCreator {
//返回的TcpSession必须派生于SendInterceptor,可以返回null(拒绝连接)
TcpSession::Ptr operator()(const Parser &header, const HttpSession &parent, const Socket::Ptr &pSock) {
// return nullptr;
if (header.Url() == "/") {
return std::make_shared<TcpSessionTypeImp<EchoSession> >(header, parent, pSock);
}
return std::make_shared<TcpSessionTypeImp<EchoSessionWithUrl> >(header, parent, pSock);
}
};
int main(int argc, char *argv[]) {
//设置日志
Logger::Instance().add(std::make_shared<ConsoleChannel>());
......@@ -71,13 +114,19 @@ int main(int argc, char *argv[]) {
TcpServer::Ptr httpSrv(new TcpServer());
//http服务器,支持websocket
httpSrv->start<WebSocketSession<EchoSession,HttpSession>>(80);//默认80
httpSrv->start<WebSocketSessionBase<EchoSessionCreator,HttpSession> >(80);//默认80
TcpServer::Ptr httpsSrv(new TcpServer());
//https服务器,支持websocket
httpsSrv->start<WebSocketSession<EchoSession,HttpsSession>>(443);//默认443
httpsSrv->start<WebSocketSessionBase<EchoSessionCreator,HttpsSession> >(443);//默认443
TcpServer::Ptr httpSrvOld(new TcpServer());
//兼容之前的代码(但是不支持根据url选择生成TcpSession类型)
httpSrvOld->start<WebSocketSession<EchoSession,HttpSession> >(8080);
DebugL << "请打开网页:http://www.websocket-test.com/,进行测试";
DebugL << "连接 ws://127.0.0.1/xxxx,ws://127.0.0.1/ 测试的效果将不同,支持根据url选择不同的处理逻辑";
DebugL << "请打开网页:http://www.websocket-test.com/,连接 ws://127.0.0.1/测试";
//设置退出信号处理函数
static semaphore sem;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论