mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-10-31 08:39:34 +08:00
避免hls拉流时m3u8文件出现问题或直播结束后,无限重试 (#2357)
Co-authored-by: xiongziliang <771730766@qq.com>
This commit is contained in:
parent
21691e34e5
commit
7fcd88d02a
@ -98,17 +98,14 @@ bool HlsParser::parse(const string &http_url, const string &m3u8) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_is_m3u8) {
|
return _is_m3u8 && onParsed(_is_m3u8_inner, _sequence, ts_map);
|
||||||
onParsed(_is_m3u8_inner, _sequence, ts_map);
|
|
||||||
}
|
|
||||||
return _is_m3u8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HlsParser::isM3u8() const {
|
bool HlsParser::isM3u8() const {
|
||||||
return _is_m3u8;
|
return _is_m3u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HlsParser::isLive() const{
|
bool HlsParser::isLive() const {
|
||||||
return _is_live;
|
return _is_live;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,8 +36,9 @@ typedef struct{
|
|||||||
|
|
||||||
class HlsParser {
|
class HlsParser {
|
||||||
public:
|
public:
|
||||||
HlsParser(){}
|
HlsParser() = default;
|
||||||
~HlsParser(){}
|
~HlsParser() = default;
|
||||||
|
|
||||||
bool parse(const std::string &http_url,const std::string &m3u8);
|
bool parse(const std::string &http_url,const std::string &m3u8);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,10 +80,16 @@ public:
|
|||||||
* 得到总时间
|
* 得到总时间
|
||||||
*/
|
*/
|
||||||
float getTotalDuration() const;
|
float getTotalDuration() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//解析出ts文件地址回调
|
/**
|
||||||
virtual void onParsed(bool is_m3u8_inner,int64_t sequence,const std::map<int,ts_segment> &ts_list) {};
|
* 解析m3u8文件回调
|
||||||
|
* @param is_m3u8_inner 该m3u8文件中是否包含多个hls地址
|
||||||
|
* @param sequence ts序号
|
||||||
|
* @param ts_list ts地址列表
|
||||||
|
* @return 是否解析成功,返回false时,将导致HlsParser::parse返回false
|
||||||
|
*/
|
||||||
|
virtual bool onParsed(bool is_m3u8_inner, int64_t sequence, const std::map<int, ts_segment> &ts_list) = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _is_m3u8 = false;
|
bool _is_m3u8 = false;
|
||||||
|
@ -51,7 +51,7 @@ void HlsPlayer::teardown_l(const SockException &ex) {
|
|||||||
} else {
|
} else {
|
||||||
_try_fetch_index_times += 1;
|
_try_fetch_index_times += 1;
|
||||||
shutdown(ex);
|
shutdown(ex);
|
||||||
WarnL << "重新尝试拉取索引文件[" << _try_fetch_index_times << "]:" << _play_url;
|
WarnL << "Attempt to pull the m3u8 file again[" << _try_fetch_index_times << "]:" << _play_url;
|
||||||
fetchIndexFile();
|
fetchIndexFile();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -118,7 +118,7 @@ void HlsPlayer::fetchSegment() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (err) {
|
if (err) {
|
||||||
WarnL << "download ts segment " << url << " failed:" << err.what();
|
WarnL << "Download ts segment " << url << " failed:" << err.what();
|
||||||
if (err.getErrCode() == Err_timeout) {
|
if (err.getErrCode() == Err_timeout) {
|
||||||
strong_self->_timeout_multiple = MAX(strong_self->_timeout_multiple + 1, MAX_TIMEOUT_MULTIPLE);
|
strong_self->_timeout_multiple = MAX(strong_self->_timeout_multiple + 1, MAX_TIMEOUT_MULTIPLE);
|
||||||
}else{
|
}else{
|
||||||
@ -147,30 +147,41 @@ void HlsPlayer::fetchSegment() {
|
|||||||
_http_ts_player->sendRequest(url);
|
_http_ts_player->sendRequest(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HlsPlayer::onParsed(bool is_m3u8_inner, int64_t sequence, const map<int, ts_segment> &ts_map) {
|
bool HlsPlayer::onParsed(bool is_m3u8_inner, int64_t sequence, const map<int, ts_segment> &ts_map) {
|
||||||
if (!is_m3u8_inner) {
|
if (!is_m3u8_inner) {
|
||||||
//这是ts播放列表
|
// 这是ts播放列表
|
||||||
if (_last_sequence == sequence) {
|
if (_last_sequence == sequence) {
|
||||||
return;
|
// 如果是重复的ts列表,那么忽略
|
||||||
|
// 但是需要注意, 如果当前ts列表为空了, 那么表明直播结束了或者m3u8文件有问题,需要重新拉流
|
||||||
|
// 这里的5倍是为了防止m3u8文件有问题导致的无限重试
|
||||||
|
if (_last_sequence > 0 && _ts_list.empty() && HlsParser::isLive()
|
||||||
|
&& _wait_index_update_ticker.elapsedTime() > (uint64_t)HlsParser::getTargetDur() * 1000 * 5) {
|
||||||
|
_wait_index_update_ticker.resetTime();
|
||||||
|
WarnL << "Fetch new ts list from m3u8 timeout";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_last_sequence = sequence;
|
_last_sequence = sequence;
|
||||||
|
_wait_index_update_ticker.resetTime();
|
||||||
for (auto &pr : ts_map) {
|
for (auto &pr : ts_map) {
|
||||||
auto &ts = pr.second;
|
auto &ts = pr.second;
|
||||||
if (_ts_url_cache.emplace(ts.url).second) {
|
if (_ts_url_cache.emplace(ts.url).second) {
|
||||||
//该ts未重复
|
// 该ts未重复
|
||||||
_ts_list.emplace_back(ts);
|
_ts_list.emplace_back(ts);
|
||||||
//按时间排序
|
// 按时间排序
|
||||||
_ts_url_sort.emplace_back(ts.url);
|
_ts_url_sort.emplace_back(ts.url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_ts_url_sort.size() > 2 * ts_map.size()) {
|
if (_ts_url_sort.size() > 2 * ts_map.size()) {
|
||||||
//去除防重列表中过多的数据
|
// 去除防重列表中过多的数据
|
||||||
_ts_url_cache.erase(_ts_url_sort.front());
|
_ts_url_cache.erase(_ts_url_sort.front());
|
||||||
_ts_url_sort.pop_front();
|
_ts_url_sort.pop_front();
|
||||||
}
|
}
|
||||||
fetchSegment();
|
fetchSegment();
|
||||||
} else {
|
} else {
|
||||||
//这是m3u8列表,我们播放最高清的子hls
|
// 这是m3u8列表,我们播放最高清的子hls
|
||||||
if (ts_map.empty()) {
|
if (ts_map.empty()) {
|
||||||
throw invalid_argument("empty sub hls list:" + getUrl());
|
throw invalid_argument("empty sub hls list:" + getUrl());
|
||||||
}
|
}
|
||||||
@ -184,6 +195,7 @@ void HlsPlayer::onParsed(bool is_m3u8_inner, int64_t sequence, const map<int, ts
|
|||||||
}
|
}
|
||||||
}, false);
|
}, false);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HlsPlayer::onResponseHeader(const string &status, const HttpClient::HttpHeader &headers) {
|
void HlsPlayer::onResponseHeader(const string &status, const HttpClient::HttpHeader &headers) {
|
||||||
@ -193,7 +205,7 @@ void HlsPlayer::onResponseHeader(const string &status, const HttpClient::HttpHea
|
|||||||
}
|
}
|
||||||
auto content_type = strToLower(const_cast<HttpClient::HttpHeader &>(headers)["Content-Type"]);
|
auto content_type = strToLower(const_cast<HttpClient::HttpHeader &>(headers)["Content-Type"]);
|
||||||
if (content_type.find("application/vnd.apple.mpegurl") != 0 && content_type.find("/x-mpegurl") == _StrPrinter::npos) {
|
if (content_type.find("application/vnd.apple.mpegurl") != 0 && content_type.find("/x-mpegurl") == _StrPrinter::npos) {
|
||||||
WarnL << "may not a hls video: " << content_type << ", url: " << getUrl();
|
WarnL << "May not a hls video: " << content_type << ", url: " << getUrl();
|
||||||
}
|
}
|
||||||
_m3u8.clear();
|
_m3u8.clear();
|
||||||
}
|
}
|
||||||
@ -208,7 +220,7 @@ void HlsPlayer::onResponseCompleted(const SockException &ex) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!HlsParser::parse(getUrl(), _m3u8)) {
|
if (!HlsParser::parse(getUrl(), _m3u8)) {
|
||||||
teardown_l(SockException(Err_other, "parse m3u8 failed:" + _m3u8));
|
teardown_l(SockException(Err_other, "parse m3u8 failed:" + _play_url));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!_play_result) {
|
if (!_play_result) {
|
||||||
|
@ -73,11 +73,11 @@ protected:
|
|||||||
virtual void onPacket(const char *data, size_t len) = 0;
|
virtual void onPacket(const char *data, size_t len) = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onParsed(bool is_m3u8_inner,int64_t sequence,const map<int,ts_segment> &ts_map) override;
|
bool onParsed(bool is_m3u8_inner, int64_t sequence, const map<int, ts_segment> &ts_map) override;
|
||||||
void onResponseHeader(const std::string &status,const HttpHeader &headers) override;
|
void onResponseHeader(const std::string &status, const HttpHeader &headers) override;
|
||||||
void onResponseBody(const char *buf,size_t size) override;
|
void onResponseBody(const char *buf, size_t size) override;
|
||||||
void onResponseCompleted(const toolkit::SockException &e) override;
|
void onResponseCompleted(const toolkit::SockException &e) override;
|
||||||
bool onRedirectUrl(const std::string &url,bool temporary) override;
|
bool onRedirectUrl(const std::string &url, bool temporary) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void playDelay();
|
void playDelay();
|
||||||
@ -101,6 +101,7 @@ private:
|
|||||||
std::string _play_url;
|
std::string _play_url;
|
||||||
toolkit::Timer::Ptr _timer;
|
toolkit::Timer::Ptr _timer;
|
||||||
toolkit::Timer::Ptr _timer_ts;
|
toolkit::Timer::Ptr _timer_ts;
|
||||||
|
toolkit::Ticker _wait_index_update_ticker;
|
||||||
std::list<ts_segment> _ts_list;
|
std::list<ts_segment> _ts_list;
|
||||||
std::list<std::string> _ts_url_sort;
|
std::list<std::string> _ts_url_sort;
|
||||||
std::set<std::string, UrlComp> _ts_url_cache;
|
std::set<std::string, UrlComp> _ts_url_cache;
|
||||||
|
Loading…
Reference in New Issue
Block a user