winlin

fix #136, support hls without io(in ram). 2.0.112

@@ -473,6 +473,19 @@ vhost with-hls.srs.com { @@ -473,6 +473,19 @@ vhost with-hls.srs.com {
473 # in a word, the hls_path is for vhost. 473 # in a word, the hls_path is for vhost.
474 # default: ./objs/nginx/html 474 # default: ./objs/nginx/html
475 hls_path ./objs/nginx/html; 475 hls_path ./objs/nginx/html;
  476 + # the hls storage: disk, ram or both.
  477 + # disk, to write hls m3u8/ts to disk.
  478 + # ram, serve m3u8/ts in memory, which use embeded http server to delivery.
  479 + # both, disk and ram.
  480 + # default: disk
  481 + hls_storage disk;
  482 + # the hls mount for hls_storage ram,
  483 + # which use srs embeded http server to delivery HLS,
  484 + # where the mount specifies the HTTP url to mount.
  485 + # @see the mount of http_remux.
  486 + # @remark the hls_mount must endswith .m3u8.
  487 + # default: [vhost]/[app]/[stream].m3u8
  488 + hls_mount [vhost]/[app]/[stream].m3u8;
476 } 489 }
477 } 490 }
478 # the vhost with hls disabled. 491 # the vhost with hls disabled.
@@ -1479,7 +1479,9 @@ int SrsConfig::check_config() @@ -1479,7 +1479,9 @@ int SrsConfig::check_config()
1479 } else if (n == "hls") { 1479 } else if (n == "hls") {
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"
  1484 + ) {
1483 ret = ERROR_SYSTEM_CONFIG_INVALID; 1485 ret = ERROR_SYSTEM_CONFIG_INVALID;
1484 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);
1485 return ret; 1487 return ret;
@@ -1799,6 +1801,7 @@ int SrsConfig::check_config() @@ -1799,6 +1801,7 @@ int SrsConfig::check_config()
1799 } 1801 }
1800 } 1802 }
1801 #endif 1803 #endif
  1804 + // TODO: FIXME: required http server when hls storage is ram or both.
1802 } 1805 }
1803 1806
1804 return ret; 1807 return ret;
@@ -3278,6 +3281,40 @@ string SrsConfig::get_hls_on_error(string vhost) @@ -3278,6 +3281,40 @@ string SrsConfig::get_hls_on_error(string vhost)
3278 return conf->arg0(); 3281 return conf->arg0();
3279 } 3282 }
3280 3283
  3284 +string SrsConfig::get_hls_storage(string vhost)
  3285 +{
  3286 + SrsConfDirective* hls = get_hls(vhost);
  3287 +
  3288 + if (!hls) {
  3289 + return SRS_CONF_DEFAULT_HLS_STORAGE;
  3290 + }
  3291 +
  3292 + SrsConfDirective* conf = hls->get("hls_storage");
  3293 +
  3294 + if (!conf) {
  3295 + return SRS_CONF_DEFAULT_HLS_STORAGE;
  3296 + }
  3297 +
  3298 + return conf->arg0();
  3299 +}
  3300 +
  3301 +string SrsConfig::get_hls_mount(string vhost)
  3302 +{
  3303 + SrsConfDirective* hls = get_hls(vhost);
  3304 +
  3305 + if (!hls) {
  3306 + return SRS_CONF_DEFAULT_HLS_MOUNT;
  3307 + }
  3308 +
  3309 + SrsConfDirective* conf = hls->get("hls_mount");
  3310 +
  3311 + if (!conf) {
  3312 + return SRS_CONF_DEFAULT_HLS_MOUNT;
  3313 + }
  3314 +
  3315 + return conf->arg0();
  3316 +}
  3317 +
