wenjie.zhao

add hds supported.

@@ -392,7 +392,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then @@ -392,7 +392,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
392 "srs_app_json" "srs_app_ingest" "srs_app_ffmpeg" "srs_app_utility" "srs_app_dvr" "srs_app_edge" 392 "srs_app_json" "srs_app_ingest" "srs_app_ffmpeg" "srs_app_utility" "srs_app_dvr" "srs_app_edge"
393 "srs_app_kbps" "srs_app_heartbeat" "srs_app_empty" "srs_app_http_client" 393 "srs_app_kbps" "srs_app_heartbeat" "srs_app_empty" "srs_app_http_client"
394 "srs_app_recv_thread" "srs_app_security" "srs_app_statistic" 394 "srs_app_recv_thread" "srs_app_security" "srs_app_statistic"
395 - "srs_app_mpegts_udp" "srs_app_rtsp" "srs_app_listener") 395 + "srs_app_mpegts_udp" "srs_app_rtsp" "srs_app_listener" "srs_app_hds")
396 APP_INCS="src/app"; MODULE_DIR=${APP_INCS} . auto/modules.sh 396 APP_INCS="src/app"; MODULE_DIR=${APP_INCS} . auto/modules.sh
397 APP_OBJS="${MODULE_OBJS[@]}" 397 APP_OBJS="${MODULE_OBJS[@]}"
398 fi 398 fi
@@ -1407,6 +1407,7 @@ int SrsConfig::check_config() @@ -1407,6 +1407,7 @@ int SrsConfig::check_config()
1407 && n != "mr" && n != "mw_latency" && n != "min_latency" 1407 && n != "mr" && n != "mw_latency" && n != "min_latency"
1408 && n != "security" && n != "http_remux" 1408 && n != "security" && n != "http_remux"
1409 && n != "http" && n != "http_static" 1409 && n != "http" && n != "http_static"
  1410 + && n != "hds"
1410 ) { 1411 ) {
1411 ret = ERROR_SYSTEM_CONFIG_INVALID; 1412 ret = ERROR_SYSTEM_CONFIG_INVALID;
1412 srs_error("unsupported vhost directive %s, ret=%d", n.c_str(), ret); 1413 srs_error("unsupported vhost directive %s, ret=%d", n.c_str(), ret);
@@ -3310,6 +3311,85 @@ string SrsConfig::get_hls_vcodec(string vhost) @@ -3310,6 +3311,85 @@ string SrsConfig::get_hls_vcodec(string vhost)
3310 return conf->arg0(); 3311 return conf->arg0();
3311 } 3312 }
3312 3313
  3314 +SrsConfDirective *SrsConfig::get_hds(const string &vhost)
  3315 +{
  3316 + SrsConfDirective* conf = get_vhost(vhost);
  3317 +
  3318 + if (!conf) {
  3319 + return NULL;
  3320 + }
  3321 +
  3322 + return conf->get("hds");
  3323 +}
  3324 +
  3325 +bool SrsConfig::get_hds_enabled(const string &vhost)
  3326 +{
  3327 + SrsConfDirective* hds = get_hds(vhost);
  3328 +
  3329 + if (!hds) {
  3330 + return false;
  3331 + }
  3332 +
  3333 + SrsConfDirective* conf = hds->get("enabled");
  3334 +
  3335 + if (!conf) {
  3336 + return false;
  3337 + }
  3338 +
  3339 + return conf->arg0() == "on";
  3340 +}
  3341 +
  3342 +string SrsConfig::get_hds_path(const string &vhost)
  3343 +{
  3344 + SrsConfDirective* hds = get_hds(vhost);
  3345 +
  3346 + if (!hds) {
  3347 + return SRS_CONF_DEFAULT_HDS_PATH;
  3348 + }
  3349 +
  3350 + SrsConfDirective* conf = hds->get("hds_path");
  3351 +
  3352 + if (!conf) {
  3353 + return SRS_CONF_DEFAULT_HDS_PATH;
  3354 + }
  3355 +
  3356 + return conf->arg0();
  3357 +}
  3358 +
  3359 +double SrsConfig::get_hds_fragment(const string &vhost)
  3360 +{
  3361 + SrsConfDirective* hds = get_hds(vhost);
  3362 +
  3363 + if (!hds) {
  3364 + return SRS_CONF_DEFAULT_HDS_FRAGMENT;
  3365 + }
  3366 +
  3367 + SrsConfDirective* conf = hds->get("hds_fragment");
  3368 +
  3369 + if (!conf) {
  3370 + return SRS_CONF_DEFAULT_HDS_FRAGMENT;
  3371 + }
  3372 +
  3373 + return ::atof(conf->arg0().c_str());
  3374 +}
  3375 +
  3376 +double SrsConfig::get_hds_window(const string &vhost)
  3377 +{
  3378 + SrsConfDirective* hds = get_hds(vhost);
  3379 +
  3380 + if (!hds) {
  3381 + return SRS_CONF_DEFAULT_HDS_WINDOW;
  3382 + }
  3383 +
  3384 + SrsConfDirective* conf = hds->get("hds_window");
  3385 +
  3386 + if (!conf) {
  3387 + return SRS_CONF_DEFAULT_HDS_WINDOW;
  3388 + }
  3389 +
  3390 + return ::atof(conf->arg0().c_str());
  3391 +}
  3392 +
