From 0aed40885572ec49c7223d097635d750cc86311f Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 8 Jul 2015 17:44:25 +0800 Subject: [PATCH] refine code, to 644 and unix encoding. --- trunk/src/app/srs_app_edge.cpp | 0 trunk/src/app/srs_app_edge.hpp | 0 trunk/src/app/srs_app_hds.cpp | 1500 +++++++++++++-------------- trunk/src/app/srs_app_hds.hpp | 136 +-- trunk/src/app/srs_app_rtmp_conn.cpp | 0 trunk/src/app/srs_app_rtmp_conn.hpp | 0 trunk/src/app/srs_app_source.cpp | 0 trunk/src/app/srs_app_source.hpp | 0 8 files changed, 818 insertions(+), 818 deletions(-) mode change 100755 => 100644 trunk/src/app/srs_app_edge.cpp mode change 100755 => 100644 trunk/src/app/srs_app_edge.hpp mode change 100755 => 100644 trunk/src/app/srs_app_rtmp_conn.cpp mode change 100755 => 100644 trunk/src/app/srs_app_rtmp_conn.hpp mode change 100755 => 100644 trunk/src/app/srs_app_source.cpp mode change 100755 => 100644 trunk/src/app/srs_app_source.hpp diff --git a/trunk/src/app/srs_app_edge.cpp b/trunk/src/app/srs_app_edge.cpp old mode 100755 new mode 100644 diff --git a/trunk/src/app/srs_app_edge.hpp b/trunk/src/app/srs_app_edge.hpp old mode 100755 new mode 100644 diff --git a/trunk/src/app/srs_app_hds.cpp b/trunk/src/app/srs_app_hds.cpp index 2255cc655..de0762991 100644 --- a/trunk/src/app/srs_app_hds.cpp +++ b/trunk/src/app/srs_app_hds.cpp @@ -1,750 +1,750 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013-2015 SRS(simple-rtmp-server) - -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 - -#ifdef SRS_AUTO_HDS - -#include -#include -#include -#include -#include -#include -using namespace std; - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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)); - } - - 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; - - 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); - 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 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; - } - - 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) - , fragment_index(1) - , video_sh(NULL) - , audio_sh(NULL) - , hds_req(NULL) - , hds_enabled(false) -{ - -} - -SrsHds::~SrsHds() -{ - -} - -int SrsHds::on_publish(SrsRequest *req) -{ - int ret = ERROR_SUCCESS; - if (hds_enabled) { - return ret; - } - - std::string vhost = req->vhost; - if (!_srs_config->get_hds_enabled(vhost)) { - hds_enabled = false; - return ret; - } - hds_enabled = true; - - hds_req = req->copy(); - - return flush_mainfest(); -} - -int SrsHds::on_unpublish() -{ - int ret = ERROR_SUCCESS; - - if (!hds_enabled) { - return ret; - } - - hds_enabled = false; - - 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 (!hds_enabled) { - return ret; - } - - 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 (!hds_enabled) { - return ret; - } - - 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; - - if ((ret = abst.initialize(start_abst, size)) != ERROR_SUCCESS) { - return ret; - } - - // @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; - - // 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; - - // 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); - 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); - } -} - -#endif +/* +The MIT License (MIT) + +Copyright (c) 2013-2015 SRS(simple-rtmp-server) + +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 + +#ifdef SRS_AUTO_HDS + +#include +#include +#include +#include +#include +#include +using namespace std; + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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)); + } + + 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; + + 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); + 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 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; + } + + 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) + , fragment_index(1) + , video_sh(NULL) + , audio_sh(NULL) + , hds_req(NULL) + , hds_enabled(false) +{ + +} + +SrsHds::~SrsHds() +{ + +} + +int SrsHds::on_publish(SrsRequest *req) +{ + int ret = ERROR_SUCCESS; + if (hds_enabled) { + return ret; + } + + std::string vhost = req->vhost; + if (!_srs_config->get_hds_enabled(vhost)) { + hds_enabled = false; + return ret; + } + hds_enabled = true; + + hds_req = req->copy(); + + return flush_mainfest(); +} + +int SrsHds::on_unpublish() +{ + int ret = ERROR_SUCCESS; + + if (!hds_enabled) { + return ret; + } + + hds_enabled = false; + + 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 (!hds_enabled) { + return ret; + } + + 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 (!hds_enabled) { + return ret; + } + + 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; + + if ((ret = abst.initialize(start_abst, size)) != ERROR_SUCCESS) { + return ret; + } + + // @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; + + // 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; + + // 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); + 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); + } +} + +#endif diff --git a/trunk/src/app/srs_app_hds.hpp b/trunk/src/app/srs_app_hds.hpp index 17030bc4e..edc2e4f48 100644 --- a/trunk/src/app/srs_app_hds.hpp +++ b/trunk/src/app/srs_app_hds.hpp @@ -1,68 +1,68 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013-2015 SRS(simple-rtmp-server) - -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 - -#ifdef SRS_AUTO_HDS - -#include - -class SrsRequest; -class SrsSharedPtrMessage; -class SrsHdsFragment; -class SrsSource; - -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: - std::list fragments; - SrsHdsFragment *currentSegment; - int fragment_index; - SrsSharedPtrMessage *video_sh; - SrsSharedPtrMessage *audio_sh; - - SrsRequest *hds_req; - bool hds_enabled; -}; - -#endif - -#endif +/* +The MIT License (MIT) + +Copyright (c) 2013-2015 SRS(simple-rtmp-server) + +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 + +#ifdef SRS_AUTO_HDS + +#include + +class SrsRequest; +class SrsSharedPtrMessage; +class SrsHdsFragment; +class SrsSource; + +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: + std::list fragments; + SrsHdsFragment *currentSegment; + int fragment_index; + SrsSharedPtrMessage *video_sh; + SrsSharedPtrMessage *audio_sh; + + SrsRequest *hds_req; + bool hds_enabled; +}; + +#endif + +#endif diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp old mode 100755 new mode 100644 diff --git a/trunk/src/app/srs_app_rtmp_conn.hpp b/trunk/src/app/srs_app_rtmp_conn.hpp old mode 100755 new mode 100644 diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp old mode 100755 new mode 100644 diff --git a/trunk/src/app/srs_app_source.hpp b/trunk/src/app/srs_app_source.hpp old mode 100755 new mode 100644