RtmpPusher.cpp 8.51 KB
Newer Older
xiongziliang committed
1
/*
xiongziliang committed
2
 * MIT License
xzl committed
3
 *
xiongziliang committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 * Copyright (c) 2016 xiongziliang <771730766@qq.com>
 *
 * 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.
xzl committed
25 26 27 28
 */
#include "RtmpPusher.h"
#include "Rtmp/utils.h"
#include "Rtsp/Rtsp.h"
xiongzilaing committed
29 30 31
#include "Util/util.h"
#include "Util/onceToken.h"
#include "Thread/ThreadPool.h"
xzl committed
32 33 34 35 36 37 38

using namespace ZL::Util;

namespace ZL {
namespace Rtmp {

unordered_map<string, RtmpPusher::rtmpCMDHandle> RtmpPusher::g_mapCmd;
39 40
RtmpPusher::RtmpPusher(const char *strVhost,const char *strApp,const char *strStream) {
    auto src = dynamic_pointer_cast<RtmpMediaSource>(MediaSource::find(RTMP_SCHEMA,strVhost,strApp,strStream));
xzl committed
41
    if (!src) {
42
        auto strErr = StrPrinter << "media source:"  << strVhost << "/" << strApp << "/" << strStream << "not found!" << endl;
xzl committed
43 44
        throw std::runtime_error(strErr);
    }
45 46 47 48 49 50 51 52 53 54 55 56 57
    init(src);
}
RtmpPusher::RtmpPusher(const RtmpMediaSource::Ptr &src){
    init(src);
}

void RtmpPusher::init(const RtmpMediaSource::Ptr  &src){
    static onceToken token([]() {
        g_mapCmd.emplace("_error",&RtmpPusher::onCmd_result);
        g_mapCmd.emplace("_result",&RtmpPusher::onCmd_result);
        g_mapCmd.emplace("onStatus",&RtmpPusher::onCmd_onStatus);
    }, []() {});
    m_pMediaSrc=src;
xzl committed
58 59 60 61 62 63 64 65 66 67 68
}

RtmpPusher::~RtmpPusher() {
	teardown();
	DebugL << endl;
}
void RtmpPusher::teardown() {
	if (alive()) {
		m_strApp.clear();
		m_strStream.clear();
		m_strTcUrl.clear();
xzl committed
69 70 71 72
		{
			lock_guard<recursive_mutex> lck(m_mtxOnResultCB);
			m_mapOnResultCB.clear();
		}
xzl committed
73
        {
xzl committed
74
            lock_guard<recursive_mutex> lck(m_mtxOnStatusCB);
xzl committed
75 76
            m_dqOnStatusCB.clear();
        }
xiongziliang committed
77
		m_pPublishTimer.reset();
xiongziliang committed
78
        reset();
xzl committed
79 80 81 82 83 84 85 86 87 88 89 90
        shutdown();
	}
}

void RtmpPusher::publish(const char* strUrl)  {
	teardown();
	string strHost = FindField(strUrl, "://", "/");
	m_strApp = 	FindField(strUrl, (strHost + "/").data(), "/");
    m_strStream = FindField(strUrl, (strHost + "/" + m_strApp + "/").data(), NULL);
    m_strTcUrl = string("rtmp://") + strHost + "/" + m_strApp;

    if (!m_strApp.size() || !m_strStream.size()) {
xiongziliang committed
91
        onPublishResult(SockException(Err_other,"rtmp url非法"));
xzl committed
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
        return;
    }
	DebugL << strHost << " " << m_strApp << " " << m_strStream;

	auto iPort = atoi(FindField(strHost.c_str(), ":", NULL).c_str());
	if (iPort <= 0) {
        //rtmp 默认端口1935
		iPort = 1935;
	} else {
        //服务器域名
		strHost = FindField(strHost.c_str(), NULL, ":");
	}
	startConnect(strHost, iPort);
}

void RtmpPusher::onErr(const SockException &ex){
xiongziliang committed
108
	onShutdown(ex);
xzl committed
109 110 111
}
void RtmpPusher::onConnect(const SockException &err){
	if(err.getErrCode()!=Err_success) {
xiongziliang committed
112
		onPublishResult(err);
xzl committed
113 114 115
		return;
	}
	weak_ptr<RtmpPusher> weakSelf = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
xiongziliang committed
116
	m_pPublishTimer.reset( new Timer(10,  [weakSelf]() {
xzl committed
117 118 119 120
		auto strongSelf=weakSelf.lock();
		if(!strongSelf) {
			return false;
		}
xiongziliang committed
121
		strongSelf->onPublishResult(SockException(Err_timeout,"publish rtmp timeout"));
xzl committed
122 123 124 125 126 127 128 129 130 131 132 133
		strongSelf->teardown();
		return false;
	}));
	startClientSession([weakSelf](){
        auto strongSelf=weakSelf.lock();
        if(!strongSelf) {
            return;
        }
        //strongSelf->sendChunkSize(60000);
        strongSelf->send_connect();
	});
}
134
void RtmpPusher::onRecv(const Buffer::Ptr &pBuf){
xzl committed
135 136 137 138
	try {
		onParseRtmp(pBuf->data(), pBuf->size());
	} catch (exception &e) {
		SockException ex(Err_other, e.what());
xiongziliang committed
139 140
		onPublishResult(ex);
		onShutdown(ex);
xzl committed
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
		teardown();
	}
}


inline void RtmpPusher::send_connect() {
	AMFValue obj(AMF_OBJECT);
	obj.set("app", m_strApp);
	obj.set("type", "nonprivate");
	obj.set("tcUrl", m_strTcUrl);
	obj.set("swfUrl", m_strTcUrl);
	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"){
160
			throw std::runtime_error(StrPrinter <<"connect 失败:" << level << " " << code << endl);
xzl committed
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
		}
		send_createStream();
	});
}

