mirror of
https://github.com/ossrs/srs.git
synced 2025-02-12 19:31:53 +00:00
For #299, extract fragment and fragment window for hls
This commit is contained in:
parent
6da6e0511d
commit
da4c390d69
9 changed files with 450 additions and 192 deletions
2
trunk/configure
vendored
2
trunk/configure
vendored
|
@ -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_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_hourglass" "srs_app_dash")
|
||||
"srs_app_hourglass" "srs_app_dash" "srs_app_fragment")
|
||||
DEFINES=""
|
||||
# add each modules for app
|
||||
for SRS_MODULE in ${SRS_MODULES[*]}; do
|
||||
|
|
|
@ -84,6 +84,7 @@
|
|||
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 */; };
|
||||
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 */; };
|
||||
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 */; };
|
||||
|
@ -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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
|
@ -609,6 +612,8 @@
|
|||
3C12325B1AAE81D900CE8F6C /* srs_app_ffmpeg.hpp */,
|
||||
3C12325C1AAE81D900CE8F6C /* srs_app_forward.cpp */,
|
||||
3C12325D1AAE81D900CE8F6C /* srs_app_forward.hpp */,
|
||||
3C4D184A1E73F133008806F7 /* srs_app_fragment.cpp */,
|
||||
3C4D184B1E73F133008806F7 /* srs_app_fragment.hpp */,
|
||||
3C1EE6AC1AB1055800576EE9 /* srs_app_hds.cpp */,
|
||||
3C1EE6AD1AB1055800576EE9 /* srs_app_hds.hpp */,
|
||||
3C12325E1AAE81D900CE8F6C /* srs_app_heartbeat.cpp */,
|
||||
|
@ -1025,6 +1030,7 @@
|
|||
3CA432AB1E40AEBC001DA0C6 /* Makefile in Sources */,
|
||||
3C1232B21AAE81D900CE8F6C /* srs_app_source.cpp in Sources */,
|
||||
3C1231F71AAE652D00CE8F6C /* srs_core_performance.cpp in Sources */,
|
||||
3C4D184C1E73F133008806F7 /* srs_app_fragment.cpp in Sources */,
|
||||
3CC52DD81ACE4023006FEB01 /* srs_utest_amf0.cpp in Sources */,
|
||||
3C4F97121B8B466D00FF0E46 /* srs_app_process.cpp in Sources */,
|
||||
3C1232981AAE81D900CE8F6C /* srs_app_edge.cpp in Sources */,
|
||||
|
|
|
@ -158,6 +158,13 @@ SrsDashController::SrsDashController()
|
|||
SrsDashController::~SrsDashController()
|
||||
{
|
||||
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)
|
||||
|
|
|
@ -30,6 +30,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
#include <srs_core.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <srs_app_fragment.hpp>
|
||||
|
||||
class SrsRequest;
|
||||
class SrsOriginHub;
|
||||
|
@ -39,7 +42,7 @@ class SrsFormat;
|
|||
/**
|
||||
* The FMP4(Fragmented MP4) for DASH streaming.
|
||||
*/
|
||||
class SrsFragmentedMp4
|
||||
class SrsFragmentedMp4 : public SrsFragment
|
||||
{
|
||||
public:
|
||||
SrsFragmentedMp4();
|
||||
|
@ -82,6 +85,7 @@ class SrsDashController
|
|||
private:
|
||||
SrsRequest* req;
|
||||
SrsMpdWriter* mpd;
|
||||
std::vector<SrsFragmentedMp4*> fragments;
|
||||
private:
|
||||
std::string home;
|
||||
int video_tack_id;
|
||||
|
|
252
trunk/src/app/srs_app_fragment.cpp
Normal file
252
trunk/src/app/srs_app_fragment.cpp
Normal 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);
|
||||
}
|
||||
|
110
trunk/src/app/srs_app_fragment.hpp
Normal file
110
trunk/src/app/srs_app_fragment.hpp
Normal 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
|
||||
|
|
@ -54,8 +54,6 @@ using namespace std;
|
|||
|
||||
// drop the segment when duration of ts too small.
|
||||
#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.
|
||||
#define SRS_HLS_FLOOR_REAP_PERCENT 0.3
|
||||
|
@ -64,10 +62,7 @@ using namespace std;
|
|||
|
||||
SrsHlsSegment::SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc)
|
||||
{
|
||||
duration = 0;
|
||||
sequence_no = 0;
|
||||
segment_start_dts = 0;
|
||||
is_sequence_header = false;
|
||||
writer = new SrsFileWriter();
|
||||
tscw = new SrsTsContextWriter(writer, c, ac, vc);
|
||||
}
|
||||
|
@ -78,27 +73,6 @@ SrsHlsSegment::~SrsHlsSegment()
|
|||
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)
|
||||
{
|
||||
req = r->copy();
|
||||
|
@ -225,17 +199,11 @@ SrsHlsMuxer::SrsHlsMuxer()
|
|||
current = NULL;
|
||||
async = new SrsAsyncCallWorker();
|
||||
context = new SrsTsContext();
|
||||
segments = new SrsFragmentWindow();
|
||||
}
|
||||
|
||||
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(req);
|
||||
srs_freep(async);
|
||||
|
@ -244,20 +212,13 @@ SrsHlsMuxer::~SrsHlsMuxer()
|
|||
|
||||
void SrsHlsMuxer::dispose()
|
||||
{
|
||||
std::vector<SrsHlsSegment*>::iterator it;
|
||||
for (it = segments.begin(); it != segments.end(); ++it) {
|
||||
SrsHlsSegment* segment = *it;
|
||||
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();
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
segments->dispose();
|
||||
|
||||
if (current) {
|
||||
std::string path = current->full_path + ".tmp";
|
||||
if (unlink(path.c_str()) < 0) {
|
||||
srs_warn("dispose unlink path failed, file=%s", path.c_str());
|
||||
if ((ret = current->unlink_tmpfile()) != ERROR_SUCCESS) {
|
||||
srs_warn("Unlink tmp ts failed, ret=%d", ret);
|
||||
}
|
||||
srs_freep(current);
|
||||
}
|
||||
|
@ -266,8 +227,6 @@ void SrsHlsMuxer::dispose()
|
|||
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() : "");
|
||||
}
|
||||
|
||||
|
@ -283,7 +242,7 @@ string SrsHlsMuxer::ts_url()
|
|||
|
||||
double SrsHlsMuxer::duration()
|
||||
{
|
||||
return current? current->duration:0;
|
||||
return current? current->duration()/1000.0:0;
|
||||
}
|
||||
|
||||
int SrsHlsMuxer::deviation()
|
||||
|
@ -347,7 +306,7 @@ int SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int SrsHlsMuxer::segment_open(int64_t segment_start_dts)
|
||||
int SrsHlsMuxer::segment_open()
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
|
@ -395,7 +354,6 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts)
|
|||
// new segment.
|
||||
current = new SrsHlsSegment(context, default_acodec, default_vcodec);
|
||||
current->sequence_no = _sequence_no++;
|
||||
current->segment_start_dts = segment_start_dts;
|
||||
|
||||
// generate filename.
|
||||
std::string ts_file = hls_ts_file;
|
||||
|
@ -440,11 +398,12 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts)
|
|||
ss << current->sequence_no;
|
||||
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);
|
||||
|
||||
// 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)) {
|
||||
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;
|
||||
|
||||
// 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) {
|
||||
srs_error("create app dir %s failed. ret=%d", ts_dir.c_str(), 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());
|
||||
|
||||
// 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) {
|
||||
srs_error("open hls muxer failed. ret=%d", ret);
|
||||
return ret;
|
||||
|
@ -490,7 +449,7 @@ int SrsHlsMuxer::on_sequence_header()
|
|||
|
||||
// set the current segment to sequence header,
|
||||
// when close the segement, it will write a discontinuity to m3u8 file.
|
||||
current->is_sequence_header = true;
|
||||
current->set_sequence_header(true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -500,7 +459,7 @@ bool SrsHlsMuxer::is_segment_overflow()
|
|||
srs_assert(current);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -509,7 +468,7 @@ bool SrsHlsMuxer::is_segment_overflow()
|
|||
srs_info("hls: dur=%.2f, tar=%.2f, dev=%.2fms/%dp, frag=%.2f",
|
||||
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()
|
||||
|
@ -523,7 +482,7 @@ bool SrsHlsMuxer::is_segment_absolutely_overflow()
|
|||
srs_assert(current);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -532,7 +491,7 @@ bool SrsHlsMuxer::is_segment_absolutely_overflow()
|
|||
srs_info("hls: dur=%.2f, tar=%.2f, dev=%.2fms/%dp, frag=%.2f",
|
||||
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()
|
||||
|
@ -555,7 +514,7 @@ int SrsHlsMuxer::flush_audio(SrsTsMessageCache* cache)
|
|||
}
|
||||
|
||||
// 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) {
|
||||
return ret;
|
||||
|
@ -584,7 +543,7 @@ int SrsHlsMuxer::flush_video(SrsTsMessageCache* cache)
|
|||
srs_assert(current);
|
||||
|
||||
// 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) {
|
||||
return ret;
|
||||
|
@ -596,7 +555,7 @@ int SrsHlsMuxer::flush_video(SrsTsMessageCache* cache)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int SrsHlsMuxer::segment_close(string log_desc)
|
||||
int SrsHlsMuxer::segment_close()
|
||||
{
|
||||
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.
|
||||
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
|
||||
// when too small, it maybe not enough data to play.
|
||||
// when too large, it maybe timestamp corrupt.
|
||||
// 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) {
|
||||
segments.push_back(current);
|
||||
|
||||
if (current->duration() >= SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS && (int)current->duration() <= max_td * 2 * 1000) {
|
||||
// use async to call the http hooks, for it will cause thread switch.
|
||||
if ((ret = async->execute(new SrsDvrAsyncCallOnHls(
|
||||
_srs_context->get_id(), req,
|
||||
current->full_path, current->uri, m3u8, m3u8_url,
|
||||
current->sequence_no, current->duration))) != ERROR_SUCCESS)
|
||||
current->fullpath(), current->uri, m3u8, m3u8_url,
|
||||
current->sequence_no, current->duration() / 1000.0))) != ERROR_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
@ -640,69 +592,35 @@ int SrsHlsMuxer::segment_close(string log_desc)
|
|||
|
||||
// close the muxer of finished segment.
|
||||
srs_freep(current->tscw);
|
||||
std::string full_path = current->full_path;
|
||||
current = NULL;
|
||||
|
||||
// rename from tmp to real path
|
||||
std::string tmp_file = full_path + ".tmp";
|
||||
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);
|
||||
if ((ret = current->rename()) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
segments->append(current);
|
||||
current = NULL;
|
||||
} else {
|
||||
// reuse current segment index.
|
||||
_sequence_no--;
|
||||
|
||||
srs_trace("%s drop ts segment, sequence_no=%d, uri=%s, duration=%.2f, start=%"PRId64"",
|
||||
log_desc.c_str(), current->sequence_no, current->uri.c_str(), current->duration,
|
||||
current->segment_start_dts);
|
||||
srs_trace("Drop ts segment, sequence_no=%d, uri=%s, duration=%dms", current->sequence_no, current->uri.c_str(), current->duration());
|
||||
|
||||
// rename from tmp to real path
|
||||
std::string tmp_file = current->full_path + ".tmp";
|
||||
if (unlink(tmp_file.c_str()) < 0) {
|
||||
srs_warn("ignore unlink path failed, file=%s.", tmp_file.c_str());
|
||||
if ((ret = current->unlink_tmpfile()) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
srs_freep(current);
|
||||
}
|
||||
|
||||
// the segments to remove
|
||||
std::vector<SrsHlsSegment*> segment_to_remove;
|
||||
|
||||
// shrink the segments.
|
||||
double duration = 0;
|
||||
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);
|
||||
}
|
||||
segments->shrink(hls_window * 1000);
|
||||
|
||||
// refresh the m3u8, donot contains the removed ts
|
||||
ret = refresh_m3u8();
|
||||
|
||||
// remove the ts file.
|
||||
for (int i = 0; i < (int)segment_to_remove.size(); i++) {
|
||||
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();
|
||||
segments->clear_expired(hls_cleanup);
|
||||
|
||||
// check ret of refresh m3u8
|
||||
if (ret != ERROR_SUCCESS) {
|
||||
|
@ -718,7 +636,7 @@ int SrsHlsMuxer::refresh_m3u8()
|
|||
int ret = ERROR_SUCCESS;
|
||||
|
||||
// no segments, also no m3u8, return.
|
||||
if (segments.size() == 0) {
|
||||
if (segments->empty()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -745,7 +663,7 @@ int SrsHlsMuxer::_refresh_m3u8(string m3u8_file)
|
|||
int ret = ERROR_SUCCESS;
|
||||
|
||||
// no segments, return.
|
||||
if (segments.size() == 0) {
|
||||
if (segments->empty()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -766,7 +684,7 @@ int SrsHlsMuxer::_refresh_m3u8(string m3u8_file)
|
|||
srs_verbose("write m3u8 header success.");
|
||||
|
||||
// #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;
|
||||
srs_verbose("write m3u8 sequence success.");
|
||||
|
||||
|
@ -783,21 +701,17 @@ int SrsHlsMuxer::_refresh_m3u8(string m3u8_file)
|
|||
* typical target duration is 10 seconds.
|
||||
*/
|
||||
// @see https://github.com/ossrs/srs/issues/304#issuecomment-74000081
|
||||
int target_duration = 0;
|
||||
for (it = segments.begin(); it != segments.end(); ++it) {
|
||||
SrsHlsSegment* segment = *it;
|
||||
target_duration = srs_max(target_duration, (int)ceil(segment->duration));
|
||||
}
|
||||
int target_duration = (int)ceil(segments->max_duration() / 1000.0);
|
||||
target_duration = srs_max(target_duration, max_td);
|
||||
|
||||
ss << "#EXT-X-TARGETDURATION:" << target_duration << SRS_CONSTS_LF;
|
||||
srs_verbose("write m3u8 duration success.");
|
||||
|
||||
// write all segments
|
||||
for (it = segments.begin(); it != segments.end(); ++it) {
|
||||
SrsHlsSegment* segment = *it;
|
||||
for (int i = 0; i < segments->size(); i++) {
|
||||
SrsHlsSegment* segment = dynamic_cast<SrsHlsSegment*>(segments->at(i));
|
||||
|
||||
if (segment->is_sequence_header) {
|
||||
if (segment->is_sequence_header()) {
|
||||
// #EXT-X-DISCONTINUITY\n
|
||||
ss << "#EXT-X-DISCONTINUITY" << SRS_CONSTS_LF;
|
||||
srs_verbose("write m3u8 segment discontinuity success.");
|
||||
|
@ -806,7 +720,7 @@ int SrsHlsMuxer::_refresh_m3u8(string m3u8_file)
|
|||
// "#EXTINF:4294967295.208,\n"
|
||||
ss.precision(3);
|
||||
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.");
|
||||
|
||||
// {file name}\n
|
||||
|
@ -867,7 +781,7 @@ int SrsHlsController::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;
|
||||
|
||||
|
@ -905,7 +819,7 @@ int SrsHlsController::on_publish(SrsRequest* req, int64_t segment_start_dts)
|
|||
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);
|
||||
return ret;
|
||||
}
|
||||
|
@ -925,7 +839,7 @@ int SrsHlsController::on_unpublish()
|
|||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = muxer->segment_close("unpublish")) != ERROR_SUCCESS) {
|
||||
if ((ret = muxer->segment_close()) != ERROR_SUCCESS) {
|
||||
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
|
||||
if (tsmc->audio && muxer->is_segment_absolutely_overflow()) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -1001,7 +915,7 @@ int SrsHlsController::write_video(SrsVideoFrame* frame, int64_t dts)
|
|||
// b. always reap when not wait keyframe.
|
||||
if (!muxer->wait_keyframe() || frame->frame_type == SrsVideoAvcFrameTypeKeyFrame) {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
@ -1016,7 +930,7 @@ int SrsHlsController::write_video(SrsVideoFrame* frame, int64_t dts)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int SrsHlsController::reap_segment(string log_desc, int64_t segment_start_dts)
|
||||
int SrsHlsController::reap_segment()
|
||||
{
|
||||
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?
|
||||
|
||||
// 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);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// 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);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1065,7 +979,6 @@ SrsHls::SrsHls()
|
|||
controller = new SrsHlsController();
|
||||
|
||||
pprint = SrsPithyPrint::create_hls();
|
||||
stream_dts = 0;
|
||||
}
|
||||
|
||||
SrsHls::~SrsHls()
|
||||
|
@ -1148,7 +1061,7 @@ int SrsHls::on_publish()
|
|||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = controller->on_publish(req, stream_dts)) != ERROR_SUCCESS) {
|
||||
if ((ret = controller->on_publish(req)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1213,9 +1126,6 @@ int SrsHls::on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format)
|
|||
// the dts calc from rtmp/flv header.
|
||||
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) {
|
||||
srs_error("hls cache write audio failed. ret=%d", ret);
|
||||
return ret;
|
||||
|
@ -1262,7 +1172,6 @@ int SrsHls::on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format)
|
|||
}
|
||||
|
||||
int64_t dts = video->timestamp * 90;
|
||||
stream_dts = dts;
|
||||
if ((ret = controller->write_video(format->video, dts)) != ERROR_SUCCESS) {
|
||||
srs_error("hls cache write video failed. ret=%d", ret);
|
||||
return ret;
|
||||
|
@ -1285,8 +1194,8 @@ void SrsHls::hls_show_mux_log()
|
|||
// the run time is not equals to stream time,
|
||||
// @see: https://github.com/ossrs/srs/issues/81#issuecomment-48100994
|
||||
// it's ok.
|
||||
srs_trace("-> "SRS_CONSTS_LOG_HLS" time=%"PRId64", stream dts=%"PRId64"(%"PRId64"ms), sno=%d, ts=%s, dur=%.2f, dva=%dp",
|
||||
pprint->age(), stream_dts, stream_dts / 90, controller->sequence_no(), controller->ts_url().c_str(),
|
||||
srs_trace("-> "SRS_CONSTS_LOG_HLS" time=%"PRId64", sno=%d, ts=%s, dur=%.2f, dva=%dp",
|
||||
pprint->age(), controller->sequence_no(), controller->ts_url().c_str(),
|
||||
controller->duration(), controller->deviation());
|
||||
}
|
||||
|
||||
|
|
|
@ -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_file.hpp>
|
||||
#include <srs_app_async_call.hpp>
|
||||
#include <srs_app_fragment.hpp>
|
||||
|
||||
class SrsFormat;
|
||||
class SrsSharedPtrMessage;
|
||||
|
@ -58,34 +59,20 @@ class SrsTsContext;
|
|||
* 3.3.2. EXTINF
|
||||
* The EXTINF tag specifies the duration of a media segment.
|
||||
*/
|
||||
class SrsHlsSegment
|
||||
class SrsHlsSegment : public SrsFragment
|
||||
{
|
||||
public:
|
||||
// duration in seconds in m3u8.
|
||||
double duration;
|
||||
// sequence number in m3u8.
|
||||
int sequence_no;
|
||||
// ts uri in m3u8.
|
||||
std::string uri;
|
||||
// ts full file to write.
|
||||
std::string full_path;
|
||||
// the underlayer file writer.
|
||||
SrsFileWriter* writer;
|
||||
// The TS context writer to write TS to file.
|
||||
SrsTsContextWriter* tscw;
|
||||
// current segment start dts for m3u8
|
||||
int64_t segment_start_dts;
|
||||
// whether current segement is sequence header.
|
||||
bool is_sequence_header;
|
||||
public:
|
||||
SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc);
|
||||
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;
|
||||
double duration;
|
||||
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);
|
||||
virtual ~SrsDvrAsyncCallOnHls();
|
||||
public:
|
||||
|
@ -147,6 +135,7 @@ private:
|
|||
bool hls_wait_keyframe;
|
||||
std::string m3u8_dir;
|
||||
double hls_aof_ratio;
|
||||
// TODO: FIXME: Use TBN 1000.
|
||||
double hls_fragment;
|
||||
double hls_window;
|
||||
SrsAsyncCallWorker* async;
|
||||
|
@ -166,13 +155,9 @@ private:
|
|||
std::string m3u8;
|
||||
std::string m3u8_url;
|
||||
private:
|
||||
/**
|
||||
* m3u8 segments.
|
||||
*/
|
||||
std::vector<SrsHlsSegment*> segments;
|
||||
/**
|
||||
* current writing segment.
|
||||
*/
|
||||
// The available cached segments in m3u8.
|
||||
SrsFragmentWindow* segments;
|
||||
// The current writing segment.
|
||||
SrsHlsSegment* current;
|
||||
/**
|
||||
* the ts context, to keep cc continous between ts.
|
||||
|
@ -202,11 +187,9 @@ public:
|
|||
double fragment, double window, bool ts_floor, double aof_ratio,
|
||||
bool cleanup, bool wait_keyframe);
|
||||
/**
|
||||
* 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(int64_t segment_start_dts);
|
||||
* open a new segment(a new ts file)
|
||||
*/
|
||||
virtual int segment_open();
|
||||
virtual int on_sequence_header();
|
||||
/**
|
||||
* whether segment overflow,
|
||||
|
@ -231,10 +214,9 @@ public:
|
|||
virtual int flush_audio(SrsTsMessageCache* cache);
|
||||
virtual int flush_video(SrsTsMessageCache* cache);
|
||||
/**
|
||||
* close segment(ts).
|
||||
* @param log_desc the description for log.
|
||||
*/
|
||||
virtual int segment_close(std::string log_desc);
|
||||
* Close segment(ts).
|
||||
*/
|
||||
virtual int segment_close();
|
||||
private:
|
||||
virtual int refresh_m3u8();
|
||||
virtual int _refresh_m3u8(std::string m3u8_file);
|
||||
|
@ -279,7 +261,7 @@ public:
|
|||
/**
|
||||
* 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();
|
||||
/**
|
||||
* when get sequence header,
|
||||
|
@ -303,7 +285,7 @@ private:
|
|||
* then write the key frame to the new segment.
|
||||
* 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;
|
||||
SrsRtmpJitter* jitter;
|
||||
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:
|
||||
SrsHls();
|
||||
virtual ~SrsHls();
|
||||
|
|
|
@ -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_HOURGLASS_RESOLUTION 1065
|
||||
#define ERROR_SYSTEM_DNS_RESOLVE 1066
|
||||
#define ERROR_SYSTEM_FRAGMENT_UNLINK 1067
|
||||
#define ERROR_SYSTEM_FRAGMENT_RENAME 1068
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// RTMP protocol error.
|
||||
|
|
Loading…
Reference in a new issue