From e0ee8de2fb2b6014fd655fcd3fe121cc60fb79ef Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 23 Jan 2015 13:17:50 +0800 Subject: [PATCH] for #299, srs http server support dash, flash request range in range or bytes. --- trunk/conf/full.conf | 8 +++++ trunk/src/app/srs_app_http.cpp | 44 ++++++++++++++++++++++-- trunk/src/app/srs_app_http.hpp | 11 ++++-- trunk/src/app/srs_app_http_conn.cpp | 53 +++++++++++++++++++++++++++++ trunk/src/app/srs_app_http_conn.hpp | 1 + 5 files changed, 113 insertions(+), 4 deletions(-) diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 6d0ebf9da..02a72fde9 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -133,6 +133,14 @@ http_server { dir ./objs/nginx/html; } +############################################################################################# +# Streamer sections +############################################################################################# +# the streamer cast stream from other protocol to SRS over RTMP. +# @see https://github.com/winlinvip/simple-rtmp-server/tree/develop#stream-architecture +streamer { +} + ############################################################################################# # RTMP/HTTP VHOST sections ############################################################################################# diff --git a/trunk/src/app/srs_app_http.cpp b/trunk/src/app/srs_app_http.cpp index 492ff00c2..15728f114 100644 --- a/trunk/src/app/srs_app_http.cpp +++ b/trunk/src/app/srs_app_http.cpp @@ -317,6 +317,40 @@ int SrsGoHttpFileServer::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* } return serve_flv_stream(w, r, fullpath, offset); + } else if (srs_string_ends_with(fullpath, ".mp4")) { + // for flash to request mp4 range in query string. + // for example, http://digitalprimates.net/dash/DashTest.html?url=http://dashdemo.edgesuite.net/digitalprimates/nexus/oops-20120802-manifest.mpd + std::string range = r->query_get("range"); + // or, use bytes to request range, + // for example, http://dashas.castlabs.com/demo/try.html + if (range.empty()) { + range = r->query_get("bytes"); + } + + // rollback to serve whole file. + size_t pos = string::npos; + if (range.empty() || (pos = range.find("-")) == string::npos) { + return serve_file(w, r, fullpath); + } + + // parse the start in query string + int start = 0; + if (pos > 0) { + start = ::atoi(range.substr(0, pos).c_str()); + } + + // parse end in query string. + int end = -1; + if (pos < range.length() - 1) { + end = ::atoi(range.substr(pos + 1).c_str()); + } + + // invalid param, serve as whole mp4 file. + if (start < 0 || (end != -1 && start > end)) { + return serve_file(w, r, fullpath); + } + + return serve_mp4_range(w, r, fullpath, start, end); } // serve common static file. @@ -377,7 +411,7 @@ int SrsGoHttpFileServer::serve_file(ISrsGoHttpResponseWriter* w, SrsHttpMessage* } if (_mime.find(ext) == _mime.end()) { - w->header()->set_content_type("text/html;charset=utf-8"); + w->header()->set_content_type("application/octet-stream"); } else { w->header()->set_content_type(_mime[ext]); } @@ -398,6 +432,11 @@ int SrsGoHttpFileServer::serve_flv_stream(ISrsGoHttpResponseWriter* w, SrsHttpMe return serve_file(w, r, fullpath); } +int SrsGoHttpFileServer::serve_mp4_range(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, string fullpath, int start, int end) +{ + return serve_file(w, r, fullpath); +} + int SrsGoHttpFileServer::copy(ISrsGoHttpResponseWriter* w, SrsFileReader* fs, SrsHttpMessage* r, int size) { int ret = ERROR_SUCCESS; @@ -407,7 +446,8 @@ int SrsGoHttpFileServer::copy(ISrsGoHttpResponseWriter* w, SrsFileReader* fs, Sr while (left > 0) { ssize_t nread = -1; - if ((ret = fs->read(buf, __SRS_HTTP_TS_SEND_BUFFER_SIZE, &nread)) != ERROR_SUCCESS) { + int max_read = srs_min(left, __SRS_HTTP_TS_SEND_BUFFER_SIZE); + if ((ret = fs->read(buf, max_read, &nread)) != ERROR_SUCCESS) { break; } diff --git a/trunk/src/app/srs_app_http.hpp b/trunk/src/app/srs_app_http.hpp index 5ca2de67b..6a9816b61 100644 --- a/trunk/src/app/srs_app_http.hpp +++ b/trunk/src/app/srs_app_http.hpp @@ -213,13 +213,20 @@ public: virtual int serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r); protected: /** - * serve the file by specified path. + * serve the file by specified path */ virtual int serve_file(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath); /** - * when access flv file with start=xxx. + * when access flv file with x.flv?start=xxx */ virtual int serve_flv_stream(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath, int offset); + /** + * when access mp4 file with x.mp4?range=start-end + * @param start the start offset in bytes. + * @param end the end offset in bytes. -1 to end of file. + * @remark response data in [start, end]. + */ + virtual int serve_mp4_range(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath, int start, int end); protected: /** * copy the fs to response writer in size bytes. diff --git a/trunk/src/app/srs_app_http_conn.cpp b/trunk/src/app/srs_app_http_conn.cpp index 91685630a..60dcaf687 100644 --- a/trunk/src/app/srs_app_http_conn.cpp +++ b/trunk/src/app/srs_app_http_conn.cpp @@ -141,6 +141,59 @@ int SrsVodStream::serve_flv_stream(ISrsGoHttpResponseWriter* w, SrsHttpMessage* return ret; } +int SrsVodStream::serve_mp4_range(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, string fullpath, int start, int end) +{ + int ret = ERROR_SUCCESS; + + srs_assert(start >= 0); + srs_assert(end == -1 || end >= 0); + + SrsFileReader fs; + + // open flv file + if ((ret = fs.open(fullpath)) != ERROR_SUCCESS) { + return ret; + } + + // parse -1 to whole file. + if (end == -1) { + end = fs.filesize(); + } + + if (end > fs.filesize() || start > end) { + ret = ERROR_HTTP_REMUX_OFFSET_OVERFLOW; + srs_warn("http mp4 streaming %s overflow. size=%"PRId64", offset=%d, ret=%d", + fullpath.c_str(), fs.filesize(), start, ret); + return ret; + } + + // seek to data offset, [start, end] for range. + int64_t left = end - start + 1; + + // write http header for ts. + w->header()->set_content_length(left); + w->header()->set_content_type("video/mp4"); + + // status code 206 to make dash.as happy. + w->write_header(SRS_CONSTS_HTTP_PartialContent); + + // response the content range header. + std::stringstream content_range; + content_range << "bytes " << start << "-" << end << "/" << fs.filesize(); + w->header()->set("Content-Range", content_range.str()); + + // write body. + fs.lseek(start); + + // send data + if ((ret = copy(w, &fs, r, left)) != ERROR_SUCCESS) { + srs_warn("read mp4=%s size=%d failed, ret=%d", fullpath.c_str(), left, ret); + return ret; + } + + return ret; +} + SrsStreamCache::SrsStreamCache(SrsSource* s, SrsRequest* r) { req = r->copy(); diff --git a/trunk/src/app/srs_app_http_conn.hpp b/trunk/src/app/srs_app_http_conn.hpp index 6fe8f8c18..2a51d7559 100644 --- a/trunk/src/app/srs_app_http_conn.hpp +++ b/trunk/src/app/srs_app_http_conn.hpp @@ -66,6 +66,7 @@ public: virtual ~SrsVodStream(); protected: virtual int serve_flv_stream(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath, int offset); + virtual int serve_mp4_range(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath, int start, int end); }; /**