winlin

support dvr. change to 0.9.69

@@ -113,14 +113,14 @@ vhost demo.srs.com { @@ -113,14 +113,14 @@ vhost demo.srs.com {
113 } 113 }
114 } 114 }
115 ingest { 115 ingest {
116 - enable on; 116 + enabled on;
117 input { 117 input {
118 type file; 118 type file;
119 url ./doc/source.200kbps.768x320.flv; 119 url ./doc/source.200kbps.768x320.flv;
120 } 120 }
121 ffmpeg ./objs/ffmpeg/bin/ffmpeg; 121 ffmpeg ./objs/ffmpeg/bin/ffmpeg;
122 engine { 122 engine {
123 - enable off; 123 + enabled off;
124 output rtmp://127.0.0.1:[port]/live?vhost=[vhost]/livestream; 124 output rtmp://127.0.0.1:[port]/live?vhost=[vhost]/livestream;
125 } 125 }
126 } 126 }
@@ -158,14 +158,14 @@ vhost players { @@ -158,14 +158,14 @@ vhost players {
158 } 158 }
159 } 159 }
160 ingest { 160 ingest {
161 - enable on; 161 + enabled on;
162 input { 162 input {
163 type file; 163 type file;
164 url ./doc/source.200kbps.768x320.flv; 164 url ./doc/source.200kbps.768x320.flv;
165 } 165 }
166 ffmpeg ./objs/ffmpeg/bin/ffmpeg; 166 ffmpeg ./objs/ffmpeg/bin/ffmpeg;
167 engine { 167 engine {
168 - enable off; 168 + enabled off;
169 output rtmp://127.0.0.1:[port]/live?vhost=[vhost]/demo; 169 output rtmp://127.0.0.1:[port]/live?vhost=[vhost]/demo;
170 } 170 }
171 } 171 }
@@ -91,15 +91,39 @@ http_stream { @@ -91,15 +91,39 @@ http_stream {
91 vhost __defaultVhost__ { 91 vhost __defaultVhost__ {
92 } 92 }
93 93
  94 +# vhost for dvr
  95 +vhost dvr.srs.com {
  96 + # dvr RTMP stream to file,
  97 + # when encoder(FMLE/ffmpeg/flash) start to publish,
  98 + # start the dvr and record RTMP to file(flv).
  99 + # stop record when encoder stop to publish.
  100 + dvr {
  101 + # whether enabled dvr features
  102 + # default: off
  103 + enabled on;
  104 + # the dvr output path.
  105 + # the app dir is auto created under the dvr_path.
  106 + # for example, for rtmp stream:
  107 + # rtmp://127.0.0.1/live/livestream
  108 + # http://127.0.0.1/live/livestream.m3u8
  109 + # where dvr_path is /dvr, srs will create the following files:
  110 + # /dvr/live the app dir for all streams.
  111 + # /dvr/live/livestream.flv the dvr flv file.
  112 + # in a word, the dvr_path is for vhost.
  113 + # default: ./objs/nginx/html
  114 + dvr_path ./objs/nginx/html;
  115 + }
  116 +}
  117 +
94 # vhost for ingest 118 # vhost for ingest
95 vhost ingest.srs.com { 119 vhost ingest.srs.com {
96 # ingest file/stream/device then push to SRS over RTMP. 120 # ingest file/stream/device then push to SRS over RTMP.
97 # the name/id used to identify the ingest, must be unique in global. 121 # the name/id used to identify the ingest, must be unique in global.
98 # ingest id is used in reload or http api management. 122 # ingest id is used in reload or http api management.
99 ingest livestream { 123 ingest livestream {
100 - # whether enable ingest features 124 + # whether enabled ingest features
101 # default: off 125 # default: off
102 - enable on; 126 + enabled on;
103 # input file/stream/device 127 # input file/stream/device
104 # @remark only support one input. 128 # @remark only support one input.
105 input { 129 input {
@@ -121,7 +145,7 @@ vhost ingest.srs.com { @@ -121,7 +145,7 @@ vhost ingest.srs.com {
121 # @see enabled of transcode engine. 145 # @see enabled of transcode engine.
122 # if disabled or vcodec/acodec not specified, use copy. 146 # if disabled or vcodec/acodec not specified, use copy.
123 # default: off. 147 # default: off.
124 - enable off; 148 + enabled off;
125 # output stream. variables: 149 # output stream. variables:
126 # [vhost] current vhost which start the ingest. 150 # [vhost] current vhost which start the ingest.
127 # [port] system RTMP stream port. 151 # [port] system RTMP stream port.
@@ -134,7 +158,7 @@ vhost ingest.srs.com { @@ -134,7 +158,7 @@ vhost ingest.srs.com {
134 vhost http.srs.com { 158 vhost http.srs.com {
135 # http vhost specified config 159 # http vhost specified config
136 http { 160 http {
137 - # whether enable the http streaming service for vhost. 161 + # whether enabled the http streaming service for vhost.
138 # default: off 162 # default: off
139 enabled on; 163 enabled on;
140 # the virtual directory root for this vhost to mount at 164 # the virtual directory root for this vhost to mount at
@@ -743,7 +767,7 @@ vhost with-hls.srs.com { @@ -743,7 +767,7 @@ vhost with-hls.srs.com {
743 # /hls/live/livestream-1.ts the HLS media/ts file. 767 # /hls/live/livestream-1.ts the HLS media/ts file.
744 # in a word, the hls_path is for vhost. 768 # in a word, the hls_path is for vhost.
745 # default: ./objs/nginx/html 769 # default: ./objs/nginx/html
746 - hls_path /data/nginx/html; 770 + hls_path ./objs/nginx/html;
747 # the hls fragment in seconds, the duration of a piece of ts. 771 # the hls fragment in seconds, the duration of a piece of ts.
748 # default: 10 772 # default: 10
749 hls_fragment 10; 773 hls_fragment 10;
@@ -766,7 +790,7 @@ vhost no-hls.srs.com { @@ -766,7 +790,7 @@ vhost no-hls.srs.com {
766 vhost min.delay.com { 790 vhost min.delay.com {
767 # whether cache the last gop. 791 # whether cache the last gop.
768 # if on, cache the last gop and dispatch to client, 792 # if on, cache the last gop and dispatch to client,
769 - # to enable fast startup for client, client play immediately. 793 + # to enabled fast startup for client, client play immediately.
770 # if off, send the latest media data to client, 794 # if off, send the latest media data to client,
771 # client need to wait for the next Iframe to decode and show the video. 795 # client need to wait for the next Iframe to decode and show the video.
772 # set to off if requires min delay; 796 # set to off if requires min delay;
@@ -5,14 +5,14 @@ @@ -5,14 +5,14 @@
5 listen 1935; 5 listen 1935;
6 vhost __defaultVhost__ { 6 vhost __defaultVhost__ {
7 ingest livestream { 7 ingest livestream {
8 - enable on; 8 + enabled on;
9 input { 9 input {
10 type file; 10 type file;
11 url ./doc/source.200kbps.768x320.flv; 11 url ./doc/source.200kbps.768x320.flv;
12 } 12 }
13 ffmpeg ./objs/ffmpeg/bin/ffmpeg; 13 ffmpeg ./objs/ffmpeg/bin/ffmpeg;
14 engine { 14 engine {
15 - enable off; 15 + enabled off;
16 output rtmp://127.0.0.1:[port]/live?vhost=[vhost]/livestream; 16 output rtmp://127.0.0.1:[port]/live?vhost=[vhost]/livestream;
17 } 17 }
18 } 18 }
@@ -2109,7 +2109,7 @@ SrsConfDirective* SrsConfig::get_ingest_by_id(std::string vhost, std::string ing @@ -2109,7 +2109,7 @@ SrsConfDirective* SrsConfig::get_ingest_by_id(std::string vhost, std::string ing
2109 2109
2110 bool SrsConfig::get_ingest_enabled(SrsConfDirective* ingest) 2110 bool SrsConfig::get_ingest_enabled(SrsConfDirective* ingest)
2111 { 2111 {
2112 - SrsConfDirective* conf = ingest->get("enable"); 2112 + SrsConfDirective* conf = ingest->get("enabled");
2113 2113
2114 if (!conf || conf->arg0() != "on") { 2114 if (!conf || conf->arg0() != "on") {
2115 return false; 2115 return false;
@@ -2294,6 +2294,55 @@ double SrsConfig::get_hls_window(string vhost) @@ -2294,6 +2294,55 @@ double SrsConfig::get_hls_window(string vhost)
2294 return ::atof(conf->arg0().c_str()); 2294 return ::atof(conf->arg0().c_str());
2295 } 2295 }
2296 2296
  2297 +SrsConfDirective* SrsConfig::get_dvr(string vhost)
  2298 +{
  2299 + SrsConfDirective* conf = get_vhost(vhost);
  2300 +
  2301 + if (!conf) {
  2302 + return NULL;
  2303 + }
  2304 +
  2305 + return conf->get("dvr");
  2306 +}
  2307 +
  2308 +bool SrsConfig::get_dvr_enabled(string vhost)
  2309 +{
  2310 + SrsConfDirective* dvr = get_dvr(vhost);
  2311 +
  2312 + if (!dvr) {
  2313 + return false;
  2314 + }
  2315 +
  2316 + SrsConfDirective* conf = dvr->get("enabled");
  2317 +
  2318 + if (!conf) {
  2319 + return false;
  2320 + }
  2321 +
  2322 + if (conf->arg0() == "on") {
  2323 + return true;
  2324 + }
  2325 +
  2326 + return false;
  2327 +}
  2328 +
  2329 +string SrsConfig::get_dvr_path(string vhost)
  2330 +{
  2331 + SrsConfDirective* dvr = get_dvr(vhost);
  2332 +
  2333 + if (!dvr) {
  2334 + return SRS_CONF_DEFAULT_DVR_PATH;
  2335 + }
  2336 +
  2337 + SrsConfDirective* conf = dvr->get("dvr_path");
  2338 +
  2339 + if (!conf) {
  2340 + return SRS_CONF_DEFAULT_DVR_PATH;
  2341 + }
  2342 +
  2343 + return conf->arg0();
  2344 +}
  2345 +
2297 SrsConfDirective* SrsConfig::get_http_api() 2346 SrsConfDirective* SrsConfig::get_http_api()
2298 { 2347 {
2299 return root->get("http_api"); 2348 return root->get("http_api");
@@ -44,6 +44,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -44,6 +44,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
44 #define SRS_CONF_DEFAULT_HLS_PATH "./objs/nginx/html" 44 #define SRS_CONF_DEFAULT_HLS_PATH "./objs/nginx/html"
45 #define SRS_CONF_DEFAULT_HLS_FRAGMENT 10 45 #define SRS_CONF_DEFAULT_HLS_FRAGMENT 10
46 #define SRS_CONF_DEFAULT_HLS_WINDOW 60 46 #define SRS_CONF_DEFAULT_HLS_WINDOW 60
  47 +#define SRS_CONF_DEFAULT_DVR_PATH "./objs/nginx/html"
47 // in ms, for HLS aac sync time. 48 // in ms, for HLS aac sync time.
48 #define SRS_CONF_DEFAULT_AAC_SYNC 100 49 #define SRS_CONF_DEFAULT_AAC_SYNC 100
49 // in ms, for HLS aac flush the audio 50 // in ms, for HLS aac flush the audio
@@ -222,6 +223,12 @@ public: @@ -222,6 +223,12 @@ public:
222 virtual std::string get_hls_path(std::string vhost); 223 virtual std::string get_hls_path(std::string vhost);
223 virtual double get_hls_fragment(std::string vhost); 224 virtual double get_hls_fragment(std::string vhost);
224 virtual double get_hls_window(std::string vhost); 225 virtual double get_hls_window(std::string vhost);
  226 +// dvr section
  227 +private:
  228 + virtual SrsConfDirective* get_dvr(std::string vhost);
  229 +public:
  230 + virtual bool get_dvr_enabled(std::string vhost);
  231 + virtual std::string get_dvr_path(std::string vhost);
225 // http api section 232 // http api section
226 private: 233 private:
227 virtual SrsConfDirective* get_http_api(); 234 virtual SrsConfDirective* get_http_api();
@@ -25,45 +25,413 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -25,45 +25,413 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 25
26 #ifdef SRS_AUTO_DVR 26 #ifdef SRS_AUTO_DVR
27 27
  28 +#include <fcntl.h>
  29 +using namespace std;
  30 +
  31 +#include <srs_app_config.hpp>
28 #include <srs_kernel_error.hpp> 32 #include <srs_kernel_error.hpp>
  33 +#include <srs_protocol_rtmp.hpp>
29 #include <srs_protocol_rtmp_stack.hpp> 34 #include <srs_protocol_rtmp_stack.hpp>
  35 +#include <srs_app_source.hpp>
  36 +#include <srs_core_autofree.hpp>
  37 +#include <srs_kernel_stream.hpp>
  38 +
  39 +SrsFileStream::SrsFileStream()
  40 +{
  41 + fd = -1;
  42 +}
  43 +
  44 +SrsFileStream::~SrsFileStream()
  45 +{
  46 + close();
  47 +}
  48 +
  49 +int SrsFileStream::open(string file)
  50 +{
  51 + int ret = ERROR_SUCCESS;
  52 +
  53 + if (fd > 0) {
  54 + ret = ERROR_SYSTEM_FILE_ALREADY_OPENED;
  55 + srs_error("file %s already opened. ret=%d", _file.c_str(), ret);
  56 + return ret;
  57 + }
  58 +
  59 + int flags = O_CREAT|O_WRONLY|O_TRUNC;
  60 + mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH;
  61 +
  62 + if ((fd = ::open(file.c_str(), flags, mode)) < 0) {
  63 + ret = ERROR_SYSTEM_FILE_OPENE;
  64 + srs_error("open file %s failed. ret=%d", file.c_str(), ret);
  65 + return ret;
  66 + }
  67 +
  68 + _file = file;
  69 +
  70 + return ret;
  71 +}
  72 +
  73 +int SrsFileStream::close()
  74 +{
  75 + int ret = ERROR_SUCCESS;
  76 +
  77 + if (fd < 0) {
  78 + return ret;
  79 + }
  80 +
  81 + if (::close(fd) < 0) {
  82 + ret = ERROR_SYSTEM_FILE_CLOSE;
  83 + srs_error("close file %s failed. ret=%d", _file.c_str(), ret);
  84 + return ret;
  85 + }
  86 + fd = -1;
  87 +
  88 + return ret;
  89 +}
  90 +
  91 +int SrsFileStream::read(void* buf, size_t count, ssize_t* pnread)
  92 +{
  93 + int ret = ERROR_SUCCESS;
  94 +
  95 + ssize_t nread;
  96 + if ((nread = ::read(fd, buf, count)) < 0) {
  97 + ret = ERROR_SYSTEM_FILE_READ;
  98 + srs_error("read from file %s failed. ret=%d", _file.c_str(), ret);
  99 + return ret;
  100 + }
  101 +
  102 + if (nread == 0) {
  103 + ret = ERROR_SYSTEM_FILE_EOF;
  104 + return ret;
  105 + }
  106 +
  107 + if (pnread != NULL) {
  108 + *pnread = nread;
  109 + }
  110 +
  111 + return ret;
  112 +}
  113 +
  114 +int SrsFileStream::write(void* buf, size_t count, ssize_t* pnwrite)
  115 +{
  116 + int ret = ERROR_SUCCESS;
  117 +
  118 + ssize_t nwrite;
  119 + if ((nwrite = ::write(fd, buf, count)) < 0) {
  120 + ret = ERROR_SYSTEM_FILE_WRITE;
  121 + srs_error("write to file %s failed. ret=%d", _file.c_str(), ret);
  122 + return ret;
  123 + }
  124 +
  125 + if (pnwrite != NULL) {
  126 + *pnwrite = nwrite;
  127 + }
  128 +
  129 + return ret;
  130 +}
  131 +
  132 +int64_t SrsFileStream::size()
  133 +{
  134 + ::lseek(fd, 0, SEEK_SET);
  135 + return ::lseek(fd, 0, SEEK_END);
  136 +}
  137 +
  138 +off_t SrsFileStream::lseek(off_t offset)
  139 +{
  140 + return ::lseek(fd, offset, SEEK_SET);
  141 +}
  142 +
  143 +SrsFlvEncoder::SrsFlvEncoder()
  144 +{
  145 + _fs = NULL;
  146 + has_audio = false;
  147 + has_video = false;
  148 + tag_stream = new SrsStream();
  149 +}
  150 +
  151 +SrsFlvEncoder::~SrsFlvEncoder()
  152 +{
  153 + srs_freep(tag_stream);
  154 +}
  155 +
  156 +int SrsFlvEncoder::initialize(SrsFileStream* fs)
  157 +{
  158 + int ret = ERROR_SUCCESS;
  159 +
  160 + _fs = fs;
  161 + has_audio = true;
  162 + has_video = true;
  163 +
  164 + return ret;
  165 +}
  166 +
  167 +int SrsFlvEncoder::write_header()
  168 +{
  169 + int ret = ERROR_SUCCESS;
  170 +
  171 + // seek to header.
  172 + _fs->lseek(0);
  173 +
  174 + static char flv_header[] = {
  175 + 'F', 'L', 'V', // Signatures "FLV"
  176 + (char)0x01, // File version (for example, 0x01 for FLV version 1)
  177 + (char)0x00, // 4, audio; 1, video; 5 audio+video.
  178 + (char)0x00, (char)0x00, (char)0x00, (char)0x09, // DataOffset UI32 The length of this header in bytes
  179 + (char)0x00, (char)0x00, (char)0x00, (char)0x00// PreviousTagSize0 UI32 Always 0
  180 + };
  181 +
  182 + // generate audio/video flag.
  183 + const static int av_index = 4;
  184 +
  185 + flv_header[av_index] = 0x00;
  186 + if (has_audio) {
  187 + flv_header[av_index] += 4;
  188 + }
  189 + if (has_video) {
  190 + flv_header[av_index] += 1;
  191 + }
  192 +
  193 + // write data.
  194 + if ((ret = _fs->write(flv_header, sizeof(flv_header), NULL)) != ERROR_SUCCESS) {
  195 + srs_error("write flv header failed. ret=%d", ret);
  196 + return ret;
  197 + }
  198 +
  199 + return ret;
  200 +}
  201 +
  202 +int SrsFlvEncoder::write_metadata(char* data, int size)
  203 +{
  204 + int ret = ERROR_SUCCESS;
  205 +
  206 + static char tag_header[] = {
  207 + (char)18, // TagType UB [5], 18 = script data
  208 + (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message.
  209 + (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies.
  210 + (char)0x00, // TimestampExtended UI8
  211 + (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0.
  212 + };
  213 +
  214 + // write data size.
  215 + if ((ret = tag_stream->initialize(tag_header + 1, 3)) != ERROR_SUCCESS) {
  216 + return ret;
  217 + }
  218 + tag_stream->write_3bytes(size);
  219 +
  220 + if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) {
  221 + srs_error("write flv data tag failed. ret=%d", ret);
  222 + return ret;
  223 + }
  224 +
  225 + return ret;
  226 +}
  227 +
  228 +int SrsFlvEncoder::write_audio(int32_t timestamp, char* data, int size)
  229 +{
  230 + int ret = ERROR_SUCCESS;
  231 +
  232 + has_audio = true;
  233 +
  234 + static char tag_header[] = {
  235 + (char)8, // TagType UB [5], 8 = audio
  236 + (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message.
  237 + (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies.
  238 + (char)0x00, // TimestampExtended UI8
  239 + (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0.
  240 + };
  241 +
  242 + // write data size.
  243 + if ((ret = tag_stream->initialize(tag_header + 1, 7)) != ERROR_SUCCESS) {
  244 + return ret;
  245 + }
  246 + tag_stream->write_3bytes(size);
  247 + tag_stream->write_3bytes(timestamp);
  248 + // default to little-endian
  249 + tag_stream->write_1bytes((timestamp >> 24) & 0xFF);
  250 +
  251 + if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) {
  252 + srs_error("write flv audio tag failed. ret=%d", ret);
  253 + return ret;
  254 + }
  255 +
  256 + return ret;
  257 +}
  258 +
  259 +int SrsFlvEncoder::write_video(int32_t timestamp, char* data, int size)
  260 +{
  261 + int ret = ERROR_SUCCESS;
  262 +
  263 + has_video = true;
  264 +
  265 + static char tag_header[] = {
  266 + (char)9, // TagType UB [5], 9 = video
  267 + (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message.
  268 + (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies.
  269 + (char)0x00, // TimestampExtended UI8
  270 + (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0.
  271 + };
  272 +
  273 + // write data size.
  274 + if ((ret = tag_stream->initialize(tag_header + 1, 7)) != ERROR_SUCCESS) {
  275 + return ret;
  276 + }
  277 + tag_stream->write_3bytes(size);
  278 + tag_stream->write_3bytes(timestamp);
  279 + // default to little-endian
  280 + tag_stream->write_1bytes((timestamp >> 24) & 0xFF);
  281 +
  282 + if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) {
  283 + srs_error("write flv video tag failed. ret=%d", ret);
  284 + return ret;
  285 + }
  286 +
  287 + return ret;
  288 +}
  289 +
  290 +int SrsFlvEncoder::write_tag(char* header, int header_size, char* data, int size)
  291 +{
  292 + int ret = ERROR_SUCCESS;
  293 +
  294 + // write tag header.
  295 + if ((ret = _fs->write(header, header_size, NULL)) != ERROR_SUCCESS) {
  296 + srs_error("write flv tag header failed. ret=%d", ret);
  297 + return ret;
  298 + }
  299 +
  300 + // write tag data.
  301 + if ((ret = _fs->write(data, size, NULL)) != ERROR_SUCCESS) {
  302 + srs_error("write flv tag failed. ret=%d", ret);
  303 + return ret;
  304 + }
  305 +
  306 + // PreviousTagSizeN UI32 Size of last tag, including its header, in bytes.
  307 + static char pre_size[4];
  308 + if ((ret = tag_stream->initialize(pre_size, 4)) != ERROR_SUCCESS) {
  309 + return ret;
  310 + }
  311 + tag_stream->write_4bytes(size + header_size);
  312 + if ((ret = _fs->write(pre_size, sizeof(pre_size), NULL)) != ERROR_SUCCESS) {
  313 + srs_error("write flv previous tag size failed. ret=%d", ret);
  314 + return ret;
  315 + }
  316 +
  317 + return ret;
  318 +}
30 319
31 SrsDvr::SrsDvr(SrsSource* source) 320 SrsDvr::SrsDvr(SrsSource* source)
32 { 321 {
33 _source = source; 322 _source = source;
  323 + dvr_enabled = false;
  324 + fs = new SrsFileStream();
  325 + enc = new SrsFlvEncoder();
34 } 326 }
35 327
36 SrsDvr::~SrsDvr() 328 SrsDvr::~SrsDvr()
37 { 329 {
  330 + srs_freep(fs);
  331 + srs_freep(enc);
38 } 332 }
39 333
40 int SrsDvr::on_publish(SrsRequest* req) 334 int SrsDvr::on_publish(SrsRequest* req)
41 { 335 {
42 int ret = ERROR_SUCCESS; 336 int ret = ERROR_SUCCESS;
  337 +
  338 + // support multiple publish.
  339 + if (dvr_enabled) {
  340 + return ret;
  341 + }
  342 +
  343 + if (!_srs_config->get_dvr_enabled(req->vhost)) {
  344 + return ret;
  345 + }
  346 +
  347 + std::string path = _srs_config->get_dvr_path(req->vhost);
  348 + path += "/";
  349 + path += req->app;
  350 + path += "/";
  351 + path += req->stream;
  352 + path += ".flv";
  353 +
  354 + if ((ret = fs->open(path)) != ERROR_SUCCESS) {
  355 + srs_error("open file stream for file %s failed. ret=%d", path.c_str(), ret);
  356 + return ret;
  357 + }
  358 +
  359 + if ((ret = enc->initialize(fs)) != ERROR_SUCCESS) {
  360 + srs_error("initialize enc by fs for file %s failed. ret=%d", path.c_str(), ret);
  361 + return ret;
  362 + }
  363 +
  364 + if ((ret = enc->write_header()) != ERROR_SUCCESS) {
  365 + srs_error("write flv header for file %s failed. ret=%d", path.c_str(), ret);
  366 + return ret;
  367 + }
  368 +
  369 + if ((ret = _source->on_dvr_start()) != ERROR_SUCCESS) {
  370 + return ret;
  371 + }
  372 +
  373 + srs_trace("dvr stream %s to file %s", req->get_stream_url().c_str(), path.c_str());
  374 + dvr_enabled = true;
  375 +
43 return ret; 376 return ret;
44 } 377 }
45 378
46 void SrsDvr::on_unpublish() 379 void SrsDvr::on_unpublish()
47 { 380 {
  381 + // support multiple publish.
  382 + if (!dvr_enabled) {
  383 + return;
  384 + }
  385 +
  386 + // ignore error.
  387 + fs->close();
  388 +
  389 + dvr_enabled = false;
48 } 390 }
49 391
50 -int SrsDvr::on_meta_data(SrsAmf0Object* metadata) 392 +int SrsDvr::on_meta_data(SrsOnMetaDataPacket* metadata)
51 { 393 {
52 int ret = ERROR_SUCCESS; 394 int ret = ERROR_SUCCESS;
  395 +
  396 + int size = 0;
  397 + char* payload = NULL;
  398 + if ((ret = metadata->encode(size, payload)) != ERROR_SUCCESS) {
  399 + return ret;
  400 + }
  401 + SrsAutoFree(char, payload, true);
  402 +
  403 + if ((ret = enc->write_metadata(payload, size)) != ERROR_SUCCESS) {
  404 + return ret;
  405 + }
  406 +
53 return ret; 407 return ret;
54 } 408 }
55 409
56 int SrsDvr::on_audio(SrsSharedPtrMessage* audio) 410 int SrsDvr::on_audio(SrsSharedPtrMessage* audio)
57 { 411 {
58 int ret = ERROR_SUCCESS; 412 int ret = ERROR_SUCCESS;
59 - srs_freep(audio); 413 +
  414 + int32_t timestamp = audio->header.timestamp;
  415 + char* payload = (char*)audio->payload;
  416 + int size = (int)audio->size;
  417 + if ((ret = enc->write_audio(timestamp, payload, size)) != ERROR_SUCCESS) {
  418 + return ret;
  419 + }
  420 +
60 return ret; 421 return ret;
61 } 422 }
62 423
63 int SrsDvr::on_video(SrsSharedPtrMessage* video) 424 int SrsDvr::on_video(SrsSharedPtrMessage* video)
64 { 425 {
65 int ret = ERROR_SUCCESS; 426 int ret = ERROR_SUCCESS;
66 - srs_freep(video); 427 +
  428 + int32_t timestamp = video->header.timestamp;
  429 + char* payload = (char*)video->payload;
  430 + int size = (int)video->size;
  431 + if ((ret = enc->write_video(timestamp, payload, size)) != ERROR_SUCCESS) {
  432 + return ret;
  433 + }
  434 +
67 return ret; 435 return ret;
68 } 436 }
69 437
@@ -33,14 +33,77 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -33,14 +33,77 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 33
34 class SrsSource; 34 class SrsSource;
35 class SrsRequest; 35 class SrsRequest;
36 -class SrsAmf0Object; 36 +class SrsStream;
  37 +class SrsOnMetaDataPacket;
37 class SrsSharedPtrMessage; 38 class SrsSharedPtrMessage;
38 39
39 /** 40 /**
  41 +* file stream to read/write file.
  42 +*/
  43 +class SrsFileStream
  44 +{
  45 +private:
  46 + std::string _file;
  47 + int fd;
  48 +public:
  49 + SrsFileStream();
  50 + virtual ~SrsFileStream();
  51 +public:
  52 + virtual int open(std::string file);
  53 + virtual int close();
  54 +public:
  55 + /**
  56 + * @param pnread, return the read size. NULL to ignore.
  57 + */
  58 + virtual int read(void* buf, size_t count, ssize_t* pnread);
  59 + /**
  60 + * @param pnwrite, return the write size. NULL to ignore.
  61 + */
  62 + virtual int write(void* buf, size_t count, ssize_t* pnwrite);
  63 +public:
  64 + /**
  65 + * get size of file.
  66 + */
  67 + virtual int64_t size();
  68 + /**
  69 + * wrapper for system lseek where whence always use SEEK_SET
  70 + */
  71 + virtual off_t lseek(off_t offset);
  72 +};
  73 +
  74 +/**
40 * encode data to flv file. 75 * encode data to flv file.
41 */ 76 */
42 class SrsFlvEncoder 77 class SrsFlvEncoder
43 { 78 {
  79 +private:
  80 + SrsFileStream* _fs;
  81 +private:
  82 + bool has_audio;
  83 + bool has_video;
  84 + SrsStream* tag_stream;
  85 +public:
  86 + SrsFlvEncoder();
  87 + virtual ~SrsFlvEncoder();
  88 +public:
  89 + virtual int initialize(SrsFileStream* fs);
  90 +public:
  91 + /**
  92 + * write flv header.
  93 + * user can invoke this method multiple times,
  94 + * for example, when get audio/video sequence header.
  95 + *
  96 + * write following:
  97 + * 1. E.2 The FLV header
  98 + * 2. PreviousTagSize0 UI32 Always 0
  99 + * that is, 9+4=13bytes.
  100 + */
  101 + virtual int write_header();
  102 + virtual int write_metadata(char* data, int size);
  103 + virtual int write_audio(int32_t timestamp, char* data, int size);
  104 + virtual int write_video(int32_t timestamp, char* data, int size);
  105 +private:
  106 + virtual int write_tag(char* header, int header_size, char* data, int size);
44 }; 107 };
45 108
46 /** 109 /**
@@ -51,6 +114,10 @@ class SrsDvr @@ -51,6 +114,10 @@ class SrsDvr
51 { 114 {
52 private: 115 private:
53 SrsSource* _source; 116 SrsSource* _source;
  117 +private:
  118 + bool dvr_enabled;
  119 + SrsFileStream* fs;
  120 + SrsFlvEncoder* enc;
54 public: 121 public:
55 SrsDvr(SrsSource* source); 122 SrsDvr(SrsSource* source);
56 virtual ~SrsDvr(); 123 virtual ~SrsDvr();
@@ -68,7 +135,7 @@ public: @@ -68,7 +135,7 @@ public:
68 /** 135 /**
69 * get some information from metadata, it's optinal. 136 * get some information from metadata, it's optinal.
70 */ 137 */
71 - virtual int on_meta_data(SrsAmf0Object* metadata); 138 + virtual int on_meta_data(SrsOnMetaDataPacket* metadata);
72 /** 139 /**
73 * mux the audio packets to dvr. 140 * mux the audio packets to dvr.
74 */ 141 */
@@ -37,6 +37,7 @@ using namespace std; @@ -37,6 +37,7 @@ using namespace std;
37 #include <srs_app_encoder.hpp> 37 #include <srs_app_encoder.hpp>
38 #include <srs_protocol_rtmp.hpp> 38 #include <srs_protocol_rtmp.hpp>
39 #include <srs_app_dvr.hpp> 39 #include <srs_app_dvr.hpp>
  40 +#include <srs_kernel_stream.hpp>
40 41
41 #define CONST_MAX_JITTER_MS 500 42 #define CONST_MAX_JITTER_MS 500
42 #define DEFAULT_FRAME_TIME_MS 40 43 #define DEFAULT_FRAME_TIME_MS 40
@@ -651,7 +652,6 @@ int SrsSource::on_hls_start() @@ -651,7 +652,6 @@ int SrsSource::on_hls_start()
651 int ret = ERROR_SUCCESS; 652 int ret = ERROR_SUCCESS;
652 653
653 #ifdef SRS_AUTO_HLS 654 #ifdef SRS_AUTO_HLS
654 -  
655 // feed the hls the metadata/sequence header, 655 // feed the hls the metadata/sequence header,
656 // when reload to start hls, hls will never get the sequence header in stream, 656 // when reload to start hls, hls will never get the sequence header in stream,
657 // use the SrsSource.on_hls_start to push the sequence header to HLS. 657 // use the SrsSource.on_hls_start to push the sequence header to HLS.
@@ -664,7 +664,49 @@ int SrsSource::on_hls_start() @@ -664,7 +664,49 @@ int SrsSource::on_hls_start()
664 srs_error("hls process audio sequence header message failed. ret=%d", ret); 664 srs_error("hls process audio sequence header message failed. ret=%d", ret);
665 return ret; 665 return ret;
666 } 666 }
  667 +#endif
  668 +
  669 + return ret;
  670 +}
  671 +
  672 +int SrsSource::on_dvr_start()
  673 +{
  674 + int ret = ERROR_SUCCESS;
  675 +
  676 +#ifdef SRS_AUTO_DVR
  677 + // feed the dvr the metadata/sequence header,
  678 + // when reload to start dvr, dvr will never get the sequence header in stream,
  679 + // use the SrsSource.on_dvr_start to push the sequence header to DVR.
  680 + if (cache_metadata) {
  681 + char* payload = (char*)cache_metadata->payload;
  682 + int size = (int)cache_metadata->size;
  683 +
  684 + SrsStream stream;
  685 + if ((ret = stream.initialize(payload, size)) != ERROR_SUCCESS) {
  686 + srs_error("dvr decode metadata stream failed. ret=%d", ret);
  687 + return ret;
  688 + }
  689 +
  690 + SrsOnMetaDataPacket pkt;
  691 + if ((ret = pkt.decode(&stream)) != ERROR_SUCCESS) {
  692 + srs_error("dvr decode metadata packet failed.");
  693 + return ret;
  694 + }
667 695
  696 + if ((ret = dvr->on_meta_data(&pkt)) != ERROR_SUCCESS) {
  697 + srs_error("dvr process onMetaData message failed. ret=%d", ret);
  698 + return ret;
  699 + }
  700 + }
  701 +
  702 + if (cache_sh_video && (ret = dvr->on_video(cache_sh_video->copy())) != ERROR_SUCCESS) {
  703 + srs_error("dvr process video sequence header message failed. ret=%d", ret);
  704 + return ret;
  705 + }
  706 + if (cache_sh_audio && (ret = dvr->on_audio(cache_sh_audio->copy())) != ERROR_SUCCESS) {
  707 + srs_error("dvr process audio sequence header message failed. ret=%d", ret);
  708 + return ret;
  709 + }
668 #endif 710 #endif
669 711
670 return ret; 712 return ret;
@@ -687,7 +729,7 @@ int SrsSource::on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata @@ -687,7 +729,7 @@ int SrsSource::on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata
687 #endif 729 #endif
688 730
689 #ifdef SRS_AUTO_DVR 731 #ifdef SRS_AUTO_DVR
690 - if (metadata && (ret = dvr->on_meta_data(metadata->metadata)) != ERROR_SUCCESS) { 732 + if (metadata && (ret = dvr->on_meta_data(metadata)) != ERROR_SUCCESS) {
691 srs_error("dvr process onMetaData message failed. ret=%d", ret); 733 srs_error("dvr process onMetaData message failed. ret=%d", ret);
692 return ret; 734 return ret;
693 } 735 }
@@ -284,6 +284,8 @@ public: @@ -284,6 +284,8 @@ public:
284 virtual int on_forwarder_start(SrsForwarder* forwarder); 284 virtual int on_forwarder_start(SrsForwarder* forwarder);
285 // for the SrsHls to callback to request the sequence headers. 285 // for the SrsHls to callback to request the sequence headers.
286 virtual int on_hls_start(); 286 virtual int on_hls_start();
  287 + // for the SrsDvr to callback to request the sequence headers.
  288 + virtual int on_dvr_start();
287 public: 289 public:
288 virtual bool can_publish(); 290 virtual bool can_publish();
289 virtual int on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata); 291 virtual int on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata);
@@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 // current release version 31 // current release version
32 #define VERSION_MAJOR "0" 32 #define VERSION_MAJOR "0"
33 #define VERSION_MINOR "9" 33 #define VERSION_MINOR "9"
34 -#define VERSION_REVISION "68" 34 +#define VERSION_REVISION "69"
35 #define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION 35 #define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION
36 // server info. 36 // server info.
37 #define RTMP_SIG_SRS_KEY "srs" 37 #define RTMP_SIG_SRS_KEY "srs"
@@ -100,6 +100,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -100,6 +100,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
100 #define ERROR_SYSTEM_PID_WRITE_FILE 420 100 #define ERROR_SYSTEM_PID_WRITE_FILE 420
101 #define ERROR_SYSTEM_PID_GET_FILE_INFO 421 101 #define ERROR_SYSTEM_PID_GET_FILE_INFO 421
102 #define ERROR_SYSTEM_PID_SET_FILE_INFO 422 102 #define ERROR_SYSTEM_PID_SET_FILE_INFO 422
  103 +#define ERROR_SYSTEM_FILE_ALREADY_OPENED 423
  104 +#define ERROR_SYSTEM_FILE_OPENE 424
  105 +#define ERROR_SYSTEM_FILE_CLOSE 425
  106 +#define ERROR_SYSTEM_FILE_READ 426
  107 +#define ERROR_SYSTEM_FILE_WRITE 427
  108 +#define ERROR_SYSTEM_FILE_EOF 428
103 109
104 // see librtmp. 110 // see librtmp.
105 // failed when open ssl create the dh 111 // failed when open ssl create the dh
@@ -199,6 +199,16 @@ void SrsStream::write_4bytes(int32_t value) @@ -199,6 +199,16 @@ void SrsStream::write_4bytes(int32_t value)
199 *p++ = pp[0]; 199 *p++ = pp[0];
200 } 200 }
201 201
  202 +void SrsStream::write_3bytes(int32_t value)
  203 +{
  204 + srs_assert(require(3));
  205 +
  206 + pp = (char*)&value;
  207 + *p++ = pp[2];
  208 + *p++ = pp[1];
  209 + *p++ = pp[0];
  210 +}
  211 +
202 void SrsStream::write_8bytes(int64_t value) 212 void SrsStream::write_8bytes(int64_t value)
203 { 213 {
204 srs_assert(require(8)); 214 srs_assert(require(8));
@@ -118,6 +118,10 @@ public: @@ -118,6 +118,10 @@ public:
118 */ 118 */
119 virtual void write_4bytes(int32_t value); 119 virtual void write_4bytes(int32_t value);
120 /** 120 /**
  121 + * write 3bytes int to stream.
  122 + */
  123 + virtual void write_3bytes(int32_t value);
  124 + /**
121 * write 8bytes int to stream. 125 * write 8bytes int to stream.
122 */ 126 */
123 virtual void write_8bytes(int64_t value); 127 virtual void write_8bytes(int64_t value);