From d4198ee8ba1ca8178d577adf931e9655ba961247 Mon Sep 17 00:00:00 2001 From: "wenjie.zhao" Date: Wed, 11 Mar 2015 13:34:58 +0800 Subject: [PATCH 1/3] add hds supported. --- trunk/configure | 2 +- trunk/src/app/srs_app_config.cpp | 80 +++ trunk/src/app/srs_app_config.hpp | 33 +- trunk/src/app/srs_app_hds.cpp | 724 ++++++++++++++++++++++++++ trunk/src/app/srs_app_hds.hpp | 65 +++ trunk/src/app/srs_app_source.cpp | 28 + trunk/src/app/srs_app_source.hpp | 2 + trunk/src/kernel/srs_kernel_error.hpp | 7 + 8 files changed, 939 insertions(+), 2 deletions(-) create mode 100644 trunk/src/app/srs_app_hds.cpp create mode 100644 trunk/src/app/srs_app_hds.hpp diff --git a/trunk/configure b/trunk/configure index 44bb1916a1..6362898723 100755 --- a/trunk/configure +++ b/trunk/configure @@ -392,7 +392,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then "srs_app_json" "srs_app_ingest" "srs_app_ffmpeg" "srs_app_utility" "srs_app_dvr" "srs_app_edge" "srs_app_kbps" "srs_app_heartbeat" "srs_app_empty" "srs_app_http_client" "srs_app_recv_thread" "srs_app_security" "srs_app_statistic" - "srs_app_mpegts_udp" "srs_app_rtsp" "srs_app_listener") + "srs_app_mpegts_udp" "srs_app_rtsp" "srs_app_listener" "srs_app_hds") APP_INCS="src/app"; MODULE_DIR=${APP_INCS} . auto/modules.sh APP_OBJS="${MODULE_OBJS[@]}" fi diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index b0f8219c1c..d748b864ee 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -1407,6 +1407,7 @@ int SrsConfig::check_config() && n != "mr" && n != "mw_latency" && n != "min_latency" && n != "security" && n != "http_remux" && n != "http" && n != "http_static" + && n != "hds" ) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("unsupported vhost directive %s, ret=%d", n.c_str(), ret); @@ -3310,6 +3311,85 @@ string SrsConfig::get_hls_vcodec(string vhost) return conf->arg0(); } +SrsConfDirective *SrsConfig::get_hds(const string &vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("hds"); +} + +bool SrsConfig::get_hds_enabled(const string &vhost) +{ + SrsConfDirective* hds = get_hds(vhost); + + if (!hds) { + return false; + } + + SrsConfDirective* conf = hds->get("enabled"); + + if (!conf) { + return false; + } + + return conf->arg0() == "on"; +} + +string SrsConfig::get_hds_path(const string &vhost) +{ + SrsConfDirective* hds = get_hds(vhost); + + if (!hds) { + return SRS_CONF_DEFAULT_HDS_PATH; + } + + SrsConfDirective* conf = hds->get("hds_path"); + + if (!conf) { + return SRS_CONF_DEFAULT_HDS_PATH; + } + + return conf->arg0(); +} + +double SrsConfig::get_hds_fragment(const string &vhost) +{ + SrsConfDirective* hds = get_hds(vhost); + + if (!hds) { + return SRS_CONF_DEFAULT_HDS_FRAGMENT; + } + + SrsConfDirective* conf = hds->get("hds_fragment"); + + if (!conf) { + return SRS_CONF_DEFAULT_HDS_FRAGMENT; + } + + return ::atof(conf->arg0().c_str()); +} + +double SrsConfig::get_hds_window(const string &vhost) +{ + SrsConfDirective* hds = get_hds(vhost); + + if (!hds) { + return SRS_CONF_DEFAULT_HDS_WINDOW; + } + + SrsConfDirective* conf = hds->get("hds_window"); + + if (!conf) { + return SRS_CONF_DEFAULT_HDS_WINDOW; + } + + return ::atof(conf->arg0().c_str()); +} + SrsConfDirective* SrsConfig::get_dvr(string vhost) { SrsConfDirective* conf = get_vhost(vhost); diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index e2e595ef49..59fe0eda73 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -102,10 +102,15 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define SRS_CONF_DEFAULT_TRANSCODE_IFORMAT "flv" #define SRS_CONF_DEFAULT_TRANSCODE_OFORMAT "flv" +// hds default value +#define SRS_CONF_DEFAULT_HDS_PATH "./objs/nginx/html" +#define SRS_CONF_DEFAULT_HDS_WINDOW (60) +#define SRS_CONF_DEFAULT_HDS_FRAGMENT (10) + namespace _srs_internal { class SrsConfigBuffer; -}; +} /** * the config directive. @@ -912,6 +917,32 @@ class SrsConfig * get the HLS default video codec. */ virtual std::string get_hls_vcodec(std::string vhost); + + // hds section +private: + /** + * get the hds directive of vhost. + */ + virtual SrsConfDirective* get_hds(const std::string &vhost); +public: + /** + * whether HDS is enabled. + */ + virtual bool get_hds_enabled(const std::string &vhost); + /** + * get the HDS file store path. + */ + virtual std::string get_hds_path(const std::string &vhost); + /** + * get the hds fragment time, in seconds. + */ + virtual double get_hds_fragment(const std::string &vhost); + /** + * get the hds window time, in seconds. + * a window is a set of hds fragments. + */ + virtual double get_hds_window(const std::string &vhost); + // dvr section private: /** diff --git a/trunk/src/app/srs_app_hds.cpp b/trunk/src/app/srs_app_hds.cpp new file mode 100644 index 0000000000..f41288a555 --- /dev/null +++ b/trunk/src/app/srs_app_hds.cpp @@ -0,0 +1,724 @@ +/* +The MIT License (MIT) + +Copyright (c) 2013-2015 wenjiegit +Copyright (c) 2013-2015 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +static void update_box(char *start, int size) +{ + char *p_size = (char*)&size; + start[0] = p_size[3]; + start[1] = p_size[2]; + start[2] = p_size[1]; + start[3] = p_size[0]; +} + +char flv_header[] = {'F', 'L', 'V', + 0x01, 0x05, 0x00, 0x00, 0x00, 0x09, + 0x00, 0x00, 0x00, 0x00}; + +string serialFlv(SrsSharedPtrMessage *msg) +{ + SrsStream *stream = new SrsStream; + + int size = 15 + msg->size; + char *byte = new char[size]; + stream->initialize(byte, size); + + // tag header + long long dts = msg->timestamp; + char type = msg->is_video() ? 0x09 : 0x08; + + stream->write_1bytes(type); + stream->write_3bytes(msg->size); + stream->write_3bytes(dts); + stream->write_1bytes(dts >> 24 & 0xFF); + stream->write_3bytes(0); + stream->write_bytes(msg->payload, msg->size); + + // pre tag size + int preTagSize = msg->size + 11; + stream->write_4bytes(preTagSize); + + string ret(stream->data(), stream->size()); + + delete stream; + delete [] byte; + + return ret; +} + +class SrsHdsFragment +{ +public: + SrsHdsFragment(SrsRequest *r) + : req(r) + , index(-1) + , start_time(0) + , videoSh(NULL) + , audioSh(NULL) + { + + } + + ~SrsHdsFragment() + { + srs_freep(videoSh); + srs_freep(audioSh); + + // clean msgs + list::iterator iter; + for (iter = msgs.begin(); iter != msgs.end(); ++iter) { + SrsSharedPtrMessage *msg = *iter; + srs_freep(msg); + } + } + + void on_video(SrsSharedPtrMessage *msg) + { + SrsSharedPtrMessage *_msg = msg->copy(); + msgs.push_back(_msg); + } + + void on_audio(SrsSharedPtrMessage *msg) + { + SrsSharedPtrMessage *_msg = msg->copy(); + msgs.push_back(_msg); + } + + /*! + flush data to disk. + */ + int flush() + { + string data; + if (videoSh) { + videoSh->timestamp = start_time; + data.append(serialFlv(videoSh)); + } + + if (audioSh) { + audioSh->timestamp = start_time; + data.append(serialFlv(audioSh)); + } + + list::iterator iter; + for (iter = msgs.begin(); iter != msgs.end(); ++iter) { + SrsSharedPtrMessage *msg = *iter; + data.append(serialFlv(msg)); + } + + static char box_header[8]; + SrsStream ss; + ss.initialize(box_header, 8); + ss.write_4bytes(8 + data.size()); + ss.write_string("mdat"); + + data = string(ss.data(), ss.size()) + data; + + char file_path[1024] = {0}; + sprintf(file_path, "%s/%s/%sSeg1-Frag%d", _srs_config->get_hds_path(req->vhost).c_str() + , req->app.c_str(), req->stream.c_str(), index); + + int fd = open(file_path, O_WRONLY | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH); + if (fd < 0) { + srs_error("open fragment file failed, path=%s", file_path); + return -1; + } + + if (write(fd, data.data(), data.size()) != (int)data.size()) { + srs_error("write fragment file failed, path=", file_path); + close(fd); + return -1; + } + close(fd); + + srs_trace("build fragment success=%s", file_path); + + return ERROR_SUCCESS; + } + + /*! + calc the segment duration in milliseconds. + @return 0 if no msgs + or the last msg dts minus the first msg dts. + */ + int duration() + { + int duration_ms = 0; + long long first_msg_ts = 0; + long long last_msg_ts = 0; + + if (msgs.size() >= 2) { + SrsSharedPtrMessage *first_msg = msgs.front(); + first_msg_ts = first_msg->timestamp; + + SrsSharedPtrMessage *last_msg = msgs.back(); + last_msg_ts = last_msg->timestamp; + + duration_ms = last_msg_ts - first_msg_ts; + } + + return duration_ms; + } + + /*! + set/get index + */ + inline void set_index(int idx) + { + char fg_name[1024] = {0}; + sprintf(fg_name, "/var/www/live/stream0Seg1-Frag%d", idx); + path = fg_name; + index = idx; + } + + inline int get_index() + { + return index; + } + + /*! + set/get start time + */ + inline void set_start_time(long long st) + { + start_time = st; + } + + inline long long get_start_time() + { + return start_time; + } + + void set_video_sh(SrsSharedPtrMessage *msg) + { + srs_freep(videoSh); + videoSh = msg->copy(); + } + + void set_audio_sh(SrsSharedPtrMessage *msg) + { + srs_freep(audioSh); + audioSh = msg->copy(); + } + + string fragment_path() + { + return path; + } + +private: + SrsRequest *req; + list msgs; + + /*! + the index of this fragment + */ + int index; + long long start_time; + + SrsSharedPtrMessage *videoSh; + SrsSharedPtrMessage *audioSh; + string path; +}; + +SrsHds::SrsHds(SrsSource *s) + : currentSegment(NULL) + , source(s) + , fragment_index(1) + , video_sh(NULL) + , audio_sh(NULL) + , hds_req(NULL) +{ + +} + +SrsHds::~SrsHds() +{ + +} + +int SrsHds::on_publish(SrsRequest *req) +{ + hds_req = req->copy(); + + return flush_mainfest(); +} + +int SrsHds::on_unpublish() +{ + int ret = ERROR_SUCCESS; + + srs_freep(video_sh); + srs_freep(audio_sh); + srs_freep(hds_req); + + // clean fragments + list::iterator iter; + for (iter = fragments.begin(); iter != fragments.end(); ++iter) { + SrsHdsFragment *st = *iter; + srs_freep(st); + } + fragments.clear(); + + srs_freep(currentSegment); + + srs_trace("HDS un-published"); + + return ret; +} + +int SrsHds::on_video(SrsSharedPtrMessage* msg) +{ + int ret = ERROR_SUCCESS; + + if (SrsFlvCodec::video_is_sequence_header(msg->payload, msg->size)) { + srs_freep(video_sh); + video_sh = msg->copy(); + } + + if (!currentSegment) { + currentSegment = new SrsHdsFragment(hds_req); + currentSegment->set_index(fragment_index++); + currentSegment->set_start_time(msg->timestamp); + + if (video_sh) + currentSegment->set_video_sh(video_sh); + + if (audio_sh) + currentSegment->set_audio_sh(audio_sh); + } + + currentSegment->on_video(msg); + + double fragment_duration = _srs_config->get_hds_fragment(hds_req->vhost) * 1000; + if (currentSegment->duration() >= fragment_duration) { + // flush segment + if ((ret = currentSegment->flush()) != ERROR_SUCCESS) { + srs_error("flush segment failed."); + return ret; + } + + srs_trace("flush Segment success."); + fragments.push_back(currentSegment); + currentSegment = NULL; + adjust_windows(); + + // flush bootstrap + if ((ret = flush_bootstrap()) != ERROR_SUCCESS) { + srs_error("flush bootstrap failed."); + return ret; + } + + srs_trace("flush BootStrap success."); + } + + return ret; +} + +int SrsHds::on_audio(SrsSharedPtrMessage* msg) +{ + int ret = ERROR_SUCCESS; + + if (SrsFlvCodec::audio_is_sequence_header(msg->payload, msg->size)) { + srs_freep(audio_sh); + audio_sh = msg->copy(); + } + + if (!currentSegment) { + currentSegment = new SrsHdsFragment(hds_req); + currentSegment->set_index(fragment_index++); + currentSegment->set_start_time(msg->timestamp); + + if (video_sh) + currentSegment->set_video_sh(video_sh); + + if (audio_sh) + currentSegment->set_audio_sh(audio_sh); + } + + currentSegment->on_audio(msg); + + double fragment_duration = _srs_config->get_hds_fragment(hds_req->vhost) * 1000; + if (currentSegment->duration() >= fragment_duration) { + // flush segment + if ((ret = currentSegment->flush()) != ERROR_SUCCESS) { + srs_error("flush segment failed."); + return ret; + } + + srs_info("flush Segment success."); + + // reset the current segment + fragments.push_back(currentSegment); + currentSegment = NULL; + adjust_windows(); + + // flush bootstrap + if ((ret = flush_bootstrap()) != ERROR_SUCCESS) { + srs_error("flush bootstrap failed."); + return ret; + } + + srs_info("flush BootStrap success."); + } + + return ret; +} + +int SrsHds::flush_mainfest() +{ + int ret = ERROR_SUCCESS; + + char buf[1024] = {0}; + sprintf(buf, "\n" + "\n\t" + "%s.f4m\n\t" + "live\n\t" + "streaming\n\t" + "\n\t" + "\n" + "" + , hds_req->stream.c_str(), hds_req->stream.c_str(), hds_req->stream.c_str()); + + string dir = _srs_config->get_hds_path(hds_req->vhost) + "/" + hds_req->app; + if ((ret = srs_create_dir_recursively(dir)) != ERROR_SUCCESS) { + srs_error("hds create dir failed. ret=%d", ret); + return ret; + } + string path = dir + "/" + hds_req->stream + ".f4m"; + + int fd = open(path.c_str(), O_WRONLY | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH); + if (fd < 0) { + srs_error("open manifest file failed, path=%s", path.c_str()); + ret = ERROR_HDS_OPEN_F4M_FAILED; + return ret; + } + + int f4m_size = strlen(buf); + if (write(fd, buf, f4m_size) != f4m_size) { + srs_error("write manifest file failed, path=", path.c_str()); + close(fd); + ret = ERROR_HDS_WRITE_F4M_FAILED; + return ret; + } + close(fd); + + srs_trace("build manifest success=%s", path.c_str()); + + return ERROR_SUCCESS; +} + +int SrsHds::flush_bootstrap() +{ + int ret = ERROR_SUCCESS; + + SrsStream abst; + + int size = 1024*100; + + char *start_abst = new char[1024*100]; + SrsAutoFree(char, start_abst); + + int size_abst = 0; + char *start_asrt = NULL; + int size_asrt = 0; + char *start_afrt = NULL; + int size_afrt = 0; + + abst.initialize(start_abst, size); + + // @see video_file_format_spec_v10_1 + // page: 46 + abst.write_4bytes(0); + abst.write_string("abst"); + abst.write_1bytes(0x00); // Either 0 or 1 + abst.write_3bytes(0x00); // Flags always 0 + size_abst += 12; + /*! + @BootstrapinfoVersion UI32 + The version number of the bootstrap information. + When the Update field is set, BootstrapinfoVersion + indicates the version number that is being updated. + we assume this is the last. + */ + abst.write_4bytes(fragment_index - 1); // BootstrapinfoVersion + + abst.write_1bytes(0x20); // profile, live, update + abst.write_4bytes(1000); // TimeScale Typically, the value is 1000, for a unit of milliseconds + size_abst += 9; + /*! + The timestamp in TimeScale units of the latest available Fragment in the media presentation. + This timestamp is used to request the right fragment number. + The CurrentMedia Time can be the total duration. + For media presentations that are not live, CurrentMediaTime can be 0. + */ + SrsHdsFragment *st = fragments.back(); + abst.write_8bytes(st->get_start_time()); + + // SmpteTimeCodeOffset + abst.write_8bytes(0); + size_abst += 16; + + /*! + @MovieIdentifier STRING + The identifier of this presentation. + we write null string. + */ + abst.write_1bytes(0); + size_abst += 1; + /*! + @ServerEntryCount UI8 + The number of ServerEntryTable entries. + The minimum value is 0. + */ + abst.write_1bytes(0); + size_abst += 1; + /*! + @ServerEntryTable + because we write 0 of ServerEntryCount, so this feild is ignored. + */ + + /*! + @QualityEntryCount UI8 + The number of QualityEntryTable entries, which is + also the number of available quality levels. The + minimum value is 0. Available quality levels are for, + for example, multi bit rate files or trick files. + */ + abst.write_1bytes(0); + size_abst += 1; + /*! + @QualityEntryTable + because we write 0 of QualityEntryCount, so this feild is ignored. + */ + + /*! + @DrmData STRING + Null or null-terminated UTF-8 string. This string + holds Digital Rights Management metadata. + Encrypted files use this metadata to get the + necessary keys and licenses for decryption and play back. + we write null string. + */ + abst.write_1bytes(0); + size_abst += 1; + /*! + @MetaData STRING + Null or null-terminated UTF - 8 string that holds metadata. + we write null string. + */ + abst.write_1bytes(0); + size_abst += 1; + /*! + @SegmentRunTableCount UI8 + The number of entries in SegmentRunTableEntries. + The minimum value is 1. Typically, one table + contains all segment runs. However, this count + provides the flexibility to define the segment runs + individually for each quality level (or trick file). + */ + abst.write_1bytes(1); + size_abst += 1; + + /////////////////////////////////////////////////// + start_asrt = start_abst + size_abst; + // MStream asrt; + // follows by asrt + abst.write_4bytes(0); + abst.write_string("asrt"); + size_asrt += 8; + /*! + @Version UI8 + @Flags UI24 + */ + abst.write_4bytes(0); + size_asrt += 4; + /*! + @QualityEntryCount UI8 + The number of QualitySegmen tUrlModifiers + (quality level references) that follow. If 0, this + Segment Run Table applies to all quality levels, + and there shall be only one Segment Run Table + box in the Bootstrap Info box. + */ + abst.write_1bytes(0); + size_asrt += 1; + + /*! + @QualitySegmentUrlModifiers + ignored. + */ + + /*! + @SegmentRunEntryCount + The number of items in this + SegmentRunEn tryTable. The minimum value is 1. + */ + abst.write_4bytes(1); + size_asrt += 4; + /*! + @SegmentRunEntryTable + */ + for (int i = 0; i < 1; ++i) { + /*! + @FirstSegment UI32 + The identifying number of the first segment in the run of + segments containing the same number of fragments. + The segment corresponding to the FirstSegment in the next + SEGMENTRUNENTRY will terminate this run. + */ + abst.write_4bytes(1); + + /*! + @FragmentsPerSegment UI32 + The number of fragments in each segment in this run. + */ + abst.write_4bytes(fragment_index - 1); + size_asrt += 8; + } + + update_box(start_asrt, size_asrt); + size_abst += size_asrt; + + /*! + @FragmentRunTableCount UI8 + The number of entries in FragmentRunTable-Entries. + The min i mum value is 1. + */ + abst.write_1bytes(1); + size_abst += 1; + + ///////////////////////////////////// + //MStream afrt; + // follows by afrt + start_afrt = start_abst + size_abst; + + abst.write_4bytes(0); + abst.write_string("afrt"); + size_afrt += 8; + + /*! + @Version UI8 + @Flags UI24 + */ + abst.write_4bytes(0); + size_afrt += 4; + /*! + @TimeScale UI32 + The number of time units per second, used in the FirstFragmentTime stamp and + Fragment Duration fields. + Typically, the value is 1000. + */ + abst.write_4bytes(1000); + size_afrt += 4; + /*! + @QualityEntryCount UI8 + The number of QualitySegment Url Modifiers + (quality level references) that follow. + If 0, this Fragment Run Table applies to all quality levels, + and there shall be only one Fragment Run Table + box in the Bootstrap Info box. + */ + abst.write_1bytes(0); + size_afrt += 1; + + /*! + @FragmentRunEntryCount UI32 + The number of items in this FragmentRunEntryTable. + The minimum value is 1. + */ + abst.write_4bytes(fragments.size()); + size_afrt += 4; + + list::iterator iter; + for (iter = fragments.begin(); iter != fragments.end(); ++iter) { + SrsHdsFragment *st = *iter; + abst.write_4bytes(st->get_index()); + abst.write_8bytes(st->get_start_time()); + abst.write_4bytes(st->duration()); + size_afrt += 16; + } + + update_box(start_afrt, size_afrt); +// abst.append(afrt); + + size_abst += size_afrt; + update_box(start_abst, size_abst); + + string path = _srs_config->get_hds_path(hds_req->vhost) + "/" + hds_req->app + "/" + hds_req->stream +".abst"; + + int fd = open(path.c_str(), O_WRONLY | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH); + if (fd < 0) { + srs_error("open bootstrap file failed, path=%s", path.c_str()); + ret = ERROR_HDS_OPEN_BOOTSTRAP_FAILED; + return ret; + } + + if (write(fd, start_abst, size_abst) != size_abst) { + srs_error("write bootstrap file failed, path=", path.c_str()); + close(fd); + ret = ERROR_HDS_WRITE_BOOTSTRAP_FAILED; + return ret; + } + close(fd); + + srs_trace("build bootstrap success=%s", path.c_str()); + + return ERROR_SUCCESS; +} + +void SrsHds::adjust_windows() +{ + int windows_size = 0; + list::iterator iter; + for (iter = fragments.begin(); iter != fragments.end(); ++iter) { + SrsHdsFragment *fragment = *iter; + windows_size += fragment->duration(); + } + + double windows_size_limit = _srs_config->get_hds_window(hds_req->vhost) * 1000; + if (windows_size > windows_size_limit ) { + SrsHdsFragment *fragment = fragments.front(); + unlink(fragment->fragment_path().c_str()); + fragments.erase(fragments.begin()); + srs_freep(fragment); + } +} diff --git a/trunk/src/app/srs_app_hds.hpp b/trunk/src/app/srs_app_hds.hpp new file mode 100644 index 0000000000..ef1b202d39 --- /dev/null +++ b/trunk/src/app/srs_app_hds.hpp @@ -0,0 +1,65 @@ +/* +The MIT License (MIT) + +Copyright (c) 2013-2015 wenjiegit +Copyright (c) 2013-2015 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_APP_HDS_HPP +#define SRS_APP_HDS_HPP + +#include + +class SrsRequest; +class SrsSharedPtrMessage; +class SrsHdsFragment; +class SrsSource; + +using namespace std; + +class SrsHds +{ +public: + SrsHds(SrsSource* s); + ~SrsHds(); + + int on_publish(SrsRequest* req); + int on_unpublish(); + + int on_video(SrsSharedPtrMessage* msg); + int on_audio(SrsSharedPtrMessage* msg); + +private: + int flush_mainfest(); + int flush_bootstrap(); + void adjust_windows(); + +private: + list fragments; + SrsHdsFragment *currentSegment; + SrsSource *source; + int fragment_index; + SrsSharedPtrMessage *video_sh; + SrsSharedPtrMessage *audio_sh; + + SrsRequest *hds_req; +}; + +#endif // SRS_APP_HDS_HPP diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index a59f00aff9..be129f1515 100644 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -42,6 +42,7 @@ using namespace std; #include #include #include +#include #define CONST_MAX_JITTER_MS 500 #define DEFAULT_FRAME_TIME_MS 40 @@ -769,6 +770,8 @@ SrsSource::SrsSource(ISrsHlsHandler* hh) #ifdef SRS_AUTO_TRANSCODE encoder = new SrsEncoder(); #endif + + hds = new SrsHds(this); cache_metadata = cache_sh_video = cache_sh_audio = NULL; @@ -1323,6 +1326,15 @@ int SrsSource::on_audio(SrsCommonMessage* __audio) ret = ERROR_SUCCESS; } #endif + + if ((ret = hds->on_audio(&msg)) != ERROR_SUCCESS) { + // unpublish, ignore ret. + hds->on_unpublish(); + // ignore. + ret = ERROR_SUCCESS; + + srs_warn("hds process audio message failed, ignore and disable dvr. ret=%d", ret); + } // copy to all consumer int nb_consumers = (int)consumers.size(); @@ -1455,6 +1467,15 @@ int SrsSource::on_video(SrsCommonMessage* __video) ret = ERROR_SUCCESS; } #endif + + if ((ret = hds->on_video(&msg)) != ERROR_SUCCESS) { + // unpublish, ignore ret. + hds->on_unpublish(); + // ignore. + ret = ERROR_SUCCESS; + + srs_warn("hds process video message failed, ignore and disable dvr. ret=%d", ret); + } // copy to all consumer if (true) { @@ -1693,6 +1714,11 @@ int SrsSource::on_publish() } #endif + if ((ret = hds->on_publish(_req)) != ERROR_SUCCESS) { + srs_error("start hds failed. ret=%d", ret); + return ret; + } + // notify the handler. srs_assert(handler); if ((ret = handler->on_publish(this, _req)) != ERROR_SUCCESS) { @@ -1720,6 +1746,8 @@ void SrsSource::on_unpublish() dvr->on_unpublish(); #endif + hds->on_unpublish(); + gop_cache->clear(); srs_freep(cache_metadata); diff --git a/trunk/src/app/srs_app_source.hpp b/trunk/src/app/srs_app_source.hpp index eb18082f1f..54511750f3 100644 --- a/trunk/src/app/srs_app_source.hpp +++ b/trunk/src/app/srs_app_source.hpp @@ -62,6 +62,7 @@ class SrsEncoder; #endif class SrsStream; class ISrsHlsHandler; +class SrsHds; /** * the time jitter algorithm: @@ -411,6 +412,7 @@ class SrsSource : public ISrsReloadHandler #ifdef SRS_AUTO_TRANSCODE SrsEncoder* encoder; #endif + SrsHds *hds; // edge control service SrsPlayEdge* play_edge; SrsPublishEdge* publish_edge; diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 49f3553a51..7f0223cd67 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -213,6 +213,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_HTTP_DVR_REQUEST 3051 #define ERROR_HTTP_JSON_REQUIRED 3052 #define ERROR_HTTP_DVR_CREATE_REQUEST 3053 +// HDS error code +#define ERROR_HDS_OPEN_F4M_FAILED 3054 +#define ERROR_HDS_WRITE_F4M_FAILED 3055 +#define ERROR_HDS_OPEN_BOOTSTRAP_FAILED 3056 +#define ERROR_HDS_WRITE_BOOTSTRAP_FAILED 3057 +#define ERROR_HDS_OPEN_FRAGMENT_FAILED 3058 +#define ERROR_HDS_WRITE_FRAGMENT_FAILED 3059 /////////////////////////////////////////////////////// // HTTP/StreamCaster protocol error. From 570c0d66bdd255b12e3c3bb202628d742399cc66 Mon Sep 17 00:00:00 2001 From: "wenjie.zhao" Date: Wed, 11 Mar 2015 14:34:00 +0800 Subject: [PATCH 2/3] fix path issue. --- trunk/src/app/srs_app_hds.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/trunk/src/app/srs_app_hds.cpp b/trunk/src/app/srs_app_hds.cpp index f41288a555..fa981fe2aa 100644 --- a/trunk/src/app/srs_app_hds.cpp +++ b/trunk/src/app/srs_app_hds.cpp @@ -152,10 +152,7 @@ class SrsHdsFragment data = string(ss.data(), ss.size()) + data; - char file_path[1024] = {0}; - sprintf(file_path, "%s/%s/%sSeg1-Frag%d", _srs_config->get_hds_path(req->vhost).c_str() - , req->app.c_str(), req->stream.c_str(), index); - + const char *file_path = path.c_str(); int fd = open(file_path, O_WRONLY | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH); if (fd < 0) { srs_error("open fragment file failed, path=%s", file_path); @@ -203,9 +200,11 @@ class SrsHdsFragment */ inline void set_index(int idx) { - char fg_name[1024] = {0}; - sprintf(fg_name, "/var/www/live/stream0Seg1-Frag%d", idx); - path = fg_name; + char file_path[1024] = {0}; + sprintf(file_path, "%s/%s/%sSeg1-Frag%d", _srs_config->get_hds_path(req->vhost).c_str() + , req->app.c_str(), req->stream.c_str(), idx); + + path = file_path; index = idx; } From 07d8f060eb7cc13aea0ccda790d3d952dc84b052 Mon Sep 17 00:00:00 2001 From: "wenjie.zhao" Date: Wed, 11 Mar 2015 14:36:28 +0800 Subject: [PATCH 3/3] refine annotation --- trunk/src/app/srs_app_hds.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/trunk/src/app/srs_app_hds.cpp b/trunk/src/app/srs_app_hds.cpp index fa981fe2aa..18c67d2ad9 100644 --- a/trunk/src/app/srs_app_hds.cpp +++ b/trunk/src/app/srs_app_hds.cpp @@ -557,9 +557,8 @@ int SrsHds::flush_bootstrap() abst.write_1bytes(1); size_abst += 1; - /////////////////////////////////////////////////// start_asrt = start_abst + size_abst; - // MStream asrt; + // follows by asrt abst.write_4bytes(0); abst.write_string("asrt"); @@ -625,8 +624,6 @@ int SrsHds::flush_bootstrap() abst.write_1bytes(1); size_abst += 1; - ///////////////////////////////////// - //MStream afrt; // follows by afrt start_afrt = start_abst + size_abst; @@ -677,8 +674,6 @@ int SrsHds::flush_bootstrap() } update_box(start_afrt, size_afrt); -// abst.append(afrt); - size_abst += size_afrt; update_box(start_abst, size_abst);