3313 SrsConfDirective* SrsConfig::get_dvr(string vhost) 3393 SrsConfDirective* SrsConfig::get_dvr(string vhost)
3314 { 3394 {
3315 SrsConfDirective* conf = get_vhost(vhost); 3395 SrsConfDirective* conf = get_vhost(vhost);
@@ -102,10 +102,15 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -102,10 +102,15 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
102 #define SRS_CONF_DEFAULT_TRANSCODE_IFORMAT "flv" 102 #define SRS_CONF_DEFAULT_TRANSCODE_IFORMAT "flv"
103 #define SRS_CONF_DEFAULT_TRANSCODE_OFORMAT "flv" 103 #define SRS_CONF_DEFAULT_TRANSCODE_OFORMAT "flv"
104 104
  105 +// hds default value
  106 +#define SRS_CONF_DEFAULT_HDS_PATH "./objs/nginx/html"
  107 +#define SRS_CONF_DEFAULT_HDS_WINDOW (60)
  108 +#define SRS_CONF_DEFAULT_HDS_FRAGMENT (10)
  109 +
105 namespace _srs_internal 110 namespace _srs_internal
106 { 111 {
107 class SrsConfigBuffer; 112 class SrsConfigBuffer;
108 -}; 113 +}
109 114
110 /** 115 /**
111 * the config directive. 116 * the config directive.
@@ -912,6 +917,32 @@ public: @@ -912,6 +917,32 @@ public:
912 * get the HLS default video codec. 917 * get the HLS default video codec.
913 */ 918 */
914 virtual std::string get_hls_vcodec(std::string vhost); 919 virtual std::string get_hls_vcodec(std::string vhost);
  920 +
  921 + // hds section
  922 +private:
  923 + /**
  924 + * get the hds directive of vhost.
  925 + */
  926 + virtual SrsConfDirective* get_hds(const std::string &vhost);
  927 +public:
  928 + /**
  929 + * whether HDS is enabled.
  930 + */
  931 + virtual bool get_hds_enabled(const std::string &vhost);
  932 + /**
  933 + * get the HDS file store path.
  934 + */
  935 + virtual std::string get_hds_path(const std::string &vhost);
  936 + /**
  937 + * get the hds fragment time, in seconds.
  938 + */
  939 + virtual double get_hds_fragment(const std::string &vhost);
  940 + /**
  941 + * get the hds window time, in seconds.
  942 + * a window is a set of hds fragments.
  943 + */
  944 + virtual double get_hds_window(const std::string &vhost);
  945 +
915 // dvr section 946 // dvr section
916 private: 947 private:
917 /** 948 /**
  1 +/*
  2 +The MIT License (MIT)
  3 +
  4 +Copyright (c) 2013-2015 wenjiegit
  5 +Copyright (c) 2013-2015 winlin
  6 +
  7 +Permission is hereby granted, free of charge, to any person obtaining a copy of
  8 +this software and associated documentation files (the "Software"), to deal in
  9 +the Software without restriction, including without limitation the rights to
  10 +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  11 +the Software, and to permit persons to whom the Software is furnished to do so,
  12 +subject to the following conditions:
  13 +
  14 +The above copyright notice and this permission notice shall be included in all
  15 +copies or substantial portions of the Software.
  16 +
  17 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  19 +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  20 +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  21 +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  22 +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  23 +*/
  24 +
  25 +#include <string>
  26 +#include <vector>
  27 +#include <sys/types.h>
  28 +#include <sys/stat.h>
  29 +#include <fcntl.h>
  30 +
  31 +#include <srs_app_hds.hpp>
  32 +#include <srs_rtmp_sdk.hpp>
  33 +#include <srs_kernel_log.hpp>
  34 +#include <srs_kernel_codec.hpp>
  35 +#include <srs_rtmp_stack.hpp>
  36 +#include <srs_kernel_stream.hpp>
  37 +#include <srs_core_autofree.hpp>
  38 +#include <srs_kernel_utility.hpp>
  39 +#include <srs_app_config.hpp>
  40 +
  41 +using namespace std;
  42 +
  43 +static void update_box(char *start, int size)
  44 +{
  45 + char *p_size = (char*)&size;
  46 + start[0] = p_size[3];
  47 + start[1] = p_size[2];
  48 + start[2] = p_size[1];
  49 + start[3] = p_size[0];
  50 +}
  51 +
  52 +char flv_header[] = {'F', 'L', 'V',
  53 + 0x01, 0x05, 0x00, 0x00, 0x00, 0x09,
  54 + 0x00, 0x00, 0x00, 0x00};
  55 +
  56 +string serialFlv(SrsSharedPtrMessage *msg)
  57 +{
  58 + SrsStream *stream = new SrsStream;
  59 +
  60 + int size = 15 + msg->size;
  61 + char *byte = new char[size];
  62 + stream->initialize(byte, size);
  63 +
  64 + // tag header
  65 + long long dts = msg->timestamp;
  66 + char type = msg->is_video() ? 0x09 : 0x08;
  67 +
  68 + stream->write_1bytes(type);
  69 + stream->write_3bytes(msg->size);
  70 + stream->write_3bytes(dts);
  71 + stream->write_1bytes(dts >> 24 & 0xFF);
  72 + stream->write_3bytes(0);
  73 + stream->write_bytes(msg->payload, msg->size);
  74 +
  75 + // pre tag size
  76 + int preTagSize = msg->size + 11;
  77 + stream->write_4bytes(preTagSize);
  78 +
  79 + string ret(stream->data(), stream->size());
  80 +
  81 + delete stream;
  82 + delete [] byte;
  83 +
  84 + return ret;
  85 +}
  86 +
  87 +class SrsHdsFragment
  88 +{
  89 +public:
  90 + SrsHdsFragment(SrsRequest *r)
  91 + : req(r)
  92 + , index(-1)
  93 + , start_time(0)
  94 + , videoSh(NULL)
  95 + , audioSh(NULL)
  96 + {
  97 +
  98 + }
  99 +
  100 + ~SrsHdsFragment()
  101 + {
  102 + srs_freep(videoSh);
  103 + srs_freep(audioSh);
  104 +
  105 + // clean msgs
  106 + list<SrsSharedPtrMessage *>::iterator iter;
  107 + for (iter = msgs.begin(); iter != msgs.end(); ++iter) {
  108 + SrsSharedPtrMessage *msg = *iter;
  109 + srs_freep(msg);
  110 + }
  111 + }
  112 +
  113 + void on_video(SrsSharedPtrMessage *msg)
  114 + {
  115 + SrsSharedPtrMessage *_msg = msg->copy();
  116 + msgs.push_back(_msg);
  117 + }
  118 +
  119 + void on_audio(SrsSharedPtrMessage *msg)
  120 + {
  121 + SrsSharedPtrMessage *_msg = msg->copy();
  122 + msgs.push_back(_msg);
  123 + }
  124 +
  125 + /*!
  126 + flush data to disk.
  127 + */
  128 + int flush()
  129 + {
  130 + string data;
  131 + if (videoSh) {
  132 + videoSh->timestamp = start_time;
  133 + data.append(serialFlv(videoSh));
  134 + }
  135 +
  136 + if (audioSh) {
  137 + audioSh->timestamp = start_time;
  138 + data.append(serialFlv(audioSh));
  139 + }
  140 +
  141 + list<SrsSharedPtrMessage *>::iterator iter;
  142 + for (iter = msgs.begin(); iter != msgs.end(); ++iter) {
  143 + SrsSharedPtrMessage *msg = *iter;
  144 + data.append(serialFlv(msg));
  145 + }
  146 +
  147 + static char box_header[8];
  148 + SrsStream ss;
  149 + ss.initialize(box_header, 8);
  150 + ss.write_4bytes(8 + data.size());
  151 + ss.write_string("mdat");
  152 +
  153 + data = string(ss.data(), ss.size()) + data;
  154 +
  155 + char file_path[1024] = {0};
  156 + sprintf(file_path, "%s/%s/%sSeg1-Frag%d", _srs_config->get_hds_path(req->vhost).c_str()
  157 + , req->app.c_str(), req->stream.c_str(), index);
  158 +
  159 + int fd = open(file_path, O_WRONLY | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH);
  160 + if (fd < 0) {
  161 + srs_error("open fragment file failed, path=%s", file_path);
  162 + return -1;
  163 + }
  164 +
  165 + if (write(fd, data.data(), data.size()) != (int)data.size()) {
  166 + srs_error("write fragment file failed, path=", file_path);
  167 + close(fd);
  168 + return -1;
  169 + }
  170 + close(fd);
  171 +
  172 + srs_trace("build fragment success=%s", file_path);
  173 +
  174 + return ERROR_SUCCESS;
  175 + }
  176 +
  177 + /*!
  178 + calc the segment duration in milliseconds.
  179 + @return 0 if no msgs
  180 + or the last msg dts minus the first msg dts.
  181 + */
  182 + int duration()
  183 + {
  184 + int duration_ms = 0;
  185 + long long first_msg_ts = 0;
  186 + long long last_msg_ts = 0;
  187 +
  188 + if (msgs.size() >= 2) {
  189 + SrsSharedPtrMessage *first_msg = msgs.front();
  190 + first_msg_ts = first_msg->timestamp;
  191 +
  192 + SrsSharedPtrMessage *last_msg = msgs.back();
  193 + last_msg_ts = last_msg->timestamp;
  194 +
  195 + duration_ms = last_msg_ts - first_msg_ts;
  196 + }
  197 +
  198 + return duration_ms;
  199 + }
  200 +
  201 + /*!
  202 + set/get index
  203 + */
  204 + inline void set_index(int idx)
  205 + {
  206 + char fg_name[1024] = {0};
  207 + sprintf(fg_name, "/var/www/live/stream0Seg1-Frag%d", idx);
  208 + path = fg_name;
  209 + index = idx;
  210 + }
  211 +
  212 + inline int get_index()
  213 + {
  214 + return index;
  215 + }
  216 +
  217 + /*!
  218 + set/get start time
  219 + */
  220 + inline void set_start_time(long long st)
  221 + {
  222 + start_time = st;
  223 + }
  224 +
  225 + inline long long get_start_time()
  226 + {
  227 + return start_time;
  228 + }
  229 +
  230 + void set_video_sh(SrsSharedPtrMessage *msg)
  231 + {
  232 + srs_freep(videoSh);
  233 + videoSh = msg->copy();
  234 + }
  235 +
  236 + void set_audio_sh(SrsSharedPtrMessage *msg)
  237 + {
  238 + srs_freep(audioSh);
  239 + audioSh = msg->copy();
  240 + }
  241 +
  242 + string fragment_path()
  243 + {
  244 + return path;
  245 + }
  246 +
  247 +private:
  248 + SrsRequest *req;
  249 + list<SrsSharedPtrMessage *> msgs;
  250 +
  251 + /*!
  252 + the index of this fragment
  253 + */
  254 + int index;
  255 + long long start_time;
  256 +
  257 + SrsSharedPtrMessage *videoSh;
  258 + SrsSharedPtrMessage *audioSh;
  259 + string path;
  260 +};
  261 +
  262 +SrsHds::SrsHds(SrsSource *s)
  263 + : currentSegment(NULL)
  264 + , source(s)
  265 + , fragment_index(1)
  266 + , video_sh(NULL)
  267 + , audio_sh(NULL)
  268 + , hds_req(NULL)
  269 +{
  270 +
  271 +}
  272 +
  273 +SrsHds::~SrsHds()
  274 +{
  275 +
  276 +}
  277 +
  278 +int SrsHds::on_publish(SrsRequest *req)
  279 +{
  280 + hds_req = req->copy();
  281 +
  282 + return flush_mainfest();
  283 +}
  284 +
  285 +int SrsHds::on_unpublish()
  286 +{
  287 + int ret = ERROR_SUCCESS;
  288 +
  289 + srs_freep(video_sh);
  290 + srs_freep(audio_sh);
  291 + srs_freep(hds_req);
  292 +
  293 + // clean fragments
  294 + list<SrsHdsFragment *>::iterator iter;
  295 + for (iter = fragments.begin(); iter != fragments.end(); ++iter) {
  296 + SrsHdsFragment *st = *iter;
  297 + srs_freep(st);
  298 + }
  299 + fragments.clear();
  300 +
  301 + srs_freep(currentSegment);
  302 +
  303 + srs_trace("HDS un-published");
  304 +
  305 + return ret;
  306 +}
  307 +
  308 +int SrsHds::on_video(SrsSharedPtrMessage* msg)
  309 +{
  310 + int ret = ERROR_SUCCESS;
  311 +
  312 + if (SrsFlvCodec::video_is_sequence_header(msg->payload, msg->size)) {
  313 + srs_freep(video_sh);
  314 + video_sh = msg->copy();
  315 + }
  316 +
  317 + if (!currentSegment) {
  318 + currentSegment = new SrsHdsFragment(hds_req);
  319 + currentSegment->set_index(fragment_index++);
  320 + currentSegment->set_start_time(msg->timestamp);
  321 +
  322 + if (video_sh)
  323 + currentSegment->set_video_sh(video_sh);
  324 +
  325 + if (audio_sh)
  326 + currentSegment->set_audio_sh(audio_sh);
  327 + }
  328 +
  329 + currentSegment->on_video(msg);
  330 +
  331 + double fragment_duration = _srs_config->get_hds_fragment(hds_req->vhost) * 1000;
  332 + if (currentSegment->duration() >= fragment_duration) {
  333 + // flush segment
  334 + if ((ret = currentSegment->flush()) != ERROR_SUCCESS) {
  335 + srs_error("flush segment failed.");
  336 + return ret;
  337 + }
  338 +
  339 + srs_trace("flush Segment success.");
  340 + fragments.push_back(currentSegment);
  341 + currentSegment = NULL;
  342 + adjust_windows();
  343 +
  344 + // flush bootstrap
  345 + if ((ret = flush_bootstrap()) != ERROR_SUCCESS) {
  346 + srs_error("flush bootstrap failed.");
  347 + return ret;
  348 + }
  349 +
  350 + srs_trace("flush BootStrap success.");
  351 + }
  352 +
  353 + return ret;
  354 +}
  355 +
  356 +int SrsHds::on_audio(SrsSharedPtrMessage* msg)
  357 +{
  358 + int ret = ERROR_SUCCESS;
  359 +
  360 + if (SrsFlvCodec::audio_is_sequence_header(msg->payload, msg->size)) {
  361 + srs_freep(audio_sh);
  362 + audio_sh = msg->copy();
  363 + }
  364 +
  365 + if (!currentSegment) {
  366 + currentSegment = new SrsHdsFragment(hds_req);
  367 + currentSegment->set_index(fragment_index++);
  368 + currentSegment->set_start_time(msg->timestamp);
  369 +
  370 + if (video_sh)
  371 + currentSegment->set_video_sh(video_sh);
  372 +
  373 + if (audio_sh)
  374 + currentSegment->set_audio_sh(audio_sh);
  375 + }
  376 +
  377 + currentSegment->on_audio(msg);
  378 +
  379 + double fragment_duration = _srs_config->get_hds_fragment(hds_req->vhost) * 1000;
  380 + if (currentSegment->duration() >= fragment_duration) {
  381 + // flush segment
  382 + if ((ret = currentSegment->flush()) != ERROR_SUCCESS) {
  383 + srs_error("flush segment failed.");
  384 + return ret;
  385 + }
  386 +
  387 + srs_info("flush Segment success.");
  388 +
  389 + // reset the current segment
  390 + fragments.push_back(currentSegment);
  391 + currentSegment = NULL;
  392 + adjust_windows();
  393 +
  394 + // flush bootstrap
  395 + if ((ret = flush_bootstrap()) != ERROR_SUCCESS) {
  396 + srs_error("flush bootstrap failed.");
  397 + return ret;
  398 + }
  399 +
  400 + srs_info("flush BootStrap success.");
  401 + }
  402 +
  403 + return ret;
  404 +}
  405 +
  406 +int SrsHds::flush_mainfest()
  407 +{
  408 + int ret = ERROR_SUCCESS;
  409 +
  410 + char buf[1024] = {0};
  411 + sprintf(buf, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
  412 + "<manifest xmlns=\"http://ns.adobe.com/f4m/1.0\">\n\t"
  413 + "<id>%s.f4m</id>\n\t"
  414 + "<streamType>live</streamType>\n\t"
  415 + "<deliveryType>streaming</deliveryType>\n\t"
  416 + "<bootstrapInfo profile=\"named\" url=\"%s.abst\" id=\"bootstrap0\" />\n\t"
  417 + "<media bitrate=\"0\" url=\"%s\" bootstrapInfoId=\"bootstrap0\"></media>\n"
  418 + "</manifest>"
  419 + , hds_req->stream.c_str(), hds_req->stream.c_str(), hds_req->stream.c_str());
  420 +
  421 + string dir = _srs_config->get_hds_path(hds_req->vhost) + "/" + hds_req->app;
  422 + if ((ret = srs_create_dir_recursively(dir)) != ERROR_SUCCESS) {
  423 + srs_error("hds create dir failed. ret=%d", ret);
  424 + return ret;
  425 + }
  426 + string path = dir + "/" + hds_req->stream + ".f4m";
  427 +
  428 + int fd = open(path.c_str(), O_WRONLY | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH);
  429 + if (fd < 0) {
  430 + srs_error("open manifest file failed, path=%s", path.c_str());
  431 + ret = ERROR_HDS_OPEN_F4M_FAILED;
  432 + return ret;
  433 + }
  434 +
  435 + int f4m_size = strlen(buf);
  436 + if (write(fd, buf, f4m_size) != f4m_size) {
  437 + srs_error("write manifest file failed, path=", path.c_str());
  438 + close(fd);
  439 + ret = ERROR_HDS_WRITE_F4M_FAILED;
  440 + return ret;
  441 + }
  442 + close(fd);
  443 +
  444 + srs_trace("build manifest success=%s", path.c_str());
  445 +
  446 + return ERROR_SUCCESS;
  447 +}
  448 +
  449 +int SrsHds::flush_bootstrap()
  450 +{
  451 + int ret = ERROR_SUCCESS;
  452 +
  453 + SrsStream abst;
  454 +
  455 + int size = 1024*100;
  456 +
  457 + char *start_abst = new char[1024*100];
  458 + SrsAutoFree(char, start_abst);
  459 +
  460 + int size_abst = 0;
  461 + char *start_asrt = NULL;
  462 + int size_asrt = 0;
  463 + char *start_afrt = NULL;
  464 + int size_afrt = 0;
  465 +
  466 + abst.initialize(start_abst, size);
  467 +
  468 + // @see video_file_format_spec_v10_1
  469 + // page: 46
  470 + abst.write_4bytes(0);
  471 + abst.write_string("abst");
  472 + abst.write_1bytes(0x00); // Either 0 or 1
  473 + abst.write_3bytes(0x00); // Flags always 0
  474 + size_abst += 12;
  475 + /*!
  476 + @BootstrapinfoVersion UI32
  477 + The version number of the bootstrap information.
  478 + When the Update field is set, BootstrapinfoVersion
  479 + indicates the version number that is being updated.
  480 + we assume this is the last.
  481 + */
  482 + abst.write_4bytes(fragment_index - 1); // BootstrapinfoVersion
  483 +
  484 + abst.write_1bytes(0x20); // profile, live, update
  485 + abst.write_4bytes(1000); // TimeScale Typically, the value is 1000, for a unit of milliseconds
  486 + size_abst += 9;
  487 + /*!
  488 + The timestamp in TimeScale units of the latest available Fragment in the media presentation.
  489 + This timestamp is used to request the right fragment number.
  490 + The CurrentMedia Time can be the total duration.
  491 + For media presentations that are not live, CurrentMediaTime can be 0.
  492 + */
  493 + SrsHdsFragment *st = fragments.back();
  494 + abst.write_8bytes(st->get_start_time());
  495 +
  496 + // SmpteTimeCodeOffset
  497 + abst.write_8bytes(0);
  498 + size_abst += 16;
  499 +
  500 + /*!
  501 + @MovieIdentifier STRING
  502 + The identifier of this presentation.
  503 + we write null string.
  504 + */
  505 + abst.write_1bytes(0);
  506 + size_abst += 1;
  507 + /*!
  508 + @ServerEntryCount UI8
  509 + The number of ServerEntryTable entries.
  510 + The minimum value is 0.
  511 + */
  512 + abst.write_1bytes(0);
  513 + size_abst += 1;
  514 + /*!
  515 + @ServerEntryTable
  516 + because we write 0 of ServerEntryCount, so this feild is ignored.
  517 + */
  518 +
  519 + /*!
  520 + @QualityEntryCount UI8
  521 + The number of QualityEntryTable entries, which is
  522 + also the number of available quality levels. The
  523 + minimum value is 0. Available quality levels are for,
  524 + for example, multi bit rate files or trick files.
  525 + */
  526 + abst.write_1bytes(0);
  527 + size_abst += 1;
  528 + /*!
  529 + @QualityEntryTable
  530 + because we write 0 of QualityEntryCount, so this feild is ignored.
  531 + */
  532 +
  533 + /*!
  534 + @DrmData STRING
  535 + Null or null-terminated UTF-8 string. This string
  536 + holds Digital Rights Management metadata.
  537 + Encrypted files use this metadata to get the
  538 + necessary keys and licenses for decryption and play back.
  539 + we write null string.
  540 + */
  541 + abst.write_1bytes(0);
  542 + size_abst += 1;
  543 + /*!
  544 + @MetaData STRING
  545 + Null or null-terminated UTF - 8 string that holds metadata.
  546 + we write null string.
  547 + */
  548 + abst.write_1bytes(0);
  549 + size_abst += 1;
  550 + /*!
  551 + @SegmentRunTableCount UI8
  552 + The number of entries in SegmentRunTableEntries.
  553 + The minimum value is 1. Typically, one table
  554 + contains all segment runs. However, this count
  555 + provides the flexibility to define the segment runs
  556 + individually for each quality level (or trick file).
  557 + */
  558 + abst.write_1bytes(1);
  559 + size_abst += 1;
  560 +
  561 + ///////////////////////////////////////////////////
  562 + start_asrt = start_abst + size_abst;
  563 + // MStream asrt;
  564 + // follows by asrt
  565 + abst.write_4bytes(0);
  566 + abst.write_string("asrt");
  567 + size_asrt += 8;
  568 + /*!
  569 + @Version UI8
  570 + @Flags UI24
  571 + */
  572 + abst.write_4bytes(0);
  573 + size_asrt += 4;
  574 + /*!
  575 + @QualityEntryCount UI8
  576 + The number of QualitySegmen tUrlModifiers
  577 + (quality level references) that follow. If 0, this
  578 + Segment Run Table applies to all quality levels,
  579 + and there shall be only one Segment Run Table
  580 + box in the Bootstrap Info box.
  581 + */
  582 + abst.write_1bytes(0);
  583 + size_asrt += 1;
  584 +
  585 + /*!
  586 + @QualitySegmentUrlModifiers
  587 + ignored.
  588 + */
  589 +
  590 + /*!
  591 + @SegmentRunEntryCount
  592 + The number of items in this
  593 + SegmentRunEn tryTable. The minimum value is 1.
  594 + */
  595 + abst.write_4bytes(1);
  596 + size_asrt += 4;
  597 + /*!
  598 + @SegmentRunEntryTable
  599 + */
  600 + for (int i = 0; i < 1; ++i) {
  601 + /*!
  602 + @FirstSegment UI32
  603 + The identifying number of the first segment in the run of
  604 + segments containing the same number of fragments.
  605 + The segment corresponding to the FirstSegment in the next
  606 + SEGMENTRUNENTRY will terminate this run.
  607 + */
  608 + abst.write_4bytes(1);
  609 +
  610 + /*!
  611 + @FragmentsPerSegment UI32
  612 + The number of fragments in each segment in this run.
  613 + */
  614 + abst.write_4bytes(fragment_index - 1);
  615 + size_asrt += 8;
  616 + }
  617 +
  618 + update_box(start_asrt, size_asrt);
  619 + size_abst += size_asrt;
  620 +
  621 + /*!
  622 + @FragmentRunTableCount UI8
  623 + The number of entries in FragmentRunTable-Entries.
  624 + The min i mum value is 1.
  625 + */
  626 + abst.write_1bytes(1);
  627 + size_abst += 1;
  628 +
  629 + /////////////////////////////////////
  630 + //MStream afrt;
  631 + // follows by afrt
  632 + start_afrt = start_abst + size_abst;
  633 +
  634 + abst.write_4bytes(0);
  635 + abst.write_string("afrt");
  636 + size_afrt += 8;
  637 +
  638 + /*!
  639 + @Version UI8
  640 + @Flags UI24
  641 + */
  642 + abst.write_4bytes(0);
  643 + size_afrt += 4;
  644 + /*!
  645 + @TimeScale UI32
  646 + The number of time units per second, used in the FirstFragmentTime stamp and
  647 + Fragment Duration fields.
  648 + Typically, the value is 1000.
  649 + */
  650 + abst.write_4bytes(1000);
  651 + size_afrt += 4;
  652 + /*!
  653 + @QualityEntryCount UI8
  654 + The number of QualitySegment Url Modifiers
  655 + (quality level references) that follow.
  656 + If 0, this Fragment Run Table applies to all quality levels,
  657 + and there shall be only one Fragment Run Table
  658 + box in the Bootstrap Info box.
  659 + */
  660 + abst.write_1bytes(0);
  661 + size_afrt += 1;
  662 +
  663 + /*!
  664 + @FragmentRunEntryCount UI32
  665 + The number of items in this FragmentRunEntryTable.
  666 + The minimum value is 1.
  667 + */
  668 + abst.write_4bytes(fragments.size());
  669 + size_afrt += 4;
  670 +
  671 + list<SrsHdsFragment *>::iterator iter;
  672 + for (iter = fragments.begin(); iter != fragments.end(); ++iter) {
  673 + SrsHdsFragment *st = *iter;
  674 + abst.write_4bytes(st->get_index());
  675 + abst.write_8bytes(st->get_start_time());
  676 + abst.write_4bytes(st->duration());
  677 + size_afrt += 16;
  678 + }
  679 +
  680 + update_box(start_afrt, size_afrt);
  681 +// abst.append(afrt);
  682 +
  683 + size_abst += size_afrt;
  684 + update_box(start_abst, size_abst);
  685 +
  686 + string path = _srs_config->get_hds_path(hds_req->vhost) + "/" + hds_req->app + "/" + hds_req->stream +".abst";
  687 +
  688 + int fd = open(path.c_str(), O_WRONLY | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH);
  689 + if (fd < 0) {
  690 + srs_error("open bootstrap file failed, path=%s", path.c_str());
  691 + ret = ERROR_HDS_OPEN_BOOTSTRAP_FAILED;
  692 + return ret;
  693 + }
  694 +
  695 + if (write(fd, start_abst, size_abst) != size_abst) {
  696 + srs_error("write bootstrap file failed, path=", path.c_str());
  697 + close(fd);
  698 + ret = ERROR_HDS_WRITE_BOOTSTRAP_FAILED;
  699 + return ret;
  700 + }
  701 + close(fd);
  702 +
  703 + srs_trace("build bootstrap success=%s", path.c_str());
  704 +
  705 + return ERROR_SUCCESS;
  706 +}
  707 +
  708 +void SrsHds::adjust_windows()
  709 +{
  710 + int windows_size = 0;
  711 + list<SrsHdsFragment *>::iterator iter;
  712 + for (iter = fragments.begin(); iter != fragments.end(); ++iter) {
  713 + SrsHdsFragment *fragment = *iter;
  714 + windows_size += fragment->duration();
  715 + }
  716 +
  717 + double windows_size_limit = _srs_config->get_hds_window(hds_req->vhost) * 1000;
  718 + if (windows_size > windows_size_limit ) {
  719 + SrsHdsFragment *fragment = fragments.front();
  720 + unlink(fragment->fragment_path().c_str());
  721 + fragments.erase(fragments.begin());
  722 + srs_freep(fragment);
  723 + }
  724 +}
  1 +/*
  2 +The MIT License (MIT)
  3 +
  4 +Copyright (c) 2013-2015 wenjiegit
  5 +Copyright (c) 2013-2015 winlin
  6 +
  7 +Permission is hereby granted, free of charge, to any person obtaining a copy of
  8 +this software and associated documentation files (the "Software"), to deal in
  9 +the Software without restriction, including without limitation the rights to
  10 +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  11 +the Software, and to permit persons to whom the Software is furnished to do so,
  12 +subject to the following conditions:
  13 +
  14 +The above copyright notice and this permission notice shall be included in all
  15 +copies or substantial portions of the Software.
  16 +
  17 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  19 +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  20 +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  21 +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  22 +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  23 +*/
  24 +
  25 +#ifndef SRS_APP_HDS_HPP
  26 +#define SRS_APP_HDS_HPP
  27 +
  28 +#include <list>
  29 +
  30 +class SrsRequest;
  31 +class SrsSharedPtrMessage;
  32 +class SrsHdsFragment;
  33 +class SrsSource;
  34 +
  35 +using namespace std;
  36 +
  37 +class SrsHds
  38 +{
  39 +public:
  40 + SrsHds(SrsSource* s);
  41 + ~SrsHds();
  42 +
  43 + int on_publish(SrsRequest* req);
  44 + int on_unpublish();
  45 +
  46 + int on_video(SrsSharedPtrMessage* msg);
  47 + int on_audio(SrsSharedPtrMessage* msg);
  48 +
  49 +private:
  50 + int flush_mainfest();
  51 + int flush_bootstrap();
  52 + void adjust_windows();
  53 +
  54 +private:
  55 + list<SrsHdsFragment *> fragments;
  56 + SrsHdsFragment *currentSegment;
  57 + SrsSource *source;
  58 + int fragment_index;
  59 + SrsSharedPtrMessage *video_sh;
  60 + SrsSharedPtrMessage *audio_sh;
  61 +
  62 + SrsRequest *hds_req;
  63 +};
  64 +
  65 +#endif // SRS_APP_HDS_HPP
@@ -42,6 +42,7 @@ using namespace std; @@ -42,6 +42,7 @@ using namespace std;
42 #include <srs_kernel_utility.hpp> 42 #include <srs_kernel_utility.hpp>
43 #include <srs_kernel_codec.hpp> 43 #include <srs_kernel_codec.hpp>
44 #include <srs_rtmp_msg_array.hpp> 44 #include <srs_rtmp_msg_array.hpp>
  45 +#include <srs_app_hds.hpp>
45 46
46 #define CONST_MAX_JITTER_MS 500 47 #define CONST_MAX_JITTER_MS 500
47 #define DEFAULT_FRAME_TIME_MS 40 48 #define DEFAULT_FRAME_TIME_MS 40
@@ -770,6 +771,8 @@ SrsSource::SrsSource(ISrsHlsHandler* hh) @@ -770,6 +771,8 @@ SrsSource::SrsSource(ISrsHlsHandler* hh)
770 encoder = new SrsEncoder(); 771 encoder = new SrsEncoder();
771 #endif 772 #endif
772 773
  774 + hds = new SrsHds(this);
  775 +
773 cache_metadata = cache_sh_video = cache_sh_audio = NULL; 776 cache_metadata = cache_sh_video = cache_sh_audio = NULL;
774 777
775 frame_rate = sample_rate = 0; 778 frame_rate = sample_rate = 0;
@@ -1324,6 +1327,15 @@ int SrsSource::on_audio(SrsCommonMessage* __audio) @@ -1324,6 +1327,15 @@ int SrsSource::on_audio(SrsCommonMessage* __audio)
1324 } 1327 }
1325 #endif 1328 #endif
1326 1329
  1330 + if ((ret = hds->on_audio(&msg)) != ERROR_SUCCESS) {
  1331 + // unpublish, ignore ret.
  1332 + hds->on_unpublish();
  1333 + // ignore.
  1334 + ret = ERROR_SUCCESS;
  1335 +
  1336 + srs_warn("hds process audio message failed, ignore and disable dvr. ret=%d", ret);
  1337 + }
  1338 +
