ZLMediaKit/src/Rtmp/RtmpPusher.cpp

315 lines
9.2 KiB
C++
Raw Normal View History

2017-10-09 22:11:01 +08:00
/*
2017-09-27 16:20:30 +08:00
* MIT License
2017-04-01 16:35:56 +08:00
*
2019-05-08 15:40:07 +08:00
* Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
2017-09-27 16:20:30 +08:00
*
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
2017-04-01 16:35:56 +08:00
*/
#include "RtmpPusher.h"
#include "Rtmp/utils.h"
2017-04-25 11:35:41 +08:00
#include "Util/util.h"
#include "Util/onceToken.h"
#include "Thread/ThreadPool.h"
2018-10-24 17:17:55 +08:00
using namespace toolkit;
2019-03-27 18:56:49 +08:00
using namespace mediakit::Client;
2017-04-01 16:35:56 +08:00
2018-10-24 17:17:55 +08:00
namespace mediakit {
2017-04-01 16:35:56 +08:00
RtmpPusher::RtmpPusher(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr &src) : TcpClient(poller){
2019-03-26 18:04:06 +08:00
_pMediaSrc=src;
2017-04-01 16:35:56 +08:00
}
RtmpPusher::~RtmpPusher() {
teardown();
DebugL << endl;
}
void RtmpPusher::teardown() {
if (alive()) {
2018-10-24 15:43:52 +08:00
_strApp.clear();
_strStream.clear();
_strTcUrl.clear();
2017-05-12 13:59:58 +08:00
{
2018-10-24 15:43:52 +08:00
lock_guard<recursive_mutex> lck(_mtxOnResultCB);
_mapOnResultCB.clear();
2017-05-12 13:59:58 +08:00
}
2017-04-01 16:35:56 +08:00
{
2018-10-24 15:43:52 +08:00
lock_guard<recursive_mutex> lck(_mtxOnStatusCB);
_dqOnStatusCB.clear();
2017-04-01 16:35:56 +08:00
}
2018-10-24 15:43:52 +08:00
_pPublishTimer.reset();
2017-08-03 13:55:46 +08:00
reset();
2019-05-29 18:08:50 +08:00
shutdown(SockException(Err_shutdown,"teardown"));
2017-04-01 16:35:56 +08:00
}
}
void RtmpPusher::onPublishResult(const SockException &ex,bool handshakeCompleted) {
if(!handshakeCompleted){
//播放结果回调
_pPublishTimer.reset();
if(_onPublished){
_onPublished(ex);
}
} else {
//播放成功后异常断开回调
if(_onShutdown){
_onShutdown(ex);
}
2019-03-28 09:43:47 +08:00
}
if(ex){
teardown();
}
}
2019-03-27 18:41:52 +08:00
void RtmpPusher::publish(const string &strUrl) {
2017-04-01 16:35:56 +08:00
teardown();
2019-03-27 18:41:52 +08:00
string strHost = FindField(strUrl.data(), "://", "/");
_strApp = FindField(strUrl.data(), (strHost + "/").data(), "/");
_strStream = FindField(strUrl.data(), (strHost + "/" + _strApp + "/").data(), NULL);
2018-10-24 15:43:52 +08:00
_strTcUrl = string("rtmp://") + strHost + "/" + _strApp;
2017-04-01 16:35:56 +08:00
2018-10-24 15:43:52 +08:00
if (!_strApp.size() || !_strStream.size()) {
onPublishResult(SockException(Err_other,"rtmp url非法"),false);
2017-04-01 16:35:56 +08:00
return;
}
2018-10-24 15:43:52 +08:00
DebugL << strHost << " " << _strApp << " " << _strStream;
2017-04-01 16:35:56 +08:00
2019-03-27 18:41:52 +08:00
auto iPort = atoi(FindField(strHost.data(), ":", NULL).data());
2017-04-01 16:35:56 +08:00
if (iPort <= 0) {
//rtmp 默认端口1935
iPort = 1935;
} else {
//服务器域名
2019-03-27 18:41:52 +08:00
strHost = FindField(strHost.data(), NULL, ":");
2017-04-01 16:35:56 +08:00
}
2019-03-27 18:41:52 +08:00
weak_ptr<RtmpPusher> weakSelf = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
float publishTimeOutSec = (*this)[kTimeoutMS].as<int>() / 1000.0;
_pPublishTimer.reset( new Timer(publishTimeOutSec, [weakSelf]() {
2019-03-27 18:41:52 +08:00
auto strongSelf=weakSelf.lock();
if(!strongSelf) {
return false;
}
strongSelf->onPublishResult(SockException(Err_timeout,"publish rtmp timeout"), false);
2019-03-27 18:41:52 +08:00
return false;
},getPoller()));
if(!(*this)[kNetAdapter].empty()){
setNetAdapter((*this)[kNetAdapter]);
}
2017-04-01 16:35:56 +08:00
startConnect(strHost, iPort);
}
void RtmpPusher::onErr(const SockException &ex){
//定时器_pPublishTimer为空后表明握手结束了
onPublishResult(ex,!_pPublishTimer);
2017-04-01 16:35:56 +08:00
}
void RtmpPusher::onConnect(const SockException &err){
2019-03-27 18:41:52 +08:00
if(err) {
onPublishResult(err,false);
2017-04-01 16:35:56 +08:00
return;
}
//推流器不需要多大的接收缓存,节省内存占用
_sock->setReadBuffer(std::make_shared<BufferRaw>(1 * 1024));
2017-04-01 16:35:56 +08:00
weak_ptr<RtmpPusher> weakSelf = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
startClientSession([weakSelf](){
auto strongSelf=weakSelf.lock();
if(!strongSelf) {
return;
}
2019-03-27 18:41:52 +08:00
strongSelf->sendChunkSize(60000);
2017-04-01 16:35:56 +08:00
strongSelf->send_connect();
});
}
2018-02-23 15:36:51 +08:00
void RtmpPusher::onRecv(const Buffer::Ptr &pBuf){
2017-04-01 16:35:56 +08:00
try {
onParseRtmp(pBuf->data(), pBuf->size());
} catch (exception &e) {
SockException ex(Err_other, e.what());
//定时器_pPublishTimer为空后表明握手结束了
onPublishResult(ex,!_pPublishTimer);
2017-04-01 16:35:56 +08:00
}
}
inline void RtmpPusher::send_connect() {
AMFValue obj(AMF_OBJECT);
2018-10-24 15:43:52 +08:00
obj.set("app", _strApp);
2017-04-01 16:35:56 +08:00
obj.set("type", "nonprivate");
2018-10-24 15:43:52 +08:00
obj.set("tcUrl", _strTcUrl);
obj.set("swfUrl", _strTcUrl);
2017-04-01 16:35:56 +08:00
sendInvoke("connect", obj);
addOnResultCB([this](AMFDecoder &dec){
//TraceL << "connect result";
dec.load<AMFValue>();
auto val = dec.load<AMFValue>();
auto level = val["level"].as_string();
auto code = val["code"].as_string();
if(level != "status"){
2017-08-09 18:39:30 +08:00
throw std::runtime_error(StrPrinter <<"connect 失败:" << level << " " << code << endl);
2017-04-01 16:35:56 +08:00
}
send_createStream();
});
}
inline void RtmpPusher::send_createStream() {
AMFValue obj(AMF_NULL);
sendInvoke("createStream", obj);
addOnResultCB([this](AMFDecoder &dec){
//TraceL << "createStream result";
dec.load<AMFValue>();
2018-10-24 15:43:52 +08:00
_ui32StreamId = dec.load<int>();
2017-04-01 16:35:56 +08:00
send_publish();
});
}
inline void RtmpPusher::send_publish() {
AMFEncoder enc;
2018-10-24 15:43:52 +08:00
enc << "publish" << ++_iReqID << nullptr << _strStream << _strApp ;
2017-04-01 16:35:56 +08:00
sendRequest(MSG_CMD, enc.data());
addOnStatusCB([this](AMFValue &val) {
auto level = val["level"].as_string();
auto code = val["code"].as_string();
if(level != "status") {
2017-08-09 18:39:30 +08:00
throw std::runtime_error(StrPrinter <<"publish 失败:" << level << " " << code << endl);
2017-04-01 16:35:56 +08:00
}
//start send media
send_metaData();
});
}
inline void RtmpPusher::send_metaData(){
2018-10-24 15:43:52 +08:00
auto src = _pMediaSrc.lock();
2017-04-01 16:35:56 +08:00
if (!src) {
2017-08-09 18:39:30 +08:00
throw std::runtime_error("the media source was released");
2017-04-01 16:35:56 +08:00
}
2017-04-01 16:35:56 +08:00
AMFEncoder enc;
enc << "@setDataFrame" << "onMetaData" << src->getMetaData();
sendRequest(MSG_DATA, enc.data());
2017-12-04 23:55:09 +08:00
src->getConfigFrame([&](const RtmpPacket::Ptr &pkt){
sendRtmp(pkt->typeId, _ui32StreamId, pkt, pkt->timeStamp, pkt->chunkId );
2017-04-01 16:35:56 +08:00
});
_pRtmpReader = src->getRing()->attach(getPoller());
2017-04-01 16:35:56 +08:00
weak_ptr<RtmpPusher> weakSelf = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
2018-10-24 15:43:52 +08:00
_pRtmpReader->setReadCB([weakSelf](const RtmpPacket::Ptr &pkt){
2017-04-01 16:35:56 +08:00
auto strongSelf = weakSelf.lock();
if(!strongSelf) {
return;
}
strongSelf->sendRtmp(pkt->typeId, strongSelf->_ui32StreamId, pkt, pkt->timeStamp, pkt->chunkId);
2017-04-01 16:35:56 +08:00
});
2018-10-24 15:43:52 +08:00
_pRtmpReader->setDetachCB([weakSelf](){
2017-04-01 16:35:56 +08:00
auto strongSelf = weakSelf.lock();
if(strongSelf){
strongSelf->onPublishResult(SockException(Err_other,"媒体源被释放"), !strongSelf->_pPublishTimer);
2017-04-01 16:35:56 +08:00
}
});
onPublishResult(SockException(Err_success,"success"), false);
//提升发送性能
setSocketFlags();
2017-04-01 16:35:56 +08:00
}
void RtmpPusher::setSocketFlags(){
GET_CONFIG(bool,ultraLowDelay,General::kUltraLowDelay);
if(!ultraLowDelay) {
//提高发送性能
(*this) << SocketFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE);
SockUtil::setNoDelay(_sock->rawFD(), false);
}
}
2017-04-01 16:35:56 +08:00
void RtmpPusher::onCmd_result(AMFDecoder &dec){
auto iReqId = dec.load<int>();
2018-10-24 15:43:52 +08:00
lock_guard<recursive_mutex> lck(_mtxOnResultCB);
auto it = _mapOnResultCB.find(iReqId);
if(it != _mapOnResultCB.end()){
2017-04-01 16:35:56 +08:00
it->second(dec);
2018-10-24 15:43:52 +08:00
_mapOnResultCB.erase(it);
2017-04-01 16:35:56 +08:00
}else{
WarnL << "unhandled _result";
}
}
void RtmpPusher::onCmd_onStatus(AMFDecoder &dec) {
AMFValue val;
while(true){
val = dec.load<AMFValue>();
if(val.type() == AMF_OBJECT){
break;
}
}
if(val.type() != AMF_OBJECT){
2017-08-09 18:39:30 +08:00
throw std::runtime_error("onStatus:the result object was not found");
2017-04-01 16:35:56 +08:00
}
2018-10-24 15:43:52 +08:00
lock_guard<recursive_mutex> lck(_mtxOnStatusCB);
if(_dqOnStatusCB.size()){
_dqOnStatusCB.front()(val);
_dqOnStatusCB.pop_front();
2017-04-01 16:35:56 +08:00
}else{
auto level = val["level"];
auto code = val["code"].as_string();
if(level.type() == AMF_STRING){
if(level.as_string() != "status"){
throw std::runtime_error(StrPrinter <<"onStatus 失败:" << level.as_string() << " " << code << endl);
}
}
}
}
void RtmpPusher::onRtmpChunk(RtmpPacket &chunkData) {
switch (chunkData.typeId) {
case MSG_CMD:
case MSG_CMD3: {
2019-03-26 18:04:06 +08:00
typedef void (RtmpPusher::*rtmpCMDHandle)(AMFDecoder &dec);
static unordered_map<string, rtmpCMDHandle> g_mapCmd;
static onceToken token([]() {
g_mapCmd.emplace("_error",&RtmpPusher::onCmd_result);
g_mapCmd.emplace("_result",&RtmpPusher::onCmd_result);
g_mapCmd.emplace("onStatus",&RtmpPusher::onCmd_onStatus);
}, []() {});
2017-04-01 16:35:56 +08:00
AMFDecoder dec(chunkData.strBuf, 0);
std::string type = dec.load<std::string>();
auto it = g_mapCmd.find(type);
if(it != g_mapCmd.end()){
auto fun = it->second;
(this->*fun)(dec);
}else{
WarnL << "can not support cmd:" << type;
}
}
break;
default:
//WarnL << "unhandled message:" << (int) chunkData.typeId << hexdump(chunkData.strBuf.data(), chunkData.strBuf.size());
break;
}
}
2018-10-24 17:17:55 +08:00
} /* namespace mediakit */
2017-04-01 16:35:56 +08:00