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
|
||||
# where dvr_path is /dvr, srs will create the following files:
|
||||
# /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.
|
||||
# default: ./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();
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
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_WINDOW 60
|
||||
#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.
|
||||
#define SRS_CONF_DEFAULT_AAC_SYNC 100
|
||||
// in ms, for HLS aac flush the audio
|
||||
|
@ -229,6 +233,8 @@ private:
|
|||
public:
|
||||
virtual bool get_dvr_enabled(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
|
||||
private:
|
||||
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
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sstream>
|
||||
using namespace std;
|
||||
|
||||
#include <srs_app_config.hpp>
|
||||
|
@ -35,6 +36,7 @@ using namespace std;
|
|||
#include <srs_app_source.hpp>
|
||||
#include <srs_core_autofree.hpp>
|
||||
#include <srs_kernel_stream.hpp>
|
||||
#include <srs_kernel_utility.hpp>
|
||||
|
||||
SrsFileStream::SrsFileStream()
|
||||
{
|
||||
|
@ -302,7 +304,7 @@ SrsDvrPlan::~SrsDvrPlan()
|
|||
srs_freep(enc);
|
||||
}
|
||||
|
||||
int SrsDvrPlan::initialize(SrsSource* source)
|
||||
int SrsDvrPlan::initialize(SrsSource* source, SrsRequest* /*req*/)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
|
@ -401,9 +403,16 @@ int SrsDvrPlan::on_video(SrsSharedPtrMessage* video)
|
|||
return ret;
|
||||
}
|
||||
|
||||
SrsDvrPlan* SrsDvrPlan::create_plan()
|
||||
SrsDvrPlan* SrsDvrPlan::create_plan(string vhost)
|
||||
{
|
||||
return new SrsDvrSessionPlan();
|
||||
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();
|
||||
} else {
|
||||
return new SrsDvrSessionPlan();
|
||||
}
|
||||
}
|
||||
|
||||
SrsDvrSessionPlan::SrsDvrSessionPlan()
|
||||
|
@ -427,14 +436,13 @@ int SrsDvrSessionPlan::on_publish(SrsRequest* req)
|
|||
return ret;
|
||||
}
|
||||
|
||||
std::string path = _srs_config->get_dvr_path(req->vhost);
|
||||
path += "/";
|
||||
path += req->app;
|
||||
path += "/";
|
||||
path += req->stream;
|
||||
path += ".flv";
|
||||
std::stringstream path;
|
||||
|
||||
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;
|
||||
}
|
||||
dvr_enabled = true;
|
||||
|
@ -458,6 +466,132 @@ void SrsDvrSessionPlan::on_unpublish()
|
|||
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)
|
||||
{
|
||||
_source = source;
|
||||
|
@ -469,14 +603,14 @@ SrsDvr::~SrsDvr()
|
|||
srs_freep(plan);
|
||||
}
|
||||
|
||||
int SrsDvr::initialize()
|
||||
int SrsDvr::initialize(SrsRequest* req)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ public:
|
|||
SrsDvrPlan();
|
||||
virtual ~SrsDvrPlan();
|
||||
public:
|
||||
virtual int initialize(SrsSource* source);
|
||||
virtual int initialize(SrsSource* source, SrsRequest* req);
|
||||
virtual int on_publish(SrsRequest* req) = 0;
|
||||
virtual void on_unpublish() = 0;
|
||||
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_close();
|
||||
public:
|
||||
static SrsDvrPlan* create_plan();
|
||||
static SrsDvrPlan* create_plan(std::string vhost);
|
||||
};
|
||||
|
||||
/**
|
||||
* default session plan:
|
||||
* 1. start dvr when session start(publish).
|
||||
* 2. stop dvr when session stop(unpublish).
|
||||
* 3. always dvr to file: dvr_path/app/stream.flv
|
||||
* session plan: reap flv when session complete(unpublish)
|
||||
*/
|
||||
class SrsDvrSessionPlan : public SrsDvrPlan
|
||||
{
|
||||
|
@ -156,6 +153,29 @@ public:
|
|||
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.
|
||||
* TODO: FIXME: add utest for it.
|
||||
|
@ -175,7 +195,7 @@ public:
|
|||
* when system initialize(encoder publish at first time, or reload),
|
||||
* initialize the dvr will reinitialize the plan, the whole dvr framework.
|
||||
*/
|
||||
virtual int initialize();
|
||||
virtual int initialize(SrsRequest* req);
|
||||
/**
|
||||
* publish stream event,
|
||||
* when encoder start to publish RTMP stream.
|
||||
|
|
|
@ -507,7 +507,7 @@ int SrsSource::initialize()
|
|||
int ret = ERROR_SUCCESS;
|
||||
|
||||
#ifdef SRS_AUTO_DVR
|
||||
if ((ret = dvr->initialize()) != ERROR_SUCCESS) {
|
||||
if ((ret = dvr->initialize(req)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
@ -641,7 +641,7 @@ int SrsSource::on_reload_vhost_dvr(string vhost)
|
|||
dvr->on_unpublish();
|
||||
|
||||
// reinitialize the dvr, update plan.
|
||||
if ((ret = dvr->initialize()) != ERROR_SUCCESS) {
|
||||
if ((ret = dvr->initialize(req)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,10 @@ static int64_t _srs_system_time_us_cache = 0;
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue