for #304, use stringstream to generate m3u8, add hls_td_ratio. 2.0.116.
正在显示
7 个修改的文件
包含
60 行增加
和
52 行删除
| @@ -525,6 +525,7 @@ Supported operating systems and hardware: | @@ -525,6 +525,7 @@ Supported operating systems and hardware: | ||
| 525 | 525 | ||
| 526 | ### SRS 2.0 history | 526 | ### SRS 2.0 history |
| 527 | 527 | ||
| 528 | +* v2.0, 2015-02-12, for [#304](https://github.com/winlinvip/simple-rtmp-server/issues/304), use stringstream to generate m3u8, add hls_td_ratio. 2.0.116. | ||
| 528 | * v2.0, 2015-02-11, dev code ZhouGuowen for 2.0.115. | 529 | * v2.0, 2015-02-11, dev code ZhouGuowen for 2.0.115. |
| 529 | * v2.0, 2015-02-10, for [#311](https://github.com/winlinvip/simple-rtmp-server/issues/311), set pcr_base to dts. 2.0.114. | 530 | * v2.0, 2015-02-10, for [#311](https://github.com/winlinvip/simple-rtmp-server/issues/311), set pcr_base to dts. 2.0.114. |
| 530 | * v2.0, 2015-02-10, fix [the bug](https://github.com/winlinvip/simple-rtmp-server/commit/87519aaae835199e5adb60c0ae2c1cd24939448c) of ibmf format which decoded in annexb. | 531 | * v2.0, 2015-02-10, fix [the bug](https://github.com/winlinvip/simple-rtmp-server/commit/87519aaae835199e5adb60c0ae2c1cd24939448c) of ibmf format which decoded in annexb. |
| @@ -451,6 +451,12 @@ vhost with-hls.srs.com { | @@ -451,6 +451,12 @@ vhost with-hls.srs.com { | ||
| 451 | # the hls fragment in seconds, the duration of a piece of ts. | 451 | # the hls fragment in seconds, the duration of a piece of ts. |
| 452 | # default: 10 | 452 | # default: 10 |
| 453 | hls_fragment 10; | 453 | hls_fragment 10; |
| 454 | + # the hls m3u8 target duration ratio, | ||
| 455 | + # EXT-X-TARGETDURATION = hls_td_ratio * hls_fragment // init | ||
| 456 | + # EXT-X-TARGETDURATION = max(ts_duration, EXT-X-TARGETDURATION) // for each ts | ||
| 457 | + # @see https://github.com/winlinvip/simple-rtmp-server/issues/304#issuecomment-74000081 | ||
| 458 | + # default: 1.5 | ||
| 459 | + hls_td_ratio 1.5; | ||
| 454 | # the hls window in seconds, the number of ts in m3u8. | 460 | # the hls window in seconds, the number of ts in m3u8. |
| 455 | # default: 60 | 461 | # default: 60 |
| 456 | hls_window 60; | 462 | hls_window 60; |
| @@ -1480,7 +1480,7 @@ int SrsConfig::check_config() | @@ -1480,7 +1480,7 @@ int SrsConfig::check_config() | ||
| 1480 | for (int j = 0; j < (int)conf->directives.size(); j++) { | 1480 | for (int j = 0; j < (int)conf->directives.size(); j++) { |
| 1481 | string m = conf->at(j)->name.c_str(); | 1481 | string m = conf->at(j)->name.c_str(); |
| 1482 | if (m != "enabled" && m != "hls_path" && m != "hls_fragment" && m != "hls_window" && m != "hls_on_error" | 1482 | if (m != "enabled" && m != "hls_path" && m != "hls_fragment" && m != "hls_window" && m != "hls_on_error" |
| 1483 | - && m != "hls_storage" && m != "hls_mount" | 1483 | + && m != "hls_storage" && m != "hls_mount" && m != "hls_td_ratio" |
| 1484 | ) { | 1484 | ) { |
| 1485 | ret = ERROR_SYSTEM_CONFIG_INVALID; | 1485 | ret = ERROR_SYSTEM_CONFIG_INVALID; |
| 1486 | srs_error("unsupported vhost hls directive %s, ret=%d", m.c_str(), ret); | 1486 | srs_error("unsupported vhost hls directive %s, ret=%d", m.c_str(), ret); |
| @@ -3247,6 +3247,23 @@ double SrsConfig::get_hls_fragment(string vhost) | @@ -3247,6 +3247,23 @@ double SrsConfig::get_hls_fragment(string vhost) | ||
| 3247 | return ::atof(conf->arg0().c_str()); | 3247 | return ::atof(conf->arg0().c_str()); |
| 3248 | } | 3248 | } |
| 3249 | 3249 | ||
| 3250 | +double SrsConfig::get_hls_td_ratio(string vhost) | ||
| 3251 | +{ | ||
| 3252 | + SrsConfDirective* hls = get_hls(vhost); | ||
| 3253 | + | ||
| 3254 | + if (!hls) { | ||
| 3255 | + return SRS_CONF_DEFAULT_HLS_TD_RATIO; | ||
| 3256 | + } | ||
| 3257 | + | ||
| 3258 | + SrsConfDirective* conf = hls->get("hls_td_ratio"); | ||
| 3259 | + | ||
| 3260 | + if (!conf) { | ||
| 3261 | + return SRS_CONF_DEFAULT_HLS_TD_RATIO; | ||
| 3262 | + } | ||
| 3263 | + | ||
| 3264 | + return ::atof(conf->arg0().c_str()); | ||
| 3265 | +} | ||
| 3266 | + | ||
| 3250 | double SrsConfig::get_hls_window(string vhost) | 3267 | double SrsConfig::get_hls_window(string vhost) |
| 3251 | { | 3268 | { |
| 3252 | SrsConfDirective* hls = get_hls(vhost); | 3269 | SrsConfDirective* hls = get_hls(vhost); |
| @@ -47,6 +47,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -47,6 +47,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 47 | #define SRS_CONF_DEFAULT_MAX_CONNECTIONS 1000 | 47 | #define SRS_CONF_DEFAULT_MAX_CONNECTIONS 1000 |
| 48 | #define SRS_CONF_DEFAULT_HLS_PATH "./objs/nginx/html" | 48 | #define SRS_CONF_DEFAULT_HLS_PATH "./objs/nginx/html" |
| 49 | #define SRS_CONF_DEFAULT_HLS_FRAGMENT 10 | 49 | #define SRS_CONF_DEFAULT_HLS_FRAGMENT 10 |
| 50 | +#define SRS_CONF_DEFAULT_HLS_TD_RATIO 1.5 | ||
| 50 | #define SRS_CONF_DEFAULT_HLS_WINDOW 60 | 51 | #define SRS_CONF_DEFAULT_HLS_WINDOW 60 |
| 51 | #define SRS_CONF_DEFAULT_HLS_ON_ERROR_IGNORE "ignore" | 52 | #define SRS_CONF_DEFAULT_HLS_ON_ERROR_IGNORE "ignore" |
| 52 | #define SRS_CONF_DEFAULT_HLS_ON_ERROR_DISCONNECT "disconnect" | 53 | #define SRS_CONF_DEFAULT_HLS_ON_ERROR_DISCONNECT "disconnect" |
| @@ -896,6 +897,11 @@ public: | @@ -896,6 +897,11 @@ public: | ||
| 896 | */ | 897 | */ |
| 897 | virtual double get_hls_fragment(std::string vhost); | 898 | virtual double get_hls_fragment(std::string vhost); |
| 898 | /** | 899 | /** |
| 900 | + * get the hls td(target duration) ratio. | ||
| 901 | + * a fragment is a ts file. | ||
| 902 | + */ | ||
| 903 | + virtual double get_hls_td_ratio(std::string vhost); | ||
| 904 | + /** | ||
| 899 | * get the hls window time, in seconds. | 905 | * get the hls window time, in seconds. |
| 900 | * a window is a set of ts, the ts collection in m3u8. | 906 | * a window is a set of ts, the ts collection in m3u8. |
| 901 | * @remark SRS will delete the ts exceed the window. | 907 | * @remark SRS will delete the ts exceed the window. |
| @@ -33,8 +33,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -33,8 +33,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 33 | #include <fcntl.h> | 33 | #include <fcntl.h> |
| 34 | #include <stdlib.h> | 34 | #include <stdlib.h> |
| 35 | #include <string.h> | 35 | #include <string.h> |
| 36 | +#include <math.h> | ||
| 36 | 37 | ||
| 37 | #include <algorithm> | 38 | #include <algorithm> |
| 39 | +#include <sstream> | ||
| 38 | using namespace std; | 40 | using namespace std; |
| 39 | 41 | ||
| 40 | #include <srs_kernel_error.hpp> | 42 | #include <srs_kernel_error.hpp> |
| @@ -166,6 +168,7 @@ SrsHlsMuxer::SrsHlsMuxer(ISrsHlsHandler* h) | @@ -166,6 +168,7 @@ SrsHlsMuxer::SrsHlsMuxer(ISrsHlsHandler* h) | ||
| 166 | req = NULL; | 168 | req = NULL; |
| 167 | handler = h; | 169 | handler = h; |
| 168 | hls_fragment = hls_window = 0; | 170 | hls_fragment = hls_window = 0; |
| 171 | + target_duration = 0; | ||
| 169 | _sequence_no = 0; | 172 | _sequence_no = 0; |
| 170 | current = NULL; | 173 | current = NULL; |
| 171 | acodec = SrsCodecAudioReserved1; | 174 | acodec = SrsCodecAudioReserved1; |
| @@ -201,6 +204,7 @@ int SrsHlsMuxer::update_config(SrsRequest* r, string path, int fragment, int win | @@ -201,6 +204,7 @@ int SrsHlsMuxer::update_config(SrsRequest* r, string path, int fragment, int win | ||
| 201 | hls_path = path; | 204 | hls_path = path; |
| 202 | hls_fragment = fragment; | 205 | hls_fragment = fragment; |
| 203 | hls_window = window; | 206 | hls_window = window; |
| 207 | + target_duration = (int)(fragment * _srs_config->get_hls_td_ratio(r->vhost)); | ||
| 204 | 208 | ||
| 205 | std::string storage = _srs_config->get_hls_storage(r->vhost); | 209 | std::string storage = _srs_config->get_hls_storage(r->vhost); |
| 206 | if (storage == "ram") { | 210 | if (storage == "ram") { |
| @@ -503,35 +507,21 @@ int SrsHlsMuxer::_refresh_m3u8(string m3u8_file) | @@ -503,35 +507,21 @@ int SrsHlsMuxer::_refresh_m3u8(string m3u8_file) | ||
| 503 | } | 507 | } |
| 504 | srs_info("open m3u8 file %s success.", m3u8_file.c_str()); | 508 | srs_info("open m3u8 file %s success.", m3u8_file.c_str()); |
| 505 | 509 | ||
| 506 | - // #EXTM3U\n#EXT-X-VERSION:3\n | ||
| 507 | - char header[] = { | ||
| 508 | - // #EXTM3U\n | ||
| 509 | - 0x23, 0x45, 0x58, 0x54, 0x4d, 0x33, 0x55, SRS_CONSTS_LF, | ||
| 510 | - // #EXT-X-VERSION:3\n | ||
| 511 | - 0x23, 0x45, 0x58, 0x54, 0x2d, 0x58, 0x2d, 0x56, 0x45, 0x52, | ||
| 512 | - 0x53, 0x49, 0x4f, 0x4e, 0x3a, 0x33, SRS_CONSTS_LF, | ||
| 513 | - // #EXT-X-ALLOW-CACHE:NO\n | ||
| 514 | - 0x23, 0x45, 0x58, 0x54, 0x2d, 0x58, 0x2d, 0x41, 0x4c, 0x4c, | ||
| 515 | - 0x4f, 0x57, 0x2d, 0x43, 0x41, 0x43, 0x48, 0x45, 0x3a, 0x4e, 0x4f, SRS_CONSTS_LF | ||
| 516 | - }; | ||
| 517 | - if ((ret = writer.write(header, sizeof(header), NULL)) != ERROR_SUCCESS) { | ||
| 518 | - srs_error("write m3u8 header failed. ret=%d", ret); | ||
| 519 | - return ret; | ||
| 520 | - } | 510 | + // #EXTM3U\n |
| 511 | + // #EXT-X-VERSION:3\n | ||
| 512 | + // #EXT-X-ALLOW-CACHE:NO\n | ||
| 513 | + std::stringstream ss; | ||
| 514 | + ss << "#EXTM3U" << SRS_CONSTS_LF | ||
| 515 | + << "#EXT-X-VERSION:3" << SRS_CONSTS_LF | ||
| 516 | + << "#EXT-X-ALLOW-CACHE:NO" << SRS_CONSTS_LF; | ||
| 521 | srs_verbose("write m3u8 header success."); | 517 | srs_verbose("write m3u8 header success."); |
| 522 | 518 | ||
| 523 | // #EXT-X-MEDIA-SEQUENCE:4294967295\n | 519 | // #EXT-X-MEDIA-SEQUENCE:4294967295\n |
| 524 | SrsHlsSegment* first = *segments.begin(); | 520 | SrsHlsSegment* first = *segments.begin(); |
| 525 | - char sequence[34] = {}; | ||
| 526 | - int len = snprintf(sequence, sizeof(sequence), "#EXT-X-MEDIA-SEQUENCE:%d%c", first->sequence_no, SRS_CONSTS_LF); | ||
| 527 | - if ((ret = writer.write(sequence, len, NULL)) != ERROR_SUCCESS) { | ||
| 528 | - srs_error("write m3u8 sequence failed. ret=%d", ret); | ||
| 529 | - return ret; | ||
| 530 | - } | 521 | + ss << "#EXT-X-MEDIA-SEQUENCE:" << first->sequence_no << SRS_CONSTS_LF; |
| 531 | srs_verbose("write m3u8 sequence success."); | 522 | srs_verbose("write m3u8 sequence success."); |
| 532 | 523 | ||
| 533 | // #EXT-X-TARGETDURATION:4294967295\n | 524 | // #EXT-X-TARGETDURATION:4294967295\n |
| 534 | - int target_duration = 0; | ||
| 535 | /** | 525 | /** |
| 536 | * @see hls-m3u8-draft-pantos-http-live-streaming-12.pdf, page 25 | 526 | * @see hls-m3u8-draft-pantos-http-live-streaming-12.pdf, page 25 |
| 537 | * The Media Playlist file MUST contain an EXT-X-TARGETDURATION tag. | 527 | * The Media Playlist file MUST contain an EXT-X-TARGETDURATION tag. |
| @@ -540,20 +530,13 @@ int SrsHlsMuxer::_refresh_m3u8(string m3u8_file) | @@ -540,20 +530,13 @@ int SrsHlsMuxer::_refresh_m3u8(string m3u8_file) | ||
| 540 | * rounded to the nearest integer. Its value MUST NOT change. A | 530 | * rounded to the nearest integer. Its value MUST NOT change. A |
| 541 | * typical target duration is 10 seconds. | 531 | * typical target duration is 10 seconds. |
| 542 | */ | 532 | */ |
| 543 | - // TODO: FIXME: finger it out whether it should not changed. | 533 | + // @see https://github.com/winlinvip/simple-rtmp-server/issues/304#issuecomment-74000081 |
| 544 | std::vector<SrsHlsSegment*>::iterator it; | 534 | std::vector<SrsHlsSegment*>::iterator it; |
| 545 | for (it = segments.begin(); it != segments.end(); ++it) { | 535 | for (it = segments.begin(); it != segments.end(); ++it) { |
| 546 | SrsHlsSegment* segment = *it; | 536 | SrsHlsSegment* segment = *it; |
| 547 | - target_duration = srs_max(target_duration, (int)segment->duration); | ||
| 548 | - } | ||
| 549 | - // TODO: maybe need to take an around value | ||
| 550 | - target_duration += 1; | ||
| 551 | - char duration[34]; // 23+10+1 | ||
| 552 | - len = snprintf(duration, sizeof(duration), "#EXT-X-TARGETDURATION:%d%c", target_duration, SRS_CONSTS_LF); | ||
| 553 | - if ((ret = writer.write(duration, len, NULL)) != ERROR_SUCCESS) { | ||
| 554 | - srs_error("write m3u8 duration failed. ret=%d", ret); | ||
| 555 | - return ret; | 537 | + target_duration = srs_max(target_duration, (int)ceil(segment->duration)); |
| 556 | } | 538 | } |
| 539 | + ss << "#EXT-X-TARGETDURATION:" << target_duration << SRS_CONSTS_LF; | ||
| 557 | srs_verbose("write m3u8 duration success."); | 540 | srs_verbose("write m3u8 duration success."); |
| 558 | 541 | ||
| 559 | // write all segments | 542 | // write all segments |
| @@ -562,33 +545,27 @@ int SrsHlsMuxer::_refresh_m3u8(string m3u8_file) | @@ -562,33 +545,27 @@ int SrsHlsMuxer::_refresh_m3u8(string m3u8_file) | ||
| 562 | 545 | ||
| 563 | if (segment->is_sequence_header) { | 546 | if (segment->is_sequence_header) { |
| 564 | // #EXT-X-DISCONTINUITY\n | 547 | // #EXT-X-DISCONTINUITY\n |
| 565 | - char ext_discon[22]; // 21+1 | ||
| 566 | - len = snprintf(ext_discon, sizeof(ext_discon), "#EXT-X-DISCONTINUITY%c", SRS_CONSTS_LF); | ||
| 567 | - if ((ret = writer.write(ext_discon, len, NULL)) != ERROR_SUCCESS) { | ||
| 568 | - srs_error("write m3u8 segment discontinuity failed. ret=%d", ret); | ||
| 569 | - return ret; | ||
| 570 | - } | 548 | + ss << "#EXT-X-DISCONTINUITY" << SRS_CONSTS_LF; |
| 571 | srs_verbose("write m3u8 segment discontinuity success."); | 549 | srs_verbose("write m3u8 segment discontinuity success."); |
| 572 | } | 550 | } |
| 573 | 551 | ||
| 574 | // "#EXTINF:4294967295.208,\n" | 552 | // "#EXTINF:4294967295.208,\n" |
| 575 | - char ext_info[25]; // 14+10+1 | ||
| 576 | - len = snprintf(ext_info, sizeof(ext_info), "#EXTINF:%.3f,%c", segment->duration, SRS_CONSTS_LF); | ||
| 577 | - if ((ret = writer.write(ext_info, len, NULL)) != ERROR_SUCCESS) { | ||
| 578 | - srs_error("write m3u8 segment info failed. ret=%d", ret); | ||
| 579 | - return ret; | ||
| 580 | - } | 553 | + ss.precision(3); |
| 554 | + ss.setf(std::ios::fixed, std::ios::floatfield); | ||
| 555 | + ss << "#EXTINF:" << segment->duration << "," << SRS_CONSTS_LF; | ||
| 581 | srs_verbose("write m3u8 segment info success."); | 556 | srs_verbose("write m3u8 segment info success."); |
| 582 | 557 | ||
| 583 | // {file name}\n | 558 | // {file name}\n |
| 584 | - std::string filename = segment->uri; | ||
| 585 | - filename += SRS_CONSTS_LF; | ||
| 586 | - if ((ret = writer.write((char*)filename.c_str(), (int)filename.length(), NULL)) != ERROR_SUCCESS) { | ||
| 587 | - srs_error("write m3u8 segment uri failed. ret=%d", ret); | ||
| 588 | - return ret; | ||
| 589 | - } | 559 | + ss << segment->uri << SRS_CONSTS_LF; |
| 590 | srs_verbose("write m3u8 segment uri success."); | 560 | srs_verbose("write m3u8 segment uri success."); |
| 591 | } | 561 | } |
| 562 | + | ||
| 563 | + // write m3u8 to writer. | ||
| 564 | + std::string m3u8 = ss.str(); | ||
| 565 | + if ((ret = writer.write((char*)m3u8.c_str(), (int)m3u8.length(), NULL)) != ERROR_SUCCESS) { | ||
| 566 | + srs_error("write m3u8 failed. ret=%d", ret); | ||
| 567 | + return ret; | ||
| 568 | + } | ||
| 592 | srs_info("write m3u8 %s success.", m3u8_file.c_str()); | 569 | srs_info("write m3u8 %s success.", m3u8_file.c_str()); |
| 593 | 570 | ||
| 594 | // notify handler for update m3u8. | 571 | // notify handler for update m3u8. |
| @@ -172,6 +172,7 @@ private: | @@ -172,6 +172,7 @@ private: | ||
| 172 | int hls_window; | 172 | int hls_window; |
| 173 | private: | 173 | private: |
| 174 | int _sequence_no; | 174 | int _sequence_no; |
| 175 | + int target_duration; | ||
| 175 | std::string m3u8; | 176 | std::string m3u8; |
| 176 | private: | 177 | private: |
| 177 | ISrsHlsHandler* handler; | 178 | ISrsHlsHandler* handler; |
| @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 31 | // current release version | 31 | // current release version |
| 32 | #define VERSION_MAJOR 2 | 32 | #define VERSION_MAJOR 2 |
| 33 | #define VERSION_MINOR 0 | 33 | #define VERSION_MINOR 0 |
| 34 | -#define VERSION_REVISION 115 | 34 | +#define VERSION_REVISION 116 |
| 35 | 35 | ||
| 36 | // server info. | 36 | // server info. |
| 37 | #define RTMP_SIG_SRS_KEY "SRS" | 37 | #define RTMP_SIG_SRS_KEY "SRS" |
-
请 注册 或 登录 后发表评论