3281 SrsConfDirective* SrsConfig::get_dvr(string vhost) 3318 SrsConfDirective* SrsConfig::get_dvr(string vhost)
3282 { 3319 {
3283 SrsConfDirective* conf = get_vhost(vhost); 3320 SrsConfDirective* conf = get_vhost(vhost);
@@ -52,6 +52,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -52,6 +52,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
52 #define SRS_CONF_DEFAULT_HLS_ON_ERROR_DISCONNECT "disconnect" 52 #define SRS_CONF_DEFAULT_HLS_ON_ERROR_DISCONNECT "disconnect"
53 #define SRS_CONF_DEFAULT_HLS_ON_ERROR_CONTINUE "continue" 53 #define SRS_CONF_DEFAULT_HLS_ON_ERROR_CONTINUE "continue"
54 #define SRS_CONF_DEFAULT_HLS_ON_ERROR SRS_CONF_DEFAULT_HLS_ON_ERROR_IGNORE 54 #define SRS_CONF_DEFAULT_HLS_ON_ERROR SRS_CONF_DEFAULT_HLS_ON_ERROR_IGNORE
  55 +#define SRS_CONF_DEFAULT_HLS_STORAGE "disk"
  56 +#define SRS_CONF_DEFAULT_HLS_MOUNT "[vhost]/[app]/[stream].m3u8"
55 #define SRS_CONF_DEFAULT_DVR_PATH "./objs/nginx/html" 57 #define SRS_CONF_DEFAULT_DVR_PATH "./objs/nginx/html"
56 #define SRS_CONF_DEFAULT_DVR_PLAN_SESSION "session" 58 #define SRS_CONF_DEFAULT_DVR_PLAN_SESSION "session"
57 #define SRS_CONF_DEFAULT_DVR_PLAN_SEGMENT "segment" 59 #define SRS_CONF_DEFAULT_DVR_PLAN_SEGMENT "segment"
@@ -906,6 +908,14 @@ public: @@ -906,6 +908,14 @@ public:
906 * @see https://github.com/winlinvip/simple-rtmp-server/issues/264 908 * @see https://github.com/winlinvip/simple-rtmp-server/issues/264
907 */ 909 */
908 virtual std::string get_hls_on_error(std::string vhost); 910 virtual std::string get_hls_on_error(std::string vhost);
  911 + /**
  912 + * get the HLS storage type.
  913 + */
  914 + virtual std::string get_hls_storage(std::string vhost);
  915 + /**
  916 + * get the HLS mount url for HTTP server.
  917 + */
  918 + virtual std::string get_hls_mount(std::string vhost);
909 // dvr section 919 // dvr section
910 private: 920 private:
911 /** 921 /**
@@ -55,13 +55,87 @@ using namespace std; @@ -55,13 +55,87 @@ using namespace std;
55 // drop the segment when duration of ts too small. 55 // drop the segment when duration of ts too small.
56 #define SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS 100 56 #define SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS 100
57 57
58 -SrsHlsSegment::SrsHlsSegment() 58 +ISrsHlsHandler::ISrsHlsHandler()
  59 +{
  60 +}
  61 +
  62 +ISrsHlsHandler::~ISrsHlsHandler()
  63 +{
  64 +}
  65 +
  66 +SrsHlsCacheWriter::SrsHlsCacheWriter(bool write_cache, bool write_file)
  67 +{
  68 + should_write_cache = write_cache;
  69 + should_write_file = write_file;
  70 +}
  71 +
  72 +SrsHlsCacheWriter::~SrsHlsCacheWriter()
  73 +{
  74 +}
  75 +
  76 +int SrsHlsCacheWriter::open(string file)
  77 +{
  78 + if (!should_write_file) {
  79 + return ERROR_SUCCESS;
  80 + }
  81 +
  82 + return impl.open(file);
  83 +}
  84 +
  85 +void SrsHlsCacheWriter::close()
  86 +{
  87 + if (!should_write_file) {
  88 + return;
  89 + }
  90 +
  91 + impl.close();
  92 +}
  93 +
  94 +bool SrsHlsCacheWriter::is_open()
  95 +{
  96 + if (!should_write_file) {
  97 + return true;
  98 + }
  99 +
  100 + return impl.is_open();
  101 +}
  102 +
  103 +int64_t SrsHlsCacheWriter::tellg()
  104 +{
  105 + if (!should_write_file) {
  106 + return 0;
  107 + }
  108 +
  109 + return impl.tellg();
  110 +}
  111 +
  112 +int SrsHlsCacheWriter::write(void* buf, size_t count, ssize_t* pnwrite)
  113 +{
  114 + if (should_write_cache) {
  115 + if (count > 0) {
  116 + data.append((char*)buf, count);
  117 + }
  118 + }
  119 +
  120 + if (should_write_file) {
  121 + return impl.write(buf, count, pnwrite);
  122 + }
  123 +
  124 + return ERROR_SUCCESS;
  125 +}
  126 +
  127 +string SrsHlsCacheWriter::cache()
  128 +{
  129 + return data;
  130 +}
  131 +
  132 +SrsHlsSegment::SrsHlsSegment(bool write_cache, bool write_file)
59 { 133 {
60 duration = 0; 134 duration = 0;
61 sequence_no = 0; 135 sequence_no = 0;
62 segment_start_dts = 0; 136 segment_start_dts = 0;
63 is_sequence_header = false; 137 is_sequence_header = false;
64 - writer = new SrsFileWriter(); 138 + writer = new SrsHlsCacheWriter(write_cache, write_file);
65 muxer = new SrsTSMuxer(writer); 139 muxer = new SrsTSMuxer(writer);
66 } 140 }
67 141
@@ -87,12 +161,16 @@ void SrsHlsSegment::update_duration(int64_t current_frame_dts) @@ -87,12 +161,16 @@ void SrsHlsSegment::update_duration(int64_t current_frame_dts)
87 return; 161 return;
88 } 162 }
89 163
90 -SrsHlsMuxer::SrsHlsMuxer() 164 +SrsHlsMuxer::SrsHlsMuxer(ISrsHlsHandler* h)
91 { 165 {
  166 + req = NULL;
  167 + handler = h;
92 hls_fragment = hls_window = 0; 168 hls_fragment = hls_window = 0;
93 _sequence_no = 0; 169 _sequence_no = 0;
94 current = NULL; 170 current = NULL;
95 acodec = SrsCodecAudioReserved1; 171 acodec = SrsCodecAudioReserved1;
  172 + should_write_cache = false;
  173 + should_write_file = true;
96 } 174 }
97 175
98 SrsHlsMuxer::~SrsHlsMuxer() 176 SrsHlsMuxer::~SrsHlsMuxer()
@@ -105,6 +183,7 @@ SrsHlsMuxer::~SrsHlsMuxer() @@ -105,6 +183,7 @@ SrsHlsMuxer::~SrsHlsMuxer()
105 segments.clear(); 183 segments.clear();
106 184
107 srs_freep(current); 185 srs_freep(current);
  186 + srs_freep(req);
108 } 187 }
109 188
110 int SrsHlsMuxer::sequence_no() 189 int SrsHlsMuxer::sequence_no()
@@ -112,17 +191,30 @@ int SrsHlsMuxer::sequence_no() @@ -112,17 +191,30 @@ int SrsHlsMuxer::sequence_no()
112 return _sequence_no; 191 return _sequence_no;
113 } 192 }
114 193
115 -int SrsHlsMuxer::update_config(  
116 - string _app, string _stream, string path, int fragment, int window  
117 -) { 194 +int SrsHlsMuxer::update_config(SrsRequest* r, string path, int fragment, int window)
  195 +{
118 int ret = ERROR_SUCCESS; 196 int ret = ERROR_SUCCESS;
119 197
120 - app = _app;  
121 - stream = _stream; 198 + srs_freep(req);
  199 + req = r->copy();
  200 +
122 hls_path = path; 201 hls_path = path;
123 hls_fragment = fragment; 202 hls_fragment = fragment;
124 hls_window = window; 203 hls_window = window;
125 204
  205 + std::string storage = _srs_config->get_hls_storage(r->vhost);
  206 + if (storage == "ram") {
  207 + should_write_cache = true;
  208 + should_write_file = false;
  209 + } else if (storage == "disk") {
  210 + should_write_cache = false;
  211 + should_write_file = true;
  212 + } else {
  213 + srs_assert(storage == "both");
  214 + should_write_cache = true;
  215 + should_write_file = true;
  216 + }
  217 +
126 return ret; 218 return ret;
127 } 219 }
128 220
@@ -137,7 +229,7 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts) @@ -137,7 +229,7 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts)
137 229
138 // TODO: create all parents dirs. 230 // TODO: create all parents dirs.
139 // create dir for app. 231 // create dir for app.
140 - if ((ret = create_dir()) != ERROR_SUCCESS) { 232 + if (should_write_file && (ret = create_dir()) != ERROR_SUCCESS) {
141 return ret; 233 return ret;
142 } 234 }
143 235
@@ -145,19 +237,19 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts) @@ -145,19 +237,19 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts)
145 srs_assert(!current); 237 srs_assert(!current);
146 238
147 // new segment. 239 // new segment.
148 - current = new SrsHlsSegment(); 240 + current = new SrsHlsSegment(should_write_cache, should_write_file);
149 current->sequence_no = _sequence_no++; 241 current->sequence_no = _sequence_no++;
150 current->segment_start_dts = segment_start_dts; 242 current->segment_start_dts = segment_start_dts;
151 243
152 // generate filename. 244 // generate filename.
153 char filename[128]; 245 char filename[128];
154 snprintf(filename, sizeof(filename), 246 snprintf(filename, sizeof(filename),
155 - "%s-%d.ts", stream.c_str(), current->sequence_no); 247 + "%s-%d.ts", req->stream.c_str(), current->sequence_no);
156 248
157 // TODO: use temp file and rename it. 249 // TODO: use temp file and rename it.
158 current->full_path = hls_path; 250 current->full_path = hls_path;
159 current->full_path += "/"; 251 current->full_path += "/";
160 - current->full_path += app; 252 + current->full_path += req->app;
161 current->full_path += "/"; 253 current->full_path += "/";
162 current->full_path += filename; 254 current->full_path += filename;
163 255
@@ -290,6 +382,13 @@ int SrsHlsMuxer::segment_close(string log_desc) @@ -290,6 +382,13 @@ int SrsHlsMuxer::segment_close(string log_desc)
290 log_desc.c_str(), current->sequence_no, current->uri.c_str(), current->duration, 382 log_desc.c_str(), current->sequence_no, current->uri.c_str(), current->duration,
291 current->segment_start_dts); 383 current->segment_start_dts);
292 384
  385 + // notify handler for update ts.
  386 + srs_assert(current->writer);
  387 + if (handler && (ret = handler->on_update_ts(req, current->uri, current->writer->cache())) != ERROR_SUCCESS) {
  388 + srs_error("notify handler for update ts failed. ret=%d", ret);
  389 + return ret;
  390 + }
  391 +
293 // close the muxer of finished segment. 392 // close the muxer of finished segment.
294 srs_freep(current->muxer); 393 srs_freep(current->muxer);
295 std::string full_path = current->full_path; 394 std::string full_path = current->full_path;
@@ -297,7 +396,7 @@ int SrsHlsMuxer::segment_close(string log_desc) @@ -297,7 +396,7 @@ int SrsHlsMuxer::segment_close(string log_desc)
297 396
298 // rename from tmp to real path 397 // rename from tmp to real path
299 std::string tmp_file = full_path + ".tmp"; 398 std::string tmp_file = full_path + ".tmp";
300 - if (rename(tmp_file.c_str(), full_path.c_str()) < 0) { 399 + if (should_write_file && rename(tmp_file.c_str(), full_path.c_str()) < 0) {
301 ret = ERROR_HLS_WRITE_FAILED; 400 ret = ERROR_HLS_WRITE_FAILED;
302 srs_error("rename ts file failed, %s => %s. ret=%d", 401 srs_error("rename ts file failed, %s => %s. ret=%d",
303 tmp_file.c_str(), full_path.c_str(), ret); 402 tmp_file.c_str(), full_path.c_str(), ret);
@@ -313,7 +412,9 @@ int SrsHlsMuxer::segment_close(string log_desc) @@ -313,7 +412,9 @@ int SrsHlsMuxer::segment_close(string log_desc)
313 412
314 // rename from tmp to real path 413 // rename from tmp to real path
315 std::string tmp_file = current->full_path + ".tmp"; 414 std::string tmp_file = current->full_path + ".tmp";
  415 + if (should_write_file) {
316 unlink(tmp_file.c_str()); 416 unlink(tmp_file.c_str());
  417 + }
317 418
318 srs_freep(current); 419 srs_freep(current);
319 } 420 }
@@ -365,22 +466,18 @@ int SrsHlsMuxer::refresh_m3u8() @@ -365,22 +466,18 @@ int SrsHlsMuxer::refresh_m3u8()
365 466
366 std::string m3u8_file = hls_path; 467 std::string m3u8_file = hls_path;
367 m3u8_file += "/"; 468 m3u8_file += "/";
368 - m3u8_file += app; 469 + m3u8_file += req->app;
369 m3u8_file += "/"; 470 m3u8_file += "/";
370 - m3u8_file += stream; 471 + m3u8_file += req->stream;
371 m3u8_file += ".m3u8"; 472 m3u8_file += ".m3u8";
372 473
373 m3u8 = m3u8_file; 474 m3u8 = m3u8_file;
374 m3u8_file += ".temp"; 475 m3u8_file += ".temp";
375 476
376 - int fd = -1;  
377 - ret = _refresh_m3u8(fd, m3u8_file);  
378 - if (fd >= 0) {  
379 - close(fd);  
380 - if (rename(m3u8_file.c_str(), m3u8.c_str()) < 0) { 477 + if ((ret = _refresh_m3u8(m3u8_file)) == ERROR_SUCCESS) {
  478 + if (should_write_file && rename(m3u8_file.c_str(), m3u8.c_str()) < 0) {
381 ret = ERROR_HLS_WRITE_FAILED; 479 ret = ERROR_HLS_WRITE_FAILED;
382 - srs_error("rename m3u8 file failed. "  
383 - "%s => %s, ret=%d", m3u8_file.c_str(), m3u8.c_str(), ret); 480 + srs_error("rename m3u8 file failed. %s => %s, ret=%d", m3u8_file.c_str(), m3u8.c_str(), ret);
384 } 481 }
385 } 482 }
386 483
@@ -390,7 +487,7 @@ int SrsHlsMuxer::refresh_m3u8() @@ -390,7 +487,7 @@ int SrsHlsMuxer::refresh_m3u8()
390 return ret; 487 return ret;
391 } 488 }
392 489
393 -int SrsHlsMuxer::_refresh_m3u8(int& fd, string m3u8_file) 490 +int SrsHlsMuxer::_refresh_m3u8(string m3u8_file)
394 { 491 {
395 int ret = ERROR_SUCCESS; 492 int ret = ERROR_SUCCESS;
396 493
@@ -399,10 +496,8 @@ int SrsHlsMuxer::_refresh_m3u8(int& fd, string m3u8_file) @@ -399,10 +496,8 @@ int SrsHlsMuxer::_refresh_m3u8(int& fd, string m3u8_file)
399 return ret; 496 return ret;
400 } 497 }
401 498
402 - int flags = O_CREAT|O_WRONLY|O_TRUNC;  
403 - mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH;  
404 - if ((fd = ::open(m3u8_file.c_str(), flags, mode)) < 0) {  
405 - ret = ERROR_HLS_OPEN_FAILED; 499 + SrsHlsCacheWriter writer(should_write_cache, should_write_file);
  500 + if ((ret = writer.open(m3u8_file)) != ERROR_SUCCESS) {
406 srs_error("open m3u8 file %s failed. ret=%d", m3u8_file.c_str(), ret); 501 srs_error("open m3u8 file %s failed. ret=%d", m3u8_file.c_str(), ret);
407 return ret; 502 return ret;
408 } 503 }
@@ -419,8 +514,7 @@ int SrsHlsMuxer::_refresh_m3u8(int& fd, string m3u8_file) @@ -419,8 +514,7 @@ int SrsHlsMuxer::_refresh_m3u8(int& fd, string m3u8_file)
419 0x23, 0x45, 0x58, 0x54, 0x2d, 0x58, 0x2d, 0x41, 0x4c, 0x4c, 514 0x23, 0x45, 0x58, 0x54, 0x2d, 0x58, 0x2d, 0x41, 0x4c, 0x4c,
420 0x4f, 0x57, 0x2d, 0x43, 0x41, 0x43, 0x48, 0x45, 0x3a, 0x4e, 0x4f, 0x0a 515 0x4f, 0x57, 0x2d, 0x43, 0x41, 0x43, 0x48, 0x45, 0x3a, 0x4e, 0x4f, 0x0a
421 }; 516 };
422 - if (::write(fd, header, sizeof(header)) != sizeof(header)) {  
423 - ret = ERROR_HLS_WRITE_FAILED; 517 + if ((ret = writer.write(header, sizeof(header), NULL)) != ERROR_SUCCESS) {
424 srs_error("write m3u8 header failed. ret=%d", ret); 518 srs_error("write m3u8 header failed. ret=%d", ret);
425 return ret; 519 return ret;
426 } 520 }
@@ -430,8 +524,7 @@ int SrsHlsMuxer::_refresh_m3u8(int& fd, string m3u8_file) @@ -430,8 +524,7 @@ int SrsHlsMuxer::_refresh_m3u8(int& fd, string m3u8_file)
430 SrsHlsSegment* first = *segments.begin(); 524 SrsHlsSegment* first = *segments.begin();
431 char sequence[34] = {}; 525 char sequence[34] = {};
432 int len = snprintf(sequence, sizeof(sequence), "#EXT-X-MEDIA-SEQUENCE:%d\n", first->sequence_no); 526 int len = snprintf(sequence, sizeof(sequence), "#EXT-X-MEDIA-SEQUENCE:%d\n", first->sequence_no);
433 - if (::write(fd, sequence, len) != len) {  
434 - ret = ERROR_HLS_WRITE_FAILED; 527 + if ((ret = writer.write(sequence, len, NULL)) != ERROR_SUCCESS) {
435 srs_error("write m3u8 sequence failed. ret=%d", ret); 528 srs_error("write m3u8 sequence failed. ret=%d", ret);
436 return ret; 529 return ret;
437 } 530 }
@@ -448,8 +541,7 @@ int SrsHlsMuxer::_refresh_m3u8(int& fd, string m3u8_file) @@ -448,8 +541,7 @@ int SrsHlsMuxer::_refresh_m3u8(int& fd, string m3u8_file)
448 target_duration += 1; 541 target_duration += 1;
449 char duration[34]; // 23+10+1 542 char duration[34]; // 23+10+1
450 len = snprintf(duration, sizeof(duration), "#EXT-X-TARGETDURATION:%d\n", target_duration); 543 len = snprintf(duration, sizeof(duration), "#EXT-X-TARGETDURATION:%d\n", target_duration);
451 - if (::write(fd, duration, len) != len) {  
452 - ret = ERROR_HLS_WRITE_FAILED; 544 + if ((ret = writer.write(duration, len, NULL)) != ERROR_SUCCESS) {
453 srs_error("write m3u8 duration failed. ret=%d", ret); 545 srs_error("write m3u8 duration failed. ret=%d", ret);
454 return ret; 546 return ret;
455 } 547 }
@@ -463,8 +555,7 @@ int SrsHlsMuxer::_refresh_m3u8(int& fd, string m3u8_file) @@ -463,8 +555,7 @@ int SrsHlsMuxer::_refresh_m3u8(int& fd, string m3u8_file)
463 // #EXT-X-DISCONTINUITY\n 555 // #EXT-X-DISCONTINUITY\n
464 char ext_discon[22]; // 21+1 556 char ext_discon[22]; // 21+1
465 len = snprintf(ext_discon, sizeof(ext_discon), "#EXT-X-DISCONTINUITY\n"); 557 len = snprintf(ext_discon, sizeof(ext_discon), "#EXT-X-DISCONTINUITY\n");
466 - if (::write(fd, ext_discon, len) != len) {  
467 - ret = ERROR_HLS_WRITE_FAILED; 558 + if ((ret = writer.write(ext_discon, len, NULL)) != ERROR_SUCCESS) {
468 srs_error("write m3u8 segment discontinuity failed. ret=%d", ret); 559 srs_error("write m3u8 segment discontinuity failed. ret=%d", ret);
469 return ret; 560 return ret;
470 } 561 }
@@ -474,8 +565,7 @@ int SrsHlsMuxer::_refresh_m3u8(int& fd, string m3u8_file) @@ -474,8 +565,7 @@ int SrsHlsMuxer::_refresh_m3u8(int& fd, string m3u8_file)
474 // "#EXTINF:4294967295.208,\n" 565 // "#EXTINF:4294967295.208,\n"
475 char ext_info[25]; // 14+10+1 566 char ext_info[25]; // 14+10+1
476 len = snprintf(ext_info, sizeof(ext_info), "#EXTINF:%.3f\n", segment->duration); 567 len = snprintf(ext_info, sizeof(ext_info), "#EXTINF:%.3f\n", segment->duration);
477 - if (::write(fd, ext_info, len) != len) {  
478 - ret = ERROR_HLS_WRITE_FAILED; 568 + if ((ret = writer.write(ext_info, len, NULL)) != ERROR_SUCCESS) {
479 srs_error("write m3u8 segment info failed. ret=%d", ret); 569 srs_error("write m3u8 segment info failed. ret=%d", ret);
480 return ret; 570 return ret;
481 } 571 }
@@ -484,8 +574,7 @@ int SrsHlsMuxer::_refresh_m3u8(int& fd, string m3u8_file) @@ -484,8 +574,7 @@ int SrsHlsMuxer::_refresh_m3u8(int& fd, string m3u8_file)
484 // file name 574 // file name
485 std::string filename = segment->uri; 575 std::string filename = segment->uri;
486 filename += "\n"; 576 filename += "\n";
487 - if (::write(fd, filename.c_str(), filename.length()) != (int)filename.length()) {  
488 - ret = ERROR_HLS_WRITE_FAILED; 577 + if ((ret = writer.write((char*)filename.c_str(), (int)filename.length(), NULL)) != ERROR_SUCCESS) {
489 srs_error("write m3u8 segment uri failed. ret=%d", ret); 578 srs_error("write m3u8 segment uri failed. ret=%d", ret);
490 return ret; 579 return ret;
491 } 580 }
@@ -493,6 +582,12 @@ int SrsHlsMuxer::_refresh_m3u8(int& fd, string m3u8_file) @@ -493,6 +582,12 @@ int SrsHlsMuxer::_refresh_m3u8(int& fd, string m3u8_file)
493 } 582 }
494 srs_info("write m3u8 %s success.", m3u8_file.c_str()); 583 srs_info("write m3u8 %s success.", m3u8_file.c_str());
495 584
  585 + // notify handler for update m3u8.
  586 + if (handler && (ret = handler->on_update_m3u8(req, writer.cache())) != ERROR_SUCCESS) {
  587 + srs_error("notify handler for update m3u8 failed. ret=%d", ret);
  588 + return ret;
  589 + }
  590 +
496 return ret; 591 return ret;
497 } 592 }
498 593
@@ -500,9 +595,13 @@ int SrsHlsMuxer::create_dir() @@ -500,9 +595,13 @@ int SrsHlsMuxer::create_dir()
500 { 595 {
501 int ret = ERROR_SUCCESS; 596 int ret = ERROR_SUCCESS;
502 597
  598 + if (!should_write_file) {
  599 + return ret;
  600 + }
  601 +
503 std::string app_dir = hls_path; 602 std::string app_dir = hls_path;
504 app_dir += "/"; 603 app_dir += "/";
505 - app_dir += app; 604 + app_dir += req->app;
506 605
507 // TODO: cleanup the dir when startup. 606 // TODO: cleanup the dir when startup.
508 607
@@ -543,7 +642,7 @@ int SrsHlsCache::on_publish(SrsHlsMuxer* muxer, SrsRequest* req, int64_t segment @@ -543,7 +642,7 @@ int SrsHlsCache::on_publish(SrsHlsMuxer* muxer, SrsRequest* req, int64_t segment
543 // for the HLS donot requires the EXT-X-MEDIA-SEQUENCE be monotonically increase. 642 // for the HLS donot requires the EXT-X-MEDIA-SEQUENCE be monotonically increase.
544 643
545 // open muxer 644 // open muxer
546 - if ((ret = muxer->update_config(app, stream, hls_path, hls_fragment, hls_window)) != ERROR_SUCCESS) { 645 + if ((ret = muxer->update_config(req, hls_path, hls_fragment, hls_window)) != ERROR_SUCCESS) {
547 srs_error("m3u8 muxer update config failed. ret=%d", ret); 646 srs_error("m3u8 muxer update config failed. ret=%d", ret);
548 return ret; 647 return ret;
549 } 648 }
@@ -679,16 +778,18 @@ int SrsHlsCache::reap_segment(string log_desc, SrsHlsMuxer* muxer, int64_t segme @@ -679,16 +778,18 @@ int SrsHlsCache::reap_segment(string log_desc, SrsHlsMuxer* muxer, int64_t segme
679 return ret; 778 return ret;
680 } 779 }
681 780
682 -SrsHls::SrsHls(SrsSource* _source) 781 +SrsHls::SrsHls(SrsSource* s, ISrsHlsHandler* h)
683 { 782 {
  783 + source = s;
  784 + handler = h;
  785 +
684 hls_enabled = false; 786 hls_enabled = false;
685 787
686 - source = _source;  
687 codec = new SrsAvcAacCodec(); 788 codec = new SrsAvcAacCodec();
688 sample = new SrsCodecSample(); 789 sample = new SrsCodecSample();
689 jitter = new SrsRtmpJitter(); 790 jitter = new SrsRtmpJitter();
690 791
691 - muxer = new SrsHlsMuxer(); 792 + muxer = new SrsHlsMuxer(h);
692 hls_cache = new SrsHlsCache(); 793 hls_cache = new SrsHlsCache();
693 794
694 pithy_print = new SrsPithyPrint(SRS_CONSTS_STAGE_HLS); 795 pithy_print = new SrsPithyPrint(SRS_CONSTS_STAGE_HLS);
@@ -38,6 +38,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -38,6 +38,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 #include <vector> 38 #include <vector>
39 39
40 #include <srs_kernel_codec.hpp> 40 #include <srs_kernel_codec.hpp>
  41 +#include <srs_kernel_file.hpp>
41 42
42 class SrsSharedPtrMessage; 43 class SrsSharedPtrMessage;
43 class SrsCodecSample; 44 class SrsCodecSample;
@@ -53,6 +54,70 @@ class SrsFileWriter; @@ -53,6 +54,70 @@ class SrsFileWriter;
53 class SrsSimpleBuffer; 54 class SrsSimpleBuffer;
54 class SrsTsAacJitter; 55 class SrsTsAacJitter;
55 class SrsTsCache; 56 class SrsTsCache;
  57 +class SrsHlsSegment;
  58 +
  59 +/**
  60 +* the handler for hls event.
  61 +* for example, we use memory only hls for
  62 +*/
  63 +class ISrsHlsHandler
  64 +{
  65 +public:
  66 + ISrsHlsHandler();
  67 + virtual ~ISrsHlsHandler();
  68 +public:
  69 + /**
  70 + * when publish stream
  71 + */
  72 + virtual int on_hls_publish(SrsRequest* req) = 0;
  73 + /**
  74 + * when update the m3u8 file.
  75 + */
  76 + virtual int on_update_m3u8(SrsRequest* r, std::string m3u8) = 0;
  77 + /**
  78 + * when reap new ts file.
  79 + */
  80 + virtual int on_update_ts(SrsRequest* r, std::string uri, std::string ts) = 0;
  81 + /**
  82 + * when unpublish stream
  83 + */
  84 + virtual int on_hls_unpublish(SrsRequest* req) = 0;
  85 +};
  86 +
  87 +/**
  88 +* write to file and cache.
  89 +*/
  90 +class SrsHlsCacheWriter : public SrsFileWriter
  91 +{
  92 +private:
  93 + SrsFileWriter impl;
  94 + std::string data;
  95 + bool should_write_cache;
  96 + bool should_write_file;
  97 +public:
  98 + SrsHlsCacheWriter(bool write_cache, bool write_file);
  99 + virtual ~SrsHlsCacheWriter();
  100 +public:
  101 + /**
  102 + * open file writer, can open then close then open...
  103 + */
  104 + virtual int open(std::string file);
  105 + virtual void close();
  106 +public:
  107 + virtual bool is_open();
  108 + virtual int64_t tellg();
  109 +public:
  110 + /**
  111 + * write to file.
  112 + * @param pnwrite the output nb_write, NULL to ignore.
  113 + */
  114 + virtual int write(void* buf, size_t count, ssize_t* pnwrite);
  115 +public:
  116 + /**
  117 + * get the string cache.
  118 + */
  119 + virtual std::string cache();
  120 +};
56 121
57 /** 122 /**
58 * the wrapper of m3u8 segment from specification: 123 * the wrapper of m3u8 segment from specification:
@@ -72,16 +137,16 @@ public: @@ -72,16 +137,16 @@ public:
72 // ts full file to write. 137 // ts full file to write.
73 std::string full_path; 138 std::string full_path;
74 // the muxer to write ts. 139 // the muxer to write ts.
75 - SrsFileWriter* writer; 140 + SrsHlsCacheWriter* writer;
76 SrsTSMuxer* muxer; 141 SrsTSMuxer* muxer;
77 // current segment start dts for m3u8 142 // current segment start dts for m3u8
78 int64_t segment_start_dts; 143 int64_t segment_start_dts;
79 // whether current segement is sequence header. 144 // whether current segement is sequence header.
80 bool is_sequence_header; 145 bool is_sequence_header;
81 -  
82 - SrsHlsSegment(); 146 +public:
  147 + SrsHlsSegment(bool write_cache, bool write_file);
83 virtual ~SrsHlsSegment(); 148 virtual ~SrsHlsSegment();
84 - 149 +public:
85 /** 150 /**
86 * update the segment duration. 151 * update the segment duration.
87 * @current_frame_dts the dts of frame, in tbn of ts. 152 * @current_frame_dts the dts of frame, in tbn of ts.
@@ -100,8 +165,7 @@ public: @@ -100,8 +165,7 @@ public:
100 class SrsHlsMuxer 165 class SrsHlsMuxer
101 { 166 {
102 private: 167 private:
103 - std::string app;  
104 - std::string stream; 168 + SrsRequest* req;
105 private: 169 private:
106 std::string hls_path; 170 std::string hls_path;
107 int hls_fragment; 171 int hls_fragment;
@@ -110,6 +174,10 @@ private: @@ -110,6 +174,10 @@ private:
110 int _sequence_no; 174 int _sequence_no;
111 std::string m3u8; 175 std::string m3u8;
112 private: 176 private:
  177 + ISrsHlsHandler* handler;
  178 + bool should_write_cache;
  179 + bool should_write_file;
  180 +private:
113 /** 181 /**
114 * m3u8 segments. 182 * m3u8 segments.
115 */ 183 */
@@ -125,12 +193,15 @@ private: @@ -125,12 +193,15 @@ private:
125 */ 193 */
126 SrsCodecAudio acodec; 194 SrsCodecAudio acodec;
127 public: 195 public:
128 - SrsHlsMuxer(); 196 + SrsHlsMuxer(ISrsHlsHandler* h);
129 virtual ~SrsHlsMuxer(); 197 virtual ~SrsHlsMuxer();
130 public: 198 public:
131 virtual int sequence_no(); 199 virtual int sequence_no();
132 public: 200 public:
133 - virtual int update_config(std::string _app, std::string _stream, std::string path, int fragment, int window); 201 + /**
  202 + * when publish, update the config for muxer.
  203 + */
  204 + virtual int update_config(SrsRequest* r, std::string path, int fragment, int window);
134 /** 205 /**
135 * open a new segment(a new ts file), 206 * open a new segment(a new ts file),
136 * @param segment_start_dts use to calc the segment duration, 207 * @param segment_start_dts use to calc the segment duration,
@@ -160,7 +231,7 @@ public: @@ -160,7 +231,7 @@ public:
160 virtual int segment_close(std::string log_desc); 231 virtual int segment_close(std::string log_desc);
161 private: 232 private:
162 virtual int refresh_m3u8(); 233 virtual int refresh_m3u8();
163 - virtual int _refresh_m3u8(int& fd, std::string m3u8_file); 234 + virtual int _refresh_m3u8(std::string m3u8_file);
164 virtual int create_dir(); 235 virtual int create_dir();
165 }; 236 };
166 237
@@ -229,6 +300,7 @@ class SrsHls @@ -229,6 +300,7 @@ class SrsHls
229 private: 300 private:
230 SrsHlsMuxer* muxer; 301 SrsHlsMuxer* muxer;
231 SrsHlsCache* hls_cache; 302 SrsHlsCache* hls_cache;
  303 + ISrsHlsHandler* handler;
232 private: 304 private:
233 bool hls_enabled; 305 bool hls_enabled;
234 SrsSource* source; 306 SrsSource* source;
@@ -251,7 +323,7 @@ private: @@ -251,7 +323,7 @@ private:
251 */ 323 */
252 int64_t stream_dts; 324 int64_t stream_dts;
253 public: 325 public:
254 - SrsHls(SrsSource* _source); 326 + SrsHls(SrsSource* s, ISrsHlsHandler* h);
255 virtual ~SrsHls(); 327 virtual ~SrsHls();
256 public: 328 public:
257 /** 329 /**
@@ -379,7 +379,9 @@ int SrsGoHttpFileServer::serve_file(ISrsGoHttpResponseWriter* w, SrsHttpMessage* @@ -379,7 +379,9 @@ int SrsGoHttpFileServer::serve_file(ISrsGoHttpResponseWriter* w, SrsHttpMessage*
379 // write body. 379 // write body.
380 int64_t left = length; 380 int64_t left = length;
381 if ((ret = copy(w, &fs, r, left)) != ERROR_SUCCESS) { 381 if ((ret = copy(w, &fs, r, left)) != ERROR_SUCCESS) {
382 - srs_warn("read file=%s size=%d failed, ret=%d", fullpath.c_str(), left, ret); 382 + if (!srs_is_client_gracefully_close(ret)) {
  383 + srs_error("read file=%s size=%d failed, ret=%d", fullpath.c_str(), left, ret);
  384 + }
383 return ret; 385 return ret;
384 } 386 }
385 387
@@ -680,18 +680,96 @@ SrsLiveEntry::SrsLiveEntry() @@ -680,18 +680,96 @@ SrsLiveEntry::SrsLiveEntry()
680 cache = NULL; 680 cache = NULL;
681 } 681 }
682 682
  683 +SrsHlsM3u8Stream::SrsHlsM3u8Stream()
  684 +{
  685 +}
  686 +
  687 +SrsHlsM3u8Stream::~SrsHlsM3u8Stream()
  688 +{
  689 +}
  690 +
  691 +void SrsHlsM3u8Stream::set_m3u8(std::string v)
  692 +{
  693 + m3u8 = v;
  694 +}
  695 +
  696 +int SrsHlsM3u8Stream::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r)
  697 +{
  698 + int ret = ERROR_SUCCESS;
  699 +
  700 + std::string data = m3u8;
  701 +
  702 + w->header()->set_content_length((int)data.length());
  703 + w->header()->set_content_type("application/x-mpegURL;charset=utf-8");
  704 +
  705 + if ((ret = w->write((char*)data.data(), (int)data.length())) != ERROR_SUCCESS) {
  706 + if (!srs_is_client_gracefully_close(ret)) {
  707 + srs_error("send m3u8 failed. ret=%d", ret);
  708 + }
  709 + return ret;
  710 + }
  711 +
  712 + return ret;
  713 +}
  714 +
  715 +SrsHlsTsStream::SrsHlsTsStream()
  716 +{
  717 +}
  718 +
  719 +SrsHlsTsStream::~SrsHlsTsStream()
  720 +{
  721 +}
  722 +
  723 +void SrsHlsTsStream::set_ts(std::string v)
  724 +{
  725 + ts = v;
  726 +}
  727 +
  728 +int SrsHlsTsStream::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r)
  729 +{
  730 + int ret = ERROR_SUCCESS;
  731 +
  732 + std::string data = ts;
  733 +
  734 + w->header()->set_content_length((int)data.length());
  735 + w->header()->set_content_type("video/MP2T");
  736 +
  737 + if ((ret = w->write((char*)data.data(), (int)data.length())) != ERROR_SUCCESS) {
  738 + if (!srs_is_client_gracefully_close(ret)) {
  739 + srs_error("send ts failed. ret=%d", ret);
  740 + }
  741 + return ret;
  742 + }
  743 +
  744 + return ret;
  745 +}
  746 +
  747 +SrsHlsEntry::SrsHlsEntry()
  748 +{
  749 +}
  750 +
683 SrsHttpServer::SrsHttpServer() 751 SrsHttpServer::SrsHttpServer()
684 { 752 {
685 } 753 }
686 754
687 SrsHttpServer::~SrsHttpServer() 755 SrsHttpServer::~SrsHttpServer()
688 { 756 {
  757 + if (true) {
689 std::map<std::string, SrsLiveEntry*>::iterator it; 758 std::map<std::string, SrsLiveEntry*>::iterator it;
690 for (it = flvs.begin(); it != flvs.end(); ++it) { 759 for (it = flvs.begin(); it != flvs.end(); ++it) {
691 SrsLiveEntry* entry = it->second; 760 SrsLiveEntry* entry = it->second;
692 srs_freep(entry); 761 srs_freep(entry);
693 } 762 }
694 flvs.clear(); 763 flvs.clear();
  764 + }
  765 + if (true) {
  766 + std::map<std::string, SrsHlsEntry*>::iterator it;
  767 + for (it = hls.begin(); it != hls.end(); ++it) {
  768 + SrsHlsEntry* entry = it->second;
  769 + srs_freep(entry);
  770 + }
  771 + hls.clear();
  772 + }
695 } 773 }
696 774
697 int SrsHttpServer::initialize() 775 int SrsHttpServer::initialize()
@@ -700,12 +778,17 @@ int SrsHttpServer::initialize() @@ -700,12 +778,17 @@ int SrsHttpServer::initialize()
700 778
701 // static file 779 // static file
702 // flv vod streaming. 780 // flv vod streaming.
703 - if ((ret = mount_static_file()) != ERROR_SUCCESS) { 781 + if ((ret = initialize_static_file()) != ERROR_SUCCESS) {
704 return ret; 782 return ret;
705 } 783 }
706 784
707 // remux rtmp to flv live streaming 785 // remux rtmp to flv live streaming
708 - if ((ret = mount_flv_streaming()) != ERROR_SUCCESS) { 786 + if ((ret = initialize_flv_streaming()) != ERROR_SUCCESS) {
  787 + return ret;
  788 + }
  789 +
  790 + // remux rtmp to hls live streaming
  791 + if ((ret = initialize_hls_streaming()) != ERROR_SUCCESS) {
709 return ret; 792 return ret;
710 } 793 }
711 794
@@ -769,6 +852,128 @@ void SrsHttpServer::unmount(SrsSource* s, SrsRequest* r) @@ -769,6 +852,128 @@ void SrsHttpServer::unmount(SrsSource* s, SrsRequest* r)
769 entry->stream->entry->enabled = false; 852 entry->stream->entry->enabled = false;
770 } 853 }
771 854
  855 +int SrsHttpServer::mount_hls(SrsRequest* r)
  856 +{
  857 + int ret = ERROR_SUCCESS;
  858 +
  859 + if (hls.find(r->vhost) == hls.end()) {
  860 + srs_info("ignore mount hls stream for disabled");
  861 + return ret;
  862 + }
  863 +
  864 + SrsHlsEntry* entry = hls[r->vhost];
  865 +
  866 + // TODO: FIXME: supports reload.
  867 + std::map<std::string, ISrsGoHttpHandler*>::iterator it;
  868 + for (it = entry->streams.begin(); it != entry->streams.end(); ++it) {
  869 + ISrsGoHttpHandler* stream = it->second;
  870 + stream->entry->enabled = true;
  871 + }
  872 +
  873 + return ret;
  874 +}
  875 +
  876 +int SrsHttpServer::hls_update_m3u8(SrsRequest* r, string m3u8)
  877 +{
  878 + int ret = ERROR_SUCCESS;
  879 +
  880 + srs_assert(hls.find(r->vhost) != hls.end());
  881 + SrsHlsEntry* entry = hls[r->vhost];
  882 + srs_assert(entry);
  883 +
  884 + std::string mount = entry->mount;
  885 +
  886 + // replace the vhost variable
  887 + mount = srs_string_replace(mount, "[vhost]", r->vhost);
  888 + mount = srs_string_replace(mount, "[app]", r->app);
  889 + mount = srs_string_replace(mount, "[stream]", r->stream);
  890 +
  891 + // remove the default vhost mount
  892 + mount = srs_string_replace(mount, SRS_CONSTS_RTMP_DEFAULT_VHOST"/", "/");
  893 +
  894 + if (entry->streams.find(mount) == entry->streams.end()) {
  895 + ISrsGoHttpHandler* he = new SrsHlsM3u8Stream();
  896 + entry->streams[mount] = he;
  897 +
  898 + if ((ret = mux.handle(mount, he)) != ERROR_SUCCESS) {
  899 + srs_error("handle mount=%s failed. ret=%d", mount.c_str(), ret);
  900 + return ret;
  901 + }
  902 + }
  903 +
  904 + // update the m3u8 stream.
  905 + SrsHlsM3u8Stream* hms = dynamic_cast<SrsHlsM3u8Stream*>(entry->streams[mount]);
  906 + if (hms) {
  907 + hms->set_m3u8(m3u8);
  908 + }
  909 + srs_trace("hls update m3u8 ok, mount=%s", mount.c_str());
  910 +
  911 + return ret;
  912 +}
  913 +
  914 +int SrsHttpServer::hls_update_ts(SrsRequest* r, string uri, string ts)
  915 +{
  916 + int ret = ERROR_SUCCESS;
  917 +
  918 + srs_assert(hls.find(r->vhost) != hls.end());
  919 + SrsHlsEntry* entry = hls[r->vhost];
  920 + srs_assert(entry);
  921 +
  922 + std::string mount = entry->mount;
  923 +
  924 + // the ts is relative from the m3u8, the same start dir.
  925 + size_t pos = string::npos;
  926 + if ((pos = mount.rfind("/")) != string::npos) {
  927 + mount = mount.substr(0, pos);
  928 + }
  929 +
  930 + // replace the vhost variable
  931 + mount = srs_string_replace(mount, "[vhost]", r->vhost);
  932 + mount = srs_string_replace(mount, "[app]", r->app);
  933 +
  934 + // remove the default vhost mount
  935 + mount = srs_string_replace(mount, SRS_CONSTS_RTMP_DEFAULT_VHOST"/", "/");
  936 +
  937 + // mount with ts.
  938 + mount += "/";
  939 + mount += uri;
  940 +
  941 + if (entry->streams.find(mount) == entry->streams.end()) {
  942 + ISrsGoHttpHandler* he = new SrsHlsTsStream();
  943 + entry->streams[mount] = he;
  944 +
  945 + if ((ret = mux.handle(mount, he)) != ERROR_SUCCESS) {
  946 + srs_error("handle mount=%s failed. ret=%d", mount.c_str(), ret);
  947 + return ret;
  948 + }
  949 + }
  950 +
  951 + // update the ts stream.
  952 + SrsHlsTsStream* hts = dynamic_cast<SrsHlsTsStream*>(entry->streams[mount]);
  953 + if (hts) {
  954 + hts->set_ts(ts);
  955 + }
  956 + srs_trace("hls update ts ok, mount=%s", mount.c_str());
  957 +
  958 + return ret;
  959 +}
  960 +
  961 +void SrsHttpServer::unmount_hls(SrsRequest* r)
  962 +{
  963 + if (hls.find(r->vhost) == hls.end()) {
  964 + srs_info("ignore unmount hls stream for disabled");
  965 + return;
  966 + }
  967 +
  968 + SrsHlsEntry* entry = hls[r->vhost];
  969 +
  970 + std::map<std::string, ISrsGoHttpHandler*>::iterator it;
  971 + for (it = entry->streams.begin(); it != entry->streams.end(); ++it) {
  972 + ISrsGoHttpHandler* stream = it->second;
  973 + stream->entry->enabled = false;
  974 + }
  975 +}
  976 +
772 int SrsHttpServer::on_reload_vhost_http_updated() 977 int SrsHttpServer::on_reload_vhost_http_updated()
773 { 978 {
774 int ret = ERROR_SUCCESS; 979 int ret = ERROR_SUCCESS;
@@ -783,7 +988,14 @@ int SrsHttpServer::on_reload_vhost_http_remux_updated() @@ -783,7 +988,14 @@ int SrsHttpServer::on_reload_vhost_http_remux_updated()
783 return ret; 988 return ret;
784 } 989 }
785 990
786 -int SrsHttpServer::mount_static_file() 991 +int SrsHttpServer::on_reload_vhost_hls(string vhost)
  992 +{
  993 + int ret = ERROR_SUCCESS;
  994 + // TODO: FIXME: implements it.
  995 + return ret;
  996 +}
  997 +
  998 +int SrsHttpServer::initialize_static_file()
787 { 999 {
788 int ret = ERROR_SUCCESS; 1000 int ret = ERROR_SUCCESS;
789 1001
@@ -843,7 +1055,7 @@ int SrsHttpServer::mount_static_file() @@ -843,7 +1055,7 @@ int SrsHttpServer::mount_static_file()
843 return ret; 1055 return ret;
844 } 1056 }
845 1057
846 -int SrsHttpServer::mount_flv_streaming() 1058 +int SrsHttpServer::initialize_flv_streaming()
847 { 1059 {
848 int ret = ERROR_SUCCESS; 1060 int ret = ERROR_SUCCESS;
849 1061
@@ -872,6 +1084,40 @@ int SrsHttpServer::mount_flv_streaming() @@ -872,6 +1084,40 @@ int SrsHttpServer::mount_flv_streaming()
872 return ret; 1084 return ret;
873 } 1085 }
874 1086
  1087 +int SrsHttpServer::initialize_hls_streaming()
  1088 +{
  1089 + int ret = ERROR_SUCCESS;
  1090 +
  1091 + // http hls live stream mount for each vhost.
  1092 + SrsConfDirective* root = _srs_config->get_root();
  1093 + for (int i = 0; i < (int)root->directives.size(); i++) {
  1094 + SrsConfDirective* conf = root->at(i);
  1095 +
  1096 + if (!conf->is_vhost()) {
  1097 + continue;
  1098 + }
  1099 +
  1100 + std::string vhost = conf->arg0();
  1101 + if (!_srs_config->get_hls_enabled(vhost)) {
  1102 + continue;
  1103 + }
  1104 +
  1105 + std::string storage = _srs_config->get_hls_storage(vhost);
  1106 + if (storage != "ram" && storage != "both") {
  1107 + continue;
  1108 + }
  1109 +
  1110 + SrsHlsEntry* entry = new SrsHlsEntry();
  1111 + entry->vhost = vhost;
  1112 + entry->mount = _srs_config->get_hls_mount(vhost);
  1113 + hls[vhost] = entry;
  1114 + srs_trace("http hls live stream, vhost=%s, mount=%s",
  1115 + vhost.c_str(), entry->mount.c_str());
  1116 + }
  1117 +
  1118 + return ret;
  1119 +}
  1120 +
875 SrsHttpConn::SrsHttpConn(SrsServer* svr, st_netfd_t fd, SrsHttpServer* m) 1121 SrsHttpConn::SrsHttpConn(SrsServer* svr, st_netfd_t fd, SrsHttpServer* m)
876 : SrsConnection(svr, fd) 1122 : SrsConnection(svr, fd)
877 { 1123 {
@@ -261,6 +261,53 @@ struct SrsLiveEntry @@ -261,6 +261,53 @@ struct SrsLiveEntry
261 }; 261 };
262 262
263 /** 263 /**
  264 +* the m3u8 stream handler.
  265 +*/
  266 +class SrsHlsM3u8Stream : public ISrsGoHttpHandler
  267 +{
  268 +private:
  269 + std::string m3u8;
  270 +public:
  271 + SrsHlsM3u8Stream();
  272 + virtual ~SrsHlsM3u8Stream();
  273 +public:
  274 + virtual void set_m3u8(std::string v);
  275 +public:
  276 + virtual int serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r);
  277 +};
  278 +
  279 +/**
  280 +* the ts stream handler.
  281 +*/
  282 +class SrsHlsTsStream : public ISrsGoHttpHandler
  283 +{
  284 +private:
  285 + std::string ts;
  286 +public:
  287 + SrsHlsTsStream();
  288 + virtual ~SrsHlsTsStream();
  289 +public:
  290 + virtual void set_ts(std::string v);
  291 +public:
  292 + virtual int serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r);
  293 +};
  294 +
  295 +/**
  296 +* the srs hls entry.
  297 +*/
  298 +struct SrsHlsEntry
  299 +{
  300 + std::string vhost;
  301 + std::string mount;
  302 +
  303 + // key: the m3u8/ts file path.
  304 + // value: the http handler.
  305 + std::map<std::string, ISrsGoHttpHandler*> streams;
  306 +
  307 + SrsHlsEntry();
  308 +};
  309 +
  310 +/**
264 * the http server instance, 311 * the http server instance,
265 * serve http static file, flv vod stream and flv live stream. 312 * serve http static file, flv vod stream and flv live stream.
266 */ 313 */
@@ -270,21 +317,32 @@ public: @@ -270,21 +317,32 @@ public:
270 SrsGoHttpServeMux mux; 317 SrsGoHttpServeMux mux;
271 // the flv live streaming template. 318 // the flv live streaming template.
272 std::map<std::string, SrsLiveEntry*> flvs; 319 std::map<std::string, SrsLiveEntry*> flvs;
  320 + // the hls live streaming template.
  321 + std::map<std::string, SrsHlsEntry*> hls;
