正在显示
13 个修改的文件
包含
600 行增加
和
21 行删除
@@ -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 | + } | ||
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 | + } | ||
667 | 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); |
-
请 注册 或 登录 后发表评论