diff --git a/src/Http/HlsParser.cpp b/src/Http/HlsParser.cpp index ff9c7856..c7b45e3e 100644 --- a/src/Http/HlsParser.cpp +++ b/src/Http/HlsParser.cpp @@ -98,17 +98,14 @@ bool HlsParser::parse(const string &http_url, const string &m3u8) { continue; } - if (_is_m3u8) { - onParsed(_is_m3u8_inner, _sequence, ts_map); - } - return _is_m3u8; + return _is_m3u8 && onParsed(_is_m3u8_inner, _sequence, ts_map); } bool HlsParser::isM3u8() const { return _is_m3u8; } -bool HlsParser::isLive() const{ +bool HlsParser::isLive() const { return _is_live; } diff --git a/src/Http/HlsParser.h b/src/Http/HlsParser.h index eafaf338..c54fc6e5 100644 --- a/src/Http/HlsParser.h +++ b/src/Http/HlsParser.h @@ -36,8 +36,9 @@ typedef struct{ class HlsParser { public: - HlsParser(){} - ~HlsParser(){} + HlsParser() = default; + ~HlsParser() = default; + bool parse(const std::string &http_url,const std::string &m3u8); /** @@ -79,10 +80,16 @@ public: * 得到总时间 */ float getTotalDuration() const; - + protected: - //解析出ts文件地址回调 - virtual void onParsed(bool is_m3u8_inner,int64_t sequence,const std::map &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 &ts_list) = 0; private: bool _is_m3u8 = false; diff --git a/src/Http/HlsPlayer.cpp b/src/Http/HlsPlayer.cpp index ad2cadef..b87826c4 100644 --- a/src/Http/HlsPlayer.cpp +++ b/src/Http/HlsPlayer.cpp @@ -51,7 +51,7 @@ void HlsPlayer::teardown_l(const SockException &ex) { } else { _try_fetch_index_times += 1; shutdown(ex); - WarnL << "重新尝试拉取索引文件[" << _try_fetch_index_times << "]:" << _play_url; + WarnL << "Attempt to pull the m3u8 file again[" << _try_fetch_index_times << "]:" << _play_url; fetchIndexFile(); return; } @@ -118,7 +118,7 @@ void HlsPlayer::fetchSegment() { return; } if (err) { - WarnL << "download ts segment " << url << " failed:" << err.what(); + WarnL << "Download ts segment " << url << " failed:" << err.what(); if (err.getErrCode() == Err_timeout) { strong_self->_timeout_multiple = MAX(strong_self->_timeout_multiple + 1, MAX_TIMEOUT_MULTIPLE); }else{ @@ -147,30 +147,41 @@ void HlsPlayer::fetchSegment() { _http_ts_player->sendRequest(url); } -void HlsPlayer::onParsed(bool is_m3u8_inner, int64_t sequence, const map &ts_map) { +bool HlsPlayer::onParsed(bool is_m3u8_inner, int64_t sequence, const map &ts_map) { if (!is_m3u8_inner) { - //这是ts播放列表 + // 这是ts播放列表 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; + _wait_index_update_ticker.resetTime(); for (auto &pr : ts_map) { auto &ts = pr.second; if (_ts_url_cache.emplace(ts.url).second) { - //该ts未重复 + // 该ts未重复 _ts_list.emplace_back(ts); - //按时间排序 + // 按时间排序 _ts_url_sort.emplace_back(ts.url); } } if (_ts_url_sort.size() > 2 * ts_map.size()) { - //去除防重列表中过多的数据 + // 去除防重列表中过多的数据 _ts_url_cache.erase(_ts_url_sort.front()); _ts_url_sort.pop_front(); } fetchSegment(); } else { - //这是m3u8列表,我们播放最高清的子hls + // 这是m3u8列表,我们播放最高清的子hls if (ts_map.empty()) { throw invalid_argument("empty sub hls list:" + getUrl()); } @@ -184,6 +195,7 @@ void HlsPlayer::onParsed(bool is_m3u8_inner, int64_t sequence, const map(headers)["Content-Type"]); 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(); } @@ -208,7 +220,7 @@ void HlsPlayer::onResponseCompleted(const SockException &ex) { return; } 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; } if (!_play_result) { diff --git a/src/Http/HlsPlayer.h b/src/Http/HlsPlayer.h index 4970087a..422fe3f5 100644 --- a/src/Http/HlsPlayer.h +++ b/src/Http/HlsPlayer.h @@ -73,11 +73,11 @@ protected: virtual void onPacket(const char *data, size_t len) = 0; private: - void onParsed(bool is_m3u8_inner,int64_t sequence,const map &ts_map) override; - void onResponseHeader(const std::string &status,const HttpHeader &headers) override; - void onResponseBody(const char *buf,size_t size) override; + bool onParsed(bool is_m3u8_inner, int64_t sequence, const map &ts_map) override; + void onResponseHeader(const std::string &status, const HttpHeader &headers) override; + void onResponseBody(const char *buf, size_t size) 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: void playDelay(); @@ -101,6 +101,7 @@ private: std::string _play_url; toolkit::Timer::Ptr _timer; toolkit::Timer::Ptr _timer_ts; + toolkit::Ticker _wait_index_update_ticker; std::list _ts_list; std::list _ts_url_sort; std::set _ts_url_cache;