1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-02-15 04:42:04 +00:00
srs/trunk/src/app/srs_app_dvr.cpp

1055 lines
26 KiB
C++
Raw Normal View History

2017-03-25 09:21:39 +00:00
/**
* The MIT License (MIT)
*
2019-12-30 02:10:35 +00:00
* Copyright (c) 2013-2020 Winlin
2017-03-25 09:21:39 +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-17 08:06:49 +00:00
#include <fcntl.h>
2014-04-17 09:35:21 +00:00
#include <sstream>
#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>
#include <srs_kernel_flv.hpp>
#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>
#include <srs_app_utility.hpp>
2017-02-06 10:33:26 +00:00
#include <srs_kernel_mp4.hpp>
2017-03-18 13:41:01 +00:00
#include <srs_app_fragment.hpp>
2017-02-06 10:33:26 +00:00
SrsDvrSegmenter::SrsDvrSegmenter()
2014-04-23 08:33:42 +00:00
{
req = NULL;
jitter = NULL;
2017-02-06 10:33:26 +00:00
plan = NULL;
2017-03-18 13:41:01 +00:00
fragment = new SrsFragment();
fs = new SrsFileWriter();
jitter_algorithm = SrsRtmpJitterAlgorithmOFF;
2017-03-25 09:21:39 +00:00
_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
{
_srs_config->unsubscribe(this);
2017-03-25 09:21:39 +00:00
2017-03-18 13:41:01 +00:00
srs_freep(fragment);
srs_freep(jitter);
srs_freep(fs);
2014-06-28 16:09:55 +00:00
}
2017-06-11 01:40:07 +00:00
srs_error_t SrsDvrSegmenter::initialize(SrsDvrPlan* p, SrsRequest* r)
2014-04-23 08:33:42 +00:00
{
req = r;
2017-02-06 10:33:26 +00:00
plan = p;
jitter_algorithm = (SrsRtmpJitterAlgorithm)_srs_config->get_dvr_time_jitter(req->vhost);
wait_keyframe = _srs_config->get_dvr_wait_keyframe(req->vhost);
2017-03-25 09:21:39 +00:00
2017-06-11 01:40:07 +00:00
return srs_success;
2014-04-23 08:33:42 +00:00
}
2017-03-18 13:41:01 +00:00
SrsFragment* SrsDvrSegmenter::current()
2017-02-06 10:33:26 +00:00
{
2017-03-18 13:41:01 +00:00
return fragment;
2014-04-15 06:24:03 +00:00
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrSegmenter::open()
2014-04-15 06:24:03 +00:00
{
srs_error_t err = srs_success;
// ignore when already open.
if (fs->is_open()) {
2017-09-23 14:12:33 +00:00
return err;
}
2017-03-25 09:21:39 +00:00
2017-03-18 13:41:01 +00:00
string path = generate_path();
if (srs_path_exists(path)) {
2017-09-23 14:12:33 +00:00
return srs_error_new(ERROR_DVR_CANNOT_APPEND, "DVR can't append to exists path=%s", path.c_str());
2017-02-06 10:33:26 +00:00
}
2017-03-18 13:41:01 +00:00
fragment->set_path(path);
2017-02-06 12:58:52 +00:00
// create dir first.
if ((err = fragment->create_dir()) != srs_success) {
2017-09-23 14:12:33 +00:00
return srs_error_wrap(err, "create dir");
}
2017-03-25 09:21:39 +00:00
// create jitter.
2017-02-06 12:58:52 +00:00
srs_freep(jitter);
jitter = new SrsRtmpJitter();
// open file writer, in append or create mode.
2017-03-18 13:41:01 +00:00
string tmp_dvr_file = fragment->tmppath();
2018-01-01 14:32:54 +00:00
if ((err = fs->open(tmp_dvr_file)) != srs_success) {
return srs_error_wrap(err, "open file %s", path.c_str());
}
2017-03-25 09:21:39 +00:00
// initialize the encoder.
2017-09-23 14:12:33 +00:00
if ((err = open_encoder()) != srs_success) {
return srs_error_wrap(err, "open encoder");
}
srs_trace("dvr stream %s to file %s", req->stream.c_str(), path.c_str());
2017-09-23 14:12:33 +00:00
return err;
2014-04-15 06:24:03 +00:00
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrSegmenter::write_metadata(SrsSharedPtrMessage* metadata)
{
return encode_metadata(metadata);
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrSegmenter::write_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format)
{
2017-09-23 14:12:33 +00:00
srs_error_t err = srs_success;
SrsSharedPtrMessage* audio = shared_audio->copy();
SrsAutoFree(SrsSharedPtrMessage, audio);
2017-09-23 14:12:33 +00:00
if ((err = jitter->correct(audio, jitter_algorithm)) != srs_success) {
return srs_error_wrap(err, "jitter");
}
2017-09-23 14:12:33 +00:00
if ((err = on_update_duration(audio)) != srs_success) {
return srs_error_wrap(err, "update duration");
}
2017-09-23 14:12:33 +00:00
if ((err = encode_audio(audio, format)) != srs_success) {
return srs_error_wrap(err, "encode audio");
}
2017-09-23 14:12:33 +00:00
return err;
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrSegmenter::write_video(SrsSharedPtrMessage* shared_video, SrsFormat* format)
{
2017-09-23 14:12:33 +00:00
srs_error_t err = srs_success;
SrsSharedPtrMessage* video = shared_video->copy();
SrsAutoFree(SrsSharedPtrMessage, video);
2017-09-23 14:12:33 +00:00
if ((err = jitter->correct(video, jitter_algorithm)) != srs_success) {
return srs_error_wrap(err, "jitter");
}
2017-09-23 14:12:33 +00:00
if ((err = encode_video(video, format)) != srs_success) {
return srs_error_wrap(err, "encode video");
}
2017-09-23 14:12:33 +00:00
if ((err = on_update_duration(video)) != srs_success) {
return srs_error_wrap(err, "update duration");
}
2017-09-23 14:12:33 +00:00
return err;
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrSegmenter::close()
2014-04-15 06:24:03 +00:00
{
srs_error_t err = srs_success;
2014-04-17 08:06:49 +00:00
// ignore when already closed.
if (!fs->is_open()) {
2017-09-23 14:12:33 +00:00
return err;
}
2017-03-25 09:21:39 +00:00
2017-02-06 10:33:26 +00:00
// Close the encoder, then close the fs object.
2017-09-23 14:12:33 +00:00
if ((err = close_encoder()) != srs_success) {
return srs_error_wrap(err, "close encoder");
}
fs->close();
// when tmp flv file exists, reap it.
if ((err = fragment->rename()) != srs_success) {
2017-09-23 14:12:33 +00:00
return srs_error_wrap(err, "rename fragment");
}
2017-03-25 09:21:39 +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.
if ((err = plan->on_reap_segment()) != srs_success) {
2017-09-23 14:12:33 +00:00
return srs_error_wrap(err, "reap segment");
}
2017-03-25 09:21:39 +00:00
2017-09-23 14:12:33 +00:00
return err;
}
2017-02-06 10:33:26 +00:00
string SrsDvrSegmenter::generate_path()
{
2017-03-25 09:21:39 +00:00
// the path in config, for example,
2017-02-06 10:33:26 +00:00
// /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
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);
2017-03-25 09:21:39 +00:00
2017-02-06 10:33:26 +00:00
return flv_path;
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrSegmenter::on_update_duration(SrsSharedPtrMessage* msg)
2017-02-06 10:33:26 +00:00
{
2017-09-22 08:14:30 +00:00
fragment->append(msg->timestamp);
2017-09-23 14:12:33 +00:00
return srs_success;
2017-09-22 08:14:30 +00:00
}
srs_error_t SrsDvrSegmenter::on_reload_vhost_dvr(std::string vhost)
{
srs_error_t err = srs_success;
if (req->vhost != vhost) {
2017-09-22 08:14:30 +00:00
return err;
}
2017-02-06 10:33:26 +00:00
jitter_algorithm = (SrsRtmpJitterAlgorithm)_srs_config->get_dvr_time_jitter(req->vhost);
wait_keyframe = _srs_config->get_dvr_wait_keyframe(req->vhost);
2017-02-06 10:33:26 +00:00
2017-09-22 08:14:30 +00:00
return err;
2017-02-06 10:33:26 +00:00
}
SrsDvrFlvSegmenter::SrsDvrFlvSegmenter()
{
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-09-23 14:12:33 +00:00
srs_error_t SrsDvrFlvSegmenter::refresh_metadata()
2017-02-06 10:33:26 +00:00
{
2017-09-23 14:12:33 +00:00
srs_error_t err = srs_success;
2017-02-06 10:33:26 +00:00
// no duration or filesize specified.
if (!duration_offset || !filesize_offset) {
2017-09-23 14:12:33 +00:00
return err;
2017-02-06 10:33:26 +00:00
}
int64_t cur = fs->tellg();
// buffer to write the size.
char* buf = new char[SrsAmf0Size::number()];
SrsAutoFreeA(char, buf);
2018-01-01 14:32:54 +00:00
SrsBuffer stream(buf, SrsAmf0Size::number());
2017-02-06 10:33:26 +00:00
// filesize to buf.
SrsAmf0Any* size = SrsAmf0Any::number((double)cur);
SrsAutoFree(SrsAmf0Any, size);
stream.skip(-1 * stream.pos());
2018-01-01 14:32:54 +00:00
if ((err = size->write(&stream)) != srs_success) {
return srs_error_wrap(err, "write filesize");
}
// update the flesize.
fs->seek2(filesize_offset);
2018-01-01 14:32:54 +00:00
if ((err = fs->write(buf, SrsAmf0Size::number(), NULL)) != srs_success) {
return srs_error_wrap(err, "update filesize");
}
// duration to buf
SrsAmf0Any* dur = SrsAmf0Any::number((double)srsu2ms(fragment->duration()) / 1000.0);
SrsAutoFree(SrsAmf0Any, dur);
stream.skip(-1 * stream.pos());
2018-01-01 14:32:54 +00:00
if ((err = dur->write(&stream)) != srs_success) {
return srs_error_wrap(err, "write duration");
}
// update the duration
fs->seek2(duration_offset);
2018-01-01 14:32:54 +00:00
if ((err = fs->write(buf, SrsAmf0Size::number(), NULL)) != srs_success) {
return srs_error_wrap(err, "update duration");
}
// reset the offset.
fs->seek2(cur);
2017-02-06 10:33:26 +00:00
2017-09-23 14:12:33 +00:00
return err;
2017-02-06 10:33:26 +00:00
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrFlvSegmenter::open_encoder()
2017-02-06 10:33:26 +00:00
{
2017-09-23 14:12:33 +00:00
srs_error_t err = srs_success;
2017-02-06 10:33:26 +00:00
has_keyframe = false;
2017-02-06 10:33:26 +00:00
// update the duration and filesize offset.
duration_offset = 0;
filesize_offset = 0;
2017-02-06 10:33:26 +00:00
srs_freep(enc);
enc = new SrsFlvTransmuxer();
2018-01-01 14:32:54 +00:00
if ((err = enc->initialize(fs)) != srs_success) {
return srs_error_wrap(err, "init encoder");
}
2017-03-25 09:21:39 +00:00
// write the flv header to writer.
2018-01-01 14:32:54 +00:00
if ((err = enc->write_header()) != srs_success) {
return srs_error_wrap(err, "write flv header");
}
2017-02-06 10:33:26 +00:00
2017-09-23 14:12:33 +00:00
return err;
2017-02-06 10:33:26 +00:00
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrFlvSegmenter::encode_metadata(SrsSharedPtrMessage* metadata)
2017-02-06 10:33:26 +00:00
{
2017-09-23 14:12:33 +00:00
srs_error_t err = srs_success;
2017-02-06 10:33:26 +00:00
// Ignore when metadata already written.
if (duration_offset || filesize_offset) {
2017-09-23 14:12:33 +00:00
return err;
2017-02-06 10:33:26 +00:00
}
2018-01-01 14:32:54 +00:00
SrsBuffer stream(metadata->payload, metadata->size);
2017-02-06 10:33:26 +00:00
SrsAmf0Any* name = SrsAmf0Any::str();
SrsAutoFree(SrsAmf0Any, name);
2018-01-01 14:32:54 +00:00
if ((err = name->read(&stream)) != srs_success) {
return srs_error_wrap(err, "read name");
2014-04-17 10:13:59 +00:00
}
2017-02-06 10:33:26 +00:00
SrsAmf0Object* obj = SrsAmf0Any::object();
SrsAutoFree(SrsAmf0Object, obj);
2018-01-01 14:32:54 +00:00
if ((err = obj->read(&stream)) != srs_success) {
return srs_error_wrap(err, "read object");
}
2017-02-06 10:33:26 +00:00
// remove duration and filesize.
obj->set("filesize", NULL);
obj->set("duration", NULL);
2017-02-06 10:33:26 +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];
SrsAutoFreeA(char, payload);
2017-02-06 10:33:26 +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
// convert metadata to bytes.
2018-01-01 14:32:54 +00:00
SrsBuffer buf(payload, size);
if ((err = name->write(&buf)) != srs_success) {
return srs_error_wrap(err, "write name");
}
2018-01-01 14:32:54 +00:00
if ((err = obj->write(&buf)) != srs_success) {
return srs_error_wrap(err, "write object");
}
2014-04-17 10:13:59 +00:00
// to flv file.
2018-01-01 14:32:54 +00:00
if ((err = enc->write_metadata(18, payload, size)) != srs_success) {
return srs_error_wrap(err, "write metadata");
2014-04-17 10:13:59 +00:00
}
2017-09-23 14:12:33 +00:00
return err;
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrFlvSegmenter::encode_audio(SrsSharedPtrMessage* audio, SrsFormat* format)
{
2017-09-23 14:12:33 +00:00
srs_error_t err = srs_success;
2017-02-06 10:33:26 +00:00
char* payload = audio->payload;
int size = audio->size;
2018-01-01 14:32:54 +00:00
if ((err = enc->write_audio(audio->timestamp, payload, size)) != srs_success) {
return srs_error_wrap(err, "write audio");
}
2017-02-06 10:33:26 +00:00
2017-09-23 14:12:33 +00:00
return err;
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrFlvSegmenter::encode_video(SrsSharedPtrMessage* video, SrsFormat* format)
{
2017-09-23 14:12:33 +00:00
srs_error_t err = srs_success;
2017-02-06 10:33:26 +00:00
char* payload = video->payload;
int size = video->size;
2017-06-04 07:10:35 +00:00
bool sh = (format->video->avc_packet_type == SrsVideoAvcFrameTraitSequenceHeader);
bool keyframe = (!sh && format->video->frame_type == SrsVideoAvcFrameTypeKeyFrame);
if (keyframe) {
has_keyframe = true;
}
2017-02-06 10:33:26 +00:00
// accept the sequence header here.
// when got no keyframe, ignore when should wait keyframe.
2017-09-23 14:12:33 +00:00
if (!has_keyframe && !sh && wait_keyframe) {
return err;
}
2018-01-01 14:32:54 +00:00
if ((err = enc->write_video(video->timestamp, payload, size)) != srs_success) {
return srs_error_wrap(err, "write video");
}
2017-09-23 14:12:33 +00:00
return err;
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrFlvSegmenter::close_encoder()
{
return refresh_metadata();
}
2017-02-06 10:33:26 +00:00
SrsDvrMp4Segmenter::SrsDvrMp4Segmenter()
{
2017-02-06 10:33:26 +00:00
enc = new SrsMp4Encoder();
}
2017-02-06 10:33:26 +00:00
SrsDvrMp4Segmenter::~SrsDvrMp4Segmenter()
{
2017-02-06 10:33:26 +00:00
srs_freep(enc);
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrMp4Segmenter::refresh_metadata()
{
2017-09-23 14:12:33 +00:00
return srs_success;
2017-02-06 10:33:26 +00:00
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrMp4Segmenter::open_encoder()
2017-02-06 10:33:26 +00:00
{
2017-09-23 14:12:33 +00:00
srs_error_t err = srs_success;
srs_freep(enc);
enc = new SrsMp4Encoder();
2018-01-01 14:32:54 +00:00
if ((err = enc->initialize(fs)) != srs_success) {
return srs_error_wrap(err, "init encoder");
}
2017-09-23 14:12:33 +00:00
return err;
2017-02-06 10:33:26 +00:00
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrMp4Segmenter::encode_metadata(SrsSharedPtrMessage* /*metadata*/)
2017-02-06 10:33:26 +00:00
{
2017-09-23 14:12:33 +00:00
return srs_success;
2017-02-06 10:33:26 +00:00
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrMp4Segmenter::encode_audio(SrsSharedPtrMessage* audio, SrsFormat* format)
2017-02-06 10:33:26 +00:00
{
2017-09-23 14:12:33 +00:00
srs_error_t err = srs_success;
2017-06-04 07:10:35 +00:00
SrsAudioCodecId sound_format = format->acodec->id;
SrsAudioSampleRate sound_rate = format->acodec->sound_rate;
SrsAudioSampleBits sound_size = format->acodec->sound_size;
SrsAudioChannels channels = format->acodec->sound_type;
2017-06-04 07:10:35 +00:00
SrsAudioAacFrameTrait ct = format->audio->aac_packet_type;
2017-02-12 12:38:39 +00:00
if (ct == SrsAudioAacFrameTraitSequenceHeader) {
enc->acodec = sound_format;
enc->sample_rate = sound_rate;
enc->sound_bits = sound_size;
enc->channels = channels;
}
2017-06-04 07:10:35 +00:00
uint8_t* sample = (uint8_t*)format->raw;
uint32_t nb_sample = (uint32_t)format->nb_raw;
uint32_t dts = (uint32_t)audio->timestamp;
if ((err = enc->write_sample(format, SrsMp4HandlerTypeSOUN, 0x00, ct, dts, dts, sample, nb_sample)) != srs_success) {
2018-01-01 14:32:54 +00:00
return srs_error_wrap(err, "write sample");
2017-09-23 14:12:33 +00:00
}
return err;
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrMp4Segmenter::encode_video(SrsSharedPtrMessage* video, SrsFormat* format)
{
2017-09-23 14:12:33 +00:00
srs_error_t err = srs_success;
2017-06-04 07:10:35 +00:00
SrsVideoAvcFrameType frame_type = format->video->frame_type;
SrsVideoCodecId codec_id = format->vcodec->id;
2017-06-04 07:10:35 +00:00
SrsVideoAvcFrameTrait ct = format->video->avc_packet_type;
uint32_t cts = (uint32_t)format->video->cts;
2017-02-12 12:38:39 +00:00
if (ct == SrsVideoAvcFrameTraitSequenceHeader) {
enc->vcodec = codec_id;
}
uint32_t dts = (uint32_t)video->timestamp;
uint32_t pts = dts + cts;
2017-06-04 07:10:35 +00:00
uint8_t* sample = (uint8_t*)format->raw;
uint32_t nb_sample = (uint32_t)format->nb_raw;
if ((err = enc->write_sample(format, SrsMp4HandlerTypeVIDE, frame_type, ct, dts, pts, sample, nb_sample)) != srs_success) {
2018-01-01 14:32:54 +00:00
return srs_error_wrap(err, "write sample");
2017-09-23 14:12:33 +00:00
}
return err;
2014-04-17 10:13:59 +00:00
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrMp4Segmenter::close_encoder()
{
2017-09-23 14:12:33 +00:00
srs_error_t err = srs_success;
2018-01-01 14:32:54 +00:00
if ((err = enc->flush()) != srs_success) {
return srs_error_wrap(err, "flush encoder");
}
2017-09-23 14:12:33 +00:00
return err;
}
SrsDvrAsyncCallOnDvr::SrsDvrAsyncCallOnDvr(int c, SrsRequest* r, string p)
{
cid = c;
2015-05-22 23:46:45 +00:00
req = r->copy();
path = p;
}
SrsDvrAsyncCallOnDvr::~SrsDvrAsyncCallOnDvr()
{
2015-05-22 23:46:45 +00:00
srs_freep(req);
}
srs_error_t SrsDvrAsyncCallOnDvr::call()
{
srs_error_t err = srs_success;
if (!_srs_config->get_vhost_http_hooks_enabled(req->vhost)) {
return err;
}
// 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
vector<string> hooks;
if (true) {
SrsConfDirective* conf = _srs_config->get_vhost_on_dvr(req->vhost);
2017-09-23 14:12:33 +00:00
if (conf) {
hooks = conf->args;
}
}
for (int i = 0; i < (int)hooks.size(); i++) {
std::string url = hooks.at(i);
if ((err = SrsHttpHooks::on_dvr(cid, url, req, path)) != srs_success) {
return srs_error_wrap(err, "callback on_dvr %s", url.c_str());
}
}
2017-03-25 09:21:39 +00:00
return err;
}
string SrsDvrAsyncCallOnDvr::to_string()
{
std::stringstream ss;
ss << "vhost=" << req->vhost << ", file=" << path;
return ss.str();
}
SrsDvrPlan::SrsDvrPlan()
{
req = NULL;
2017-03-25 09:21:39 +00:00
dvr_enabled = false;
2017-02-06 10:33:26 +00:00
segment = NULL;
async = new SrsAsyncCallWorker();
}
SrsDvrPlan::~SrsDvrPlan()
2014-04-15 06:24:03 +00:00
{
srs_freep(segment);
srs_freep(async);
2014-04-15 06:24:03 +00:00
}
2017-06-11 01:40:07 +00:00
srs_error_t SrsDvrPlan::initialize(SrsOriginHub* h, SrsDvrSegmenter* s, SrsRequest* r)
2014-04-15 06:24:03 +00:00
{
2017-06-11 01:40:07 +00:00
srs_error_t err = srs_success;
2017-02-06 12:58:52 +00:00
hub = h;
req = r;
2017-02-06 10:33:26 +00:00
segment = s;
2017-03-25 09:21:39 +00:00
2017-06-11 01:40:07 +00:00
if ((err = segment->initialize(this, r)) != srs_success) {
return srs_error_wrap(err, "segmenter");
2014-04-17 10:13:59 +00:00
}
2017-03-25 09:21:39 +00:00
return err;
}
srs_error_t SrsDvrPlan::on_publish()
{
srs_error_t err = srs_success;
if ((err = async->start()) != srs_success) {
return srs_error_wrap(err, "async");
}
2017-06-11 01:40:07 +00:00
return err;
2014-04-15 06:24:03 +00:00
}
void SrsDvrPlan::on_unpublish()
{
async->stop();
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrPlan::on_meta_data(SrsSharedPtrMessage* shared_metadata)
2014-04-22 09:32:45 +00:00
{
2017-09-23 14:12:33 +00:00
srs_error_t err = srs_success;
if (!dvr_enabled) {
2017-09-23 14:12:33 +00:00
return err;
}
return segment->write_metadata(shared_metadata);
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrPlan::on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format)
{
2017-09-23 14:12:33 +00:00
srs_error_t err = srs_success;
if (!dvr_enabled) {
2017-09-23 14:12:33 +00:00
return err;
}
2017-03-25 09:21:39 +00:00
2017-09-23 14:12:33 +00:00
if ((err = segment->write_audio(shared_audio, format)) != srs_success) {
return srs_error_wrap(err, "write audio");
2014-04-24 04:22:36 +00:00
}
2017-09-23 14:12:33 +00:00
return err;
2014-04-22 09:32:45 +00:00
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrPlan::on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format)
{
2017-09-23 14:12:33 +00:00
srs_error_t err = srs_success;
if (!dvr_enabled) {
2017-09-23 14:12:33 +00:00
return err;
}
2017-03-25 09:21:39 +00:00
2017-09-23 14:12:33 +00:00
if ((err = segment->write_video(shared_video, format)) != srs_success) {
return srs_error_wrap(err, "write video");
}
2017-09-23 14:12:33 +00:00
return err;
}
srs_error_t SrsDvrPlan::on_reap_segment()
{
srs_error_t err = srs_success;
2017-03-25 09:21:39 +00:00
int cid = _srs_context->get_id();
2017-03-18 13:41:01 +00:00
SrsFragment* fragment = segment->current();
string fullpath = fragment->fullpath();
if ((err = async->execute(new SrsDvrAsyncCallOnDvr(cid, req, fullpath))) != srs_success) {
return srs_error_wrap(err, "reap segment");
}
2017-03-25 09:21:39 +00:00
return err;
}
2017-06-11 01:40:07 +00:00
srs_error_t SrsDvrPlan::create_plan(string vhost, SrsDvrPlan** pplan)
{
2014-04-17 09:35:21 +00:00
std::string plan = _srs_config->get_dvr_plan(vhost);
if (srs_config_dvr_is_plan_segment(plan)) {
2017-02-06 10:33:26 +00:00
*pplan = new SrsDvrSegmentPlan();
} 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-06-11 01:40:07 +00:00
return srs_error_new(ERROR_DVR_ILLEGAL_PLAN, "illegal plan=%s, vhost=%s",
plan.c_str(), vhost.c_str());
2014-04-17 09:35:21 +00:00
}
2017-02-06 10:33:26 +00:00
2017-06-11 01:40:07 +00:00
return srs_success;
}
SrsDvrSessionPlan::SrsDvrSessionPlan()
{
}
SrsDvrSessionPlan::~SrsDvrSessionPlan()
{
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrSessionPlan::on_publish()
{
2017-09-23 14:12:33 +00:00
srs_error_t err = srs_success;
if ((err = SrsDvrPlan::on_publish()) != srs_success) {
return err;
}
// support multiple publish.
if (dvr_enabled) {
2017-09-23 14:12:33 +00:00
return err;
}
2017-03-25 09:21:39 +00:00
if (!_srs_config->get_dvr_enabled(req->vhost)) {
2017-09-23 14:12:33 +00:00
return err;
}
2017-03-25 09:21:39 +00:00
2017-09-23 14:12:33 +00:00
if ((err = segment->close()) != srs_success) {
return srs_error_wrap(err, "close segment");
}
2017-03-25 09:21:39 +00:00
2017-09-23 14:12:33 +00:00
if ((err = segment->open()) != srs_success) {
return srs_error_wrap(err, "open segment");
}
2017-03-25 09:21:39 +00:00
dvr_enabled = true;
2017-03-25 09:21:39 +00:00
2017-09-23 14:12:33 +00:00
return err;
}
void SrsDvrSessionPlan::on_unpublish()
{
// support multiple publish.
if (!dvr_enabled) {
return;
}
// ignore error.
2017-09-23 14:12:33 +00:00
srs_error_t err = segment->close();
if (err != srs_success) {
srs_warn("ignore flv close error %s", srs_error_desc(err).c_str());
}
dvr_enabled = false;
// We should notify the on_dvr, then stop the async.
// @see https://github.com/ossrs/srs/issues/1601
SrsDvrPlan::on_unpublish();
}
2014-04-17 09:35:21 +00:00
SrsDvrSegmentPlan::SrsDvrSegmentPlan()
{
cduration = 0;
2017-02-06 12:58:52 +00:00
wait_keyframe = false;
2014-04-17 09:35:21 +00:00
}
SrsDvrSegmentPlan::~SrsDvrSegmentPlan()
{
}
2017-06-11 01:40:07 +00:00
srs_error_t SrsDvrSegmentPlan::initialize(SrsOriginHub* h, SrsDvrSegmenter* s, SrsRequest* r)
2014-04-17 09:35:21 +00:00
{
2017-06-11 01:40:07 +00:00
srs_error_t err = srs_success;
2014-04-17 09:35:21 +00:00
2017-06-11 01:40:07 +00:00
if ((err = SrsDvrPlan::initialize(h, s, r)) != srs_success) {
return srs_error_wrap(err, "segment plan");
2014-04-17 09:35:21 +00:00
}
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
2017-06-11 01:40:07 +00:00
return srs_success;
2014-04-17 09:35:21 +00:00
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrSegmentPlan::on_publish()
2014-04-17 09:35:21 +00:00
{
2017-09-23 14:12:33 +00:00
srs_error_t err = srs_success;
if ((err = SrsDvrPlan::on_publish()) != srs_success) {
return err;
}
2014-04-17 09:35:21 +00:00
// support multiple publish.
if (dvr_enabled) {
2017-09-23 14:12:33 +00:00
return err;
2014-04-17 09:35:21 +00:00
}
2017-03-25 09:21:39 +00:00
if (!_srs_config->get_dvr_enabled(req->vhost)) {
2017-09-23 14:12:33 +00:00
return err;
}
2017-03-25 09:21:39 +00:00
2017-09-23 14:12:33 +00:00
if ((err = segment->close()) != srs_success) {
return srs_error_wrap(err, "segment close");
}
2017-03-25 09:21:39 +00:00
2017-09-23 14:12:33 +00:00
if ((err = segment->open()) != srs_success) {
return srs_error_wrap(err, "segment open");
}
2017-03-25 09:21:39 +00:00
dvr_enabled = true;
2017-03-25 09:21:39 +00:00
2017-09-23 14:12:33 +00:00
return err;
2014-04-17 09:35:21 +00:00
}
void SrsDvrSegmentPlan::on_unpublish()
{
srs_error_t err = srs_success;
if ((err = segment->close()) != srs_success) {
srs_warn("ignore err %s", srs_error_desc(err).c_str());
srs_freep(err);
}
dvr_enabled = false;
// We should notify the on_dvr, then stop the async.
// @see https://github.com/ossrs/srs/issues/1601
SrsDvrPlan::on_unpublish();
2014-04-17 09:35:21 +00:00
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrSegmentPlan::on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format)
{
2017-09-23 14:12:33 +00:00
srs_error_t err = srs_success;
2017-03-25 09:21:39 +00:00
2017-09-23 14:12:33 +00:00
if ((err = update_duration(shared_audio)) != srs_success) {
return srs_error_wrap(err, "update duration");
}
2017-09-23 14:12:33 +00:00
if ((err = SrsDvrPlan::on_audio(shared_audio, format)) != srs_success) {
return srs_error_wrap(err, "consume audio");
}
2017-03-25 09:21:39 +00:00
2017-09-23 14:12:33 +00:00
return err;
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrSegmentPlan::on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format)
{
2017-09-23 14:12:33 +00:00
srs_error_t err = srs_success;
2017-03-25 09:21:39 +00:00
2017-09-23 14:12:33 +00:00
if ((err = update_duration(shared_video)) != srs_success) {
return srs_error_wrap(err, "update duration");
}
2017-09-23 14:12:33 +00:00
if ((err = SrsDvrPlan::on_video(shared_video, format)) != srs_success) {
return srs_error_wrap(err, "consume video");
}
2017-03-25 09:21:39 +00:00
2017-09-23 14:12:33 +00:00
return err;
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg)
2014-04-17 09:35:21 +00:00
{
2017-09-23 14:12:33 +00:00
srs_error_t err = srs_success;
2014-04-17 09:35:21 +00:00
2014-04-23 08:33:42 +00:00
srs_assert(segment);
// ignore if duration ok.
2017-03-18 13:41:01 +00:00
SrsFragment* fragment = segment->current();
if (cduration <= 0 || fragment->duration() < cduration) {
2017-09-23 14:12:33 +00:00
return err;
}
// 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) {
if (!msg->is_video()) {
2017-09-23 14:12:33 +00:00
return err;
2014-04-17 10:13:59 +00:00
}
char* payload = msg->payload;
int size = msg->size;
2017-09-23 14:12:33 +00:00
bool is_key_frame = SrsFlvVideo::h264(payload, size) && SrsFlvVideo::keyframe(payload, size) && !SrsFlvVideo::sh(payload, size);
if (!is_key_frame) {
2017-09-23 14:12:33 +00:00
return err;
}
2014-04-17 09:35:21 +00:00
}
// reap segment
2017-09-23 14:12:33 +00:00
if ((err = segment->close()) != srs_success) {
return srs_error_wrap(err, "segment close");
}
// open new flv file
2017-09-23 14:12:33 +00:00
if ((err = segment->open()) != srs_success) {
return srs_error_wrap(err, "segment open");
}
// update sequence header
2017-09-23 14:12:33 +00:00
if ((err = hub->on_dvr_request_sh()) != srs_success) {
return srs_error_wrap(err, "request sh");
}
2017-02-06 12:58:52 +00:00
2017-09-23 14:12:33 +00:00
return err;
2017-02-06 12:58:52 +00:00
}
2017-09-22 08:14:30 +00:00
srs_error_t SrsDvrSegmentPlan::on_reload_vhost_dvr(string vhost)
2017-02-06 12:58:52 +00:00
{
2017-09-22 08:14:30 +00:00
srs_error_t err = srs_success;
2017-02-06 12:58:52 +00:00
if (req->vhost != vhost) {
2017-09-22 08:14:30 +00:00
return err;
}
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);
2017-09-22 08:14:30 +00:00
return err;
2014-04-17 09:35:21 +00:00
}
SrsDvr::SrsDvr()
{
hub = NULL;
plan = NULL;
req = NULL;
actived = false;
_srs_config->subscribe(this);
}
SrsDvr::~SrsDvr()
{
_srs_config->unsubscribe(this);
srs_freep(plan);
}
2017-06-11 01:40:07 +00:00
srs_error_t SrsDvr::initialize(SrsOriginHub* h, SrsRequest* r)
{
2017-06-11 01:40:07 +00:00
srs_error_t err = srs_success;
2017-03-25 09:21:39 +00:00
req = r;
hub = h;
SrsConfDirective* conf = _srs_config->get_dvr_apply(r->vhost);
actived = srs_config_apply_filter(conf, r);
srs_freep(plan);
2017-06-11 01:40:07 +00:00
if ((err = SrsDvrPlan::create_plan(r->vhost, &plan)) != srs_success) {
return srs_error_wrap(err, "create plan");
2017-02-06 10:33:26 +00:00
}
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-06-11 01:40:07 +00:00
if ((err = plan->initialize(hub, segmenter, r)) != srs_success) {
return srs_error_wrap(err, "plan initialize");
}
2017-06-11 01:40:07 +00:00
return err;
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvr::on_publish()
{
2017-09-23 14:12:33 +00:00
srs_error_t err = srs_success;
// the dvr for this stream is not actived.
if (!actived) {
2017-09-23 14:12:33 +00:00
return err;
}
2017-09-23 14:12:33 +00:00
if ((err = plan->on_publish()) != srs_success) {
return srs_error_wrap(err, "publish");
}
2017-09-23 14:12:33 +00:00
return err;
}
void SrsDvr::on_unpublish()
{
plan->on_unpublish();
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvr::on_meta_data(SrsSharedPtrMessage* metadata)
{
2017-09-23 14:12:33 +00:00
srs_error_t err = srs_success;
// the dvr for this stream is not actived.
if (!actived) {
2017-09-23 14:12:33 +00:00
return err;
}
2017-03-25 09:21:39 +00:00
2017-09-23 14:12:33 +00:00
if ((err = plan->on_meta_data(metadata)) != srs_success) {
return srs_error_wrap(err, "metadata");
}
2017-09-23 14:12:33 +00:00
return err;
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvr::on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format)
{
// the dvr for this stream is not actived.
if (!actived) {
2017-09-23 14:12:33 +00:00
return srs_success;
}
2017-06-04 07:10:35 +00:00
return plan->on_audio(shared_audio, format);
}
2017-09-23 14:12:33 +00:00
srs_error_t SrsDvr::on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format)
{
// the dvr for this stream is not actived.
if (!actived) {
2017-09-23 14:12:33 +00:00
return srs_success;
}
2017-06-04 07:10:35 +00:00
return plan->on_video(shared_video, format);
}
2017-09-22 08:14:30 +00:00
srs_error_t SrsDvr::on_reload_vhost_dvr_apply(string vhost)
{
2017-09-22 08:14:30 +00:00
srs_error_t err = srs_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) {
2017-09-22 08:14:30 +00:00
return err;
2017-02-11 13:14:28 +00:00
}
actived = v;
on_unpublish();
if (!actived) {
2017-09-22 08:14:30 +00:00
return err;
2017-02-11 13:14:28 +00:00
}
2017-09-23 14:12:33 +00:00
if ((err = on_publish()) != srs_success) {
return srs_error_wrap(err, "on publish");
2017-02-11 13:14:28 +00:00
}
2017-09-23 14:12:33 +00:00
if ((err = hub->on_dvr_request_sh()) != srs_success) {
return srs_error_wrap(err, "request sh");
}
2017-09-22 08:14:30 +00:00
return err;
}
2014-08-02 14:18:39 +00:00