2014-04-15 06:01:57 +00:00
|
|
|
/*
|
|
|
|
The MIT License (MIT)
|
|
|
|
|
2016-12-16 03:57:25 +00:00
|
|
|
Copyright (c) 2013-2017 SRS(ossrs)
|
2014-04-15 06:01:57 +00:00
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
|
|
this software and associated documentation files (the "Software"), to deal in
|
|
|
|
the Software without restriction, including without limitation the rights to
|
|
|
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
|
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
|
|
subject to the following conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
|
|
copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
|
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
|
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
2014-04-16 01:28:02 +00:00
|
|
|
#include <srs_app_dvr.hpp>
|
2014-04-15 06:01:57 +00:00
|
|
|
|
2014-04-17 08:06:49 +00:00
|
|
|
#include <fcntl.h>
|
2014-04-17 09:35:21 +00:00
|
|
|
#include <sstream>
|
2015-02-27 12:39:36 +00:00
|
|
|
#include <algorithm>
|
2014-04-17 08:06:49 +00:00
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
#include <srs_app_config.hpp>
|
2015-06-13 08:04:59 +00:00
|
|
|
#include <srs_rtmp_stack.hpp>
|
2014-04-17 08:06:49 +00:00
|
|
|
#include <srs_core_autofree.hpp>
|
2014-04-17 09:35:21 +00:00
|
|
|
#include <srs_kernel_utility.hpp>
|
2014-04-22 09:32:45 +00:00
|
|
|
#include <srs_app_http_hooks.hpp>
|
2014-05-28 09:37:15 +00:00
|
|
|
#include <srs_kernel_codec.hpp>
|
2014-05-28 07:37:06 +00:00
|
|
|
#include <srs_kernel_flv.hpp>
|
2014-07-04 23:33:18 +00:00
|
|
|
#include <srs_kernel_file.hpp>
|
2015-09-22 01:05:21 +00:00
|
|
|
#include <srs_protocol_amf0.hpp>
|
2015-09-22 00:48:55 +00:00
|
|
|
#include <srs_kernel_buffer.hpp>
|
2015-08-21 08:20:19 +00:00
|
|
|
#include <srs_protocol_json.hpp>
|
2015-03-30 04:44:04 +00:00
|
|
|
#include <srs_app_utility.hpp>
|
2017-02-06 10:33:26 +00:00
|
|
|
#include <srs_kernel_mp4.hpp>
|
2015-02-21 11:14:05 +00:00
|
|
|
|
2017-02-06 10:33:26 +00:00
|
|
|
SrsDvrSegmenter::SrsDvrSegmenter()
|
2014-04-23 08:33:42 +00:00
|
|
|
{
|
2015-02-21 08:25:04 +00:00
|
|
|
req = NULL;
|
|
|
|
jitter = NULL;
|
2017-02-06 10:33:26 +00:00
|
|
|
plan = NULL;
|
|
|
|
duration = 0;
|
2017-02-07 13:56:20 +00:00
|
|
|
stream_previous_pkt_time = -1;
|
2017-02-06 10:33:26 +00:00
|
|
|
|
|
|
|
path = "";
|
2015-02-21 08:25:04 +00:00
|
|
|
fs = new SrsFileWriter();
|
|
|
|
jitter_algorithm = SrsRtmpJitterAlgorithmOFF;
|
|
|
|
|
|
|
|
_srs_config->subscribe(this);
|
2014-04-23 08:33:42 +00:00
|
|
|
}
|
|
|
|
|
2017-02-06 10:33:26 +00:00
|
|
|
SrsDvrSegmenter::~SrsDvrSegmenter()
|
2014-06-28 16:09:55 +00:00
|
|
|
{
|
2015-02-21 08:25:04 +00:00
|
|
|
_srs_config->unsubscribe(this);
|
|
|
|
|
|
|
|
srs_freep(jitter);
|
|
|
|
srs_freep(fs);
|
2014-06-28 16:09:55 +00:00
|
|
|
}
|
|
|
|
|
2017-02-06 10:33:26 +00:00
|
|
|
int SrsDvrSegmenter::initialize(SrsDvrPlan* p, SrsRequest* r)
|
2014-04-23 08:33:42 +00:00
|
|
|
{
|
2015-02-21 08:25:04 +00:00
|
|
|
int ret = ERROR_SUCCESS;
|
2017-02-06 10:33:26 +00:00
|
|
|
|
2015-02-21 08:25:04 +00:00
|
|
|
req = r;
|
2017-02-06 10:33:26 +00:00
|
|
|
plan = p;
|
|
|
|
|
2017-02-06 12:22:07 +00:00
|
|
|
jitter_algorithm = (SrsRtmpJitterAlgorithm)_srs_config->get_dvr_time_jitter(req->vhost);
|
|
|
|
wait_keyframe = _srs_config->get_dvr_wait_keyframe(req->vhost);
|
2015-02-21 08:25:04 +00:00
|
|
|
|
|
|
|
return ret;
|
2014-04-23 08:33:42 +00:00
|
|
|
}
|
|
|
|
|
2017-02-06 10:33:26 +00:00
|
|
|
string SrsDvrSegmenter::get_path()
|
|
|
|
{
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2017-02-06 12:58:52 +00:00
|
|
|
int64_t SrsDvrSegmenter::get_duration()
|
2014-04-15 06:24:03 +00:00
|
|
|
{
|
2017-02-06 12:58:52 +00:00
|
|
|
return duration;
|
2014-04-15 06:24:03 +00:00
|
|
|
}
|
|
|
|
|
2017-02-06 12:22:07 +00:00
|
|
|
int SrsDvrSegmenter::open()
|
2014-04-15 06:24:03 +00:00
|
|
|
{
|
2015-02-21 08:25:04 +00:00
|
|
|
int ret = ERROR_SUCCESS;
|
2014-06-25 09:14:11 +00:00
|
|
|
|
2015-02-21 08:25:04 +00:00
|
|
|
// ignore when already open.
|
|
|
|
if (fs->is_open()) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
path = generate_path();
|
2017-02-06 12:22:07 +00:00
|
|
|
if (srs_path_exists(path)) {
|
2017-02-06 10:33:26 +00:00
|
|
|
ret = ERROR_DVR_CANNOT_APPEND;
|
|
|
|
srs_error("DVR can't append to exists path=%s. ret=%d", path.c_str(), ret);
|
|
|
|
return ret;
|
|
|
|
}
|
2015-02-21 08:25:04 +00:00
|
|
|
|
2017-02-06 12:58:52 +00:00
|
|
|
tmp_dvr_file = path + ".tmp";
|
|
|
|
|
2015-02-21 08:25:04 +00:00
|
|
|
// create dir first.
|
2015-10-13 08:06:37 +00:00
|
|
|
std::string dir = srs_path_dirname(path);
|
2015-02-21 08:25:04 +00:00
|
|
|
if ((ret = srs_create_dir_recursively(dir)) != ERROR_SUCCESS) {
|
|
|
|
srs_error("create dir=%s failed. ret=%d", dir.c_str(), ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
srs_info("create dir=%s ok", dir.c_str());
|
|
|
|
|
|
|
|
// create jitter.
|
2017-02-06 12:58:52 +00:00
|
|
|
srs_freep(jitter);
|
|
|
|
jitter = new SrsRtmpJitter();
|
|
|
|
duration = 0;
|
2015-02-21 08:25:04 +00:00
|
|
|
|
2017-02-07 13:56:20 +00:00
|
|
|
// fresh stream starting.
|
|
|
|
stream_previous_pkt_time = -1;
|
|
|
|
|
2015-02-21 08:25:04 +00:00
|
|
|
// open file writer, in append or create mode.
|
2017-02-06 12:22:07 +00:00
|
|
|
if ((ret = fs->open(tmp_dvr_file)) != ERROR_SUCCESS) {
|
|
|
|
srs_error("open file stream for file %s failed. ret=%d", path.c_str(), ret);
|
|
|
|
return ret;
|
2015-02-21 08:25:04 +00:00
|
|
|
}
|
2015-02-22 02:45:13 +00:00
|
|
|
|
|
|
|
// initialize the encoder.
|
2017-02-06 10:33:26 +00:00
|
|
|
if ((ret = open_encoder()) != ERROR_SUCCESS) {
|
2017-02-06 12:22:07 +00:00
|
|
|
srs_error("initialize enc by fs for file %s failed. ret=%d", path.c_str(), ret);
|
2015-02-22 02:45:13 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2015-02-21 08:25:04 +00:00
|
|
|
|
|
|
|
srs_trace("dvr stream %s to file %s", req->stream.c_str(), path.c_str());
|
|
|
|
|
|
|
|
return ret;
|
2014-04-15 06:24:03 +00:00
|
|
|
}
|
|
|
|
|
2017-02-06 12:22:07 +00:00
|
|
|
int SrsDvrSegmenter::write_metadata(SrsSharedPtrMessage* metadata)
|
|
|
|
{
|
|
|
|
return encode_metadata(metadata);
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsDvrSegmenter::write_audio(SrsSharedPtrMessage* shared_audio)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
SrsSharedPtrMessage* audio = shared_audio->copy();
|
|
|
|
SrsAutoFree(SrsSharedPtrMessage, audio);
|
|
|
|
|
|
|
|
if ((jitter->correct(audio, jitter_algorithm)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-07 13:56:20 +00:00
|
|
|
if ((ret = on_update_duration(audio)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-06 12:58:52 +00:00
|
|
|
if ((ret = encode_audio(audio)) != ERROR_SUCCESS) {
|
2017-02-06 12:22:07 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsDvrSegmenter::write_video(SrsSharedPtrMessage* shared_video)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
SrsSharedPtrMessage* video = shared_video->copy();
|
|
|
|
SrsAutoFree(SrsSharedPtrMessage, video);
|
|
|
|
|
|
|
|
if ((jitter->correct(video, jitter_algorithm)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-07 13:56:20 +00:00
|
|
|
if ((ret = encode_video(video)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
2017-02-06 12:22:07 +00:00
|
|
|
|
2017-02-07 13:56:20 +00:00
|
|
|
if ((ret = on_update_duration(video)) != ERROR_SUCCESS) {
|
2017-02-06 12:22:07 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-06 10:33:26 +00:00
|
|
|
int SrsDvrSegmenter::close()
|
2014-04-15 06:24:03 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
2014-04-17 08:06:49 +00:00
|
|
|
|
2015-02-21 08:25:04 +00:00
|
|
|
// ignore when already closed.
|
|
|
|
if (!fs->is_open()) {
|
|
|
|
return ret;
|
|
|
|
}
|
2015-02-21 11:14:05 +00:00
|
|
|
|
2017-02-06 10:33:26 +00:00
|
|
|
// Close the encoder, then close the fs object.
|
|
|
|
if ((ret = close_encoder()) != ERROR_SUCCESS) {
|
2015-02-21 11:14:05 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2014-06-25 09:14:11 +00:00
|
|
|
|
2015-02-21 08:25:04 +00:00
|
|
|
fs->close();
|
|
|
|
|
|
|
|
// when tmp flv file exists, reap it.
|
2017-02-06 10:33:26 +00:00
|
|
|
if (tmp_dvr_file != path) {
|
|
|
|
if (rename(tmp_dvr_file.c_str(), path.c_str()) < 0) {
|
2015-02-21 08:25:04 +00:00
|
|
|
ret = ERROR_SYSTEM_FILE_RENAME;
|
|
|
|
srs_error("rename flv file failed, %s => %s. ret=%d",
|
2017-02-06 10:33:26 +00:00
|
|
|
tmp_dvr_file.c_str(), path.c_str(), ret);
|
2015-02-21 08:25:04 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
2015-02-21 15:09:21 +00:00
|
|
|
|
2015-02-22 02:45:13 +00:00
|
|
|
// TODO: FIXME: the http callback is async, which will trigger thread switch,
|
|
|
|
// so the on_video maybe invoked during the http callback, and error.
|
2015-02-21 15:09:21 +00:00
|
|
|
if ((ret = plan->on_reap_segment()) != ERROR_SUCCESS) {
|
|
|
|
srs_error("dvr: notify plan to reap segment failed. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
2014-04-17 08:06:49 +00:00
|
|
|
|
2014-04-17 08:57:04 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-07 13:56:20 +00:00
|
|
|
int SrsDvrSegmenter::on_update_duration(SrsSharedPtrMessage* msg)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
// no previous packet or timestamp overflow.
|
|
|
|
if (stream_previous_pkt_time < 0 || stream_previous_pkt_time > msg->timestamp) {
|
|
|
|
stream_previous_pkt_time = msg->timestamp;
|
|
|
|
}
|
|
|
|
|
|
|
|
// collect segment and stream duration, timestamp overflow is ok.
|
|
|
|
duration += msg->timestamp - stream_previous_pkt_time;
|
|
|
|
|
|
|
|
// update previous packet time
|
|
|
|
stream_previous_pkt_time = msg->timestamp;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-06 10:33:26 +00:00
|
|
|
string SrsDvrSegmenter::generate_path()
|
|
|
|
{
|
|
|
|
// the path in config, for example,
|
|
|
|
// /data/[vhost]/[app]/[stream]/[2006]/[01]/[02]/[15].[04].[05].[999].flv
|
|
|
|
std::string path_config = _srs_config->get_dvr_path(req->vhost);
|
|
|
|
|
|
|
|
// add [stream].[timestamp].flv as filename for dir
|
2017-02-07 13:56:20 +00:00
|
|
|
if (!srs_string_ends_with(path_config, ".flv", ".mp4")) {
|
2017-02-06 10:33:26 +00:00
|
|
|
path_config += "/[stream].[timestamp].flv";
|
|
|
|
}
|
|
|
|
|
|
|
|
// the flv file path
|
|
|
|
std::string flv_path = path_config;
|
|
|
|
flv_path = srs_path_build_stream(flv_path, req->vhost, req->app, req->stream);
|
|
|
|
flv_path = srs_path_build_timestamp(flv_path);
|
|
|
|
|
|
|
|
return flv_path;
|
|
|
|
}
|
|
|
|
|
2017-02-06 12:22:07 +00:00
|
|
|
int SrsDvrSegmenter::on_reload_vhost_dvr(std::string vhost)
|
2017-02-06 10:33:26 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
2017-02-06 12:22:07 +00:00
|
|
|
if (req->vhost != vhost) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-06 10:33:26 +00:00
|
|
|
jitter_algorithm = (SrsRtmpJitterAlgorithm)_srs_config->get_dvr_time_jitter(req->vhost);
|
2017-02-06 12:22:07 +00:00
|
|
|
wait_keyframe = _srs_config->get_dvr_wait_keyframe(req->vhost);
|
2017-02-06 10:33:26 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsDvrFlvSegmenter::SrsDvrFlvSegmenter()
|
|
|
|
{
|
2017-02-12 13:50:02 +00:00
|
|
|
enc = new SrsFlvTransmuxer();
|
2017-02-06 10:33:26 +00:00
|
|
|
|
|
|
|
duration_offset = 0;
|
|
|
|
filesize_offset = 0;
|
|
|
|
|
|
|
|
has_keyframe = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsDvrFlvSegmenter::~SrsDvrFlvSegmenter()
|
|
|
|
{
|
|
|
|
srs_freep(enc);
|
|
|
|
}
|
|
|
|
|
2017-02-06 12:22:07 +00:00
|
|
|
int SrsDvrFlvSegmenter::refresh_metadata()
|
2017-02-06 10:33:26 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
2017-02-06 12:22:07 +00:00
|
|
|
// no duration or filesize specified.
|
|
|
|
if (!duration_offset || !filesize_offset) {
|
2017-02-06 10:33:26 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-06 12:22:07 +00:00
|
|
|
int64_t cur = fs->tellg();
|
|
|
|
|
|
|
|
// buffer to write the size.
|
|
|
|
char* buf = new char[SrsAmf0Size::number()];
|
|
|
|
SrsAutoFreeA(char, buf);
|
|
|
|
|
|
|
|
SrsBuffer stream;
|
|
|
|
if ((ret = stream.initialize(buf, SrsAmf0Size::number())) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
2017-02-06 10:33:26 +00:00
|
|
|
}
|
|
|
|
|
2017-02-06 12:22:07 +00:00
|
|
|
// filesize to buf.
|
|
|
|
SrsAmf0Any* size = SrsAmf0Any::number((double)cur);
|
|
|
|
SrsAutoFree(SrsAmf0Any, size);
|
|
|
|
|
|
|
|
stream.skip(-1 * stream.pos());
|
|
|
|
if ((ret = size->write(&stream)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// update the flesize.
|
|
|
|
fs->seek2(filesize_offset);
|
|
|
|
if ((ret = fs->write(buf, SrsAmf0Size::number(), NULL)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// duration to buf
|
|
|
|
SrsAmf0Any* dur = SrsAmf0Any::number((double)duration / 1000.0);
|
|
|
|
SrsAutoFree(SrsAmf0Any, dur);
|
|
|
|
|
|
|
|
stream.skip(-1 * stream.pos());
|
|
|
|
if ((ret = dur->write(&stream)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// update the duration
|
|
|
|
fs->seek2(duration_offset);
|
|
|
|
if ((ret = fs->write(buf, SrsAmf0Size::number(), NULL)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// reset the offset.
|
|
|
|
fs->seek2(cur);
|
2017-02-06 10:33:26 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-06 12:22:07 +00:00
|
|
|
int SrsDvrFlvSegmenter::open_encoder()
|
2017-02-06 10:33:26 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
2017-02-06 12:22:07 +00:00
|
|
|
has_keyframe = false;
|
2017-02-06 10:33:26 +00:00
|
|
|
|
2017-02-06 12:22:07 +00:00
|
|
|
// update the duration and filesize offset.
|
|
|
|
duration_offset = 0;
|
|
|
|
filesize_offset = 0;
|
2017-02-06 10:33:26 +00:00
|
|
|
|
2017-02-07 13:56:20 +00:00
|
|
|
srs_freep(enc);
|
2017-02-12 13:50:02 +00:00
|
|
|
enc = new SrsFlvTransmuxer();
|
2017-02-07 13:56:20 +00:00
|
|
|
|
2017-02-06 12:22:07 +00:00
|
|
|
if ((ret = enc->initialize(fs)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// write the flv header to writer.
|
|
|
|
if ((ret = enc->write_header()) != ERROR_SUCCESS) {
|
|
|
|
srs_error("write flv header failed. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
2017-02-06 10:33:26 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-06 12:22:07 +00:00
|
|
|
int SrsDvrFlvSegmenter::encode_metadata(SrsSharedPtrMessage* metadata)
|
2017-02-06 10:33:26 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
// Ignore when metadata already written.
|
|
|
|
if (duration_offset || filesize_offset) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-09-22 00:57:31 +00:00
|
|
|
SrsBuffer stream;
|
2015-02-21 11:14:05 +00:00
|
|
|
if ((ret = stream.initialize(metadata->payload, metadata->size)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
2017-02-06 10:33:26 +00:00
|
|
|
|
2015-02-21 11:14:05 +00:00
|
|
|
SrsAmf0Any* name = SrsAmf0Any::str();
|
|
|
|
SrsAutoFree(SrsAmf0Any, name);
|
|
|
|
if ((ret = name->read(&stream)) != ERROR_SUCCESS) {
|
2014-04-17 10:13:59 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2017-02-06 10:33:26 +00:00
|
|
|
|
2015-02-21 11:14:05 +00:00
|
|
|
SrsAmf0Object* obj = SrsAmf0Any::object();
|
|
|
|
SrsAutoFree(SrsAmf0Object, obj);
|
|
|
|
if ((ret = obj->read(&stream)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
2017-02-06 10:33:26 +00:00
|
|
|
|
2015-02-21 11:14:05 +00:00
|
|
|
// remove duration and filesize.
|
|
|
|
obj->set("filesize", NULL);
|
|
|
|
obj->set("duration", NULL);
|
2017-02-06 10:33:26 +00:00
|
|
|
|
2015-02-21 11:14:05 +00:00
|
|
|
// add properties.
|
|
|
|
obj->set("service", SrsAmf0Any::str(RTMP_SIG_SRS_SERVER));
|
|
|
|
obj->set("filesize", SrsAmf0Any::number(0));
|
|
|
|
obj->set("duration", SrsAmf0Any::number(0));
|
|
|
|
|
|
|
|
int size = name->total_size() + obj->total_size();
|
|
|
|
char* payload = new char[size];
|
2015-11-02 03:29:20 +00:00
|
|
|
SrsAutoFreeA(char, payload);
|
2017-02-06 10:33:26 +00:00
|
|
|
|
2015-02-21 11:14:05 +00:00
|
|
|
// 11B flv header, 3B object EOF, 8B number value, 1B number flag.
|
|
|
|
duration_offset = fs->tellg() + size + 11 - SrsAmf0Size::object_eof() - SrsAmf0Size::number();
|
|
|
|
// 2B string flag, 8B number value, 8B string 'duration', 1B number flag
|
|
|
|
filesize_offset = duration_offset - SrsAmf0Size::utf8("duration") - SrsAmf0Size::number();
|
2017-02-06 10:33:26 +00:00
|
|
|
|
2015-02-21 11:14:05 +00:00
|
|
|
// convert metadata to bytes.
|
|
|
|
if ((ret = stream.initialize(payload, size)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
if ((ret = name->write(&stream)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
if ((ret = obj->write(&stream)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
2014-04-17 10:13:59 +00:00
|
|
|
|
2015-02-21 11:14:05 +00:00
|
|
|
// to flv file.
|
2015-02-21 08:25:04 +00:00
|
|
|
if ((ret = enc->write_metadata(18, payload, size)) != ERROR_SUCCESS) {
|
2014-04-17 10:13:59 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-02-21 08:25:04 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-06 12:58:52 +00:00
|
|
|
int SrsDvrFlvSegmenter::encode_audio(SrsSharedPtrMessage* audio)
|
2015-02-21 08:25:04 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
2017-02-06 10:33:26 +00:00
|
|
|
|
2015-02-21 08:25:04 +00:00
|
|
|
char* payload = audio->payload;
|
|
|
|
int size = audio->size;
|
2017-02-06 12:58:52 +00:00
|
|
|
if ((ret = enc->write_audio(audio->timestamp, payload, size)) != ERROR_SUCCESS) {
|
2015-02-21 08:25:04 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2017-02-06 10:33:26 +00:00
|
|
|
|
2014-04-23 09:53:14 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-07 13:56:20 +00:00
|
|
|
int SrsDvrFlvSegmenter::encode_video(SrsSharedPtrMessage* video)
|
2014-04-23 09:53:14 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
2017-02-06 10:33:26 +00:00
|
|
|
|
2017-02-07 13:56:20 +00:00
|
|
|
char* payload = video->payload;
|
|
|
|
int size = video->size;
|
2017-02-12 12:38:39 +00:00
|
|
|
bool sh = SrsFlvVideo::sh(payload, size);
|
|
|
|
bool keyframe = SrsFlvVideo::h264(payload, size)
|
|
|
|
&& SrsFlvVideo::keyframe(payload, size) && !sh;
|
2017-02-07 13:56:20 +00:00
|
|
|
|
2017-02-06 12:22:07 +00:00
|
|
|
if (keyframe) {
|
2015-02-21 08:25:04 +00:00
|
|
|
has_keyframe = true;
|
|
|
|
}
|
2017-02-06 10:33:26 +00:00
|
|
|
|
2015-02-27 13:11:29 +00:00
|
|
|
// accept the sequence header here.
|
|
|
|
// when got no keyframe, ignore when should wait keyframe.
|
2017-02-06 12:22:07 +00:00
|
|
|
if (!has_keyframe && !sh) {
|
2015-02-27 13:11:29 +00:00
|
|
|
if (wait_keyframe) {
|
|
|
|
srs_info("dvr: ignore when wait keyframe.");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
2014-04-23 09:53:14 +00:00
|
|
|
|
2017-02-06 12:58:52 +00:00
|
|
|
if ((ret = enc->write_video(video->timestamp, payload, size)) != ERROR_SUCCESS) {
|
2015-02-21 08:25:04 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-06 12:22:07 +00:00
|
|
|
int SrsDvrFlvSegmenter::close_encoder()
|
|
|
|
{
|
|
|
|
return refresh_metadata();
|
|
|
|
}
|
|
|
|
|
2017-02-06 10:33:26 +00:00
|
|
|
SrsDvrMp4Segmenter::SrsDvrMp4Segmenter()
|
2015-02-21 15:09:21 +00:00
|
|
|
{
|
2017-02-06 10:33:26 +00:00
|
|
|
enc = new SrsMp4Encoder();
|
2017-02-07 13:56:20 +00:00
|
|
|
buffer = new SrsBuffer();
|
2015-02-21 15:09:21 +00:00
|
|
|
}
|
|
|
|
|
2017-02-06 10:33:26 +00:00
|
|
|
SrsDvrMp4Segmenter::~SrsDvrMp4Segmenter()
|
2015-02-21 08:25:04 +00:00
|
|
|
{
|
2017-02-06 10:33:26 +00:00
|
|
|
srs_freep(enc);
|
2017-02-07 13:56:20 +00:00
|
|
|
srs_freep(buffer);
|
2015-02-21 08:25:04 +00:00
|
|
|
}
|
|
|
|
|
2017-02-06 12:22:07 +00:00
|
|
|
int SrsDvrMp4Segmenter::refresh_metadata()
|
2015-02-21 08:25:04 +00:00
|
|
|
{
|
2017-02-07 13:56:20 +00:00
|
|
|
return ERROR_SUCCESS;
|
2017-02-06 10:33:26 +00:00
|
|
|
}
|
2015-02-21 08:25:04 +00:00
|
|
|
|
2017-02-06 12:22:07 +00:00
|
|
|
int SrsDvrMp4Segmenter::open_encoder()
|
2017-02-06 10:33:26 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
2017-02-07 13:56:20 +00:00
|
|
|
|
|
|
|
srs_freep(enc);
|
|
|
|
enc = new SrsMp4Encoder();
|
|
|
|
|
|
|
|
if ((ret = enc->initialize(fs)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-06 10:33:26 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2015-02-21 08:25:04 +00:00
|
|
|
|
2017-02-07 13:56:20 +00:00
|
|
|
int SrsDvrMp4Segmenter::encode_metadata(SrsSharedPtrMessage* /*metadata*/)
|
2017-02-06 10:33:26 +00:00
|
|
|
{
|
2017-02-07 13:56:20 +00:00
|
|
|
return ERROR_SUCCESS;
|
2017-02-06 10:33:26 +00:00
|
|
|
}
|
2015-02-21 08:25:04 +00:00
|
|
|
|
2017-02-06 12:58:52 +00:00
|
|
|
int SrsDvrMp4Segmenter::encode_audio(SrsSharedPtrMessage* audio)
|
2017-02-06 10:33:26 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
2017-02-07 13:56:20 +00:00
|
|
|
|
|
|
|
if ((ret = buffer->initialize(audio->payload, audio->size)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// E.4.2.1 AUDIODATA, flv_v10_1.pdf, page 3
|
|
|
|
if (!buffer->require(1)) {
|
|
|
|
ret = ERROR_FLV_REQUIRE_SPACE;
|
|
|
|
srs_error("DVR require flva 1 byte space. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
uint8_t v = buffer->read_1bytes();
|
2017-02-12 12:38:39 +00:00
|
|
|
SrsAudioCodecId sound_format = (SrsAudioCodecId)((v >> 4) & 0x0f);
|
|
|
|
SrsAudioSampleRate sound_rate = (SrsAudioSampleRate)((v >> 2) & 0x03);
|
2017-02-12 12:46:24 +00:00
|
|
|
SrsAudioSampleBits sound_size = (SrsAudioSampleBits)((v >> 1) & 0x01);
|
|
|
|
SrsAudioChannels channels = (SrsAudioChannels)(v&0x01);
|
2017-02-07 13:56:20 +00:00
|
|
|
|
|
|
|
uint16_t ct = 0x00;
|
2017-02-12 12:38:39 +00:00
|
|
|
if (sound_format == SrsAudioCodecIdAAC) {
|
2017-02-07 13:56:20 +00:00
|
|
|
if (!buffer->require(1)) {
|
|
|
|
ret = ERROR_FLV_REQUIRE_SPACE;
|
|
|
|
srs_error("DVR require flva 1 byte space, format=%d. ret=%d", sound_format, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
v = buffer->read_1bytes();
|
2017-02-12 12:38:39 +00:00
|
|
|
ct = (v == 0? SrsAudioAacFrameTraitSequenceHeader:SrsAudioAacFrameTraitRawData);
|
2017-02-07 13:56:20 +00:00
|
|
|
}
|
|
|
|
|
2017-02-12 12:38:39 +00:00
|
|
|
if (ct == SrsAudioAacFrameTraitSequenceHeader) {
|
2017-02-07 13:56:20 +00:00
|
|
|
enc->acodec = sound_format;
|
|
|
|
enc->sample_rate = sound_rate;
|
|
|
|
enc->sound_bits = sound_size;
|
|
|
|
enc->channels = channels;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t* sample = (uint8_t*)(buffer->data() + buffer->pos());
|
|
|
|
uint32_t nb_sample = (uint32_t)(buffer->size() - buffer->pos());
|
|
|
|
|
|
|
|
uint32_t dts = (uint32_t)audio->timestamp;
|
|
|
|
return enc->write_sample(SrsMp4HandlerTypeSOUN, 0x00, ct, dts, dts, sample, nb_sample);
|
2014-04-24 08:32:19 +00:00
|
|
|
}
|
|
|
|
|
2017-02-07 13:56:20 +00:00
|
|
|
int SrsDvrMp4Segmenter::encode_video(SrsSharedPtrMessage* video)
|
2014-04-24 08:32:19 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
2017-02-07 13:56:20 +00:00
|
|
|
|
|
|
|
if ((ret = buffer->initialize(video->payload, video->size)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// E.4.3.1 VIDEODATA, flv_v10_1.pdf, page 5
|
|
|
|
if (!buffer->require(1)) {
|
|
|
|
ret = ERROR_FLV_REQUIRE_SPACE;
|
|
|
|
srs_error("DVR require flvv 1 byte space. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
uint8_t v = buffer->read_1bytes();
|
2017-02-12 12:38:39 +00:00
|
|
|
SrsVideoAvcFrameType frame_type = (SrsVideoAvcFrameType)((v>>4)&0x0f);
|
|
|
|
SrsVideoCodecId codec_id = (SrsVideoCodecId)(v&0x0f);
|
2017-02-07 13:56:20 +00:00
|
|
|
|
|
|
|
if (!buffer->require(4)) {
|
|
|
|
ret = ERROR_FLV_REQUIRE_SPACE;
|
|
|
|
srs_error("DVR require flvv 4 bytes space, codec=%d. ret=%d", codec_id, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
2017-02-12 12:38:39 +00:00
|
|
|
SrsVideoAvcFrameTrait ct = (SrsVideoAvcFrameTrait)buffer->read_1bytes();
|
2017-02-07 13:56:20 +00:00
|
|
|
uint32_t cts = (uint32_t)buffer->read_3bytes();
|
|
|
|
|
2017-02-12 12:38:39 +00:00
|
|
|
if (ct == SrsVideoAvcFrameTraitSequenceHeader) {
|
2017-02-07 13:56:20 +00:00
|
|
|
enc->vcodec = codec_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t dts = (uint32_t)video->timestamp;
|
|
|
|
uint32_t pts = dts + cts;
|
|
|
|
|
|
|
|
uint8_t* sample = (uint8_t*)(buffer->data() + buffer->pos());
|
|
|
|
uint32_t nb_sample = (uint32_t)(buffer->size() - buffer->pos());
|
|
|
|
return enc->write_sample(SrsMp4HandlerTypeVIDE, frame_type, ct, dts, pts, sample, nb_sample);
|
2014-04-17 10:13:59 +00:00
|
|
|
}
|
|
|
|
|
2017-02-06 12:22:07 +00:00
|
|
|
int SrsDvrMp4Segmenter::close_encoder()
|
2014-04-24 08:32:19 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
2017-02-07 13:56:20 +00:00
|
|
|
|
|
|
|
if ((ret = enc->flush()) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-04-24 08:32:19 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-09-14 05:47:25 +00:00
|
|
|
SrsDvrAsyncCallOnDvr::SrsDvrAsyncCallOnDvr(int c, SrsRequest* r, string p)
|
2015-02-23 11:23:32 +00:00
|
|
|
{
|
2015-09-14 05:47:25 +00:00
|
|
|
cid = c;
|
2015-05-22 23:46:45 +00:00
|
|
|
req = r->copy();
|
2015-02-23 11:23:32 +00:00
|
|
|
path = p;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsDvrAsyncCallOnDvr::~SrsDvrAsyncCallOnDvr()
|
|
|
|
{
|
2015-05-22 23:46:45 +00:00
|
|
|
srs_freep(req);
|
2015-02-23 11:23:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int SrsDvrAsyncCallOnDvr::call()
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
2015-09-14 04:22:19 +00:00
|
|
|
if (!_srs_config->get_vhost_http_hooks_enabled(req->vhost)) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// the http hooks will cause context switch,
|
|
|
|
// so we must copy all hooks for the on_connect may freed.
|
2015-11-11 02:37:50 +00:00
|
|
|
// @see https://github.com/ossrs/srs/issues/475
|
2015-09-14 04:22:19 +00:00
|
|
|
vector<string> hooks;
|
|
|
|
|
|
|
|
if (true) {
|
|
|
|
SrsConfDirective* conf = _srs_config->get_vhost_on_dvr(req->vhost);
|
|
|
|
|
|
|
|
if (!conf) {
|
2015-02-23 11:23:32 +00:00
|
|
|
srs_info("ignore the empty http callback: on_dvr");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-09-14 04:22:19 +00:00
|
|
|
hooks = conf->args;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < (int)hooks.size(); i++) {
|
|
|
|
std::string url = hooks.at(i);
|
2015-09-14 05:47:25 +00:00
|
|
|
if ((ret = SrsHttpHooks::on_dvr(cid, url, req, path)) != ERROR_SUCCESS) {
|
2015-09-14 04:22:19 +00:00
|
|
|
srs_error("hook client on_dvr failed. url=%s, ret=%d", url.c_str(), ret);
|
|
|
|
return ret;
|
2015-02-23 11:23:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
string SrsDvrAsyncCallOnDvr::to_string()
|
|
|
|
{
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << "vhost=" << req->vhost << ", file=" << path;
|
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
|
2015-02-21 08:25:04 +00:00
|
|
|
SrsDvrPlan::SrsDvrPlan()
|
2014-04-24 08:32:19 +00:00
|
|
|
{
|
2015-02-21 08:25:04 +00:00
|
|
|
req = NULL;
|
|
|
|
|
|
|
|
dvr_enabled = false;
|
2017-02-06 10:33:26 +00:00
|
|
|
segment = NULL;
|
2015-05-22 23:57:45 +00:00
|
|
|
async = new SrsAsyncCallWorker();
|
2014-04-24 08:32:19 +00:00
|
|
|
}
|
|
|
|
|
2015-02-21 08:25:04 +00:00
|
|
|
SrsDvrPlan::~SrsDvrPlan()
|
2014-04-15 06:24:03 +00:00
|
|
|
{
|
2015-02-21 08:25:04 +00:00
|
|
|
srs_freep(segment);
|
2015-02-23 11:23:32 +00:00
|
|
|
srs_freep(async);
|
2014-04-15 06:24:03 +00:00
|
|
|
}
|
|
|
|
|
2017-02-06 12:58:52 +00:00
|
|
|
int SrsDvrPlan::initialize(SrsOriginHub* h, SrsDvrSegmenter* s, SrsRequest* r)
|
2014-04-15 06:24:03 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
2015-02-27 12:39:36 +00:00
|
|
|
|
2017-02-06 12:58:52 +00:00
|
|
|
hub = h;
|
2015-02-21 08:25:04 +00:00
|
|
|
req = r;
|
2017-02-06 10:33:26 +00:00
|
|
|
segment = s;
|
2015-02-21 08:25:04 +00:00
|
|
|
|
2017-02-06 10:33:26 +00:00
|
|
|
if ((ret = segment->initialize(this, r)) != ERROR_SUCCESS) {
|
2014-04-17 10:13:59 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2015-02-21 08:25:04 +00:00
|
|
|
|
2015-02-23 11:23:32 +00:00
|
|
|
if ((ret = async->start()) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-04-15 06:24:03 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-03-21 03:55:28 +00:00
|
|
|
int SrsDvrPlan::on_meta_data(SrsSharedPtrMessage* shared_metadata)
|
2014-04-22 09:32:45 +00:00
|
|
|
{
|
2014-04-22 09:50:22 +00:00
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
2015-02-21 08:25:04 +00:00
|
|
|
if (!dvr_enabled) {
|
|
|
|
return ret;
|
2014-04-22 09:50:22 +00:00
|
|
|
}
|
|
|
|
|
2015-03-21 03:55:28 +00:00
|
|
|
return segment->write_metadata(shared_metadata);
|
2014-04-23 08:25:18 +00:00
|
|
|
}
|
|
|
|
|
2015-03-21 03:55:28 +00:00
|
|
|
int SrsDvrPlan::on_audio(SrsSharedPtrMessage* shared_audio)
|
2014-04-23 08:25:18 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
2015-02-21 08:25:04 +00:00
|
|
|
if (!dvr_enabled) {
|
|
|
|
return ret;
|
2014-04-23 08:25:18 +00:00
|
|
|
}
|
2015-02-21 08:25:04 +00:00
|
|
|
|
2015-03-21 03:55:28 +00:00
|
|
|
if ((ret = segment->write_audio(shared_audio)) != ERROR_SUCCESS) {
|
2015-02-21 08:25:04 +00:00
|
|
|
return ret;
|
2014-04-24 04:22:36 +00:00
|
|
|
}
|
|
|
|
|
2014-04-22 09:50:22 +00:00
|
|
|
return ret;
|
2014-04-22 09:32:45 +00:00
|
|
|
}
|
|
|
|
|
2015-03-21 03:55:28 +00:00
|
|
|
int SrsDvrPlan::on_video(SrsSharedPtrMessage* shared_video)
|
2014-04-24 08:32:19 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
2015-02-21 08:25:04 +00:00
|
|
|
if (!dvr_enabled) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-03-21 03:55:28 +00:00
|
|
|
if ((ret = segment->write_video(shared_video)) != ERROR_SUCCESS) {
|
2014-04-24 08:32:19 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-02-21 15:09:21 +00:00
|
|
|
int SrsDvrPlan::on_reap_segment()
|
|
|
|
{
|
2015-02-23 11:23:32 +00:00
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
2015-09-14 05:47:25 +00:00
|
|
|
int cid = _srs_context->get_id();
|
|
|
|
if ((ret = async->execute(new SrsDvrAsyncCallOnDvr(cid, req, segment->get_path()))) != ERROR_SUCCESS) {
|
2015-02-23 11:23:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2015-02-21 15:09:21 +00:00
|
|
|
}
|
|
|
|
|
2017-02-06 10:33:26 +00:00
|
|
|
int SrsDvrPlan::create_plan(string vhost, SrsDvrPlan** pplan)
|
2014-04-17 08:57:04 +00:00
|
|
|
{
|
2017-02-06 10:33:26 +00:00
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
2014-04-17 09:35:21 +00:00
|
|
|
std::string plan = _srs_config->get_dvr_plan(vhost);
|
2015-07-06 03:11:59 +00:00
|
|
|
if (srs_config_dvr_is_plan_segment(plan)) {
|
2017-02-06 10:33:26 +00:00
|
|
|
*pplan = new SrsDvrSegmentPlan();
|
2015-07-06 03:11:59 +00:00
|
|
|
} else if (srs_config_dvr_is_plan_session(plan)) {
|
2017-02-06 10:33:26 +00:00
|
|
|
*pplan = new SrsDvrSessionPlan();
|
2014-04-17 09:35:21 +00:00
|
|
|
} else {
|
2017-02-06 12:22:07 +00:00
|
|
|
ret = ERROR_DVR_ILLEGAL_PLAN;
|
|
|
|
srs_error("DVR illegal plan=%s, vhost=%s. ret=%d", plan.c_str(), vhost.c_str(), ret);
|
|
|
|
return ret;
|
2014-04-17 09:35:21 +00:00
|
|
|
}
|
2017-02-06 10:33:26 +00:00
|
|
|
|
|
|
|
return ret;
|
2014-04-17 08:57:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsDvrSessionPlan::SrsDvrSessionPlan()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsDvrSessionPlan::~SrsDvrSessionPlan()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2015-02-21 08:25:04 +00:00
|
|
|
int SrsDvrSessionPlan::on_publish()
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
// support multiple publish.
|
|
|
|
if (dvr_enabled) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!_srs_config->get_dvr_enabled(req->vhost)) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ret = segment->close()) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-06 12:22:07 +00:00
|
|
|
if ((ret = segment->open()) != ERROR_SUCCESS) {
|
2015-02-21 08:25:04 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
dvr_enabled = true;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-04-17 08:57:04 +00:00
|
|
|
void SrsDvrSessionPlan::on_unpublish()
|
|
|
|
{
|
|
|
|
// support multiple publish.
|
|
|
|
if (!dvr_enabled) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ignore error.
|
2015-02-21 08:25:04 +00:00
|
|
|
int ret = segment->close();
|
2014-04-17 08:57:04 +00:00
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
|
|
srs_warn("ignore flv close error. ret=%d", ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
dvr_enabled = false;
|
|
|
|
}
|
|
|
|
|
2014-04-17 09:35:21 +00:00
|
|
|
SrsDvrSegmentPlan::SrsDvrSegmentPlan()
|
|
|
|
{
|
2017-02-06 12:58:52 +00:00
|
|
|
cduration = -1;
|
|
|
|
wait_keyframe = false;
|
2014-04-17 09:35:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsDvrSegmentPlan::~SrsDvrSegmentPlan()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-02-06 12:58:52 +00:00
|
|
|
int SrsDvrSegmentPlan::initialize(SrsOriginHub* h, SrsDvrSegmenter* s, SrsRequest* r)
|
2014-04-17 09:35:21 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
2017-02-06 12:58:52 +00:00
|
|
|
if ((ret = SrsDvrPlan::initialize(h, s, r)) != ERROR_SUCCESS) {
|
2014-04-17 09:35:21 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-06 12:58:52 +00:00
|
|
|
wait_keyframe = _srs_config->get_dvr_wait_keyframe(req->vhost);
|
|
|
|
|
|
|
|
cduration = _srs_config->get_dvr_duration(req->vhost);
|
2014-04-17 09:35:21 +00:00
|
|
|
// to ms
|
2017-02-06 12:58:52 +00:00
|
|
|
cduration *= 1000;
|
2014-04-17 09:35:21 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-04-17 10:13:59 +00:00
|
|
|
int SrsDvrSegmentPlan::on_publish()
|
2014-04-17 09:35:21 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
2015-02-21 08:25:04 +00:00
|
|
|
// support multiple publish.
|
|
|
|
if (dvr_enabled) {
|
2014-04-17 09:35:21 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2015-02-21 08:25:04 +00:00
|
|
|
|
|
|
|
if (!_srs_config->get_dvr_enabled(req->vhost)) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ret = segment->close()) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-06 12:22:07 +00:00
|
|
|
if ((ret = segment->open()) != ERROR_SUCCESS) {
|
2015-02-21 08:25:04 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
dvr_enabled = true;
|
|
|
|
|
|
|
|
return ret;
|
2014-04-17 09:35:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SrsDvrSegmentPlan::on_unpublish()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2015-03-21 03:55:28 +00:00
|
|
|
int SrsDvrSegmentPlan::on_audio(SrsSharedPtrMessage* shared_audio)
|
2014-08-03 12:27:57 +00:00
|
|
|
{
|
2015-02-21 08:25:04 +00:00
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
2015-03-21 03:55:28 +00:00
|
|
|
if ((ret = update_duration(shared_audio)) != ERROR_SUCCESS) {
|
2015-02-21 08:25:04 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2014-08-03 12:27:57 +00:00
|
|
|
|
2015-03-21 03:55:28 +00:00
|
|
|
if ((ret = SrsDvrPlan::on_audio(shared_audio)) != ERROR_SUCCESS) {
|
2015-02-21 08:25:04 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2014-08-03 12:27:57 +00:00
|
|
|
}
|
|
|
|
|
2015-03-21 03:55:28 +00:00
|
|
|
int SrsDvrSegmentPlan::on_video(SrsSharedPtrMessage* shared_video)
|
2014-08-03 12:27:57 +00:00
|
|
|
{
|
2015-02-21 08:25:04 +00:00
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
2015-03-21 03:55:28 +00:00
|
|
|
if ((ret = update_duration(shared_video)) != ERROR_SUCCESS) {
|
2015-02-21 08:25:04 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2014-08-03 12:27:57 +00:00
|
|
|
|
2015-03-21 03:55:28 +00:00
|
|
|
if ((ret = SrsDvrPlan::on_video(shared_video)) != ERROR_SUCCESS) {
|
2015-02-21 08:25:04 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2014-08-03 12:27:57 +00:00
|
|
|
}
|
|
|
|
|
2014-04-29 06:44:07 +00:00
|
|
|
int SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg)
|
2014-04-17 09:35:21 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
2014-04-23 08:33:42 +00:00
|
|
|
srs_assert(segment);
|
|
|
|
|
2014-09-25 02:59:29 +00:00
|
|
|
// ignore if duration ok.
|
2017-02-06 12:58:52 +00:00
|
|
|
if (cduration <= 0 || segment->get_duration() < cduration) {
|
2014-09-25 02:59:29 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// when wait keyframe, ignore if no frame arrived.
|
2015-11-11 02:37:50 +00:00
|
|
|
// @see https://github.com/ossrs/srs/issues/177
|
2017-02-06 12:58:52 +00:00
|
|
|
if (wait_keyframe) {
|
2014-12-07 03:25:05 +00:00
|
|
|
if (!msg->is_video()) {
|
2014-04-17 10:13:59 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2014-08-03 12:27:57 +00:00
|
|
|
|
2014-09-26 08:34:13 +00:00
|
|
|
char* payload = msg->payload;
|
|
|
|
int size = msg->size;
|
2017-02-12 12:38:39 +00:00
|
|
|
bool is_key_frame = SrsFlvVideo::h264(payload, size)
|
|
|
|
&& SrsFlvVideo::keyframe(payload, size)
|
|
|
|
&& !SrsFlvVideo::sh(payload, size);
|
2014-09-25 02:59:29 +00:00
|
|
|
if (!is_key_frame) {
|
2014-08-03 12:27:57 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2014-04-17 09:35:21 +00:00
|
|
|
}
|
|
|
|
|
2014-09-25 02:59:29 +00:00
|
|
|
// reap segment
|
2015-02-21 08:25:04 +00:00
|
|
|
if ((ret = segment->close()) != ERROR_SUCCESS) {
|
2014-09-25 02:59:29 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// open new flv file
|
2017-02-06 12:22:07 +00:00
|
|
|
if ((ret = segment->open()) != ERROR_SUCCESS) {
|
2014-09-25 02:59:29 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// update sequence header
|
2017-02-06 12:58:52 +00:00
|
|
|
if ((ret = hub->on_dvr_request_sh()) != ERROR_SUCCESS) {
|
2014-09-25 02:59:29 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2017-02-06 12:58:52 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsDvrSegmentPlan::on_reload_vhost_dvr(string vhost)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if (req->vhost != vhost) {
|
2014-09-25 02:59:29 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-06 12:58:52 +00:00
|
|
|
wait_keyframe = _srs_config->get_dvr_wait_keyframe(req->vhost);
|
|
|
|
|
|
|
|
cduration = _srs_config->get_dvr_duration(req->vhost);
|
|
|
|
// to ms
|
|
|
|
cduration *= 1000;
|
|
|
|
|
2014-04-17 09:35:21 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-02-27 12:39:36 +00:00
|
|
|
SrsDvr::SrsDvr()
|
2014-04-17 08:57:04 +00:00
|
|
|
{
|
2017-01-19 04:38:55 +00:00
|
|
|
hub = NULL;
|
2014-04-17 08:57:04 +00:00
|
|
|
plan = NULL;
|
2015-09-15 07:58:57 +00:00
|
|
|
req = NULL;
|
|
|
|
actived = false;
|
|
|
|
|
|
|
|
_srs_config->subscribe(this);
|
2014-04-17 08:57:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsDvr::~SrsDvr()
|
|
|
|
{
|
2015-09-15 07:58:57 +00:00
|
|
|
_srs_config->unsubscribe(this);
|
|
|
|
|
2014-04-17 08:57:04 +00:00
|
|
|
srs_freep(plan);
|
|
|
|
}
|
|
|
|
|
2017-01-19 04:38:55 +00:00
|
|
|
int SrsDvr::initialize(SrsOriginHub* h, SrsRequest* r)
|
2014-04-17 08:57:04 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
2015-02-27 12:39:36 +00:00
|
|
|
|
2015-09-15 07:58:57 +00:00
|
|
|
req = r;
|
2017-01-19 04:38:55 +00:00
|
|
|
hub = h;
|
2014-04-17 08:57:04 +00:00
|
|
|
|
2015-09-15 07:58:57 +00:00
|
|
|
SrsConfDirective* conf = _srs_config->get_dvr_apply(r->vhost);
|
|
|
|
actived = srs_config_apply_filter(conf, r);
|
|
|
|
|
2014-04-17 08:57:04 +00:00
|
|
|
srs_freep(plan);
|
2017-02-06 10:33:26 +00:00
|
|
|
if ((ret = SrsDvrPlan::create_plan(r->vhost, &plan)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string path = _srs_config->get_dvr_path(r->vhost);
|
|
|
|
SrsDvrSegmenter* segmenter = NULL;
|
|
|
|
if (srs_string_ends_with(path, ".mp4")) {
|
|
|
|
segmenter = new SrsDvrMp4Segmenter();
|
|
|
|
} else {
|
|
|
|
segmenter = new SrsDvrFlvSegmenter();
|
|
|
|
}
|
|
|
|
|
2017-02-06 12:58:52 +00:00
|
|
|
if ((ret = plan->initialize(hub, segmenter, r)) != ERROR_SUCCESS) {
|
2015-02-27 12:39:36 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2014-04-17 08:57:04 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-11 13:14:28 +00:00
|
|
|
int SrsDvr::on_publish()
|
2014-04-17 08:57:04 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
2015-09-15 07:58:57 +00:00
|
|
|
// the dvr for this stream is not actived.
|
|
|
|
if (!actived) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-04-17 10:13:59 +00:00
|
|
|
if ((ret = plan->on_publish()) != ERROR_SUCCESS) {
|
2014-04-17 08:57:04 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SrsDvr::on_unpublish()
|
|
|
|
{
|
|
|
|
plan->on_unpublish();
|
|
|
|
}
|
|
|
|
|
2017-02-06 12:58:52 +00:00
|
|
|
int SrsDvr::on_meta_data(SrsSharedPtrMessage* metadata)
|
2014-04-17 08:57:04 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
2015-09-15 07:58:57 +00:00
|
|
|
|
|
|
|
// the dvr for this stream is not actived.
|
|
|
|
if (!actived) {
|
|
|
|
return ret;
|
|
|
|
}
|
2015-02-21 11:14:05 +00:00
|
|
|
|
2017-02-06 12:58:52 +00:00
|
|
|
if ((ret = plan->on_meta_data(metadata)) != ERROR_SUCCESS) {
|
2014-04-17 08:57:04 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-03-21 03:55:28 +00:00
|
|
|
int SrsDvr::on_audio(SrsSharedPtrMessage* shared_audio)
|
2014-04-17 08:57:04 +00:00
|
|
|
{
|
2015-09-15 07:58:57 +00:00
|
|
|
// the dvr for this stream is not actived.
|
|
|
|
if (!actived) {
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2015-03-21 03:55:28 +00:00
|
|
|
return plan->on_audio(shared_audio);
|
2014-04-17 08:57:04 +00:00
|
|
|
}
|
|
|
|
|
2015-03-21 03:55:28 +00:00
|
|
|
int SrsDvr::on_video(SrsSharedPtrMessage* shared_video)
|
2014-04-17 08:57:04 +00:00
|
|
|
{
|
2015-09-15 07:58:57 +00:00
|
|
|
// the dvr for this stream is not actived.
|
|
|
|
if (!actived) {
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2015-03-21 03:55:28 +00:00
|
|
|
return plan->on_video(shared_video);
|
2014-04-17 08:57:04 +00:00
|
|
|
}
|
|
|
|
|
2015-09-15 07:58:57 +00:00
|
|
|
int SrsDvr::on_reload_vhost_dvr_apply(string vhost)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
SrsConfDirective* conf = _srs_config->get_dvr_apply(req->vhost);
|
|
|
|
bool v = srs_config_apply_filter(conf, req);
|
|
|
|
|
|
|
|
// the apply changed, republish the dvr.
|
2017-02-11 13:14:28 +00:00
|
|
|
if (v == actived) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
actived = v;
|
|
|
|
|
|
|
|
on_unpublish();
|
|
|
|
if (!actived) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ret = on_publish()) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
if ((ret = hub->on_dvr_request_sh()) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
2015-09-15 07:58:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-08-02 14:18:39 +00:00
|
|
|
|