From 73459547e1c8e9c1ad029d9b4fc834de21eaf1a0 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 17 Apr 2014 16:06:49 +0800 Subject: [PATCH] support dvr. change to 0.9.69 --- trunk/conf/demo.conf | 8 +- trunk/conf/full.conf | 36 ++- trunk/conf/ingest.conf | 4 +- trunk/src/app/srs_app_config.cpp | 51 +++- trunk/src/app/srs_app_config.hpp | 7 + trunk/src/app/srs_app_dvr.cpp | 374 ++++++++++++++++++++++++- trunk/src/app/srs_app_dvr.hpp | 71 ++++- trunk/src/app/srs_app_source.cpp | 46 ++- trunk/src/app/srs_app_source.hpp | 2 + trunk/src/core/srs_core.hpp | 2 +- trunk/src/kernel/srs_kernel_error.hpp | 6 + trunk/src/kernel/srs_kernel_stream.cpp | 10 + trunk/src/kernel/srs_kernel_stream.hpp | 4 + 13 files changed, 600 insertions(+), 21 deletions(-) diff --git a/trunk/conf/demo.conf b/trunk/conf/demo.conf index 89fbbb6fd..2889dce9b 100644 --- a/trunk/conf/demo.conf +++ b/trunk/conf/demo.conf @@ -113,14 +113,14 @@ vhost demo.srs.com { } } ingest { - enable on; + enabled on; input { type file; url ./doc/source.200kbps.768x320.flv; } ffmpeg ./objs/ffmpeg/bin/ffmpeg; engine { - enable off; + enabled off; output rtmp://127.0.0.1:[port]/live?vhost=[vhost]/livestream; } } @@ -158,14 +158,14 @@ vhost players { } } ingest { - enable on; + enabled on; input { type file; url ./doc/source.200kbps.768x320.flv; } ffmpeg ./objs/ffmpeg/bin/ffmpeg; engine { - enable off; + enabled off; output rtmp://127.0.0.1:[port]/live?vhost=[vhost]/demo; } } diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 6fe87f542..fed04b8ab 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -91,15 +91,39 @@ http_stream { vhost __defaultVhost__ { } +# vhost for dvr +vhost dvr.srs.com { + # dvr RTMP stream to file, + # when encoder(FMLE/ffmpeg/flash) start to publish, + # start the dvr and record RTMP to file(flv). + # stop record when encoder stop to publish. + dvr { + # whether enabled dvr features + # default: off + enabled on; + # the dvr output path. + # the app dir is auto created under the dvr_path. + # for example, for rtmp stream: + # rtmp://127.0.0.1/live/livestream + # 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. + # in a word, the dvr_path is for vhost. + # default: ./objs/nginx/html + dvr_path ./objs/nginx/html; + } +} + # vhost for ingest vhost ingest.srs.com { # ingest file/stream/device then push to SRS over RTMP. # the name/id used to identify the ingest, must be unique in global. # ingest id is used in reload or http api management. ingest livestream { - # whether enable ingest features + # whether enabled ingest features # default: off - enable on; + enabled on; # input file/stream/device # @remark only support one input. input { @@ -121,7 +145,7 @@ vhost ingest.srs.com { # @see enabled of transcode engine. # if disabled or vcodec/acodec not specified, use copy. # default: off. - enable off; + enabled off; # output stream. variables: # [vhost] current vhost which start the ingest. # [port] system RTMP stream port. @@ -134,7 +158,7 @@ vhost ingest.srs.com { vhost http.srs.com { # http vhost specified config http { - # whether enable the http streaming service for vhost. + # whether enabled the http streaming service for vhost. # default: off enabled on; # the virtual directory root for this vhost to mount at @@ -743,7 +767,7 @@ vhost with-hls.srs.com { # /hls/live/livestream-1.ts the HLS media/ts file. # in a word, the hls_path is for vhost. # default: ./objs/nginx/html - hls_path /data/nginx/html; + hls_path ./objs/nginx/html; # the hls fragment in seconds, the duration of a piece of ts. # default: 10 hls_fragment 10; @@ -766,7 +790,7 @@ vhost no-hls.srs.com { vhost min.delay.com { # whether cache the last gop. # if on, cache the last gop and dispatch to client, - # to enable fast startup for client, client play immediately. + # to enabled fast startup for client, client play immediately. # if off, send the latest media data to client, # client need to wait for the next Iframe to decode and show the video. # set to off if requires min delay; diff --git a/trunk/conf/ingest.conf b/trunk/conf/ingest.conf index 9bee70f47..e6a1d659f 100644 --- a/trunk/conf/ingest.conf +++ b/trunk/conf/ingest.conf @@ -5,14 +5,14 @@ listen 1935; vhost __defaultVhost__ { ingest livestream { - enable on; + enabled on; input { type file; url ./doc/source.200kbps.768x320.flv; } ffmpeg ./objs/ffmpeg/bin/ffmpeg; engine { - enable off; + enabled off; output rtmp://127.0.0.1:[port]/live?vhost=[vhost]/livestream; } } diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index a67ac6a9e..25a1949c7 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -2109,7 +2109,7 @@ SrsConfDirective* SrsConfig::get_ingest_by_id(std::string vhost, std::string ing bool SrsConfig::get_ingest_enabled(SrsConfDirective* ingest) { - SrsConfDirective* conf = ingest->get("enable"); + SrsConfDirective* conf = ingest->get("enabled"); if (!conf || conf->arg0() != "on") { return false; @@ -2294,6 +2294,55 @@ double SrsConfig::get_hls_window(string vhost) return ::atof(conf->arg0().c_str()); } +SrsConfDirective* SrsConfig::get_dvr(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("dvr"); +} + +bool SrsConfig::get_dvr_enabled(string vhost) +{ + SrsConfDirective* dvr = get_dvr(vhost); + + if (!dvr) { + return false; + } + + SrsConfDirective* conf = dvr->get("enabled"); + + if (!conf) { + return false; + } + + if (conf->arg0() == "on") { + return true; + } + + return false; +} + +string SrsConfig::get_dvr_path(string vhost) +{ + SrsConfDirective* dvr = get_dvr(vhost); + + if (!dvr) { + return SRS_CONF_DEFAULT_DVR_PATH; + } + + SrsConfDirective* conf = dvr->get("dvr_path"); + + if (!conf) { + return SRS_CONF_DEFAULT_DVR_PATH; + } + + return conf->arg0(); +} + SrsConfDirective* SrsConfig::get_http_api() { return root->get("http_api"); diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 7a108c8ad..a4a46c0ca 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -44,6 +44,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define SRS_CONF_DEFAULT_HLS_PATH "./objs/nginx/html" #define SRS_CONF_DEFAULT_HLS_FRAGMENT 10 #define SRS_CONF_DEFAULT_HLS_WINDOW 60 +#define SRS_CONF_DEFAULT_DVR_PATH "./objs/nginx/html" // in ms, for HLS aac sync time. #define SRS_CONF_DEFAULT_AAC_SYNC 100 // in ms, for HLS aac flush the audio @@ -222,6 +223,12 @@ public: virtual std::string get_hls_path(std::string vhost); virtual double get_hls_fragment(std::string vhost); virtual double get_hls_window(std::string vhost); +// dvr section +private: + virtual SrsConfDirective* get_dvr(std::string vhost); +public: + virtual bool get_dvr_enabled(std::string vhost); + virtual std::string get_dvr_path(std::string vhost); // http api section private: virtual SrsConfDirective* get_http_api(); diff --git a/trunk/src/app/srs_app_dvr.cpp b/trunk/src/app/srs_app_dvr.cpp index 642a36d47..8b7a14f8d 100644 --- a/trunk/src/app/srs_app_dvr.cpp +++ b/trunk/src/app/srs_app_dvr.cpp @@ -25,45 +25,413 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifdef SRS_AUTO_DVR +#include +using namespace std; + +#include #include +#include #include +#include +#include +#include + +SrsFileStream::SrsFileStream() +{ + fd = -1; +} + +SrsFileStream::~SrsFileStream() +{ + close(); +} + +int SrsFileStream::open(string file) +{ + int ret = ERROR_SUCCESS; + + if (fd > 0) { + ret = ERROR_SYSTEM_FILE_ALREADY_OPENED; + srs_error("file %s already opened. ret=%d", _file.c_str(), ret); + return ret; + } + + int flags = O_CREAT|O_WRONLY|O_TRUNC; + mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH; + + if ((fd = ::open(file.c_str(), flags, mode)) < 0) { + ret = ERROR_SYSTEM_FILE_OPENE; + srs_error("open file %s failed. ret=%d", file.c_str(), ret); + return ret; + } + + _file = file; + + return ret; +} + +int SrsFileStream::close() +{ + int ret = ERROR_SUCCESS; + + if (fd < 0) { + return ret; + } + + if (::close(fd) < 0) { + ret = ERROR_SYSTEM_FILE_CLOSE; + srs_error("close file %s failed. ret=%d", _file.c_str(), ret); + return ret; + } + fd = -1; + + return ret; +} + +int SrsFileStream::read(void* buf, size_t count, ssize_t* pnread) +{ + int ret = ERROR_SUCCESS; + + ssize_t nread; + if ((nread = ::read(fd, buf, count)) < 0) { + ret = ERROR_SYSTEM_FILE_READ; + srs_error("read from file %s failed. ret=%d", _file.c_str(), ret); + return ret; + } + + if (nread == 0) { + ret = ERROR_SYSTEM_FILE_EOF; + return ret; + } + + if (pnread != NULL) { + *pnread = nread; + } + + return ret; +} + +int SrsFileStream::write(void* buf, size_t count, ssize_t* pnwrite) +{ + int ret = ERROR_SUCCESS; + + ssize_t nwrite; + if ((nwrite = ::write(fd, buf, count)) < 0) { + ret = ERROR_SYSTEM_FILE_WRITE; + srs_error("write to file %s failed. ret=%d", _file.c_str(), ret); + return ret; + } + + if (pnwrite != NULL) { + *pnwrite = nwrite; + } + + return ret; +} + +int64_t SrsFileStream::size() +{ + ::lseek(fd, 0, SEEK_SET); + return ::lseek(fd, 0, SEEK_END); +} + +off_t SrsFileStream::lseek(off_t offset) +{ + return ::lseek(fd, offset, SEEK_SET); +} + +SrsFlvEncoder::SrsFlvEncoder() +{ + _fs = NULL; + has_audio = false; + has_video = false; + tag_stream = new SrsStream(); +} + +SrsFlvEncoder::~SrsFlvEncoder() +{ + srs_freep(tag_stream); +} + +int SrsFlvEncoder::initialize(SrsFileStream* fs) +{ + int ret = ERROR_SUCCESS; + + _fs = fs; + has_audio = true; + has_video = true; + + return ret; +} + +int SrsFlvEncoder::write_header() +{ + int ret = ERROR_SUCCESS; + + // seek to header. + _fs->lseek(0); + + static char flv_header[] = { + 'F', 'L', 'V', // Signatures "FLV" + (char)0x01, // File version (for example, 0x01 for FLV version 1) + (char)0x00, // 4, audio; 1, video; 5 audio+video. + (char)0x00, (char)0x00, (char)0x00, (char)0x09, // DataOffset UI32 The length of this header in bytes + (char)0x00, (char)0x00, (char)0x00, (char)0x00// PreviousTagSize0 UI32 Always 0 + }; + + // generate audio/video flag. + const static int av_index = 4; + + flv_header[av_index] = 0x00; + if (has_audio) { + flv_header[av_index] += 4; + } + if (has_video) { + flv_header[av_index] += 1; + } + + // write data. + if ((ret = _fs->write(flv_header, sizeof(flv_header), NULL)) != ERROR_SUCCESS) { + srs_error("write flv header failed. ret=%d", ret); + return ret; + } + + return ret; +} + +int SrsFlvEncoder::write_metadata(char* data, int size) +{ + int ret = ERROR_SUCCESS; + + static char tag_header[] = { + (char)18, // TagType UB [5], 18 = script data + (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message. + (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies. + (char)0x00, // TimestampExtended UI8 + (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0. + }; + + // write data size. + if ((ret = tag_stream->initialize(tag_header + 1, 3)) != ERROR_SUCCESS) { + return ret; + } + tag_stream->write_3bytes(size); + + if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) { + srs_error("write flv data tag failed. ret=%d", ret); + return ret; + } + + return ret; +} + +int SrsFlvEncoder::write_audio(int32_t timestamp, char* data, int size) +{ + int ret = ERROR_SUCCESS; + + has_audio = true; + + static char tag_header[] = { + (char)8, // TagType UB [5], 8 = audio + (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message. + (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies. + (char)0x00, // TimestampExtended UI8 + (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0. + }; + + // write data size. + if ((ret = tag_stream->initialize(tag_header + 1, 7)) != ERROR_SUCCESS) { + return ret; + } + tag_stream->write_3bytes(size); + tag_stream->write_3bytes(timestamp); + // default to little-endian + tag_stream->write_1bytes((timestamp >> 24) & 0xFF); + + if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) { + srs_error("write flv audio tag failed. ret=%d", ret); + return ret; + } + + return ret; +} + +int SrsFlvEncoder::write_video(int32_t timestamp, char* data, int size) +{ + int ret = ERROR_SUCCESS; + + has_video = true; + + static char tag_header[] = { + (char)9, // TagType UB [5], 9 = video + (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message. + (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies. + (char)0x00, // TimestampExtended UI8 + (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0. + }; + + // write data size. + if ((ret = tag_stream->initialize(tag_header + 1, 7)) != ERROR_SUCCESS) { + return ret; + } + tag_stream->write_3bytes(size); + tag_stream->write_3bytes(timestamp); + // default to little-endian + tag_stream->write_1bytes((timestamp >> 24) & 0xFF); + + if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) { + srs_error("write flv video tag failed. ret=%d", ret); + return ret; + } + + return ret; +} + +int SrsFlvEncoder::write_tag(char* header, int header_size, char* data, int size) +{ + int ret = ERROR_SUCCESS; + + // write tag header. + if ((ret = _fs->write(header, header_size, NULL)) != ERROR_SUCCESS) { + srs_error("write flv tag header failed. ret=%d", ret); + return ret; + } + + // write tag data. + if ((ret = _fs->write(data, size, NULL)) != ERROR_SUCCESS) { + srs_error("write flv tag failed. ret=%d", ret); + return ret; + } + + // PreviousTagSizeN UI32 Size of last tag, including its header, in bytes. + static char pre_size[4]; + if ((ret = tag_stream->initialize(pre_size, 4)) != ERROR_SUCCESS) { + return ret; + } + tag_stream->write_4bytes(size + header_size); + if ((ret = _fs->write(pre_size, sizeof(pre_size), NULL)) != ERROR_SUCCESS) { + srs_error("write flv previous tag size failed. ret=%d", ret); + return ret; + } + + return ret; +} SrsDvr::SrsDvr(SrsSource* source) { _source = source; + dvr_enabled = false; + fs = new SrsFileStream(); + enc = new SrsFlvEncoder(); } SrsDvr::~SrsDvr() { + srs_freep(fs); + srs_freep(enc); } int SrsDvr::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::string path = _srs_config->get_dvr_path(req->vhost); + path += "/"; + path += req->app; + path += "/"; + path += req->stream; + path += ".flv"; + + if ((ret = fs->open(path)) != ERROR_SUCCESS) { + srs_error("open file stream for file %s failed. ret=%d", path.c_str(), ret); + return ret; + } + + if ((ret = enc->initialize(fs)) != ERROR_SUCCESS) { + srs_error("initialize enc by fs for file %s failed. ret=%d", path.c_str(), ret); + return ret; + } + + if ((ret = enc->write_header()) != ERROR_SUCCESS) { + srs_error("write flv header for file %s failed. ret=%d", path.c_str(), ret); + return ret; + } + + if ((ret = _source->on_dvr_start()) != ERROR_SUCCESS) { + return ret; + } + + srs_trace("dvr stream %s to file %s", req->get_stream_url().c_str(), path.c_str()); + dvr_enabled = true; + return ret; } void SrsDvr::on_unpublish() { + // support multiple publish. + if (!dvr_enabled) { + return; + } + + // ignore error. + fs->close(); + + dvr_enabled = false; } -int SrsDvr::on_meta_data(SrsAmf0Object* metadata) +int SrsDvr::on_meta_data(SrsOnMetaDataPacket* metadata) { int ret = ERROR_SUCCESS; + + int size = 0; + char* payload = NULL; + if ((ret = metadata->encode(size, payload)) != ERROR_SUCCESS) { + return ret; + } + SrsAutoFree(char, payload, true); + + if ((ret = enc->write_metadata(payload, size)) != ERROR_SUCCESS) { + return ret; + } + return ret; } int SrsDvr::on_audio(SrsSharedPtrMessage* audio) { int ret = ERROR_SUCCESS; - srs_freep(audio); + + int32_t timestamp = audio->header.timestamp; + char* payload = (char*)audio->payload; + int size = (int)audio->size; + if ((ret = enc->write_audio(timestamp, payload, size)) != ERROR_SUCCESS) { + return ret; + } + return ret; } int SrsDvr::on_video(SrsSharedPtrMessage* video) { int ret = ERROR_SUCCESS; - srs_freep(video); + + int32_t timestamp = video->header.timestamp; + char* payload = (char*)video->payload; + int size = (int)video->size; + if ((ret = enc->write_video(timestamp, payload, size)) != ERROR_SUCCESS) { + return ret; + } + return ret; } diff --git a/trunk/src/app/srs_app_dvr.hpp b/trunk/src/app/srs_app_dvr.hpp index 596b4bab8..5a2e1df80 100644 --- a/trunk/src/app/srs_app_dvr.hpp +++ b/trunk/src/app/srs_app_dvr.hpp @@ -33,14 +33,77 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. class SrsSource; class SrsRequest; -class SrsAmf0Object; +class SrsStream; +class SrsOnMetaDataPacket; class SrsSharedPtrMessage; +/** +* file stream to read/write file. +*/ +class SrsFileStream +{ +private: + std::string _file; + int fd; +public: + SrsFileStream(); + virtual ~SrsFileStream(); +public: + virtual int open(std::string file); + virtual int close(); +public: + /** + * @param pnread, return the read size. NULL to ignore. + */ + virtual int read(void* buf, size_t count, ssize_t* pnread); + /** + * @param pnwrite, return the write size. NULL to ignore. + */ + virtual int write(void* buf, size_t count, ssize_t* pnwrite); +public: + /** + * get size of file. + */ + virtual int64_t size(); + /** + * wrapper for system lseek where whence always use SEEK_SET + */ + virtual off_t lseek(off_t offset); +}; + /** * encode data to flv file. */ class SrsFlvEncoder { +private: + SrsFileStream* _fs; +private: + bool has_audio; + bool has_video; + SrsStream* tag_stream; +public: + SrsFlvEncoder(); + virtual ~SrsFlvEncoder(); +public: + virtual int initialize(SrsFileStream* fs); +public: + /** + * write flv header. + * user can invoke this method multiple times, + * for example, when get audio/video sequence header. + * + * write following: + * 1. E.2 The FLV header + * 2. PreviousTagSize0 UI32 Always 0 + * that is, 9+4=13bytes. + */ + virtual int write_header(); + virtual int write_metadata(char* data, int size); + virtual int write_audio(int32_t timestamp, char* data, int size); + virtual int write_video(int32_t timestamp, char* data, int size); +private: + virtual int write_tag(char* header, int header_size, char* data, int size); }; /** @@ -51,6 +114,10 @@ class SrsDvr { private: SrsSource* _source; +private: + bool dvr_enabled; + SrsFileStream* fs; + SrsFlvEncoder* enc; public: SrsDvr(SrsSource* source); virtual ~SrsDvr(); @@ -68,7 +135,7 @@ public: /** * get some information from metadata, it's optinal. */ - virtual int on_meta_data(SrsAmf0Object* metadata); + virtual int on_meta_data(SrsOnMetaDataPacket* metadata); /** * mux the audio packets to dvr. */ diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index 2b4e9e05a..f81b7476b 100644 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -37,6 +37,7 @@ using namespace std; #include #include #include +#include #define CONST_MAX_JITTER_MS 500 #define DEFAULT_FRAME_TIME_MS 40 @@ -651,7 +652,6 @@ int SrsSource::on_hls_start() int ret = ERROR_SUCCESS; #ifdef SRS_AUTO_HLS - // feed the hls the metadata/sequence header, // when reload to start hls, hls will never get the sequence header in stream, // use the SrsSource.on_hls_start to push the sequence header to HLS. @@ -664,7 +664,49 @@ int SrsSource::on_hls_start() srs_error("hls process audio sequence header message failed. ret=%d", ret); return ret; } +#endif + return ret; +} + +int SrsSource::on_dvr_start() +{ + int ret = ERROR_SUCCESS; + +#ifdef SRS_AUTO_DVR + // feed the dvr the metadata/sequence header, + // when reload to start dvr, dvr will never get the sequence header in stream, + // use the SrsSource.on_dvr_start to push the sequence header to DVR. + if (cache_metadata) { + char* payload = (char*)cache_metadata->payload; + int size = (int)cache_metadata->size; + + SrsStream stream; + if ((ret = stream.initialize(payload, size)) != ERROR_SUCCESS) { + srs_error("dvr decode metadata stream failed. ret=%d", ret); + return ret; + } + + SrsOnMetaDataPacket pkt; + if ((ret = pkt.decode(&stream)) != ERROR_SUCCESS) { + srs_error("dvr decode metadata packet failed."); + return ret; + } + + if ((ret = dvr->on_meta_data(&pkt)) != ERROR_SUCCESS) { + srs_error("dvr process onMetaData message failed. ret=%d", ret); + return ret; + } + } + + if (cache_sh_video && (ret = dvr->on_video(cache_sh_video->copy())) != ERROR_SUCCESS) { + srs_error("dvr process video sequence header message failed. ret=%d", ret); + return ret; + } + if (cache_sh_audio && (ret = dvr->on_audio(cache_sh_audio->copy())) != ERROR_SUCCESS) { + srs_error("dvr process audio sequence header message failed. ret=%d", ret); + return ret; + } #endif return ret; @@ -687,7 +729,7 @@ int SrsSource::on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata #endif #ifdef SRS_AUTO_DVR - if (metadata && (ret = dvr->on_meta_data(metadata->metadata)) != ERROR_SUCCESS) { + if (metadata && (ret = dvr->on_meta_data(metadata)) != ERROR_SUCCESS) { srs_error("dvr process onMetaData message failed. ret=%d", ret); return ret; } diff --git a/trunk/src/app/srs_app_source.hpp b/trunk/src/app/srs_app_source.hpp index 1087726f9..893966320 100644 --- a/trunk/src/app/srs_app_source.hpp +++ b/trunk/src/app/srs_app_source.hpp @@ -284,6 +284,8 @@ public: virtual int on_forwarder_start(SrsForwarder* forwarder); // for the SrsHls to callback to request the sequence headers. virtual int on_hls_start(); + // for the SrsDvr to callback to request the sequence headers. + virtual int on_dvr_start(); public: virtual bool can_publish(); virtual int on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata); diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 08d7546cd..f69cb0ea6 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR "0" #define VERSION_MINOR "9" -#define VERSION_REVISION "68" +#define VERSION_REVISION "69" #define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION // server info. #define RTMP_SIG_SRS_KEY "srs" diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 2c4e09d44..b56c48a56 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -100,6 +100,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_SYSTEM_PID_WRITE_FILE 420 #define ERROR_SYSTEM_PID_GET_FILE_INFO 421 #define ERROR_SYSTEM_PID_SET_FILE_INFO 422 +#define ERROR_SYSTEM_FILE_ALREADY_OPENED 423 +#define ERROR_SYSTEM_FILE_OPENE 424 +#define ERROR_SYSTEM_FILE_CLOSE 425 +#define ERROR_SYSTEM_FILE_READ 426 +#define ERROR_SYSTEM_FILE_WRITE 427 +#define ERROR_SYSTEM_FILE_EOF 428 // see librtmp. // failed when open ssl create the dh diff --git a/trunk/src/kernel/srs_kernel_stream.cpp b/trunk/src/kernel/srs_kernel_stream.cpp index 2f16560bc..4aa0caf3b 100644 --- a/trunk/src/kernel/srs_kernel_stream.cpp +++ b/trunk/src/kernel/srs_kernel_stream.cpp @@ -199,6 +199,16 @@ void SrsStream::write_4bytes(int32_t value) *p++ = pp[0]; } +void SrsStream::write_3bytes(int32_t value) +{ + srs_assert(require(3)); + + pp = (char*)&value; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; +} + void SrsStream::write_8bytes(int64_t value) { srs_assert(require(8)); diff --git a/trunk/src/kernel/srs_kernel_stream.hpp b/trunk/src/kernel/srs_kernel_stream.hpp index 060278875..1d1d95599 100644 --- a/trunk/src/kernel/srs_kernel_stream.hpp +++ b/trunk/src/kernel/srs_kernel_stream.hpp @@ -118,6 +118,10 @@ public: */ virtual void write_4bytes(int32_t value); /** + * write 3bytes int to stream. + */ + virtual void write_3bytes(int32_t value); + /** * write 8bytes int to stream. */ virtual void write_8bytes(int64_t value);