diff --git a/trunk/src/app/srs_app_http.cpp b/trunk/src/app/srs_app_http.cpp index ff9a10f87..cdaaca3f3 100644 --- a/trunk/src/app/srs_app_http.cpp +++ b/trunk/src/app/srs_app_http.cpp @@ -38,7 +38,6 @@ using namespace std; #include #include #include -#include #include #define SRS_DEFAULT_HTTP_PORT 80 @@ -289,23 +288,23 @@ int SrsGoHttpFileServer::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* if (srs_string_ends_with(fullpath, ".flv") || srs_string_ends_with(fullpath, ".fhv")) { std::string start = r->query_get("start"); if (start.empty()) { - return serve_file(fullpath, w, r); + return serve_file(w, r, fullpath); } int offset = ::atoi(start.c_str()); if (offset <= 0) { - return serve_file(fullpath, w, r); + return serve_file(w, r, fullpath); } - return serve_flv_stream(fullpath, w, r, offset); + return serve_flv_stream(w, r, fullpath, offset); } else { - return serve_file(fullpath, w, r); + return serve_file(w, r, fullpath); } return ret; } -int SrsGoHttpFileServer::serve_file(string fullpath, ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoHttpFileServer::serve_file(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, string fullpath) { int ret = ERROR_SUCCESS; @@ -345,110 +344,35 @@ int SrsGoHttpFileServer::serve_file(string fullpath, ISrsGoHttpResponseWriter* w // write body. int64_t left = length; - char* buf = r->http_ts_send_buffer(); - - while (left > 0) { - ssize_t nread = -1; - if ((ret = fs.read(buf, __SRS_HTTP_TS_SEND_BUFFER_SIZE, &nread)) != ERROR_SUCCESS) { - srs_warn("read file %s failed, ret=%d", fullpath.c_str(), ret); - break; - } - - left -= nread; - if ((ret = w->write(buf, nread)) != ERROR_SUCCESS) { - break; - } + if ((ret = copy(&fs, w, r, left)) != ERROR_SUCCESS) { + srs_warn("read file=%s size=%d failed, ret=%d", fullpath.c_str(), left, ret); + return ret; } return ret; } -int SrsGoHttpFileServer::serve_flv_stream(string fullpath, ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, int offset) +int SrsGoHttpFileServer::serve_flv_stream(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, string fullpath, int offset) +{ + return serve_file(w, r, fullpath); +} + +int SrsGoHttpFileServer::copy(SrsFileReader* fs, ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, int size) { int ret = ERROR_SUCCESS; - SrsFileReader fs; - - // open flv file - if ((ret = fs.open(fullpath)) != ERROR_SUCCESS) { - return ret; - } - - if (offset > fs.filesize()) { - ret = ERROR_HTTP_FLV_OFFSET_OVERFLOW; - srs_warn("http flv streaming %s overflow. size=%"PRId64", offset=%d, ret=%d", - fullpath.c_str(), fs.filesize(), offset, ret); - return ret; - } - - SrsFlvVodStreamDecoder ffd; - - // open fast decoder - if ((ret = ffd.initialize(&fs)) != ERROR_SUCCESS) { - return ret; - } - - // save header, send later. - char flv_header[13]; - - // send flv header - if ((ret = ffd.read_header_ext(flv_header)) != ERROR_SUCCESS) { - return ret; - } - - // save sequence header, send later - char* sh_data = NULL; - int sh_size = 0; - - if (true) { - // send sequence header - int64_t start = 0; - if ((ret = ffd.read_sequence_header_summary(&start, &sh_size)) != ERROR_SUCCESS) { - return ret; - } - if (sh_size <= 0) { - ret = ERROR_HTTP_FLV_SEQUENCE_HEADER; - srs_warn("http flv streaming no sequence header. size=%d, ret=%d", sh_size, ret); - return ret; - } - } - sh_data = new char[sh_size]; - SrsAutoFree(char, sh_data); - if ((ret = fs.read(sh_data, sh_size, NULL)) != ERROR_SUCCESS) { - return ret; - } - - // seek to data offset - int64_t left = fs.filesize() - offset; - - // write http header for ts. - w->header()->set_content_length((int)(sizeof(flv_header) + sh_size + left)); - w->header()->set_content_type("video/x-flv"); - - // write flv header and sequence header. - if ((ret = w->write(flv_header, sizeof(flv_header))) != ERROR_SUCCESS) { - return ret; - } - if (sh_size > 0 && (ret = w->write(sh_data, sh_size)) != ERROR_SUCCESS) { - return ret; - } - - // write body. + int left = size; char* buf = r->http_ts_send_buffer(); - if ((ret = ffd.lseek(offset)) != ERROR_SUCCESS) { - return ret; - } - // send data while (left > 0) { ssize_t nread = -1; - if ((ret = fs.read(buf, __SRS_HTTP_TS_SEND_BUFFER_SIZE, &nread)) != ERROR_SUCCESS) { - return ret; + if ((ret = fs->read(buf, __SRS_HTTP_TS_SEND_BUFFER_SIZE, &nread)) != ERROR_SUCCESS) { + break; } left -= nread; if ((ret = w->write(buf, nread)) != ERROR_SUCCESS) { - return ret; + break; } } diff --git a/trunk/src/app/srs_app_http.hpp b/trunk/src/app/srs_app_http.hpp index 9e14b2844..3c4005023 100644 --- a/trunk/src/app/srs_app_http.hpp +++ b/trunk/src/app/srs_app_http.hpp @@ -40,12 +40,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include -class SrsSimpleBuffer; class SrsRequest; class SrsStSocket; class SrsHttpUri; class SrsHttpMessage; -class SrsHttpHandler; +class SrsFileReader; +class SrsSimpleBuffer; class ISrsGoHttpResponseWriter; // http specification @@ -197,16 +197,27 @@ public: // http.Handle("/", SrsGoHttpFileServer("static-dir")) class SrsGoHttpFileServer : public ISrsGoHttpHandler { -private: +protected: std::string dir; public: SrsGoHttpFileServer(std::string root_dir); virtual ~SrsGoHttpFileServer(); public: virtual int serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r); -private: - virtual int serve_file(std::string fullpath, ISrsGoHttpResponseWriter* w, SrsHttpMessage* r); - virtual int serve_flv_stream(std::string fullpath, ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, int offset); +protected: + /** + * serve the file by specified path. + */ + virtual int serve_file(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath); + /** + * when access flv file with start=xxx. + */ + virtual int serve_flv_stream(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath, int offset); +protected: + /** + * copy the fs to response writer in size bytes. + */ + virtual int copy(SrsFileReader* fs, ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, int size); }; // the mux entry for server mux. diff --git a/trunk/src/app/srs_app_http_conn.cpp b/trunk/src/app/srs_app_http_conn.cpp index bbeccd5ed..8c6db2a3e 100644 --- a/trunk/src/app/srs_app_http_conn.cpp +++ b/trunk/src/app/srs_app_http_conn.cpp @@ -39,6 +39,101 @@ using namespace std; #include #include #include +#include +#include + +SrsVodStream::SrsVodStream(string root_dir) + : SrsGoHttpFileServer(root_dir) +{ +} + +SrsVodStream::~SrsVodStream() +{ +} + +int SrsVodStream::serve_flv_stream(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, string fullpath, int offset) +{ + int ret = ERROR_SUCCESS; + + SrsFileReader fs; + + // open flv file + if ((ret = fs.open(fullpath)) != ERROR_SUCCESS) { + return ret; + } + + if (offset > fs.filesize()) { + ret = ERROR_HTTP_FLV_OFFSET_OVERFLOW; + srs_warn("http flv streaming %s overflow. size=%"PRId64", offset=%d, ret=%d", + fullpath.c_str(), fs.filesize(), offset, ret); + return ret; + } + + SrsFlvVodStreamDecoder ffd; + + // open fast decoder + if ((ret = ffd.initialize(&fs)) != ERROR_SUCCESS) { + return ret; + } + + // save header, send later. + char flv_header[13]; + + // send flv header + if ((ret = ffd.read_header_ext(flv_header)) != ERROR_SUCCESS) { + return ret; + } + + // save sequence header, send later + char* sh_data = NULL; + int sh_size = 0; + + if (true) { + // send sequence header + int64_t start = 0; + if ((ret = ffd.read_sequence_header_summary(&start, &sh_size)) != ERROR_SUCCESS) { + return ret; + } + if (sh_size <= 0) { + ret = ERROR_HTTP_FLV_SEQUENCE_HEADER; + srs_warn("http flv streaming no sequence header. size=%d, ret=%d", sh_size, ret); + return ret; + } + } + sh_data = new char[sh_size]; + SrsAutoFree(char, sh_data); + if ((ret = fs.read(sh_data, sh_size, NULL)) != ERROR_SUCCESS) { + return ret; + } + + // seek to data offset + int64_t left = fs.filesize() - offset; + + // write http header for ts. + w->header()->set_content_length((int)(sizeof(flv_header) + sh_size + left)); + w->header()->set_content_type("video/x-flv"); + + // write flv header and sequence header. + if ((ret = w->write(flv_header, sizeof(flv_header))) != ERROR_SUCCESS) { + return ret; + } + if (sh_size > 0 && (ret = w->write(sh_data, sh_size)) != ERROR_SUCCESS) { + return ret; + } + + // write body. + if ((ret = ffd.lseek(offset)) != ERROR_SUCCESS) { + return ret; + } + + // send data + if ((ret = copy(&fs, w, r, left)) != ERROR_SUCCESS) { + srs_warn("read flv=%s size=%d failed, ret=%d", fullpath.c_str(), left, ret); + return ret; + } + + return ret; +} SrsHttpServer::SrsHttpServer() { @@ -71,7 +166,7 @@ int SrsHttpServer::initialize() std::string mount = _srs_config->get_vhost_http_mount(vhost); std::string dir = _srs_config->get_vhost_http_dir(vhost); - if ((ret = mux.handle(mount, new SrsGoHttpFileServer(dir))) != ERROR_SUCCESS) { + if ((ret = mux.handle(mount, new SrsVodStream(dir))) != ERROR_SUCCESS) { srs_error("http: mount dir=%s for vhost=%s failed. ret=%d", dir.c_str(), vhost.c_str(), ret); return ret; } @@ -84,7 +179,7 @@ int SrsHttpServer::initialize() if (!default_root_exists) { // add root std::string dir = _srs_config->get_http_stream_dir(); - if ((ret = mux.handle("/", new SrsGoHttpFileServer(dir))) != ERROR_SUCCESS) { + if ((ret = mux.handle("/", new SrsVodStream(dir))) != ERROR_SUCCESS) { srs_error("http: mount root dir=%s failed. ret=%d", dir.c_str(), ret); return ret; } diff --git a/trunk/src/app/srs_app_http_conn.hpp b/trunk/src/app/srs_app_http_conn.hpp index e258fea85..cf79f1beb 100644 --- a/trunk/src/app/srs_app_http_conn.hpp +++ b/trunk/src/app/srs_app_http_conn.hpp @@ -41,7 +41,21 @@ class SrsHttpParser; class SrsHttpMessage; class SrsHttpHandler; -// for http server. +/** +* the flv vod stream supports flv?start=offset-bytes. +* for example, http://server/file.flv?start=10240 +* server will write flv header and sequence header, +* then seek(10240) and response flv tag data. +*/ +class SrsVodStream : public SrsGoHttpFileServer +{ +public: + SrsVodStream(std::string root_dir); + virtual ~SrsVodStream(); +protected: + virtual int serve_flv_stream(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath, int offset); +}; + class SrsHttpServer { public: