mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
refine the hls module, clear logic dead code, rename TSCache to HlsCache, M3u8Muxer to HlsMuxer. that is, make it to more readable.
This commit is contained in:
parent
6bc18f2e44
commit
b708f588fc
3 changed files with 241 additions and 162 deletions
|
@ -212,7 +212,7 @@ if [ $SRS_ARM_UBUNTU12 = YES ]; then
|
|||
unzip -q ../3rdparty/st-1.9.zip && cd st-1.9 &&
|
||||
patch -p0 < ../../3rdparty/patches/1.st.arm.patch &&
|
||||
make CC=${SrsArmCC} AR=${SrsArmAR} LD=${SrsArmLD} RANDLIB=${SrsArmRANDLIB} linux-debug &&
|
||||
cd .. && rm -f st && ln -sf st-1.9/obj st &&
|
||||
cd .. && rm -rf st && ln -sf st-1.9/obj st &&
|
||||
cd .. && touch ${SRS_OBJS}/_flag.st.arm.tmp
|
||||
)
|
||||
fi
|
||||
|
@ -226,7 +226,7 @@ else
|
|||
rm -rf ${SRS_OBJS}/st-1.9 && cd ${SRS_OBJS} &&
|
||||
unzip -q ../3rdparty/st-1.9.zip && cd st-1.9 &&
|
||||
make linux-debug &&
|
||||
cd .. && rm -f st && ln -sf st-1.9/obj st &&
|
||||
cd .. && rm -rf st && ln -sf st-1.9/obj st &&
|
||||
cd .. && rm -f ${SRS_OBJS}/_flag.st.arm.tmp
|
||||
)
|
||||
fi
|
||||
|
@ -250,7 +250,7 @@ if [ $SRS_HTTP_CALLBACK = YES ]; then
|
|||
sed -i "s/CPPFLAGS_FAST +=.*$/CPPFLAGS_FAST = \$\(CPPFLAGS_DEBUG\)/g" Makefile &&
|
||||
sed -i "s/CFLAGS_FAST =.*$/CFLAGS_FAST = \$\(CFLAGS_DEBUG\)/g" Makefile &&
|
||||
make package &&
|
||||
cd .. && rm -f hp && ln -sf http-parser-2.1 hp
|
||||
cd .. && rm -rf hp && ln -sf http-parser-2.1 hp
|
||||
)
|
||||
fi
|
||||
# check status
|
||||
|
@ -282,7 +282,7 @@ if [ $__SRS_BUILD_NGINX = YES ]; then
|
|||
rm -rf ${SRS_OBJS}/nginx-1.5.7 && cd ${SRS_OBJS} &&
|
||||
unzip -q ../3rdparty/nginx-1.5.7.zip && cd nginx-1.5.7 &&
|
||||
./configure --prefix=`pwd`/_release && make ${SRS_JOBS} && make install &&
|
||||
cd .. && ln -sf nginx-1.5.7/_release nginx
|
||||
cd .. && rm -rf nginx && ln -sf nginx-1.5.7/_release nginx
|
||||
)
|
||||
fi
|
||||
# check status
|
||||
|
@ -356,14 +356,14 @@ else
|
|||
fi
|
||||
|
||||
echo "link players to cherrypy static-dir"
|
||||
rm -f research/api-server/static-dir/players &&
|
||||
rm -rf research/api-server/static-dir/players &&
|
||||
ln -sf `pwd`/research/players research/api-server/static-dir/players &&
|
||||
rm -f research/api-server/static-dir/crossdomain.xml &&
|
||||
ln -sf `pwd`/research/players/crossdomain.xml research/api-server/static-dir/crossdomain.xml &&
|
||||
rm -f research/api-server/static-dir/live &&
|
||||
rm -rf research/api-server/static-dir/live &&
|
||||
mkdir -p `pwd`/${SRS_OBJS}/nginx/html/live &&
|
||||
ln -sf `pwd`/${SRS_OBJS}/nginx/html/live research/api-server/static-dir/live &&
|
||||
rm -f research/api-server/static-dir/forward &&
|
||||
rm -rf research/api-server/static-dir/forward &&
|
||||
mkdir -p `pwd`/${SRS_OBJS}/nginx/html/forward &&
|
||||
ln -sf `pwd`/${SRS_OBJS}/nginx/html/forward research/api-server/static-dir/forward
|
||||
|
||||
|
@ -410,7 +410,7 @@ if [ $SRS_SSL = YES ]; then
|
|||
unzip -q ../3rdparty/openssl-1.0.1f.zip && cd openssl-1.0.1f &&
|
||||
./Configure --prefix=`pwd`/_release -no-shared no-asm linux-armv4 &&
|
||||
make CC=${SrsArmCC} GCC=${SrsArmGCC} AR="${SrsArmAR} r" LD=${SrsArmLD} LINK=${SrsArmGCC} RANDLIB=${SrsArmRANDLIB} && make install &&
|
||||
cd .. && ln -sf openssl-1.0.1f/_release openssl &&
|
||||
cd .. && rm -rf openssl && ln -sf openssl-1.0.1f/_release openssl &&
|
||||
cd .. && touch ${SRS_OBJS}/_flag.ssl.arm.tmp
|
||||
)
|
||||
fi
|
||||
|
@ -425,7 +425,7 @@ if [ $SRS_SSL = YES ]; then
|
|||
unzip -q ../3rdparty/openssl-1.0.1f.zip && cd openssl-1.0.1f &&
|
||||
./config --prefix=`pwd`/_release -no-shared &&
|
||||
make && make install &&
|
||||
cd .. && ln -sf openssl-1.0.1f/_release openssl &&
|
||||
cd .. && rm -rf openssl && ln -sf openssl-1.0.1f/_release openssl &&
|
||||
cd .. && rm -f ${SRS_OBJS}/_flag.ssl.arm.tmp
|
||||
)
|
||||
fi
|
||||
|
@ -453,7 +453,7 @@ if [ $SRS_FFMPEG = YES ]; then
|
|||
cd ${SRS_OBJS} && pwd_dir=`pwd` &&
|
||||
rm -rf ffmepg.src && mkdir -p ffmpeg.src && cd ffmpeg.src &&
|
||||
rm -f build_ffmpeg.sh && ln -sf ../../auto/build_ffmpeg.sh && . build_ffmpeg.sh &&
|
||||
cd ${pwd_dir} && ln -sf ffmpeg.src/_release ffmpeg
|
||||
cd ${pwd_dir} && rm -rf ffmpeg && ln -sf ffmpeg.src/_release ffmpeg
|
||||
)
|
||||
fi
|
||||
# check status
|
||||
|
@ -491,7 +491,7 @@ if [ $SRS_UTEST = YES ]; then
|
|||
(
|
||||
rm -rf ${SRS_OBJS}/gtest-1.6.0 && cd ${SRS_OBJS} &&
|
||||
unzip -q ../3rdparty/gtest-1.6.0.zip &&
|
||||
rm -f gtest && ln -sf gtest-1.6.0 gtest
|
||||
rm -rf gtest && ln -sf gtest-1.6.0 gtest
|
||||
)
|
||||
fi
|
||||
# check status
|
||||
|
@ -511,8 +511,8 @@ if [ $SRS_GPERF = YES ]; then
|
|||
rm -rf ${SRS_OBJS}/gperftools-2.1 && cd ${SRS_OBJS} &&
|
||||
unzip -q ../3rdparty/gperftools-2.1.zip && cd gperftools-2.1 &&
|
||||
./configure --prefix=`pwd`/_release --enable-frame-pointers && make ${SRS_JOBS} && make install &&
|
||||
cd .. && rm -f gperf && ln -sf gperftools-2.1/_release gperf &&
|
||||
rm -f pprof && ln -sf gperf/bin/pprof pprof
|
||||
cd .. && rm -rf gperf && ln -sf gperftools-2.1/_release gperf &&
|
||||
rm -rf pprof && ln -sf gperf/bin/pprof pprof
|
||||
)
|
||||
fi
|
||||
# check status
|
||||
|
|
|
@ -121,14 +121,15 @@ u_int8_t mpegts_header[] = {
|
|||
};
|
||||
|
||||
// @see: ngx_rtmp_SrsMpegtsFrame_t
|
||||
struct SrsMpegtsFrame
|
||||
class SrsMpegtsFrame
|
||||
{
|
||||
int64_t pts;
|
||||
int64_t dts;
|
||||
int pid;
|
||||
int sid;
|
||||
int cc;
|
||||
bool key;
|
||||
public:
|
||||
int64_t pts;
|
||||
int64_t dts;
|
||||
int pid;
|
||||
int sid;
|
||||
int cc;
|
||||
bool key;
|
||||
|
||||
SrsMpegtsFrame()
|
||||
{
|
||||
|
@ -467,7 +468,7 @@ void SrsTSMuxer::close()
|
|||
}
|
||||
}
|
||||
|
||||
SrsM3u8Segment::SrsM3u8Segment()
|
||||
SrsHlsSegment::SrsHlsSegment()
|
||||
{
|
||||
duration = 0;
|
||||
sequence_no = 0;
|
||||
|
@ -475,14 +476,14 @@ SrsM3u8Segment::SrsM3u8Segment()
|
|||
segment_start_dts = 0;
|
||||
}
|
||||
|
||||
SrsM3u8Segment::~SrsM3u8Segment()
|
||||
SrsHlsSegment::~SrsHlsSegment()
|
||||
{
|
||||
srs_freep(muxer);
|
||||
}
|
||||
|
||||
double SrsM3u8Segment::update_duration(int64_t video_stream_dts)
|
||||
double SrsHlsSegment::update_duration(int64_t current_frame_dts)
|
||||
{
|
||||
duration = (video_stream_dts - segment_start_dts) / 90000.0;
|
||||
duration = (current_frame_dts - segment_start_dts) / 90000.0;
|
||||
srs_assert(duration >= 0);
|
||||
|
||||
return duration;
|
||||
|
@ -497,19 +498,19 @@ SrsHlsAacJitter::SrsHlsAacJitter()
|
|||
sync_ms = SRS_CONF_DEFAULT_AAC_SYNC;
|
||||
}
|
||||
|
||||
SrsM3u8Muxer::SrsM3u8Muxer()
|
||||
SrsHlsMuxer::SrsHlsMuxer()
|
||||
{
|
||||
hls_fragment = hls_window = 0;
|
||||
video_stream_dts = 0;
|
||||
file_index = 0;
|
||||
current = NULL;
|
||||
video_count = 0;
|
||||
}
|
||||
|
||||
SrsM3u8Muxer::~SrsM3u8Muxer()
|
||||
SrsHlsMuxer::~SrsHlsMuxer()
|
||||
{
|
||||
std::vector<SrsM3u8Segment*>::iterator it;
|
||||
std::vector<SrsHlsSegment*>::iterator it;
|
||||
for (it = segments.begin(); it != segments.end(); ++it) {
|
||||
SrsM3u8Segment* segment = *it;
|
||||
SrsHlsSegment* segment = *it;
|
||||
srs_freep(segment);
|
||||
}
|
||||
segments.clear();
|
||||
|
@ -517,7 +518,7 @@ SrsM3u8Muxer::~SrsM3u8Muxer()
|
|||
srs_freep(current);
|
||||
}
|
||||
|
||||
int SrsM3u8Muxer::update_config(
|
||||
int SrsHlsMuxer::update_config(
|
||||
std::string _app, std::string _stream,
|
||||
std::string path, int fragment, int window
|
||||
) {
|
||||
|
@ -532,7 +533,7 @@ int SrsM3u8Muxer::update_config(
|
|||
return ret;
|
||||
}
|
||||
|
||||
int SrsM3u8Muxer::segment_open()
|
||||
int SrsHlsMuxer::segment_open(int64_t segment_start_dts)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
|
@ -541,6 +542,9 @@ int SrsM3u8Muxer::segment_open()
|
|||
return ret;
|
||||
}
|
||||
|
||||
// reset video count for new publish session.
|
||||
video_count = 0;
|
||||
|
||||
// TODO: create all parents dirs.
|
||||
// create dir for app.
|
||||
if ((ret = create_dir()) != ERROR_SUCCESS) {
|
||||
|
@ -551,9 +555,9 @@ int SrsM3u8Muxer::segment_open()
|
|||
srs_assert(!current);
|
||||
|
||||
// new segment.
|
||||
current = new SrsM3u8Segment();
|
||||
current = new SrsHlsSegment();
|
||||
current->sequence_no = file_index++;
|
||||
current->segment_start_dts = video_stream_dts;
|
||||
current->segment_start_dts = segment_start_dts;
|
||||
|
||||
// generate filename.
|
||||
char filename[128];
|
||||
|
@ -580,7 +584,14 @@ int SrsM3u8Muxer::segment_open()
|
|||
return ret;
|
||||
}
|
||||
|
||||
int SrsM3u8Muxer::flush_audio(SrsMpegtsFrame* af, SrsCodecBuffer* ab)
|
||||
bool SrsHlsMuxer::is_segment_overflow()
|
||||
{
|
||||
srs_assert(current);
|
||||
|
||||
return current->duration >= hls_fragment;
|
||||
}
|
||||
|
||||
int SrsHlsMuxer::flush_audio(SrsMpegtsFrame* af, SrsCodecBuffer* ab)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
|
@ -604,9 +615,8 @@ int SrsM3u8Muxer::flush_audio(SrsMpegtsFrame* af, SrsCodecBuffer* ab)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int SrsM3u8Muxer::flush_video(
|
||||
SrsMpegtsFrame* af, SrsCodecBuffer* ab,
|
||||
SrsMpegtsFrame* vf, SrsCodecBuffer* vb)
|
||||
int SrsHlsMuxer::flush_video(
|
||||
SrsMpegtsFrame* af, SrsCodecBuffer* ab, SrsMpegtsFrame* vf, SrsCodecBuffer* vb)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
|
@ -616,53 +626,19 @@ int SrsM3u8Muxer::flush_video(
|
|||
return ret;
|
||||
}
|
||||
|
||||
video_stream_dts = vf->dts;
|
||||
|
||||
srs_assert(current);
|
||||
// reopen the muxer for a gop
|
||||
if (vf->key && current->duration >= hls_fragment) {
|
||||
// TODO: flush audio before or after segment?
|
||||
/*
|
||||
if ((ret = flush_audio(af, ab)) != ERROR_SUCCESS) {
|
||||
srs_error("m3u8 muxer flush audio failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
*/
|
||||
|
||||
if ((ret = segment_close()) != ERROR_SUCCESS) {
|
||||
srs_error("m3u8 muxer close segment failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = segment_open()) != ERROR_SUCCESS) {
|
||||
srs_error("m3u8 muxer open segment failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// TODO: flush audio before or after segment?
|
||||
// segment open, flush the audio.
|
||||
// @see: ngx_rtmp_hls_open_fragment
|
||||
/* start fragment with audio to make iPhone happy */
|
||||
if ((ret = flush_audio(af, ab)) != ERROR_SUCCESS) {
|
||||
srs_error("m3u8 muxer flush audio failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// update the duration of segment.
|
||||
current->update_duration(video_stream_dts);
|
||||
current->update_duration(vf->dts);
|
||||
|
||||
if ((ret = current->muxer->write_video(vf, vb)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// write success, clear and free the buffer
|
||||
vb->free();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int SrsM3u8Muxer::segment_close()
|
||||
int SrsHlsMuxer::segment_close()
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
|
@ -675,7 +651,7 @@ int SrsM3u8Muxer::segment_close()
|
|||
srs_assert(current);
|
||||
|
||||
// assert segment duplicate.
|
||||
std::vector<SrsM3u8Segment*>::iterator it;
|
||||
std::vector<SrsHlsSegment*>::iterator it;
|
||||
it = std::find(segments.begin(), segments.end(), current);
|
||||
srs_assert(it == segments.end());
|
||||
|
||||
|
@ -691,13 +667,13 @@ int SrsM3u8Muxer::segment_close()
|
|||
current = NULL;
|
||||
|
||||
// the segments to remove
|
||||
std::vector<SrsM3u8Segment*> segment_to_remove;
|
||||
std::vector<SrsHlsSegment*> segment_to_remove;
|
||||
|
||||
// shrink the segments.
|
||||
double duration = 0;
|
||||
int remove_index = -1;
|
||||
for (int i = segments.size() - 1; i >= 0; i--) {
|
||||
SrsM3u8Segment* segment = segments[i];
|
||||
SrsHlsSegment* segment = segments[i];
|
||||
duration += segment->duration;
|
||||
|
||||
if ((int)duration > hls_window) {
|
||||
|
@ -706,7 +682,7 @@ int SrsM3u8Muxer::segment_close()
|
|||
}
|
||||
}
|
||||
for (int i = 0; i < remove_index && !segments.empty(); i++) {
|
||||
SrsM3u8Segment* segment = *segments.begin();
|
||||
SrsHlsSegment* segment = *segments.begin();
|
||||
segments.erase(segments.begin());
|
||||
segment_to_remove.push_back(segment);
|
||||
}
|
||||
|
@ -716,7 +692,7 @@ int SrsM3u8Muxer::segment_close()
|
|||
|
||||
// remove the ts file.
|
||||
for (int i = 0; i < (int)segment_to_remove.size(); i++) {
|
||||
SrsM3u8Segment* segment = segment_to_remove[i];
|
||||
SrsHlsSegment* segment = segment_to_remove[i];
|
||||
unlink(segment->full_path.c_str());
|
||||
srs_freep(segment);
|
||||
}
|
||||
|
@ -731,7 +707,7 @@ int SrsM3u8Muxer::segment_close()
|
|||
return ret;
|
||||
}
|
||||
|
||||
int SrsM3u8Muxer::refresh_m3u8()
|
||||
int SrsHlsMuxer::refresh_m3u8()
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
|
@ -761,7 +737,7 @@ int SrsM3u8Muxer::refresh_m3u8()
|
|||
return ret;
|
||||
}
|
||||
|
||||
int SrsM3u8Muxer::_refresh_m3u8(int& fd, std::string m3u8_file)
|
||||
int SrsHlsMuxer::_refresh_m3u8(int& fd, std::string m3u8_file)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
|
@ -795,7 +771,7 @@ int SrsM3u8Muxer::_refresh_m3u8(int& fd, std::string m3u8_file)
|
|||
srs_verbose("write m3u8 header success.");
|
||||
|
||||
// #EXT-X-MEDIA-SEQUENCE:4294967295\n
|
||||
SrsM3u8Segment* first = *segments.begin();
|
||||
SrsHlsSegment* first = *segments.begin();
|
||||
char sequence[34] = {};
|
||||
int len = snprintf(sequence, sizeof(sequence), "#EXT-X-MEDIA-SEQUENCE:%d\n", first->sequence_no);
|
||||
if (::write(fd, sequence, len) != len) {
|
||||
|
@ -807,9 +783,9 @@ int SrsM3u8Muxer::_refresh_m3u8(int& fd, std::string m3u8_file)
|
|||
|
||||
// #EXT-X-TARGETDURATION:4294967295\n
|
||||
int target_duration = 0;
|
||||
std::vector<SrsM3u8Segment*>::iterator it;
|
||||
std::vector<SrsHlsSegment*>::iterator it;
|
||||
for (it = segments.begin(); it != segments.end(); ++it) {
|
||||
SrsM3u8Segment* segment = *it;
|
||||
SrsHlsSegment* segment = *it;
|
||||
target_duration = srs_max(target_duration, (int)segment->duration);
|
||||
}
|
||||
// TODO: maybe need to take an around value
|
||||
|
@ -825,7 +801,7 @@ int SrsM3u8Muxer::_refresh_m3u8(int& fd, std::string m3u8_file)
|
|||
|
||||
// write all segments
|
||||
for (it = segments.begin(); it != segments.end(); ++it) {
|
||||
SrsM3u8Segment* segment = *it;
|
||||
SrsHlsSegment* segment = *it;
|
||||
|
||||
// "#EXTINF:4294967295.208,\n"
|
||||
char ext_info[25];
|
||||
|
@ -852,7 +828,7 @@ int SrsM3u8Muxer::_refresh_m3u8(int& fd, std::string m3u8_file)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int SrsM3u8Muxer::create_dir()
|
||||
int SrsHlsMuxer::create_dir()
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
|
@ -875,7 +851,7 @@ int SrsM3u8Muxer::create_dir()
|
|||
return ret;
|
||||
}
|
||||
|
||||
SrsTSCache::SrsTSCache()
|
||||
SrsHlsCache::SrsHlsCache()
|
||||
{
|
||||
aac_jitter = new SrsHlsAacJitter();
|
||||
|
||||
|
@ -886,7 +862,7 @@ SrsTSCache::SrsTSCache()
|
|||
vf = new SrsMpegtsFrame();
|
||||
}
|
||||
|
||||
SrsTSCache::~SrsTSCache()
|
||||
SrsHlsCache::~SrsHlsCache()
|
||||
{
|
||||
srs_freep(aac_jitter);
|
||||
|
||||
|
@ -900,7 +876,51 @@ SrsTSCache::~SrsTSCache()
|
|||
srs_freep(vf);
|
||||
}
|
||||
|
||||
int SrsTSCache::write_audio(SrsCodec* codec, SrsM3u8Muxer* muxer, int64_t pts, SrsCodecSample* sample)
|
||||
int SrsHlsCache::on_publish(SrsHlsMuxer* muxer, SrsRequest* req, int64_t segment_start_dts)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
std::string vhost = req->vhost;
|
||||
std::string stream = req->stream;
|
||||
std::string app = req->app;
|
||||
|
||||
int hls_fragment = _srs_config->get_hls_fragment(vhost);
|
||||
int hls_window = _srs_config->get_hls_window(vhost);
|
||||
|
||||
// get the hls path config
|
||||
std::string hls_path = _srs_config->get_hls_path(vhost);
|
||||
|
||||
// open muxer
|
||||
if ((ret = muxer->update_config(app, stream, hls_path, hls_fragment, hls_window)) != ERROR_SUCCESS) {
|
||||
srs_error("m3u8 muxer update config failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = muxer->segment_open(segment_start_dts)) != ERROR_SUCCESS) {
|
||||
srs_error("m3u8 muxer open segment failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int SrsHlsCache::on_unpublish(SrsHlsMuxer* muxer)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
if ((ret = muxer->flush_audio(af, ab)) != ERROR_SUCCESS) {
|
||||
srs_error("m3u8 muxer flush audio failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = muxer->segment_close()) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int SrsHlsCache::write_audio(SrsCodec* codec, SrsHlsMuxer* muxer, int64_t pts, SrsCodecSample* sample)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
|
@ -939,7 +959,7 @@ int SrsTSCache::write_audio(SrsCodec* codec, SrsM3u8Muxer* muxer, int64_t pts, S
|
|||
return ret;
|
||||
}
|
||||
|
||||
int SrsTSCache::write_video(SrsCodec* codec, SrsM3u8Muxer* muxer, int64_t dts, SrsCodecSample* sample)
|
||||
int SrsHlsCache::write_video(SrsCodec* codec, SrsHlsMuxer* muxer, int64_t dts, SrsCodecSample* sample)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
|
@ -954,28 +974,43 @@ int SrsTSCache::write_video(SrsCodec* codec, SrsM3u8Muxer* muxer, int64_t dts, S
|
|||
vf->sid = TS_VIDEO_AVC;
|
||||
vf->key = sample->frame_type == SrsCodecVideoAVCFrameKeyFrame;
|
||||
|
||||
// reopen the muxer for a gop
|
||||
// close current segment, open a new segment,
|
||||
// then write the key frame to the new segment.
|
||||
if (vf->key && muxer->is_segment_overflow()) {
|
||||
if ((ret = muxer->segment_close()) != ERROR_SUCCESS) {
|
||||
srs_error("m3u8 muxer close segment failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = muxer->segment_open(vf->dts)) != ERROR_SUCCESS) {
|
||||
srs_error("m3u8 muxer open segment failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// TODO: flush audio before or after segment?
|
||||
// segment open, flush the audio.
|
||||
// @see: ngx_rtmp_hls_open_fragment
|
||||
/* start fragment with audio to make iPhone happy */
|
||||
if ((ret = muxer->flush_audio(af, ab)) != ERROR_SUCCESS) {
|
||||
srs_error("m3u8 muxer flush audio failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// flush video when got one
|
||||
if ((ret = muxer->flush_video(af, ab, vf, vb)) != ERROR_SUCCESS) {
|
||||
srs_error("m3u8 muxer flush video failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int SrsTSCache::flush_audio(SrsM3u8Muxer* muxer)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
if ((ret = muxer->flush_audio(af, ab)) != ERROR_SUCCESS) {
|
||||
srs_error("m3u8 muxer flush audio failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
// write success, clear and free the buffer
|
||||
vb->free();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int SrsTSCache::cache_audio(SrsCodec* codec, SrsCodecSample* sample)
|
||||
int SrsHlsCache::cache_audio(SrsCodec* codec, SrsCodecSample* sample)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
|
@ -1042,7 +1077,7 @@ int SrsTSCache::cache_audio(SrsCodec* codec, SrsCodecSample* sample)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int SrsTSCache::cache_video(SrsCodec* codec, SrsCodecSample* sample)
|
||||
int SrsHlsCache::cache_video(SrsCodec* codec, SrsCodecSample* sample)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
|
@ -1118,10 +1153,11 @@ SrsHls::SrsHls(SrsSource* _source)
|
|||
sample = new SrsCodecSample();
|
||||
jitter = new SrsRtmpJitter();
|
||||
|
||||
muxer = new SrsM3u8Muxer();
|
||||
ts_cache = new SrsTSCache();
|
||||
muxer = new SrsHlsMuxer();
|
||||
hls_cache = new SrsHlsCache();
|
||||
|
||||
pithy_print = new SrsPithyPrint(SRS_STAGE_HLS);
|
||||
stream_dts = 0;
|
||||
}
|
||||
|
||||
SrsHls::~SrsHls()
|
||||
|
@ -1131,7 +1167,7 @@ SrsHls::~SrsHls()
|
|||
srs_freep(jitter);
|
||||
|
||||
srs_freep(muxer);
|
||||
srs_freep(ts_cache);
|
||||
srs_freep(hls_cache);
|
||||
|
||||
srs_freep(pithy_print);
|
||||
}
|
||||
|
@ -1146,30 +1182,11 @@ int SrsHls::on_publish(SrsRequest* req)
|
|||
}
|
||||
|
||||
std::string vhost = req->vhost;
|
||||
std::string stream = req->stream;
|
||||
std::string app = req->app;
|
||||
|
||||
if (!_srs_config->get_hls_enabled(vhost)) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// if enabled, open the muxer.
|
||||
hls_enabled = true;
|
||||
|
||||
int hls_fragment = _srs_config->get_hls_fragment(vhost);
|
||||
int hls_window = _srs_config->get_hls_window(vhost);
|
||||
|
||||
// get the hls path config
|
||||
std::string hls_path = _srs_config->get_hls_path(vhost);
|
||||
|
||||
// open muxer
|
||||
if ((ret = muxer->update_config(app, stream, hls_path, hls_fragment, hls_window)) != ERROR_SUCCESS) {
|
||||
srs_error("m3u8 muxer update config failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = muxer->segment_open()) != ERROR_SUCCESS) {
|
||||
srs_error("m3u8 muxer open segment failed. ret=%d", ret);
|
||||
if ((ret = hls_cache->on_publish(muxer, req, stream_dts)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1179,6 +1196,9 @@ int SrsHls::on_publish(SrsRequest* req)
|
|||
return ret;
|
||||
}
|
||||
|
||||
// if enabled, open the muxer.
|
||||
hls_enabled = true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1191,11 +1211,7 @@ void SrsHls::on_unpublish()
|
|||
return;
|
||||
}
|
||||
|
||||
// close muxer when unpublish.
|
||||
ret = ts_cache->flush_audio(muxer);
|
||||
ret += muxer->segment_close();
|
||||
|
||||
if (ret != ERROR_SUCCESS) {
|
||||
if ((ret = hls_cache->on_unpublish(muxer)) != ERROR_SUCCESS) {
|
||||
srs_error("ignore m3u8 muxer flush/close audio failed. ret=%d", ret);
|
||||
}
|
||||
|
||||
|
@ -1287,8 +1303,11 @@ int SrsHls::on_audio(SrsSharedPtrMessage* audio)
|
|||
// the pts calc from rtmp/flv header.
|
||||
int64_t pts = audio->header.timestamp * 90;
|
||||
|
||||
if ((ret = ts_cache->write_audio(codec, muxer, pts, sample)) != ERROR_SUCCESS) {
|
||||
srs_error("ts cache write audio failed. ret=%d", ret);
|
||||
// for pure audio, we need to update the stream dts also.
|
||||
stream_dts = pts;
|
||||
|
||||
if ((ret = hls_cache->write_audio(codec, muxer, pts, sample)) != ERROR_SUCCESS) {
|
||||
srs_error("hls cache write audio failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1327,8 +1346,9 @@ int SrsHls::on_video(SrsSharedPtrMessage* video)
|
|||
}
|
||||
|
||||
int64_t dts = video->header.timestamp * 90;
|
||||
if ((ret = ts_cache->write_video(codec, muxer, dts, sample)) != ERROR_SUCCESS) {
|
||||
srs_error("ts cache write video failed. ret=%d", ret);
|
||||
stream_dts = dts;
|
||||
if ((ret = hls_cache->write_video(codec, muxer, dts, sample)) != ERROR_SUCCESS) {
|
||||
srs_error("hls cache write video failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -76,7 +76,10 @@ public:
|
|||
virtual void on_buffer_continue();
|
||||
};
|
||||
|
||||
//TODO: refine the ts muxer, do more jobs.
|
||||
/**
|
||||
* write data from frame(header info) and buffer(data) to ts file.
|
||||
* it's a simple object wrapper for utility from nginx-rtmp: SrsMpegtsWriter
|
||||
*/
|
||||
class SrsTSMuxer
|
||||
{
|
||||
private:
|
||||
|
@ -93,11 +96,14 @@ public:
|
|||
};
|
||||
|
||||
/**
|
||||
* the wrapper of m3u8 segment from specification:
|
||||
*
|
||||
* 3.3.2. EXTINF
|
||||
* The EXTINF tag specifies the duration of a media segment.
|
||||
*/
|
||||
struct SrsM3u8Segment
|
||||
class SrsHlsSegment
|
||||
{
|
||||
public:
|
||||
// duration in seconds in m3u8.
|
||||
double duration;
|
||||
// sequence number in m3u8.
|
||||
|
@ -111,19 +117,25 @@ struct SrsM3u8Segment
|
|||
// current segment start dts for m3u8
|
||||
int64_t segment_start_dts;
|
||||
|
||||
SrsM3u8Segment();
|
||||
virtual ~SrsM3u8Segment();
|
||||
SrsHlsSegment();
|
||||
virtual ~SrsHlsSegment();
|
||||
|
||||
/**
|
||||
* update the segment duration.
|
||||
* @current_frame_dts the dts of frame, in tbn of ts.
|
||||
*/
|
||||
virtual double update_duration(int64_t video_stream_dts);
|
||||
virtual double update_duration(int64_t current_frame_dts);
|
||||
};
|
||||
|
||||
/**
|
||||
* muxer the m3u8 and ts files.
|
||||
* muxer the HLS stream(m3u8 and ts files).
|
||||
* generally, the m3u8 muxer only provides methods to open/close segments,
|
||||
* to flush video/audio, without any mechenisms.
|
||||
*
|
||||
* that is, user must use HlsCache, which will control the methods of muxer,
|
||||
* and provides HLS mechenisms.
|
||||
*/
|
||||
class SrsM3u8Muxer
|
||||
class SrsHlsMuxer
|
||||
{
|
||||
private:
|
||||
std::string app;
|
||||
|
@ -135,23 +147,39 @@ private:
|
|||
private:
|
||||
int file_index;
|
||||
std::string m3u8;
|
||||
private:
|
||||
/**
|
||||
* for pure audio HLS application,
|
||||
* the video count used to count the video,
|
||||
* if zero and audio buffer overflow, reap the ts,
|
||||
* just like we got a keyframe.
|
||||
*/
|
||||
u_int32_t video_count;
|
||||
private:
|
||||
/**
|
||||
* m3u8 segments.
|
||||
*/
|
||||
std::vector<SrsM3u8Segment*> segments;
|
||||
std::vector<SrsHlsSegment*> segments;
|
||||
/**
|
||||
* current writing segment.
|
||||
*/
|
||||
SrsM3u8Segment* current;
|
||||
// last known dts
|
||||
int64_t video_stream_dts;
|
||||
SrsHlsSegment* current;
|
||||
public:
|
||||
SrsM3u8Muxer();
|
||||
virtual ~SrsM3u8Muxer();
|
||||
SrsHlsMuxer();
|
||||
virtual ~SrsHlsMuxer();
|
||||
public:
|
||||
virtual int update_config(std::string _app, std::string _stream, std::string path, int fragment, int window);
|
||||
virtual int segment_open();
|
||||
/**
|
||||
* 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);
|
||||
/**
|
||||
* whether video overflow,
|
||||
* that is whether the current segment duration >= the segment in config
|
||||
*/
|
||||
virtual bool is_segment_overflow();
|
||||
virtual int flush_audio(SrsMpegtsFrame* af, SrsCodecBuffer* ab);
|
||||
virtual int flush_video(SrsMpegtsFrame* af, SrsCodecBuffer* ab, SrsMpegtsFrame* vf, SrsCodecBuffer* vb);
|
||||
virtual int segment_close();
|
||||
|
@ -162,9 +190,23 @@ private:
|
|||
};
|
||||
|
||||
/**
|
||||
* ts need to cache some audio then flush
|
||||
* hls stream cache,
|
||||
* use to cache hls stream and flush to hls muxer.
|
||||
*
|
||||
* when write stream to ts file:
|
||||
* video frame will directly flush to M3u8Muxer,
|
||||
* audio frame need to cache, because it's small and flv tbn problem.
|
||||
*
|
||||
* whatever, the Hls cache used to cache video/audio,
|
||||
* and flush video/audio to m3u8 muxer if needed.
|
||||
*
|
||||
* about the flv tbn problem:
|
||||
* flv tbn is 1/1000, ts tbn is 1/90000,
|
||||
* when timestamp convert to flv tbn, it will loose precise,
|
||||
* so we must gather audio frame together, and recalc the timestamp @see SrsHlsAacJitter,
|
||||
* we use a aac jitter to correct the audio pts.
|
||||
*/
|
||||
class SrsTSCache
|
||||
class SrsHlsCache
|
||||
{
|
||||
private:
|
||||
// current frame and buffer
|
||||
|
@ -178,34 +220,37 @@ private:
|
|||
// time jitter for aac
|
||||
SrsHlsAacJitter* aac_jitter;
|
||||
public:
|
||||
SrsTSCache();
|
||||
virtual ~SrsTSCache();
|
||||
SrsHlsCache();
|
||||
virtual ~SrsHlsCache();
|
||||
public:
|
||||
/**
|
||||
* when publish or unpublish stream.
|
||||
*/
|
||||
virtual int on_publish(SrsHlsMuxer* muxer, SrsRequest* req, int64_t segment_start_dts);
|
||||
virtual int on_unpublish(SrsHlsMuxer* muxer);
|
||||
/**
|
||||
* write audio to cache, if need to flush, flush to muxer.
|
||||
*/
|
||||
virtual int write_audio(SrsCodec* codec, SrsM3u8Muxer* muxer, int64_t pts, SrsCodecSample* sample);
|
||||
virtual int write_audio(SrsCodec* codec, SrsHlsMuxer* muxer, int64_t pts, SrsCodecSample* sample);
|
||||
/**
|
||||
* write video to muxer.
|
||||
*/
|
||||
virtual int write_video(SrsCodec* codec, SrsM3u8Muxer* muxer, int64_t dts, SrsCodecSample* sample);
|
||||
/**
|
||||
* flush audio in cache to muxer.
|
||||
*/
|
||||
virtual int flush_audio(SrsM3u8Muxer* muxer);
|
||||
virtual int write_video(SrsCodec* codec, SrsHlsMuxer* muxer, int64_t dts, SrsCodecSample* sample);
|
||||
private:
|
||||
virtual int cache_audio(SrsCodec* codec, SrsCodecSample* sample);
|
||||
virtual int cache_video(SrsCodec* codec, SrsCodecSample* sample);
|
||||
};
|
||||
|
||||
/**
|
||||
* write m3u8 hls.
|
||||
* delivery RTMP stream to HLS(m3u8 and ts),
|
||||
* SrsHls provides interface with SrsSource.
|
||||
*
|
||||
*/
|
||||
class SrsHls
|
||||
{
|
||||
private:
|
||||
SrsM3u8Muxer* muxer;
|
||||
SrsTSCache* ts_cache;
|
||||
SrsHlsMuxer* muxer;
|
||||
SrsHlsCache* hls_cache;
|
||||
private:
|
||||
bool hls_enabled;
|
||||
SrsSource* source;
|
||||
|
@ -213,6 +258,20 @@ private:
|
|||
SrsCodecSample* sample;
|
||||
SrsRtmpJitter* jitter;
|
||||
SrsPithyPrint* pithy_print;
|
||||
/**
|
||||
* 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(SrsSource* _source);
|
||||
virtual ~SrsHls();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue