2017-10-09 22:11:01 +08:00
|
|
|
|
/*
|
2020-04-04 20:30:09 +08:00
|
|
|
|
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
|
2017-09-27 16:20:30 +08:00
|
|
|
|
*
|
2021-01-17 18:31:50 +08:00
|
|
|
|
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
|
2017-09-27 16:20:30 +08:00
|
|
|
|
*
|
2020-04-04 20:30:09 +08:00
|
|
|
|
* Use of this source code is governed by MIT license that can be found in the
|
|
|
|
|
* LICENSE file in the root of the source tree. All contributing project authors
|
|
|
|
|
* may be found in the AUTHORS file in the root of the source tree.
|
2017-04-01 16:35:56 +08:00
|
|
|
|
*/
|
2020-04-04 20:30:09 +08:00
|
|
|
|
|
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
|
|
|
|
|
2020-08-30 11:40:03 +08:00
|
|
|
|
RtmpPusher::RtmpPusher(const EventPoller::Ptr &poller, const RtmpMediaSource::Ptr &src) : TcpClient(poller){
|
|
|
|
|
_publish_src = src;
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RtmpPusher::~RtmpPusher() {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
teardown();
|
|
|
|
|
DebugL << endl;
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
2020-08-30 11:40:03 +08:00
|
|
|
|
|
2017-04-01 16:35:56 +08:00
|
|
|
|
void RtmpPusher::teardown() {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
if (alive()) {
|
2020-08-30 11:40:03 +08:00
|
|
|
|
_app.clear();
|
|
|
|
|
_stream_id.clear();
|
|
|
|
|
_tc_url.clear();
|
2020-03-20 11:51:24 +08:00
|
|
|
|
{
|
2020-08-30 11:40:03 +08:00
|
|
|
|
lock_guard<recursive_mutex> lck(_mtx_on_result);
|
|
|
|
|
_map_on_result.clear();
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
2017-04-01 16:35:56 +08:00
|
|
|
|
{
|
2020-08-30 11:40:03 +08:00
|
|
|
|
lock_guard<recursive_mutex> lck(_mtx_on_status);
|
|
|
|
|
_deque_on_status.clear();
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
2020-08-30 11:40:03 +08:00
|
|
|
|
_publish_timer.reset();
|
2017-08-03 13:55:46 +08:00
|
|
|
|
reset();
|
2020-08-30 11:40:03 +08:00
|
|
|
|
shutdown(SockException(Err_shutdown, "teardown"));
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-30 11:40:03 +08:00
|
|
|
|
void RtmpPusher::onPublishResult(const SockException &ex, bool handshake_done) {
|
|
|
|
|
if (ex.getErrCode() == Err_shutdown) {
|
|
|
|
|
//主动shutdown的,不触发回调
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!handshake_done) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//播放结果回调
|
2020-08-30 11:40:03 +08:00
|
|
|
|
_publish_timer.reset();
|
|
|
|
|
if (_on_published) {
|
|
|
|
|
_on_published(ex);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
//播放成功后异常断开回调
|
2020-08-30 11:40:03 +08:00
|
|
|
|
if (_on_shutdown) {
|
|
|
|
|
_on_shutdown(ex);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-28 09:43:47 +08:00
|
|
|
|
|
2020-08-30 11:40:03 +08:00
|
|
|
|
if (ex) {
|
2021-01-17 10:28:06 +08:00
|
|
|
|
shutdown(SockException(Err_shutdown,"teardown"));
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
2019-03-28 09:43:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-30 11:40:03 +08:00
|
|
|
|
void RtmpPusher::publish(const string &url) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
teardown();
|
2020-08-30 11:40:03 +08:00
|
|
|
|
string host_url = FindField(url.data(), "://", "/");
|
|
|
|
|
_app = FindField(url.data(), (host_url + "/").data(), "/");
|
|
|
|
|
_stream_id = FindField(url.data(), (host_url + "/" + _app + "/").data(), NULL);
|
|
|
|
|
_tc_url = string("rtmp://") + host_url + "/" + _app;
|
2017-04-01 16:35:56 +08:00
|
|
|
|
|
2020-08-30 11:40:03 +08:00
|
|
|
|
if (!_app.size() || !_stream_id.size()) {
|
|
|
|
|
onPublishResult(SockException(Err_other, "rtmp url非法"), false);
|
2017-04-01 16:35:56 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2020-08-30 11:40:03 +08:00
|
|
|
|
DebugL << host_url << " " << _app << " " << _stream_id;
|
2017-04-01 16:35:56 +08:00
|
|
|
|
|
2020-08-30 11:40:03 +08:00
|
|
|
|
auto iPort = atoi(FindField(host_url.data(), ":", NULL).data());
|
2020-03-20 11:51:24 +08:00
|
|
|
|
if (iPort <= 0) {
|
2017-04-01 16:35:56 +08:00
|
|
|
|
//rtmp 默认端口1935
|
2020-03-20 11:51:24 +08:00
|
|
|
|
iPort = 1935;
|
|
|
|
|
} else {
|
2017-04-01 16:35:56 +08:00
|
|
|
|
//服务器域名
|
2020-08-30 11:40:03 +08:00
|
|
|
|
host_url = FindField(host_url.data(), NULL, ":");
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
2019-03-27 18:41:52 +08:00
|
|
|
|
|
|
|
|
|
weak_ptr<RtmpPusher> weakSelf = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
|
2021-01-17 18:31:50 +08:00
|
|
|
|
float publishTimeOutSec = (*this)[kTimeoutMS].as<int>() / 1000.0f;
|
2020-08-30 11:40:03 +08:00
|
|
|
|
_publish_timer.reset(new Timer(publishTimeOutSec, [weakSelf]() {
|
|
|
|
|
auto strongSelf = weakSelf.lock();
|
|
|
|
|
if (!strongSelf) {
|
2019-03-27 18:41:52 +08:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-08-30 11:40:03 +08:00
|
|
|
|
strongSelf->onPublishResult(SockException(Err_timeout, "publish rtmp timeout"), false);
|
2019-03-27 18:41:52 +08:00
|
|
|
|
return false;
|
2020-08-30 11:40:03 +08:00
|
|
|
|
}, getPoller()));
|
2019-03-27 18:41:52 +08:00
|
|
|
|
|
2020-08-30 11:40:03 +08:00
|
|
|
|
if (!(*this)[kNetAdapter].empty()) {
|
2019-03-27 18:41:52 +08:00
|
|
|
|
setNetAdapter((*this)[kNetAdapter]);
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-30 11:40:03 +08:00
|
|
|
|
startConnect(host_url, iPort);
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RtmpPusher::onErr(const SockException &ex){
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//定时器_pPublishTimer为空后表明握手结束了
|
2020-08-30 11:40:03 +08:00
|
|
|
|
onPublishResult(ex, !_publish_timer);
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
2020-08-30 11:40:03 +08:00
|
|
|
|
|
2017-04-01 16:35:56 +08:00
|
|
|
|
void RtmpPusher::onConnect(const SockException &err){
|
2020-08-30 11:40:03 +08:00
|
|
|
|
if (err) {
|
|
|
|
|
onPublishResult(err, false);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2020-08-30 11:40:03 +08:00
|
|
|
|
weak_ptr<RtmpPusher> weak_self = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
|
|
|
|
|
startClientSession([weak_self]() {
|
|
|
|
|
auto strong_self = weak_self.lock();
|
|
|
|
|
if (!strong_self) {
|
2017-04-01 16:35:56 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2019-03-27 18:41:52 +08:00
|
|
|
|
|
2020-08-30 11:40:03 +08:00
|
|
|
|
strong_self->sendChunkSize(60000);
|
|
|
|
|
strong_self->send_connect();
|
2020-03-20 11:51:24 +08:00
|
|
|
|
});
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
2020-08-30 11:40:03 +08:00
|
|
|
|
|
|
|
|
|
void RtmpPusher::onRecv(const Buffer::Ptr &buf){
|
2020-03-20 11:51:24 +08:00
|
|
|
|
try {
|
2020-08-30 11:40:03 +08:00
|
|
|
|
onParseRtmp(buf->data(), buf->size());
|
2020-03-20 11:51:24 +08:00
|
|
|
|
} catch (exception &e) {
|
|
|
|
|
SockException ex(Err_other, e.what());
|
|
|
|
|
//定时器_pPublishTimer为空后表明握手结束了
|
2020-08-30 11:40:03 +08:00
|
|
|
|
onPublishResult(ex, !_publish_timer);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void RtmpPusher::send_connect() {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
AMFValue obj(AMF_OBJECT);
|
2020-08-30 11:40:03 +08:00
|
|
|
|
obj.set("app", _app);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
obj.set("type", "nonprivate");
|
2020-08-30 11:40:03 +08:00
|
|
|
|
obj.set("tcUrl", _tc_url);
|
|
|
|
|
obj.set("swfUrl", _tc_url);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
sendInvoke("connect", obj);
|
2020-08-30 11:40:03 +08:00
|
|
|
|
addOnResultCB([this](AMFDecoder &dec) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//TraceL << "connect result";
|
|
|
|
|
dec.load<AMFValue>();
|
|
|
|
|
auto val = dec.load<AMFValue>();
|
|
|
|
|
auto level = val["level"].as_string();
|
|
|
|
|
auto code = val["code"].as_string();
|
2020-08-30 11:40:03 +08:00
|
|
|
|
if (level != "status") {
|
|
|
|
|
throw std::runtime_error(StrPrinter << "connect 失败:" << level << " " << code << endl);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
|
|
|
|
send_createStream();
|
|
|
|
|
});
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void RtmpPusher::send_createStream() {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
AMFValue obj(AMF_NULL);
|
|
|
|
|
sendInvoke("createStream", obj);
|
2020-08-30 11:40:03 +08:00
|
|
|
|
addOnResultCB([this](AMFDecoder &dec) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//TraceL << "createStream result";
|
|
|
|
|
dec.load<AMFValue>();
|
2020-08-30 10:48:34 +08:00
|
|
|
|
_stream_index = dec.load<int>();
|
2020-03-20 11:51:24 +08:00
|
|
|
|
send_publish();
|
|
|
|
|
});
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
2020-08-30 11:40:03 +08:00
|
|
|
|
|
2017-04-01 16:35:56 +08:00
|
|
|
|
inline void RtmpPusher::send_publish() {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
AMFEncoder enc;
|
2020-08-30 11:40:03 +08:00
|
|
|
|
enc << "publish" << ++_send_req_id << nullptr << _stream_id << _app;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
sendRequest(MSG_CMD, enc.data());
|
2017-04-01 16:35:56 +08:00
|
|
|
|
|
2020-03-20 11:51:24 +08:00
|
|
|
|
addOnStatusCB([this](AMFValue &val) {
|
|
|
|
|
auto level = val["level"].as_string();
|
|
|
|
|
auto code = val["code"].as_string();
|
2020-08-30 11:40:03 +08:00
|
|
|
|
if (level != "status") {
|
|
|
|
|
throw std::runtime_error(StrPrinter << "publish 失败:" << level << " " << code << endl);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
|
|
|
|
//start send media
|
|
|
|
|
send_metaData();
|
|
|
|
|
});
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void RtmpPusher::send_metaData(){
|
2020-08-30 11:40:03 +08:00
|
|
|
|
auto src = _publish_src.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
|
|
|
|
}
|
2018-08-31 14:13:00 +08:00
|
|
|
|
|
2017-04-01 16:35:56 +08:00
|
|
|
|
AMFEncoder enc;
|
2020-08-30 11:40:03 +08:00
|
|
|
|
enc << "@setDataFrame" << "onMetaData" << src->getMetaData();
|
2017-04-01 16:35:56 +08:00
|
|
|
|
sendRequest(MSG_DATA, enc.data());
|
2020-08-30 11:40:03 +08:00
|
|
|
|
|
|
|
|
|
src->getConfigFrame([&](const RtmpPacket::Ptr &pkt) {
|
|
|
|
|
sendRtmp(pkt->type_id, _stream_index, pkt, pkt->time_stamp, pkt->chunk_id);
|
2017-04-01 16:35:56 +08:00
|
|
|
|
});
|
2020-08-30 11:40:03 +08:00
|
|
|
|
|
|
|
|
|
_rtmp_reader = src->getRing()->attach(getPoller());
|
|
|
|
|
weak_ptr<RtmpPusher> weak_self = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
|
|
|
|
|
_rtmp_reader->setReadCB([weak_self](const RtmpMediaSource::RingDataType &pkt) {
|
|
|
|
|
auto strong_self = weak_self.lock();
|
|
|
|
|
if (!strong_self) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2020-04-09 16:19:03 +08:00
|
|
|
|
|
2021-01-17 18:31:50 +08:00
|
|
|
|
size_t i = 0;
|
|
|
|
|
auto size = pkt->size();
|
2020-08-30 11:40:03 +08:00
|
|
|
|
strong_self->setSendFlushFlag(false);
|
|
|
|
|
pkt->for_each([&](const RtmpPacket::Ptr &rtmp) {
|
|
|
|
|
if (++i == size) {
|
|
|
|
|
strong_self->setSendFlushFlag(true);
|
2020-04-09 16:19:03 +08:00
|
|
|
|
}
|
2020-08-30 11:40:03 +08:00
|
|
|
|
strong_self->sendRtmp(rtmp->type_id, strong_self->_stream_index, rtmp, rtmp->time_stamp, rtmp->chunk_id);
|
2020-04-09 16:19:03 +08:00
|
|
|
|
});
|
2017-04-01 16:35:56 +08:00
|
|
|
|
});
|
2020-08-30 11:40:03 +08:00
|
|
|
|
_rtmp_reader->setDetachCB([weak_self]() {
|
|
|
|
|
auto strong_self = weak_self.lock();
|
|
|
|
|
if (strong_self) {
|
|
|
|
|
strong_self->onPublishResult(SockException(Err_other, "媒体源被释放"), !strong_self->_publish_timer);
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
});
|
2020-08-30 11:40:03 +08:00
|
|
|
|
onPublishResult(SockException(Err_success, "success"), false);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//提升发送性能
|
|
|
|
|
setSocketFlags();
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
2019-09-04 18:57:54 +08:00
|
|
|
|
|
|
|
|
|
void RtmpPusher::setSocketFlags(){
|
2020-04-29 11:08:43 +08:00
|
|
|
|
GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS);
|
2020-08-30 11:40:03 +08:00
|
|
|
|
if (mergeWriteMS > 0) {
|
2019-09-04 18:57:54 +08:00
|
|
|
|
//提高发送性能
|
2020-04-23 17:50:12 +08:00
|
|
|
|
setSendFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE);
|
2020-09-12 19:03:52 +08:00
|
|
|
|
SockUtil::setNoDelay(getSock()->rawFD(), false);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
2019-09-04 18:57:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-01 16:35:56 +08:00
|
|
|
|
void RtmpPusher::onCmd_result(AMFDecoder &dec){
|
2020-08-30 11:40:03 +08:00
|
|
|
|
auto req_id = dec.load<int>();
|
|
|
|
|
lock_guard<recursive_mutex> lck(_mtx_on_result);
|
|
|
|
|
auto it = _map_on_result.find(req_id);
|
|
|
|
|
if (it != _map_on_result.end()) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
it->second(dec);
|
2020-08-30 11:40:03 +08:00
|
|
|
|
_map_on_result.erase(it);
|
|
|
|
|
} else {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
WarnL << "unhandled _result";
|
|
|
|
|
}
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
2020-08-30 11:40:03 +08:00
|
|
|
|
|
2017-04-01 16:35:56 +08:00
|
|
|
|
void RtmpPusher::onCmd_onStatus(AMFDecoder &dec) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
AMFValue val;
|
2020-08-30 11:40:03 +08:00
|
|
|
|
while (true) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
val = dec.load<AMFValue>();
|
2020-08-30 11:40:03 +08:00
|
|
|
|
if (val.type() == AMF_OBJECT) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-30 11:40:03 +08:00
|
|
|
|
if (val.type() != AMF_OBJECT) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
throw std::runtime_error("onStatus:the result object was not found");
|
|
|
|
|
}
|
2017-04-01 16:35:56 +08:00
|
|
|
|
|
2020-08-30 11:40:03 +08:00
|
|
|
|
lock_guard<recursive_mutex> lck(_mtx_on_status);
|
|
|
|
|
if (_deque_on_status.size()) {
|
|
|
|
|
_deque_on_status.front()(val);
|
|
|
|
|
_deque_on_status.pop_front();
|
|
|
|
|
} else {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
auto level = val["level"];
|
|
|
|
|
auto code = val["code"].as_string();
|
2020-08-30 11:40:03 +08:00
|
|
|
|
if (level.type() == AMF_STRING) {
|
|
|
|
|
if (level.as_string() != "status") {
|
|
|
|
|
throw std::runtime_error(StrPrinter << "onStatus 失败:" << level.as_string() << " " << code << endl);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-30 11:40:03 +08:00
|
|
|
|
void RtmpPusher::onRtmpChunk(RtmpPacket &chunk_data) {
|
|
|
|
|
switch (chunk_data.type_id) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
case MSG_CMD:
|
|
|
|
|
case MSG_CMD3: {
|
|
|
|
|
typedef void (RtmpPusher::*rtmpCMDHandle)(AMFDecoder &dec);
|
|
|
|
|
static unordered_map<string, rtmpCMDHandle> g_mapCmd;
|
|
|
|
|
static onceToken token([]() {
|
2020-08-30 11:40:03 +08:00
|
|
|
|
g_mapCmd.emplace("_error", &RtmpPusher::onCmd_result);
|
|
|
|
|
g_mapCmd.emplace("_result", &RtmpPusher::onCmd_result);
|
|
|
|
|
g_mapCmd.emplace("onStatus", &RtmpPusher::onCmd_onStatus);
|
2020-08-30 10:48:34 +08:00
|
|
|
|
});
|
2019-03-26 18:04:06 +08:00
|
|
|
|
|
2020-08-30 11:40:03 +08:00
|
|
|
|
AMFDecoder dec(chunk_data.buffer, 0);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
std::string type = dec.load<std::string>();
|
|
|
|
|
auto it = g_mapCmd.find(type);
|
2020-08-30 11:40:03 +08:00
|
|
|
|
if (it != g_mapCmd.end()) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
auto fun = it->second;
|
|
|
|
|
(this->*fun)(dec);
|
2020-08-30 11:40:03 +08:00
|
|
|
|
} else {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
WarnL << "can not support cmd:" << type;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2020-08-30 11:40:03 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-20 11:51:24 +08:00
|
|
|
|
default:
|
2020-08-30 11:40:03 +08:00
|
|
|
|
//WarnL << "unhandled message:" << (int) chunk_data.type_id << hexdump(chunk_data.buffer.data(), chunk_data.buffer.size());
|
2020-03-20 11:51:24 +08:00
|
|
|
|
break;
|
2020-08-30 11:40:03 +08:00
|
|
|
|
}
|
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
|
|
|
|
|