update dvr, support segment plan

pull/133/head
winlin 11 years ago
parent 6f19a83114
commit 2d1c28cff9

@ -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;
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)) != ERROR_SUCCESS) {
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…
Cancel
Save