mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
support dvr to hss. change to 0.9.74
This commit is contained in:
parent
e271cb607f
commit
35f3a93239
5 changed files with 185 additions and 24 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -541,12 +545,25 @@ int SrsDvrPlan::update_duration(SrsSharedPtrMessage* msg)
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
// foreach msg, collect the duration.
|
// we must assumpt that the stream timestamp is monotonically increase,
|
||||||
if (segment->starttime < 0 || segment->starttime > msg->header.timestamp) {
|
// that is, always use time jitter to correct the timestamp.
|
||||||
|
|
||||||
|
// set the segment starttime at first time
|
||||||
|
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…
Add table
Add a link
Reference in a new issue