mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
update dvr, support segment plan
This commit is contained in:
parent
6f19a83114
commit
2d1c28cff9
7 changed files with 230 additions and 23 deletions
|
@ -108,10 +108,19 @@ vhost dvr.srs.com {
|
||||||
# http://127.0.0.1/live/livestream.m3u8
|
# http://127.0.0.1/live/livestream.m3u8
|
||||||
# where dvr_path is /dvr, srs will create the following files:
|
# where dvr_path is /dvr, srs will create the following files:
|
||||||
# /dvr/live the app dir for all streams.
|
# /dvr/live the app dir for all streams.
|
||||||
# /dvr/live/livestream.flv the dvr flv file.
|
# /dvr/live/livestream.{time}.flv the dvr flv file.
|
||||||
|
# @remark, the time use system timestamp in ms, user can use http callback to rename it.
|
||||||
# in a word, the dvr_path is for vhost.
|
# in a word, the dvr_path is for vhost.
|
||||||
# default: ./objs/nginx/html
|
# default: ./objs/nginx/html
|
||||||
dvr_path ./objs/nginx/html;
|
dvr_path ./objs/nginx/html;
|
||||||
|
# the dvr plan. canbe:
|
||||||
|
# session reap flv when session end(unpublish).
|
||||||
|
# segment reap flv when flv duration exceed the specified duration.
|
||||||
|
# default: session
|
||||||
|
dvr_plan session;
|
||||||
|
# the param for plan(segment), in seconds.
|
||||||
|
# default: 30
|
||||||
|
dvr_duration 30;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2354,6 +2354,40 @@ string SrsConfig::get_dvr_path(string vhost)
|
||||||
return conf->arg0();
|
return conf->arg0();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string SrsConfig::get_dvr_plan(string vhost)
|
||||||
|
{
|
||||||
|
SrsConfDirective* dvr = get_dvr(vhost);
|
||||||
|
|
||||||
|
if (!dvr) {
|
||||||
|
return SRS_CONF_DEFAULT_DVR_PLAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsConfDirective* conf = dvr->get("dvr_plan");
|
||||||
|
|
||||||
|
if (!conf) {
|
||||||
|
return SRS_CONF_DEFAULT_DVR_PLAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf->arg0();
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsConfig::get_dvr_duration(string vhost)
|
||||||
|
{
|
||||||
|
SrsConfDirective* dvr = get_dvr(vhost);
|
||||||
|
|
||||||
|
if (!dvr) {
|
||||||
|
return SRS_CONF_DEFAULT_DVR_DURATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsConfDirective* conf = dvr->get("dvr_duration");
|
||||||
|
|
||||||
|
if (!conf) {
|
||||||
|
return SRS_CONF_DEFAULT_DVR_DURATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ::atoi(conf->arg0().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
SrsConfDirective* SrsConfig::get_http_api()
|
SrsConfDirective* SrsConfig::get_http_api()
|
||||||
{
|
{
|
||||||
return root->get("http_api");
|
return root->get("http_api");
|
||||||
|
|
|
@ -45,6 +45,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
#define SRS_CONF_DEFAULT_HLS_FRAGMENT 10
|
#define SRS_CONF_DEFAULT_HLS_FRAGMENT 10
|
||||||
#define SRS_CONF_DEFAULT_HLS_WINDOW 60
|
#define SRS_CONF_DEFAULT_HLS_WINDOW 60
|
||||||
#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_SEGMENT "segment"
|
||||||
|
#define SRS_CONF_DEFAULT_DVR_PLAN SRS_CONF_DEFAULT_DVR_PLAN_SESSION
|
||||||
|
#define SRS_CONF_DEFAULT_DVR_DURATION 30
|
||||||
// in ms, for HLS aac sync time.
|
// in ms, for HLS aac sync time.
|
||||||
#define SRS_CONF_DEFAULT_AAC_SYNC 100
|
#define SRS_CONF_DEFAULT_AAC_SYNC 100
|
||||||
// in ms, for HLS aac flush the audio
|
// in ms, for HLS aac flush the audio
|
||||||
|
@ -229,6 +233,8 @@ private:
|
||||||
public:
|
public:
|
||||||
virtual bool get_dvr_enabled(std::string vhost);
|
virtual bool get_dvr_enabled(std::string vhost);
|
||||||
virtual std::string get_dvr_path(std::string vhost);
|
virtual std::string get_dvr_path(std::string vhost);
|
||||||
|
virtual std::string get_dvr_plan(std::string vhost);
|
||||||
|
virtual int get_dvr_duration(std::string vhost);
|
||||||
// http api section
|
// http api section
|
||||||
private:
|
private:
|
||||||
virtual SrsConfDirective* get_http_api();
|
virtual SrsConfDirective* get_http_api();
|
||||||
|
|
|
@ -26,6 +26,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
#ifdef SRS_AUTO_DVR
|
#ifdef SRS_AUTO_DVR
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <sstream>
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
#include <srs_app_config.hpp>
|
#include <srs_app_config.hpp>
|
||||||
|
@ -35,6 +36,7 @@ using namespace std;
|
||||||
#include <srs_app_source.hpp>
|
#include <srs_app_source.hpp>
|
||||||
#include <srs_core_autofree.hpp>
|
#include <srs_core_autofree.hpp>
|
||||||
#include <srs_kernel_stream.hpp>
|
#include <srs_kernel_stream.hpp>
|
||||||
|
#include <srs_kernel_utility.hpp>
|
||||||
|
|
||||||
SrsFileStream::SrsFileStream()
|
SrsFileStream::SrsFileStream()
|
||||||
{
|
{
|
||||||
|
@ -302,7 +304,7 @@ SrsDvrPlan::~SrsDvrPlan()
|
||||||
srs_freep(enc);
|
srs_freep(enc);
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsDvrPlan::initialize(SrsSource* source)
|
int SrsDvrPlan::initialize(SrsSource* source, SrsRequest* /*req*/)
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
@ -401,9 +403,16 @@ int SrsDvrPlan::on_video(SrsSharedPtrMessage* video)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsDvrPlan* SrsDvrPlan::create_plan()
|
SrsDvrPlan* SrsDvrPlan::create_plan(string vhost)
|
||||||
{
|
{
|
||||||
|
std::string plan = _srs_config->get_dvr_plan(vhost);
|
||||||
|
if (plan == SRS_CONF_DEFAULT_DVR_PLAN_SEGMENT) {
|
||||||
|
return new SrsDvrSegmentPlan();
|
||||||
|
} else if (plan == SRS_CONF_DEFAULT_DVR_PLAN_SESSION) {
|
||||||
return new SrsDvrSessionPlan();
|
return new SrsDvrSessionPlan();
|
||||||
|
} else {
|
||||||
|
return new SrsDvrSessionPlan();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsDvrSessionPlan::SrsDvrSessionPlan()
|
SrsDvrSessionPlan::SrsDvrSessionPlan()
|
||||||
|
@ -427,14 +436,13 @@ int SrsDvrSessionPlan::on_publish(SrsRequest* req)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string path = _srs_config->get_dvr_path(req->vhost);
|
std::stringstream path;
|
||||||
path += "/";
|
|
||||||
path += req->app;
|
|
||||||
path += "/";
|
|
||||||
path += req->stream;
|
|
||||||
path += ".flv";
|
|
||||||
|
|
||||||
if ((ret = flv_open(req->get_stream_url(), path)) != ERROR_SUCCESS) {
|
path << _srs_config->get_dvr_path(req->vhost)
|
||||||
|
<< "/" << req->app << "/"
|
||||||
|
<< req->stream << "." << srs_get_system_time_ms() << ".flv";
|
||||||
|
|
||||||
|
if ((ret = flv_open(req->get_stream_url(), path.str())) != ERROR_SUCCESS) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
dvr_enabled = true;
|
dvr_enabled = true;
|
||||||
|
@ -458,6 +466,132 @@ void SrsDvrSessionPlan::on_unpublish()
|
||||||
dvr_enabled = false;
|
dvr_enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SrsDvrSegmentPlan::SrsDvrSegmentPlan()
|
||||||
|
{
|
||||||
|
starttime = -1;
|
||||||
|
duration = 0;
|
||||||
|
segment_duration = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsDvrSegmentPlan::~SrsDvrSegmentPlan()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsDvrSegmentPlan::initialize(SrsSource* source, SrsRequest* req)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if ((ret = SrsDvrPlan::initialize(source, req)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
segment_duration = _srs_config->get_dvr_duration(req->vhost);
|
||||||
|
// to ms
|
||||||
|
segment_duration *= 1000;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsDvrSegmentPlan::on_publish(SrsRequest* req)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
// support multiple publish.
|
||||||
|
if (dvr_enabled) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_srs_config->get_dvr_enabled(req->vhost)) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stringstream path;
|
||||||
|
|
||||||
|
path << _srs_config->get_dvr_path(req->vhost)
|
||||||
|
<< "/" << req->app << "/"
|
||||||
|
<< req->stream << "." << srs_get_system_time_ms() << ".flv";
|
||||||
|
|
||||||
|
if ((ret = flv_open(req->get_stream_url(), path.str())) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
dvr_enabled = true;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsDvrSegmentPlan::on_unpublish()
|
||||||
|
{
|
||||||
|
// support multiple publish.
|
||||||
|
if (!dvr_enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore error.
|
||||||
|
int ret = flv_close();
|
||||||
|
if (ret != ERROR_SUCCESS) {
|
||||||
|
srs_warn("ignore flv close error. ret=%d", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
dvr_enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsDvrSegmentPlan::on_audio(SrsSharedPtrMessage* audio)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if (!dvr_enabled) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ret = update_duration(audio)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ret = SrsDvrPlan::on_audio(audio)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsDvrSegmentPlan::on_video(SrsSharedPtrMessage* video)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if (!dvr_enabled) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ret = update_duration(video)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ret = SrsDvrPlan::on_video(video)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
// foreach msg, collect the duration.
|
||||||
|
if (starttime < 0 || starttime > msg->header.timestamp) {
|
||||||
|
starttime = msg->header.timestamp;
|
||||||
|
}
|
||||||
|
duration += msg->header.timestamp - starttime;
|
||||||
|
starttime = msg->header.timestamp;
|
||||||
|
|
||||||
|
// reap if exceed duration.
|
||||||
|
if (duration > 0 && segment_duration > 0 && duration > segment_duration) {
|
||||||
|
on_unpublish();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
SrsDvr::SrsDvr(SrsSource* source)
|
SrsDvr::SrsDvr(SrsSource* source)
|
||||||
{
|
{
|
||||||
_source = source;
|
_source = source;
|
||||||
|
@ -469,14 +603,14 @@ SrsDvr::~SrsDvr()
|
||||||
srs_freep(plan);
|
srs_freep(plan);
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsDvr::initialize()
|
int SrsDvr::initialize(SrsRequest* req)
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
srs_freep(plan);
|
srs_freep(plan);
|
||||||
plan = SrsDvrPlan::create_plan();
|
plan = SrsDvrPlan::create_plan(req->vhost);
|
||||||
|
|
||||||
if ((ret = plan->initialize(_source)) != ERROR_SUCCESS) {
|
if ((ret = plan->initialize(_source, req)) != ERROR_SUCCESS) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -127,7 +127,7 @@ public:
|
||||||
SrsDvrPlan();
|
SrsDvrPlan();
|
||||||
virtual ~SrsDvrPlan();
|
virtual ~SrsDvrPlan();
|
||||||
public:
|
public:
|
||||||
virtual int initialize(SrsSource* source);
|
virtual int initialize(SrsSource* source, SrsRequest* req);
|
||||||
virtual int on_publish(SrsRequest* req) = 0;
|
virtual int on_publish(SrsRequest* req) = 0;
|
||||||
virtual void on_unpublish() = 0;
|
virtual void on_unpublish() = 0;
|
||||||
virtual int on_meta_data(SrsOnMetaDataPacket* metadata);
|
virtual int on_meta_data(SrsOnMetaDataPacket* metadata);
|
||||||
|
@ -137,14 +137,11 @@ protected:
|
||||||
virtual int flv_open(std::string stream, std::string path);
|
virtual int flv_open(std::string stream, std::string path);
|
||||||
virtual int flv_close();
|
virtual int flv_close();
|
||||||
public:
|
public:
|
||||||
static SrsDvrPlan* create_plan();
|
static SrsDvrPlan* create_plan(std::string vhost);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* default session plan:
|
* session plan: reap flv when session complete(unpublish)
|
||||||
* 1. start dvr when session start(publish).
|
|
||||||
* 2. stop dvr when session stop(unpublish).
|
|
||||||
* 3. always dvr to file: dvr_path/app/stream.flv
|
|
||||||
*/
|
*/
|
||||||
class SrsDvrSessionPlan : public SrsDvrPlan
|
class SrsDvrSessionPlan : public SrsDvrPlan
|
||||||
{
|
{
|
||||||
|
@ -156,6 +153,29 @@ public:
|
||||||
virtual void on_unpublish();
|
virtual void on_unpublish();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* segment plan: reap flv when duration exceed.
|
||||||
|
*/
|
||||||
|
class SrsDvrSegmentPlan : public SrsDvrPlan
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
int64_t duration;
|
||||||
|
int64_t starttime;
|
||||||
|
// in config, in ms
|
||||||
|
int segment_duration;
|
||||||
|
public:
|
||||||
|
SrsDvrSegmentPlan();
|
||||||
|
virtual ~SrsDvrSegmentPlan();
|
||||||
|
public:
|
||||||
|
virtual int initialize(SrsSource* source, SrsRequest* req);
|
||||||
|
virtual int on_publish(SrsRequest* req);
|
||||||
|
virtual void on_unpublish();
|
||||||
|
virtual int on_audio(SrsSharedPtrMessage* audio);
|
||||||
|
virtual int on_video(SrsSharedPtrMessage* video);
|
||||||
|
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.
|
||||||
|
@ -175,7 +195,7 @@ public:
|
||||||
* when system initialize(encoder publish at first time, or reload),
|
* when system initialize(encoder publish at first time, or reload),
|
||||||
* initialize the dvr will reinitialize the plan, the whole dvr framework.
|
* initialize the dvr will reinitialize the plan, the whole dvr framework.
|
||||||
*/
|
*/
|
||||||
virtual int initialize();
|
virtual int initialize(SrsRequest* req);
|
||||||
/**
|
/**
|
||||||
* publish stream event,
|
* publish stream event,
|
||||||
* when encoder start to publish RTMP stream.
|
* when encoder start to publish RTMP stream.
|
||||||
|
|
|
@ -507,7 +507,7 @@ int SrsSource::initialize()
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
#ifdef SRS_AUTO_DVR
|
#ifdef SRS_AUTO_DVR
|
||||||
if ((ret = dvr->initialize()) != ERROR_SUCCESS) {
|
if ((ret = dvr->initialize(req)) != ERROR_SUCCESS) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -641,7 +641,7 @@ int SrsSource::on_reload_vhost_dvr(string vhost)
|
||||||
dvr->on_unpublish();
|
dvr->on_unpublish();
|
||||||
|
|
||||||
// reinitialize the dvr, update plan.
|
// reinitialize the dvr, update plan.
|
||||||
if ((ret = dvr->initialize()) != ERROR_SUCCESS) {
|
if ((ret = dvr->initialize(req)) != ERROR_SUCCESS) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,10 @@ static int64_t _srs_system_time_us_cache = 0;
|
||||||
|
|
||||||
int64_t srs_get_system_time_ms()
|
int64_t srs_get_system_time_ms()
|
||||||
{
|
{
|
||||||
|
if (_srs_system_time_us_cache <= 0) {
|
||||||
|
srs_update_system_time_ms();
|
||||||
|
}
|
||||||
|
|
||||||
return _srs_system_time_us_cache / 1000;
|
return _srs_system_time_us_cache / 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue