正在显示
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); |
-
请 注册 或 登录 后发表评论