From 91b922856977a2c317925c6e6c6162b2ac9c97d4 Mon Sep 17 00:00:00 2001 From: chundonglinlin Date: Wed, 29 Jun 2022 09:47:57 +0800 Subject: [PATCH] HLS: parse exist m3u8 when stream publish. --- trunk/src/app/srs_app_hls.cpp | 136 +++++++++++++++++++++++++++++++++- 1 file changed, 135 insertions(+), 1 deletion(-) diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp index 3ae959897..f2ec18985 100644 --- a/trunk/src/app/srs_app_hls.cpp +++ b/trunk/src/app/srs_app_hls.cpp @@ -392,7 +392,8 @@ srs_error_t SrsHlsMuxer::restore_stream() return err; } - srs_trace("hls: restore stream m3u8=%s, m3u8_url=%s", m3u8.c_str(), m3u8_url.c_str()); + srs_trace("hls: restore stream m3u8=%s, m3u8_url=%s, hls_path=%s", + m3u8.c_str(), m3u8_url.c_str(), hls_path.c_str()); // read m3u8 SrsFileReader fr; @@ -408,7 +409,140 @@ srs_error_t SrsHlsMuxer::restore_stream() } fr.close(); + std::string body = fbuf; + // parse + if (body.empty()) { + return srs_error_wrap(err, "read empty m3u8"); + } + + int sequence_num = 0; + srs_utime_t max_target_duration = 0; + bool discon = false; + + std::string ptl; + while (!body.empty()) { + size_t pos = string::npos; + + std::string line; + if ((pos = body.find("\n")) != string::npos) { + line = body.substr(0, pos); + body = body.substr(pos + 1); + } else { + line = body; + body = ""; + } + + line = srs_string_replace(line, "\r", ""); + line = srs_string_replace(line, " ", ""); + + // #EXT-X-VERSION:3 + // the version must be 3.0 + if (srs_string_starts_with(line, "#EXT-X-VERSION:")) { + if (!srs_string_ends_with(line, ":3")) { + srs_warn("m3u8 3.0 required, actual is %s", line.c_str()); + } + continue; + } + + // #EXT-X-PLAYLIST-TYPE:VOD + // the playlist type, vod or nothing. + if (srs_string_starts_with(line, "#EXT-X-PLAYLIST-TYPE:")) { + ptl = line; + continue; + } + + // #EXT-X-MEDIA-SEQUENCE:4294967295 + // the media sequence no. + if (srs_string_starts_with(line, "#EXT-X-MEDIA-SEQUENCE:")) { + sequence_num = ::atof(line.substr(string("#EXT-X-MEDIA-SEQUENCE:").length()).c_str()); + } + + // #EXT-X-TARGETDURATION:12 + // the target duration is required. + if (srs_string_starts_with(line, "#EXT-X-TARGETDURATION:")) { + max_target_duration = ::atof(line.substr(string("#EXT-X-TARGETDURATION:").length()).c_str()); + } + + // #EXT-X-DISCONTINUITY + // the discontinuity tag. + if (srs_string_starts_with(line, "#EXT-X-DISCONTINUITY")) { + discon = true; + } + + // #EXT-X-ENDLIST + // parse completed. + if (line == "#EXT-X-ENDLIST") { + break; + } + + // #EXTINF:11.401, + // livestream-5.ts + // parse each ts entry, expect current line is inf. + if (!srs_string_starts_with(line, "#EXTINF:")) { + continue; + } + + // expect next line is url. + std::string ts_url; + if ((pos = body.find("\n")) != string::npos) { + ts_url = body.substr(0, pos); + body = body.substr(pos + 1); + } else { + srs_warn("ts entry unexpected eof, inf=%s", line.c_str()); + break; + } + + // parse the ts duration. + line = line.substr(string("#EXTINF:").length()); + if ((pos = line.find(",")) != string::npos) { + line = line.substr(0, pos); + } + + double ts_duration = ::atof(line.c_str()); + + // load the default acodec from config. + SrsAudioCodecId default_acodec = SrsAudioCodecIdAAC; + if (true) { + std::string default_acodec_str = _srs_config->get_hls_acodec(req->vhost); + if (default_acodec_str == "mp3") { + default_acodec = SrsAudioCodecIdMP3; + } else if (default_acodec_str == "aac") { + default_acodec = SrsAudioCodecIdAAC; + } else if (default_acodec_str == "an") { + default_acodec = SrsAudioCodecIdDisabled; + } else { + srs_warn("hls: use aac for other codec=%s", default_acodec_str.c_str()); + } + } + + // load the default vcodec from config. + SrsVideoCodecId default_vcodec = SrsVideoCodecIdAVC; + if (true) { + std::string default_vcodec_str = _srs_config->get_hls_vcodec(req->vhost); + if (default_vcodec_str == "h264") { + default_vcodec = SrsVideoCodecIdAVC; + } else if (default_vcodec_str == "vn") { + default_vcodec = SrsVideoCodecIdDisabled; + } else { + srs_warn("hls: use h264 for other codec=%s", default_vcodec_str.c_str()); + } + } + + // new segment. + SrsHlsSegment* seg = new SrsHlsSegment(context, default_acodec, default_vcodec, writer); + seg->sequence_no = sequence_num++; + seg->set_path(hls_path + "/" + req->app + "/" + ts_url); + seg->uri = ts_url; + seg->set_sequence_header(discon); + + seg->append(0); + seg->append(ts_duration * 1000); + + segments->append(seg); + + discon = false; + } return err; }