winlin

for #304, use stringstream to generate m3u8, add hls_td_ratio. 2.0.116.

@@ -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"