1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00

For #299, extract fragment and fragment window for hls

This commit is contained in:
winlin 2017-03-18 21:29:08 +08:00
parent 6da6e0511d
commit da4c390d69
9 changed files with 450 additions and 192 deletions

2
trunk/configure vendored
View file

@ -187,7 +187,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
"srs_app_recv_thread" "srs_app_security" "srs_app_statistic" "srs_app_hds" "srs_app_recv_thread" "srs_app_security" "srs_app_statistic" "srs_app_hds"
"srs_app_mpegts_udp" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call" "srs_app_mpegts_udp" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call"
"srs_app_caster_flv" "srs_app_process" "srs_app_ng_exec" "srs_app_kafka" "srs_app_caster_flv" "srs_app_process" "srs_app_ng_exec" "srs_app_kafka"
"srs_app_hourglass" "srs_app_dash") "srs_app_hourglass" "srs_app_dash" "srs_app_fragment")
DEFINES="" DEFINES=""
# add each modules for app # add each modules for app
for SRS_MODULE in ${SRS_MODULES[*]}; do for SRS_MODULE in ${SRS_MODULES[*]}; do

View file

@ -84,6 +84,7 @@
3C36DB5D1ABD1CB90066CCAF /* srs_librtmp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C36DB591ABD1CB90066CCAF /* srs_librtmp.cpp */; }; 3C36DB5D1ABD1CB90066CCAF /* srs_librtmp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C36DB591ABD1CB90066CCAF /* srs_librtmp.cpp */; };
3C44AACF1E3AF50200D4ABC3 /* srs_kernel_mp4.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C44AACD1E3AF50200D4ABC3 /* srs_kernel_mp4.cpp */; }; 3C44AACF1E3AF50200D4ABC3 /* srs_kernel_mp4.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C44AACD1E3AF50200D4ABC3 /* srs_kernel_mp4.cpp */; };
3C4AB9331B8C9148006627D3 /* srs_app_ng_exec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C4AB9311B8C9148006627D3 /* srs_app_ng_exec.cpp */; }; 3C4AB9331B8C9148006627D3 /* srs_app_ng_exec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C4AB9311B8C9148006627D3 /* srs_app_ng_exec.cpp */; };
3C4D184C1E73F133008806F7 /* srs_app_fragment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C4D184A1E73F133008806F7 /* srs_app_fragment.cpp */; };
3C4F97121B8B466D00FF0E46 /* srs_app_process.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C4F97101B8B466D00FF0E46 /* srs_app_process.cpp */; }; 3C4F97121B8B466D00FF0E46 /* srs_app_process.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C4F97101B8B466D00FF0E46 /* srs_app_process.cpp */; };
3C5265B41B241BF0009CA186 /* srs_core_mem_watch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C5265B21B241BF0009CA186 /* srs_core_mem_watch.cpp */; }; 3C5265B41B241BF0009CA186 /* srs_core_mem_watch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C5265B21B241BF0009CA186 /* srs_core_mem_watch.cpp */; };
3C663F0F1AB0155100286D8B /* srs_aac_raw_publish.c in Sources */ = {isa = PBXBuildFile; fileRef = 3C663F021AB0155100286D8B /* srs_aac_raw_publish.c */; }; 3C663F0F1AB0155100286D8B /* srs_aac_raw_publish.c in Sources */ = {isa = PBXBuildFile; fileRef = 3C663F021AB0155100286D8B /* srs_aac_raw_publish.c */; };
@ -352,6 +353,8 @@
3C4AB9311B8C9148006627D3 /* srs_app_ng_exec.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_app_ng_exec.cpp; path = ../../../src/app/srs_app_ng_exec.cpp; sourceTree = "<group>"; }; 3C4AB9311B8C9148006627D3 /* srs_app_ng_exec.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_app_ng_exec.cpp; path = ../../../src/app/srs_app_ng_exec.cpp; sourceTree = "<group>"; };
3C4AB9321B8C9148006627D3 /* srs_app_ng_exec.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_app_ng_exec.hpp; path = ../../../src/app/srs_app_ng_exec.hpp; sourceTree = "<group>"; }; 3C4AB9321B8C9148006627D3 /* srs_app_ng_exec.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_app_ng_exec.hpp; path = ../../../src/app/srs_app_ng_exec.hpp; sourceTree = "<group>"; };
3C4AB9341B8C9FF9006627D3 /* exec.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = exec.conf; path = ../../../conf/exec.conf; sourceTree = "<group>"; }; 3C4AB9341B8C9FF9006627D3 /* exec.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = exec.conf; path = ../../../conf/exec.conf; sourceTree = "<group>"; };
3C4D184A1E73F133008806F7 /* srs_app_fragment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_app_fragment.cpp; path = ../../../src/app/srs_app_fragment.cpp; sourceTree = "<group>"; };
3C4D184B1E73F133008806F7 /* srs_app_fragment.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_app_fragment.hpp; path = ../../../src/app/srs_app_fragment.hpp; sourceTree = "<group>"; };
3C4F97101B8B466D00FF0E46 /* srs_app_process.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_app_process.cpp; path = ../../../src/app/srs_app_process.cpp; sourceTree = "<group>"; }; 3C4F97101B8B466D00FF0E46 /* srs_app_process.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_app_process.cpp; path = ../../../src/app/srs_app_process.cpp; sourceTree = "<group>"; };
3C4F97111B8B466D00FF0E46 /* srs_app_process.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_app_process.hpp; path = ../../../src/app/srs_app_process.hpp; sourceTree = "<group>"; }; 3C4F97111B8B466D00FF0E46 /* srs_app_process.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_app_process.hpp; path = ../../../src/app/srs_app_process.hpp; sourceTree = "<group>"; };
3C5265B21B241BF0009CA186 /* srs_core_mem_watch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_core_mem_watch.cpp; path = ../../../src/core/srs_core_mem_watch.cpp; sourceTree = "<group>"; }; 3C5265B21B241BF0009CA186 /* srs_core_mem_watch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_core_mem_watch.cpp; path = ../../../src/core/srs_core_mem_watch.cpp; sourceTree = "<group>"; };
@ -609,6 +612,8 @@
3C12325B1AAE81D900CE8F6C /* srs_app_ffmpeg.hpp */, 3C12325B1AAE81D900CE8F6C /* srs_app_ffmpeg.hpp */,
3C12325C1AAE81D900CE8F6C /* srs_app_forward.cpp */, 3C12325C1AAE81D900CE8F6C /* srs_app_forward.cpp */,
3C12325D1AAE81D900CE8F6C /* srs_app_forward.hpp */, 3C12325D1AAE81D900CE8F6C /* srs_app_forward.hpp */,
3C4D184A1E73F133008806F7 /* srs_app_fragment.cpp */,
3C4D184B1E73F133008806F7 /* srs_app_fragment.hpp */,
3C1EE6AC1AB1055800576EE9 /* srs_app_hds.cpp */, 3C1EE6AC1AB1055800576EE9 /* srs_app_hds.cpp */,
3C1EE6AD1AB1055800576EE9 /* srs_app_hds.hpp */, 3C1EE6AD1AB1055800576EE9 /* srs_app_hds.hpp */,
3C12325E1AAE81D900CE8F6C /* srs_app_heartbeat.cpp */, 3C12325E1AAE81D900CE8F6C /* srs_app_heartbeat.cpp */,
@ -1025,6 +1030,7 @@
3CA432AB1E40AEBC001DA0C6 /* Makefile in Sources */, 3CA432AB1E40AEBC001DA0C6 /* Makefile in Sources */,
3C1232B21AAE81D900CE8F6C /* srs_app_source.cpp in Sources */, 3C1232B21AAE81D900CE8F6C /* srs_app_source.cpp in Sources */,
3C1231F71AAE652D00CE8F6C /* srs_core_performance.cpp in Sources */, 3C1231F71AAE652D00CE8F6C /* srs_core_performance.cpp in Sources */,
3C4D184C1E73F133008806F7 /* srs_app_fragment.cpp in Sources */,
3CC52DD81ACE4023006FEB01 /* srs_utest_amf0.cpp in Sources */, 3CC52DD81ACE4023006FEB01 /* srs_utest_amf0.cpp in Sources */,
3C4F97121B8B466D00FF0E46 /* srs_app_process.cpp in Sources */, 3C4F97121B8B466D00FF0E46 /* srs_app_process.cpp in Sources */,
3C1232981AAE81D900CE8F6C /* srs_app_edge.cpp in Sources */, 3C1232981AAE81D900CE8F6C /* srs_app_edge.cpp in Sources */,

