正在显示
13 个修改的文件
包含
235 行增加
和
110 行删除
| @@ -562,6 +562,7 @@ Supported operating systems and hardware: | @@ -562,6 +562,7 @@ Supported operating systems and hardware: | ||
| 562 | 562 | ||
| 563 | ### SRS 2.0 history | 563 | ### SRS 2.0 history |
| 564 | 564 | ||
| 565 | +* v2.0, 2015-03-30, for [#351](https://github.com/winlinvip/simple-rtmp-server/issues/351), support config the m3u8/ts path for hls. 2.0.149. | ||
| 565 | * v2.0, 2015-03-17, for [#155](https://github.com/winlinvip/simple-rtmp-server/issues/155), osx(darwin) support demo with nginx and ffmpeg. 2.0.143. | 566 | * v2.0, 2015-03-17, for [#155](https://github.com/winlinvip/simple-rtmp-server/issues/155), osx(darwin) support demo with nginx and ffmpeg. 2.0.143. |
| 566 | * v2.0, 2015-03-15, start [2.0release branch](https://github.com/winlinvip/simple-rtmp-server/tree/2.0release), 80773 lines. | 567 | * v2.0, 2015-03-15, start [2.0release branch](https://github.com/winlinvip/simple-rtmp-server/tree/2.0release), 80773 lines. |
| 567 | * v2.0, 2015-03-14, fix [#324](https://github.com/winlinvip/simple-rtmp-server/issues/324), support hstrs(http stream trigger rtmp source) edge mode. 2.0.140. | 568 | * v2.0, 2015-03-14, fix [#324](https://github.com/winlinvip/simple-rtmp-server/issues/324), support hstrs(http stream trigger rtmp source) edge mode. 2.0.140. |
| @@ -528,17 +528,38 @@ vhost with-hls.srs.com { | @@ -528,17 +528,38 @@ vhost with-hls.srs.com { | ||
| 528 | # default: disk | 528 | # default: disk |
| 529 | hls_storage disk; | 529 | hls_storage disk; |
| 530 | # the hls output path. | 530 | # the hls output path. |
| 531 | - # the app dir is auto created under the hls_path. | ||
| 532 | - # for example, for rtmp stream: | ||
| 533 | - # rtmp://127.0.0.1/live/livestream | ||
| 534 | - # http://127.0.0.1/live/livestream.m3u8 | ||
| 535 | - # where hls_path is /hls, srs will create the following files: | ||
| 536 | - # /hls/live the app dir for all streams. | ||
| 537 | - # /hls/live/livestream.m3u8 the HLS m3u8 file. | ||
| 538 | - # /hls/live/livestream-1.ts the HLS media/ts file. | ||
| 539 | - # in a word, the hls_path is for vhost. | 531 | + # the m3u8 file is configed by hls_path/hls_m3u8_file, the default is: |
| 532 | + # ./objs/nginx/html/[app]/[stream].m3u8 | ||
| 533 | + # the ts file is configed by hls_path/hls_ts_file, the default is: | ||
| 534 | + # ./objs/nginx/html/[app]/[stream]-[seq].ts | ||
| 535 | + # @remark the hls_path is compatible with srs v1 config. | ||
| 540 | # default: ./objs/nginx/html | 536 | # default: ./objs/nginx/html |
| 541 | hls_path ./objs/nginx/html; | 537 | hls_path ./objs/nginx/html; |
| 538 | + # the hls m3u8 file name. | ||
| 539 | + # we supports some variables to generate the filename. | ||
| 540 | + # [vhost], the vhost of stream. | ||
| 541 | + # [app], the app of stream. | ||
| 542 | + # [stream], the stream name of stream. | ||
| 543 | + # default: [app]/[stream].m3u8 | ||
| 544 | + hls_m3u8_file [app]/[stream].m3u8; | ||
| 545 | + # the hls ts file name. | ||
| 546 | + # we supports some variables to generate the filename. | ||
| 547 | + # [vhost], the vhost of stream. | ||
| 548 | + # [app], the app of stream. | ||
| 549 | + # [stream], the stream name of stream. | ||
| 550 | + # [2006], replace this const to current year. | ||
| 551 | + # [01], replace this const to current month. | ||
| 552 | + # [02], replace this const to current date. | ||
| 553 | + # [15], replace this const to current hour. | ||
| 554 | + # [04], repleace this const to current minute. | ||
| 555 | + # [05], repleace this const to current second. | ||
| 556 | + # [999], repleace this const to current millisecond. | ||
| 557 | + # [timestamp],replace this const to current UNIX timestamp in ms. | ||
| 558 | + # [seq], the sequence number of ts. | ||
| 559 | + # @see https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DVR#custom-path | ||
| 560 | + # @see https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DeliveryHLS#hls-config | ||
| 561 | + # default: [app]/[stream]-[seq].ts | ||
| 562 | + hls_ts_file [app]/[stream]-[seq].ts; | ||
| 542 | # the hls entry prefix, which is base url of ts url. | 563 | # the hls entry prefix, which is base url of ts url. |
| 543 | # if specified, the ts path in m3u8 will be like: | 564 | # if specified, the ts path in m3u8 will be like: |
| 544 | # http://your-server/live/livestream-0.ts | 565 | # http://your-server/live/livestream-0.ts |
| @@ -10,5 +10,7 @@ vhost __defaultVhost__ { | @@ -10,5 +10,7 @@ vhost __defaultVhost__ { | ||
| 10 | hls_fragment 10; | 10 | hls_fragment 10; |
| 11 | hls_window 60; | 11 | hls_window 60; |
| 12 | hls_path ./objs/nginx/html; | 12 | hls_path ./objs/nginx/html; |
| 13 | + hls_m3u8_file [app]/[stream].m3u8; | ||
| 14 | + hls_ts_file [app]/[stream]-[seq].ts; | ||
| 13 | } | 15 | } |
| 14 | } | 16 | } |
| @@ -12,8 +12,10 @@ http_server { | @@ -12,8 +12,10 @@ http_server { | ||
| 12 | vhost __defaultVhost__ { | 12 | vhost __defaultVhost__ { |
| 13 | hls { | 13 | hls { |
| 14 | enabled on; | 14 | enabled on; |
| 15 | - hls_path ./objs/nginx/html; | ||
| 16 | hls_fragment 10; | 15 | hls_fragment 10; |
| 17 | hls_window 60; | 16 | hls_window 60; |
| 17 | + hls_path ./objs/nginx/html; | ||
| 18 | + hls_m3u8_file [app]/[stream].m3u8; | ||
| 19 | + hls_ts_file [app]/[stream]-[seq].ts; | ||
| 18 | } | 20 | } |
| 19 | } | 21 | } |
| @@ -7,9 +7,11 @@ max_connections 1000; | @@ -7,9 +7,11 @@ max_connections 1000; | ||
| 7 | vhost __defaultVhost__ { | 7 | vhost __defaultVhost__ { |
| 8 | hls { | 8 | hls { |
| 9 | enabled on; | 9 | enabled on; |
| 10 | - hls_path ./objs/nginx/html; | ||
| 11 | hls_fragment 10; | 10 | hls_fragment 10; |
| 12 | hls_window 60; | 11 | hls_window 60; |
| 12 | + hls_path ./objs/nginx/html; | ||
| 13 | + hls_m3u8_file [app]/[stream].m3u8; | ||
| 14 | + hls_ts_file [app]/[stream]-[seq].ts; | ||
| 13 | } | 15 | } |
| 14 | transcode { | 16 | transcode { |
| 15 | enabled on; | 17 | enabled on; |
| @@ -1482,6 +1482,7 @@ int SrsConfig::check_config() | @@ -1482,6 +1482,7 @@ int SrsConfig::check_config() | ||
| 1482 | string m = conf->at(j)->name.c_str(); | 1482 | string m = conf->at(j)->name.c_str(); |
| 1483 | if (m != "enabled" && m != "hls_entry_prefix" && m != "hls_path" && m != "hls_fragment" && m != "hls_window" && m != "hls_on_error" | 1483 | if (m != "enabled" && m != "hls_entry_prefix" && m != "hls_path" && m != "hls_fragment" && m != "hls_window" && m != "hls_on_error" |
| 1484 | && m != "hls_storage" && m != "hls_mount" && m != "hls_td_ratio" && m != "hls_aof_ratio" && m != "hls_acodec" && m != "hls_vcodec" | 1484 | && m != "hls_storage" && m != "hls_mount" && m != "hls_td_ratio" && m != "hls_aof_ratio" && m != "hls_acodec" && m != "hls_vcodec" |
| 1485 | + && m != "hls_m3u8_file" && m != "hls_ts_file" | ||
| 1485 | ) { | 1486 | ) { |
| 1486 | ret = ERROR_SYSTEM_CONFIG_INVALID; | 1487 | ret = ERROR_SYSTEM_CONFIG_INVALID; |
| 1487 | srs_error("unsupported vhost hls directive %s, ret=%d", m.c_str(), ret); | 1488 | srs_error("unsupported vhost hls directive %s, ret=%d", m.c_str(), ret); |
| @@ -3168,7 +3169,41 @@ string SrsConfig::get_hls_path(string vhost) | @@ -3168,7 +3169,41 @@ string SrsConfig::get_hls_path(string vhost) | ||
| 3168 | if (!conf) { | 3169 | if (!conf) { |
| 3169 | return SRS_CONF_DEFAULT_HLS_PATH; | 3170 | return SRS_CONF_DEFAULT_HLS_PATH; |
| 3170 | } | 3171 | } |
| 3172 | + | ||
| 3173 | + return conf->arg0(); | ||
| 3174 | +} | ||
| 3175 | + | ||
| 3176 | +string SrsConfig::get_hls_m3u8_file(string vhost) | ||
| 3177 | +{ | ||
| 3178 | + SrsConfDirective* hls = get_hls(vhost); | ||
| 3179 | + | ||
| 3180 | + if (!hls) { | ||
| 3181 | + return SRS_CONF_DEFAULT_HLS_M3U8_FILE; | ||
| 3182 | + } | ||
| 3183 | + | ||
| 3184 | + SrsConfDirective* conf = hls->get("hls_m3u8_file"); | ||
| 3185 | + | ||
| 3186 | + if (!conf) { | ||
| 3187 | + return SRS_CONF_DEFAULT_HLS_M3U8_FILE; | ||
| 3188 | + } | ||
| 3189 | + | ||
| 3190 | + return conf->arg0(); | ||
| 3191 | +} | ||
| 3171 | 3192 | ||
| 3193 | +string SrsConfig::get_hls_ts_file(string vhost) | ||
| 3194 | +{ | ||
| 3195 | + SrsConfDirective* hls = get_hls(vhost); | ||
| 3196 | + | ||
| 3197 | + if (!hls) { | ||
| 3198 | + return SRS_CONF_DEFAULT_HLS_TS_FILE; | ||
| 3199 | + } | ||
| 3200 | + | ||
| 3201 | + SrsConfDirective* conf = hls->get("hls_ts_file"); | ||
| 3202 | + | ||
| 3203 | + if (!conf) { | ||
| 3204 | + return SRS_CONF_DEFAULT_HLS_TS_FILE; | ||
| 3205 | + } | ||
| 3206 | + | ||
| 3172 | return conf->arg0(); | 3207 | return conf->arg0(); |
| 3173 | } | 3208 | } |
| 3174 | 3209 |
| @@ -46,6 +46,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -46,6 +46,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 46 | 46 | ||
| 47 | #define SRS_CONF_DEFAULT_MAX_CONNECTIONS 1000 | 47 | #define SRS_CONF_DEFAULT_MAX_CONNECTIONS 1000 |
| 48 | #define SRS_CONF_DEFAULT_HLS_PATH "./objs/nginx/html" | 48 | #define SRS_CONF_DEFAULT_HLS_PATH "./objs/nginx/html" |
| 49 | +#define SRS_CONF_DEFAULT_HLS_M3U8_FILE "[app]/[stream].m3u8" | ||
| 50 | +#define SRS_CONF_DEFAULT_HLS_TS_FILE "[app]/[stream]-[seq].ts" | ||
| 49 | #define SRS_CONF_DEFAULT_HLS_FRAGMENT 10 | 51 | #define SRS_CONF_DEFAULT_HLS_FRAGMENT 10 |
| 50 | #define SRS_CONF_DEFAULT_HLS_TD_RATIO 1.5 | 52 | #define SRS_CONF_DEFAULT_HLS_TD_RATIO 1.5 |
| 51 | #define SRS_CONF_DEFAULT_HLS_AOF_RATIO 2.0 | 53 | #define SRS_CONF_DEFAULT_HLS_AOF_RATIO 2.0 |
| @@ -871,10 +873,18 @@ public: | @@ -871,10 +873,18 @@ public: | ||
| 871 | */ | 873 | */ |
| 872 | virtual std::string get_hls_entry_prefix(std::string vhost); | 874 | virtual std::string get_hls_entry_prefix(std::string vhost); |
| 873 | /** | 875 | /** |
| 874 | - * get the HLS ts/m3u8 file store path. | ||
| 875 | - */ | 876 | + * get the HLS ts/m3u8 file store path. |
| 877 | + */ | ||
| 876 | virtual std::string get_hls_path(std::string vhost); | 878 | virtual std::string get_hls_path(std::string vhost); |
| 877 | /** | 879 | /** |
| 880 | + * get the HLS m3u8 file path template. | ||
| 881 | + */ | ||
| 882 | + virtual std::string get_hls_m3u8_file(std::string vhost); | ||
| 883 | + /** | ||
| 884 | + * get the HLS ts file path template. | ||
| 885 | + */ | ||
| 886 | + virtual std::string get_hls_ts_file(std::string vhost); | ||
| 887 | + /** | ||
| 878 | * get the hls fragment time, in seconds. | 888 | * get the hls fragment time, in seconds. |
| 879 | */ | 889 | */ |
| 880 | virtual double get_hls_fragment(std::string vhost); | 890 | virtual double get_hls_fragment(std::string vhost); |
| @@ -27,7 +27,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -27,7 +27,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 27 | 27 | ||
| 28 | #include <fcntl.h> | 28 | #include <fcntl.h> |
| 29 | #include <sstream> | 29 | #include <sstream> |
| 30 | -#include <sys/time.h> | ||
| 31 | #include <algorithm> | 30 | #include <algorithm> |
| 32 | using namespace std; | 31 | using namespace std; |
| 33 | 32 | ||
| @@ -42,6 +41,7 @@ using namespace std; | @@ -42,6 +41,7 @@ using namespace std; | ||
| 42 | #include <srs_rtmp_amf0.hpp> | 41 | #include <srs_rtmp_amf0.hpp> |
| 43 | #include <srs_kernel_stream.hpp> | 42 | #include <srs_kernel_stream.hpp> |
| 44 | #include <srs_app_json.hpp> | 43 | #include <srs_app_json.hpp> |
| 44 | +#include <srs_app_utility.hpp> | ||
| 45 | 45 | ||
| 46 | // update the flv duration and filesize every this interval in ms. | 46 | // update the flv duration and filesize every this interval in ms. |
| 47 | #define SRS_DVR_UPDATE_DURATION_INTERVAL 60000 | 47 | #define SRS_DVR_UPDATE_DURATION_INTERVAL 60000 |
| @@ -422,76 +422,8 @@ string SrsFlvSegment::generate_path() | @@ -422,76 +422,8 @@ string SrsFlvSegment::generate_path() | ||
| 422 | 422 | ||
| 423 | // the flv file path | 423 | // the flv file path |
| 424 | std::string flv_path = path_config; | 424 | std::string flv_path = path_config; |
| 425 | - | ||
| 426 | - // variable [vhost] | ||
| 427 | - flv_path = srs_string_replace(flv_path, "[vhost]", req->vhost); | ||
| 428 | - // variable [app] | ||
| 429 | - flv_path = srs_string_replace(flv_path, "[app]", req->app); | ||
| 430 | - // variable [stream] | ||
| 431 | - flv_path = srs_string_replace(flv_path, "[stream]", req->stream); | ||
| 432 | - | ||
| 433 | - // date and time substitude | ||
| 434 | - // clock time | ||
| 435 | - timeval tv; | ||
| 436 | - if (gettimeofday(&tv, NULL) == -1) { | ||
| 437 | - return flv_path; | ||
| 438 | - } | ||
| 439 | - | ||
| 440 | - // to calendar time | ||
| 441 | - struct tm* tm; | ||
| 442 | - if ((tm = localtime(&tv.tv_sec)) == NULL) { | ||
| 443 | - return flv_path; | ||
| 444 | - } | ||
| 445 | - | ||
| 446 | - // the buffer to format the date and time. | ||
| 447 | - char buf[64]; | ||
| 448 | - | ||
| 449 | - // [2006], replace with current year. | ||
| 450 | - if (true) { | ||
| 451 | - snprintf(buf, sizeof(buf), "%d", 1900 + tm->tm_year); | ||
| 452 | - flv_path = srs_string_replace(flv_path, "[2006]", buf); | ||
| 453 | - } | ||
| 454 | - // [2006], replace with current year. | ||
| 455 | - if (true) { | ||
| 456 | - snprintf(buf, sizeof(buf), "%d", 1900 + tm->tm_year); | ||
| 457 | - flv_path = srs_string_replace(flv_path, "[2006]", buf); | ||
| 458 | - } | ||
| 459 | - // [01], replace this const to current month. | ||
| 460 | - if (true) { | ||
| 461 | - snprintf(buf, sizeof(buf), "%d", 1 + tm->tm_mon); | ||
| 462 | - flv_path = srs_string_replace(flv_path, "[01]", buf); | ||
| 463 | - } | ||
| 464 | - // [02], replace this const to current date. | ||
| 465 | - if (true) { | ||
| 466 | - snprintf(buf, sizeof(buf), "%d", tm->tm_mday); | ||
| 467 | - flv_path = srs_string_replace(flv_path, "[02]", buf); | ||
| 468 | - } | ||
| 469 | - // [15], replace this const to current hour. | ||
| 470 | - if (true) { | ||
| 471 | - snprintf(buf, sizeof(buf), "%d", tm->tm_hour); | ||
| 472 | - flv_path = srs_string_replace(flv_path, "[15]", buf); | ||
| 473 | - } | ||
| 474 | - // [04], repleace this const to current minute. | ||
| 475 | - if (true) { | ||
| 476 | - snprintf(buf, sizeof(buf), "%d", tm->tm_min); | ||
| 477 | - flv_path = srs_string_replace(flv_path, "[04]", buf); | ||
| 478 | - } | ||
| 479 | - // [05], repleace this const to current second. | ||
| 480 | - if (true) { | ||
| 481 | - snprintf(buf, sizeof(buf), "%d", tm->tm_sec); | ||
| 482 | - flv_path = srs_string_replace(flv_path, "[05]", buf); | ||
| 483 | - } | ||
| 484 | - // [999], repleace this const to current millisecond. | ||
| 485 | - if (true) { | ||
| 486 | - snprintf(buf, sizeof(buf), "%03d", (int)(tv.tv_usec / 1000)); | ||
| 487 | - flv_path = srs_string_replace(flv_path, "[999]", buf); | ||
| 488 | - } | ||
| 489 | - // [timestamp],replace this const to current UNIX timestamp in ms. | ||
| 490 | - if (true) { | ||
| 491 | - int64_t now_us = ((int64_t)tv.tv_sec) * 1000 * 1000 + (int64_t)tv.tv_usec; | ||
| 492 | - snprintf(buf, sizeof(buf), "%"PRId64, now_us / 1000); | ||
| 493 | - flv_path = srs_string_replace(flv_path, "[timestamp]", buf); | ||
| 494 | - } | 425 | + flv_path = srs_path_build_stream(flv_path, req->vhost, req->app, req->stream); |
| 426 | + flv_path = srs_path_build_timestamp(flv_path); | ||
| 495 | 427 | ||
| 496 | return flv_path; | 428 | return flv_path; |
| 497 | } | 429 | } |
| @@ -53,6 +53,7 @@ using namespace std; | @@ -53,6 +53,7 @@ using namespace std; | ||
| 53 | #include <srs_kernel_file.hpp> | 53 | #include <srs_kernel_file.hpp> |
| 54 | #include <srs_rtmp_buffer.hpp> | 54 | #include <srs_rtmp_buffer.hpp> |
| 55 | #include <srs_kernel_ts.hpp> | 55 | #include <srs_kernel_ts.hpp> |
| 56 | +#include <srs_app_utility.hpp> | ||
| 56 | 57 | ||
| 57 | // drop the segment when duration of ts too small. | 58 | // drop the segment when duration of ts too small. |
| 58 | #define SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS 100 | 59 | #define SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS 100 |
| @@ -204,8 +205,9 @@ int SrsHlsMuxer::sequence_no() | @@ -204,8 +205,9 @@ int SrsHlsMuxer::sequence_no() | ||
| 204 | return _sequence_no; | 205 | return _sequence_no; |
| 205 | } | 206 | } |
| 206 | 207 | ||
| 207 | -int SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix, string path, int fragment, int window, double aof_ratio) | ||
| 208 | -{ | 208 | +int SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix, |
| 209 | + string path, string m3u8_file, string ts_file, int fragment, int window, double aof_ratio | ||
| 210 | +) { | ||
| 209 | int ret = ERROR_SUCCESS; | 211 | int ret = ERROR_SUCCESS; |
| 210 | 212 | ||
| 211 | srs_freep(req); | 213 | srs_freep(req); |
| @@ -213,6 +215,8 @@ int SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix, string path, | @@ -213,6 +215,8 @@ int SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix, string path, | ||
| 213 | 215 | ||
| 214 | hls_entry_prefix = entry_prefix; | 216 | hls_entry_prefix = entry_prefix; |
| 215 | hls_path = path; | 217 | hls_path = path; |
| 218 | + hls_m3u8_file = m3u8_file; | ||
| 219 | + hls_ts_file = ts_file; | ||
| 216 | hls_fragment = fragment; | 220 | hls_fragment = fragment; |
| 217 | hls_aof_ratio = aof_ratio; | 221 | hls_aof_ratio = aof_ratio; |
| 218 | hls_window = window; | 222 | hls_window = window; |
| @@ -249,7 +253,7 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts) | @@ -249,7 +253,7 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts) | ||
| 249 | 253 | ||
| 250 | // TODO: create all parents dirs. | 254 | // TODO: create all parents dirs. |
| 251 | // create dir for app. | 255 | // create dir for app. |
| 252 | - if (should_write_file && (ret = create_dir()) != ERROR_SUCCESS) { | 256 | + if (should_write_file && (ret = create_dir(current->full_path)) != ERROR_SUCCESS) { |
| 253 | return ret; | 257 | return ret; |
| 254 | } | 258 | } |
| 255 | 259 | ||
| @@ -292,22 +296,23 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts) | @@ -292,22 +296,23 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts) | ||
| 292 | current->segment_start_dts = segment_start_dts; | 296 | current->segment_start_dts = segment_start_dts; |
| 293 | 297 | ||
| 294 | // generate filename. | 298 | // generate filename. |
| 295 | - char filename[128]; | ||
| 296 | - snprintf(filename, sizeof(filename), | ||
| 297 | - "%s-%d.ts", req->stream.c_str(), current->sequence_no); | 299 | + std::string ts_file = hls_ts_file; |
| 300 | + ts_file = srs_path_build_stream(ts_file, req->vhost, req->app, req->stream); | ||
| 301 | + ts_file = srs_path_build_timestamp(ts_file); | ||
| 302 | + if (true) { | ||
| 303 | + std::stringstream ss; | ||
| 304 | + ss << current->sequence_no; | ||
| 305 | + ts_file = srs_string_replace(ts_file, "[seq]", ss.str()); | ||
| 306 | + } | ||
| 298 | 307 | ||
| 299 | - // TODO: use temp file and rename it. | ||
| 300 | - current->full_path = hls_path; | ||
| 301 | - current->full_path += "/"; | ||
| 302 | - current->full_path += req->app; | ||
| 303 | - current->full_path += "/"; | ||
| 304 | - current->full_path += filename; | 308 | + // replace variables |
| 309 | + current->full_path = hls_path + "/" + ts_file; | ||
| 305 | 310 | ||
| 306 | current->uri += hls_entry_prefix; | 311 | current->uri += hls_entry_prefix; |
| 307 | if (!hls_entry_prefix.empty() && !srs_string_ends_with(hls_entry_prefix, "/")) { | 312 | if (!hls_entry_prefix.empty() && !srs_string_ends_with(hls_entry_prefix, "/")) { |
| 308 | current->uri += "/"; | 313 | current->uri += "/"; |
| 309 | } | 314 | } |
| 310 | - current->uri += filename; | 315 | + current->uri += ts_file; |
| 311 | 316 | ||
| 312 | std::string tmp_file = current->full_path + ".tmp"; | 317 | std::string tmp_file = current->full_path + ".tmp"; |
| 313 | if ((ret = current->muxer->open(tmp_file.c_str())) != ERROR_SUCCESS) { | 318 | if ((ret = current->muxer->open(tmp_file.c_str())) != ERROR_SUCCESS) { |
| @@ -524,10 +529,8 @@ int SrsHlsMuxer::refresh_m3u8() | @@ -524,10 +529,8 @@ int SrsHlsMuxer::refresh_m3u8() | ||
| 524 | 529 | ||
| 525 | std::string m3u8_file = hls_path; | 530 | std::string m3u8_file = hls_path; |
| 526 | m3u8_file += "/"; | 531 | m3u8_file += "/"; |
| 527 | - m3u8_file += req->app; | ||
| 528 | - m3u8_file += "/"; | ||
| 529 | - m3u8_file += req->stream; | ||
| 530 | - m3u8_file += ".m3u8"; | 532 | + m3u8_file += hls_m3u8_file; |
| 533 | + m3u8_file = srs_path_build_stream(m3u8_file, req->vhost, req->app, req->stream); | ||
| 531 | 534 | ||
| 532 | m3u8 = m3u8_file; | 535 | m3u8 = m3u8_file; |
| 533 | m3u8_file += ".temp"; | 536 | m3u8_file += ".temp"; |
| @@ -631,7 +634,7 @@ int SrsHlsMuxer::_refresh_m3u8(string m3u8_file) | @@ -631,7 +634,7 @@ int SrsHlsMuxer::_refresh_m3u8(string m3u8_file) | ||
| 631 | return ret; | 634 | return ret; |
| 632 | } | 635 | } |
| 633 | 636 | ||
| 634 | -int SrsHlsMuxer::create_dir() | 637 | +int SrsHlsMuxer::create_dir(string filepath) |
| 635 | { | 638 | { |
| 636 | int ret = ERROR_SUCCESS; | 639 | int ret = ERROR_SUCCESS; |
| 637 | 640 | ||
| @@ -639,9 +642,11 @@ int SrsHlsMuxer::create_dir() | @@ -639,9 +642,11 @@ int SrsHlsMuxer::create_dir() | ||
| 639 | return ret; | 642 | return ret; |
| 640 | } | 643 | } |
| 641 | 644 | ||
| 642 | - std::string app_dir = hls_path; | ||
| 643 | - app_dir += "/"; | ||
| 644 | - app_dir += req->app; | 645 | + std::string app_dir = filepath; |
| 646 | + size_t pos = string::npos; | ||
| 647 | + if ((pos = app_dir.rfind("/")) != string::npos) { | ||
| 648 | + app_dir = app_dir.substr(0, pos); | ||
| 649 | + } | ||
| 645 | 650 | ||
| 646 | // TODO: cleanup the dir when startup. | 651 | // TODO: cleanup the dir when startup. |
| 647 | 652 | ||
| @@ -678,7 +683,9 @@ int SrsHlsCache::on_publish(SrsHlsMuxer* muxer, SrsRequest* req, int64_t segment | @@ -678,7 +683,9 @@ int SrsHlsCache::on_publish(SrsHlsMuxer* muxer, SrsRequest* req, int64_t segment | ||
| 678 | // get the hls m3u8 ts list entry prefix config | 683 | // get the hls m3u8 ts list entry prefix config |
| 679 | std::string entry_prefix = _srs_config->get_hls_entry_prefix(vhost); | 684 | std::string entry_prefix = _srs_config->get_hls_entry_prefix(vhost); |
| 680 | // get the hls path config | 685 | // get the hls path config |
| 681 | - std::string hls_path = _srs_config->get_hls_path(vhost); | 686 | + std::string path = _srs_config->get_hls_path(vhost); |
| 687 | + std::string m3u8_file = _srs_config->get_hls_m3u8_file(vhost); | ||
| 688 | + std::string ts_file = _srs_config->get_hls_ts_file(vhost); | ||
| 682 | // the audio overflow, for pure audio to reap segment. | 689 | // the audio overflow, for pure audio to reap segment. |
| 683 | double hls_aof_ratio = _srs_config->get_hls_aof_ratio(vhost); | 690 | double hls_aof_ratio = _srs_config->get_hls_aof_ratio(vhost); |
| 684 | 691 | ||
| @@ -686,7 +693,7 @@ int SrsHlsCache::on_publish(SrsHlsMuxer* muxer, SrsRequest* req, int64_t segment | @@ -686,7 +693,7 @@ int SrsHlsCache::on_publish(SrsHlsMuxer* muxer, SrsRequest* req, int64_t segment | ||
| 686 | // for the HLS donot requires the EXT-X-MEDIA-SEQUENCE be monotonically increase. | 693 | // for the HLS donot requires the EXT-X-MEDIA-SEQUENCE be monotonically increase. |
| 687 | 694 | ||
| 688 | // open muxer | 695 | // open muxer |
| 689 | - if ((ret = muxer->update_config(req, entry_prefix, hls_path, hls_fragment, hls_window, hls_aof_ratio)) != ERROR_SUCCESS) { | 696 | + if ((ret = muxer->update_config(req, entry_prefix, path, m3u8_file, ts_file, hls_fragment, hls_window, hls_aof_ratio)) != ERROR_SUCCESS) { |
| 690 | srs_error("m3u8 muxer update config failed. ret=%d", ret); | 697 | srs_error("m3u8 muxer update config failed. ret=%d", ret); |
| 691 | return ret; | 698 | return ret; |
| 692 | } | 699 | } |
| @@ -169,6 +169,8 @@ private: | @@ -169,6 +169,8 @@ private: | ||
| 169 | private: | 169 | private: |
| 170 | std::string hls_entry_prefix; | 170 | std::string hls_entry_prefix; |
| 171 | std::string hls_path; | 171 | std::string hls_path; |
| 172 | + std::string hls_m3u8_file; | ||
| 173 | + std::string hls_ts_file; | ||
| 172 | double hls_aof_ratio; | 174 | double hls_aof_ratio; |
| 173 | int hls_fragment; | 175 | int hls_fragment; |
| 174 | int hls_window; | 176 | int hls_window; |
| @@ -209,7 +211,9 @@ public: | @@ -209,7 +211,9 @@ public: | ||
| 209 | /** | 211 | /** |
| 210 | * when publish, update the config for muxer. | 212 | * when publish, update the config for muxer. |
| 211 | */ | 213 | */ |
| 212 | - virtual int update_config(SrsRequest* r, std::string entry_prefix, std::string path, int fragment, int window, double aof_ratio); | 214 | + virtual int update_config(SrsRequest* r, std::string entry_prefix, |
| 215 | + std::string path, std::string m3u8_file, std::string ts_file, | ||
| 216 | + int fragment, int window, double aof_ratio); | ||
| 213 | /** | 217 | /** |
| 214 | * open a new segment(a new ts file), | 218 | * open a new segment(a new ts file), |
| 215 | * @param segment_start_dts use to calc the segment duration, | 219 | * @param segment_start_dts use to calc the segment duration, |
| @@ -240,7 +244,7 @@ public: | @@ -240,7 +244,7 @@ public: | ||
| 240 | private: | 244 | private: |
| 241 | virtual int refresh_m3u8(); | 245 | virtual int refresh_m3u8(); |
| 242 | virtual int _refresh_m3u8(std::string m3u8_file); | 246 | virtual int _refresh_m3u8(std::string m3u8_file); |
| 243 | - virtual int create_dir(); | 247 | + virtual int create_dir(std::string filepath); |
| 244 | }; | 248 | }; |
| 245 | 249 | ||
| 246 | /** | 250 | /** |
| @@ -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 <sys/sysctl.h> | 32 | #include <sys/sysctl.h> |
| 33 | #endif | 33 | #endif |
| 34 | #include <stdlib.h> | 34 | #include <stdlib.h> |
| 35 | +#include <sys/time.h> | ||
| 35 | using namespace std; | 36 | using namespace std; |
| 36 | 37 | ||
| 37 | #include <srs_kernel_log.hpp> | 38 | #include <srs_kernel_log.hpp> |
| @@ -111,6 +112,91 @@ int srs_get_log_level(string level) | @@ -111,6 +112,91 @@ int srs_get_log_level(string level) | ||
| 111 | } | 112 | } |
| 112 | } | 113 | } |
| 113 | 114 | ||
| 115 | +string srs_path_build_stream(string template_path, string vhost, string app, string stream) | ||
| 116 | +{ | ||
| 117 | + std::string path = template_path; | ||
| 118 | + | ||
| 119 | + // variable [vhost] | ||
| 120 | + path = srs_string_replace(path, "[vhost]", vhost); | ||
| 121 | + // variable [app] | ||
| 122 | + path = srs_string_replace(path, "[app]", app); | ||
| 123 | + // variable [stream] | ||
| 124 | + path = srs_string_replace(path, "[stream]", stream); | ||
| 125 | + | ||
| 126 | + return path; | ||
| 127 | +} | ||
| 128 | + | ||
| 129 | +string srs_path_build_timestamp(string template_path) | ||
| 130 | +{ | ||
| 131 | + std::string path = template_path; | ||
| 132 | + | ||
| 133 | + | ||
| 134 | + // date and time substitude | ||
| 135 | + // clock time | ||
| 136 | + timeval tv; | ||
| 137 | + if (gettimeofday(&tv, NULL) == -1) { | ||
| 138 | + return path; | ||
| 139 | + } | ||
| 140 | + | ||
| 141 | + // to calendar time | ||
| 142 | + struct tm* tm; | ||
| 143 | + if ((tm = localtime(&tv.tv_sec)) == NULL) { | ||
| 144 | + return path; | ||
| 145 | + } | ||
| 146 | + | ||
| 147 | + // the buffer to format the date and time. | ||
| 148 | + char buf[64]; | ||
| 149 | + | ||
| 150 | + // [2006], replace with current year. | ||
| 151 | + if (true) { | ||
| 152 | + snprintf(buf, sizeof(buf), "%d", 1900 + tm->tm_year); | ||
| 153 | + path = srs_string_replace(path, "[2006]", buf); | ||
| 154 | + } | ||
| 155 | + // [2006], replace with current year. | ||
| 156 | + if (true) { | ||
| 157 | + snprintf(buf, sizeof(buf), "%d", 1900 + tm->tm_year); | ||
| 158 | + path = srs_string_replace(path, "[2006]", buf); | ||
| 159 | + } | ||
| 160 | + // [01], replace this const to current month. | ||
| 161 | + if (true) { | ||
| 162 | + snprintf(buf, sizeof(buf), "%d", 1 + tm->tm_mon); | ||
| 163 | + path = srs_string_replace(path, "[01]", buf); | ||
| 164 | + } | ||
| 165 | + // [02], replace this const to current date. | ||
| 166 | + if (true) { | ||
| 167 | + snprintf(buf, sizeof(buf), "%d", tm->tm_mday); | ||
| 168 | + path = srs_string_replace(path, "[02]", buf); | ||
| 169 | + } | ||
| 170 | + // [15], replace this const to current hour. | ||
| 171 | + if (true) { | ||
| 172 | + snprintf(buf, sizeof(buf), "%d", tm->tm_hour); | ||
| 173 | + path = srs_string_replace(path, "[15]", buf); | ||
| 174 | + } | ||
| 175 | + // [04], repleace this const to current minute. | ||
| 176 | + if (true) { | ||
| 177 | + snprintf(buf, sizeof(buf), "%d", tm->tm_min); | ||
| 178 | + path = srs_string_replace(path, "[04]", buf); | ||
| 179 | + } | ||
| 180 | + // [05], repleace this const to current second. | ||
| 181 | + if (true) { | ||
| 182 | + snprintf(buf, sizeof(buf), "%d", tm->tm_sec); | ||
| 183 | + path = srs_string_replace(path, "[05]", buf); | ||
| 184 | + } | ||
| 185 | + // [999], repleace this const to current millisecond. | ||
| 186 | + if (true) { | ||
| 187 | + snprintf(buf, sizeof(buf), "%03d", (int)(tv.tv_usec / 1000)); | ||
| 188 | + path = srs_string_replace(path, "[999]", buf); | ||
| 189 | + } | ||
| 190 | + // [timestamp],replace this const to current UNIX timestamp in ms. | ||
| 191 | + if (true) { | ||
| 192 | + int64_t now_us = ((int64_t)tv.tv_sec) * 1000 * 1000 + (int64_t)tv.tv_usec; | ||
| 193 | + snprintf(buf, sizeof(buf), "%"PRId64, now_us / 1000); | ||
| 194 | + path = srs_string_replace(path, "[timestamp]", buf); | ||
| 195 | + } | ||
| 196 | + | ||
| 197 | + return path; | ||
| 198 | +} | ||
| 199 | + | ||
| 114 | void srs_parse_endpoint(string ip_port, string& ip, string& port) | 200 | void srs_parse_endpoint(string ip_port, string& ip, string& port) |
| 115 | { | 201 | { |
| 116 | ip = "0.0.0.0"; | 202 | ip = "0.0.0.0"; |
| @@ -51,6 +51,29 @@ extern int srs_socket_connect(std::string server, int port, int64_t timeout, st_ | @@ -51,6 +51,29 @@ extern int srs_socket_connect(std::string server, int port, int64_t timeout, st_ | ||
| 51 | extern int srs_get_log_level(std::string level); | 51 | extern int srs_get_log_level(std::string level); |
| 52 | 52 | ||
| 53 | /** | 53 | /** |
| 54 | +* build the path according to vhost/app/stream, where replace variables: | ||
| 55 | +* [vhost], the vhost of stream. | ||
| 56 | +* [app], the app of stream. | ||
| 57 | +* [stream], the stream name of stream. | ||
| 58 | +* @return the replaced path. | ||
| 59 | +*/ | ||
| 60 | +extern std::string srs_path_build_stream(std::string template_path, std::string vhost, std::string app, std::string stream); | ||
| 61 | + | ||
| 62 | +/** | ||
| 63 | +* build the path according to timestamp, where replace variables: | ||
| 64 | +* [2006], replace this const to current year. | ||
| 65 | +* [01], replace this const to current month. | ||
| 66 | +* [02], replace this const to current date. | ||
| 67 | +* [15], replace this const to current hour. | ||
| 68 | +* [04], repleace this const to current minute. | ||
| 69 | +* [05], repleace this const to current second. | ||
| 70 | +* [999], repleace this const to current millisecond. | ||
| 71 | +* [timestamp],replace this const to current UNIX timestamp in ms. | ||
| 72 | +* @return the replaced path. | ||
| 73 | +*/ | ||
| 74 | +extern std::string srs_path_build_timestamp(std::string template_path); | ||
| 75 | + | ||
| 76 | +/** | ||
| 54 | * parse the endpoint to ip and port. | 77 | * parse the endpoint to ip and port. |
| 55 | * @param ip_port the ip and port which formats in <[ip:]port> | 78 | * @param ip_port the ip and port which formats in <[ip:]port> |
| 56 | */ | 79 | */ |
| @@ -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 148 | 34 | +#define VERSION_REVISION 149 |
| 35 | 35 | ||
| 36 | // server info. | 36 | // server info. |
| 37 | #define RTMP_SIG_SRS_KEY "SRS" | 37 | #define RTMP_SIG_SRS_KEY "SRS" |
-
请 注册 或 登录 后发表评论