273 public: 322 public:
274 SrsHttpServer(); 323 SrsHttpServer();
275 virtual ~SrsHttpServer(); 324 virtual ~SrsHttpServer();
276 public: 325 public:
277 virtual int initialize(); 326 virtual int initialize();
  327 +// http flv/ts/mp3/aac stream
278 public: 328 public:
279 virtual int mount(SrsSource* s, SrsRequest* r); 329 virtual int mount(SrsSource* s, SrsRequest* r);
280 virtual void unmount(SrsSource* s, SrsRequest* r); 330 virtual void unmount(SrsSource* s, SrsRequest* r);
  331 +// hls stream
  332 +public:
  333 + virtual int mount_hls(SrsRequest* r);
  334 + virtual int hls_update_m3u8(SrsRequest* r, std::string m3u8);
  335 + virtual int hls_update_ts(SrsRequest* r, std::string uri, std::string ts);
  336 + virtual void unmount_hls(SrsRequest* r);
281 // interface ISrsThreadHandler. 337 // interface ISrsThreadHandler.
282 public: 338 public:
283 virtual int on_reload_vhost_http_updated(); 339 virtual int on_reload_vhost_http_updated();
284 virtual int on_reload_vhost_http_remux_updated(); 340 virtual int on_reload_vhost_http_remux_updated();
  341 + virtual int on_reload_vhost_hls(std::string vhost);