inline void RtmpPusher::send_createStream() {
	AMFValue obj(AMF_NULL);
	sendInvoke("createStream", obj);
	addOnResultCB([this](AMFDecoder &dec){
		//TraceL << "createStream result";
		dec.load<AMFValue>();
		m_ui32StreamId = dec.load<int>();
		send_publish();
	});
}
inline void RtmpPusher::send_publish() {
	AMFEncoder enc;
	enc << "publish" << ++m_iReqID << nullptr << m_strStream << m_strApp ;
	sendRequest(MSG_CMD, enc.data());

	addOnStatusCB([this](AMFValue &val) {
		auto level = val["level"].as_string();
		auto code = val["code"].as_string();
		if(level != "status") {
185
			throw std::runtime_error(StrPrinter <<"publish 失败:" << level << " " << code << endl);
xzl committed
186 187 188 189 190 191 192 193 194
		}
		//start send media
		send_metaData();
	});
}

inline void RtmpPusher::send_metaData(){
    auto src = m_pMediaSrc.lock();
    if (!src) {
195
        throw std::runtime_error("the media source was released");
xzl committed
196 197
    }
    if (!src->ready()) {
198
        throw std::runtime_error("the media source is not ready");
xzl committed
199 200 201 202 203 204
    }
    
    AMFEncoder enc;
    enc << "@setDataFrame" << "onMetaData" <<  src->getMetaData();
    sendRequest(MSG_DATA, enc.data());
    
xiongziliang committed
205
    src->getConfigFrame([&](const RtmpPacket::Ptr &pkt){
771730766@qq.com committed
206
        sendRtmp(pkt->typeId, m_ui32StreamId, pkt->strBuf, pkt->timeStamp, pkt->chunkId , true);
xzl committed
207 208 209 210
    });
    
    m_pRtmpReader = src->getRing()->attach();
    weak_ptr<RtmpPusher> weakSelf = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
xiongziliang committed
211
    m_pRtmpReader->setReadCB([weakSelf](const RtmpPacket::Ptr &pkt){
xzl committed
212 213 214 215
    	auto strongSelf = weakSelf.lock();
    	if(!strongSelf) {
    		return;
    	}
771730766@qq.com committed
216
    	strongSelf->sendRtmp(pkt->typeId, strongSelf->m_ui32StreamId, pkt->strBuf, pkt->timeStamp, pkt->chunkId , true);
xzl committed
217 218 219 220
    });
    m_pRtmpReader->setDetachCB([weakSelf](){
        auto strongSelf = weakSelf.lock();
        if(strongSelf){
xiongziliang committed
221
            strongSelf->onShutdown(SockException(Err_other,"媒体源被释放"));
xzl committed
222 223 224
            strongSelf->teardown();
        }
    });
xiongziliang committed
225
    onPublishResult(SockException(Err_success,"success"));
xzl committed
226 227 228
}
void RtmpPusher::onCmd_result(AMFDecoder &dec){
	auto iReqId = dec.load<int>();
xzl committed
229
	lock_guard<recursive_mutex> lck(m_mtxOnResultCB);
xzl committed
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
	auto it = m_mapOnResultCB.find(iReqId);
	if(it != m_mapOnResultCB.end()){
		it->second(dec);
		m_mapOnResultCB.erase(it);
	}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){
247
		throw std::runtime_error("onStatus:the result object was not found");
xzl committed
248 249
	}

xzl committed
250
    lock_guard<recursive_mutex> lck(m_mtxOnStatusCB);
xzl committed
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
	if(m_dqOnStatusCB.size()){
		m_dqOnStatusCB.front()(val);
		m_dqOnStatusCB.pop_front();
	}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: {
			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;
		}
}


} /* namespace Rtmp */
} /* namespace ZL */