ZLMediaKit/webrtc/WebRtcPlayer.cpp
Talus 46842e6f29
修复WebRTC播放导致媒体延迟注销问题 (#2246)
因WebRtcPlayer中使用RtspMediaSource的共享指针,特定情况下引起媒体注销无法触发的问题。

- 重现步骤
    在ZL的webrtc demo页面推流
    浏览器打开如下html
    webrtc.html
    关闭推流器页面,推流器停止推流
    webrtc.htm浏览器console->network将观察到:即使推流停止,但webrtc sdp请求一直能成功获取sdp,且流媒体一直不注销

- 原因
        因为每个WebRtc 播放 SDP请求都会产生 WebRtcPlayer,产生RtspMediaSource的共享指针,产生强引用。
        而DTLS超时释放需要一定的时间,WebRtcPlayer销毁需要超时。如果请求sdp的时间足够短,强引用会一直存在。将永远无法触发媒体注销

- 场景
        webrtc播放存在重试,但是udp不通。DTLS无法创建
        有人对ZLM执行恶意攻击,短时间内不断请求SDP但是不建立WebRTC通信
2023-02-20 16:23:29 +08:00

101 lines
3.7 KiB
C++

/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
*
* 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.
*/
#include "WebRtcPlayer.h"
#include "Common/config.h"
using namespace std;
namespace mediakit {
WebRtcPlayer::Ptr WebRtcPlayer::create(const EventPoller::Ptr &poller,
const RtspMediaSource::Ptr &src,
const MediaInfo &info,
bool preferred_tcp) {
WebRtcPlayer::Ptr ret(new WebRtcPlayer(poller, src, info, preferred_tcp), [](WebRtcPlayer *ptr) {
ptr->onDestory();
delete ptr;
});
ret->onCreate();
return ret;
}
WebRtcPlayer::WebRtcPlayer(const EventPoller::Ptr &poller,
const RtspMediaSource::Ptr &src,
const MediaInfo &info,
bool preferred_tcp) : WebRtcTransportImp(poller,preferred_tcp) {
_media_info = info;
_play_src = src;
CHECK(src);
}
void WebRtcPlayer::onStartWebRTC() {
auto playSrc = _play_src.lock();
if(!playSrc){
onShutdown(SockException(Err_shutdown, "rtsp media source was shutdown"));
return ;
}
WebRtcTransportImp::onStartWebRTC();
if (canSendRtp()) {
playSrc->pause(false);
_reader = playSrc->getRing()->attach(getPoller(), true);
weak_ptr<WebRtcPlayer> weak_self = static_pointer_cast<WebRtcPlayer>(shared_from_this());
weak_ptr<Session> weak_session = getSession();
_reader->setGetInfoCB([weak_session]() { return weak_session.lock(); });
_reader->setReadCB([weak_self](const RtspMediaSource::RingDataType &pkt) {
auto strong_self = weak_self.lock();
if (!strong_self) {
return;
}
size_t i = 0;
pkt->for_each([&](const RtpPacket::Ptr &rtp) {
//TraceL<<"send track type:"<<rtp->type<<" ts:"<<rtp->getStamp()<<" ntp:"<<rtp->ntp_stamp<<" size:"<<rtp->getPayloadSize()<<" i:"<<i;
strong_self->onSendRtp(rtp, ++i == pkt->size());
});
});
_reader->setDetachCB([weak_self]() {
auto strong_self = weak_self.lock();
if (!strong_self) {
return;
}
strong_self->onShutdown(SockException(Err_shutdown, "rtsp ring buffer detached"));
});
}
}
void WebRtcPlayer::onDestory() {
WebRtcTransportImp::onDestory();
auto duration = getDuration();
auto bytes_usage = getBytesUsage();
//流量统计事件广播
GET_CONFIG(uint32_t, iFlowThreshold, General::kFlowThreshold);
if (_reader && getSession()) {
WarnL << "RTC播放器("
<< _media_info.shortUrl()
<< ")结束播放,耗时(s):" << duration;
if (bytes_usage >= iFlowThreshold * 1024) {
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _media_info, bytes_usage, duration,
true, static_cast<SockInfo &>(*getSession()));
}
}
}
void WebRtcPlayer::onRtcConfigure(RtcConfigure &configure) const {
auto playSrc = _play_src.lock();
if(!playSrc){
return ;
}
WebRtcTransportImp::onRtcConfigure(configure);
//这是播放
configure.audio.direction = configure.video.direction = RtpDirection::sendonly;
configure.setPlayRtspInfo(playSrc->getSdp());
}
}// namespace mediakit