winlin

for #299, srs http server support dash, flash request range in range or bytes.

@@ -134,6 +134,14 @@ http_server { @@ -134,6 +134,14 @@ http_server {
134 } 134 }
135 135
136 ############################################################################################# 136 #############################################################################################
  137 +# Streamer sections
  138 +#############################################################################################
  139 +# the streamer cast stream from other protocol to SRS over RTMP.
  140 +# @see https://github.com/winlinvip/simple-rtmp-server/tree/develop#stream-architecture
  141 +streamer {
  142 +}
  143 +
  144 +#############################################################################################
137 # RTMP/HTTP VHOST sections 145 # RTMP/HTTP VHOST sections
138 ############################################################################################# 146 #############################################################################################
139 # vhost list, the __defaultVhost__ is the default vhost 147 # vhost list, the __defaultVhost__ is the default vhost
@@ -317,6 +317,40 @@ int SrsGoHttpFileServer::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* @@ -317,6 +317,40 @@ int SrsGoHttpFileServer::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage*
317 } 317 }
318 318
319 return serve_flv_stream(w, r, fullpath, offset); 319 return serve_flv_stream(w, r, fullpath, offset);
  320 + } else if (srs_string_ends_with(fullpath, ".mp4")) {
  321 + // for flash to request mp4 range in query string.
  322 + // for example, http://digitalprimates.net/dash/DashTest.html?url=http://dashdemo.edgesuite.net/digitalprimates/nexus/oops-20120802-manifest.mpd
  323 + std::string range = r->query_get("range");
  324 + // or, use bytes to request range,
  325 + // for example, http://dashas.castlabs.com/demo/try.html
  326 + if (range.empty()) {
  327 + range = r->query_get("bytes");
  328 + }
  329 +
  330 + // rollback to serve whole file.
  331 + size_t pos = string::npos;
  332 + if (range.empty() || (pos = range.find("-")) == string::npos) {
  333 + return serve_file(w, r, fullpath);
  334 + }
  335 +
  336 + // parse the start in query string
  337 + int start = 0;
  338 + if (pos > 0) {
  339 + start = ::atoi(range.substr(0, pos).c_str());
  340 + }
  341 +
  342 + // parse end in query string.
  343 + int end = -1;
  344 + if (pos < range.length() - 1) {
  345 + end = ::atoi(range.substr(pos + 1).c_str());
  346 + }
  347 +
  348 + // invalid param, serve as whole mp4 file.
  349 + if (start < 0 || (end != -1 && start > end)) {
  350 + return serve_file(w, r, fullpath);
  351 + }
  352 +
  353 + return serve_mp4_range(w, r, fullpath, start, end);
320 } 354 }
321 355
322 // serve common static file. 356 // serve common static file.
@@ -377,7 +411,7 @@ int SrsGoHttpFileServer::serve_file(ISrsGoHttpResponseWriter* w, SrsHttpMessage* @@ -377,7 +411,7 @@ int SrsGoHttpFileServer::serve_file(ISrsGoHttpResponseWriter* w, SrsHttpMessage*
377 } 411 }
378 412
379 if (_mime.find(ext) == _mime.end()) { 413 if (_mime.find(ext) == _mime.end()) {
380 - w->header()->set_content_type("text/html;charset=utf-8"); 414 + w->header()->set_content_type("application/octet-stream");
381 } else { 415 } else {
382 w->header()->set_content_type(_mime[ext]); 416 w->header()->set_content_type(_mime[ext]);
383 } 417 }
@@ -398,6 +432,11 @@ int SrsGoHttpFileServer::serve_flv_stream(ISrsGoHttpResponseWriter* w, SrsHttpMe @@ -398,6 +432,11 @@ int SrsGoHttpFileServer::serve_flv_stream(ISrsGoHttpResponseWriter* w, SrsHttpMe
398 return serve_file(w, r, fullpath); 432 return serve_file(w, r, fullpath);
399 } 433 }
400 434
  435 +int SrsGoHttpFileServer::serve_mp4_range(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, string fullpath, int start, int end)
  436 +{
  437 + return serve_file(w, r, fullpath);
  438 +}
  439 +
