support dvr to hss. change to 0.9.74

pull/133/head
winlin 11 years ago
parent e271cb607f
commit 35f3a93239

@ -47,6 +47,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define SRS_CONF_DEFAULT_DVR_PATH "./objs/nginx/html" #define SRS_CONF_DEFAULT_DVR_PATH "./objs/nginx/html"
#define SRS_CONF_DEFAULT_DVR_PLAN_SESSION "session" #define SRS_CONF_DEFAULT_DVR_PLAN_SESSION "session"
#define SRS_CONF_DEFAULT_DVR_PLAN_SEGMENT "segment" #define SRS_CONF_DEFAULT_DVR_PLAN_SEGMENT "segment"
// chnvideo hss
#define SRS_CONF_DEFAULT_DVR_PLAN_HSS "hss"
#define SRS_CONF_DEFAULT_DVR_PLAN SRS_CONF_DEFAULT_DVR_PLAN_SESSION #define SRS_CONF_DEFAULT_DVR_PLAN SRS_CONF_DEFAULT_DVR_PLAN_SESSION
#define SRS_CONF_DEFAULT_DVR_DURATION 30 #define SRS_CONF_DEFAULT_DVR_DURATION 30
// in ms, for HLS aac sync time. // in ms, for HLS aac sync time.

@ -299,18 +299,20 @@ int SrsFlvEncoder::write_tag(char* header, int header_size, char* tag, int tag_s
SrsFlvSegment::SrsFlvSegment() SrsFlvSegment::SrsFlvSegment()
{ {
current_flv_path = ""; path = "";
segment_has_keyframe = false; has_keyframe = false;
duration = 0; duration = 0;
starttime = -1; starttime = -1;
stream_starttime = 0; stream_starttime = 0;
stream_previous_pkt_time = -1;
stream_duration = 0;
} }
void SrsFlvSegment::reset() void SrsFlvSegment::reset()
{ {
segment_has_keyframe = false; has_keyframe = false;
duration = 0;
starttime = -1; starttime = -1;
duration = 0;
} }
SrsDvrPlan::SrsDvrPlan() SrsDvrPlan::SrsDvrPlan()
@ -357,7 +359,7 @@ int SrsDvrPlan::on_publish()
return ret; return ret;
} }
// jitter. // jitter when publish, ensure whole stream start from 0.
srs_freep(jitter); srs_freep(jitter);
jitter = new SrsRtmpJitter(); jitter = new SrsRtmpJitter();
@ -365,7 +367,9 @@ int SrsDvrPlan::on_publish()
srs_update_system_time_ms(); srs_update_system_time_ms();
// when republish, stream starting. // when republish, stream starting.
segment->stream_previous_pkt_time = -1;
segment->stream_starttime = srs_get_system_time_ms(); segment->stream_starttime = srs_get_system_time_ms();
segment->stream_duration = 0;
if ((ret = open_new_segment()) != ERROR_SUCCESS) { if ((ret = open_new_segment()) != ERROR_SUCCESS) {
return ret; return ret;
@ -470,7 +474,7 @@ int SrsDvrPlan::on_video(SrsSharedPtrMessage* video)
#ifdef SRS_AUTO_HTTP_CALLBACK #ifdef SRS_AUTO_HTTP_CALLBACK
bool is_key_frame = SrsCodec::video_is_keyframe((int8_t*)payload, size); bool is_key_frame = SrsCodec::video_is_keyframe((int8_t*)payload, size);
if (is_key_frame) { if (is_key_frame) {
segment->segment_has_keyframe = true; segment->has_keyframe = true;
} }
srs_verbose("dvr video is key: %d", is_key_frame); srs_verbose("dvr video is key: %d", is_key_frame);
#endif #endif
@ -504,7 +508,7 @@ int SrsDvrPlan::flv_open(string stream, string path)
return ret; return ret;
} }
segment->current_flv_path = path; segment->path = path;
srs_trace("dvr stream %s to file %s", stream.c_str(), path.c_str()); srs_trace("dvr stream %s to file %s", stream.c_str(), path.c_str());
return ret; return ret;
@ -518,16 +522,16 @@ int SrsDvrPlan::flv_close()
return ret; return ret;
} }
std::string tmp_file = segment->current_flv_path + ".tmp"; std::string tmp_file = segment->path + ".tmp";
if (rename(tmp_file.c_str(), segment->current_flv_path.c_str()) < 0) { if (rename(tmp_file.c_str(), segment->path.c_str()) < 0) {
ret = ERROR_SYSTEM_FILE_RENAME; ret = ERROR_SYSTEM_FILE_RENAME;
srs_error("rename flv file failed, %s => %s. ret=%d", srs_error("rename flv file failed, %s => %s. ret=%d",
tmp_file.c_str(), segment->current_flv_path.c_str(), ret); tmp_file.c_str(), segment->path.c_str(), ret);
return ret; return ret;
} }
#ifdef SRS_AUTO_HTTP_CALLBACK #ifdef SRS_AUTO_HTTP_CALLBACK
if (segment->segment_has_keyframe) { if (segment->has_keyframe) {
if ((ret = on_dvr_keyframe()) != ERROR_SUCCESS) { if ((ret = on_dvr_keyframe()) != ERROR_SUCCESS) {
return ret; return ret;
} }
@ -540,13 +544,26 @@ int SrsDvrPlan::flv_close()
int SrsDvrPlan::update_duration(SrsSharedPtrMessage* msg) int SrsDvrPlan::update_duration(SrsSharedPtrMessage* msg)
{ {
int ret = ERROR_SUCCESS; int ret = ERROR_SUCCESS;
// we must assumpt that the stream timestamp is monotonically increase,
// that is, always use time jitter to correct the timestamp.
// foreach msg, collect the duration. // set the segment starttime at first time
if (segment->starttime < 0 || segment->starttime > msg->header.timestamp) { if (segment->starttime < 0) {
segment->starttime = msg->header.timestamp; segment->starttime = msg->header.timestamp;
} }
segment->duration += msg->header.timestamp - segment->starttime;
segment->starttime = msg->header.timestamp; // no previous packet or timestamp overflow.
if (segment->stream_previous_pkt_time < 0 || segment->stream_previous_pkt_time > msg->header.timestamp) {
segment->stream_previous_pkt_time = msg->header.timestamp;
}
// collect segment and stream duration, timestamp overflow is ok.
segment->duration += msg->header.timestamp - segment->stream_previous_pkt_time;
segment->stream_duration += msg->header.timestamp - segment->stream_previous_pkt_time;
// update previous packet time
segment->stream_previous_pkt_time = msg->header.timestamp;
return ret; return ret;
} }
@ -579,6 +596,8 @@ SrsDvrPlan* SrsDvrPlan::create_plan(string vhost)
return new SrsDvrSegmentPlan(); return new SrsDvrSegmentPlan();
} else if (plan == SRS_CONF_DEFAULT_DVR_PLAN_SESSION) { } else if (plan == SRS_CONF_DEFAULT_DVR_PLAN_SESSION) {
return new SrsDvrSessionPlan(); return new SrsDvrSessionPlan();
} else if (plan == SRS_CONF_DEFAULT_DVR_PLAN_HSS) {
return new SrsDvrHssPlan();
} else { } else {
return new SrsDvrSessionPlan(); return new SrsDvrSessionPlan();
} }
@ -683,6 +702,111 @@ int SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg)
return ret; return ret;
} }
SrsDvrHssPlan::SrsDvrHssPlan()
{
segment_duration = -1;
start_deviation = 0;
expect_reap_time = 0;
}
SrsDvrHssPlan::~SrsDvrHssPlan()
{
}
int SrsDvrHssPlan::initialize(SrsSource* source, SrsRequest* req)
{
int ret = ERROR_SUCCESS;
if ((ret = SrsDvrPlan::initialize(source, req)) != ERROR_SUCCESS) {
return ret;
}
// TODO: FIXME: support reload
segment_duration = _srs_config->get_dvr_duration(req->vhost);
// to ms
segment_duration *= 1000;
return ret;
}
int SrsDvrHssPlan::on_publish()
{
int ret = ERROR_SUCCESS;
// if already opened, continue to dvr.
// the segment plan maybe keep running longer than the encoder.
// for example, segment running, encoder restart,
// the segment plan will just continue going and donot open new segment.
if (fs->is_open()) {
dvr_enabled = true;
return ret;
}
if ((ret = SrsDvrPlan::on_publish()) != ERROR_SUCCESS) {
return ret;
}
// 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;
}
void SrsDvrHssPlan::on_unpublish()
{
// support multiple publish.
if (!dvr_enabled) {
return;
}
dvr_enabled = false;
}
int SrsDvrHssPlan::update_duration(SrsSharedPtrMessage* msg)
{
int ret = ERROR_SUCCESS;
if ((ret = SrsDvrPlan::update_duration(msg)) != ERROR_SUCCESS) {
return ret;
}
srs_assert(segment);
// if not initialized, ignore reap.
if (expect_reap_time <= 0
|| segment->stream_starttime <= 0
|| segment->stream_duration <= 0
) {
return ret;
}
// reap if exceed atc expect time.
if (segment->stream_starttime + segment->stream_duration > expect_reap_time) {
srs_warn("hss reap start=%"PRId64", duration=%"PRId64", expect=%"PRId64
", segment(start=%"PRId64", adjust=%"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
expect_reap_time += segment_duration;
if ((ret = flv_close()) != ERROR_SUCCESS) {
segment->reset();
return ret;
}
on_unpublish();
if ((ret = open_new_segment()) != ERROR_SUCCESS) {
return ret;
}
}
return ret;
}
SrsDvr::SrsDvr(SrsSource* source) SrsDvr::SrsDvr(SrsSource* source)
{ {
_source = source; _source = source;

@ -114,22 +114,34 @@ class SrsFlvSegment
{ {
public: public:
/** /**
* current flv file path. * current segment flv file path.
*/ */
std::string current_flv_path; std::string path;
/** /**
* whether current segment has keyframe. * whether current segment has keyframe.
*/ */
bool segment_has_keyframe; bool has_keyframe;
/** /**
* current segment duration and starttime. * current segment starttime, RTMP pkt time.
*/ */
int64_t duration;
int64_t starttime; int64_t starttime;
/** /**
* stream start time, to generate atc pts. * current segment duration
*/
int64_t duration;
/**
* stream start time, to generate atc pts. abs time.
*/ */
int64_t stream_starttime; int64_t stream_starttime;
/**
* stream duration, to generate atc segment.
*/
int64_t stream_duration;
/**
* previous stream RTMP pkt time, used to calc the duration.
* for the RTMP timestamp will overflow.
*/
int64_t stream_previous_pkt_time;
public: public:
SrsFlvSegment(); SrsFlvSegment();
virtual void reset(); virtual void reset();
@ -213,6 +225,28 @@ private:
virtual int update_duration(SrsSharedPtrMessage* msg); virtual int update_duration(SrsSharedPtrMessage* msg);
}; };
/**
* hss plan: use atc time to reap flv segment
*/
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();
virtual ~SrsDvrHssPlan();
public:
virtual int initialize(SrsSource* source, SrsRequest* req);
virtual int on_publish();
virtual void on_unpublish();
private:
virtual int update_duration(SrsSharedPtrMessage* msg);
};
/** /**
* dvr(digital video recorder) to record RTMP stream to flv file. * dvr(digital video recorder) to record RTMP stream to flv file.
* TODO: FIXME: add utest for it. * TODO: FIXME: add utest for it.

@ -462,10 +462,11 @@ void SrsHttpHooks::on_dvr_keyframe(string url, SrsRequest* req, SrsFlvSegment* s
{ {
int ret = ERROR_SUCCESS; int ret = ERROR_SUCCESS;
srs_assert(segment);
srs_trace("flv segment %s, atc_start=%"PRId64", " srs_trace("flv segment %s, atc_start=%"PRId64", "
"has_key=%d, starttime=%"PRId64", duration=%d", "has_key=%d, starttime=%"PRId64", duration=%d",
segment->current_flv_path.c_str(), segment->stream_starttime, segment->path.c_str(), segment->stream_starttime,
segment->segment_has_keyframe, segment->starttime, (int)segment->duration); segment->has_keyframe, segment->starttime, (int)segment->duration);
SrsHttpUri uri; SrsHttpUri uri;
if ((ret = uri.initialize(url)) != ERROR_SUCCESS) { if ((ret = uri.initialize(url)) != ERROR_SUCCESS) {

@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// current release version // current release version
#define VERSION_MAJOR "0" #define VERSION_MAJOR "0"
#define VERSION_MINOR "9" #define VERSION_MINOR "9"
#define VERSION_REVISION "73" #define VERSION_REVISION "74"
#define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION #define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION
// server info. // server info.
#define RTMP_SIG_SRS_KEY "srs" #define RTMP_SIG_SRS_KEY "srs"

Loading…
Cancel
Save