Toggle navigation
Toggle navigation
此项目
正在载入...
Sign in
胡斌
/
srs
转到一个项目
Toggle navigation
项目
群组
代码片段
帮助
Toggle navigation pinning
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
winlin
2014-03-20 18:19:08 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
b708f588fcc23c1e6c3d457d9e997ed61235237b
b708f588
1 parent
6bc18f2e
refine the hls module, clear logic dead code, rename TSCache to HlsCache, M3u8Mu…
…xer to HlsMuxer. that is, make it to more readable.
隐藏空白字符变更
内嵌
并排对比
正在显示
3 个修改的文件
包含
241 行增加
和
162 行删除
trunk/auto/depends.sh
trunk/src/app/srs_app_hls.cpp
trunk/src/app/srs_app_hls.hpp
trunk/auto/depends.sh
查看文件 @
b708f58
...
...
@@ -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 -
r
f 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 -
r
f 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 -
r
f 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 -
r
f 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 -
r
f 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 -
r
f 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 -
r
f 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
...
...
trunk/src/app/srs_app_hls.cpp
查看文件 @
b708f58
...
...
@@ -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()
}
}
Srs
M3u8Segment
::
SrsM3u8
Segment
()
Srs
HlsSegment
::
SrsHls
Segment
()
{
duration
=
0
;
sequence_no
=
0
;
...
...
@@ -475,14 +476,14 @@ SrsM3u8Segment::SrsM3u8Segment()
segment_start_dts
=
0
;
}
Srs
M3u8Segment
::~
SrsM3u8
Segment
()
Srs
HlsSegment
::~
SrsHls
Segment
()
{
srs_freep
(
muxer
);
}
double
Srs
M3u8Segment
::
update_duration
(
int64_t
video_stream
_dts
)
double
Srs
HlsSegment
::
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
;
}
Srs
M3u8Muxer
::
SrsM3u8
Muxer
()
Srs
HlsMuxer
::
SrsHls
Muxer
()
{
hls_fragment
=
hls_window
=
0
;
video_stream_dts
=
0
;
file_index
=
0
;
current
=
NULL
;
video_count
=
0
;
}
Srs
M3u8Muxer
::~
SrsM3u8
Muxer
()
Srs
HlsMuxer
::~
SrsHls
Muxer
()
{
std
::
vector
<
Srs
M3u8
Segment
*>::
iterator
it
;
std
::
vector
<
Srs
Hls
Segment
*>::
iterator
it
;
for
(
it
=
segments
.
begin
();
it
!=
segments
.
end
();
++
it
)
{
Srs
M3u8
Segment
*
segment
=
*
it
;
Srs
Hls
Segment
*
segment
=
*
it
;
srs_freep
(
segment
);
}
segments
.
clear
();
...
...
@@ -517,7 +518,7 @@ SrsM3u8Muxer::~SrsM3u8Muxer()
srs_freep
(
current
);
}
int
Srs
M3u8
Muxer
::
update_config
(
int
Srs
Hls
Muxer
::
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
Srs
M3u8Muxer
::
segment_open
(
)
int
Srs
HlsMuxer
::
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
Srs
M3u8
Segment
();
current
=
new
Srs
Hls
Segment
();
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
(
v
ideo_stream_
dts
);
current
->
update_duration
(
v
f
->
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
Srs
M3u8
Muxer
::
segment_close
()
int
Srs
Hls
Muxer
::
segment_close
()
{
int
ret
=
ERROR_SUCCESS
;
...
...
@@ -675,7 +651,7 @@ int SrsM3u8Muxer::segment_close()
srs_assert
(
current
);
// assert segment duplicate.
std
::
vector
<
Srs
M3u8
Segment
*>::
iterator
it
;
std
::
vector
<
Srs
Hls
Segment
*>::
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
<
Srs
M3u8
Segment
*>
segment_to_remove
;
std
::
vector
<
Srs
Hls
Segment
*>
segment_to_remove
;
// shrink the segments.
double
duration
=
0
;
int
remove_index
=
-
1
;
for
(
int
i
=
segments
.
size
()
-
1
;
i
>=
0
;
i
--
)
{
Srs
M3u8
Segment
*
segment
=
segments
[
i
];
Srs
Hls
Segment
*
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
++
)
{
Srs
M3u8
Segment
*
segment
=
*
segments
.
begin
();
Srs
Hls
Segment
*
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
++
)
{
Srs
M3u8
Segment
*
segment
=
segment_to_remove
[
i
];
Srs
Hls
Segment
*
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
Srs
M3u8
Muxer
::
refresh_m3u8
()
int
Srs
Hls
Muxer
::
refresh_m3u8
()
{
int
ret
=
ERROR_SUCCESS
;
...
...
@@ -761,7 +737,7 @@ int SrsM3u8Muxer::refresh_m3u8()
return
ret
;
}
int
Srs
M3u8
Muxer
::
_refresh_m3u8
(
int
&
fd
,
std
::
string
m3u8_file
)
int
Srs
Hls
Muxer
::
_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
Srs
M3u8
Segment
*
first
=
*
segments
.
begin
();
Srs
Hls
Segment
*
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
<
Srs
M3u8
Segment
*>::
iterator
it
;
std
::
vector
<
Srs
Hls
Segment
*>::
iterator
it
;
for
(
it
=
segments
.
begin
();
it
!=
segments
.
end
();
++
it
)
{
Srs
M3u8
Segment
*
segment
=
*
it
;
Srs
Hls
Segment
*
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
)
{
Srs
M3u8
Segment
*
segment
=
*
it
;
Srs
Hls
Segment
*
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
Srs
M3u8
Muxer
::
create_dir
()
int
Srs
Hls
Muxer
::
create_dir
()
{
int
ret
=
ERROR_SUCCESS
;
...
...
@@ -875,7 +851,7 @@ int SrsM3u8Muxer::create_dir()
return
ret
;
}
Srs
TSCache
::
SrsTS
Cache
()
Srs
HlsCache
::
SrsHls
Cache
()
{
aac_jitter
=
new
SrsHlsAacJitter
();
...
...
@@ -886,7 +862,7 @@ SrsTSCache::SrsTSCache()
vf
=
new
SrsMpegtsFrame
();
}
Srs
TSCache
::~
SrsTS
Cache
()
Srs
HlsCache
::~
SrsHls
Cache
()
{
srs_freep
(
aac_jitter
);
...
...
@@ -899,8 +875,52 @@ SrsTSCache::~SrsTSCache()
srs_freep
(
af
);
srs_freep
(
vf
);
}
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
SrsTSCache
::
write_audio
(
SrsCodec
*
codec
,
SrsM3u8Muxer
*
muxer
,
int64_t
pts
,
SrsCodecSample
*
sample
)
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
Srs
TSCache
::
write_video
(
SrsCodec
*
codec
,
SrsM3u8
Muxer
*
muxer
,
int64_t
dts
,
SrsCodecSample
*
sample
)
int
Srs
HlsCache
::
write_video
(
SrsCodec
*
codec
,
SrsHls
Muxer
*
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
Srs
TS
Cache
::
cache_audio
(
SrsCodec
*
codec
,
SrsCodecSample
*
sample
)
int
Srs
Hls
Cache
::
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
Srs
TS
Cache
::
cache_video
(
SrsCodec
*
codec
,
SrsCodecSample
*
sample
)
int
Srs
Hls
Cache
::
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
(
t
s_cache
);
srs_freep
(
hl
s_cache
);
srs_freep
(
pithy_print
);
}
...
...
@@ -1144,32 +1180,13 @@ int SrsHls::on_publish(SrsRequest* req)
if
(
hls_enabled
)
{
return
ret
;
}
std
::
string
vhost
=
req
->
vhost
;
std
::
string
stream
=
req
->
stream
;
std
::
string
app
=
req
->
app
;
std
::
string
vhost
=
req
->
vhost
;
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
;
}
...
...
@@ -1178,6 +1195,9 @@ int SrsHls::on_publish(SrsRequest* req)
srs_error
(
"callback source hls start failed. ret=%d"
,
ret
);
return
ret
;
}
// if enabled, open the muxer.
hls_enabled
=
true
;
return
ret
;
}
...
...
@@ -1190,12 +1210,8 @@ void SrsHls::on_unpublish()
if
(
!
hls_enabled
)
{
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
;
}
...
...
trunk/src/app/srs_app_hls.hpp
查看文件 @
b708f58
...
...
@@ -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
SrsM3u8
Segment
class
SrsHls
Segment
{
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
Srs
M3u8
Muxer
class
Srs
Hls
Muxer
{
private
:
std
::
string
app
;
...
...
@@ -137,21 +149,37 @@ private:
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
<
Srs
M3u8
Segment
*>
segments
;
std
::
vector
<
Srs
Hls
Segment
*>
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
Srs
TS
Cache
class
Srs
Hls
Cache
{
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
:
/**
* w
rite audio to cache, if need to flush, flush to muxer
.
* w
hen publish or unpublish stream
.
*/
virtual
int
write_audio
(
SrsCodec
*
codec
,
SrsM3u8Muxer
*
muxer
,
int64_t
pts
,
SrsCodecSample
*
sample
);
virtual
int
on_publish
(
SrsHlsMuxer
*
muxer
,
SrsRequest
*
req
,
int64_t
segment_start_dts
);
virtual
int
on_unpublish
(
SrsHlsMuxer
*
muxer
);
/**
* write
video
to muxer.
* write
audio to cache, if need to flush, flush
to muxer.
*/
virtual
int
write_
video
(
SrsCodec
*
codec
,
SrsM3u8Muxer
*
muxer
,
int64_t
d
ts
,
SrsCodecSample
*
sample
);
virtual
int
write_
audio
(
SrsCodec
*
codec
,
SrsHlsMuxer
*
muxer
,
int64_t
p
ts
,
SrsCodecSample
*
sample
);
/**
*
flush audio in cache
to muxer.
*
write video
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
();
...
...
请
注册
或
登录
后发表评论