285 private: 342 private:
286 - virtual int mount_static_file();  
287 - virtual int mount_flv_streaming(); 343 + virtual int initialize_static_file();
  344 + virtual int initialize_flv_streaming();
  345 + virtual int initialize_hls_streaming();
288 }; 346 };
289 347
290 class SrsHttpConn : public SrsConnection 348 class SrsHttpConn : public SrsConnection
@@ -393,7 +393,7 @@ int SrsRtmpConn::stream_service_cycle() @@ -393,7 +393,7 @@ int SrsRtmpConn::stream_service_cycle()
393 393
394 // find a source to serve. 394 // find a source to serve.
395 SrsSource* source = NULL; 395 SrsSource* source = NULL;
396 - if ((ret = SrsSource::find(req, server, &source)) != ERROR_SUCCESS) { 396 + if ((ret = SrsSource::find(req, server, server, &source)) != ERROR_SUCCESS) {
397 return ret; 397 return ret;
398 } 398 }
399 srs_assert(source != NULL); 399 srs_assert(source != NULL);
@@ -32,6 +32,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -32,6 +32,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 #include <fcntl.h> 32 #include <fcntl.h>
33 33
34 #include <algorithm> 34 #include <algorithm>
  35 +using namespace std;
35 36
36 #include <srs_kernel_log.hpp> 37 #include <srs_kernel_log.hpp>
37 #include <srs_kernel_error.hpp> 38 #include <srs_kernel_error.hpp>
@@ -1277,3 +1278,53 @@ void SrsServer::on_unpublish(SrsSource* s, SrsRequest* r) @@ -1277,3 +1278,53 @@ void SrsServer::on_unpublish(SrsSource* s, SrsRequest* r)
1277 #endif 1278 #endif
1278 } 1279 }
1279 1280
  1281 +int SrsServer::on_hls_publish(SrsRequest* r)
  1282 +{
  1283 + int ret = ERROR_SUCCESS;
  1284 +
  1285 +#ifdef SRS_AUTO_HTTP_SERVER
  1286 + if ((ret = http_stream_mux->mount_hls(r)) != ERROR_SUCCESS) {
  1287 + return ret;
  1288 + }
  1289 +#endif
  1290 +
  1291 + return ret;
  1292 +}
  1293 +
  1294 +int SrsServer::on_update_m3u8(SrsRequest* r, string m3u8)
  1295 +{
  1296 + int ret = ERROR_SUCCESS;
  1297 +
  1298 +#ifdef SRS_AUTO_HTTP_SERVER
  1299 + if ((ret = http_stream_mux->hls_update_m3u8(r, m3u8)) != ERROR_SUCCESS) {
  1300 + return ret;
  1301 + }
  1302 +#endif
  1303 +
  1304 + return ret;
  1305 +}
  1306 +
  1307 +int SrsServer::on_update_ts(SrsRequest* r, string uri, string ts)
  1308 +{
  1309 + int ret = ERROR_SUCCESS;
  1310 +
  1311 +#ifdef SRS_AUTO_HTTP_SERVER
  1312 + if ((ret = http_stream_mux->hls_update_ts(r, uri, ts)) != ERROR_SUCCESS) {
  1313 + return ret;
  1314 + }
  1315 +#endif
  1316 +
  1317 + return ret;
  1318 +}
  1319 +
  1320 +int SrsServer::on_hls_unpublish(SrsRequest* r)
  1321 +{
  1322 + int ret = ERROR_SUCCESS;
  1323 +
  1324 +#ifdef SRS_AUTO_HTTP_SERVER
  1325 + http_stream_mux->unmount_hls(r);
  1326 +#endif
  1327 +
  1328 + return ret;
  1329 +}
  1330 +