View file

@ -158,6 +158,13 @@ SrsDashController::SrsDashController()
SrsDashController::~SrsDashController() SrsDashController::~SrsDashController()
{ {
srs_freep(mpd); srs_freep(mpd);
vector<SrsFragmentedMp4*>::iterator it;
for (it = fragments.begin(); it != fragments.end(); ++it) {
SrsFragmentedMp4* fragment = *it;
srs_freep(fragment);
}
fragments.clear();
} }
int SrsDashController::initialize(SrsRequest* r) int SrsDashController::initialize(SrsRequest* r)

View file

@ -30,6 +30,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_core.hpp> #include <srs_core.hpp>
#include <string> #include <string>
#include <vector>
#include <srs_app_fragment.hpp>
class SrsRequest; class SrsRequest;
class SrsOriginHub; class SrsOriginHub;
@ -39,7 +42,7 @@ class SrsFormat;
/** /**
* The FMP4(Fragmented MP4) for DASH streaming. * The FMP4(Fragmented MP4) for DASH streaming.
*/ */
class SrsFragmentedMp4 class SrsFragmentedMp4 : public SrsFragment
{ {
public: public:
SrsFragmentedMp4(); SrsFragmentedMp4();
@ -82,6 +85,7 @@ class SrsDashController
private: private:
SrsRequest* req; SrsRequest* req;
SrsMpdWriter* mpd; SrsMpdWriter* mpd;
std::vector<SrsFragmentedMp4*> fragments;
private: private:
std::string home; std::string home;
int video_tack_id; int video_tack_id;

View file

@ -0,0 +1,252 @@
/*
The MIT License (MIT)
Copyright (c) 2013-2017 SRS(ossrs)
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 <srs_app_fragment.hpp>
#include <srs_kernel_utility.hpp>
#include <srs_kernel_log.hpp>
#include <srs_kernel_error.hpp>
#include <unistd.h>
using namespace std;
SrsFragment::SrsFragment()
{
dur = 0;
start_dts = -1;
sequence_header = false;
}
SrsFragment::~SrsFragment()
{
}
void SrsFragment::append(int64_t dts)
{
if (start_dts == -1) {
start_dts = dts;
}
// TODO: FIXME: Use cumulus dts.
start_dts = srs_min(start_dts, dts);
dur = dts - start_dts;
}
int64_t SrsFragment::duration()
{
return dur;
}
bool SrsFragment::is_sequence_header()
{
return sequence_header;
}
void SrsFragment::set_sequence_header(bool v)
{
sequence_header = v;
}
string SrsFragment::fullpath()
{
return filepath;
}
void SrsFragment::set_path(string v)
{
filepath = v;
}
int SrsFragment::unlink_file()
{
int ret = ERROR_SUCCESS;
if (::unlink(filepath.c_str()) < 0) {
ret = ERROR_SYSTEM_FRAGMENT_UNLINK;
srs_error("Unlink fragment failed, file=%s, ret=%d.", filepath.c_str(), ret);
return ret;
}
return ret;
}
string SrsFragment::tmppath()
{
return filepath + ".tmp";
}
int SrsFragment::unlink_tmpfile()
{
int ret = ERROR_SUCCESS;
string filepath = tmppath();
if (::unlink(filepath.c_str()) < 0) {
ret = ERROR_SYSTEM_FRAGMENT_UNLINK;
srs_error("Unlink temporary fragment failed, file=%s, ret=%d.", filepath.c_str(), ret);
return ret;
}
return ret;
}
int SrsFragment::rename()
{
int ret = ERROR_SUCCESS;
string full_path = fullpath();
string tmp_file = tmppath();
if (::rename(tmp_file.c_str(), full_path.c_str()) < 0) {
ret = ERROR_SYSTEM_FRAGMENT_RENAME;
srs_error("rename ts file failed, %s => %s. ret=%d", tmp_file.c_str(), full_path.c_str(), ret);
return ret;
}
return ret;
}
SrsFragmentWindow::SrsFragmentWindow()
{
}
SrsFragmentWindow::~SrsFragmentWindow()
{
vector<SrsFragment*>::iterator it;
for (it = fragments.begin(); it != fragments.end(); ++it) {
SrsFragment* fragment = *it;
srs_freep(fragment);
}
fragments.clear();
for (it = expired_fragments.begin(); it != expired_fragments.end(); ++it) {
SrsFragment* fragment = *it;
srs_freep(fragment);
}
expired_fragments.clear();
}
void SrsFragmentWindow::dispose()
{
int ret = ERROR_SUCCESS;
std::vector<SrsFragment*>::iterator it;
for (it = fragments.begin(); it != fragments.end(); ++it) {
SrsFragment* fragment = *it;
if ((ret = fragment->unlink_file()) != ERROR_SUCCESS) {
srs_warn("Unlink ts failed, file=%s, ret=%d", fragment->fullpath().c_str(), ret);
}
srs_freep(fragment);
}
fragments.clear();
for (it = expired_fragments.begin(); it != expired_fragments.end(); ++it) {
SrsFragment* fragment = *it;
if ((ret = fragment->unlink_file()) != ERROR_SUCCESS) {
srs_warn("Unlink ts failed, file=%s, ret=%d", fragment->fullpath().c_str(), ret);
}
srs_freep(fragment);
}
expired_fragments.clear();
}
void SrsFragmentWindow::append(SrsFragment* fragment)
{
fragments.push_back(fragment);
}
void SrsFragmentWindow::shrink(int64_t window)
{
int64_t duration = 0;
int remove_index = -1;
for (int i = (int)fragments.size() - 1; i >= 0; i--) {
SrsFragment* fragment = fragments[i];
duration += fragment->duration();
if (duration > window) {
remove_index = i;
break;
}
}
for (int i = 0; i < remove_index && !fragments.empty(); i++) {
SrsFragment* fragment = *fragments.begin();
fragments.erase(fragments.begin());
expired_fragments.push_back(fragment);
}
}
void SrsFragmentWindow::clear_expired(bool delete_files)
{
int ret = ERROR_SUCCESS;
std::vector<SrsFragment*>::iterator it;
for (it = expired_fragments.begin(); it != expired_fragments.end(); ++it) {
SrsFragment* fragment = *it;
if (delete_files && (ret = fragment->unlink_file()) != ERROR_SUCCESS) {
srs_warn("Unlink ts failed, file=%s, ret=%d", fragment->fullpath().c_str(), ret);
}
srs_freep(fragment);
}
expired_fragments.clear();
}
int64_t SrsFragmentWindow::max_duration()
{
int64_t v = 0;
std::vector<SrsFragment*>::iterator it;
for (it = fragments.begin(); it != fragments.end(); ++it) {
SrsFragment* fragment = *it;
v = srs_max(v, fragment->duration());
}
return v;
}
bool SrsFragmentWindow::empty()
{
return fragments.empty();
}
SrsFragment* SrsFragmentWindow::first()
{
return fragments.at(0);
}
int SrsFragmentWindow::size()
{
return (int)fragments.size();
}
SrsFragment* SrsFragmentWindow::at(int index)
{
return fragments.at(index);
}

View file

@ -0,0 +1,110 @@
/*
The MIT License (MIT)
Copyright (c) 2013-2017 SRS(ossrs)
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_FRAGMENT_HPP
#define SRS_APP_FRAGMENT_HPP
/*
#include <srs_app_fragment.hpp>
*/
#include <srs_core.hpp>
#include <string>
#include <vector>
/**
* Represent a fragment, such as HLS segment, DVR segment or DASH segment.
* It's a media file, for example FLV or MP4, with duration.
*/
class SrsFragment
{
private:
// The duration in ms.
int64_t dur;
// The full file path of fragment.
std::string filepath;
// The start DTS in ms of segment.
int64_t start_dts;
// Whether current segement contains sequence header.
bool sequence_header;
public:
SrsFragment();
virtual ~SrsFragment();
public:
// Append a frame with dts into fragment.
// @dts The dts of frame in ms.
virtual void append(int64_t dts);
// Get the duration of fragment in ms.
virtual int64_t duration();
// Whether the fragment contains any sequence header.
virtual bool is_sequence_header();
// Set whether contains sequence header.
virtual void set_sequence_header(bool v);
// Get the full path of fragment.
virtual std::string fullpath();
// Set the full path of fragment.
virtual void set_path(std::string v);
// Unlink the fragment, to delete the file.
// @remark Ignore any error.
virtual int unlink_file();
public:
// Get the temporary path for file.
virtual std::string tmppath();
// Unlink the temporary file.
virtual int unlink_tmpfile();
// Rename the temp file to final file.
virtual int rename();
};
/**
* The fragment window manage a series of fragment.
*/
class SrsFragmentWindow
{
private:
std::vector<SrsFragment*> fragments;
// The expired fragments, need to be free in future.
std::vector<SrsFragment*> expired_fragments;
public:
SrsFragmentWindow();
virtual ~SrsFragmentWindow();
public:
// Dispose all fragments, delete the files.
virtual void dispose();
// Append a new fragment, which is ready to delivery to client.
virtual void append(SrsFragment* fragment);
// Shrink the window, push the expired fragment to a queue.
virtual void shrink(int64_t window);
// Clear the expired fragments.
virtual void clear_expired(bool delete_files);
// Get the max duration in ms of all fragments.
virtual int64_t max_duration();
public:
virtual bool empty();
virtual SrsFragment* first();
virtual int size();
virtual SrsFragment* at(int index);
};
#endif

View file

@ -54,8 +54,6 @@ using namespace std;
// drop the segment when duration of ts too small. // drop the segment when duration of ts too small.
#define SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS 100 #define SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS 100
// when hls timestamp jump, reset it.
#define SRS_AUTO_HLS_SEGMENT_TIMESTAMP_JUMP_MS 300
// fragment plus the deviation percent. // fragment plus the deviation percent.
#define SRS_HLS_FLOOR_REAP_PERCENT 0.3 #define SRS_HLS_FLOOR_REAP_PERCENT 0.3
@ -64,10 +62,7 @@ using namespace std;
SrsHlsSegment::SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc) SrsHlsSegment::SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc)
{ {
duration = 0;
sequence_no = 0; sequence_no = 0;
segment_start_dts = 0;
is_sequence_header = false;
writer = new SrsFileWriter(); writer = new SrsFileWriter();
tscw = new SrsTsContextWriter(writer, c, ac, vc); tscw = new SrsTsContextWriter(writer, c, ac, vc);
} }
@ -78,27 +73,6 @@ SrsHlsSegment::~SrsHlsSegment()
srs_freep(writer); srs_freep(writer);
} }
void SrsHlsSegment::update_duration(int64_t current_frame_dts)
{
// we use video/audio to update segment duration,
// so when reap segment, some previous audio frame will
// update the segment duration, which is nagetive,
// just ignore it.
if (current_frame_dts < segment_start_dts) {
// for atc and timestamp jump, reset the start dts.
if (current_frame_dts < segment_start_dts - SRS_AUTO_HLS_SEGMENT_TIMESTAMP_JUMP_MS * 90) {
srs_warn("hls timestamp jump %"PRId64"=>%"PRId64, segment_start_dts, current_frame_dts);
segment_start_dts = current_frame_dts;
}
return;
}
duration = (current_frame_dts - segment_start_dts) / 90000.0;
srs_assert(duration >= 0);
return;
}
SrsDvrAsyncCallOnHls::SrsDvrAsyncCallOnHls(int c, SrsRequest* r, string p, string t, string m, string mu, int s, double d) SrsDvrAsyncCallOnHls::SrsDvrAsyncCallOnHls(int c, SrsRequest* r, string p, string t, string m, string mu, int s, double d)
{ {
req = r->copy(); req = r->copy();
@ -225,17 +199,11 @@ SrsHlsMuxer::SrsHlsMuxer()
current = NULL; current = NULL;
async = new SrsAsyncCallWorker(); async = new SrsAsyncCallWorker();
context = new SrsTsContext(); context = new SrsTsContext();
segments = new SrsFragmentWindow();
} }
SrsHlsMuxer::~SrsHlsMuxer() SrsHlsMuxer::~SrsHlsMuxer()
{ {
std::vector<SrsHlsSegment*>::iterator it;
for (it = segments.begin(); it != segments.end(); ++it) {
SrsHlsSegment* segment = *it;
srs_freep(segment);
}
segments.clear();
srs_freep(current); srs_freep(current);
srs_freep(req); srs_freep(req);
srs_freep(async); srs_freep(async);
@ -244,20 +212,13 @@ SrsHlsMuxer::~SrsHlsMuxer()
void SrsHlsMuxer::dispose() void SrsHlsMuxer::dispose()
{ {
std::vector<SrsHlsSegment*>::iterator it; int ret = ERROR_SUCCESS;
for (it = segments.begin(); it != segments.end(); ++it) {
SrsHlsSegment* segment = *it; segments->dispose();
if (unlink(segment->full_path.c_str()) < 0) {
srs_warn("dispose unlink path failed, file=%s.", segment->full_path.c_str());
}
srs_freep(segment);
}
segments.clear();
if (current) { if (current) {
std::string path = current->full_path + ".tmp"; if ((ret = current->unlink_tmpfile()) != ERROR_SUCCESS) {
if (unlink(path.c_str()) < 0) { srs_warn("Unlink tmp ts failed, ret=%d", ret);
srs_warn("dispose unlink path failed, file=%s", path.c_str());
} }
srs_freep(current); srs_freep(current);
} }
@ -266,8 +227,6 @@ void SrsHlsMuxer::dispose()
srs_warn("dispose unlink path failed. file=%s", m3u8.c_str()); srs_warn("dispose unlink path failed. file=%s", m3u8.c_str());
} }
// TODO: FIXME: support hls dispose in HTTP cache.
srs_trace("gracefully dispose hls %s", req? req->get_stream_url().c_str() : ""); srs_trace("gracefully dispose hls %s", req? req->get_stream_url().c_str() : "");
} }
@ -283,7 +242,7 @@ string SrsHlsMuxer::ts_url()
double SrsHlsMuxer::duration() double SrsHlsMuxer::duration()
{ {
return current? current->duration:0; return current? current->duration()/1000.0:0;
} }
int SrsHlsMuxer::deviation() int SrsHlsMuxer::deviation()
@ -347,7 +306,7 @@ int SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix,
return ret; return ret;
} }
int SrsHlsMuxer::segment_open(int64_t segment_start_dts) int SrsHlsMuxer::segment_open()
{ {
int ret = ERROR_SUCCESS; int ret = ERROR_SUCCESS;
@ -395,7 +354,6 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts)
// new segment. // new segment.
current = new SrsHlsSegment(context, default_acodec, default_vcodec); current = new SrsHlsSegment(context, default_acodec, default_vcodec);
current->sequence_no = _sequence_no++; current->sequence_no = _sequence_no++;
current->segment_start_dts = segment_start_dts;
// generate filename. // generate filename.
std::string ts_file = hls_ts_file; std::string ts_file = hls_ts_file;
@ -440,11 +398,12 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts)
ss << current->sequence_no; ss << current->sequence_no;
ts_file = srs_string_replace(ts_file, "[seq]", ss.str()); ts_file = srs_string_replace(ts_file, "[seq]", ss.str());
} }
current->full_path = hls_path + "/" + ts_file; current->set_path(hls_path + "/" + ts_file);
srs_info("hls: generate ts path %s, tmpl=%s, floor=%d", ts_file.c_str(), hls_ts_file.c_str(), hls_ts_floor); srs_info("hls: generate ts path %s, tmpl=%s, floor=%d", ts_file.c_str(), hls_ts_file.c_str(), hls_ts_floor);
// the ts url, relative or absolute url. // the ts url, relative or absolute url.
std::string ts_url = current->full_path; // TODO: FIXME: Use url and path manager.
std::string ts_url = current->fullpath();
if (srs_string_starts_with(ts_url, m3u8_dir)) { if (srs_string_starts_with(ts_url, m3u8_dir)) {
ts_url = ts_url.substr(m3u8_dir.length()); ts_url = ts_url.substr(m3u8_dir.length());
} }
@ -464,7 +423,7 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts)
current->uri += ts_url; current->uri += ts_url;
// create dir recursively for hls. // create dir recursively for hls.
std::string ts_dir = srs_path_dirname(current->full_path); std::string ts_dir = srs_path_dirname(current->fullpath());
if ((ret = srs_create_dir_recursively(ts_dir)) != ERROR_SUCCESS) { if ((ret = srs_create_dir_recursively(ts_dir)) != ERROR_SUCCESS) {
srs_error("create app dir %s failed. ret=%d", ts_dir.c_str(), ret); srs_error("create app dir %s failed. ret=%d", ts_dir.c_str(), ret);
return ret; return ret;
@ -472,7 +431,7 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts)
srs_info("create ts dir %s ok", ts_dir.c_str()); srs_info("create ts dir %s ok", ts_dir.c_str());
// open temp ts file. // open temp ts file.
std::string tmp_file = current->full_path + ".tmp"; std::string tmp_file = current->tmppath();
if ((ret = current->tscw->open(tmp_file.c_str())) != ERROR_SUCCESS) { if ((ret = current->tscw->open(tmp_file.c_str())) != ERROR_SUCCESS) {
srs_error("open hls muxer failed. ret=%d", ret); srs_error("open hls muxer failed. ret=%d", ret);
return ret; return ret;
@ -490,7 +449,7 @@ int SrsHlsMuxer::on_sequence_header()
// set the current segment to sequence header, // set the current segment to sequence header,
// when close the segement, it will write a discontinuity to m3u8 file. // when close the segement, it will write a discontinuity to m3u8 file.
current->is_sequence_header = true; current->set_sequence_header(true);
return ret; return ret;
} }
@ -500,7 +459,7 @@ bool SrsHlsMuxer::is_segment_overflow()
srs_assert(current); srs_assert(current);
// to prevent very small segment. // to prevent very small segment.
if (current->duration * 1000 < 2 * SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS) { if (current->duration() < 2 * SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS) {
return false; return false;
} }
@ -509,7 +468,7 @@ bool SrsHlsMuxer::is_segment_overflow()
srs_info("hls: dur=%.2f, tar=%.2f, dev=%.2fms/%dp, frag=%.2f", srs_info("hls: dur=%.2f, tar=%.2f, dev=%.2fms/%dp, frag=%.2f",
current->duration, hls_fragment + deviation, deviation, deviation_ts, hls_fragment); current->duration, hls_fragment + deviation, deviation, deviation_ts, hls_fragment);
return current->duration >= hls_fragment + deviation; return current->duration() >= (hls_fragment + deviation) * 1000;
} }
bool SrsHlsMuxer::wait_keyframe() bool SrsHlsMuxer::wait_keyframe()
@ -523,7 +482,7 @@ bool SrsHlsMuxer::is_segment_absolutely_overflow()
srs_assert(current); srs_assert(current);
// to prevent very small segment. // to prevent very small segment.
if (current->duration * 1000 < 2 * SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS) { if (current->duration() < 2 * SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS) {
return false; return false;
} }
@ -532,7 +491,7 @@ bool SrsHlsMuxer::is_segment_absolutely_overflow()
srs_info("hls: dur=%.2f, tar=%.2f, dev=%.2fms/%dp, frag=%.2f", srs_info("hls: dur=%.2f, tar=%.2f, dev=%.2fms/%dp, frag=%.2f",
current->duration, hls_fragment + deviation, deviation, deviation_ts, hls_fragment); current->duration, hls_fragment + deviation, deviation, deviation_ts, hls_fragment);
return current->duration >= hls_aof_ratio * hls_fragment + deviation; return current->duration() >= (hls_aof_ratio * hls_fragment + deviation) * 1000;
} }
bool SrsHlsMuxer::pure_audio() bool SrsHlsMuxer::pure_audio()
@ -555,7 +514,7 @@ int SrsHlsMuxer::flush_audio(SrsTsMessageCache* cache)
} }
// update the duration of segment. // update the duration of segment.
current->update_duration(cache->audio->pts); current->append(cache->audio->pts / 90);
if ((ret = current->tscw->write_audio(cache->audio)) != ERROR_SUCCESS) { if ((ret = current->tscw->write_audio(cache->audio)) != ERROR_SUCCESS) {
return ret; return ret;
@ -584,7 +543,7 @@ int SrsHlsMuxer::flush_video(SrsTsMessageCache* cache)
srs_assert(current); srs_assert(current);
// update the duration of segment. // update the duration of segment.
current->update_duration(cache->video->dts); current->append(cache->video->dts / 90);
if ((ret = current->tscw->write_video(cache->video)) != ERROR_SUCCESS) { if ((ret = current->tscw->write_video(cache->video)) != ERROR_SUCCESS) {
return ret; return ret;
@ -596,7 +555,7 @@ int SrsHlsMuxer::flush_video(SrsTsMessageCache* cache)
return ret; return ret;
} }
int SrsHlsMuxer::segment_close(string log_desc) int SrsHlsMuxer::segment_close()
{ {
int ret = ERROR_SUCCESS; int ret = ERROR_SUCCESS;
@ -608,23 +567,16 @@ int SrsHlsMuxer::segment_close(string log_desc)
// when close current segment, the current segment must not be NULL. // when close current segment, the current segment must not be NULL.
srs_assert(current); srs_assert(current);
// assert segment duplicate.
std::vector<SrsHlsSegment*>::iterator it;
it = std::find(segments.begin(), segments.end(), current);
srs_assert(it == segments.end());
// valid, add to segments if segment duration is ok // valid, add to segments if segment duration is ok
// when too small, it maybe not enough data to play. // when too small, it maybe not enough data to play.
// when too large, it maybe timestamp corrupt. // when too large, it maybe timestamp corrupt.
// make the segment more acceptable, when in [min, max_td * 2], it's ok. // make the segment more acceptable, when in [min, max_td * 2], it's ok.
if (current->duration * 1000 >= SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS && (int)current->duration <= max_td * 2) { if (current->duration() >= SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS && (int)current->duration() <= max_td * 2 * 1000) {
segments.push_back(current);
// use async to call the http hooks, for it will cause thread switch. // use async to call the http hooks, for it will cause thread switch.
if ((ret = async->execute(new SrsDvrAsyncCallOnHls( if ((ret = async->execute(new SrsDvrAsyncCallOnHls(
_srs_context->get_id(), req, _srs_context->get_id(), req,
current->full_path, current->uri, m3u8, m3u8_url, current->fullpath(), current->uri, m3u8, m3u8_url,
current->sequence_no, current->duration))) != ERROR_SUCCESS) current->sequence_no, current->duration() / 1000.0))) != ERROR_SUCCESS)
{ {
return ret; return ret;
} }
@ -640,69 +592,35 @@ int SrsHlsMuxer::segment_close(string log_desc)
// close the muxer of finished segment. // close the muxer of finished segment.
srs_freep(current->tscw); srs_freep(current->tscw);
std::string full_path = current->full_path;
current = NULL;
// rename from tmp to real path // rename from tmp to real path
std::string tmp_file = full_path + ".tmp"; if ((ret = current->rename()) != ERROR_SUCCESS) {
if (rename(tmp_file.c_str(), full_path.c_str()) < 0) {
ret = ERROR_HLS_WRITE_FAILED;
srs_error("rename ts file failed, %s => %s. ret=%d",
tmp_file.c_str(), full_path.c_str(), ret);
return ret; return ret;
} }
segments->append(current);
current = NULL;
} else { } else {
// reuse current segment index. // reuse current segment index.
_sequence_no--; _sequence_no--;
srs_trace("%s drop ts segment, sequence_no=%d, uri=%s, duration=%.2f, start=%"PRId64"", srs_trace("Drop ts segment, sequence_no=%d, uri=%s, duration=%dms", current->sequence_no, current->uri.c_str(), current->duration());
log_desc.c_str(), current->sequence_no, current->uri.c_str(), current->duration,
current->segment_start_dts);
// rename from tmp to real path // rename from tmp to real path
std::string tmp_file = current->full_path + ".tmp"; if ((ret = current->unlink_tmpfile()) != ERROR_SUCCESS) {
if (unlink(tmp_file.c_str()) < 0) { return ret;
srs_warn("ignore unlink path failed, file=%s.", tmp_file.c_str());
} }
srs_freep(current); srs_freep(current);
} }
// the segments to remove
std::vector<SrsHlsSegment*> segment_to_remove;
// shrink the segments. // shrink the segments.
double duration = 0; segments->shrink(hls_window * 1000);
int remove_index = -1;
for (int i = (int)segments.size() - 1; i >= 0; i--) {
SrsHlsSegment* segment = segments[i];
duration += segment->duration;
if ((int)duration > hls_window) {
remove_index = i;
break;
}
}
for (int i = 0; i < remove_index && !segments.empty(); i++) {
SrsHlsSegment* segment = *segments.begin();
segments.erase(segments.begin());
segment_to_remove.push_back(segment);
}
// refresh the m3u8, donot contains the removed ts // refresh the m3u8, donot contains the removed ts
ret = refresh_m3u8(); ret = refresh_m3u8();
// remove the ts file. // remove the ts file.
for (int i = 0; i < (int)segment_to_remove.size(); i++) { segments->clear_expired(hls_cleanup);
SrsHlsSegment* segment = segment_to_remove[i];
if (hls_cleanup && unlink(segment->full_path.c_str()) < 0) {
srs_warn("cleanup unlink path failed, file=%s.", segment->full_path.c_str());
}
srs_freep(segment);
}
segment_to_remove.clear();
// check ret of refresh m3u8 // check ret of refresh m3u8
if (ret != ERROR_SUCCESS) { if (ret != ERROR_SUCCESS) {
@ -718,7 +636,7 @@ int SrsHlsMuxer::refresh_m3u8()
int ret = ERROR_SUCCESS; int ret = ERROR_SUCCESS;
// no segments, also no m3u8, return. // no segments, also no m3u8, return.
if (segments.size() == 0) { if (segments->empty()) {
return ret; return ret;
} }
@ -745,7 +663,7 @@ int SrsHlsMuxer::_refresh_m3u8(string m3u8_file)
int ret = ERROR_SUCCESS; int ret = ERROR_SUCCESS;
// no segments, return. // no segments, return.
if (segments.size() == 0) { if (segments->empty()) {
return ret; return ret;
} }
@ -766,7 +684,7 @@ int SrsHlsMuxer::_refresh_m3u8(string m3u8_file)
srs_verbose("write m3u8 header success."); srs_verbose("write m3u8 header success.");
// #EXT-X-MEDIA-SEQUENCE:4294967295\n // #EXT-X-MEDIA-SEQUENCE:4294967295\n
SrsHlsSegment* first = *segments.begin(); SrsHlsSegment* first = dynamic_cast<SrsHlsSegment*>(segments->first());
ss << "#EXT-X-MEDIA-SEQUENCE:" << first->sequence_no << SRS_CONSTS_LF; ss << "#EXT-X-MEDIA-SEQUENCE:" << first->sequence_no << SRS_CONSTS_LF;
srs_verbose("write m3u8 sequence success."); srs_verbose("write m3u8 sequence success.");
@ -783,21 +701,17 @@ int SrsHlsMuxer::_refresh_m3u8(string m3u8_file)
* typical target duration is 10 seconds. * typical target duration is 10 seconds.
*/ */
// @see https://github.com/ossrs/srs/issues/304#issuecomment-74000081 // @see https://github.com/ossrs/srs/issues/304#issuecomment-74000081
int target_duration = 0; int target_duration = (int)ceil(segments->max_duration() / 1000.0);
for (it = segments.begin(); it != segments.end(); ++it) {
SrsHlsSegment* segment = *it;
target_duration = srs_max(target_duration, (int)ceil(segment->duration));
}
target_duration = srs_max(target_duration, max_td); target_duration = srs_max(target_duration, max_td);
ss << "#EXT-X-TARGETDURATION:" << target_duration << SRS_CONSTS_LF; ss << "#EXT-X-TARGETDURATION:" << target_duration << SRS_CONSTS_LF;
srs_verbose("write m3u8 duration success."); srs_verbose("write m3u8 duration success.");
// write all segments // write all segments
for (it = segments.begin(); it != segments.end(); ++it) { for (int i = 0; i < segments->size(); i++) {
SrsHlsSegment* segment = *it; SrsHlsSegment* segment = dynamic_cast<SrsHlsSegment*>(segments->at(i));
if (segment->is_sequence_header) { if (segment->is_sequence_header()) {
// #EXT-X-DISCONTINUITY\n // #EXT-X-DISCONTINUITY\n
ss << "#EXT-X-DISCONTINUITY" << SRS_CONSTS_LF; ss << "#EXT-X-DISCONTINUITY" << SRS_CONSTS_LF;
srs_verbose("write m3u8 segment discontinuity success."); srs_verbose("write m3u8 segment discontinuity success.");
@ -806,7 +720,7 @@ int SrsHlsMuxer::_refresh_m3u8(string m3u8_file)
// "#EXTINF:4294967295.208,\n" // "#EXTINF:4294967295.208,\n"
ss.precision(3); ss.precision(3);
ss.setf(std::ios::fixed, std::ios::floatfield); ss.setf(std::ios::fixed, std::ios::floatfield);
ss << "#EXTINF:" << segment->duration << ", no desc" << SRS_CONSTS_LF; ss << "#EXTINF:" << segment->duration() / 1000.0 << ", no desc" << SRS_CONSTS_LF;
srs_verbose("write m3u8 segment info success."); srs_verbose("write m3u8 segment info success.");
// {file name}\n // {file name}\n
@ -867,7 +781,7 @@ int SrsHlsController::deviation()
return muxer->deviation(); return muxer->deviation();
} }
int SrsHlsController::on_publish(SrsRequest* req, int64_t segment_start_dts) int SrsHlsController::on_publish(SrsRequest* req)
{ {
int ret = ERROR_SUCCESS; int ret = ERROR_SUCCESS;
@ -905,7 +819,7 @@ int SrsHlsController::on_publish(SrsRequest* req, int64_t segment_start_dts)
return ret; return ret;
} }
if ((ret = muxer->segment_open(segment_start_dts)) != ERROR_SUCCESS) { if ((ret = muxer->segment_open()) != ERROR_SUCCESS) {
srs_error("m3u8 muxer open segment failed. ret=%d", ret); srs_error("m3u8 muxer open segment failed. ret=%d", ret);
return ret; return ret;
} }
@ -925,7 +839,7 @@ int SrsHlsController::on_unpublish()
return ret; return ret;
} }
if ((ret = muxer->segment_close("unpublish")) != ERROR_SUCCESS) { if ((ret = muxer->segment_close()) != ERROR_SUCCESS) {
return ret; return ret;
} }
@ -962,7 +876,7 @@ int SrsHlsController::write_audio(SrsAudioFrame* frame, int64_t pts)
// @see https://github.com/ossrs/srs/issues/151#issuecomment-71155184 // @see https://github.com/ossrs/srs/issues/151#issuecomment-71155184
if (tsmc->audio && muxer->is_segment_absolutely_overflow()) { if (tsmc->audio && muxer->is_segment_absolutely_overflow()) {
srs_info("hls: absolute audio reap segment."); srs_info("hls: absolute audio reap segment.");
if ((ret = reap_segment("audio", tsmc->audio->pts)) != ERROR_SUCCESS) { if ((ret = reap_segment()) != ERROR_SUCCESS) {
return ret; return ret;
} }
} }
@ -1001,7 +915,7 @@ int SrsHlsController::write_video(SrsVideoFrame* frame, int64_t dts)
// b. always reap when not wait keyframe. // b. always reap when not wait keyframe.
if (!muxer->wait_keyframe() || frame->frame_type == SrsVideoAvcFrameTypeKeyFrame) { if (!muxer->wait_keyframe() || frame->frame_type == SrsVideoAvcFrameTypeKeyFrame) {
// reap the segment, which will also flush the video. // reap the segment, which will also flush the video.
if ((ret = reap_segment("video", tsmc->video->dts)) != ERROR_SUCCESS) { if ((ret = reap_segment()) != ERROR_SUCCESS) {
return ret; return ret;
} }
} }
@ -1016,7 +930,7 @@ int SrsHlsController::write_video(SrsVideoFrame* frame, int64_t dts)
return ret; return ret;
} }
int SrsHlsController::reap_segment(string log_desc, int64_t segment_start_dts) int SrsHlsController::reap_segment()
{ {
int ret = ERROR_SUCCESS; int ret = ERROR_SUCCESS;
@ -1024,13 +938,13 @@ int SrsHlsController::reap_segment(string log_desc, int64_t segment_start_dts)
// TODO: fresh segment begin with audio or video? // TODO: fresh segment begin with audio or video?
// close current ts. // close current ts.
if ((ret = muxer->segment_close(log_desc)) != ERROR_SUCCESS) { if ((ret = muxer->segment_close()) != ERROR_SUCCESS) {
srs_error("m3u8 muxer close segment failed. ret=%d", ret); srs_error("m3u8 muxer close segment failed. ret=%d", ret);
return ret; return ret;
} }
// open new ts. // open new ts.
if ((ret = muxer->segment_open(segment_start_dts)) != ERROR_SUCCESS) { if ((ret = muxer->segment_open()) != ERROR_SUCCESS) {
srs_error("m3u8 muxer open segment failed. ret=%d", ret); srs_error("m3u8 muxer open segment failed. ret=%d", ret);
return ret; return ret;
} }
@ -1065,7 +979,6 @@ SrsHls::SrsHls()
controller = new SrsHlsController(); controller = new SrsHlsController();
pprint = SrsPithyPrint::create_hls(); pprint = SrsPithyPrint::create_hls();
stream_dts = 0;
} }
SrsHls::~SrsHls() SrsHls::~SrsHls()
@ -1148,7 +1061,7 @@ int SrsHls::on_publish()
return ret; return ret;
} }
if ((ret = controller->on_publish(req, stream_dts)) != ERROR_SUCCESS) { if ((ret = controller->on_publish(req)) != ERROR_SUCCESS) {
return ret; return ret;
} }
@ -1213,9 +1126,6 @@ int SrsHls::on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format)
// the dts calc from rtmp/flv header. // the dts calc from rtmp/flv header.
int64_t dts = audio->timestamp * 90; int64_t dts = audio->timestamp * 90;
// for pure audio, we need to update the stream dts also.
stream_dts = dts;
if ((ret = controller->write_audio(format->audio, dts)) != ERROR_SUCCESS) { if ((ret = controller->write_audio(format->audio, dts)) != ERROR_SUCCESS) {
srs_error("hls cache write audio failed. ret=%d", ret); srs_error("hls cache write audio failed. ret=%d", ret);
return ret; return ret;
@ -1262,7 +1172,6 @@ int SrsHls::on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format)
} }
int64_t dts = video->timestamp * 90; int64_t dts = video->timestamp * 90;
stream_dts = dts;
if ((ret = controller->write_video(format->video, dts)) != ERROR_SUCCESS) { if ((ret = controller->write_video(format->video, dts)) != ERROR_SUCCESS) {
srs_error("hls cache write video failed. ret=%d", ret); srs_error("hls cache write video failed. ret=%d", ret);
return ret; return ret;
@ -1285,8 +1194,8 @@ void SrsHls::hls_show_mux_log()
// the run time is not equals to stream time, // the run time is not equals to stream time,
// @see: https://github.com/ossrs/srs/issues/81#issuecomment-48100994 // @see: https://github.com/ossrs/srs/issues/81#issuecomment-48100994
// it's ok. // it's ok.
srs_trace("-> "SRS_CONSTS_LOG_HLS" time=%"PRId64", stream dts=%"PRId64"(%"PRId64"ms), sno=%d, ts=%s, dur=%.2f, dva=%dp", srs_trace("-> "SRS_CONSTS_LOG_HLS" time=%"PRId64", sno=%d, ts=%s, dur=%.2f, dva=%dp",
pprint->age(), stream_dts, stream_dts / 90, controller->sequence_no(), controller->ts_url().c_str(), pprint->age(), controller->sequence_no(), controller->ts_url().c_str(),
controller->duration(), controller->deviation()); controller->duration(), controller->deviation());
} }

