ZLMediaKit/src/Rtmp/RtmpPlayer.cpp

419 lines
13 KiB
C++
Raw Normal View History

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
*
* 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
*/
#include "RtmpPlayer.h"
2017-04-25 11:35:41 +08:00
#include "Rtmp/utils.h"
2017-04-01 16:35:56 +08:00
#include "Util/util.h"
#include "Util/onceToken.h"
2017-04-25 11:35:41 +08:00
#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-25 11:35:41 +08:00
2018-10-24 17:17:55 +08:00
namespace mediakit {
2017-04-01 16:35:56 +08:00
2020-08-30 10:48:34 +08:00
RtmpPlayer::RtmpPlayer(const EventPoller::Ptr &poller) : TcpClient(poller) {}
2017-04-01 16:35:56 +08:00
RtmpPlayer::~RtmpPlayer() {
2020-03-20 11:51:24 +08:00
DebugL << endl;
2017-04-01 16:35:56 +08:00
}
2017-04-01 16:35:56 +08:00
void RtmpPlayer::teardown() {
2020-03-20 11:51:24 +08:00
if (alive()) {
shutdown(SockException(Err_shutdown,"teardown"));
}
2020-08-30 10:48:34 +08:00
_app.clear();
_stream_id.clear();
_tc_url.clear();
_beat_timer.reset();
_play_timer.reset();
_rtmp_recv_timer.reset();
_seek_ms = 0;
2020-03-20 11:51:24 +08:00
RtmpProtocol::reset();
2020-08-30 10:48:34 +08:00
CLEAR_ARR(_fist_stamp);
CLEAR_ARR(_now_stamp);
2020-03-20 11:51:24 +08:00
2020-08-30 10:48:34 +08:00
lock_guard<recursive_mutex> lck(_mtx_on_result);
_map_on_result.clear();
lock_guard<recursive_mutex> lck2(_mtx_on_status);
_deque_on_status.clear();
2017-04-01 16:35:56 +08:00
}
2019-03-27 18:41:52 +08:00
void RtmpPlayer::play(const string &strUrl) {
2020-03-20 11:51:24 +08:00
teardown();
2020-08-30 10:48:34 +08:00
string host_url = FindField(strUrl.data(), "://", "/");
_app = FindField(strUrl.data(), (host_url + "/").data(), "/");
_stream_id = FindField(strUrl.data(), (host_url + "/" + _app + "/").data(), NULL);
_tc_url = string("rtmp://") + host_url + "/" + _app;
2017-04-01 16:35:56 +08:00
2020-08-30 10:48:34 +08:00
if (!_app.size() || !_stream_id.size()) {
onPlayResult_l(SockException(Err_other, "rtmp url非法"), false);
2017-04-01 16:35:56 +08:00
return;
}
2020-08-30 10:48:34 +08:00
DebugL << host_url << " " << _app << " " << _stream_id;
2017-04-01 16:35:56 +08:00
2020-08-30 10:48:34 +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 10:48:34 +08:00
host_url = FindField(host_url.data(), NULL, ":");
2020-03-20 11:51:24 +08:00
}
2020-08-30 10:48:34 +08:00
if (!(*this)[kNetAdapter].empty()) {
2020-03-20 11:51:24 +08:00
setNetAdapter((*this)[kNetAdapter]);
}
2020-08-30 10:48:34 +08:00
weak_ptr<RtmpPlayer> weak_self = dynamic_pointer_cast<RtmpPlayer>(shared_from_this());
float play_timeout_sec = (*this)[kTimeoutMS].as<int>() / 1000.0f;
2020-08-30 10:48:34 +08:00
_play_timer.reset(new Timer(play_timeout_sec, [weak_self]() {
auto strong_self = weak_self.lock();
if (!strong_self) {
2020-03-20 11:51:24 +08:00
return false;
}
2020-08-30 10:48:34 +08:00
strong_self->onPlayResult_l(SockException(Err_timeout, "play rtmp timeout"), false);
2020-03-20 11:51:24 +08:00
return false;
2020-08-30 10:48:34 +08:00
}, getPoller()));
2020-03-20 11:51:24 +08:00
_metadata_got = false;
2020-08-30 10:48:34 +08:00
startConnect(host_url, iPort, play_timeout_sec);
}
2020-08-30 10:48:34 +08:00
void RtmpPlayer::onErr(const SockException &ex){
//定时器_pPlayTimer为空后表明握手结束了
2020-08-30 10:48:34 +08:00
onPlayResult_l(ex, !_play_timer);
2019-03-28 12:02:59 +08:00
}
2020-08-30 11:40:03 +08:00
void RtmpPlayer::onPlayResult_l(const SockException &ex, bool handshake_done) {
2020-08-30 10:48:34 +08:00
if (ex.getErrCode() == Err_shutdown) {
//主动shutdown的不触发回调
return;
2019-05-08 15:27:37 +08:00
}
2020-08-30 10:48:34 +08:00
WarnL << ex.getErrCode() << " " << ex.what();
2020-08-30 11:40:03 +08:00
if (!handshake_done) {
2020-03-20 11:51:24 +08:00
//开始播放阶段
2020-08-30 10:48:34 +08:00
_play_timer.reset();
2020-04-08 11:16:09 +08:00
//是否为性能测试模式
_benchmark_mode = (*this)[Client::kBenchmarkMode].as<int>();
2020-08-30 10:48:34 +08:00
onPlayResult(ex);
2020-03-20 11:51:24 +08:00
} else if (ex) {
//播放成功后异常断开回调
onShutdown(ex);
} else {
//恢复播放
onResume();
}
2020-08-30 10:48:34 +08:00
if (!ex) {
//播放成功恢复rtmp接收超时定时器
_rtmp_recv_ticker.resetTime();
int timeout_ms = (*this)[kMediaTimeoutMS].as<int>();
weak_ptr<RtmpPlayer> weakSelf = dynamic_pointer_cast<RtmpPlayer>(shared_from_this());
auto lam = [weakSelf, timeout_ms]() {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
return false;
}
if (strongSelf->_rtmp_recv_ticker.elapsedTime() > timeout_ms) {
//接收rtmp媒体数据超时
SockException ex(Err_timeout, "receive rtmp timeout");
strongSelf->onPlayResult_l(ex, true);
return false;
}
return true;
};
//创建rtmp数据接收超时检测定时器
_rtmp_recv_timer = std::make_shared<Timer>(timeout_ms / 2000.0f, lam, getPoller());
2020-08-30 10:48:34 +08:00
} else {
2021-01-17 10:28:06 +08:00
shutdown(SockException(Err_shutdown,"teardown"));
2020-03-20 11:51:24 +08:00
}
}
2020-08-30 10:48:34 +08:00
void RtmpPlayer::onConnect(const SockException &err){
2020-08-30 10:48:34 +08:00
if (err.getErrCode() != Err_success) {
2020-03-20 11:51:24 +08:00
onPlayResult_l(err, false);
return;
}
2020-08-30 10:48:34 +08:00
weak_ptr<RtmpPlayer> weakSelf = dynamic_pointer_cast<RtmpPlayer>(shared_from_this());
startClientSession([weakSelf]() {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
2017-04-01 16:35:56 +08:00
return;
}
2020-03-20 11:51:24 +08:00
strongSelf->send_connect();
});
2017-04-01 16:35:56 +08:00
}
2020-08-30 10:48:34 +08:00
2020-08-30 11:40:03 +08:00
void RtmpPlayer::onRecv(const Buffer::Ptr &buf){
2020-03-20 11:51:24 +08:00
try {
2020-08-30 10:48:34 +08:00
if (_benchmark_mode && !_play_timer) {
2020-04-08 11:16:09 +08:00
//在性能测试模式下如果rtmp握手完毕后不再解析rtmp包
2020-08-30 10:48:34 +08:00
_rtmp_recv_ticker.resetTime();
2020-04-08 11:16:09 +08:00
return;
}
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());
//定时器_pPlayTimer为空后表明握手结束了
2020-08-30 10:48:34 +08:00
onPlayResult_l(ex, !_play_timer);
2020-03-20 11:51:24 +08:00
}
2017-04-01 16:35:56 +08:00
}
void RtmpPlayer::pause(bool bPause) {
2020-03-20 11:51:24 +08:00
send_pause(bPause);
2017-04-01 16:35:56 +08:00
}
inline void RtmpPlayer::send_connect() {
2020-03-20 11:51:24 +08:00
AMFValue obj(AMF_OBJECT);
2020-08-30 10:48:34 +08:00
obj.set("app", _app);
obj.set("tcUrl", _tc_url);
2020-03-20 11:51:24 +08:00
//未使用代理
obj.set("fpad", false);
//参考librtmp,什么作用?
obj.set("capabilities", 15);
//SUPPORT_VID_CLIENT_SEEK 支持seek
obj.set("videoFunction", 1);
2017-04-01 16:35:56 +08:00
//只支持aac
2020-08-30 10:48:34 +08:00
obj.set("audioCodecs", (double) (0x0400));
2017-04-01 16:35:56 +08:00
//只支持H264
2020-08-30 10:48:34 +08:00
obj.set("videoCodecs", (double) (0x0080));
2020-03-20 11:51:24 +08:00
sendInvoke("connect", obj);
2020-08-30 10:48:34 +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 10:48:34 +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 RtmpPlayer::send_createStream() {
2020-03-20 11:51:24 +08:00
AMFValue obj(AMF_NULL);
sendInvoke("createStream", obj);
2020-08-30 10:48:34 +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_play();
});
2017-04-01 16:35:56 +08:00
}
inline void RtmpPlayer::send_play() {
2020-03-20 11:51:24 +08:00
AMFEncoder enc;
2020-08-30 10:48:34 +08:00
enc << "play" << ++_send_req_id << nullptr << _stream_id << (double) _stream_index;
2020-03-20 11:51:24 +08:00
sendRequest(MSG_CMD, enc.data());
2020-08-30 10:48:34 +08:00
auto fun = [this](AMFValue &val) {
2020-03-20 11:51:24 +08:00
//TraceL << "play onStatus";
auto level = val["level"].as_string();
auto code = val["code"].as_string();
2020-08-30 10:48:34 +08:00
if (level != "status") {
throw std::runtime_error(StrPrinter << "play 失败:" << level << " " << code << endl);
2020-03-20 11:51:24 +08:00
}
};
addOnStatusCB(fun);
addOnStatusCB(fun);
2017-04-01 16:35:56 +08:00
}
2020-08-30 11:40:03 +08:00
inline void RtmpPlayer::send_pause(bool pause) {
2020-03-20 11:51:24 +08:00
AMFEncoder enc;
2020-08-30 11:40:03 +08:00
enc << "pause" << ++_send_req_id << nullptr << pause;
2020-03-20 11:51:24 +08:00
sendRequest(MSG_CMD, enc.data());
2020-08-30 11:40:03 +08:00
auto fun = [this, pause](AMFValue &val) {
2017-04-01 16:35:56 +08:00
//TraceL << "pause onStatus";
auto level = val["level"].as_string();
auto code = val["code"].as_string();
2020-08-30 10:48:34 +08:00
if (level != "status") {
2020-08-30 11:40:03 +08:00
if (!pause) {
2020-08-30 10:48:34 +08:00
throw std::runtime_error(StrPrinter << "pause 恢复播放失败:" << level << " " << code << endl);
2017-04-01 16:35:56 +08:00
}
2020-08-30 10:48:34 +08:00
} else {
2020-08-30 11:40:03 +08:00
_paused = pause;
if (!pause) {
onPlayResult_l(SockException(Err_success, "resum rtmp success"), true);
2020-08-30 10:48:34 +08:00
} else {
2017-04-01 16:35:56 +08:00
//暂停播放
2020-08-30 10:48:34 +08:00
_rtmp_recv_timer.reset();
2017-04-01 16:35:56 +08:00
}
}
2020-03-20 11:51:24 +08:00
};
addOnStatusCB(fun);
2020-08-30 10:48:34 +08:00
_beat_timer.reset();
2020-08-30 11:40:03 +08:00
if (pause) {
2020-03-20 11:51:24 +08:00
weak_ptr<RtmpPlayer> weakSelf = dynamic_pointer_cast<RtmpPlayer>(shared_from_this());
_beat_timer.reset(new Timer((*this)[kBeatIntervalMS].as<int>() / 1000.0f, [weakSelf]() {
2020-03-20 11:51:24 +08:00
auto strongSelf = weakSelf.lock();
2020-08-30 10:48:34 +08:00
if (!strongSelf) {
2020-03-20 11:51:24 +08:00
return false;
}
uint32_t timeStamp = (uint32_t)::time(NULL);
2020-03-20 11:51:24 +08:00
strongSelf->sendUserControl(CONTROL_PING_REQUEST, timeStamp);
return true;
2020-08-30 10:48:34 +08:00
}, getPoller()));
2020-03-20 11:51:24 +08:00
}
2017-04-01 16:35:56 +08:00
}
void RtmpPlayer::onCmd_result(AMFDecoder &dec){
2020-08-30 10:48:34 +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 10:48:34 +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 10:48:34 +08:00
2017-04-01 16:35:56 +08:00
void RtmpPlayer::onCmd_onStatus(AMFDecoder &dec) {
2020-03-20 11:51:24 +08:00
AMFValue val;
2020-08-30 10:48:34 +08:00
while (true) {
2020-03-20 11:51:24 +08:00
val = dec.load<AMFValue>();
2020-08-30 10:48:34 +08:00
if (val.type() == AMF_OBJECT) {
2020-03-20 11:51:24 +08:00
break;
}
}
2020-08-30 10:48:34 +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");
}
2020-08-30 10:48:34 +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 10:48:34 +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
}
}
//WarnL << "unhandled onStatus:" << code;
2017-04-01 16:35:56 +08:00
}
}
void RtmpPlayer::onCmd_onMetaData(AMFDecoder &dec) {
2020-03-20 11:51:24 +08:00
//TraceL;
auto val = dec.load<AMFValue>();
2020-08-30 10:48:34 +08:00
if (!onCheckMeta(val)) {
2020-03-20 11:51:24 +08:00
throw std::runtime_error("onCheckMeta failed");
}
_metadata_got = true;
2017-04-01 16:35:56 +08:00
}
2020-08-30 11:40:03 +08:00
void RtmpPlayer::onStreamDry(uint32_t stream_index) {
//TraceL << stream_index;
2020-08-30 10:48:34 +08:00
onPlayResult_l(SockException(Err_other, "rtmp stream dry"), true);
2017-04-01 16:35:56 +08:00
}
2020-08-30 11:40:03 +08:00
void RtmpPlayer::onMediaData_l(const RtmpPacket::Ptr &chunk_data) {
2020-08-30 10:48:34 +08:00
_rtmp_recv_ticker.resetTime();
if (!_play_timer) {
2020-03-20 11:51:24 +08:00
//已经触发了onPlayResult事件直接触发onMediaData事件
2020-08-30 11:40:03 +08:00
onMediaData(chunk_data);
2020-03-20 11:51:24 +08:00
return;
}
2020-08-30 11:40:03 +08:00
if (chunk_data->isCfgFrame()) {
2020-03-20 11:51:24 +08:00
//输入配置帧以便初始化完成各个track
2020-08-30 11:40:03 +08:00
onMediaData(chunk_data);
2020-08-30 10:48:34 +08:00
} else {
2020-03-20 11:51:24 +08:00
//先触发onPlayResult事件这个时候解码器才能初始化完毕
2020-08-30 10:48:34 +08:00
onPlayResult_l(SockException(Err_success, "play rtmp success"), false);
2020-03-20 11:51:24 +08:00
//触发onPlayResult事件后再把帧数据输入到解码器
2020-08-30 11:40:03 +08:00
onMediaData(chunk_data);
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 RtmpPlayer::onRtmpChunk(RtmpPacket &chunk_data) {
2020-03-20 11:51:24 +08:00
typedef void (RtmpPlayer::*rtmp_func_ptr)(AMFDecoder &dec);
static unordered_map<string, rtmp_func_ptr> s_func_map;
static onceToken token([]() {
2020-08-30 10:48:34 +08:00
s_func_map.emplace("_error", &RtmpPlayer::onCmd_result);
s_func_map.emplace("_result", &RtmpPlayer::onCmd_result);
s_func_map.emplace("onStatus", &RtmpPlayer::onCmd_onStatus);
s_func_map.emplace("onMetaData", &RtmpPlayer::onCmd_onMetaData);
});
2020-03-20 11:51:24 +08:00
2020-08-30 11:40:03 +08:00
switch (chunk_data.type_id) {
2020-03-20 11:51:24 +08:00
case MSG_CMD:
case MSG_CMD3:
case MSG_DATA:
case MSG_DATA3: {
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 = s_func_map.find(type);
2020-08-30 10:48:34 +08:00
if (it != s_func_map.end()) {
2020-03-20 11:51:24 +08:00
auto fun = it->second;
(this->*fun)(dec);
2020-08-30 10:48:34 +08:00
} else {
2020-03-20 11:51:24 +08:00
WarnL << "can not support cmd:" << type;
}
break;
2020-08-30 10:48:34 +08:00
}
2020-03-20 11:51:24 +08:00
case MSG_AUDIO:
case MSG_VIDEO: {
2020-08-30 11:40:03 +08:00
auto idx = chunk_data.type_id % 2;
2020-08-30 10:48:34 +08:00
if (_now_stamp_ticker[idx].elapsedTime() > 500) {
2020-03-20 11:51:24 +08:00
//计算播放进度时间轴用
2020-08-30 11:40:03 +08:00
_now_stamp[idx] = chunk_data.time_stamp;
2017-04-01 16:35:56 +08:00
}
2020-08-30 10:48:34 +08:00
if (!_metadata_got) {
if (!onCheckMeta(TitleMeta().getMetadata())) {
2020-03-20 11:51:24 +08:00
throw std::runtime_error("onCheckMeta failed");
}
_metadata_got = true;
}
2020-08-30 11:40:03 +08:00
onMediaData_l(std::make_shared<RtmpPacket>(std::move(chunk_data)));
2020-03-20 11:51:24 +08:00
break;
}
2020-08-30 10:48:34 +08:00
default: break;
}
2017-04-01 16:35:56 +08:00
}
2018-10-26 14:12:16 +08:00
uint32_t RtmpPlayer::getProgressMilliSecond() const{
2020-08-30 10:48:34 +08:00
uint32_t stamp[2] = {0, 0};
for (auto i = 0; i < 2; i++) {
stamp[i] = _now_stamp[i] - _fist_stamp[i];
2017-04-01 16:35:56 +08:00
}
2020-08-30 10:48:34 +08:00
return _seek_ms + MAX(stamp[0], stamp[1]);
2017-04-01 16:35:56 +08:00
}
2020-08-30 10:48:34 +08:00
2018-10-26 14:12:16 +08:00
void RtmpPlayer::seekToMilliSecond(uint32_t seekMS){
2020-08-30 10:48:34 +08:00
if (_paused) {
2017-04-01 16:35:56 +08:00
pause(false);
}
AMFEncoder enc;
2020-08-30 10:48:34 +08:00
enc << "seek" << ++_send_req_id << nullptr << seekMS * 1.0;
2017-04-01 16:35:56 +08:00
sendRequest(MSG_CMD, enc.data());
2020-08-30 10:48:34 +08:00
addOnStatusCB([this, seekMS](AMFValue &val) {
2017-04-01 16:35:56 +08:00
//TraceL << "seek result";
2020-08-30 10:48:34 +08:00
_now_stamp_ticker[0].resetTime();
_now_stamp_ticker[1].resetTime();
2020-03-20 11:51:24 +08:00
int iTimeInc = seekMS - getProgressMilliSecond();
2020-08-30 10:48:34 +08:00
for (auto i = 0; i < 2; i++) {
_fist_stamp[i] = _now_stamp[i] + iTimeInc;
_now_stamp[i] = _fist_stamp[i];
2017-04-01 16:35:56 +08:00
}
2020-08-30 10:48:34 +08:00
_seek_ms = seekMS;
2017-04-01 16:35:56 +08:00
});
}
2018-10-24 17:17:55 +08:00
} /* namespace mediakit */