@@ -31,11 +31,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -31,11 +31,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 #include <srs_core.hpp> 31 #include <srs_core.hpp>
32 32
33 #include <vector> 33 #include <vector>
  34 +#include <string>
34 35
35 #include <srs_app_st.hpp> 36 #include <srs_app_st.hpp>
36 #include <srs_app_reload.hpp> 37 #include <srs_app_reload.hpp>
37 #include <srs_app_thread.hpp> 38 #include <srs_app_thread.hpp>
38 #include <srs_app_source.hpp> 39 #include <srs_app_source.hpp>
  40 +#include <srs_app_hls.hpp>
39 41
40 class SrsServer; 42 class SrsServer;
41 class SrsConnection; 43 class SrsConnection;
@@ -142,7 +144,7 @@ private: @@ -142,7 +144,7 @@ private:
142 * start connection service thread, destroy client. 144 * start connection service thread, destroy client.
143 */ 145 */
144 class SrsServer : virtual public ISrsReloadHandler 146 class SrsServer : virtual public ISrsReloadHandler
145 - , virtual public ISrsSourceHandler 147 + , virtual public ISrsSourceHandler, virtual public ISrsHlsHandler
146 { 148 {
147 private: 149 private:
148 #ifdef SRS_AUTO_HTTP_API 150 #ifdef SRS_AUTO_HTTP_API
@@ -275,6 +277,12 @@ public: @@ -275,6 +277,12 @@ public:
275 public: 277 public:
276 virtual int on_publish(SrsSource* s, SrsRequest* r); 278 virtual int on_publish(SrsSource* s, SrsRequest* r);
277 virtual void on_unpublish(SrsSource* s, SrsRequest* r); 279 virtual void on_unpublish(SrsSource* s, SrsRequest* r);
  280 +// interface ISrsHlsHandler
  281 +public:
  282 + virtual int on_hls_publish(SrsRequest* r);
  283 + virtual int on_update_m3u8(SrsRequest* r, std::string m3u8);
  284 + virtual int on_update_ts(SrsRequest* r, std::string uri, std::string ts);
  285 + virtual int on_hls_unpublish(SrsRequest* r);
278 }; 286 };
279 287
280 #endif 288 #endif
@@ -713,7 +713,7 @@ ISrsSourceHandler::~ISrsSourceHandler() @@ -713,7 +713,7 @@ ISrsSourceHandler::~ISrsSourceHandler()
713 713
714 std::map<std::string, SrsSource*> SrsSource::pool; 714 std::map<std::string, SrsSource*> SrsSource::pool;
715 715
716 -int SrsSource::find(SrsRequest* r, ISrsSourceHandler* h, SrsSource** pps) 716 +int SrsSource::find(SrsRequest* r, ISrsSourceHandler* h, ISrsHlsHandler* hh, SrsSource** pps)
717 { 717 {
718 int ret = ERROR_SUCCESS; 718 int ret = ERROR_SUCCESS;
719 719
@@ -721,7 +721,7 @@ int SrsSource::find(SrsRequest* r, ISrsSourceHandler* h, SrsSource** pps) @@ -721,7 +721,7 @@ int SrsSource::find(SrsRequest* r, ISrsSourceHandler* h, SrsSource** pps)
721 string vhost = r->vhost; 721 string vhost = r->vhost;
722 722
723 if (pool.find(stream_url) == pool.end()) { 723 if (pool.find(stream_url) == pool.end()) {
724 - SrsSource* source = new SrsSource(); 724 + SrsSource* source = new SrsSource(hh);
725 if ((ret = source->initialize(r, h)) != ERROR_SUCCESS) { 725 if ((ret = source->initialize(r, h)) != ERROR_SUCCESS) {
726 srs_freep(source); 726 srs_freep(source);
727 return ret; 727 return ret;
@@ -754,13 +754,14 @@ void SrsSource::destroy() @@ -754,13 +754,14 @@ void SrsSource::destroy()
754 pool.clear(); 754 pool.clear();
755 } 755 }
756 756
757 -SrsSource::SrsSource() 757 +SrsSource::SrsSource(ISrsHlsHandler* hh)
758 { 758 {
759 _req = NULL; 759 _req = NULL;
760 jitter_algorithm = SrsRtmpJitterAlgorithmOFF; 760 jitter_algorithm = SrsRtmpJitterAlgorithmOFF;
761 761
762 #ifdef SRS_AUTO_HLS 762 #ifdef SRS_AUTO_HLS
763 - hls = new SrsHls(this); 763 + // TODO: FIXME: refine code, use subscriber pattern.
  764 + hls = new SrsHls(this, hh);
764 #endif 765 #endif
765 #ifdef SRS_AUTO_DVR 766 #ifdef SRS_AUTO_DVR
766 dvr = new SrsDvr(this); 767 dvr = new SrsDvr(this);
@@ -61,6 +61,7 @@ class SrsDvr; @@ -61,6 +61,7 @@ class SrsDvr;
61 class SrsEncoder; 61 class SrsEncoder;
62 #endif 62 #endif
63 class SrsStream; 63 class SrsStream;
  64 +class ISrsHlsHandler;
64 65
65 /** 66 /**
66 * the time jitter algorithm: 67 * the time jitter algorithm:
@@ -376,9 +377,10 @@ public: @@ -376,9 +377,10 @@ public:
376 * find stream by vhost/app/stream. 377 * find stream by vhost/app/stream.
377 * @param r the client request. 378 * @param r the client request.
378 * @param h the event handler for source. 379 * @param h the event handler for source.
  380 + * @param hh the event handler for hls.
379 * @param pps the matched source, if success never be NULL. 381 * @param pps the matched source, if success never be NULL.
380 */ 382 */
381 - static int find(SrsRequest* r, ISrsSourceHandler* h, SrsSource** pps); 383 + static int find(SrsRequest* r, ISrsSourceHandler* h, ISrsHlsHandler* hh, SrsSource** pps);
382 /** 384 /**
383 * when system exit, destroy the sources, 385 * when system exit, destroy the sources,
384 * for gmc to analysis mem leaks. 386 * for gmc to analysis mem leaks.
@@ -451,7 +453,7 @@ public: @@ -451,7 +453,7 @@ public:
451 * @param _req the client request object, 453 * @param _req the client request object,
452 * this object will deep copy it for reload. 454 * this object will deep copy it for reload.
453 */ 455 */
454 - SrsSource(); 456 + SrsSource(ISrsHlsHandler* hh);
455 virtual ~SrsSource(); 457 virtual ~SrsSource();
456 // initialize, get and setter. 458 // initialize, get and setter.
457 public: 459 public:
@@ -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 111 34 +#define VERSION_REVISION 112
35 35
36 // server info. 36 // server info.
37 #define RTMP_SIG_SRS_KEY "SRS" 37 #define RTMP_SIG_SRS_KEY "SRS"