401 int SrsGoHttpFileServer::copy(ISrsGoHttpResponseWriter* w, SrsFileReader* fs, SrsHttpMessage* r, int size) 440 int SrsGoHttpFileServer::copy(ISrsGoHttpResponseWriter* w, SrsFileReader* fs, SrsHttpMessage* r, int size)
402 { 441 {
403 int ret = ERROR_SUCCESS; 442 int ret = ERROR_SUCCESS;
@@ -407,7 +446,8 @@ int SrsGoHttpFileServer::copy(ISrsGoHttpResponseWriter* w, SrsFileReader* fs, Sr @@ -407,7 +446,8 @@ int SrsGoHttpFileServer::copy(ISrsGoHttpResponseWriter* w, SrsFileReader* fs, Sr
407 446
408 while (left > 0) { 447 while (left > 0) {
409 ssize_t nread = -1; 448 ssize_t nread = -1;
410 - if ((ret = fs->read(buf, __SRS_HTTP_TS_SEND_BUFFER_SIZE, &nread)) != ERROR_SUCCESS) { 449 + int max_read = srs_min(left, __SRS_HTTP_TS_SEND_BUFFER_SIZE);
  450 + if ((ret = fs->read(buf, max_read, &nread)) != ERROR_SUCCESS) {
411 break; 451 break;
412 } 452 }
413 453
@@ -213,13 +213,20 @@ public: @@ -213,13 +213,20 @@ public:
213 virtual int serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r); 213 virtual int serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r);
214 protected: 214 protected:
215 /** 215 /**
216 - * serve the file by specified path. 216 + * serve the file by specified path
217 */ 217 */
218 virtual int serve_file(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath); 218 virtual int serve_file(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath);
219 /** 219 /**
220 - * when access flv file with start=xxx. 220 + * when access flv file with x.flv?start=xxx
221 */ 221 */
222 virtual int serve_flv_stream(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath, int offset); 222 virtual int serve_flv_stream(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath, int offset);
  223 + /**
  224 + * when access mp4 file with x.mp4?range=start-end
  225 + * @param start the start offset in bytes.
  226 + * @param end the end offset in bytes. -1 to end of file.
  227 + * @remark response data in [start, end].
  228 + */
  229 + virtual int serve_mp4_range(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath, int start, int end);
223 protected: 230 protected:
224 /** 231 /**
225 * copy the fs to response writer in size bytes. 232 * copy the fs to response writer in size bytes.
@@ -141,6 +141,59 @@ int SrsVodStream::serve_flv_stream(ISrsGoHttpResponseWriter* w, SrsHttpMessage* @@ -141,6 +141,59 @@ int SrsVodStream::serve_flv_stream(ISrsGoHttpResponseWriter* w, SrsHttpMessage*
141 return ret; 141 return ret;
142 } 142 }
143 143
  144 +int SrsVodStream::serve_mp4_range(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, string fullpath, int start, int end)
  145 +{
  146 + int ret = ERROR_SUCCESS;
  147 +
  148 + srs_assert(start >= 0);
  149 + srs_assert(end == -1 || end >= 0);
  150 +
  151 + SrsFileReader fs;
  152 +
  153 + // open flv file
  154 + if ((ret = fs.open(fullpath)) != ERROR_SUCCESS) {
  155 + return ret;
  156 + }
  157 +
  158 + // parse -1 to whole file.
  159 + if (end == -1) {
  160 + end = fs.filesize();
  161 + }
  162 +
  163 + if (end > fs.filesize() || start > end) {
  164 + ret = ERROR_HTTP_REMUX_OFFSET_OVERFLOW;
  165 + srs_warn("http mp4 streaming %s overflow. size=%"PRId64", offset=%d, ret=%d",
  166 + fullpath.c_str(), fs.filesize(), start, ret);
  167 + return ret;
  168 + }
  169 +
  170 + // seek to data offset, [start, end] for range.
  171 + int64_t left = end - start + 1;
  172 +
  173 + // write http header for ts.
  174 + w->header()->set_content_length(left);
  175 + w->header()->set_content_type("video/mp4");
  176 +
  177 + // status code 206 to make dash.as happy.
  178 + w->write_header(SRS_CONSTS_HTTP_PartialContent);
  179 +
  180 + // response the content range header.
  181 + std::stringstream content_range;
  182 + content_range << "bytes " << start << "-" << end << "/" << fs.filesize();
  183 + w->header()->set("Content-Range", content_range.str());
  184 +
  185 + // write body.
  186 + fs.lseek(start);
  187 +
  188 + // send data
  189 + if ((ret = copy(w, &fs, r, left)) != ERROR_SUCCESS) {
  190 + srs_warn("read mp4=%s size=%d failed, ret=%d", fullpath.c_str(), left, ret);
  191 + return ret;
  192 + }
  193 +
  194 + return ret;
  195 +}
  196 +
144 SrsStreamCache::SrsStreamCache(SrsSource* s, SrsRequest* r) 197 SrsStreamCache::SrsStreamCache(SrsSource* s, SrsRequest* r)
145 { 198 {
146 req = r->copy(); 199 req = r->copy();
@@ -66,6 +66,7 @@ public: @@ -66,6 +66,7 @@ public:
66 virtual ~SrsVodStream(); 66 virtual ~SrsVodStream();
67 protected: 67 protected:
68 virtual int serve_flv_stream(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath, int offset); 68 virtual int serve_flv_stream(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath, int offset);
  69 + virtual int serve_mp4_range(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath, int start, int end);
69 }; 70 };
70 71
71 /** 72 /**