View file

@ -35,6 +35,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_kernel_codec.hpp> #include <srs_kernel_codec.hpp>
#include <srs_kernel_file.hpp> #include <srs_kernel_file.hpp>
#include <srs_app_async_call.hpp> #include <srs_app_async_call.hpp>
#include <srs_app_fragment.hpp>
class SrsFormat; class SrsFormat;
class SrsSharedPtrMessage; class SrsSharedPtrMessage;
@ -58,34 +59,20 @@ class SrsTsContext;
* 3.3.2. EXTINF * 3.3.2. EXTINF
* The EXTINF tag specifies the duration of a media segment. * The EXTINF tag specifies the duration of a media segment.
*/ */
class SrsHlsSegment class SrsHlsSegment : public SrsFragment
{ {
public: public:
// duration in seconds in m3u8.
double duration;
// sequence number in m3u8. // sequence number in m3u8.
int sequence_no; int sequence_no;
// ts uri in m3u8. // ts uri in m3u8.
std::string uri; std::string uri;
// ts full file to write.
std::string full_path;
// the underlayer file writer. // the underlayer file writer.
SrsFileWriter* writer; SrsFileWriter* writer;
// The TS context writer to write TS to file. // The TS context writer to write TS to file.
SrsTsContextWriter* tscw; SrsTsContextWriter* tscw;
// current segment start dts for m3u8
int64_t segment_start_dts;
// whether current segement is sequence header.
bool is_sequence_header;
public: public:
SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc); SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc);
virtual ~SrsHlsSegment(); virtual ~SrsHlsSegment();
public:
/**
* update the segment duration.
* @current_frame_dts the dts of frame, in tbn of ts.
*/
virtual void update_duration(int64_t current_frame_dts);
}; };
/** /**
@ -103,6 +90,7 @@ private:
SrsRequest* req; SrsRequest* req;
double duration; double duration;
public: public:
// TODO: FIXME: Use TBN 1000.
SrsDvrAsyncCallOnHls(int c, SrsRequest* r, std::string p, std::string t, std::string m, std::string mu, int s, double d); SrsDvrAsyncCallOnHls(int c, SrsRequest* r, std::string p, std::string t, std::string m, std::string mu, int s, double d);
virtual ~SrsDvrAsyncCallOnHls(); virtual ~SrsDvrAsyncCallOnHls();
public: public:
@ -147,6 +135,7 @@ private:
bool hls_wait_keyframe; bool hls_wait_keyframe;
std::string m3u8_dir; std::string m3u8_dir;
double hls_aof_ratio; double hls_aof_ratio;
// TODO: FIXME: Use TBN 1000.
double hls_fragment; double hls_fragment;
double hls_window; double hls_window;
SrsAsyncCallWorker* async; SrsAsyncCallWorker* async;
@ -166,13 +155,9 @@ private:
std::string m3u8; std::string m3u8;
std::string m3u8_url; std::string m3u8_url;
private: private:
/** // The available cached segments in m3u8.
* m3u8 segments. SrsFragmentWindow* segments;
*/ // The current writing segment.
std::vector<SrsHlsSegment*> segments;
/**
* current writing segment.
*/
SrsHlsSegment* current; SrsHlsSegment* current;
/** /**
* the ts context, to keep cc continous between ts. * the ts context, to keep cc continous between ts.
@ -202,11 +187,9 @@ public:
double fragment, double window, bool ts_floor, double aof_ratio, double fragment, double window, bool ts_floor, double aof_ratio,
bool cleanup, bool wait_keyframe); bool cleanup, bool wait_keyframe);
/** /**
* open a new segment(a new ts file), * open a new segment(a new ts file)
* @param segment_start_dts use to calc the segment duration, */
* use 0 for the first segment of HLS. virtual int segment_open();
*/
virtual int segment_open(int64_t segment_start_dts);
virtual int on_sequence_header(); virtual int on_sequence_header();
/** /**
* whether segment overflow, * whether segment overflow,
@ -231,10 +214,9 @@ public:
virtual int flush_audio(SrsTsMessageCache* cache); virtual int flush_audio(SrsTsMessageCache* cache);
virtual int flush_video(SrsTsMessageCache* cache); virtual int flush_video(SrsTsMessageCache* cache);
/** /**
* close segment(ts). * Close segment(ts).
* @param log_desc the description for log. */
*/ virtual int segment_close();
virtual int segment_close(std::string log_desc);
private: private:
virtual int refresh_m3u8(); virtual int refresh_m3u8();
virtual int _refresh_m3u8(std::string m3u8_file); virtual int _refresh_m3u8(std::string m3u8_file);
@ -279,7 +261,7 @@ public:
/** /**
* when publish or unpublish stream. * when publish or unpublish stream.
*/ */
virtual int on_publish(SrsRequest* req, int64_t segment_start_dts); virtual int on_publish(SrsRequest* req);
virtual int on_unpublish(); virtual int on_unpublish();
/** /**
* when get sequence header, * when get sequence header,
@ -303,7 +285,7 @@ private:
* then write the key frame to the new segment. * then write the key frame to the new segment.
* so, user must reap_segment then flush_video to hls muxer. * so, user must reap_segment then flush_video to hls muxer.
*/ */
virtual int reap_segment(std::string log_desc, int64_t segment_start_dts); virtual int reap_segment();
}; };
/** /**
@ -323,20 +305,6 @@ private:
SrsOriginHub* hub; SrsOriginHub* hub;
SrsRtmpJitter* jitter; SrsRtmpJitter* jitter;
SrsPithyPrint* pprint; SrsPithyPrint* pprint;
/**
* we store the stream dts,
* for when we notice the hls cache to publish,
* it need to know the segment start dts.
*
* for example. when republish, the stream dts will
* monotonically increase, and the ts dts should start
* from current dts.
*
* or, simply because the HlsCache never free when unpublish,
* so when publish or republish it must start at stream dts,
* not zero dts.
*/
int64_t stream_dts;
public: public:
SrsHls(); SrsHls();
virtual ~SrsHls(); virtual ~SrsHls();

View file

@ -105,6 +105,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define ERROR_SYSTEM_FILE_NOT_EXISTS 1064 #define ERROR_SYSTEM_FILE_NOT_EXISTS 1064
#define ERROR_SYSTEM_HOURGLASS_RESOLUTION 1065 #define ERROR_SYSTEM_HOURGLASS_RESOLUTION 1065
#define ERROR_SYSTEM_DNS_RESOLVE 1066 #define ERROR_SYSTEM_DNS_RESOLVE 1066
#define ERROR_SYSTEM_FRAGMENT_UNLINK 1067
#define ERROR_SYSTEM_FRAGMENT_RENAME 1068
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
// RTMP protocol error. // RTMP protocol error.