From 67e1988b81dfdb6af959805c08ea1120d081b618 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 24 Apr 2014 16:32:19 +0800 Subject: [PATCH] support live flashP2P(integrated by chnvideo VDN). 0.9.75 --- README.md | 2 + trunk/conf/full.conf | 14 ++- trunk/src/app/srs_app_config.cpp | 4 +- trunk/src/app/srs_app_config.hpp | 2 +- trunk/src/app/srs_app_dvr.cpp | 146 ++++++++++++++++++++------- trunk/src/app/srs_app_dvr.hpp | 29 ++++-- trunk/src/app/srs_app_http_hooks.cpp | 17 ++-- trunk/src/app/srs_app_http_hooks.hpp | 4 +- trunk/src/app/srs_app_source.cpp | 4 +- trunk/src/app/srs_app_source.hpp | 2 +- trunk/src/core/srs_core.hpp | 2 +- 11 files changed, 164 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index 8228c60d9..c4c923704 100755 --- a/README.md +++ b/README.md @@ -157,6 +157,7 @@ Supported operating systems and hardware: 1. Support stream ingester using ffmpeg. 1. Support ingest RTSP(RTP, SDP) stream to RTMP. 1. Support dvr(record live to flv file for vod) +1. Support live flashP2P(integrated by chnvideo VDN). 1. [plan] Support file to hls vod stream. 1. [plan] Support system full utest on gtest. 1. [plan] Support RTMP edge server, push/pull stream from any RTMP server @@ -184,6 +185,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v1.0, 2014-04-24, support live flashP2P(integrated by chnvideo VDN). 0.9.75 * v1.0, 2014-04-21, support android app to start srs for internal edge. 0.9.72 * v1.0, 2014-04-19, support tool over srs-librtmp to ingest flv/rtmp. 0.9.71 * v1.0, 2014-04-17, support dvr(record live to flv file for vod). 0.9.69 diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 5a0c1df85..fdae7eeee 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -97,7 +97,7 @@ vhost dvr.srs.com { # start to record to file when encoder publish, # reap flv according by specified dvr_plan. # http callbacks: - # @see http callback on_dvr_keyframe on http_hooks section. + # @see http callback on_dvr_reap_flv on http_hooks section. dvr { # whether enabled dvr features # default: off @@ -325,16 +325,22 @@ vhost hooks.callback.srs.com { # when dvr got an keyframe, call the hook, # the request in the POST data string is a object encode by json: # { - # "action": "on_dvr_keyframe", + # "action": "on_dvr_reap_flv", # "vhost": "video.test.com", "app": "live", - # "stream": "livestream" + # "stream": "livestream", + # "segment": { + # "cwd": "/usr/local/srs", + # "path": "./objs/nginx/html/live/livestream.1398315892865.flv", + # "duration": 1001, "offset":0, + # "has_keyframe": true, "pts":1398315895958 + # } # } # if valid, the hook must return HTTP code 200(Stauts OK) and response # an int value specifies the error code(0 corresponding to success): # 0 # support multiple api hooks, format: # on_stop http://xxx/api0 http://xxx/api1 http://xxx/apiN - on_dvr_keyframe http://127.0.0.1:8085/api/v1/dvrs http://localhost:8085/api/v1/dvrs; + on_dvr_reap_flv http://127.0.0.1:8085/api/v1/dvrs http://localhost:8085/api/v1/dvrs; } } diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 986cc644a..bba0d4322 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -1560,7 +1560,7 @@ SrsConfDirective* SrsConfig::get_vhost_on_stop(string vhost) return conf->get("on_stop"); } -SrsConfDirective* SrsConfig::get_vhost_on_dvr_keyframe(string vhost) +SrsConfDirective* SrsConfig::get_vhost_on_dvr_reap_flv(string vhost) { SrsConfDirective* conf = get_vhost(vhost); @@ -1578,7 +1578,7 @@ SrsConfDirective* SrsConfig::get_vhost_on_dvr_keyframe(string vhost) return NULL; } - return conf->get("on_dvr_keyframe"); + return conf->get("on_dvr_reap_flv"); } bool SrsConfig::get_vhost_enabled(string vhost) diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 5fecd9d75..1042e2a2b 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -175,7 +175,7 @@ public: virtual SrsConfDirective* get_vhost_on_unpublish(std::string vhost); virtual SrsConfDirective* get_vhost_on_play(std::string vhost); virtual SrsConfDirective* get_vhost_on_stop(std::string vhost); - virtual SrsConfDirective* get_vhost_on_dvr_keyframe(std::string vhost); + virtual SrsConfDirective* get_vhost_on_dvr_reap_flv(std::string vhost); virtual bool get_gop_cache(std::string vhost); virtual bool get_atc(std::string vhost); virtual double get_queue_length(std::string vhost); diff --git a/trunk/src/app/srs_app_dvr.cpp b/trunk/src/app/srs_app_dvr.cpp index 03e8c9947..ff92ac3da 100644 --- a/trunk/src/app/srs_app_dvr.cpp +++ b/trunk/src/app/srs_app_dvr.cpp @@ -138,6 +138,11 @@ int SrsFileStream::write(void* buf, size_t count, ssize_t* pnwrite) return ret; } +int64_t SrsFileStream::tellg() +{ + return (int64_t)::lseek(fd, 0, SEEK_CUR); +} + SrsFlvEncoder::SrsFlvEncoder() { _fs = NULL; @@ -209,10 +214,12 @@ int SrsFlvEncoder::write_metadata(char* data, int size) return ret; } -int SrsFlvEncoder::write_audio(int32_t timestamp, char* data, int size) +int SrsFlvEncoder::write_audio(int64_t timestamp, char* data, int size) { int ret = ERROR_SUCCESS; + timestamp &= 0x7fffffff; + static char tag_header[] = { (char)8, // TagType UB [5], 8 = audio (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message. @@ -238,10 +245,12 @@ int SrsFlvEncoder::write_audio(int32_t timestamp, char* data, int size) return ret; } -int SrsFlvEncoder::write_video(int32_t timestamp, char* data, int size) +int SrsFlvEncoder::write_video(int64_t timestamp, char* data, int size) { int ret = ERROR_SUCCESS; + timestamp &= 0x7fffffff; + static char tag_header[] = { (char)9, // TagType UB [5], 9 = video (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message. @@ -303,6 +312,7 @@ SrsFlvSegment::SrsFlvSegment() has_keyframe = false; duration = 0; starttime = -1; + sequence_header_offset = 0; stream_starttime = 0; stream_previous_pkt_time = -1; stream_duration = 0; @@ -313,6 +323,7 @@ void SrsFlvSegment::reset() has_keyframe = false; starttime = -1; duration = 0; + sequence_header_offset = 0; } SrsDvrPlan::SrsDvrPlan() @@ -396,14 +407,32 @@ int SrsDvrPlan::open_new_segment() } dvr_enabled = true; + return ret; +} + +int SrsDvrPlan::on_dvr_request_sh() +{ + int ret = ERROR_SUCCESS; + // the dvr is enabled, notice the source to push the data. - if ((ret = _source->on_dvr_start()) != ERROR_SUCCESS) { + if ((ret = _source->on_dvr_request_sh()) != ERROR_SUCCESS) { return ret; } return ret; } +int SrsDvrPlan::on_video_keyframe() +{ + int ret = ERROR_SUCCESS; + return ret; +} + +int64_t SrsDvrPlan::filter_timestamp(int64_t timestamp) +{ + return timestamp; +} + int SrsDvrPlan::on_meta_data(SrsOnMetaDataPacket* metadata) { int ret = ERROR_SUCCESS; @@ -438,9 +467,9 @@ int SrsDvrPlan::on_audio(SrsSharedPtrMessage* audio) return ret; } - int32_t timestamp = audio->header.timestamp; char* payload = (char*)audio->payload; int size = (int)audio->size; + int64_t timestamp = filter_timestamp(audio->header.timestamp); if ((ret = enc->write_audio(timestamp, payload, size)) != ERROR_SUCCESS) { return ret; } @@ -460,25 +489,31 @@ int SrsDvrPlan::on_video(SrsSharedPtrMessage* video) return ret; } + char* payload = (char*)video->payload; + int size = (int)video->size; + +#ifdef SRS_AUTO_HTTP_CALLBACK + bool is_key_frame = SrsCodec::video_is_h264((int8_t*)payload, size) + && SrsCodec::video_is_keyframe((int8_t*)payload, size) + && !SrsCodec::video_is_sequence_header((int8_t*)payload, size); + if (is_key_frame) { + segment->has_keyframe = true; + if ((ret = on_video_keyframe()) != ERROR_SUCCESS) { + return ret; + } + } + srs_verbose("dvr video is key: %d", is_key_frame); +#endif + if ((jitter->correct(video, 0, 0)) != ERROR_SUCCESS) { return ret; } - int32_t timestamp = video->header.timestamp; - char* payload = (char*)video->payload; - int size = (int)video->size; + int32_t timestamp = filter_timestamp(video->header.timestamp); if ((ret = enc->write_video(timestamp, payload, size)) != ERROR_SUCCESS) { return ret; } -#ifdef SRS_AUTO_HTTP_CALLBACK - bool is_key_frame = SrsCodec::video_is_keyframe((int8_t*)payload, size); - if (is_key_frame) { - segment->has_keyframe = true; - } - srs_verbose("dvr video is key: %d", is_key_frame); -#endif - if ((ret = update_duration(video)) != ERROR_SUCCESS) { return ret; } @@ -503,8 +538,7 @@ int SrsDvrPlan::flv_open(string stream, string path) return ret; } - if ((ret = enc->write_header()) != ERROR_SUCCESS) { - srs_error("write flv header for file %s failed. ret=%d", path.c_str(), ret); + if ((ret = write_flv_header()) != ERROR_SUCCESS) { return ret; } @@ -531,10 +565,8 @@ int SrsDvrPlan::flv_close() } #ifdef SRS_AUTO_HTTP_CALLBACK - if (segment->has_keyframe) { - if ((ret = on_dvr_keyframe()) != ERROR_SUCCESS) { - return ret; - } + if ((ret = on_dvr_reap_flv()) != ERROR_SUCCESS) { + return ret; } #endif @@ -568,21 +600,33 @@ int SrsDvrPlan::update_duration(SrsSharedPtrMessage* msg) return ret; } -int SrsDvrPlan::on_dvr_keyframe() +int SrsDvrPlan::write_flv_header() +{ + int ret = ERROR_SUCCESS; + + if ((ret = enc->write_header()) != ERROR_SUCCESS) { + srs_error("write flv header failed. ret=%d", ret); + return ret; + } + + return ret; +} + +int SrsDvrPlan::on_dvr_reap_flv() { int ret = ERROR_SUCCESS; #ifdef SRS_AUTO_HTTP_CALLBACK - // HTTP: on_dvr_keyframe - SrsConfDirective* on_dvr_keyframe = _srs_config->get_vhost_on_dvr_keyframe(_req->vhost); - if (!on_dvr_keyframe) { - srs_info("ignore the empty http callback: on_dvr_keyframe"); + // HTTP: on_dvr_reap_flv + SrsConfDirective* on_dvr_reap_flv = _srs_config->get_vhost_on_dvr_reap_flv(_req->vhost); + if (!on_dvr_reap_flv) { + srs_info("ignore the empty http callback: on_dvr_reap_flv"); return ret; } - for (int i = 0; i < (int)on_dvr_keyframe->args.size(); i++) { - std::string url = on_dvr_keyframe->args.at(i); - SrsHttpHooks::on_dvr_keyframe(url, _req, segment); + for (int i = 0; i < (int)on_dvr_reap_flv->args.size(); i++) { + std::string url = on_dvr_reap_flv->args.at(i); + SrsHttpHooks::on_dvr_reap_flv(url, _req, segment); } #endif @@ -705,7 +749,6 @@ int SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg) SrsDvrHssPlan::SrsDvrHssPlan() { segment_duration = -1; - start_deviation = 0; expect_reap_time = 0; } @@ -748,8 +791,6 @@ int SrsDvrHssPlan::on_publish() // expect reap flv time expect_reap_time = segment->stream_starttime + segment_duration; - // the start deviation used ensure the segment starttime in nature clock. - start_deviation = segment->stream_starttime % 1000; return ret; } @@ -763,6 +804,44 @@ void SrsDvrHssPlan::on_unpublish() dvr_enabled = false; } +int SrsDvrHssPlan::on_meta_data(SrsOnMetaDataPacket* /*metadata*/) +{ + int ret = ERROR_SUCCESS; + return ret; +} + +int SrsDvrHssPlan::write_flv_header() +{ + int ret = ERROR_SUCCESS; + return ret; +} + +int SrsDvrHssPlan::on_dvr_request_sh() +{ + int ret = ERROR_SUCCESS; + return ret; +} + +int SrsDvrHssPlan::on_video_keyframe() +{ + int ret = ERROR_SUCCESS; + + segment->sequence_header_offset = fs->tellg(); + if ((ret = SrsDvrPlan::on_dvr_request_sh()) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +int64_t SrsDvrHssPlan::filter_timestamp(int64_t timestamp) +{ + //return timestamp; + srs_assert(segment); + srs_verbose("filter timestamp from %"PRId64" to %"PRId64, timestamp, segment->stream_starttime + timestamp); + return segment->stream_starttime + timestamp; +} + int SrsDvrHssPlan::update_duration(SrsSharedPtrMessage* msg) { int ret = ERROR_SUCCESS; @@ -784,10 +863,9 @@ int SrsDvrHssPlan::update_duration(SrsSharedPtrMessage* msg) // reap if exceed atc expect time. if (segment->stream_starttime + segment->stream_duration > expect_reap_time) { srs_verbose("hss reap start=%"PRId64", duration=%"PRId64", expect=%"PRId64 - ", segment(start=%"PRId64", adjust=%"PRId64", duration=%"PRId64", file=%s", + ", segment(start=%"PRId64", duration=%"PRId64", file=%s", segment->stream_starttime, segment->stream_duration, expect_reap_time, segment->stream_starttime + segment->starttime, - segment->stream_starttime + segment->starttime - start_deviation, segment->duration, segment->path.c_str()); // update expect reap time diff --git a/trunk/src/app/srs_app_dvr.hpp b/trunk/src/app/srs_app_dvr.hpp index be521d583..e4041d468 100644 --- a/trunk/src/app/srs_app_dvr.hpp +++ b/trunk/src/app/srs_app_dvr.hpp @@ -62,6 +62,10 @@ public: * @param pnwrite, return the write size. NULL to ignore. */ virtual int write(void* buf, size_t count, ssize_t* pnwrite); + /** + * tell current offset of stream. + */ + virtual int64_t tellg(); }; /** @@ -101,8 +105,8 @@ public: /** * write audio/video packet. */ - virtual int write_audio(int32_t timestamp, char* data, int size); - virtual int write_video(int32_t timestamp, char* data, int size); + virtual int write_audio(int64_t timestamp, char* data, int size); + virtual int write_video(int64_t timestamp, char* data, int size); private: virtual int write_tag(char* header, int header_size, char* tag, int tag_size); }; @@ -122,6 +126,10 @@ public: */ bool has_keyframe; /** + * sequence header offset in file. + */ + int64_t sequence_header_offset; + /** * current segment starttime, RTMP pkt time. */ int64_t starttime; @@ -184,12 +192,15 @@ protected: virtual int flv_close(); virtual int open_new_segment(); virtual int update_duration(SrsSharedPtrMessage* msg); + virtual int write_flv_header(); + virtual int on_dvr_request_sh(); + virtual int on_video_keyframe(); + virtual int64_t filter_timestamp(int64_t timestamp); private: /** - * when srs reap the flv(close the segment), - * if has keyframe, notice the api. + * when srs reap the flv(close the segment), notice the api. */ - virtual int on_dvr_keyframe(); + virtual int on_dvr_reap_flv(); public: static SrsDvrPlan* create_plan(std::string vhost); }; @@ -233,8 +244,6 @@ class SrsDvrHssPlan : public SrsDvrPlan private: // in config, in ms int segment_duration; - // the deviation of starttime of the nature clock time. - int start_deviation; int64_t expect_reap_time; public: SrsDvrHssPlan(); @@ -243,6 +252,12 @@ public: virtual int initialize(SrsSource* source, SrsRequest* req); virtual int on_publish(); virtual void on_unpublish(); + virtual int on_meta_data(SrsOnMetaDataPacket* metadata); +protected: + virtual int write_flv_header(); + virtual int on_dvr_request_sh(); + virtual int on_video_keyframe(); + virtual int64_t filter_timestamp(int64_t timestamp); private: virtual int update_duration(SrsSharedPtrMessage* msg); }; diff --git a/trunk/src/app/srs_app_http_hooks.cpp b/trunk/src/app/srs_app_http_hooks.cpp index 0c8cb0891..d5cc8ba6e 100644 --- a/trunk/src/app/srs_app_http_hooks.cpp +++ b/trunk/src/app/srs_app_http_hooks.cpp @@ -459,7 +459,7 @@ void SrsHttpHooks::on_stop(string url, int client_id, string ip, SrsRequest* req return; } -void SrsHttpHooks::on_dvr_keyframe(string url, SrsRequest* req, SrsFlvSegment* segment) +void SrsHttpHooks::on_dvr_reap_flv(string url, SrsRequest* req, SrsFlvSegment* segment) { int ret = ERROR_SUCCESS; @@ -471,23 +471,24 @@ void SrsHttpHooks::on_dvr_keyframe(string url, SrsRequest* req, SrsFlvSegment* s SrsHttpUri uri; if ((ret = uri.initialize(url)) != ERROR_SUCCESS) { - srs_warn("http uri parse on_dvr_keyframe url failed, ignored. " + srs_warn("http uri parse on_dvr_reap_flv url failed, ignored. " "url=%s, ret=%d", url.c_str(), ret); return; } std::stringstream ss; ss << JOBJECT_START - << JFIELD_STR("action", "on_dvr_keyframe") << JFIELD_CONT + << JFIELD_STR("action", "on_dvr_reap_flv") << JFIELD_CONT << JFIELD_STR("vhost", req->vhost) << JFIELD_CONT << JFIELD_STR("app", req->app) << JFIELD_CONT << JFIELD_STR("stream", req->stream) << JFIELD_CONT << JFIELD_NAME("segment") << JOBJECT_START << JFIELD_STR("cwd", _srs_config->get_cwd()) << JFIELD_CONT << JFIELD_STR("path", segment->path) << JFIELD_CONT - << JFIELD_ORG("pts", segment->stream_starttime + segment->starttime) << JFIELD_CONT << JFIELD_ORG("duration", segment->duration) << JFIELD_CONT - << JFIELD_ORG("offset", 0) + << JFIELD_ORG("offset", segment->sequence_header_offset) << JFIELD_CONT + << JFIELD_ORG("has_keyframe", (segment->has_keyframe? "true":"false")) << JFIELD_CONT + << JFIELD_ORG("pts", segment->stream_starttime + segment->starttime) << JOBJECT_END << JOBJECT_END; std::string data = ss.str(); @@ -495,7 +496,7 @@ void SrsHttpHooks::on_dvr_keyframe(string url, SrsRequest* req, SrsFlvSegment* s SrsHttpClient http; if ((ret = http.post(&uri, data, res)) != ERROR_SUCCESS) { - srs_warn("http post on_dvr_keyframe uri failed, ignored. " + srs_warn("http post on_dvr_reap_flv uri failed, ignored. " "url=%s, request=%s, response=%s, ret=%d", url.c_str(), data.c_str(), res.c_str(), ret); return; @@ -503,12 +504,12 @@ void SrsHttpHooks::on_dvr_keyframe(string url, SrsRequest* req, SrsFlvSegment* s if (res.empty() || res != SRS_HTTP_RESPONSE_OK) { ret = ERROR_HTTP_DATA_INVLIAD; - srs_warn("http hook on_dvr_keyframe validate failed, ignored. " + srs_warn("http hook on_dvr_reap_flv validate failed, ignored. " "res=%s, ret=%d", res.c_str(), ret); return; } - srs_info("http hook on_dvr_keyframe success. " + srs_info("http hook on_dvr_reap_flv success. " "url=%s, request=%s, response=%s, ret=%d", url.c_str(), data.c_str(), res.c_str(), ret); diff --git a/trunk/src/app/srs_app_http_hooks.hpp b/trunk/src/app/srs_app_http_hooks.hpp index c51e51a80..a8eaeb2e6 100644 --- a/trunk/src/app/srs_app_http_hooks.hpp +++ b/trunk/src/app/srs_app_http_hooks.hpp @@ -124,12 +124,12 @@ public: static void on_stop(std::string url, int client_id, std::string ip, SrsRequest* req); public: /** - * on_dvr_keyframe hook, when dvr get keyframe. + * on_dvr_reap_flv hook, when dvr close flv file. * @param url the api server url, to process the event. * ignore if empty. * @param segment the current flv segment. */ - static void on_dvr_keyframe(std::string url, SrsRequest* req, SrsFlvSegment* segment); + static void on_dvr_reap_flv(std::string url, SrsRequest* req, SrsFlvSegment* segment); }; #endif diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index b798e51b4..02b206d6f 100644 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -721,14 +721,14 @@ int SrsSource::on_hls_start() return ret; } -int SrsSource::on_dvr_start() +int SrsSource::on_dvr_request_sh() { int ret = ERROR_SUCCESS; #ifdef SRS_AUTO_DVR // feed the dvr the metadata/sequence header, // when reload to start dvr, dvr will never get the sequence header in stream, - // use the SrsSource.on_dvr_start to push the sequence header to DVR. + // use the SrsSource.on_dvr_request_sh to push the sequence header to DVR. if (cache_metadata) { char* payload = (char*)cache_metadata->payload; int size = (int)cache_metadata->size; diff --git a/trunk/src/app/srs_app_source.hpp b/trunk/src/app/srs_app_source.hpp index ebbd77942..abd1143e6 100644 --- a/trunk/src/app/srs_app_source.hpp +++ b/trunk/src/app/srs_app_source.hpp @@ -288,7 +288,7 @@ public: // for the SrsHls to callback to request the sequence headers. virtual int on_hls_start(); // for the SrsDvr to callback to request the sequence headers. - virtual int on_dvr_start(); + virtual int on_dvr_request_sh(); public: virtual bool can_publish(); virtual int on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata); diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 5952a86f3..ec30f0abb 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR "0" #define VERSION_MINOR "9" -#define VERSION_REVISION "74" +#define VERSION_REVISION "75" #define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION // server info. #define RTMP_SIG_SRS_KEY "srs"