正在显示
6 个修改的文件
包含
517 行增加
和
337 行删除
| @@ -281,6 +281,12 @@ vhost dvr.srs.com { | @@ -281,6 +281,12 @@ vhost dvr.srs.com { | ||
| 281 | # whether enabled dvr features | 281 | # whether enabled dvr features |
| 282 | # default: off | 282 | # default: off |
| 283 | enabled on; | 283 | enabled on; |
| 284 | + # the dvr plan. canbe: | ||
| 285 | + # session reap flv when session end(unpublish). | ||
| 286 | + # segment reap flv when flv duration exceed the specified dvr_duration. | ||
| 287 | + # api reap flv when api required. | ||
| 288 | + # default: session | ||
| 289 | + dvr_plan session; | ||
| 284 | # the dvr output path. | 290 | # the dvr output path. |
| 285 | # we supports some variables to generate the filename. | 291 | # we supports some variables to generate the filename. |
| 286 | # [vhost], the vhost of stream. | 292 | # [vhost], the vhost of stream. |
| @@ -314,22 +320,28 @@ vhost dvr.srs.com { | @@ -314,22 +320,28 @@ vhost dvr.srs.com { | ||
| 314 | # dvr_path /data/ossrs.net/live/2015/01/livestream-03-10.57.30.776.flv; | 320 | # dvr_path /data/ossrs.net/live/2015/01/livestream-03-10.57.30.776.flv; |
| 315 | # @see https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DVR#custom-path | 321 | # @see https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DVR#custom-path |
| 316 | # @see https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DVR#custom-path | 322 | # @see https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DVR#custom-path |
| 323 | + # segment,session apply it. | ||
| 324 | + # api apply before api specified the path. | ||
| 317 | # default: ./objs/nginx/html | 325 | # default: ./objs/nginx/html |
| 318 | dvr_path ./objs/nginx/html; | 326 | dvr_path ./objs/nginx/html; |
| 319 | - # the dvr plan. canbe: | ||
| 320 | - # session reap flv when session end(unpublish). | ||
| 321 | - # segment reap flv when flv duration exceed the specified dvr_duration. | ||
| 322 | - # default: session | ||
| 323 | - dvr_plan session; | ||
| 324 | - # the param for plan(segment), in seconds. | 327 | + # the duration for dvr file, reap if exeed, in seconds. |
| 328 | + # segment apply it. | ||
| 329 | + # session,api ignore. | ||
| 325 | # default: 30 | 330 | # default: 30 |
| 326 | dvr_duration 30; | 331 | dvr_duration 30; |
| 327 | - # the param for plan(segment), | ||
| 328 | # whether wait keyframe to reap segment, | 332 | # whether wait keyframe to reap segment, |
| 329 | # if off, reap segment when duration exceed the dvr_duration, | 333 | # if off, reap segment when duration exceed the dvr_duration, |
| 330 | # if on, reap segment when duration exceed and got keyframe. | 334 | # if on, reap segment when duration exceed and got keyframe. |
| 335 | + # segment apply it. | ||
| 336 | + # session,api ignore. | ||
| 331 | # default: on | 337 | # default: on |
| 332 | dvr_wait_keyframe on; | 338 | dvr_wait_keyframe on; |
| 339 | + # whether dvr auto start when publish. | ||
| 340 | + # if off, dvr wait for api to start it. | ||
| 341 | + # api apply it. | ||
| 342 | + # segment,session ignore. | ||
| 343 | + # default: on | ||
| 344 | + dvr_autostart on; | ||
| 333 | # about the stream monotonically increasing: | 345 | # about the stream monotonically increasing: |
| 334 | # 1. video timestamp is monotonically increasing, | 346 | # 1. video timestamp is monotonically increasing, |
| 335 | # 2. audio timestamp is monotonically increasing, | 347 | # 2. audio timestamp is monotonically increasing, |
| @@ -340,10 +352,11 @@ vhost dvr.srs.com { | @@ -340,10 +352,11 @@ vhost dvr.srs.com { | ||
| 340 | # 1. full, to ensure stream start at zero, and ensure stream monotonically increasing. | 352 | # 1. full, to ensure stream start at zero, and ensure stream monotonically increasing. |
| 341 | # 2. zero, only ensure sttream start at zero, ignore timestamp jitter. | 353 | # 2. zero, only ensure sttream start at zero, ignore timestamp jitter. |
| 342 | # 3. off, disable the time jitter algorithm, like atc. | 354 | # 3. off, disable the time jitter algorithm, like atc. |
| 355 | + # apply for all dvr plan. | ||
| 343 | # default: full | 356 | # default: full |
| 344 | time_jitter full; | 357 | time_jitter full; |
| 345 | 358 | ||
| 346 | - # on_dvr | 359 | + # on_dvr, never config in here, should config in http_hooks. |
| 347 | # for the dvr http callback, @see http_hooks.on_dvr of vhost hooks.callback.srs.com | 360 | # for the dvr http callback, @see http_hooks.on_dvr of vhost hooks.callback.srs.com |
| 348 | # @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DVR#http-callback | 361 | # @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DVR#http-callback |
| 349 | # @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DVR#http-callback | 362 | # @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DVR#http-callback |
| @@ -1418,6 +1418,7 @@ int SrsConfig::check_config() | @@ -1418,6 +1418,7 @@ int SrsConfig::check_config() | ||
| 1418 | string m = conf->at(j)->name.c_str(); | 1418 | string m = conf->at(j)->name.c_str(); |
| 1419 | if (m != "enabled" && m != "dvr_path" && m != "dvr_plan" | 1419 | if (m != "enabled" && m != "dvr_path" && m != "dvr_plan" |
| 1420 | && m != "dvr_duration" && m != "dvr_wait_keyframe" && m != "time_jitter" | 1420 | && m != "dvr_duration" && m != "dvr_wait_keyframe" && m != "time_jitter" |
| 1421 | + && m != "dvr_autostart" | ||
| 1421 | ) { | 1422 | ) { |
| 1422 | ret = ERROR_SYSTEM_CONFIG_INVALID; | 1423 | ret = ERROR_SYSTEM_CONFIG_INVALID; |
| 1423 | srs_error("unsupported vhost dvr directive %s, ret=%d", m.c_str(), ret); | 1424 | srs_error("unsupported vhost dvr directive %s, ret=%d", m.c_str(), ret); |
| @@ -3377,6 +3378,23 @@ bool SrsConfig::get_dvr_wait_keyframe(string vhost) | @@ -3377,6 +3378,23 @@ bool SrsConfig::get_dvr_wait_keyframe(string vhost) | ||
| 3377 | return false; | 3378 | return false; |
| 3378 | } | 3379 | } |
| 3379 | 3380 | ||
| 3381 | +bool SrsConfig::get_dvr_autostart(string vhost) | ||
| 3382 | +{ | ||
| 3383 | + SrsConfDirective* dvr = get_dvr(vhost); | ||
| 3384 | + | ||
| 3385 | + if (!dvr) { | ||
| 3386 | + return true; | ||
| 3387 | + } | ||
| 3388 | + | ||
| 3389 | + SrsConfDirective* conf = dvr->get("dvr_autostart"); | ||
| 3390 | + | ||
| 3391 | + if (!conf || conf->arg0() != "off") { | ||
| 3392 | + return true; | ||
| 3393 | + } | ||
| 3394 | + | ||
| 3395 | + return false; | ||
| 3396 | +} | ||
| 3397 | + | ||
| 3380 | int SrsConfig::get_dvr_time_jitter(string vhost) | 3398 | int SrsConfig::get_dvr_time_jitter(string vhost) |
| 3381 | { | 3399 | { |
| 3382 | SrsConfDirective* dvr = get_dvr(vhost); | 3400 | SrsConfDirective* dvr = get_dvr(vhost); |
| @@ -60,6 +60,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -60,6 +60,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 60 | #define SRS_CONF_DEFAULT_DVR_PATH "./objs/nginx/html" | 60 | #define SRS_CONF_DEFAULT_DVR_PATH "./objs/nginx/html" |
| 61 | #define SRS_CONF_DEFAULT_DVR_PLAN_SESSION "session" | 61 | #define SRS_CONF_DEFAULT_DVR_PLAN_SESSION "session" |
| 62 | #define SRS_CONF_DEFAULT_DVR_PLAN_SEGMENT "segment" | 62 | #define SRS_CONF_DEFAULT_DVR_PLAN_SEGMENT "segment" |
| 63 | +#define SRS_CONF_DEFAULT_DVR_PLAN_API "api" | ||
| 63 | #define SRS_CONF_DEFAULT_DVR_PLAN SRS_CONF_DEFAULT_DVR_PLAN_SESSION | 64 | #define SRS_CONF_DEFAULT_DVR_PLAN SRS_CONF_DEFAULT_DVR_PLAN_SESSION |
| 64 | #define SRS_CONF_DEFAULT_DVR_DURATION 30 | 65 | #define SRS_CONF_DEFAULT_DVR_DURATION 30 |
| 65 | #define SRS_CONF_DEFAULT_TIME_JITTER "full" | 66 | #define SRS_CONF_DEFAULT_TIME_JITTER "full" |
| @@ -921,14 +922,18 @@ public: | @@ -921,14 +922,18 @@ public: | ||
| 921 | */ | 922 | */ |
| 922 | virtual std::string get_dvr_plan(std::string vhost); | 923 | virtual std::string get_dvr_plan(std::string vhost); |
| 923 | /** | 924 | /** |
| 924 | - * get the duration of dvr flv, for segment plan. | 925 | + * get the duration of dvr flv. |
| 925 | */ | 926 | */ |
| 926 | virtual int get_dvr_duration(std::string vhost); | 927 | virtual int get_dvr_duration(std::string vhost); |
| 927 | /** | 928 | /** |
| 928 | - * whether wait keyframe to reap segment, for segment plan. | 929 | + * whether wait keyframe to reap segment. |
| 929 | */ | 930 | */ |
| 930 | virtual bool get_dvr_wait_keyframe(std::string vhost); | 931 | virtual bool get_dvr_wait_keyframe(std::string vhost); |
| 931 | /** | 932 | /** |
| 933 | + * whether autostart for dvr. wait api to start dvr if false. | ||
| 934 | + */ | ||
| 935 | + virtual bool get_dvr_autostart(std::string vhost); | ||
| 936 | + /** | ||
| 932 | * get the time_jitter algorithm for dvr. | 937 | * get the time_jitter algorithm for dvr. |
| 933 | */ | 938 | */ |
| 934 | virtual int get_dvr_time_jitter(std::string vhost); | 939 | virtual int get_dvr_time_jitter(std::string vhost); |
| @@ -39,8 +39,17 @@ using namespace std; | @@ -39,8 +39,17 @@ using namespace std; | ||
| 39 | #include <srs_kernel_flv.hpp> | 39 | #include <srs_kernel_flv.hpp> |
| 40 | #include <srs_kernel_file.hpp> | 40 | #include <srs_kernel_file.hpp> |
| 41 | 41 | ||
| 42 | -SrsFlvSegment::SrsFlvSegment() | 42 | +SrsFlvSegment::SrsFlvSegment(SrsDvrPlan* p) |
| 43 | { | 43 | { |
| 44 | + req = NULL; | ||
| 45 | + source = NULL; | ||
| 46 | + jitter = NULL; | ||
| 47 | + plan = p; | ||
| 48 | + | ||
| 49 | + fs = new SrsFileWriter(); | ||
| 50 | + enc = new SrsFlvEncoder(); | ||
| 51 | + jitter_algorithm = SrsRtmpJitterAlgorithmOFF; | ||
| 52 | + | ||
| 44 | path = ""; | 53 | path = ""; |
| 45 | has_keyframe = false; | 54 | has_keyframe = false; |
| 46 | duration = 0; | 55 | duration = 0; |
| @@ -48,95 +57,238 @@ SrsFlvSegment::SrsFlvSegment() | @@ -48,95 +57,238 @@ SrsFlvSegment::SrsFlvSegment() | ||
| 48 | stream_starttime = 0; | 57 | stream_starttime = 0; |
| 49 | stream_previous_pkt_time = -1; | 58 | stream_previous_pkt_time = -1; |
| 50 | stream_duration = 0; | 59 | stream_duration = 0; |
| 60 | + | ||
| 61 | + _srs_config->subscribe(this); | ||
| 51 | } | 62 | } |
| 52 | 63 | ||
| 53 | SrsFlvSegment::~SrsFlvSegment() | 64 | SrsFlvSegment::~SrsFlvSegment() |
| 54 | { | 65 | { |
| 66 | + _srs_config->unsubscribe(this); | ||
| 67 | + | ||
| 68 | + srs_freep(jitter); | ||
| 69 | + srs_freep(fs); | ||
| 70 | + srs_freep(enc); | ||
| 55 | } | 71 | } |
| 56 | 72 | ||
| 57 | -void SrsFlvSegment::reset() | 73 | +int SrsFlvSegment::initialize(SrsSource* s, SrsRequest* r) |
| 58 | { | 74 | { |
| 59 | - has_keyframe = false; | ||
| 60 | - starttime = -1; | ||
| 61 | - duration = 0; | 75 | + int ret = ERROR_SUCCESS; |
| 76 | + | ||
| 77 | + source = s; | ||
| 78 | + req = r; | ||
| 79 | + jitter_algorithm = (SrsRtmpJitterAlgorithm)_srs_config->get_dvr_time_jitter(req->vhost); | ||
| 80 | + | ||
| 81 | + return ret; | ||
| 62 | } | 82 | } |
| 63 | 83 | ||
| 64 | -SrsDvrPlan::SrsDvrPlan() | 84 | +bool SrsFlvSegment::is_overflow(int64_t max_duration) |
| 65 | { | 85 | { |
| 66 | - _source = NULL; | ||
| 67 | - _req = NULL; | ||
| 68 | - jitter = NULL; | ||
| 69 | - dvr_enabled = false; | ||
| 70 | - fs = new SrsFileWriter(); | ||
| 71 | - enc = new SrsFlvEncoder(); | ||
| 72 | - segment = new SrsFlvSegment(); | ||
| 73 | - jitter_algorithm = SrsRtmpJitterAlgorithmOFF; | ||
| 74 | - | ||
| 75 | - _srs_config->subscribe(this); | 86 | + return duration >= max_duration; |
| 76 | } | 87 | } |
| 77 | 88 | ||
| 78 | -SrsDvrPlan::~SrsDvrPlan() | 89 | +int SrsFlvSegment::open() |
| 79 | { | 90 | { |
| 80 | - _srs_config->unsubscribe(this); | 91 | + int ret = ERROR_SUCCESS; |
| 81 | 92 | ||
| 82 | - srs_freep(jitter); | ||
| 83 | - srs_freep(fs); | ||
| 84 | - srs_freep(enc); | ||
| 85 | - srs_freep(segment); | 93 | + // ignore when already open. |
| 94 | + if (fs->is_open()) { | ||
| 95 | + return ret; | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + path = generate_path(); | ||
| 99 | + bool fresh_flv_file = !srs_path_exists(path); | ||
| 100 | + | ||
| 101 | + // create dir first. | ||
| 102 | + std::string dir = path.substr(0, path.rfind("/")); | ||
| 103 | + if ((ret = srs_create_dir_recursively(dir)) != ERROR_SUCCESS) { | ||
| 104 | + srs_error("create dir=%s failed. ret=%d", dir.c_str(), ret); | ||
| 105 | + return ret; | ||
| 106 | + } | ||
| 107 | + srs_info("create dir=%s ok", dir.c_str()); | ||
| 108 | + | ||
| 109 | + // create jitter. | ||
| 110 | + if ((ret = create_jitter(!fresh_flv_file)) != ERROR_SUCCESS) { | ||
| 111 | + srs_error("create jitter failed, path=%s, fresh=%d. ret=%d", path.c_str(), fresh_flv_file, ret); | ||
| 112 | + return ret; | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + // generate the tmp flv path. | ||
| 116 | + if (!fresh_flv_file) { | ||
| 117 | + // when path exists, always append to it. | ||
| 118 | + // so we must use the target flv path as output flv. | ||
| 119 | + tmp_flv_file = path; | ||
| 120 | + } else { | ||
| 121 | + // when path not exists, dvr to tmp file. | ||
| 122 | + tmp_flv_file = path + ".tmp"; | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + // open file writer, in append or create mode. | ||
| 126 | + if (!fresh_flv_file) { | ||
| 127 | + if ((ret = fs->open_append(tmp_flv_file)) != ERROR_SUCCESS) { | ||
| 128 | + srs_error("append file stream for file %s failed. ret=%d", path.c_str(), ret); | ||
| 129 | + return ret; | ||
| 130 | + } | ||
| 131 | + srs_trace("dvr: always append to when exists, file=%s.", path.c_str()); | ||
| 132 | + } else { | ||
| 133 | + if ((ret = fs->open(tmp_flv_file)) != ERROR_SUCCESS) { | ||
| 134 | + srs_error("open file stream for file %s failed. ret=%d", path.c_str(), ret); | ||
| 135 | + return ret; | ||
| 136 | + } | ||
| 137 | + } | ||
| 138 | + | ||
| 139 | + // when exists, donot write flv header. | ||
| 140 | + if (fresh_flv_file) { | ||
| 141 | + // initialize the encoder. | ||
| 142 | + if ((ret = enc->initialize(fs)) != ERROR_SUCCESS) { | ||
| 143 | + srs_error("initialize enc by fs for file %s failed. ret=%d", path.c_str(), ret); | ||
| 144 | + return ret; | ||
| 145 | + } | ||
| 146 | + | ||
| 147 | + // write the flv header to writer. | ||
| 148 | + if ((ret = enc->write_header()) != ERROR_SUCCESS) { | ||
| 149 | + srs_error("write flv header failed. ret=%d", ret); | ||
| 150 | + return ret; | ||
| 151 | + } | ||
| 152 | + } | ||
| 153 | + | ||
| 154 | + srs_trace("dvr stream %s to file %s", req->stream.c_str(), path.c_str()); | ||
| 155 | + | ||
| 156 | + return ret; | ||
| 86 | } | 157 | } |
| 87 | 158 | ||
| 88 | -int SrsDvrPlan::initialize(SrsSource* source, SrsRequest* req) | 159 | +int SrsFlvSegment::close() |
| 89 | { | 160 | { |
| 90 | int ret = ERROR_SUCCESS; | 161 | int ret = ERROR_SUCCESS; |
| 91 | 162 | ||
| 92 | - _source = source; | ||
| 93 | - _req = req; | 163 | + // ignore when already closed. |
| 164 | + if (!fs->is_open()) { | ||
| 165 | + return ret; | ||
| 166 | + } | ||
| 94 | 167 | ||
| 95 | - jitter_algorithm = (SrsRtmpJitterAlgorithm)_srs_config->get_dvr_time_jitter(_req->vhost); | 168 | + fs->close(); |
| 169 | + | ||
| 170 | + // when tmp flv file exists, reap it. | ||
| 171 | + if (tmp_flv_file != path) { | ||
| 172 | + if (rename(tmp_flv_file.c_str(), path.c_str()) < 0) { | ||
| 173 | + ret = ERROR_SYSTEM_FILE_RENAME; | ||
| 174 | + srs_error("rename flv file failed, %s => %s. ret=%d", | ||
| 175 | + tmp_flv_file.c_str(), path.c_str(), ret); | ||
| 176 | + return ret; | ||
| 177 | + } | ||
| 178 | + } | ||
| 179 | + | ||
| 180 | +#ifdef SRS_AUTO_HTTP_CALLBACK | ||
| 181 | + if (_srs_config->get_vhost_http_hooks_enabled(req->vhost)) { | ||
| 182 | + // HTTP: on_dvr | ||
| 183 | + SrsConfDirective* on_dvr = _srs_config->get_vhost_on_dvr(req->vhost); | ||
| 184 | + if (!on_dvr) { | ||
| 185 | + srs_info("ignore the empty http callback: on_dvr"); | ||
| 186 | + return ret; | ||
| 187 | + } | ||
| 188 | + | ||
| 189 | + int connection_id = _srs_context->get_id(); | ||
| 190 | + std::string ip = req->ip; | ||
| 191 | + std::string cwd = _srs_config->cwd(); | ||
| 192 | + std::string file = path; | ||
| 193 | + for (int i = 0; i < (int)on_dvr->args.size(); i++) { | ||
| 194 | + std::string url = on_dvr->args.at(i); | ||
| 195 | + if ((ret = SrsHttpHooks::on_dvr(url, connection_id, ip, req, cwd, file)) != ERROR_SUCCESS) { | ||
| 196 | + srs_error("hook client on_dvr failed. url=%s, ret=%d", url.c_str(), ret); | ||
| 197 | + return ret; | ||
| 198 | + } | ||
| 199 | + } | ||
| 200 | + } | ||
| 201 | +#endif | ||
| 96 | 202 | ||
| 97 | return ret; | 203 | return ret; |
| 98 | } | 204 | } |
| 99 | 205 | ||
| 100 | -int SrsDvrPlan::on_publish() | 206 | +int SrsFlvSegment::write_metadata(SrsOnMetaDataPacket* metadata) |
| 101 | { | 207 | { |
| 102 | int ret = ERROR_SUCCESS; | 208 | int ret = ERROR_SUCCESS; |
| 103 | 209 | ||
| 104 | - // support multiple publish. | ||
| 105 | - if (dvr_enabled) { | 210 | + int size = 0; |
| 211 | + char* payload = NULL; | ||
| 212 | + if ((ret = metadata->encode(size, payload)) != ERROR_SUCCESS) { | ||
| 106 | return ret; | 213 | return ret; |
| 107 | } | 214 | } |
| 215 | + SrsAutoFree(char, payload); | ||
| 108 | 216 | ||
| 109 | - SrsRequest* req = _req; | ||
| 110 | - | ||
| 111 | - if (!_srs_config->get_dvr_enabled(req->vhost)) { | 217 | + if ((ret = enc->write_metadata(18, payload, size)) != ERROR_SUCCESS) { |
| 112 | return ret; | 218 | return ret; |
| 113 | } | 219 | } |
| 114 | 220 | ||
| 115 | - // jitter when publish, ensure whole stream start from 0. | ||
| 116 | - srs_freep(jitter); | ||
| 117 | - jitter = new SrsRtmpJitter(); | ||
| 118 | - | ||
| 119 | - // always update time cache. | ||
| 120 | - srs_update_system_time_ms(); | 221 | + return ret; |
| 222 | +} | ||
| 223 | + | ||
| 224 | +int SrsFlvSegment::write_audio(SrsSharedPtrMessage* __audio) | ||
| 225 | +{ | ||
| 226 | + int ret = ERROR_SUCCESS; | ||
| 227 | + | ||
| 228 | + SrsSharedPtrMessage* audio = __audio->copy(); | ||
| 229 | + SrsAutoFree(SrsSharedPtrMessage, audio); | ||
| 121 | 230 | ||
| 122 | - // when republish, stream starting. | ||
| 123 | - segment->stream_previous_pkt_time = -1; | ||
| 124 | - segment->stream_starttime = srs_get_system_time_ms(); | ||
| 125 | - segment->stream_duration = 0; | 231 | + if ((jitter->correct(audio, 0, 0, jitter_algorithm)) != ERROR_SUCCESS) { |
| 232 | + return ret; | ||
| 233 | + } | ||
| 126 | 234 | ||
| 127 | - if ((ret = open_new_segment()) != ERROR_SUCCESS) { | 235 | + char* payload = audio->payload; |
| 236 | + int size = audio->size; | ||
| 237 | + int64_t timestamp = plan->filter_timestamp(audio->timestamp); | ||
| 238 | + if ((ret = enc->write_audio(timestamp, payload, size)) != ERROR_SUCCESS) { | ||
| 239 | + return ret; | ||
| 240 | + } | ||
| 241 | + | ||
| 242 | + if ((ret = on_update_duration(audio)) != ERROR_SUCCESS) { | ||
| 128 | return ret; | 243 | return ret; |
| 129 | } | 244 | } |
| 130 | 245 | ||
| 131 | return ret; | 246 | return ret; |
| 132 | } | 247 | } |
| 133 | 248 | ||
| 134 | -int SrsDvrPlan::open_new_segment() | 249 | +int SrsFlvSegment::write_video(SrsSharedPtrMessage* __video) |
| 135 | { | 250 | { |
| 136 | int ret = ERROR_SUCCESS; | 251 | int ret = ERROR_SUCCESS; |
| 252 | + | ||
| 253 | + SrsSharedPtrMessage* video = __video->copy(); | ||
| 254 | + SrsAutoFree(SrsSharedPtrMessage, video); | ||
| 255 | + | ||
| 256 | + char* payload = video->payload; | ||
| 257 | + int size = video->size; | ||
| 137 | 258 | ||
| 138 | - SrsRequest* req = _req; | 259 | +#ifdef SRS_AUTO_HTTP_CALLBACK |
| 260 | + bool is_key_frame = SrsFlvCodec::video_is_h264(payload, size) | ||
| 261 | + && SrsFlvCodec::video_is_keyframe(payload, size) | ||
| 262 | + && !SrsFlvCodec::video_is_sequence_header(payload, size); | ||
| 263 | + if (is_key_frame) { | ||
| 264 | + has_keyframe = true; | ||
| 265 | + if ((ret = plan->on_video_keyframe()) != ERROR_SUCCESS) { | ||
| 266 | + return ret; | ||
| 267 | + } | ||
| 268 | + } | ||
| 269 | + srs_verbose("dvr video is key: %d", is_key_frame); | ||
| 270 | +#endif | ||
| 139 | 271 | ||
| 272 | + if ((jitter->correct(video, 0, 0, jitter_algorithm)) != ERROR_SUCCESS) { | ||
| 273 | + return ret; | ||
| 274 | + } | ||
| 275 | + | ||
| 276 | + // update segment duration, session plan just update the duration, | ||
| 277 | + // the segment plan will reap segment if exceed, this video will write to next segment. | ||
| 278 | + if ((ret = on_update_duration(video)) != ERROR_SUCCESS) { | ||
| 279 | + return ret; | ||
| 280 | + } | ||
| 281 | + | ||
| 282 | + int32_t timestamp = plan->filter_timestamp(video->timestamp); | ||
| 283 | + if ((ret = enc->write_video(timestamp, payload, size)) != ERROR_SUCCESS) { | ||
| 284 | + return ret; | ||
| 285 | + } | ||
| 286 | + | ||
| 287 | + return ret; | ||
| 288 | +} | ||
| 289 | + | ||
| 290 | +string SrsFlvSegment::generate_path() | ||
| 291 | +{ | ||
| 140 | // the path in config, for example, | 292 | // the path in config, for example, |
| 141 | // /data/[vhost]/[app]/[stream]/[2006]/[01]/[02]/[15].[04].[05].[999].flv | 293 | // /data/[vhost]/[app]/[stream]/[2006]/[01]/[02]/[15].[04].[05].[999].flv |
| 142 | std::string path_config = _srs_config->get_dvr_path(req->vhost); | 294 | std::string path_config = _srs_config->get_dvr_path(req->vhost); |
| @@ -147,26 +299,26 @@ int SrsDvrPlan::open_new_segment() | @@ -147,26 +299,26 @@ int SrsDvrPlan::open_new_segment() | ||
| 147 | } | 299 | } |
| 148 | 300 | ||
| 149 | // the flv file path | 301 | // the flv file path |
| 150 | - std::string path = path_config; | 302 | + std::string flv_path = path_config; |
| 151 | 303 | ||
| 152 | // variable [vhost] | 304 | // variable [vhost] |
| 153 | - path = srs_string_replace(path, "[vhost]", req->vhost); | 305 | + flv_path = srs_string_replace(flv_path, "[vhost]", req->vhost); |
| 154 | // variable [app] | 306 | // variable [app] |
| 155 | - path = srs_string_replace(path, "[app]", req->app); | 307 | + flv_path = srs_string_replace(flv_path, "[app]", req->app); |
| 156 | // variable [stream] | 308 | // variable [stream] |
| 157 | - path = srs_string_replace(path, "[stream]", req->stream); | 309 | + flv_path = srs_string_replace(flv_path, "[stream]", req->stream); |
| 158 | 310 | ||
| 159 | // date and time substitude | 311 | // date and time substitude |
| 160 | // clock time | 312 | // clock time |
| 161 | timeval tv; | 313 | timeval tv; |
| 162 | if (gettimeofday(&tv, NULL) == -1) { | 314 | if (gettimeofday(&tv, NULL) == -1) { |
| 163 | - return ERROR_SYSTEM_TIME; | 315 | + return flv_path; |
| 164 | } | 316 | } |
| 165 | 317 | ||
| 166 | // to calendar time | 318 | // to calendar time |
| 167 | struct tm* tm; | 319 | struct tm* tm; |
| 168 | if ((tm = localtime(&tv.tv_sec)) == NULL) { | 320 | if ((tm = localtime(&tv.tv_sec)) == NULL) { |
| 169 | - return ERROR_SYSTEM_TIME; | 321 | + return flv_path; |
| 170 | } | 322 | } |
| 171 | 323 | ||
| 172 | // the buffer to format the date and time. | 324 | // the buffer to format the date and time. |
| @@ -175,316 +327,212 @@ int SrsDvrPlan::open_new_segment() | @@ -175,316 +327,212 @@ int SrsDvrPlan::open_new_segment() | ||
| 175 | // [2006], replace with current year. | 327 | // [2006], replace with current year. |
| 176 | if (true) { | 328 | if (true) { |
| 177 | snprintf(buf, sizeof(buf), "%d", 1900 + tm->tm_year); | 329 | snprintf(buf, sizeof(buf), "%d", 1900 + tm->tm_year); |
| 178 | - path = srs_string_replace(path, "[2006]", buf); | 330 | + flv_path = srs_string_replace(flv_path, "[2006]", buf); |
| 179 | } | 331 | } |
| 180 | // [2006], replace with current year. | 332 | // [2006], replace with current year. |
| 181 | if (true) { | 333 | if (true) { |
| 182 | snprintf(buf, sizeof(buf), "%d", 1900 + tm->tm_year); | 334 | snprintf(buf, sizeof(buf), "%d", 1900 + tm->tm_year); |
| 183 | - path = srs_string_replace(path, "[2006]", buf); | 335 | + flv_path = srs_string_replace(flv_path, "[2006]", buf); |
| 184 | } | 336 | } |
| 185 | // [01], replace this const to current month. | 337 | // [01], replace this const to current month. |
| 186 | if (true) { | 338 | if (true) { |
| 187 | snprintf(buf, sizeof(buf), "%d", 1 + tm->tm_mon); | 339 | snprintf(buf, sizeof(buf), "%d", 1 + tm->tm_mon); |
| 188 | - path = srs_string_replace(path, "[01]", buf); | 340 | + flv_path = srs_string_replace(flv_path, "[01]", buf); |
| 189 | } | 341 | } |
| 190 | // [02], replace this const to current date. | 342 | // [02], replace this const to current date. |
| 191 | if (true) { | 343 | if (true) { |
| 192 | snprintf(buf, sizeof(buf), "%d", tm->tm_mday); | 344 | snprintf(buf, sizeof(buf), "%d", tm->tm_mday); |
| 193 | - path = srs_string_replace(path, "[02]", buf); | 345 | + flv_path = srs_string_replace(flv_path, "[02]", buf); |
| 194 | } | 346 | } |
| 195 | // [15], replace this const to current hour. | 347 | // [15], replace this const to current hour. |
| 196 | if (true) { | 348 | if (true) { |
| 197 | snprintf(buf, sizeof(buf), "%d", tm->tm_hour); | 349 | snprintf(buf, sizeof(buf), "%d", tm->tm_hour); |
| 198 | - path = srs_string_replace(path, "[15]", buf); | 350 | + flv_path = srs_string_replace(flv_path, "[15]", buf); |
| 199 | } | 351 | } |
| 200 | // [04], repleace this const to current minute. | 352 | // [04], repleace this const to current minute. |
| 201 | if (true) { | 353 | if (true) { |
| 202 | snprintf(buf, sizeof(buf), "%d", tm->tm_min); | 354 | snprintf(buf, sizeof(buf), "%d", tm->tm_min); |
| 203 | - path = srs_string_replace(path, "[04]", buf); | 355 | + flv_path = srs_string_replace(flv_path, "[04]", buf); |
| 204 | } | 356 | } |
| 205 | // [05], repleace this const to current second. | 357 | // [05], repleace this const to current second. |
| 206 | if (true) { | 358 | if (true) { |
| 207 | snprintf(buf, sizeof(buf), "%d", tm->tm_sec); | 359 | snprintf(buf, sizeof(buf), "%d", tm->tm_sec); |
| 208 | - path = srs_string_replace(path, "[05]", buf); | 360 | + flv_path = srs_string_replace(flv_path, "[05]", buf); |
| 209 | } | 361 | } |
| 210 | // [999], repleace this const to current millisecond. | 362 | // [999], repleace this const to current millisecond. |
| 211 | if (true) { | 363 | if (true) { |
| 212 | snprintf(buf, sizeof(buf), "%03d", (int)(tv.tv_usec / 1000)); | 364 | snprintf(buf, sizeof(buf), "%03d", (int)(tv.tv_usec / 1000)); |
| 213 | - path = srs_string_replace(path, "[999]", buf); | 365 | + flv_path = srs_string_replace(flv_path, "[999]", buf); |
| 214 | } | 366 | } |
| 215 | // [timestamp],replace this const to current UNIX timestamp in ms. | 367 | // [timestamp],replace this const to current UNIX timestamp in ms. |
| 216 | if (true) { | 368 | if (true) { |
| 217 | int64_t now_us = ((int64_t)tv.tv_sec) * 1000 * 1000 + (int64_t)tv.tv_usec; | 369 | int64_t now_us = ((int64_t)tv.tv_sec) * 1000 * 1000 + (int64_t)tv.tv_usec; |
| 218 | snprintf(buf, sizeof(buf), "%"PRId64, now_us / 1000); | 370 | snprintf(buf, sizeof(buf), "%"PRId64, now_us / 1000); |
| 219 | - path = srs_string_replace(path, "[timestamp]", buf); | 371 | + flv_path = srs_string_replace(flv_path, "[timestamp]", buf); |
| 220 | } | 372 | } |
| 373 | + | ||
| 374 | + return flv_path; | ||
| 375 | +} | ||
| 376 | + | ||
| 377 | +int SrsFlvSegment::create_jitter(bool loads_from_flv) | ||
| 378 | +{ | ||
| 379 | + int ret = ERROR_SUCCESS; | ||
| 221 | 380 | ||
| 222 | - // create dir first. | ||
| 223 | - std::string dir = path.substr(0, path.rfind("/")); | ||
| 224 | - if ((ret = srs_create_dir_recursively(dir)) != ERROR_SUCCESS) { | ||
| 225 | - srs_error("create dir=%s failed. ret=%d", dir.c_str(), ret); | 381 | + // when path exists, use exists jitter. |
| 382 | + if (!loads_from_flv) { | ||
| 383 | + // jitter when publish, ensure whole stream start from 0. | ||
| 384 | + srs_freep(jitter); | ||
| 385 | + jitter = new SrsRtmpJitter(); | ||
| 386 | + | ||
| 387 | + // fresh stream starting. | ||
| 388 | + starttime = -1; | ||
| 389 | + stream_previous_pkt_time = -1; | ||
| 390 | + stream_starttime = srs_update_system_time_ms(); | ||
| 391 | + stream_duration = 0; | ||
| 392 | + | ||
| 393 | + // fresh segment starting. | ||
| 394 | + has_keyframe = false; | ||
| 395 | + duration = 0; | ||
| 396 | + | ||
| 226 | return ret; | 397 | return ret; |
| 227 | } | 398 | } |
| 228 | - srs_info("create dir=%s ok", dir.c_str()); | ||
| 229 | - | ||
| 230 | - if ((ret = flv_open(req->get_stream_url(), path)) != ERROR_SUCCESS) { | 399 | + |
| 400 | + // when jitter ok, do nothing. | ||
| 401 | + if (jitter) { | ||
| 231 | return ret; | 402 | return ret; |
| 232 | } | 403 | } |
| 233 | - dvr_enabled = true; | ||
| 234 | - | 404 | + |
| 405 | + // always ensure the jitter crote. | ||
| 406 | + // for the first time, initialize jitter from exists file. | ||
| 407 | + jitter = new SrsRtmpJitter(); | ||
| 408 | + | ||
| 409 | + // TODO: FIXME: implements it. | ||
| 410 | + | ||
| 235 | return ret; | 411 | return ret; |
| 236 | } | 412 | } |
| 237 | 413 | ||
| 238 | -int SrsDvrPlan::on_dvr_request_sh() | 414 | +int SrsFlvSegment::on_update_duration(SrsSharedPtrMessage* msg) |
| 239 | { | 415 | { |
| 240 | int ret = ERROR_SUCCESS; | 416 | int ret = ERROR_SUCCESS; |
| 241 | 417 | ||
| 242 | - // the dvr is enabled, notice the source to push the data. | ||
| 243 | - if ((ret = _source->on_dvr_request_sh()) != ERROR_SUCCESS) { | ||
| 244 | - return ret; | 418 | + // we must assumpt that the stream timestamp is monotonically increase, |
| 419 | + // that is, always use time jitter to correct the timestamp. | ||
| 420 | + // except the time jitter is disabled in config. | ||
| 421 | + | ||
| 422 | + // set the segment starttime at first time | ||
| 423 | + if (starttime < 0) { | ||
| 424 | + starttime = msg->timestamp; | ||
| 245 | } | 425 | } |
| 246 | 426 | ||
| 427 | + // no previous packet or timestamp overflow. | ||
| 428 | + if (stream_previous_pkt_time < 0 || stream_previous_pkt_time > msg->timestamp) { | ||
| 429 | + stream_previous_pkt_time = msg->timestamp; | ||
| 430 | + } | ||
| 431 | + | ||
| 432 | + // collect segment and stream duration, timestamp overflow is ok. | ||
| 433 | + duration += msg->timestamp - stream_previous_pkt_time; | ||
| 434 | + stream_duration += msg->timestamp - stream_previous_pkt_time; | ||
| 435 | + | ||
| 436 | + // update previous packet time | ||
| 437 | + stream_previous_pkt_time = msg->timestamp; | ||
| 438 | + | ||
| 247 | return ret; | 439 | return ret; |
| 248 | } | 440 | } |
| 249 | 441 | ||
| 250 | -int SrsDvrPlan::on_video_keyframe() | 442 | +int SrsFlvSegment::on_reload_vhost_dvr(std::string /*vhost*/) |
| 251 | { | 443 | { |
| 252 | int ret = ERROR_SUCCESS; | 444 | int ret = ERROR_SUCCESS; |
| 445 | + | ||
| 446 | + jitter_algorithm = (SrsRtmpJitterAlgorithm)_srs_config->get_dvr_time_jitter(req->vhost); | ||
| 447 | + | ||
| 253 | return ret; | 448 | return ret; |
| 254 | } | 449 | } |
| 255 | 450 | ||
| 256 | -int64_t SrsDvrPlan::filter_timestamp(int64_t timestamp) | 451 | +SrsDvrPlan::SrsDvrPlan() |
| 257 | { | 452 | { |
| 258 | - return timestamp; | 453 | + source = NULL; |
| 454 | + req = NULL; | ||
| 455 | + | ||
| 456 | + dvr_enabled = false; | ||
| 457 | + segment = new SrsFlvSegment(this); | ||
| 259 | } | 458 | } |
| 260 | 459 | ||
| 261 | -int SrsDvrPlan::on_meta_data(SrsOnMetaDataPacket* metadata) | 460 | +SrsDvrPlan::~SrsDvrPlan() |
| 262 | { | 461 | { |
| 263 | - int ret = ERROR_SUCCESS; | ||
| 264 | - | ||
| 265 | - if (!dvr_enabled) { | ||
| 266 | - return ret; | ||
| 267 | - } | ||
| 268 | - | ||
| 269 | - int size = 0; | ||
| 270 | - char* payload = NULL; | ||
| 271 | - if ((ret = metadata->encode(size, payload)) != ERROR_SUCCESS) { | ||
| 272 | - return ret; | ||
| 273 | - } | ||
| 274 | - SrsAutoFree(char, payload); | ||
| 275 | - | ||
| 276 | - if ((ret = enc->write_metadata(18, payload, size)) != ERROR_SUCCESS) { | ||
| 277 | - return ret; | ||
| 278 | - } | ||
| 279 | - | ||
| 280 | - return ret; | 462 | + srs_freep(segment); |
| 281 | } | 463 | } |
| 282 | 464 | ||
| 283 | -int SrsDvrPlan::on_audio(SrsSharedPtrMessage* __audio) | 465 | +int SrsDvrPlan::initialize(SrsSource* s, SrsRequest* r) |
| 284 | { | 466 | { |
| 285 | int ret = ERROR_SUCCESS; | 467 | int ret = ERROR_SUCCESS; |
| 286 | - | ||
| 287 | - if (!dvr_enabled) { | ||
| 288 | - return ret; | ||
| 289 | - } | ||
| 290 | 468 | ||
| 291 | - SrsSharedPtrMessage* audio = __audio->copy(); | ||
| 292 | - SrsAutoFree(SrsSharedPtrMessage, audio); | ||
| 293 | - | ||
| 294 | - if ((jitter->correct(audio, 0, 0, jitter_algorithm)) != ERROR_SUCCESS) { | ||
| 295 | - return ret; | ||
| 296 | - } | ||
| 297 | - | ||
| 298 | - char* payload = audio->payload; | ||
| 299 | - int size = audio->size; | ||
| 300 | - int64_t timestamp = filter_timestamp(audio->timestamp); | ||
| 301 | - if ((ret = enc->write_audio(timestamp, payload, size)) != ERROR_SUCCESS) { | ||
| 302 | - return ret; | ||
| 303 | - } | ||
| 304 | - | ||
| 305 | - if ((ret = update_duration(audio)) != ERROR_SUCCESS) { | 469 | + source = s; |
| 470 | + req = r; | ||
| 471 | + | ||
| 472 | + if ((ret = segment->initialize(s, r)) != ERROR_SUCCESS) { | ||
| 306 | return ret; | 473 | return ret; |
| 307 | } | 474 | } |
| 308 | - | 475 | + |
| 309 | return ret; | 476 | return ret; |
| 310 | } | 477 | } |
| 311 | 478 | ||
| 312 | -int SrsDvrPlan::on_video(SrsSharedPtrMessage* __video) | 479 | +int SrsDvrPlan::on_dvr_request_sh() |
| 313 | { | 480 | { |
| 314 | int ret = ERROR_SUCCESS; | 481 | int ret = ERROR_SUCCESS; |
| 315 | 482 | ||
| 316 | - if (!dvr_enabled) { | ||
| 317 | - return ret; | ||
| 318 | - } | ||
| 319 | - | ||
| 320 | - SrsSharedPtrMessage* video = __video->copy(); | ||
| 321 | - SrsAutoFree(SrsSharedPtrMessage, video); | ||
| 322 | - | ||
| 323 | - char* payload = video->payload; | ||
| 324 | - int size = video->size; | ||
| 325 | - | ||
| 326 | -#ifdef SRS_AUTO_HTTP_CALLBACK | ||
| 327 | - bool is_key_frame = SrsFlvCodec::video_is_h264(payload, size) | ||
| 328 | - && SrsFlvCodec::video_is_keyframe(payload, size) | ||
| 329 | - && !SrsFlvCodec::video_is_sequence_header(payload, size); | ||
| 330 | - if (is_key_frame) { | ||
| 331 | - segment->has_keyframe = true; | ||
| 332 | - if ((ret = on_video_keyframe()) != ERROR_SUCCESS) { | ||
| 333 | - return ret; | ||
| 334 | - } | ||
| 335 | - } | ||
| 336 | - srs_verbose("dvr video is key: %d", is_key_frame); | ||
| 337 | -#endif | ||
| 338 | - | ||
| 339 | - if ((jitter->correct(video, 0, 0, jitter_algorithm)) != ERROR_SUCCESS) { | ||
| 340 | - return ret; | ||
| 341 | - } | ||
| 342 | - | ||
| 343 | - // update segment duration, session plan just update the duration, | ||
| 344 | - // the segment plan will reap segment if exceed, this video will write to next segment. | ||
| 345 | - if ((ret = update_duration(video)) != ERROR_SUCCESS) { | ||
| 346 | - return ret; | ||
| 347 | - } | ||
| 348 | - | ||
| 349 | - int32_t timestamp = filter_timestamp(video->timestamp); | ||
| 350 | - if ((ret = enc->write_video(timestamp, payload, size)) != ERROR_SUCCESS) { | 483 | + // the dvr is enabled, notice the source to push the data. |
| 484 | + if ((ret = source->on_dvr_request_sh()) != ERROR_SUCCESS) { | ||
| 351 | return ret; | 485 | return ret; |
| 352 | } | 486 | } |
| 353 | 487 | ||
| 354 | return ret; | 488 | return ret; |
| 355 | } | 489 | } |
| 356 | 490 | ||
| 357 | -int SrsDvrPlan::on_reload_vhost_dvr(std::string /*vhost*/) | 491 | +int SrsDvrPlan::on_video_keyframe() |
| 358 | { | 492 | { |
| 359 | - int ret = ERROR_SUCCESS; | ||
| 360 | - | ||
| 361 | - jitter_algorithm = (SrsRtmpJitterAlgorithm)_srs_config->get_dvr_time_jitter(_req->vhost); | ||
| 362 | - | ||
| 363 | - return ret; | 493 | + return ERROR_SUCCESS; |
| 364 | } | 494 | } |
| 365 | 495 | ||
| 366 | -int SrsDvrPlan::flv_open(string stream, string path) | 496 | +int64_t SrsDvrPlan::filter_timestamp(int64_t timestamp) |
| 367 | { | 497 | { |
| 368 | - int ret = ERROR_SUCCESS; | ||
| 369 | - | ||
| 370 | - segment->reset(); | ||
| 371 | - | ||
| 372 | - if (srs_path_exists(path)) { | ||
| 373 | - // when path exists, always append to it. | ||
| 374 | - // so we must use the target flv path as output flv. | ||
| 375 | - tmp_flv_file = path; | ||
| 376 | - } else { | ||
| 377 | - // when path not exists, dvr to tmp file. | ||
| 378 | - tmp_flv_file = path + ".tmp"; | ||
| 379 | - } | ||
| 380 | - | ||
| 381 | - if (srs_path_exists(path)) { | ||
| 382 | - if ((ret = fs->open_append(tmp_flv_file)) != ERROR_SUCCESS) { | ||
| 383 | - srs_error("append file stream for file %s failed. ret=%d", path.c_str(), ret); | ||
| 384 | - return ret; | ||
| 385 | - } | ||
| 386 | - srs_trace("dvr: always append to when exists, file=%s.", path.c_str()); | ||
| 387 | - } else { | ||
| 388 | - if ((ret = fs->open(tmp_flv_file)) != ERROR_SUCCESS) { | ||
| 389 | - srs_error("open file stream for file %s failed. ret=%d", path.c_str(), ret); | ||
| 390 | - return ret; | ||
| 391 | - } | ||
| 392 | - } | ||
| 393 | - | ||
| 394 | - if ((ret = enc->initialize(fs)) != ERROR_SUCCESS) { | ||
| 395 | - srs_error("initialize enc by fs for file %s failed. ret=%d", path.c_str(), ret); | ||
| 396 | - return ret; | ||
| 397 | - } | ||
| 398 | - | ||
| 399 | - // when exists, donot write flv header. | ||
| 400 | - if (tmp_flv_file != path) { | ||
| 401 | - if ((ret = write_flv_header()) != ERROR_SUCCESS) { | ||
| 402 | - return ret; | ||
| 403 | - } | ||
| 404 | - } | ||
| 405 | - | ||
| 406 | - segment->path = path; | ||
| 407 | - | ||
| 408 | - srs_trace("dvr stream %s to file %s", stream.c_str(), path.c_str()); | ||
| 409 | - return ret; | 498 | + return timestamp; |
| 410 | } | 499 | } |
| 411 | 500 | ||
| 412 | -int SrsDvrPlan::flv_close() | 501 | +int SrsDvrPlan::on_meta_data(SrsOnMetaDataPacket* metadata) |
| 413 | { | 502 | { |
| 414 | int ret = ERROR_SUCCESS; | 503 | int ret = ERROR_SUCCESS; |
| 415 | 504 | ||
| 416 | - fs->close(); | ||
| 417 | - | ||
| 418 | - // when tmp flv file exists, reap it. | ||
| 419 | - if (tmp_flv_file != segment->path) { | ||
| 420 | - if (rename(tmp_flv_file.c_str(), segment->path.c_str()) < 0) { | ||
| 421 | - ret = ERROR_SYSTEM_FILE_RENAME; | ||
| 422 | - srs_error("rename flv file failed, %s => %s. ret=%d", | ||
| 423 | - tmp_flv_file.c_str(), segment->path.c_str(), ret); | ||
| 424 | - return ret; | ||
| 425 | - } | 505 | + if (!dvr_enabled) { |
| 506 | + return ret; | ||
| 426 | } | 507 | } |
| 427 | 508 | ||
| 428 | -#ifdef SRS_AUTO_HTTP_CALLBACK | ||
| 429 | - SrsRequest* req = _req; | ||
| 430 | - if (_srs_config->get_vhost_http_hooks_enabled(req->vhost)) { | ||
| 431 | - // HTTP: on_dvr | ||
| 432 | - SrsConfDirective* on_dvr = _srs_config->get_vhost_on_dvr(req->vhost); | ||
| 433 | - if (!on_dvr) { | ||
| 434 | - srs_info("ignore the empty http callback: on_dvr"); | ||
| 435 | - return ret; | ||
| 436 | - } | ||
| 437 | - | ||
| 438 | - int connection_id = _srs_context->get_id(); | ||
| 439 | - std::string ip = req->ip; | ||
| 440 | - std::string cwd = _srs_config->cwd(); | ||
| 441 | - std::string file = segment->path; | ||
| 442 | - for (int i = 0; i < (int)on_dvr->args.size(); i++) { | ||
| 443 | - std::string url = on_dvr->args.at(i); | ||
| 444 | - if ((ret = SrsHttpHooks::on_dvr(url, connection_id, ip, req, cwd, file)) != ERROR_SUCCESS) { | ||
| 445 | - srs_error("hook client on_dvr failed. url=%s, ret=%d", url.c_str(), ret); | ||
| 446 | - return ret; | ||
| 447 | - } | ||
| 448 | - } | ||
| 449 | - } | ||
| 450 | -#endif | ||
| 451 | - | ||
| 452 | - return ret; | 509 | + return segment->write_metadata(metadata); |
| 453 | } | 510 | } |
| 454 | 511 | ||
| 455 | -int SrsDvrPlan::update_duration(SrsSharedPtrMessage* msg) | 512 | +int SrsDvrPlan::on_audio(SrsSharedPtrMessage* __audio) |
| 456 | { | 513 | { |
| 457 | int ret = ERROR_SUCCESS; | 514 | int ret = ERROR_SUCCESS; |
| 458 | - | ||
| 459 | - // we must assumpt that the stream timestamp is monotonically increase, | ||
| 460 | - // that is, always use time jitter to correct the timestamp. | ||
| 461 | 515 | ||
| 462 | - // set the segment starttime at first time | ||
| 463 | - if (segment->starttime < 0) { | ||
| 464 | - segment->starttime = msg->timestamp; | 516 | + if (!dvr_enabled) { |
| 517 | + return ret; | ||
| 465 | } | 518 | } |
| 466 | - | ||
| 467 | - // no previous packet or timestamp overflow. | ||
| 468 | - if (segment->stream_previous_pkt_time < 0 || segment->stream_previous_pkt_time > msg->timestamp) { | ||
| 469 | - segment->stream_previous_pkt_time = msg->timestamp; | 519 | + |
| 520 | + if ((ret = segment->write_audio(__audio)) != ERROR_SUCCESS) { | ||
| 521 | + return ret; | ||
| 470 | } | 522 | } |
| 471 | 523 | ||
| 472 | - // collect segment and stream duration, timestamp overflow is ok. | ||
| 473 | - segment->duration += msg->timestamp - segment->stream_previous_pkt_time; | ||
| 474 | - segment->stream_duration += msg->timestamp - segment->stream_previous_pkt_time; | ||
| 475 | - | ||
| 476 | - // update previous packet time | ||
| 477 | - segment->stream_previous_pkt_time = msg->timestamp; | ||
| 478 | - | ||
| 479 | return ret; | 524 | return ret; |
| 480 | } | 525 | } |
| 481 | 526 | ||
| 482 | -int SrsDvrPlan::write_flv_header() | 527 | +int SrsDvrPlan::on_video(SrsSharedPtrMessage* __video) |
| 483 | { | 528 | { |
| 484 | int ret = ERROR_SUCCESS; | 529 | int ret = ERROR_SUCCESS; |
| 485 | 530 | ||
| 486 | - if ((ret = enc->write_header()) != ERROR_SUCCESS) { | ||
| 487 | - srs_error("write flv header failed. ret=%d", ret); | 531 | + if (!dvr_enabled) { |
| 532 | + return ret; | ||
| 533 | + } | ||
| 534 | + | ||
| 535 | + if ((ret = segment->write_video(__video)) != ERROR_SUCCESS) { | ||
| 488 | return ret; | 536 | return ret; |
| 489 | } | 537 | } |
| 490 | 538 | ||
| @@ -511,6 +559,32 @@ SrsDvrSessionPlan::~SrsDvrSessionPlan() | @@ -511,6 +559,32 @@ SrsDvrSessionPlan::~SrsDvrSessionPlan() | ||
| 511 | { | 559 | { |
| 512 | } | 560 | } |
| 513 | 561 | ||
| 562 | +int SrsDvrSessionPlan::on_publish() | ||
| 563 | +{ | ||
| 564 | + int ret = ERROR_SUCCESS; | ||
| 565 | + | ||
| 566 | + // support multiple publish. | ||
| 567 | + if (dvr_enabled) { | ||
| 568 | + return ret; | ||
| 569 | + } | ||
| 570 | + | ||
| 571 | + if (!_srs_config->get_dvr_enabled(req->vhost)) { | ||
| 572 | + return ret; | ||
| 573 | + } | ||
| 574 | + | ||
| 575 | + if ((ret = segment->close()) != ERROR_SUCCESS) { | ||
| 576 | + return ret; | ||
| 577 | + } | ||
| 578 | + | ||
| 579 | + if ((ret = segment->open()) != ERROR_SUCCESS) { | ||
| 580 | + return ret; | ||
| 581 | + } | ||
| 582 | + | ||
| 583 | + dvr_enabled = true; | ||
| 584 | + | ||
| 585 | + return ret; | ||
| 586 | +} | ||
| 587 | + | ||
| 514 | void SrsDvrSessionPlan::on_unpublish() | 588 | void SrsDvrSessionPlan::on_unpublish() |
| 515 | { | 589 | { |
| 516 | // support multiple publish. | 590 | // support multiple publish. |
| @@ -519,7 +593,7 @@ void SrsDvrSessionPlan::on_unpublish() | @@ -519,7 +593,7 @@ void SrsDvrSessionPlan::on_unpublish() | ||
| 519 | } | 593 | } |
| 520 | 594 | ||
| 521 | // ignore error. | 595 | // ignore error. |
| 522 | - int ret = flv_close(); | 596 | + int ret = segment->close(); |
| 523 | if (ret != ERROR_SUCCESS) { | 597 | if (ret != ERROR_SUCCESS) { |
| 524 | srs_warn("ignore flv close error. ret=%d", ret); | 598 | srs_warn("ignore flv close error. ret=%d", ret); |
| 525 | } | 599 | } |
| @@ -558,65 +632,86 @@ int SrsDvrSegmentPlan::on_publish() | @@ -558,65 +632,86 @@ int SrsDvrSegmentPlan::on_publish() | ||
| 558 | { | 632 | { |
| 559 | int ret = ERROR_SUCCESS; | 633 | int ret = ERROR_SUCCESS; |
| 560 | 634 | ||
| 561 | - // if already opened, continue to dvr. | ||
| 562 | - // the segment plan maybe keep running longer than the encoder. | ||
| 563 | - // for example, segment running, encoder restart, | ||
| 564 | - // the segment plan will just continue going and donot open new segment. | ||
| 565 | - if (fs->is_open()) { | ||
| 566 | - dvr_enabled = true; | 635 | + // support multiple publish. |
| 636 | + if (dvr_enabled) { | ||
| 567 | return ret; | 637 | return ret; |
| 568 | } | 638 | } |
| 569 | - | ||
| 570 | - return SrsDvrPlan::on_publish(); | 639 | + |
| 640 | + if (!_srs_config->get_dvr_enabled(req->vhost)) { | ||
| 641 | + return ret; | ||
| 642 | + } | ||
| 643 | + | ||
| 644 | + if ((ret = segment->close()) != ERROR_SUCCESS) { | ||
| 645 | + return ret; | ||
| 646 | + } | ||
| 647 | + | ||
| 648 | + if ((ret = segment->open()) != ERROR_SUCCESS) { | ||
| 649 | + return ret; | ||
| 650 | + } | ||
| 651 | + | ||
| 652 | + dvr_enabled = true; | ||
| 653 | + | ||
| 654 | + return ret; | ||
| 571 | } | 655 | } |
| 572 | 656 | ||
| 573 | void SrsDvrSegmentPlan::on_unpublish() | 657 | void SrsDvrSegmentPlan::on_unpublish() |
| 574 | { | 658 | { |
| 575 | - // support multiple publish. | ||
| 576 | - if (!dvr_enabled) { | ||
| 577 | - return; | ||
| 578 | - } | ||
| 579 | - dvr_enabled = false; | ||
| 580 | } | 659 | } |
| 581 | 660 | ||
| 582 | int SrsDvrSegmentPlan::on_audio(SrsSharedPtrMessage* audio) | 661 | int SrsDvrSegmentPlan::on_audio(SrsSharedPtrMessage* audio) |
| 583 | { | 662 | { |
| 663 | + int ret = ERROR_SUCCESS; | ||
| 664 | + | ||
| 584 | if (SrsFlvCodec::audio_is_sequence_header(audio->payload, audio->size)) { | 665 | if (SrsFlvCodec::audio_is_sequence_header(audio->payload, audio->size)) { |
| 585 | srs_freep(sh_audio); | 666 | srs_freep(sh_audio); |
| 586 | sh_audio = audio->copy(); | 667 | sh_audio = audio->copy(); |
| 587 | } | 668 | } |
| 669 | + | ||
| 670 | + if ((ret = update_duration(audio)) != ERROR_SUCCESS) { | ||
| 671 | + return ret; | ||
| 672 | + } | ||
| 588 | 673 | ||
| 589 | - return SrsDvrPlan::on_audio(audio); | 674 | + if ((ret = SrsDvrPlan::on_audio(audio)) != ERROR_SUCCESS) { |
| 675 | + return ret; | ||
| 676 | + } | ||
| 677 | + | ||
| 678 | + return ret; | ||
| 590 | } | 679 | } |
| 591 | 680 | ||
| 592 | int SrsDvrSegmentPlan::on_video(SrsSharedPtrMessage* video) | 681 | int SrsDvrSegmentPlan::on_video(SrsSharedPtrMessage* video) |
| 593 | { | 682 | { |
| 683 | + int ret = ERROR_SUCCESS; | ||
| 684 | + | ||
| 594 | if (SrsFlvCodec::video_is_sequence_header(video->payload, video->size)) { | 685 | if (SrsFlvCodec::video_is_sequence_header(video->payload, video->size)) { |
| 595 | srs_freep(sh_video); | 686 | srs_freep(sh_video); |
| 596 | sh_video = video->copy(); | 687 | sh_video = video->copy(); |
| 597 | } | 688 | } |
| 689 | + | ||
| 690 | + if ((ret = update_duration(video)) != ERROR_SUCCESS) { | ||
| 691 | + return ret; | ||
| 692 | + } | ||
| 598 | 693 | ||
| 599 | - return SrsDvrPlan::on_video(video); | 694 | + if ((ret = SrsDvrPlan::on_video(video)) != ERROR_SUCCESS) { |
| 695 | + return ret; | ||
| 696 | + } | ||
| 697 | + | ||
| 698 | + return ret; | ||
| 600 | } | 699 | } |
| 601 | 700 | ||
| 602 | int SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg) | 701 | int SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg) |
| 603 | { | 702 | { |
| 604 | int ret = ERROR_SUCCESS; | 703 | int ret = ERROR_SUCCESS; |
| 605 | 704 | ||
| 606 | - if ((ret = SrsDvrPlan::update_duration(msg)) != ERROR_SUCCESS) { | ||
| 607 | - return ret; | ||
| 608 | - } | ||
| 609 | - | ||
| 610 | srs_assert(segment); | 705 | srs_assert(segment); |
| 611 | 706 | ||
| 612 | // ignore if duration ok. | 707 | // ignore if duration ok. |
| 613 | - if (segment_duration <= 0 || segment->duration < segment_duration) { | 708 | + if (segment_duration <= 0 || !segment->is_overflow(segment_duration)) { |
| 614 | return ret; | 709 | return ret; |
| 615 | } | 710 | } |
| 616 | 711 | ||
| 617 | // when wait keyframe, ignore if no frame arrived. | 712 | // when wait keyframe, ignore if no frame arrived. |
| 618 | // @see https://github.com/winlinvip/simple-rtmp-server/issues/177 | 713 | // @see https://github.com/winlinvip/simple-rtmp-server/issues/177 |
| 619 | - if (_srs_config->get_dvr_wait_keyframe(_req->vhost)) { | 714 | + if (_srs_config->get_dvr_wait_keyframe(req->vhost)) { |
| 620 | if (!msg->is_video()) { | 715 | if (!msg->is_video()) { |
| 621 | return ret; | 716 | return ret; |
| 622 | } | 717 | } |
| @@ -632,14 +727,12 @@ int SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg) | @@ -632,14 +727,12 @@ int SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg) | ||
| 632 | } | 727 | } |
| 633 | 728 | ||
| 634 | // reap segment | 729 | // reap segment |
| 635 | - if ((ret = flv_close()) != ERROR_SUCCESS) { | ||
| 636 | - segment->reset(); | 730 | + if ((ret = segment->close()) != ERROR_SUCCESS) { |
| 637 | return ret; | 731 | return ret; |
| 638 | } | 732 | } |
| 639 | - on_unpublish(); | ||
| 640 | 733 | ||
| 641 | // open new flv file | 734 | // open new flv file |
| 642 | - if ((ret = open_new_segment()) != ERROR_SUCCESS) { | 735 | + if ((ret = segment->open()) != ERROR_SUCCESS) { |
| 643 | return ret; | 736 | return ret; |
| 644 | } | 737 | } |
| 645 | 738 | ||
| @@ -654,9 +747,9 @@ int SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg) | @@ -654,9 +747,9 @@ int SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg) | ||
| 654 | return ret; | 747 | return ret; |
| 655 | } | 748 | } |
| 656 | 749 | ||
| 657 | -SrsDvr::SrsDvr(SrsSource* source) | 750 | +SrsDvr::SrsDvr(SrsSource* s) |
| 658 | { | 751 | { |
| 659 | - _source = source; | 752 | + source = s; |
| 660 | plan = NULL; | 753 | plan = NULL; |
| 661 | } | 754 | } |
| 662 | 755 | ||
| @@ -665,21 +758,21 @@ SrsDvr::~SrsDvr() | @@ -665,21 +758,21 @@ SrsDvr::~SrsDvr() | ||
| 665 | srs_freep(plan); | 758 | srs_freep(plan); |
| 666 | } | 759 | } |
| 667 | 760 | ||
| 668 | -int SrsDvr::initialize(SrsRequest* req) | 761 | +int SrsDvr::initialize(SrsRequest* r) |
| 669 | { | 762 | { |
| 670 | int ret = ERROR_SUCCESS; | 763 | int ret = ERROR_SUCCESS; |
| 671 | 764 | ||
| 672 | srs_freep(plan); | 765 | srs_freep(plan); |
| 673 | - plan = SrsDvrPlan::create_plan(req->vhost); | 766 | + plan = SrsDvrPlan::create_plan(r->vhost); |
| 674 | 767 | ||
| 675 | - if ((ret = plan->initialize(_source, req)) != ERROR_SUCCESS) { | 768 | + if ((ret = plan->initialize(source, r)) != ERROR_SUCCESS) { |
| 676 | return ret; | 769 | return ret; |
| 677 | } | 770 | } |
| 678 | 771 | ||
| 679 | return ret; | 772 | return ret; |
| 680 | } | 773 | } |
| 681 | 774 | ||
| 682 | -int SrsDvr::on_publish(SrsRequest* /*req*/) | 775 | +int SrsDvr::on_publish(SrsRequest* /*r*/) |
| 683 | { | 776 | { |
| 684 | int ret = ERROR_SUCCESS; | 777 | int ret = ERROR_SUCCESS; |
| 685 | 778 | ||
| @@ -695,11 +788,11 @@ void SrsDvr::on_unpublish() | @@ -695,11 +788,11 @@ void SrsDvr::on_unpublish() | ||
| 695 | plan->on_unpublish(); | 788 | plan->on_unpublish(); |
| 696 | } | 789 | } |
| 697 | 790 | ||
| 698 | -int SrsDvr::on_meta_data(SrsOnMetaDataPacket* metadata) | 791 | +int SrsDvr::on_meta_data(SrsOnMetaDataPacket* m) |
| 699 | { | 792 | { |
| 700 | int ret = ERROR_SUCCESS; | 793 | int ret = ERROR_SUCCESS; |
| 701 | 794 | ||
| 702 | - if ((ret = plan->on_meta_data(metadata)) != ERROR_SUCCESS) { | 795 | + if ((ret = plan->on_meta_data(m)) != ERROR_SUCCESS) { |
| 703 | return ret; | 796 | return ret; |
| 704 | } | 797 | } |
| 705 | 798 |
| @@ -41,16 +41,34 @@ class SrsOnMetaDataPacket; | @@ -41,16 +41,34 @@ class SrsOnMetaDataPacket; | ||
| 41 | class SrsSharedPtrMessage; | 41 | class SrsSharedPtrMessage; |
| 42 | class SrsFileWriter; | 42 | class SrsFileWriter; |
| 43 | class SrsFlvEncoder; | 43 | class SrsFlvEncoder; |
| 44 | +class SrsDvrPlan; | ||
| 44 | 45 | ||
| 45 | #include <srs_app_source.hpp> | 46 | #include <srs_app_source.hpp> |
| 46 | #include <srs_app_reload.hpp> | 47 | #include <srs_app_reload.hpp> |
| 47 | 48 | ||
| 48 | /** | 49 | /** |
| 49 | * a piece of flv segment. | 50 | * a piece of flv segment. |
| 51 | +* when open segment, support start at 0 or not. | ||
| 50 | */ | 52 | */ |
| 51 | -class SrsFlvSegment | 53 | +class SrsFlvSegment : public ISrsReloadHandler |
| 52 | { | 54 | { |
| 53 | -public: | 55 | +private: |
| 56 | + SrsSource* source; | ||
| 57 | + SrsRequest* req; | ||
| 58 | + SrsDvrPlan* plan; | ||
| 59 | +private: | ||
| 60 | + /** | ||
| 61 | + * the underlayer dvr stream. | ||
| 62 | + * if close, the flv is reap and closed. | ||
| 63 | + * if open, new flv file is crote. | ||
| 64 | + */ | ||
| 65 | + SrsFlvEncoder* enc; | ||
| 66 | + SrsRtmpJitter* jitter; | ||
| 67 | + SrsRtmpJitterAlgorithm jitter_algorithm; | ||
| 68 | + SrsFileWriter* fs; | ||
| 69 | +private: | ||
| 70 | + std::string tmp_flv_file; | ||
| 71 | +private: | ||
| 54 | /** | 72 | /** |
| 55 | * current segment flv file path. | 73 | * current segment flv file path. |
| 56 | */ | 74 | */ |
| @@ -81,10 +99,56 @@ public: | @@ -81,10 +99,56 @@ public: | ||
| 81 | */ | 99 | */ |
| 82 | int64_t stream_previous_pkt_time; | 100 | int64_t stream_previous_pkt_time; |
| 83 | public: | 101 | public: |
| 84 | - SrsFlvSegment(); | 102 | + SrsFlvSegment(SrsDvrPlan* p); |
| 85 | virtual ~SrsFlvSegment(); | 103 | virtual ~SrsFlvSegment(); |
| 86 | public: | 104 | public: |
| 87 | - virtual void reset(); | 105 | + /** |
| 106 | + * initialize the segment. | ||
| 107 | + */ | ||
| 108 | + virtual int initialize(SrsSource* s, SrsRequest* r); | ||
| 109 | + /** | ||
| 110 | + * whether segment is overflow. | ||
| 111 | + */ | ||
| 112 | + virtual bool is_overflow(int64_t max_duration); | ||
| 113 | + /** | ||
| 114 | + * open new segment file, timestamp start at 0 for fresh flv file. | ||
| 115 | + * @remark ignore when already open. | ||
| 116 | + */ | ||
| 117 | + virtual int open(); | ||
| 118 | + /** | ||
| 119 | + * close current segment. | ||
| 120 | + * @remark ignore when already closed. | ||
| 121 | + */ | ||
| 122 | + virtual int close(); | ||
| 123 | + /** | ||
| 124 | + * write the metadata to segment. | ||
| 125 | + */ | ||
| 126 | + virtual int write_metadata(SrsOnMetaDataPacket* metadata); | ||
| 127 | + /** | ||
| 128 | + * @param __audio, directly ptr, copy it if need to save it. | ||
| 129 | + */ | ||
| 130 | + virtual int write_audio(SrsSharedPtrMessage* __audio); | ||
| 131 | + /** | ||
| 132 | + * @param __video, directly ptr, copy it if need to save it. | ||
| 133 | + */ | ||
| 134 | + virtual int write_video(SrsSharedPtrMessage* __video); | ||
| 135 | +private: | ||
| 136 | + /** | ||
| 137 | + * generate the flv segment path. | ||
| 138 | + */ | ||
| 139 | + virtual std::string generate_path(); | ||
| 140 | + /** | ||
| 141 | + * create flv jitter. load jitter when flv exists. | ||
| 142 | + * @param loads_from_flv whether loads the jitter from exists flv file. | ||
| 143 | + */ | ||
| 144 | + virtual int create_jitter(bool loads_from_flv); | ||
| 145 | + /** | ||
| 146 | + * when update the duration of segment by rtmp msg. | ||
| 147 | + */ | ||
| 148 | + virtual int on_update_duration(SrsSharedPtrMessage* msg); | ||
| 149 | +// interface ISrsReloadHandler | ||
| 150 | +public: | ||
| 151 | + virtual int on_reload_vhost_dvr(std::string vhost); | ||
| 88 | }; | 152 | }; |
| 89 | 153 | ||
| 90 | /** | 154 | /** |
| @@ -94,32 +158,25 @@ public: | @@ -94,32 +158,25 @@ public: | ||
| 94 | * 2. reap flv: when to reap the flv and start new piece. | 158 | * 2. reap flv: when to reap the flv and start new piece. |
| 95 | */ | 159 | */ |
| 96 | // TODO: FIXME: the plan is too fat, refine me. | 160 | // TODO: FIXME: the plan is too fat, refine me. |
| 97 | -class SrsDvrPlan : public ISrsReloadHandler | 161 | +class SrsDvrPlan |
| 98 | { | 162 | { |
| 99 | -private: | ||
| 100 | - /** | ||
| 101 | - * the underlayer dvr stream. | ||
| 102 | - * if close, the flv is reap and closed. | ||
| 103 | - * if open, new flv file is crote. | ||
| 104 | - */ | ||
| 105 | - SrsFlvEncoder* enc; | ||
| 106 | - SrsSource* _source; | ||
| 107 | - SrsRtmpJitter* jitter; | ||
| 108 | - SrsRtmpJitterAlgorithm jitter_algorithm; | 163 | +public: |
| 164 | + friend class SrsFlvSegment; | ||
| 109 | protected: | 165 | protected: |
| 166 | + SrsSource* source; | ||
| 167 | + SrsRequest* req; | ||
| 110 | SrsFlvSegment* segment; | 168 | SrsFlvSegment* segment; |
| 111 | - SrsRequest* _req; | ||
| 112 | bool dvr_enabled; | 169 | bool dvr_enabled; |
| 113 | - SrsFileWriter* fs; | ||
| 114 | -private: | ||
| 115 | - std::string tmp_flv_file; | ||
| 116 | public: | 170 | public: |
| 117 | SrsDvrPlan(); | 171 | SrsDvrPlan(); |
| 118 | virtual ~SrsDvrPlan(); | 172 | virtual ~SrsDvrPlan(); |
| 119 | public: | 173 | public: |
| 120 | - virtual int initialize(SrsSource* source, SrsRequest* req); | ||
| 121 | - virtual int on_publish(); | 174 | + virtual int initialize(SrsSource* s, SrsRequest* r); |
| 175 | + virtual int on_publish() = 0; | ||
| 122 | virtual void on_unpublish() = 0; | 176 | virtual void on_unpublish() = 0; |
| 177 | + /** | ||
| 178 | + * when got metadata. | ||
| 179 | + */ | ||
| 123 | virtual int on_meta_data(SrsOnMetaDataPacket* metadata); | 180 | virtual int on_meta_data(SrsOnMetaDataPacket* metadata); |
| 124 | /** | 181 | /** |
| 125 | * @param __audio, directly ptr, copy it if need to save it. | 182 | * @param __audio, directly ptr, copy it if need to save it. |
| @@ -129,15 +186,7 @@ public: | @@ -129,15 +186,7 @@ public: | ||
| 129 | * @param __video, directly ptr, copy it if need to save it. | 186 | * @param __video, directly ptr, copy it if need to save it. |
| 130 | */ | 187 | */ |
| 131 | virtual int on_video(SrsSharedPtrMessage* __video); | 188 | virtual int on_video(SrsSharedPtrMessage* __video); |
| 132 | -// interface ISrsReloadHandler | ||
| 133 | -public: | ||
| 134 | - virtual int on_reload_vhost_dvr(std::string vhost); | ||
| 135 | protected: | 189 | protected: |
| 136 | - virtual int flv_open(std::string stream, std::string path); | ||
| 137 | - virtual int flv_close(); | ||
| 138 | - virtual int open_new_segment(); | ||
| 139 | - virtual int update_duration(SrsSharedPtrMessage* msg); | ||
| 140 | - virtual int write_flv_header(); | ||
| 141 | virtual int on_dvr_request_sh(); | 190 | virtual int on_dvr_request_sh(); |
| 142 | virtual int on_video_keyframe(); | 191 | virtual int on_video_keyframe(); |
| 143 | virtual int64_t filter_timestamp(int64_t timestamp); | 192 | virtual int64_t filter_timestamp(int64_t timestamp); |
| @@ -154,6 +203,7 @@ public: | @@ -154,6 +203,7 @@ public: | ||
| 154 | SrsDvrSessionPlan(); | 203 | SrsDvrSessionPlan(); |
| 155 | virtual ~SrsDvrSessionPlan(); | 204 | virtual ~SrsDvrSessionPlan(); |
| 156 | public: | 205 | public: |
| 206 | + virtual int on_publish(); | ||
| 157 | virtual void on_unpublish(); | 207 | virtual void on_unpublish(); |
| 158 | }; | 208 | }; |
| 159 | 209 | ||
| @@ -193,11 +243,11 @@ private: | @@ -193,11 +243,11 @@ private: | ||
| 193 | class SrsDvr | 243 | class SrsDvr |
| 194 | { | 244 | { |
| 195 | private: | 245 | private: |
| 196 | - SrsSource* _source; | 246 | + SrsSource* source; |
| 197 | private: | 247 | private: |
| 198 | SrsDvrPlan* plan; | 248 | SrsDvrPlan* plan; |
| 199 | public: | 249 | public: |
| 200 | - SrsDvr(SrsSource* source); | 250 | + SrsDvr(SrsSource* s); |
| 201 | virtual ~SrsDvr(); | 251 | virtual ~SrsDvr(); |
| 202 | public: | 252 | public: |
| 203 | /** | 253 | /** |
| @@ -205,12 +255,12 @@ public: | @@ -205,12 +255,12 @@ public: | ||
| 205 | * when system initialize(encoder publish at first time, or reload), | 255 | * when system initialize(encoder publish at first time, or reload), |
| 206 | * initialize the dvr will reinitialize the plan, the whole dvr framework. | 256 | * initialize the dvr will reinitialize the plan, the whole dvr framework. |
| 207 | */ | 257 | */ |
| 208 | - virtual int initialize(SrsRequest* req); | 258 | + virtual int initialize(SrsRequest* r); |
| 209 | /** | 259 | /** |
| 210 | * publish stream event, | 260 | * publish stream event, |
| 211 | * when encoder start to publish RTMP stream. | 261 | * when encoder start to publish RTMP stream. |
| 212 | */ | 262 | */ |
| 213 | - virtual int on_publish(SrsRequest* req); | 263 | + virtual int on_publish(SrsRequest* r); |
| 214 | /** | 264 | /** |
| 215 | * the unpublish event., | 265 | * the unpublish event., |
| 216 | * when encoder stop(unpublish) to publish RTMP stream. | 266 | * when encoder stop(unpublish) to publish RTMP stream. |
| @@ -219,7 +269,7 @@ public: | @@ -219,7 +269,7 @@ public: | ||
| 219 | /** | 269 | /** |
| 220 | * get some information from metadata, it's optinal. | 270 | * get some information from metadata, it's optinal. |
| 221 | */ | 271 | */ |
| 222 | - virtual int on_meta_data(SrsOnMetaDataPacket* metadata); | 272 | + virtual int on_meta_data(SrsOnMetaDataPacket* m); |
| 223 | /** | 273 | /** |
| 224 | * mux the audio packets to dvr. | 274 | * mux the audio packets to dvr. |
| 225 | * @param __audio, directly ptr, copy it if need to save it. | 275 | * @param __audio, directly ptr, copy it if need to save it. |
| @@ -106,7 +106,8 @@ int SrsGoApiV1::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) | @@ -106,7 +106,8 @@ int SrsGoApiV1::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) | ||
| 106 | << __SRS_JFIELD_STR("authors", "the primary authors and contributors") << __SRS_JFIELD_CONT | 106 | << __SRS_JFIELD_STR("authors", "the primary authors and contributors") << __SRS_JFIELD_CONT |
| 107 | << __SRS_JFIELD_STR("requests", "the request itself, for http debug") << __SRS_JFIELD_CONT | 107 | << __SRS_JFIELD_STR("requests", "the request itself, for http debug") << __SRS_JFIELD_CONT |
| 108 | << __SRS_JFIELD_STR("vhosts", "dumps vhost to json") << __SRS_JFIELD_CONT | 108 | << __SRS_JFIELD_STR("vhosts", "dumps vhost to json") << __SRS_JFIELD_CONT |
| 109 | - << __SRS_JFIELD_STR("streams", "dumps streams to json") | 109 | + << __SRS_JFIELD_STR("streams", "dumps streams to json") << __SRS_JFIELD_CONT |
| 110 | + << __SRS_JFIELD_STR("dvrs", "query or control the dvr plan") | ||
| 110 | << __SRS_JOBJECT_END | 111 | << __SRS_JOBJECT_END |
| 111 | << __SRS_JOBJECT_END; | 112 | << __SRS_JOBJECT_END; |
| 112 | 113 |
-
请 注册 或 登录 后发表评论