1327 // copy to all consumer 1339 // copy to all consumer
1328 int nb_consumers = (int)consumers.size(); 1340 int nb_consumers = (int)consumers.size();
1329 if (nb_consumers > 0) { 1341 if (nb_consumers > 0) {
@@ -1456,6 +1468,15 @@ int SrsSource::on_video(SrsCommonMessage* __video) @@ -1456,6 +1468,15 @@ int SrsSource::on_video(SrsCommonMessage* __video)
1456 } 1468 }
1457 #endif 1469 #endif
1458 1470
  1471 + if ((ret = hds->on_video(&msg)) != ERROR_SUCCESS) {
  1472 + // unpublish, ignore ret.
  1473 + hds->on_unpublish();
  1474 + // ignore.
  1475 + ret = ERROR_SUCCESS;
  1476 +
  1477 + srs_warn("hds process video message failed, ignore and disable dvr. ret=%d", ret);
  1478 + }
  1479 +
1459 // copy to all consumer 1480 // copy to all consumer
1460 if (true) { 1481 if (true) {
1461 for (int i = 0; i < (int)consumers.size(); i++) { 1482 for (int i = 0; i < (int)consumers.size(); i++) {
@@ -1693,6 +1714,11 @@ int SrsSource::on_publish() @@ -1693,6 +1714,11 @@ int SrsSource::on_publish()
1693 } 1714 }
1694 #endif 1715 #endif
1695 1716
  1717 + if ((ret = hds->on_publish(_req)) != ERROR_SUCCESS) {
  1718 + srs_error("start hds failed. ret=%d", ret);
  1719 + return ret;
  1720 + }
  1721 +
1696 // notify the handler. 1722 // notify the handler.
1697 srs_assert(handler); 1723 srs_assert(handler);
1698 if ((ret = handler->on_publish(this, _req)) != ERROR_SUCCESS) { 1724 if ((ret = handler->on_publish(this, _req)) != ERROR_SUCCESS) {
@@ -1720,6 +1746,8 @@ void SrsSource::on_unpublish() @@ -1720,6 +1746,8 @@ void SrsSource::on_unpublish()
1720 dvr->on_unpublish(); 1746 dvr->on_unpublish();
1721 #endif 1747 #endif
1722 1748
  1749 + hds->on_unpublish();
  1750 +
1723 gop_cache->clear(); 1751 gop_cache->clear();
1724 1752
1725 srs_freep(cache_metadata); 1753 srs_freep(cache_metadata);
@@ -62,6 +62,7 @@ class SrsEncoder; @@ -62,6 +62,7 @@ class SrsEncoder;
62 #endif 62 #endif
63 class SrsStream; 63 class SrsStream;
64 class ISrsHlsHandler; 64 class ISrsHlsHandler;
  65 +class SrsHds;
65 66
66 /** 67 /**
67 * the time jitter algorithm: 68 * the time jitter algorithm:
@@ -411,6 +412,7 @@ private: @@ -411,6 +412,7 @@ private:
411 #ifdef SRS_AUTO_TRANSCODE 412 #ifdef SRS_AUTO_TRANSCODE
412 SrsEncoder* encoder; 413 SrsEncoder* encoder;
413 #endif 414 #endif
  415 + SrsHds *hds;
414 // edge control service 416 // edge control service
415 SrsPlayEdge* play_edge; 417 SrsPlayEdge* play_edge;
416 SrsPublishEdge* publish_edge; 418 SrsPublishEdge* publish_edge;
@@ -213,6 +213,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -213,6 +213,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
213 #define ERROR_HTTP_DVR_REQUEST 3051 213 #define ERROR_HTTP_DVR_REQUEST 3051
214 #define ERROR_HTTP_JSON_REQUIRED 3052 214 #define ERROR_HTTP_JSON_REQUIRED 3052
215 #define ERROR_HTTP_DVR_CREATE_REQUEST 3053 215 #define ERROR_HTTP_DVR_CREATE_REQUEST 3053
  216 +// HDS error code
  217 +#define ERROR_HDS_OPEN_F4M_FAILED 3054
  218 +#define ERROR_HDS_WRITE_F4M_FAILED 3055
  219 +#define ERROR_HDS_OPEN_BOOTSTRAP_FAILED 3056
  220 +#define ERROR_HDS_WRITE_BOOTSTRAP_FAILED 3057
  221 +#define ERROR_HDS_OPEN_FRAGMENT_FAILED 3058
  222 +#define ERROR_HDS_WRITE_FRAGMENT_FAILED 3059
216 223
217 /////////////////////////////////////////////////////// 224 ///////////////////////////////////////////////////////
218 // HTTP/StreamCaster protocol error. 225 // HTTP/StreamCaster protocol error.