mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-11-26 12:37:09 +08:00
整理代码
This commit is contained in:
parent
fbd711a6bb
commit
a7e99b9d37
@ -91,14 +91,14 @@ void RtmpPlayer::onErr(const SockException &ex){
|
|||||||
onPlayResult_l(ex, !_play_timer);
|
onPlayResult_l(ex, !_play_timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtmpPlayer::onPlayResult_l(const SockException &ex, bool handshakeCompleted) {
|
void RtmpPlayer::onPlayResult_l(const SockException &ex, bool handshake_done) {
|
||||||
if (ex.getErrCode() == Err_shutdown) {
|
if (ex.getErrCode() == Err_shutdown) {
|
||||||
//主动shutdown的,不触发回调
|
//主动shutdown的,不触发回调
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
WarnL << ex.getErrCode() << " " << ex.what();
|
WarnL << ex.getErrCode() << " " << ex.what();
|
||||||
if (!handshakeCompleted) {
|
if (!handshake_done) {
|
||||||
//开始播放阶段
|
//开始播放阶段
|
||||||
_play_timer.reset();
|
_play_timer.reset();
|
||||||
//是否为性能测试模式
|
//是否为性能测试模式
|
||||||
@ -152,14 +152,14 @@ void RtmpPlayer::onConnect(const SockException &err){
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtmpPlayer::onRecv(const Buffer::Ptr &pBuf){
|
void RtmpPlayer::onRecv(const Buffer::Ptr &buf){
|
||||||
try {
|
try {
|
||||||
if (_benchmark_mode && !_play_timer) {
|
if (_benchmark_mode && !_play_timer) {
|
||||||
//在性能测试模式下,如果rtmp握手完毕后,不再解析rtmp包
|
//在性能测试模式下,如果rtmp握手完毕后,不再解析rtmp包
|
||||||
_rtmp_recv_ticker.resetTime();
|
_rtmp_recv_ticker.resetTime();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onParseRtmp(pBuf->data(), pBuf->size());
|
onParseRtmp(buf->data(), buf->size());
|
||||||
} catch (exception &e) {
|
} catch (exception &e) {
|
||||||
SockException ex(Err_other, e.what());
|
SockException ex(Err_other, e.what());
|
||||||
//定时器_pPlayTimer为空后表明握手结束了
|
//定时器_pPlayTimer为空后表明握手结束了
|
||||||
@ -226,21 +226,21 @@ inline void RtmpPlayer::send_play() {
|
|||||||
addOnStatusCB(fun);
|
addOnStatusCB(fun);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void RtmpPlayer::send_pause(bool bPause) {
|
inline void RtmpPlayer::send_pause(bool pause) {
|
||||||
AMFEncoder enc;
|
AMFEncoder enc;
|
||||||
enc << "pause" << ++_send_req_id << nullptr << bPause;
|
enc << "pause" << ++_send_req_id << nullptr << pause;
|
||||||
sendRequest(MSG_CMD, enc.data());
|
sendRequest(MSG_CMD, enc.data());
|
||||||
auto fun = [this, bPause](AMFValue &val) {
|
auto fun = [this, pause](AMFValue &val) {
|
||||||
//TraceL << "pause onStatus";
|
//TraceL << "pause onStatus";
|
||||||
auto level = val["level"].as_string();
|
auto level = val["level"].as_string();
|
||||||
auto code = val["code"].as_string();
|
auto code = val["code"].as_string();
|
||||||
if (level != "status") {
|
if (level != "status") {
|
||||||
if (!bPause) {
|
if (!pause) {
|
||||||
throw std::runtime_error(StrPrinter << "pause 恢复播放失败:" << level << " " << code << endl);
|
throw std::runtime_error(StrPrinter << "pause 恢复播放失败:" << level << " " << code << endl);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_paused = bPause;
|
_paused = pause;
|
||||||
if (!bPause) {
|
if (!pause) {
|
||||||
onPlayResult_l(SockException(Err_success, "resum rtmp success"), true);
|
onPlayResult_l(SockException(Err_success, "resum rtmp success"), true);
|
||||||
} else {
|
} else {
|
||||||
//暂停播放
|
//暂停播放
|
||||||
@ -251,7 +251,7 @@ inline void RtmpPlayer::send_pause(bool bPause) {
|
|||||||
addOnStatusCB(fun);
|
addOnStatusCB(fun);
|
||||||
|
|
||||||
_beat_timer.reset();
|
_beat_timer.reset();
|
||||||
if (bPause) {
|
if (pause) {
|
||||||
weak_ptr<RtmpPlayer> weakSelf = dynamic_pointer_cast<RtmpPlayer>(shared_from_this());
|
weak_ptr<RtmpPlayer> weakSelf = dynamic_pointer_cast<RtmpPlayer>(shared_from_this());
|
||||||
_beat_timer.reset(new Timer((*this)[kBeatIntervalMS].as<int>() / 1000.0, [weakSelf]() {
|
_beat_timer.reset(new Timer((*this)[kBeatIntervalMS].as<int>() / 1000.0, [weakSelf]() {
|
||||||
auto strongSelf = weakSelf.lock();
|
auto strongSelf = weakSelf.lock();
|
||||||
@ -314,32 +314,32 @@ void RtmpPlayer::onCmd_onMetaData(AMFDecoder &dec) {
|
|||||||
_metadata_got = true;
|
_metadata_got = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtmpPlayer::onStreamDry(uint32_t stream_id) {
|
void RtmpPlayer::onStreamDry(uint32_t stream_index) {
|
||||||
//TraceL << stream_id;
|
//TraceL << stream_index;
|
||||||
onPlayResult_l(SockException(Err_other, "rtmp stream dry"), true);
|
onPlayResult_l(SockException(Err_other, "rtmp stream dry"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtmpPlayer::onMediaData_l(const RtmpPacket::Ptr &packet) {
|
void RtmpPlayer::onMediaData_l(const RtmpPacket::Ptr &chunk_data) {
|
||||||
_rtmp_recv_ticker.resetTime();
|
_rtmp_recv_ticker.resetTime();
|
||||||
if (!_play_timer) {
|
if (!_play_timer) {
|
||||||
//已经触发了onPlayResult事件,直接触发onMediaData事件
|
//已经触发了onPlayResult事件,直接触发onMediaData事件
|
||||||
onMediaData(packet);
|
onMediaData(chunk_data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packet->isCfgFrame()) {
|
if (chunk_data->isCfgFrame()) {
|
||||||
//输入配置帧以便初始化完成各个track
|
//输入配置帧以便初始化完成各个track
|
||||||
onMediaData(packet);
|
onMediaData(chunk_data);
|
||||||
} else {
|
} else {
|
||||||
//先触发onPlayResult事件,这个时候解码器才能初始化完毕
|
//先触发onPlayResult事件,这个时候解码器才能初始化完毕
|
||||||
onPlayResult_l(SockException(Err_success, "play rtmp success"), false);
|
onPlayResult_l(SockException(Err_success, "play rtmp success"), false);
|
||||||
//触发onPlayResult事件后,再把帧数据输入到解码器
|
//触发onPlayResult事件后,再把帧数据输入到解码器
|
||||||
onMediaData(packet);
|
onMediaData(chunk_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void RtmpPlayer::onRtmpChunk(RtmpPacket &chunkData) {
|
void RtmpPlayer::onRtmpChunk(RtmpPacket &chunk_data) {
|
||||||
typedef void (RtmpPlayer::*rtmp_func_ptr)(AMFDecoder &dec);
|
typedef void (RtmpPlayer::*rtmp_func_ptr)(AMFDecoder &dec);
|
||||||
static unordered_map<string, rtmp_func_ptr> s_func_map;
|
static unordered_map<string, rtmp_func_ptr> s_func_map;
|
||||||
static onceToken token([]() {
|
static onceToken token([]() {
|
||||||
@ -349,12 +349,12 @@ void RtmpPlayer::onRtmpChunk(RtmpPacket &chunkData) {
|
|||||||
s_func_map.emplace("onMetaData", &RtmpPlayer::onCmd_onMetaData);
|
s_func_map.emplace("onMetaData", &RtmpPlayer::onCmd_onMetaData);
|
||||||
});
|
});
|
||||||
|
|
||||||
switch (chunkData.type_id) {
|
switch (chunk_data.type_id) {
|
||||||
case MSG_CMD:
|
case MSG_CMD:
|
||||||
case MSG_CMD3:
|
case MSG_CMD3:
|
||||||
case MSG_DATA:
|
case MSG_DATA:
|
||||||
case MSG_DATA3: {
|
case MSG_DATA3: {
|
||||||
AMFDecoder dec(chunkData.buffer, 0);
|
AMFDecoder dec(chunk_data.buffer, 0);
|
||||||
std::string type = dec.load<std::string>();
|
std::string type = dec.load<std::string>();
|
||||||
auto it = s_func_map.find(type);
|
auto it = s_func_map.find(type);
|
||||||
if (it != s_func_map.end()) {
|
if (it != s_func_map.end()) {
|
||||||
@ -368,10 +368,10 @@ void RtmpPlayer::onRtmpChunk(RtmpPacket &chunkData) {
|
|||||||
|
|
||||||
case MSG_AUDIO:
|
case MSG_AUDIO:
|
||||||
case MSG_VIDEO: {
|
case MSG_VIDEO: {
|
||||||
auto idx = chunkData.type_id % 2;
|
auto idx = chunk_data.type_id % 2;
|
||||||
if (_now_stamp_ticker[idx].elapsedTime() > 500) {
|
if (_now_stamp_ticker[idx].elapsedTime() > 500) {
|
||||||
//计算播放进度时间轴用
|
//计算播放进度时间轴用
|
||||||
_now_stamp[idx] = chunkData.time_stamp;
|
_now_stamp[idx] = chunk_data.time_stamp;
|
||||||
}
|
}
|
||||||
if (!_metadata_got) {
|
if (!_metadata_got) {
|
||||||
if (!onCheckMeta(TitleMeta().getMetadata())) {
|
if (!onCheckMeta(TitleMeta().getMetadata())) {
|
||||||
@ -379,7 +379,7 @@ void RtmpPlayer::onRtmpChunk(RtmpPacket &chunkData) {
|
|||||||
}
|
}
|
||||||
_metadata_got = true;
|
_metadata_got = true;
|
||||||
}
|
}
|
||||||
onMediaData_l(std::make_shared<RtmpPacket>(std::move(chunkData)));
|
onMediaData_l(std::make_shared<RtmpPacket>(std::move(chunk_data)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,34 +41,34 @@ public:
|
|||||||
void teardown() override;
|
void teardown() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool onCheckMeta(const AMFValue &val) =0;
|
virtual bool onCheckMeta(const AMFValue &val) = 0;
|
||||||
virtual void onMediaData(const RtmpPacket::Ptr &chunkData) =0;
|
virtual void onMediaData(const RtmpPacket::Ptr &chunk_data) = 0;
|
||||||
uint32_t getProgressMilliSecond() const;
|
uint32_t getProgressMilliSecond() const;
|
||||||
void seekToMilliSecond(uint32_t ms);
|
void seekToMilliSecond(uint32_t ms);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void onMediaData_l(const RtmpPacket::Ptr &chunkData);
|
void onMediaData_l(const RtmpPacket::Ptr &chunk_data);
|
||||||
//在获取config帧后才触发onPlayResult_l(而不是收到play命令回复),所以此时所有track都初始化完毕了
|
//在获取config帧后才触发onPlayResult_l(而不是收到play命令回复),所以此时所有track都初始化完毕了
|
||||||
void onPlayResult_l(const SockException &ex, bool handshakeCompleted);
|
void onPlayResult_l(const SockException &ex, bool handshake_done);
|
||||||
|
|
||||||
//form Tcpclient
|
//form Tcpclient
|
||||||
void onRecv(const Buffer::Ptr &pBuf) override;
|
void onRecv(const Buffer::Ptr &buf) override;
|
||||||
void onConnect(const SockException &err) override;
|
void onConnect(const SockException &err) override;
|
||||||
void onErr(const SockException &ex) override;
|
void onErr(const SockException &ex) override;
|
||||||
//from RtmpProtocol
|
//from RtmpProtocol
|
||||||
void onRtmpChunk(RtmpPacket &chunkData) override;
|
void onRtmpChunk(RtmpPacket &chunk_data) override;
|
||||||
void onStreamDry(uint32_t ui32StreamId) override;
|
void onStreamDry(uint32_t stream_index) override;
|
||||||
void onSendRawData(const Buffer::Ptr &buffer) override{
|
void onSendRawData(const Buffer::Ptr &buffer) override {
|
||||||
send(buffer);
|
send(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FUNC>
|
template<typename FUNC>
|
||||||
inline void addOnResultCB(const FUNC &func) {
|
void addOnResultCB(const FUNC &func) {
|
||||||
lock_guard<recursive_mutex> lck(_mtx_on_result);
|
lock_guard<recursive_mutex> lck(_mtx_on_result);
|
||||||
_map_on_result.emplace(_send_req_id, func);
|
_map_on_result.emplace(_send_req_id, func);
|
||||||
}
|
}
|
||||||
template<typename FUNC>
|
template<typename FUNC>
|
||||||
inline void addOnStatusCB(const FUNC &func) {
|
void addOnStatusCB(const FUNC &func) {
|
||||||
lock_guard<recursive_mutex> lck(_mtx_on_status);
|
lock_guard<recursive_mutex> lck(_mtx_on_status);
|
||||||
_deque_on_status.emplace_back(func);
|
_deque_on_status.emplace_back(func);
|
||||||
}
|
}
|
||||||
@ -77,10 +77,10 @@ protected:
|
|||||||
void onCmd_onStatus(AMFDecoder &dec);
|
void onCmd_onStatus(AMFDecoder &dec);
|
||||||
void onCmd_onMetaData(AMFDecoder &dec);
|
void onCmd_onMetaData(AMFDecoder &dec);
|
||||||
|
|
||||||
inline void send_connect();
|
void send_connect();
|
||||||
inline void send_createStream();
|
void send_createStream();
|
||||||
inline void send_play();
|
void send_play();
|
||||||
inline void send_pause(bool bPause);
|
void send_pause(bool pause);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
string _app;
|
string _app;
|
||||||
|
@ -15,11 +15,6 @@
|
|||||||
#include "Thread/ThreadPool.h"
|
#include "Thread/ThreadPool.h"
|
||||||
using namespace toolkit;
|
using namespace toolkit;
|
||||||
|
|
||||||
#ifdef ENABLE_OPENSSL
|
|
||||||
#include "Util/SSLBox.h"
|
|
||||||
#include <openssl/hmac.h>
|
|
||||||
#include <openssl/opensslv.h>
|
|
||||||
|
|
||||||
#define C1_DIGEST_SIZE 32
|
#define C1_DIGEST_SIZE 32
|
||||||
#define C1_KEY_SIZE 128
|
#define C1_KEY_SIZE 128
|
||||||
#define C1_SCHEMA_SIZE 764
|
#define C1_SCHEMA_SIZE 764
|
||||||
@ -29,6 +24,11 @@ using namespace toolkit;
|
|||||||
#define S2_FMS_KEY_SIZE 68
|
#define S2_FMS_KEY_SIZE 68
|
||||||
#define C1_OFFSET_SIZE 4
|
#define C1_OFFSET_SIZE 4
|
||||||
|
|
||||||
|
#ifdef ENABLE_OPENSSL
|
||||||
|
#include "Util/SSLBox.h"
|
||||||
|
#include <openssl/hmac.h>
|
||||||
|
#include <openssl/opensslv.h>
|
||||||
|
|
||||||
static string openssl_HMACsha256(const void *key, unsigned int key_len, const void *data,unsigned int data_len){
|
static string openssl_HMACsha256(const void *key, unsigned int key_len, const void *data,unsigned int data_len){
|
||||||
std::shared_ptr<char> out(new char[32], [](char *ptr) { delete[] ptr; });
|
std::shared_ptr<char> out(new char[32], [](char *ptr) { delete[] ptr; });
|
||||||
unsigned int out_len;
|
unsigned int out_len;
|
||||||
|
@ -18,141 +18,147 @@ using namespace mediakit::Client;
|
|||||||
|
|
||||||
namespace mediakit {
|
namespace mediakit {
|
||||||
|
|
||||||
RtmpPusher::RtmpPusher(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr &src) : TcpClient(poller){
|
RtmpPusher::RtmpPusher(const EventPoller::Ptr &poller, const RtmpMediaSource::Ptr &src) : TcpClient(poller){
|
||||||
_pMediaSrc=src;
|
_publish_src = src;
|
||||||
}
|
}
|
||||||
|
|
||||||
RtmpPusher::~RtmpPusher() {
|
RtmpPusher::~RtmpPusher() {
|
||||||
teardown();
|
teardown();
|
||||||
DebugL << endl;
|
DebugL << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtmpPusher::teardown() {
|
void RtmpPusher::teardown() {
|
||||||
if (alive()) {
|
if (alive()) {
|
||||||
_strApp.clear();
|
_app.clear();
|
||||||
_strStream.clear();
|
_stream_id.clear();
|
||||||
_strTcUrl.clear();
|
_tc_url.clear();
|
||||||
{
|
{
|
||||||
lock_guard<recursive_mutex> lck(_mtxOnResultCB);
|
lock_guard<recursive_mutex> lck(_mtx_on_result);
|
||||||
_mapOnResultCB.clear();
|
_map_on_result.clear();
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
lock_guard<recursive_mutex> lck(_mtxOnStatusCB);
|
lock_guard<recursive_mutex> lck(_mtx_on_status);
|
||||||
_dqOnStatusCB.clear();
|
_deque_on_status.clear();
|
||||||
}
|
}
|
||||||
_pPublishTimer.reset();
|
_publish_timer.reset();
|
||||||
reset();
|
reset();
|
||||||
shutdown(SockException(Err_shutdown,"teardown"));
|
shutdown(SockException(Err_shutdown, "teardown"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtmpPusher::onPublishResult(const SockException &ex,bool handshakeCompleted) {
|
void RtmpPusher::onPublishResult(const SockException &ex, bool handshake_done) {
|
||||||
if(!handshakeCompleted){
|
if (ex.getErrCode() == Err_shutdown) {
|
||||||
|
//主动shutdown的,不触发回调
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!handshake_done) {
|
||||||
//播放结果回调
|
//播放结果回调
|
||||||
_pPublishTimer.reset();
|
_publish_timer.reset();
|
||||||
if(_onPublished){
|
if (_on_published) {
|
||||||
_onPublished(ex);
|
_on_published(ex);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//播放成功后异常断开回调
|
//播放成功后异常断开回调
|
||||||
if(_onShutdown){
|
if (_on_shutdown) {
|
||||||
_onShutdown(ex);
|
_on_shutdown(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ex){
|
if (ex) {
|
||||||
teardown();
|
teardown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtmpPusher::publish(const string &strUrl) {
|
void RtmpPusher::publish(const string &url) {
|
||||||
teardown();
|
teardown();
|
||||||
string strHost = FindField(strUrl.data(), "://", "/");
|
string host_url = FindField(url.data(), "://", "/");
|
||||||
_strApp = FindField(strUrl.data(), (strHost + "/").data(), "/");
|
_app = FindField(url.data(), (host_url + "/").data(), "/");
|
||||||
_strStream = FindField(strUrl.data(), (strHost + "/" + _strApp + "/").data(), NULL);
|
_stream_id = FindField(url.data(), (host_url + "/" + _app + "/").data(), NULL);
|
||||||
_strTcUrl = string("rtmp://") + strHost + "/" + _strApp;
|
_tc_url = string("rtmp://") + host_url + "/" + _app;
|
||||||
|
|
||||||
if (!_strApp.size() || !_strStream.size()) {
|
if (!_app.size() || !_stream_id.size()) {
|
||||||
onPublishResult(SockException(Err_other,"rtmp url非法"),false);
|
onPublishResult(SockException(Err_other, "rtmp url非法"), false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DebugL << strHost << " " << _strApp << " " << _strStream;
|
DebugL << host_url << " " << _app << " " << _stream_id;
|
||||||
|
|
||||||
auto iPort = atoi(FindField(strHost.data(), ":", NULL).data());
|
auto iPort = atoi(FindField(host_url.data(), ":", NULL).data());
|
||||||
if (iPort <= 0) {
|
if (iPort <= 0) {
|
||||||
//rtmp 默认端口1935
|
//rtmp 默认端口1935
|
||||||
iPort = 1935;
|
iPort = 1935;
|
||||||
} else {
|
} else {
|
||||||
//服务器域名
|
//服务器域名
|
||||||
strHost = FindField(strHost.data(), NULL, ":");
|
host_url = FindField(host_url.data(), NULL, ":");
|
||||||
}
|
}
|
||||||
|
|
||||||
weak_ptr<RtmpPusher> weakSelf = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
|
weak_ptr<RtmpPusher> weakSelf = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
|
||||||
float publishTimeOutSec = (*this)[kTimeoutMS].as<int>() / 1000.0;
|
float publishTimeOutSec = (*this)[kTimeoutMS].as<int>() / 1000.0;
|
||||||
_pPublishTimer.reset( new Timer(publishTimeOutSec, [weakSelf]() {
|
_publish_timer.reset(new Timer(publishTimeOutSec, [weakSelf]() {
|
||||||
auto strongSelf=weakSelf.lock();
|
auto strongSelf = weakSelf.lock();
|
||||||
if(!strongSelf) {
|
if (!strongSelf) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
strongSelf->onPublishResult(SockException(Err_timeout,"publish rtmp timeout"), false);
|
strongSelf->onPublishResult(SockException(Err_timeout, "publish rtmp timeout"), false);
|
||||||
return false;
|
return false;
|
||||||
},getPoller()));
|
}, getPoller()));
|
||||||
|
|
||||||
if(!(*this)[kNetAdapter].empty()){
|
if (!(*this)[kNetAdapter].empty()) {
|
||||||
setNetAdapter((*this)[kNetAdapter]);
|
setNetAdapter((*this)[kNetAdapter]);
|
||||||
}
|
}
|
||||||
|
|
||||||
startConnect(strHost, iPort);
|
startConnect(host_url, iPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtmpPusher::onErr(const SockException &ex){
|
void RtmpPusher::onErr(const SockException &ex){
|
||||||
//定时器_pPublishTimer为空后表明握手结束了
|
//定时器_pPublishTimer为空后表明握手结束了
|
||||||
onPublishResult(ex,!_pPublishTimer);
|
onPublishResult(ex, !_publish_timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtmpPusher::onConnect(const SockException &err){
|
void RtmpPusher::onConnect(const SockException &err){
|
||||||
if(err) {
|
if (err) {
|
||||||
onPublishResult(err,false);
|
onPublishResult(err, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//推流器不需要多大的接收缓存,节省内存占用
|
//推流器不需要多大的接收缓存,节省内存占用
|
||||||
_sock->setReadBuffer(std::make_shared<BufferRaw>(1 * 1024));
|
_sock->setReadBuffer(std::make_shared<BufferRaw>(1 * 1024));
|
||||||
|
|
||||||
weak_ptr<RtmpPusher> weakSelf = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
|
weak_ptr<RtmpPusher> weak_self = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
|
||||||
startClientSession([weakSelf](){
|
startClientSession([weak_self]() {
|
||||||
auto strongSelf=weakSelf.lock();
|
auto strong_self = weak_self.lock();
|
||||||
if(!strongSelf) {
|
if (!strong_self) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf->sendChunkSize(60000);
|
strong_self->sendChunkSize(60000);
|
||||||
strongSelf->send_connect();
|
strong_self->send_connect();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
void RtmpPusher::onRecv(const Buffer::Ptr &pBuf){
|
|
||||||
|
void RtmpPusher::onRecv(const Buffer::Ptr &buf){
|
||||||
try {
|
try {
|
||||||
onParseRtmp(pBuf->data(), pBuf->size());
|
onParseRtmp(buf->data(), buf->size());
|
||||||
} catch (exception &e) {
|
} catch (exception &e) {
|
||||||
SockException ex(Err_other, e.what());
|
SockException ex(Err_other, e.what());
|
||||||
//定时器_pPublishTimer为空后表明握手结束了
|
//定时器_pPublishTimer为空后表明握手结束了
|
||||||
onPublishResult(ex,!_pPublishTimer);
|
onPublishResult(ex, !_publish_timer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline void RtmpPusher::send_connect() {
|
inline void RtmpPusher::send_connect() {
|
||||||
AMFValue obj(AMF_OBJECT);
|
AMFValue obj(AMF_OBJECT);
|
||||||
obj.set("app", _strApp);
|
obj.set("app", _app);
|
||||||
obj.set("type", "nonprivate");
|
obj.set("type", "nonprivate");
|
||||||
obj.set("tcUrl", _strTcUrl);
|
obj.set("tcUrl", _tc_url);
|
||||||
obj.set("swfUrl", _strTcUrl);
|
obj.set("swfUrl", _tc_url);
|
||||||
sendInvoke("connect", obj);
|
sendInvoke("connect", obj);
|
||||||
addOnResultCB([this](AMFDecoder &dec){
|
addOnResultCB([this](AMFDecoder &dec) {
|
||||||
//TraceL << "connect result";
|
//TraceL << "connect result";
|
||||||
dec.load<AMFValue>();
|
dec.load<AMFValue>();
|
||||||
auto val = dec.load<AMFValue>();
|
auto val = dec.load<AMFValue>();
|
||||||
auto level = val["level"].as_string();
|
auto level = val["level"].as_string();
|
||||||
auto code = val["code"].as_string();
|
auto code = val["code"].as_string();
|
||||||
if(level != "status"){
|
if (level != "status") {
|
||||||
throw std::runtime_error(StrPrinter <<"connect 失败:" << level << " " << code << endl);
|
throw std::runtime_error(StrPrinter << "connect 失败:" << level << " " << code << endl);
|
||||||
}
|
}
|
||||||
send_createStream();
|
send_createStream();
|
||||||
});
|
});
|
||||||
@ -161,23 +167,24 @@ inline void RtmpPusher::send_connect() {
|
|||||||
inline void RtmpPusher::send_createStream() {
|
inline void RtmpPusher::send_createStream() {
|
||||||
AMFValue obj(AMF_NULL);
|
AMFValue obj(AMF_NULL);
|
||||||
sendInvoke("createStream", obj);
|
sendInvoke("createStream", obj);
|
||||||
addOnResultCB([this](AMFDecoder &dec){
|
addOnResultCB([this](AMFDecoder &dec) {
|
||||||
//TraceL << "createStream result";
|
//TraceL << "createStream result";
|
||||||
dec.load<AMFValue>();
|
dec.load<AMFValue>();
|
||||||
_stream_index = dec.load<int>();
|
_stream_index = dec.load<int>();
|
||||||
send_publish();
|
send_publish();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void RtmpPusher::send_publish() {
|
inline void RtmpPusher::send_publish() {
|
||||||
AMFEncoder enc;
|
AMFEncoder enc;
|
||||||
enc << "publish" << ++_send_req_id << nullptr << _strStream << _strApp ;
|
enc << "publish" << ++_send_req_id << nullptr << _stream_id << _app;
|
||||||
sendRequest(MSG_CMD, enc.data());
|
sendRequest(MSG_CMD, enc.data());
|
||||||
|
|
||||||
addOnStatusCB([this](AMFValue &val) {
|
addOnStatusCB([this](AMFValue &val) {
|
||||||
auto level = val["level"].as_string();
|
auto level = val["level"].as_string();
|
||||||
auto code = val["code"].as_string();
|
auto code = val["code"].as_string();
|
||||||
if(level != "status") {
|
if (level != "status") {
|
||||||
throw std::runtime_error(StrPrinter <<"publish 失败:" << level << " " << code << endl);
|
throw std::runtime_error(StrPrinter << "publish 失败:" << level << " " << code << endl);
|
||||||
}
|
}
|
||||||
//start send media
|
//start send media
|
||||||
send_metaData();
|
send_metaData();
|
||||||
@ -185,7 +192,7 @@ inline void RtmpPusher::send_publish() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline void RtmpPusher::send_metaData(){
|
inline void RtmpPusher::send_metaData(){
|
||||||
auto src = _pMediaSrc.lock();
|
auto src = _publish_src.lock();
|
||||||
if (!src) {
|
if (!src) {
|
||||||
throw std::runtime_error("the media source was released");
|
throw std::runtime_error("the media source was released");
|
||||||
}
|
}
|
||||||
@ -194,42 +201,42 @@ inline void RtmpPusher::send_metaData(){
|
|||||||
enc << "@setDataFrame" << "onMetaData" << src->getMetaData();
|
enc << "@setDataFrame" << "onMetaData" << src->getMetaData();
|
||||||
sendRequest(MSG_DATA, enc.data());
|
sendRequest(MSG_DATA, enc.data());
|
||||||
|
|
||||||
src->getConfigFrame([&](const RtmpPacket::Ptr &pkt){
|
src->getConfigFrame([&](const RtmpPacket::Ptr &pkt) {
|
||||||
sendRtmp(pkt->type_id, _stream_index, pkt, pkt->time_stamp, pkt->chunk_id );
|
sendRtmp(pkt->type_id, _stream_index, pkt, pkt->time_stamp, pkt->chunk_id);
|
||||||
});
|
});
|
||||||
|
|
||||||
_pRtmpReader = src->getRing()->attach(getPoller());
|
_rtmp_reader = src->getRing()->attach(getPoller());
|
||||||
weak_ptr<RtmpPusher> weakSelf = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
|
weak_ptr<RtmpPusher> weak_self = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
|
||||||
_pRtmpReader->setReadCB([weakSelf](const RtmpMediaSource::RingDataType &pkt){
|
_rtmp_reader->setReadCB([weak_self](const RtmpMediaSource::RingDataType &pkt) {
|
||||||
auto strongSelf = weakSelf.lock();
|
auto strong_self = weak_self.lock();
|
||||||
if(!strongSelf) {
|
if (!strong_self) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int size = pkt->size();
|
int size = pkt->size();
|
||||||
strongSelf->setSendFlushFlag(false);
|
strong_self->setSendFlushFlag(false);
|
||||||
pkt->for_each([&](const RtmpPacket::Ptr &rtmp){
|
pkt->for_each([&](const RtmpPacket::Ptr &rtmp) {
|
||||||
if(++i == size){
|
if (++i == size) {
|
||||||
strongSelf->setSendFlushFlag(true);
|
strong_self->setSendFlushFlag(true);
|
||||||
}
|
}
|
||||||
strongSelf->sendRtmp(rtmp->type_id, strongSelf->_stream_index, rtmp, rtmp->time_stamp, rtmp->chunk_id);
|
strong_self->sendRtmp(rtmp->type_id, strong_self->_stream_index, rtmp, rtmp->time_stamp, rtmp->chunk_id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
_pRtmpReader->setDetachCB([weakSelf](){
|
_rtmp_reader->setDetachCB([weak_self]() {
|
||||||
auto strongSelf = weakSelf.lock();
|
auto strong_self = weak_self.lock();
|
||||||
if(strongSelf){
|
if (strong_self) {
|
||||||
strongSelf->onPublishResult(SockException(Err_other,"媒体源被释放"), !strongSelf->_pPublishTimer);
|
strong_self->onPublishResult(SockException(Err_other, "媒体源被释放"), !strong_self->_publish_timer);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
onPublishResult(SockException(Err_success,"success"), false);
|
onPublishResult(SockException(Err_success, "success"), false);
|
||||||
//提升发送性能
|
//提升发送性能
|
||||||
setSocketFlags();
|
setSocketFlags();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtmpPusher::setSocketFlags(){
|
void RtmpPusher::setSocketFlags(){
|
||||||
GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS);
|
GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS);
|
||||||
if(mergeWriteMS > 0) {
|
if (mergeWriteMS > 0) {
|
||||||
//提高发送性能
|
//提高发送性能
|
||||||
setSendFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE);
|
setSendFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE);
|
||||||
SockUtil::setNoDelay(_sock->rawFD(), false);
|
SockUtil::setNoDelay(_sock->rawFD(), false);
|
||||||
@ -237,68 +244,70 @@ void RtmpPusher::setSocketFlags(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RtmpPusher::onCmd_result(AMFDecoder &dec){
|
void RtmpPusher::onCmd_result(AMFDecoder &dec){
|
||||||
auto iReqId = dec.load<int>();
|
auto req_id = dec.load<int>();
|
||||||
lock_guard<recursive_mutex> lck(_mtxOnResultCB);
|
lock_guard<recursive_mutex> lck(_mtx_on_result);
|
||||||
auto it = _mapOnResultCB.find(iReqId);
|
auto it = _map_on_result.find(req_id);
|
||||||
if(it != _mapOnResultCB.end()){
|
if (it != _map_on_result.end()) {
|
||||||
it->second(dec);
|
it->second(dec);
|
||||||
_mapOnResultCB.erase(it);
|
_map_on_result.erase(it);
|
||||||
}else{
|
} else {
|
||||||
WarnL << "unhandled _result";
|
WarnL << "unhandled _result";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtmpPusher::onCmd_onStatus(AMFDecoder &dec) {
|
void RtmpPusher::onCmd_onStatus(AMFDecoder &dec) {
|
||||||
AMFValue val;
|
AMFValue val;
|
||||||
while(true){
|
while (true) {
|
||||||
val = dec.load<AMFValue>();
|
val = dec.load<AMFValue>();
|
||||||
if(val.type() == AMF_OBJECT){
|
if (val.type() == AMF_OBJECT) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(val.type() != AMF_OBJECT){
|
if (val.type() != AMF_OBJECT) {
|
||||||
throw std::runtime_error("onStatus:the result object was not found");
|
throw std::runtime_error("onStatus:the result object was not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
lock_guard<recursive_mutex> lck(_mtxOnStatusCB);
|
lock_guard<recursive_mutex> lck(_mtx_on_status);
|
||||||
if(_dqOnStatusCB.size()){
|
if (_deque_on_status.size()) {
|
||||||
_dqOnStatusCB.front()(val);
|
_deque_on_status.front()(val);
|
||||||
_dqOnStatusCB.pop_front();
|
_deque_on_status.pop_front();
|
||||||
}else{
|
} else {
|
||||||
auto level = val["level"];
|
auto level = val["level"];
|
||||||
auto code = val["code"].as_string();
|
auto code = val["code"].as_string();
|
||||||
if(level.type() == AMF_STRING){
|
if (level.type() == AMF_STRING) {
|
||||||
if(level.as_string() != "status"){
|
if (level.as_string() != "status") {
|
||||||
throw std::runtime_error(StrPrinter <<"onStatus 失败:" << level.as_string() << " " << code << endl);
|
throw std::runtime_error(StrPrinter << "onStatus 失败:" << level.as_string() << " " << code << endl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtmpPusher::onRtmpChunk(RtmpPacket &chunkData) {
|
void RtmpPusher::onRtmpChunk(RtmpPacket &chunk_data) {
|
||||||
switch (chunkData.type_id) {
|
switch (chunk_data.type_id) {
|
||||||
case MSG_CMD:
|
case MSG_CMD:
|
||||||
case MSG_CMD3: {
|
case MSG_CMD3: {
|
||||||
typedef void (RtmpPusher::*rtmpCMDHandle)(AMFDecoder &dec);
|
typedef void (RtmpPusher::*rtmpCMDHandle)(AMFDecoder &dec);
|
||||||
static unordered_map<string, rtmpCMDHandle> g_mapCmd;
|
static unordered_map<string, rtmpCMDHandle> g_mapCmd;
|
||||||
static onceToken token([]() {
|
static onceToken token([]() {
|
||||||
g_mapCmd.emplace("_error",&RtmpPusher::onCmd_result);
|
g_mapCmd.emplace("_error", &RtmpPusher::onCmd_result);
|
||||||
g_mapCmd.emplace("_result",&RtmpPusher::onCmd_result);
|
g_mapCmd.emplace("_result", &RtmpPusher::onCmd_result);
|
||||||
g_mapCmd.emplace("onStatus",&RtmpPusher::onCmd_onStatus);
|
g_mapCmd.emplace("onStatus", &RtmpPusher::onCmd_onStatus);
|
||||||
});
|
});
|
||||||
|
|
||||||
AMFDecoder dec(chunkData.buffer, 0);
|
AMFDecoder dec(chunk_data.buffer, 0);
|
||||||
std::string type = dec.load<std::string>();
|
std::string type = dec.load<std::string>();
|
||||||
auto it = g_mapCmd.find(type);
|
auto it = g_mapCmd.find(type);
|
||||||
if(it != g_mapCmd.end()){
|
if (it != g_mapCmd.end()) {
|
||||||
auto fun = it->second;
|
auto fun = it->second;
|
||||||
(this->*fun)(dec);
|
(this->*fun)(dec);
|
||||||
}else{
|
} else {
|
||||||
WarnL << "can not support cmd:" << type;
|
WarnL << "can not support cmd:" << type;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
//WarnL << "unhandled message:" << (int) chunkData.type_id << hexdump(chunkData.buffer.data(), chunkData.buffer.size());
|
//WarnL << "unhandled message:" << (int) chunk_data.type_id << hexdump(chunk_data.buffer.data(), chunk_data.buffer.size());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,46 +18,47 @@
|
|||||||
|
|
||||||
namespace mediakit {
|
namespace mediakit {
|
||||||
|
|
||||||
class RtmpPusher: public RtmpProtocol , public TcpClient , public PusherBase{
|
class RtmpPusher : public RtmpProtocol, public TcpClient, public PusherBase {
|
||||||
public:
|
public:
|
||||||
typedef std::shared_ptr<RtmpPusher> Ptr;
|
typedef std::shared_ptr<RtmpPusher> Ptr;
|
||||||
RtmpPusher(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr &src);
|
RtmpPusher(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr &src);
|
||||||
virtual ~RtmpPusher();
|
~RtmpPusher() override;
|
||||||
|
|
||||||
void publish(const string &strUrl) override ;
|
|
||||||
|
|
||||||
|
void publish(const string &url) override ;
|
||||||
void teardown() override;
|
void teardown() override;
|
||||||
|
|
||||||
void setOnPublished(const Event &cb) override {
|
void setOnPublished(const Event &cb) override {
|
||||||
_onPublished = cb;
|
_on_published = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setOnShutdown(const Event &cb) override{
|
void setOnShutdown(const Event &cb) override{
|
||||||
_onShutdown = cb;
|
_on_shutdown = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//for Tcpclient override
|
//for Tcpclient override
|
||||||
void onRecv(const Buffer::Ptr &pBuf) override;
|
void onRecv(const Buffer::Ptr &buf) override;
|
||||||
void onConnect(const SockException &err) override;
|
void onConnect(const SockException &err) override;
|
||||||
void onErr(const SockException &ex) override;
|
void onErr(const SockException &ex) override;
|
||||||
|
|
||||||
//for RtmpProtocol override
|
//for RtmpProtocol override
|
||||||
void onRtmpChunk(RtmpPacket &chunkData) override;
|
void onRtmpChunk(RtmpPacket &chunk_data) override;
|
||||||
void onSendRawData(const Buffer::Ptr &buffer) override{
|
void onSendRawData(const Buffer::Ptr &buffer) override{
|
||||||
send(buffer);
|
send(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onPublishResult(const SockException &ex,bool handshakeCompleted);
|
void onPublishResult(const SockException &ex, bool handshake_done);
|
||||||
|
|
||||||
template<typename FUN>
|
template<typename FUN>
|
||||||
inline void addOnResultCB(const FUN &fun) {
|
inline void addOnResultCB(const FUN &fun) {
|
||||||
lock_guard<recursive_mutex> lck(_mtxOnResultCB);
|
lock_guard<recursive_mutex> lck(_mtx_on_result);
|
||||||
_mapOnResultCB.emplace(_send_req_id, fun);
|
_map_on_result.emplace(_send_req_id, fun);
|
||||||
}
|
}
|
||||||
template<typename FUN>
|
template<typename FUN>
|
||||||
inline void addOnStatusCB(const FUN &fun) {
|
inline void addOnStatusCB(const FUN &fun) {
|
||||||
lock_guard<recursive_mutex> lck(_mtxOnStatusCB);
|
lock_guard<recursive_mutex> lck(_mtx_on_status);
|
||||||
_dqOnStatusCB.emplace_back(fun);
|
_deque_on_status.emplace_back(fun);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onCmd_result(AMFDecoder &dec);
|
void onCmd_result(AMFDecoder &dec);
|
||||||
@ -69,23 +70,25 @@ private:
|
|||||||
inline void send_publish();
|
inline void send_publish();
|
||||||
inline void send_metaData();
|
inline void send_metaData();
|
||||||
void setSocketFlags();
|
void setSocketFlags();
|
||||||
private:
|
|
||||||
string _strApp;
|
|
||||||
string _strStream;
|
|
||||||
string _strTcUrl;
|
|
||||||
|
|
||||||
unordered_map<int, function<void(AMFDecoder &dec)> > _mapOnResultCB;
|
private:
|
||||||
recursive_mutex _mtxOnResultCB;
|
string _app;
|
||||||
deque<function<void(AMFValue &dec)> > _dqOnStatusCB;
|
string _stream_id;
|
||||||
recursive_mutex _mtxOnStatusCB;
|
string _tc_url;
|
||||||
//超时功能实现
|
|
||||||
std::shared_ptr<Timer> _pPublishTimer;
|
recursive_mutex _mtx_on_result;
|
||||||
//源
|
recursive_mutex _mtx_on_status;
|
||||||
std::weak_ptr<RtmpMediaSource> _pMediaSrc;
|
deque<function<void(AMFValue &dec)> > _deque_on_status;
|
||||||
RtmpMediaSource::RingType::RingReader::Ptr _pRtmpReader;
|
unordered_map<int, function<void(AMFDecoder &dec)> > _map_on_result;
|
||||||
|
|
||||||
//事件监听
|
//事件监听
|
||||||
Event _onShutdown;
|
Event _on_shutdown;
|
||||||
Event _onPublished;
|
Event _on_published;
|
||||||
|
|
||||||
|
//推流超时定时器
|
||||||
|
std::shared_ptr<Timer> _publish_timer;
|
||||||
|
std::weak_ptr<RtmpMediaSource> _publish_src;
|
||||||
|
RtmpMediaSource::RingType::RingReader::Ptr _rtmp_reader;
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace mediakit */
|
} /* namespace mediakit */
|
||||||
|
@ -732,14 +732,14 @@ void RtspPlayer::onRecvRTP_l(const RtpPacket::Ptr &rtp, const SdpTrack::Ptr &tra
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtspPlayer::onPlayResult_l(const SockException &ex , bool handshakeCompleted) {
|
void RtspPlayer::onPlayResult_l(const SockException &ex , bool handshake_done) {
|
||||||
if (ex.getErrCode() == Err_shutdown) {
|
if (ex.getErrCode() == Err_shutdown) {
|
||||||
//主动shutdown的,不触发回调
|
//主动shutdown的,不触发回调
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
WarnL << ex.getErrCode() << " " << ex.what();
|
WarnL << ex.getErrCode() << " " << ex.what();
|
||||||
if (!handshakeCompleted) {
|
if (!handshake_done) {
|
||||||
//开始播放阶段
|
//开始播放阶段
|
||||||
_play_check_timer.reset();
|
_play_check_timer.reset();
|
||||||
onPlayResult(ex);
|
onPlayResult(ex);
|
||||||
|
@ -87,7 +87,7 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void onRecvRTP_l(const RtpPacket::Ptr &rtp, const SdpTrack::Ptr &track);
|
void onRecvRTP_l(const RtpPacket::Ptr &rtp, const SdpTrack::Ptr &track);
|
||||||
void onPlayResult_l(const SockException &ex , bool handshakeCompleted);
|
void onPlayResult_l(const SockException &ex , bool handshake_done);
|
||||||
|
|
||||||
int getTrackIndexByInterleaved(int interleaved) const;
|
int getTrackIndexByInterleaved(int interleaved) const;
|
||||||
int getTrackIndexByTrackType(TrackType track_type) const;
|
int getTrackIndexByTrackType(TrackType track_type) const;
|
||||||
|
@ -25,46 +25,51 @@ using namespace toolkit;
|
|||||||
|
|
||||||
namespace mediakit {
|
namespace mediakit {
|
||||||
|
|
||||||
class RtspPlayerImp: public PlayerImp<RtspPlayer,RtspDemuxer> {
|
class RtspPlayerImp : public PlayerImp<RtspPlayer,RtspDemuxer> {
|
||||||
public:
|
public:
|
||||||
typedef std::shared_ptr<RtspPlayerImp> Ptr;
|
typedef std::shared_ptr<RtspPlayerImp> Ptr;
|
||||||
RtspPlayerImp(const EventPoller::Ptr &poller) : PlayerImp<RtspPlayer,RtspDemuxer>(poller){}
|
|
||||||
virtual ~RtspPlayerImp(){
|
RtspPlayerImp(const EventPoller::Ptr &poller) : PlayerImp<RtspPlayer, RtspDemuxer>(poller) {}
|
||||||
DebugL<<endl;
|
~RtspPlayerImp() override{
|
||||||
};
|
DebugL << endl;
|
||||||
float getProgress() const override{
|
}
|
||||||
if(getDuration() > 0){
|
|
||||||
|
float getProgress() const override {
|
||||||
|
if (getDuration() > 0) {
|
||||||
return getProgressMilliSecond() / (getDuration() * 1000);
|
return getProgressMilliSecond() / (getDuration() * 1000);
|
||||||
}
|
}
|
||||||
return PlayerBase::getProgress();
|
return PlayerBase::getProgress();
|
||||||
|
|
||||||
};
|
}
|
||||||
void seekTo(float fProgress) override{
|
|
||||||
fProgress = MAX(float(0),MIN(fProgress,float(1.0)));
|
void seekTo(float fProgress) override {
|
||||||
|
fProgress = MAX(float(0), MIN(fProgress, float(1.0)));
|
||||||
seekToMilliSecond(fProgress * getDuration() * 1000);
|
seekToMilliSecond(fProgress * getDuration() * 1000);
|
||||||
};
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//派生类回调函数
|
//派生类回调函数
|
||||||
bool onCheckSDP(const string &sdp) override {
|
bool onCheckSDP(const string &sdp) override {
|
||||||
_pRtspMediaSrc = dynamic_pointer_cast<RtspMediaSource>(_pMediaSrc);
|
_rtsp_media_src = dynamic_pointer_cast<RtspMediaSource>(_pMediaSrc);
|
||||||
if(_pRtspMediaSrc){
|
if (_rtsp_media_src) {
|
||||||
_pRtspMediaSrc->setSdp(sdp);
|
_rtsp_media_src->setSdp(sdp);
|
||||||
}
|
}
|
||||||
_delegate.reset(new RtspDemuxer);
|
_delegate.reset(new RtspDemuxer);
|
||||||
_delegate->loadSdp(sdp);
|
_delegate->loadSdp(sdp);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void onRecvRTP(const RtpPacket::Ptr &rtp, const SdpTrack::Ptr &track) override {
|
void onRecvRTP(const RtpPacket::Ptr &rtp, const SdpTrack::Ptr &track) override {
|
||||||
if(_pRtspMediaSrc){
|
if (_rtsp_media_src) {
|
||||||
// rtsp直接代理是无法判断该rtp是否是I帧,所以GOP缓存基本是无效的
|
// rtsp直接代理是无法判断该rtp是否是I帧,所以GOP缓存基本是无效的
|
||||||
// 为了减少内存使用,那么我们设置为一直关键帧以便清空GOP缓存
|
// 为了减少内存使用,那么我们设置为一直关键帧以便清空GOP缓存
|
||||||
_pRtspMediaSrc->onWrite(rtp,true);
|
_rtsp_media_src->onWrite(rtp, true);
|
||||||
}
|
}
|
||||||
_delegate->inputRtp(rtp);
|
_delegate->inputRtp(rtp);
|
||||||
|
|
||||||
if(_maxAnalysisMS && _delegate->isInited(_maxAnalysisMS)){
|
if (_max_analysis_ms && _delegate->isInited(_max_analysis_ms)) {
|
||||||
PlayerImp<RtspPlayer,RtspDemuxer>::onPlayResult(SockException(Err_success,"play rtsp success"));
|
PlayerImp<RtspPlayer, RtspDemuxer>::onPlayResult(SockException(Err_success, "play rtsp success"));
|
||||||
_maxAnalysisMS = 0;
|
_max_analysis_ms = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,17 +79,18 @@ private:
|
|||||||
//如果超过这个时间还未获取成功,那么会强制触发onPlayResult事件(虽然此时有些track还未初始化成功)
|
//如果超过这个时间还未获取成功,那么会强制触发onPlayResult事件(虽然此时有些track还未初始化成功)
|
||||||
void onPlayResult(const SockException &ex) override {
|
void onPlayResult(const SockException &ex) override {
|
||||||
//isInited判断条件:无超时
|
//isInited判断条件:无超时
|
||||||
if(ex || _delegate->isInited(0)){
|
if (ex || _delegate->isInited(0)) {
|
||||||
//已经初始化成功,说明sdp里面有完善的信息
|
//已经初始化成功,说明sdp里面有完善的信息
|
||||||
PlayerImp<RtspPlayer,RtspDemuxer>::onPlayResult(ex);
|
PlayerImp<RtspPlayer, RtspDemuxer>::onPlayResult(ex);
|
||||||
}else{
|
} else {
|
||||||
//还没初始化成功,说明sdp里面信息不完善,还有一些track未初始化成功
|
//还没初始化成功,说明sdp里面信息不完善,还有一些track未初始化成功
|
||||||
_maxAnalysisMS = (*this)[Client::kMaxAnalysisMS];
|
_max_analysis_ms = (*this)[Client::kMaxAnalysisMS];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RtspMediaSource::Ptr _pRtspMediaSrc;
|
int _max_analysis_ms = 0;
|
||||||
int _maxAnalysisMS = 0;
|
RtspMediaSource::Ptr _rtsp_media_src;
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace mediakit */
|
} /* namespace mediakit */
|
||||||
|
@ -17,8 +17,8 @@ using namespace mediakit::Client;
|
|||||||
|
|
||||||
namespace mediakit {
|
namespace mediakit {
|
||||||
|
|
||||||
RtspPusher::RtspPusher(const EventPoller::Ptr &poller,const RtspMediaSource::Ptr &src) : TcpClient(poller){
|
RtspPusher::RtspPusher(const EventPoller::Ptr &poller, const RtspMediaSource::Ptr &src) : TcpClient(poller) {
|
||||||
_pMediaSrc = src;
|
_push_src = src;
|
||||||
}
|
}
|
||||||
|
|
||||||
RtspPusher::~RtspPusher() {
|
RtspPusher::~RtspPusher() {
|
||||||
@ -28,30 +28,30 @@ RtspPusher::~RtspPusher() {
|
|||||||
|
|
||||||
void RtspPusher::teardown() {
|
void RtspPusher::teardown() {
|
||||||
if (alive()) {
|
if (alive()) {
|
||||||
sendRtspRequest("TEARDOWN" ,_strContentBase);
|
sendRtspRequest("TEARDOWN", _content_base);
|
||||||
shutdown(SockException(Err_shutdown,"teardown"));
|
shutdown(SockException(Err_shutdown, "teardown"));
|
||||||
}
|
}
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
CLEAR_ARR(_apUdpSock);
|
CLEAR_ARR(_udp_socks);
|
||||||
_rtspMd5Nonce.clear();
|
_nonce.clear();
|
||||||
_rtspRealm.clear();
|
_realm.clear();
|
||||||
_aTrackInfo.clear();
|
_track_vec.clear();
|
||||||
_strSession.clear();
|
_session_id.clear();
|
||||||
_strContentBase.clear();
|
_content_base.clear();
|
||||||
_strSession.clear();
|
_session_id.clear();
|
||||||
_uiCseq = 1;
|
_cseq = 1;
|
||||||
_pPublishTimer.reset();
|
_publish_timer.reset();
|
||||||
_pBeatTimer.reset();
|
_beat_timer.reset();
|
||||||
_pRtspReader.reset();
|
_rtsp_reader.reset();
|
||||||
_aTrackInfo.clear();
|
_track_vec.clear();
|
||||||
_onHandshake = nullptr;
|
_on_res_func = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtspPusher::publish(const string &strUrl) {
|
void RtspPusher::publish(const string &url_str) {
|
||||||
RtspUrl url;
|
RtspUrl url;
|
||||||
if(!url.parse(strUrl)){
|
if (!url.parse(url_str)) {
|
||||||
onPublishResult(SockException(Err_other,StrPrinter << "illegal rtsp url:" << strUrl),false);
|
onPublishResult(SockException(Err_other, StrPrinter << "illegal rtsp url:" << url_str), false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,55 +65,60 @@ void RtspPusher::publish(const string &strUrl) {
|
|||||||
(*this)[kRtspPwdIsMD5] = false;
|
(*this)[kRtspPwdIsMD5] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_strUrl = strUrl;
|
_url = url_str;
|
||||||
_eType = (Rtsp::eRtpType)(int)(*this)[kRtpType];
|
_rtp_type = (Rtsp::eRtpType) (int) (*this)[kRtpType];
|
||||||
DebugL << url._url << " " << (url._user.size() ? url._user : "null") << " " << (url._passwd.size() ? url._passwd : "null") << " " << _eType;
|
DebugL << url._url << " " << (url._user.size() ? url._user : "null") << " "
|
||||||
|
<< (url._passwd.size() ? url._passwd : "null") << " " << _rtp_type;
|
||||||
|
|
||||||
weak_ptr<RtspPusher> weakSelf = dynamic_pointer_cast<RtspPusher>(shared_from_this());
|
weak_ptr<RtspPusher> weak_self = dynamic_pointer_cast<RtspPusher>(shared_from_this());
|
||||||
float publishTimeOutSec = (*this)[kTimeoutMS].as<int>() / 1000.0;
|
float publish_timeout_sec = (*this)[kTimeoutMS].as<int>() / 1000.0;
|
||||||
_pPublishTimer.reset( new Timer(publishTimeOutSec, [weakSelf]() {
|
_publish_timer.reset(new Timer(publish_timeout_sec, [weak_self]() {
|
||||||
auto strongSelf=weakSelf.lock();
|
auto strong_self = weak_self.lock();
|
||||||
if(!strongSelf) {
|
if (!strong_self) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
strongSelf->onPublishResult(SockException(Err_timeout,"publish rtsp timeout"),false);
|
strong_self->onPublishResult(SockException(Err_timeout, "publish rtsp timeout"), false);
|
||||||
return false;
|
return false;
|
||||||
},getPoller()));
|
}, getPoller()));
|
||||||
|
|
||||||
if(!(*this)[kNetAdapter].empty()){
|
if (!(*this)[kNetAdapter].empty()) {
|
||||||
setNetAdapter((*this)[kNetAdapter]);
|
setNetAdapter((*this)[kNetAdapter]);
|
||||||
}
|
}
|
||||||
|
|
||||||
startConnect(url._host, url._port, publishTimeOutSec);
|
startConnect(url._host, url._port, publish_timeout_sec);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtspPusher::onPublishResult(const SockException &ex, bool handshakeCompleted) {
|
void RtspPusher::onPublishResult(const SockException &ex, bool handshake_done) {
|
||||||
if(!handshakeCompleted){
|
if (ex.getErrCode() == Err_shutdown) {
|
||||||
|
//主动shutdown的,不触发回调
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!handshake_done) {
|
||||||
//播放结果回调
|
//播放结果回调
|
||||||
_pPublishTimer.reset();
|
_publish_timer.reset();
|
||||||
if(_onPublished){
|
if (_on_published) {
|
||||||
_onPublished(ex);
|
_on_published(ex);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//播放成功后异常断开回调
|
//播放成功后异常断开回调
|
||||||
if(_onShutdown){
|
if (_on_shutdown) {
|
||||||
_onShutdown(ex);
|
_on_shutdown(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ex){
|
if (ex) {
|
||||||
teardown();
|
teardown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtspPusher::onErr(const SockException &ex) {
|
void RtspPusher::onErr(const SockException &ex) {
|
||||||
//定时器_pPublishTimer为空后表明握手结束了
|
//定时器_pPublishTimer为空后表明握手结束了
|
||||||
onPublishResult(ex,!_pPublishTimer);
|
onPublishResult(ex, !_publish_timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtspPusher::onConnect(const SockException &err) {
|
void RtspPusher::onConnect(const SockException &err) {
|
||||||
if(err) {
|
if (err) {
|
||||||
onPublishResult(err,false);
|
onPublishResult(err, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//推流器不需要多大的接收缓存,节省内存占用
|
//推流器不需要多大的接收缓存,节省内存占用
|
||||||
@ -121,41 +126,40 @@ void RtspPusher::onConnect(const SockException &err) {
|
|||||||
sendAnnounce();
|
sendAnnounce();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtspPusher::onRecv(const Buffer::Ptr &pBuf){
|
void RtspPusher::onRecv(const Buffer::Ptr &buf){
|
||||||
try {
|
try {
|
||||||
input(pBuf->data(), pBuf->size());
|
input(buf->data(), buf->size());
|
||||||
} catch (exception &e) {
|
} catch (exception &e) {
|
||||||
SockException ex(Err_other, e.what());
|
SockException ex(Err_other, e.what());
|
||||||
//定时器_pPublishTimer为空后表明握手结束了
|
//定时器_pPublishTimer为空后表明握手结束了
|
||||||
onPublishResult(ex,!_pPublishTimer);
|
onPublishResult(ex, !_publish_timer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtspPusher::onWholeRtspPacket(Parser &parser) {
|
void RtspPusher::onWholeRtspPacket(Parser &parser) {
|
||||||
decltype(_onHandshake) fun;
|
decltype(_on_res_func) func;
|
||||||
_onHandshake.swap(fun);
|
_on_res_func.swap(func);
|
||||||
if(fun){
|
if (func) {
|
||||||
fun(parser);
|
func(parser);
|
||||||
}
|
}
|
||||||
parser.Clear();
|
parser.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void RtspPusher::sendAnnounce() {
|
void RtspPusher::sendAnnounce() {
|
||||||
auto src = _pMediaSrc.lock();
|
auto src = _push_src.lock();
|
||||||
if (!src) {
|
if (!src) {
|
||||||
throw std::runtime_error("the media source was released");
|
throw std::runtime_error("the media source was released");
|
||||||
}
|
}
|
||||||
//解析sdp
|
//解析sdp
|
||||||
_sdpParser.load(src->getSdp());
|
_sdp_parser.load(src->getSdp());
|
||||||
_aTrackInfo = _sdpParser.getAvailableTrack();
|
_track_vec = _sdp_parser.getAvailableTrack();
|
||||||
|
|
||||||
if (_aTrackInfo.empty()) {
|
if (_track_vec.empty()) {
|
||||||
throw std::runtime_error("无有效的Sdp Track");
|
throw std::runtime_error("无有效的Sdp Track");
|
||||||
}
|
}
|
||||||
|
|
||||||
_onHandshake = std::bind(&RtspPusher::handleResAnnounce,this, placeholders::_1);
|
_on_res_func = std::bind(&RtspPusher::handleResAnnounce, this, placeholders::_1);
|
||||||
sendRtspRequest("ANNOUNCE",_strUrl,{},src->getSdp());
|
sendRtspRequest("ANNOUNCE", _url, {}, src->getSdp());
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtspPusher::handleResAnnounce(const Parser &parser) {
|
void RtspPusher::handleResAnnounce(const Parser &parser) {
|
||||||
@ -165,9 +169,9 @@ void RtspPusher::handleResAnnounce(const Parser &parser) {
|
|||||||
sendAnnounce();
|
sendAnnounce();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(parser.Url() == "302"){
|
if (parser.Url() == "302") {
|
||||||
auto newUrl = parser["Location"];
|
auto newUrl = parser["Location"];
|
||||||
if(newUrl.empty()){
|
if (newUrl.empty()) {
|
||||||
throw std::runtime_error("未找到Location字段(跳转url)");
|
throw std::runtime_error("未找到Location字段(跳转url)");
|
||||||
}
|
}
|
||||||
publish(newUrl);
|
publish(newUrl);
|
||||||
@ -176,45 +180,45 @@ void RtspPusher::handleResAnnounce(const Parser &parser) {
|
|||||||
if (parser.Url() != "200") {
|
if (parser.Url() != "200") {
|
||||||
throw std::runtime_error(StrPrinter << "ANNOUNCE:" << parser.Url() << " " << parser.Tail());
|
throw std::runtime_error(StrPrinter << "ANNOUNCE:" << parser.Url() << " " << parser.Tail());
|
||||||
}
|
}
|
||||||
_strContentBase = parser["Content-Base"];
|
_content_base = parser["Content-Base"];
|
||||||
|
|
||||||
if(_strContentBase.empty()){
|
if (_content_base.empty()) {
|
||||||
_strContentBase = _strUrl;
|
_content_base = _url;
|
||||||
}
|
}
|
||||||
if (_strContentBase.back() == '/') {
|
if (_content_base.back() == '/') {
|
||||||
_strContentBase.pop_back();
|
_content_base.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
sendSetup(0);
|
sendSetup(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RtspPusher::handleAuthenticationFailure(const string ¶msStr) {
|
bool RtspPusher::handleAuthenticationFailure(const string ¶ms_str) {
|
||||||
if(!_rtspRealm.empty()){
|
if (!_realm.empty()) {
|
||||||
//已经认证过了
|
//已经认证过了
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *realm = new char[paramsStr.size()];
|
char *realm = new char[params_str.size()];
|
||||||
char *nonce = new char[paramsStr.size()];
|
char *nonce = new char[params_str.size()];
|
||||||
char *stale = new char[paramsStr.size()];
|
char *stale = new char[params_str.size()];
|
||||||
onceToken token(nullptr,[&](){
|
onceToken token(nullptr, [&]() {
|
||||||
delete[] realm;
|
delete[] realm;
|
||||||
delete[] nonce;
|
delete[] nonce;
|
||||||
delete[] stale;
|
delete[] stale;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (sscanf(paramsStr.data(), "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\", stale=%[a-zA-Z]", realm, nonce, stale) == 3) {
|
if (sscanf(params_str.data(), "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\", stale=%[a-zA-Z]", realm, nonce, stale) == 3) {
|
||||||
_rtspRealm = (const char *)realm;
|
_realm = (const char *) realm;
|
||||||
_rtspMd5Nonce = (const char *)nonce;
|
_nonce = (const char *) nonce;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (sscanf(paramsStr.data(), "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\"", realm, nonce) == 2) {
|
if (sscanf(params_str.data(), "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\"", realm, nonce) == 2) {
|
||||||
_rtspRealm = (const char *)realm;
|
_realm = (const char *) realm;
|
||||||
_rtspMd5Nonce = (const char *)nonce;
|
_nonce = (const char *) nonce;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (sscanf(paramsStr.data(), "Basic realm=\"%[^\"]\"", realm) == 1) {
|
if (sscanf(params_str.data(), "Basic realm=\"%[^\"]\"", realm) == 1) {
|
||||||
_rtspRealm = (const char *)realm;
|
_realm = (const char *) realm;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -222,30 +226,33 @@ bool RtspPusher::handleAuthenticationFailure(const string ¶msStr) {
|
|||||||
|
|
||||||
//有必要的情况下创建udp端口
|
//有必要的情况下创建udp端口
|
||||||
void RtspPusher::createUdpSockIfNecessary(int track_idx){
|
void RtspPusher::createUdpSockIfNecessary(int track_idx){
|
||||||
auto &rtpSockRef = _apUdpSock[track_idx];
|
auto &rtp_sock = _udp_socks[track_idx];
|
||||||
if(!rtpSockRef){
|
if (!rtp_sock) {
|
||||||
rtpSockRef.reset(new Socket(getPoller()));
|
rtp_sock.reset(new Socket(getPoller()));
|
||||||
//rtp随机端口
|
//rtp随机端口
|
||||||
if (!rtpSockRef->bindUdpSock(0, get_local_ip().data())) {
|
if (!rtp_sock->bindUdpSock(0, get_local_ip().data())) {
|
||||||
rtpSockRef.reset();
|
rtp_sock.reset();
|
||||||
throw std::runtime_error("open rtp sock failed");
|
throw std::runtime_error("open rtp sock failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtspPusher::sendSetup(unsigned int trackIndex) {
|
void RtspPusher::sendSetup(unsigned int track_idx) {
|
||||||
_onHandshake = std::bind(&RtspPusher::handleResSetup,this, placeholders::_1,trackIndex);
|
_on_res_func = std::bind(&RtspPusher::handleResSetup, this, placeholders::_1, track_idx);
|
||||||
auto &track = _aTrackInfo[trackIndex];
|
auto &track = _track_vec[track_idx];
|
||||||
auto baseUrl = _strContentBase + "/" + track->_control_surffix;
|
auto base_url = _content_base + "/" + track->_control_surffix;
|
||||||
switch (_eType) {
|
switch (_rtp_type) {
|
||||||
case Rtsp::RTP_TCP: {
|
case Rtsp::RTP_TCP: {
|
||||||
sendRtspRequest("SETUP",baseUrl,{"Transport",StrPrinter << "RTP/AVP/TCP;unicast;interleaved=" << track->_type * 2 << "-" << track->_type * 2 + 1});
|
sendRtspRequest("SETUP", base_url, {"Transport",
|
||||||
|
StrPrinter << "RTP/AVP/TCP;unicast;interleaved=" << track->_type * 2
|
||||||
|
<< "-" << track->_type * 2 + 1});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Rtsp::RTP_UDP: {
|
case Rtsp::RTP_UDP: {
|
||||||
createUdpSockIfNecessary(trackIndex);
|
createUdpSockIfNecessary(track_idx);
|
||||||
int port = _apUdpSock[trackIndex]->get_local_port();
|
int port = _udp_socks[track_idx]->get_local_port();
|
||||||
sendRtspRequest("SETUP",baseUrl,{"Transport",StrPrinter << "RTP/AVP;unicast;client_port=" << port << "-" << port + 1});
|
sendRtspRequest("SETUP", base_url,
|
||||||
|
{"Transport", StrPrinter << "RTP/AVP;unicast;client_port=" << port << "-" << port + 1});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -254,42 +261,41 @@ void RtspPusher::sendSetup(unsigned int trackIndex) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void RtspPusher::handleResSetup(const Parser &parser, unsigned int uiTrackIndex) {
|
void RtspPusher::handleResSetup(const Parser &parser, unsigned int track_idx) {
|
||||||
if (parser.Url() != "200") {
|
if (parser.Url() != "200") {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(StrPrinter << "SETUP:" << parser.Url() << " " << parser.Tail() << endl);
|
||||||
StrPrinter << "SETUP:" << parser.Url() << " " << parser.Tail() << endl);
|
|
||||||
}
|
}
|
||||||
if (uiTrackIndex == 0) {
|
if (track_idx == 0) {
|
||||||
_strSession = parser["Session"];
|
_session_id = parser["Session"];
|
||||||
_strSession.append(";");
|
_session_id.append(";");
|
||||||
_strSession = FindField(_strSession.data(), nullptr, ";");
|
_session_id = FindField(_session_id.data(), nullptr, ";");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto strTransport = parser["Transport"];
|
auto transport = parser["Transport"];
|
||||||
if(strTransport.find("TCP") != string::npos || strTransport.find("interleaved") != string::npos){
|
if (transport.find("TCP") != string::npos || transport.find("interleaved") != string::npos) {
|
||||||
_eType = Rtsp::RTP_TCP;
|
_rtp_type = Rtsp::RTP_TCP;
|
||||||
string interleaved = FindField( FindField((strTransport + ";").data(), "interleaved=", ";").data(), NULL, "-");
|
string interleaved = FindField(FindField((transport + ";").data(), "interleaved=", ";").data(), NULL, "-");
|
||||||
_aTrackInfo[uiTrackIndex]->_interleaved = atoi(interleaved.data());
|
_track_vec[track_idx]->_interleaved = atoi(interleaved.data());
|
||||||
}else if(strTransport.find("multicast") != string::npos){
|
} else if (transport.find("multicast") != string::npos) {
|
||||||
throw std::runtime_error("SETUP rtsp pusher can not support multicast!");
|
throw std::runtime_error("SETUP rtsp pusher can not support multicast!");
|
||||||
}else{
|
} else {
|
||||||
_eType = Rtsp::RTP_UDP;
|
_rtp_type = Rtsp::RTP_UDP;
|
||||||
createUdpSockIfNecessary(uiTrackIndex);
|
createUdpSockIfNecessary(track_idx);
|
||||||
const char *strPos = "server_port=" ;
|
const char *strPos = "server_port=";
|
||||||
auto port_str = FindField((strTransport + ";").data(), strPos, ";");
|
auto port_str = FindField((transport + ";").data(), strPos, ";");
|
||||||
uint16_t port = atoi(FindField(port_str.data(), NULL, "-").data());
|
uint16_t port = atoi(FindField(port_str.data(), NULL, "-").data());
|
||||||
struct sockaddr_in rtpto;
|
struct sockaddr_in rtpto;
|
||||||
rtpto.sin_port = ntohs(port);
|
rtpto.sin_port = ntohs(port);
|
||||||
rtpto.sin_family = AF_INET;
|
rtpto.sin_family = AF_INET;
|
||||||
rtpto.sin_addr.s_addr = inet_addr(get_peer_ip().data());
|
rtpto.sin_addr.s_addr = inet_addr(get_peer_ip().data());
|
||||||
_apUdpSock[uiTrackIndex]->setSendPeerAddr((struct sockaddr *)&(rtpto));
|
_udp_socks[track_idx]->setSendPeerAddr((struct sockaddr *) &(rtpto));
|
||||||
}
|
}
|
||||||
|
|
||||||
RtspSplitter::enableRecvRtp(_eType == Rtsp::RTP_TCP);
|
RtspSplitter::enableRecvRtp(_rtp_type == Rtsp::RTP_TCP);
|
||||||
|
|
||||||
if (uiTrackIndex < _aTrackInfo.size() - 1) {
|
if (track_idx < _track_vec.size() - 1) {
|
||||||
//需要继续发送SETUP命令
|
//需要继续发送SETUP命令
|
||||||
sendSetup(uiTrackIndex + 1);
|
sendSetup(track_idx + 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,13 +303,12 @@ void RtspPusher::handleResSetup(const Parser &parser, unsigned int uiTrackIndex)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RtspPusher::sendOptions() {
|
void RtspPusher::sendOptions() {
|
||||||
_onHandshake = [this](const Parser& parser){};
|
_on_res_func = [this](const Parser &parser) {};
|
||||||
sendRtspRequest("OPTIONS",_strContentBase);
|
sendRtspRequest("OPTIONS", _content_base);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void RtspPusher::sendRtpPacket(const RtspMediaSource::RingDataType &pkt) {
|
inline void RtspPusher::sendRtpPacket(const RtspMediaSource::RingDataType &pkt) {
|
||||||
//InfoL<<(int)pkt.Interleaved;
|
switch (_rtp_type) {
|
||||||
switch (_eType) {
|
|
||||||
case Rtsp::RTP_TCP: {
|
case Rtsp::RTP_TCP: {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int size = pkt->size();
|
int size = pkt->size();
|
||||||
@ -315,85 +320,84 @@ inline void RtspPusher::sendRtpPacket(const RtspMediaSource::RingDataType &pkt)
|
|||||||
BufferRtp::Ptr buffer(new BufferRtp(rtp));
|
BufferRtp::Ptr buffer(new BufferRtp(rtp));
|
||||||
send(buffer);
|
send(buffer);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Rtsp::RTP_UDP: {
|
case Rtsp::RTP_UDP: {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int size = pkt->size();
|
int size = pkt->size();
|
||||||
pkt->for_each([&](const RtpPacket::Ptr &rtp) {
|
pkt->for_each([&](const RtpPacket::Ptr &rtp) {
|
||||||
int iTrackIndex = getTrackIndexByTrackType(rtp->type);
|
int iTrackIndex = getTrackIndexByTrackType(rtp->type);
|
||||||
auto &pSock = _apUdpSock[iTrackIndex];
|
auto &pSock = _udp_socks[iTrackIndex];
|
||||||
if (!pSock) {
|
if (!pSock) {
|
||||||
shutdown(SockException(Err_shutdown,"udp sock not opened yet"));
|
shutdown(SockException(Err_shutdown, "udp sock not opened yet"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferRtp::Ptr buffer(new BufferRtp(rtp,4));
|
BufferRtp::Ptr buffer(new BufferRtp(rtp, 4));
|
||||||
pSock->send(buffer, nullptr, 0, ++i == size);
|
pSock->send(buffer, nullptr, 0, ++i == size);
|
||||||
});
|
});
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
default : break;
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int RtspPusher::getTrackIndexByTrackType(TrackType type) {
|
inline int RtspPusher::getTrackIndexByTrackType(TrackType type) {
|
||||||
for (unsigned int i = 0; i < _aTrackInfo.size(); i++) {
|
for (unsigned int i = 0; i < _track_vec.size(); i++) {
|
||||||
if (type == _aTrackInfo[i]->_type) {
|
if (type == _track_vec[i]->_type) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(_aTrackInfo.size() == 1){
|
if (_track_vec.size() == 1) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
throw SockException(Err_shutdown, StrPrinter << "no such track with type:" << (int) type);
|
throw SockException(Err_shutdown, StrPrinter << "no such track with type:" << (int) type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtspPusher::sendRecord() {
|
void RtspPusher::sendRecord() {
|
||||||
_onHandshake = [this](const Parser& parser){
|
_on_res_func = [this](const Parser &parser) {
|
||||||
auto src = _pMediaSrc.lock();
|
auto src = _push_src.lock();
|
||||||
if (!src) {
|
if (!src) {
|
||||||
throw std::runtime_error("the media source was released");
|
throw std::runtime_error("the media source was released");
|
||||||
}
|
}
|
||||||
|
|
||||||
_pRtspReader = src->getRing()->attach(getPoller());
|
_rtsp_reader = src->getRing()->attach(getPoller());
|
||||||
weak_ptr<RtspPusher> weakSelf = dynamic_pointer_cast<RtspPusher>(shared_from_this());
|
weak_ptr<RtspPusher> weak_self = dynamic_pointer_cast<RtspPusher>(shared_from_this());
|
||||||
_pRtspReader->setReadCB([weakSelf](const RtspMediaSource::RingDataType &pkt){
|
_rtsp_reader->setReadCB([weak_self](const RtspMediaSource::RingDataType &pkt) {
|
||||||
auto strongSelf = weakSelf.lock();
|
auto strong_self = weak_self.lock();
|
||||||
if(!strongSelf) {
|
if (!strong_self) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
strongSelf->sendRtpPacket(pkt);
|
strong_self->sendRtpPacket(pkt);
|
||||||
});
|
});
|
||||||
_pRtspReader->setDetachCB([weakSelf](){
|
_rtsp_reader->setDetachCB([weak_self]() {
|
||||||
auto strongSelf = weakSelf.lock();
|
auto strong_self = weak_self.lock();
|
||||||
if(strongSelf){
|
if (strong_self) {
|
||||||
strongSelf->onPublishResult(SockException(Err_other,"媒体源被释放"), !strongSelf->_pPublishTimer);
|
strong_self->onPublishResult(SockException(Err_other, "媒体源被释放"), !strong_self->_publish_timer);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if(_eType != Rtsp::RTP_TCP){
|
if (_rtp_type != Rtsp::RTP_TCP) {
|
||||||
/////////////////////////心跳/////////////////////////////////
|
/////////////////////////心跳/////////////////////////////////
|
||||||
weak_ptr<RtspPusher> weakSelf = dynamic_pointer_cast<RtspPusher>(shared_from_this());
|
_beat_timer.reset(new Timer((*this)[kBeatIntervalMS].as<int>() / 1000.0, [weak_self]() {
|
||||||
_pBeatTimer.reset(new Timer((*this)[kBeatIntervalMS].as<int>() / 1000.0, [weakSelf](){
|
auto strong_self = weak_self.lock();
|
||||||
auto strongSelf = weakSelf.lock();
|
if (!strong_self) {
|
||||||
if (!strongSelf){
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
strongSelf->sendOptions();
|
strong_self->sendOptions();
|
||||||
return true;
|
return true;
|
||||||
},getPoller()));
|
}, getPoller()));
|
||||||
}
|
}
|
||||||
onPublishResult(SockException(Err_success,"success"), false);
|
onPublishResult(SockException(Err_success, "success"), false);
|
||||||
//提升发送性能
|
//提升发送性能
|
||||||
setSocketFlags();
|
setSocketFlags();
|
||||||
};
|
};
|
||||||
sendRtspRequest("RECORD",_strContentBase,{"Range","npt=0.000-"});
|
sendRtspRequest("RECORD", _content_base, {"Range", "npt=0.000-"});
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtspPusher::setSocketFlags(){
|
void RtspPusher::setSocketFlags(){
|
||||||
GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS);
|
GET_CONFIG(int, merge_write_ms, General::kMergeWriteMS);
|
||||||
if(mergeWriteMS > 0) {
|
if (merge_write_ms > 0) {
|
||||||
//提高发送性能
|
//提高发送性能
|
||||||
setSendFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE);
|
setSendFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE);
|
||||||
SockUtil::setNoDelay(_sock->rawFD(), false);
|
SockUtil::setNoDelay(_sock->rawFD(), false);
|
||||||
@ -404,26 +408,26 @@ void RtspPusher::sendRtspRequest(const string &cmd, const string &url, const std
|
|||||||
string key;
|
string key;
|
||||||
StrCaseMap header_map;
|
StrCaseMap header_map;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for(auto &val : header){
|
for (auto &val : header) {
|
||||||
if(++i % 2 == 0){
|
if (++i % 2 == 0) {
|
||||||
header_map.emplace(key,val);
|
header_map.emplace(key, val);
|
||||||
}else{
|
} else {
|
||||||
key = val;
|
key = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sendRtspRequest(cmd,url,header_map,sdp);
|
sendRtspRequest(cmd, url, header_map, sdp);
|
||||||
}
|
}
|
||||||
void RtspPusher::sendRtspRequest(const string &cmd, const string &url,const StrCaseMap &header_const,const string &sdp ) {
|
void RtspPusher::sendRtspRequest(const string &cmd, const string &url,const StrCaseMap &header_const,const string &sdp ) {
|
||||||
auto header = header_const;
|
auto header = header_const;
|
||||||
header.emplace("CSeq",StrPrinter << _uiCseq++);
|
header.emplace("CSeq", StrPrinter << _cseq++);
|
||||||
header.emplace("User-Agent",SERVER_NAME);
|
header.emplace("User-Agent", SERVER_NAME);
|
||||||
|
|
||||||
if(!_strSession.empty()){
|
if (!_session_id.empty()) {
|
||||||
header.emplace("Session",_strSession);
|
header.emplace("Session", _session_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!_rtspRealm.empty() && !(*this)[kRtspUser].empty()){
|
if (!_realm.empty() && !(*this)[kRtspUser].empty()) {
|
||||||
if(!_rtspMd5Nonce.empty()){
|
if (!_nonce.empty()) {
|
||||||
//MD5认证
|
//MD5认证
|
||||||
/*
|
/*
|
||||||
response计算方法如下:
|
response计算方法如下:
|
||||||
@ -434,41 +438,41 @@ void RtspPusher::sendRtspRequest(const string &cmd, const string &url,const StrC
|
|||||||
response= md5( md5(username:realm:password):nonce:md5(public_method:url) );
|
response= md5( md5(username:realm:password):nonce:md5(public_method:url) );
|
||||||
*/
|
*/
|
||||||
string encrypted_pwd = (*this)[kRtspPwd];
|
string encrypted_pwd = (*this)[kRtspPwd];
|
||||||
if(!(*this)[kRtspPwdIsMD5].as<bool>()){
|
if (!(*this)[kRtspPwdIsMD5].as<bool>()) {
|
||||||
encrypted_pwd = MD5((*this)[kRtspUser]+ ":" + _rtspRealm + ":" + encrypted_pwd).hexdigest();
|
encrypted_pwd = MD5((*this)[kRtspUser] + ":" + _realm + ":" + encrypted_pwd).hexdigest();
|
||||||
}
|
}
|
||||||
auto response = MD5( encrypted_pwd + ":" + _rtspMd5Nonce + ":" + MD5(cmd + ":" + url).hexdigest()).hexdigest();
|
auto response = MD5(encrypted_pwd + ":" + _nonce + ":" + MD5(cmd + ":" + url).hexdigest()).hexdigest();
|
||||||
_StrPrinter printer;
|
_StrPrinter printer;
|
||||||
printer << "Digest ";
|
printer << "Digest ";
|
||||||
printer << "username=\"" << (*this)[kRtspUser] << "\", ";
|
printer << "username=\"" << (*this)[kRtspUser] << "\", ";
|
||||||
printer << "realm=\"" << _rtspRealm << "\", ";
|
printer << "realm=\"" << _realm << "\", ";
|
||||||
printer << "nonce=\"" << _rtspMd5Nonce << "\", ";
|
printer << "nonce=\"" << _nonce << "\", ";
|
||||||
printer << "uri=\"" << url << "\", ";
|
printer << "uri=\"" << url << "\", ";
|
||||||
printer << "response=\"" << response << "\"";
|
printer << "response=\"" << response << "\"";
|
||||||
header.emplace("Authorization",printer);
|
header.emplace("Authorization", printer);
|
||||||
}else if(!(*this)[kRtspPwdIsMD5].as<bool>()){
|
} else if (!(*this)[kRtspPwdIsMD5].as<bool>()) {
|
||||||
//base64认证
|
//base64认证
|
||||||
string authStr = StrPrinter << (*this)[kRtspUser] << ":" << (*this)[kRtspPwd];
|
string authStr = StrPrinter << (*this)[kRtspUser] << ":" << (*this)[kRtspPwd];
|
||||||
char authStrBase64[1024] = {0};
|
char authStrBase64[1024] = {0};
|
||||||
av_base64_encode(authStrBase64,sizeof(authStrBase64),(uint8_t *)authStr.data(),authStr.size());
|
av_base64_encode(authStrBase64, sizeof(authStrBase64), (uint8_t *) authStr.data(), authStr.size());
|
||||||
header.emplace("Authorization",StrPrinter << "Basic " << authStrBase64 );
|
header.emplace("Authorization", StrPrinter << "Basic " << authStrBase64);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!sdp.empty()){
|
if (!sdp.empty()) {
|
||||||
header.emplace("Content-Length",StrPrinter << sdp.size());
|
header.emplace("Content-Length", StrPrinter << sdp.size());
|
||||||
header.emplace("Content-Type","application/sdp");
|
header.emplace("Content-Type", "application/sdp");
|
||||||
}
|
}
|
||||||
|
|
||||||
_StrPrinter printer;
|
_StrPrinter printer;
|
||||||
printer << cmd << " " << url << " RTSP/1.0\r\n";
|
printer << cmd << " " << url << " RTSP/1.0\r\n";
|
||||||
for (auto &pr : header){
|
for (auto &pr : header) {
|
||||||
printer << pr.first << ": " << pr.second << "\r\n";
|
printer << pr.first << ": " << pr.second << "\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
printer << "\r\n";
|
printer << "\r\n";
|
||||||
|
|
||||||
if(!sdp.empty()){
|
if (!sdp.empty()) {
|
||||||
printer << sdp;
|
printer << sdp;
|
||||||
}
|
}
|
||||||
SockSender::send(printer);
|
SockSender::send(printer);
|
||||||
|
@ -31,39 +31,39 @@ class RtspPusher : public TcpClient, public RtspSplitter, public PusherBase {
|
|||||||
public:
|
public:
|
||||||
typedef std::shared_ptr<RtspPusher> Ptr;
|
typedef std::shared_ptr<RtspPusher> Ptr;
|
||||||
RtspPusher(const EventPoller::Ptr &poller,const RtspMediaSource::Ptr &src);
|
RtspPusher(const EventPoller::Ptr &poller,const RtspMediaSource::Ptr &src);
|
||||||
virtual ~RtspPusher();
|
~RtspPusher() override;
|
||||||
|
void publish(const string &url) override;
|
||||||
void publish(const string &strUrl) override;
|
|
||||||
|
|
||||||
void teardown() override;
|
void teardown() override;
|
||||||
|
|
||||||
void setOnPublished(const Event &cb) override {
|
void setOnPublished(const Event &cb) override {
|
||||||
_onPublished = cb;
|
_on_published = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setOnShutdown(const Event & cb) override{
|
void setOnShutdown(const Event & cb) override{
|
||||||
_onShutdown = cb;
|
_on_shutdown = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//for Tcpclient override
|
//for Tcpclient override
|
||||||
void onRecv(const Buffer::Ptr &pBuf) override;
|
void onRecv(const Buffer::Ptr &buf) override;
|
||||||
void onConnect(const SockException &err) override;
|
void onConnect(const SockException &err) override;
|
||||||
void onErr(const SockException &ex) override;
|
void onErr(const SockException &ex) override;
|
||||||
|
|
||||||
//RtspSplitter override
|
//RtspSplitter override
|
||||||
void onWholeRtspPacket(Parser &parser) override ;
|
void onWholeRtspPacket(Parser &parser) override ;
|
||||||
void onRtpPacket(const char *data,uint64_t len) override {};
|
void onRtpPacket(const char *data,uint64_t len) override {};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onPublishResult(const SockException &ex, bool handshakeCompleted);
|
void onPublishResult(const SockException &ex, bool handshake_done);
|
||||||
|
|
||||||
void sendAnnounce();
|
void sendAnnounce();
|
||||||
void sendSetup(unsigned int uiTrackIndex);
|
void sendSetup(unsigned int track_idx);
|
||||||
void sendRecord();
|
void sendRecord();
|
||||||
void sendOptions();
|
void sendOptions();
|
||||||
|
|
||||||
void handleResAnnounce(const Parser &parser);
|
void handleResAnnounce(const Parser &parser);
|
||||||
void handleResSetup(const Parser &parser, unsigned int uiTrackIndex);
|
void handleResSetup(const Parser &parser, unsigned int track_idx);
|
||||||
bool handleAuthenticationFailure(const string ¶msStr);
|
bool handleAuthenticationFailure(const string ¶ms_str);
|
||||||
|
|
||||||
inline int getTrackIndexByTrackType(TrackType type);
|
inline int getTrackIndexByTrackType(TrackType type);
|
||||||
|
|
||||||
@ -73,33 +73,30 @@ private:
|
|||||||
|
|
||||||
void createUdpSockIfNecessary(int track_idx);
|
void createUdpSockIfNecessary(int track_idx);
|
||||||
void setSocketFlags();
|
void setSocketFlags();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
unsigned int _cseq = 1;
|
||||||
|
Rtsp::eRtpType _rtp_type = Rtsp::RTP_TCP;
|
||||||
|
|
||||||
//rtsp鉴权相关
|
//rtsp鉴权相关
|
||||||
string _rtspMd5Nonce;
|
string _nonce;
|
||||||
string _rtspRealm;
|
string _realm;
|
||||||
|
string _url;
|
||||||
|
string _session_id;
|
||||||
|
string _content_base;
|
||||||
|
SdpParser _sdp_parser;
|
||||||
|
vector<SdpTrack::Ptr> _track_vec;
|
||||||
|
Socket::Ptr _udp_socks[2];
|
||||||
//超时功能实现
|
//超时功能实现
|
||||||
std::shared_ptr<Timer> _pPublishTimer;
|
std::shared_ptr<Timer> _publish_timer;
|
||||||
//源
|
|
||||||
std::weak_ptr<RtspMediaSource> _pMediaSrc;
|
|
||||||
RtspMediaSource::RingType::RingReader::Ptr _pRtspReader;
|
|
||||||
//事件监听
|
|
||||||
Event _onShutdown;
|
|
||||||
Event _onPublished;
|
|
||||||
|
|
||||||
string _strUrl;
|
|
||||||
SdpParser _sdpParser;
|
|
||||||
vector<SdpTrack::Ptr> _aTrackInfo;
|
|
||||||
string _strSession;
|
|
||||||
unsigned int _uiCseq = 1;
|
|
||||||
string _strContentBase;
|
|
||||||
Rtsp::eRtpType _eType = Rtsp::RTP_TCP;
|
|
||||||
Socket::Ptr _apUdpSock[2];
|
|
||||||
function<void(const Parser&)> _onHandshake;
|
|
||||||
//心跳定时器
|
//心跳定时器
|
||||||
std::shared_ptr<Timer> _pBeatTimer;
|
std::shared_ptr<Timer> _beat_timer;
|
||||||
|
std::weak_ptr<RtspMediaSource> _push_src;
|
||||||
|
RtspMediaSource::RingType::RingReader::Ptr _rtsp_reader;
|
||||||
|
//事件监听
|
||||||
|
Event _on_shutdown;
|
||||||
|
Event _on_published;
|
||||||
|
function<void(const Parser&)> _on_res_func;
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace mediakit */
|
} /* namespace mediakit */
|
||||||
|
Loading…
Reference in New Issue
Block a user