正在显示
14 个修改的文件
包含
688 行增加
和
87 行删除
| @@ -455,24 +455,37 @@ vhost with-hls.srs.com { | @@ -455,24 +455,37 @@ vhost with-hls.srs.com { | ||
| 455 | # default: 60 | 455 | # default: 60 |
| 456 | hls_window 60; | 456 | hls_window 60; |
| 457 | # the error strategy. canbe: | 457 | # the error strategy. canbe: |
| 458 | - # ignore, when error ignore and disable hls. | ||
| 459 | - # disconnect, when error disconnect the publish connection. | ||
| 460 | - # continue, when error ignore and continue output hls. | 458 | + # ignore, when error ignore and disable hls. |
| 459 | + # disconnect, when error disconnect the publish connection. | ||
| 460 | + # continue, when error ignore and continue output hls. | ||
| 461 | # @see https://github.com/winlinvip/simple-rtmp-server/issues/264 | 461 | # @see https://github.com/winlinvip/simple-rtmp-server/issues/264 |
| 462 | # default: ignore | 462 | # default: ignore |
| 463 | hls_on_error ignore; | 463 | hls_on_error ignore; |
| 464 | # the hls output path. | 464 | # the hls output path. |
| 465 | # the app dir is auto created under the hls_path. | 465 | # the app dir is auto created under the hls_path. |
| 466 | # for example, for rtmp stream: | 466 | # for example, for rtmp stream: |
| 467 | - # rtmp://127.0.0.1/live/livestream | ||
| 468 | - # http://127.0.0.1/live/livestream.m3u8 | 467 | + # rtmp://127.0.0.1/live/livestream |
| 468 | + # http://127.0.0.1/live/livestream.m3u8 | ||
| 469 | # where hls_path is /hls, srs will create the following files: | 469 | # where hls_path is /hls, srs will create the following files: |
| 470 | - # /hls/live the app dir for all streams. | ||
| 471 | - # /hls/live/livestream.m3u8 the HLS m3u8 file. | ||
| 472 | - # /hls/live/livestream-1.ts the HLS media/ts file. | 470 | + # /hls/live the app dir for all streams. |
| 471 | + # /hls/live/livestream.m3u8 the HLS m3u8 file. | ||
| 472 | + # /hls/live/livestream-1.ts the HLS media/ts file. | ||
| 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,16 +191,29 @@ int SrsHlsMuxer::sequence_no() | @@ -112,16 +191,29 @@ 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; |
| 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 | + } | ||
| 125 | 217 | ||
| 126 | return ret; | 218 | return ret; |
| 127 | } | 219 | } |
| @@ -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 | ||
| @@ -289,6 +381,13 @@ int SrsHlsMuxer::segment_close(string log_desc) | @@ -289,6 +381,13 @@ int SrsHlsMuxer::segment_close(string log_desc) | ||
| 289 | srs_info("%s reap ts segment, sequence_no=%d, uri=%s, duration=%.2f, start=%"PRId64"", | 381 | srs_info("%s reap ts segment, sequence_no=%d, uri=%s, duration=%.2f, start=%"PRId64"", |
| 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); |
| 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 | + } | ||
| 292 | 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); |
| @@ -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"; |
| 316 | - unlink(tmp_file.c_str()); | 415 | + if (should_write_file) { |
| 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 | ||
| @@ -398,11 +495,9 @@ int SrsHlsMuxer::_refresh_m3u8(int& fd, string m3u8_file) | @@ -398,11 +495,9 @@ int SrsHlsMuxer::_refresh_m3u8(int& fd, string m3u8_file) | ||
| 398 | if (segments.size() == 0) { | 495 | if (segments.size() == 0) { |
| 399 | return ret; | 496 | return ret; |
| 400 | } | 497 | } |
| 401 | - | ||
| 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; | 498 | + |
| 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,14 +574,19 @@ int SrsHlsMuxer::_refresh_m3u8(int& fd, string m3u8_file) | @@ -484,14 +574,19 @@ 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 | } |
| 492 | srs_verbose("write m3u8 segment uri success."); | 581 | srs_verbose("write m3u8 segment uri success."); |
| 493 | } | 582 | } |
| 494 | srs_info("write m3u8 %s success.", m3u8_file.c_str()); | 583 | srs_info("write m3u8 %s success.", m3u8_file.c_str()); |
| 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 | + } | ||
| 495 | 590 | ||
| 496 | return ret; | 591 | return ret; |
| 497 | } | 592 | } |
| @@ -499,10 +594,14 @@ int SrsHlsMuxer::_refresh_m3u8(int& fd, string m3u8_file) | @@ -499,10 +594,14 @@ int SrsHlsMuxer::_refresh_m3u8(int& fd, string m3u8_file) | ||
| 499 | int SrsHlsMuxer::create_dir() | 594 | int SrsHlsMuxer::create_dir() |
| 500 | { | 595 | { |
| 501 | int ret = ERROR_SUCCESS; | 596 | int ret = ERROR_SUCCESS; |
| 597 | + | ||
| 598 | + if (!should_write_file) { | ||
| 599 | + return ret; | ||
| 600 | + } | ||
| 502 | 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 | { |
| 684 | - hls_enabled = false; | 783 | + source = s; |
| 784 | + handler = h; | ||
| 685 | 785 | ||
| 686 | - source = _source; | 786 | + hls_enabled = false; |
| 787 | + | ||
| 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 | { |
| 689 | - std::map<std::string, SrsLiveEntry*>::iterator it; | ||
| 690 | - for (it = flvs.begin(); it != flvs.end(); ++it) { | ||
| 691 | - SrsLiveEntry* entry = it->second; | ||
| 692 | - srs_freep(entry); | 757 | + if (true) { |
| 758 | + std::map<std::string, SrsLiveEntry*>::iterator it; | ||
| 759 | + for (it = flvs.begin(); it != flvs.end(); ++it) { | ||
| 760 | + SrsLiveEntry* entry = it->second; | ||
| 761 | + srs_freep(entry); | ||
| 762 | + } | ||
| 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(); | ||
| 693 | } | 772 | } |
| 694 | - flvs.clear(); | ||
| 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" |
-
请 注册 或 登录 后发表评论