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
2013-12-02 15:55:10 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
1c2f5052dd41d8ade86eeb3434ef189fa8355690
1c2f5052
1 parent
b4eb38c8
refine the hls muxer module
显示空白字符变更
内嵌
并排对比
正在显示
2 个修改的文件
包含
454 行增加
和
365 行删除
trunk/src/core/srs_core_hls.cpp
trunk/src/core/srs_core_hls.hpp
trunk/src/core/srs_core_hls.cpp
查看文件 @
1c2f505
...
...
@@ -42,6 +42,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_core_autofree.hpp>
#include <srs_core_rtmp.hpp>
// max PES packets size to flush the video.
#define SRS_HLS_AUDIO_CACHE_SIZE 512 * 1024
// @see: NGX_RTMP_HLS_DELAY,
// 63000: 700ms, ts_tbn=90000
#define SRS_HLS_DELAY 63000
...
...
@@ -399,40 +402,9 @@ void SrsHlsAacJitter::on_buffer_continue()
nb_samples
++
;
}
SrsM3u8Segment
::
SrsM3u8Segment
()
{
duration
=
0
;
sequence_no
=
0
;
muxer
=
new
SrsTSMuxer
();
segment_start_dts
=
0
;
}
SrsM3u8Segment
::~
SrsM3u8Segment
()
{
srs_freep
(
muxer
);
}
double
SrsM3u8Segment
::
update_duration
(
int64_t
video_stream_dts
)
{
duration
=
(
video_stream_dts
-
segment_start_dts
)
/
90000.0
;
srs_assert
(
duration
>
0
);
return
duration
;
}
SrsHlsAacJitter
::
SrsHlsAacJitter
()
{
base_pts
=
0
;
nb_samples
=
0
;
// TODO: config it, 0 means no adjust
sync_ms
=
SRS_CONF_DEFAULT_AAC_SYNC
;
}
SrsTSMuxer
::
SrsTSMuxer
()
{
fd
=
-
1
;
_fresh
=
false
;
}
SrsTSMuxer
::~
SrsTSMuxer
()
...
...
@@ -461,29 +433,25 @@ int SrsTSMuxer::open(std::string _path)
return
ret
;
}
_fresh
=
true
;
return
ret
;
}
int
SrsTSMuxer
::
write_audio
(
SrsMpegtsFrame
*
a
udio_frame
,
SrsCodecBuffer
*
audio_buffer
)
int
SrsTSMuxer
::
write_audio
(
SrsMpegtsFrame
*
a
f
,
SrsCodecBuffer
*
ab
)
{
int
ret
=
ERROR_SUCCESS
;
if
((
ret
=
SrsMpegtsWriter
::
write_frame
(
fd
,
a
udio_frame
,
audio_buffer
))
!=
ERROR_SUCCESS
)
{
if
((
ret
=
SrsMpegtsWriter
::
write_frame
(
fd
,
a
f
,
ab
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
_fresh
=
false
;
return
ret
;
}
int
SrsTSMuxer
::
write_video
(
SrsMpegtsFrame
*
v
ideo_frame
,
SrsCodecBuffer
*
video_buffer
)
int
SrsTSMuxer
::
write_video
(
SrsMpegtsFrame
*
v
f
,
SrsCodecBuffer
*
vb
)
{
int
ret
=
ERROR_SUCCESS
;
if
((
ret
=
SrsMpegtsWriter
::
write_frame
(
fd
,
v
ideo_frame
,
video_buffer
))
!=
ERROR_SUCCESS
)
{
if
((
ret
=
SrsMpegtsWriter
::
write_frame
(
fd
,
v
f
,
vb
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
...
...
@@ -495,44 +463,49 @@ void SrsTSMuxer::close()
if
(
fd
>
0
)
{
::
close
(
fd
);
fd
=
-
1
;
_fresh
=
false
;
}
}
bool
SrsTSMuxer
::
fresh
()
SrsM3u8Segment
::
SrsM3u8Segment
()
{
return
_fresh
;
duration
=
0
;
sequence_no
=
0
;
muxer
=
new
SrsTSMuxer
();
segment_start_dts
=
0
;
}
Srs
Hls
::
SrsHls
()
Srs
M3u8Segment
::~
SrsM3u8Segment
()
{
hls_enabled
=
false
;
codec
=
new
SrsCodec
();
sample
=
new
SrsCodecSample
();
current
=
NULL
;
jitter
=
new
SrsRtmpJitter
();
aac_jitter
=
new
SrsHlsAacJitter
();
file_index
=
0
;
audio_buffer_start_pts
=
video_stream_dts
=
0
;
hls_fragment
=
hls_window
=
0
;
srs_freep
(
muxer
);
}
// TODO: config it.
audio_delay
=
SRS_CONF_DEFAULT_AAC_DELAY
;
double
SrsM3u8Segment
::
update_duration
(
int64_t
video_stream_dts
)
{
duration
=
(
video_stream_dts
-
segment_start_dts
)
/
90000.0
;
srs_assert
(
duration
>=
0
);
return
duration
;
}
audio_buffer
=
new
SrsCodecBuffer
();
video_buffer
=
new
SrsCodecBuffer
();
SrsHlsAacJitter
::
SrsHlsAacJitter
()
{
base_pts
=
0
;
nb_samples
=
0
;
audio_frame
=
new
SrsMpegtsFrame
();
video_frame
=
new
SrsMpegtsFrame
();
// TODO: config it, 0 means no adjust
sync_ms
=
SRS_CONF_DEFAULT_AAC_SYNC
;
}
Srs
Hls
::~
SrsHls
()
Srs
M3u8Muxer
::
SrsM3u8Muxer
()
{
srs_freep
(
codec
);
srs_freep
(
sample
);
srs_freep
(
jitter
);
srs_freep
(
aac_jitter
);
hls_fragment
=
hls_window
=
0
;
video_stream_dts
=
0
;
file_index
=
0
;
current
=
NULL
;
}
SrsM3u8Muxer
::~
SrsM3u8Muxer
()
{
std
::
vector
<
SrsM3u8Segment
*>::
iterator
it
;
for
(
it
=
segments
.
begin
();
it
!=
segments
.
end
();
++
it
)
{
SrsM3u8Segment
*
segment
=
*
it
;
...
...
@@ -541,274 +514,154 @@ SrsHls::~SrsHls()
segments
.
clear
();
srs_freep
(
current
);
audio_buffer
->
free
();
video_buffer
->
free
();
srs_freep
(
audio_buffer
);
srs_freep
(
video_buffer
);
srs_freep
(
audio_frame
);
srs_freep
(
video_frame
);
}
int
SrsHls
::
on_publish
(
SrsRequest
*
req
)
{
int
SrsM3u8Muxer
::
update_config
(
std
::
string
_app
,
std
::
string
_stream
,
std
::
string
path
,
int
fragment
,
int
window
)
{
int
ret
=
ERROR_SUCCESS
;
vhost
=
req
->
vhost
;
stream
=
req
->
stream
;
app
=
req
->
app
;
// TODO: subscribe the reload event.
SrsConfDirective
*
conf
=
NULL
;
if
((
conf
=
config
->
get_hls_fragment
(
vhost
))
!=
NULL
&&
!
conf
->
arg0
().
empty
())
{
hls_fragment
=
::
atoi
(
conf
->
arg0
().
c_str
());
}
if
(
hls_fragment
<=
0
)
{
hls_fragment
=
SRS_CONF_DEFAULT_HLS_FRAGMENT
;
}
if
((
conf
=
config
->
get_hls_window
(
vhost
))
!=
NULL
&&
!
conf
->
arg0
().
empty
())
{
hls_window
=
::
atoi
(
conf
->
arg0
().
c_str
());
}
if
(
hls_window
<=
0
)
{
hls_window
=
SRS_CONF_DEFAULT_HLS_WINDOW
;
}
if
((
ret
=
reopen
())
!=
ERROR_SUCCESS
)
{
return
ret
;
}
app
=
_app
;
stream
=
_stream
;
hls_path
=
path
;
hls_fragment
=
fragment
;
hls_window
=
window
;
return
ret
;
}
void
SrsHls
::
on_unpublish
()
{
hls_enabled
=
false
;
}
int
SrsHls
::
on_meta_data
(
SrsOnMetaDataPacket
*
metadata
)
int
SrsM3u8Muxer
::
segment_open
()
{
int
ret
=
ERROR_SUCCESS
;
if
(
!
metadata
||
!
metadata
->
metadata
)
{
srs_trace
(
"no metadata persent, hls ignored it."
);
return
ret
;
}
SrsAmf0Object
*
obj
=
metadata
->
metadata
;
if
(
obj
->
size
()
<=
0
)
{
srs_trace
(
"no metadata persent, hls ignored it."
);
// TODO: create all parents dirs.
// create dir for app.
if
((
ret
=
create_dir
())
!=
ERROR_SUCCESS
)
{
return
ret
;
}
// finger out the codec info from metadata if possible.
SrsAmf0Any
*
prop
=
NULL
;
if
((
prop
=
obj
->
get_property
(
"duration"
))
!=
NULL
&&
prop
->
is_number
())
{
codec
->
duration
=
(
int
)
srs_amf0_convert
<
SrsAmf0Number
>
(
prop
)
->
value
;
}
if
((
prop
=
obj
->
get_property
(
"width"
))
!=
NULL
&&
prop
->
is_number
())
{
codec
->
width
=
(
int
)
srs_amf0_convert
<
SrsAmf0Number
>
(
prop
)
->
value
;
}
if
((
prop
=
obj
->
get_property
(
"height"
))
!=
NULL
&&
prop
->
is_number
())
{
codec
->
height
=
(
int
)
srs_amf0_convert
<
SrsAmf0Number
>
(
prop
)
->
value
;
}
if
((
prop
=
obj
->
get_property
(
"framerate"
))
!=
NULL
&&
prop
->
is_number
())
{
codec
->
frame_rate
=
(
int
)
srs_amf0_convert
<
SrsAmf0Number
>
(
prop
)
->
value
;
}
if
((
prop
=
obj
->
get_property
(
"videocodecid"
))
!=
NULL
&&
prop
->
is_number
())
{
codec
->
video_codec_id
=
(
int
)
srs_amf0_convert
<
SrsAmf0Number
>
(
prop
)
->
value
;
}
if
((
prop
=
obj
->
get_property
(
"videodatarate"
))
!=
NULL
&&
prop
->
is_number
())
{
codec
->
video_data_rate
=
(
int
)(
1000
*
srs_amf0_convert
<
SrsAmf0Number
>
(
prop
)
->
value
);
}
if
((
prop
=
obj
->
get_property
(
"audiocodecid"
))
!=
NULL
&&
prop
->
is_number
())
{
codec
->
audio_codec_id
=
(
int
)
srs_amf0_convert
<
SrsAmf0Number
>
(
prop
)
->
value
;
}
if
((
prop
=
obj
->
get_property
(
"audiodatarate"
))
!=
NULL
&&
prop
->
is_number
())
{
codec
->
audio_data_rate
=
(
int
)(
1000
*
srs_amf0_convert
<
SrsAmf0Number
>
(
prop
)
->
value
);
}
// ignore the following, for each flv/rtmp packet contains them:
// audiosamplerate, sample->sound_rate
// audiosamplesize, sample->sound_size
// stereo, sample->sound_type
return
ret
;
}
// when segment open, the current segment must be NULL.
srs_assert
(
!
current
);
int
SrsHls
::
on_audio
(
SrsSharedPtrMessage
*
audio
)
{
int
ret
=
ERROR_SUCCESS
;
// new segment.
current
=
new
SrsM3u8Segment
();
current
->
sequence_no
=
file_index
++
;
current
->
segment_start_dts
=
video_stream_dts
;
SrsAutoFree
(
SrsSharedPtrMessage
,
audio
,
false
);
// generate filename.
char
filename
[
128
];
snprintf
(
filename
,
sizeof
(
filename
),
"%s-%d.ts"
,
stream
.
c_str
(),
current
->
sequence_no
);
// TODO: maybe donot need to demux the aac?
if
(
!
hls_enabled
)
{
return
ret
;
}
// TODO: use temp file and rename it.
current
->
full_path
=
hls_path
;
current
->
full_path
+=
"/"
;
current
->
full_path
+=
app
;
current
->
full_path
+=
"/"
;
current
->
full_path
+=
filename
;
sample
->
clear
();
if
((
ret
=
codec
->
audio_aac_demux
(
audio
->
payload
,
audio
->
size
,
sample
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
// TODO: support base url, and so on.
current
->
uri
=
filename
;
if
(
codec
->
audio_codec_id
!=
SrsCodecAudioAAC
)
{
if
((
ret
=
current
->
muxer
->
open
(
current
->
full_path
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"open hls muxer failed. ret=%d"
,
ret
);
return
ret
;
}
srs_info
(
"open HLS muxer success. vhost=%s, path=%s"
,
vhost
.
c_str
(),
current
->
full_path
.
c_str
());
// ignore sequence header
if
(
sample
->
aac_packet_type
==
SrsCodecAudioTypeSequenceHeader
)
{
return
ret
;
}
}
int64_t
corrected_time
=
0
;
if
((
ret
=
jitter
->
correct
(
audio
,
0
,
0
,
&
corrected_time
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
int
SrsM3u8Muxer
::
flush_audio
(
SrsMpegtsFrame
*
af
,
SrsCodecBuffer
*
ab
)
{
int
ret
=
ERROR_SUCCESS
;
srs_assert
(
current
);
// the pts calc from rtmp/flv header.
int64_t
pts
=
corrected_time
*
90
;
// flush if audio delay exceed
if
(
pts
-
audio_buffer_start_pts
>
audio_delay
*
90
)
{
if
((
ret
=
flush_audio
())
!=
ERROR_SUCCESS
)
{
if
(
ab
->
size
<=
0
)
{
return
ret
;
}
}
// start buffer, set the audio_frame
if
(
audio_buffer
->
size
==
0
)
{
pts
=
aac_jitter
->
on_buffer_start
(
pts
,
sample
->
sound_rate
);
audio_frame
->
dts
=
audio_frame
->
pts
=
audio_buffer_start_pts
=
pts
;
audio_frame
->
pid
=
TS_AUDIO_PID
;
audio_frame
->
sid
=
TS_AUDIO_AAC
;
}
else
{
aac_jitter
->
on_buffer_continue
();
}
// write audio to cache.
if
((
ret
=
write_audio
())
!=
ERROR_SUCCESS
)
{
if
((
ret
=
current
->
muxer
->
write_audio
(
af
,
ab
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
// write cache to file.
if
(
audio_buffer
->
size
>
1024
*
1024
)
{
if
((
ret
=
flush_audio
())
!=
ERROR_SUCCESS
)
{
return
ret
;
}
}
// write success, clear and free the buffer
ab
->
free
();
return
ret
;
}
int
SrsHls
::
on_video
(
SrsSharedPtrMessage
*
video
)
int
SrsM3u8Muxer
::
flush_video
(
SrsMpegtsFrame
*
af
,
SrsCodecBuffer
*
ab
,
SrsMpegtsFrame
*
vf
,
SrsCodecBuffer
*
vb
)
{
int
ret
=
ERROR_SUCCESS
;
SrsAutoFree
(
SrsSharedPtrMessage
,
video
,
false
);
// TODO: maybe donot need to demux the avc?
if
(
!
hls_enabled
)
{
return
ret
;
}
sample
->
clear
();
if
((
ret
=
codec
->
video_avc_demux
(
video
->
payload
,
video
->
size
,
sample
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
srs_assert
(
current
);
if
(
codec
->
video_codec_id
!=
SrsCodecVideoAVC
)
{
return
ret
;
}
video_stream_dts
=
vf
->
dts
;
// ignore sequence header
if
(
sample
->
frame_type
==
SrsCodecVideoAVCFrameKeyFrame
&&
sample
->
avc_packet_type
==
SrsCodecVideoAVCTypeSequenceHeader
)
{
// 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;
}
*/
int64_t
corrected_time
=
0
;
if
((
ret
=
jitter
->
correct
(
video
,
0
,
0
,
&
corrected_time
))
!=
ERROR_SUCCESS
)
{
if
((
ret
=
segment_close
())
!=
ERROR_SUCCESS
)
{
srs_error
(
"m3u8 muxer close segment failed. ret=%d"
,
ret
);
return
ret
;
}
// write video to cache.
if
((
ret
=
write_video
())
!=
ERROR_SUCCESS
)
{
if
((
ret
=
segment_open
())
!=
ERROR_SUCCESS
)
{
srs_error
(
"m3u8 muxer open segment failed. ret=%d"
,
ret
);
return
ret
;
}
video_stream_dts
=
video_frame
->
dts
=
corrected_time
*
90
;
video_frame
->
pts
=
video_frame
->
dts
+
sample
->
cts
*
90
;
video_frame
->
pid
=
TS_VIDEO_PID
;
video_frame
->
sid
=
TS_VIDEO_AVC
;
video_frame
->
key
=
sample
->
frame_type
==
SrsCodecVideoAVCFrameKeyFrame
;
// reopen the muxer for a gop
srs_assert
(
current
);
if
(
sample
->
frame_type
==
SrsCodecVideoAVCFrameKeyFrame
)
{
if
(
current
->
duration
>=
hls_fragment
)
{
if
((
ret
=
reopen
())
!=
ERROR_SUCCESS
)
{
// 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
;
}
}
}
srs_assert
(
current
);
// update the duration of segment.
current
->
update_duration
(
video_stream_dts
);
if
((
ret
=
current
->
muxer
->
write_video
(
v
ideo_frame
,
video_buffer
))
!=
ERROR_SUCCESS
)
{
if
((
ret
=
current
->
muxer
->
write_video
(
v
f
,
vb
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
// write success, clear and free the buffer
v
ideo_buffer
->
free
();
v
b
->
free
();
return
ret
;
}
int
Srs
Hls
::
reopen
()
int
Srs
M3u8Muxer
::
segment_close
()
{
int
ret
=
ERROR_SUCCESS
;
// try to open the HLS muxer
if
(
!
config
->
get_hls_enabled
(
vhost
))
{
return
ret
;
}
// when close current segment, the current segment must not be NULL.
srs_assert
(
current
);
// TODO: check the audio and video, ensure both exsists.
// for use fixed mpegts header specifeid the audio and video pid.
// assert segment duplicate.
std
::
vector
<
SrsM3u8Segment
*>::
iterator
it
;
it
=
std
::
find
(
segments
.
begin
(),
segments
.
end
(),
current
);
srs_assert
(
it
==
segments
.
end
());
hls_enabled
=
true
;
SrsConfDirective
*
conf
=
NULL
;
hls_path
=
SRS_CONF_DEFAULT_HLS_PATH
;
if
((
conf
=
config
->
get_hls_path
(
vhost
))
!=
NULL
)
{
hls_path
=
conf
->
arg0
();
}
// TODO: create all parents dirs.
// create dir for app.
if
((
ret
=
create_dir
())
!=
ERROR_SUCCESS
)
{
return
ret
;
}
// close current segment and update the m3u8 file.
if
(
current
)
{
// assert segment duplicate.
std
::
vector
<
SrsM3u8Segment
*>::
iterator
it
;
it
=
std
::
find
(
segments
.
begin
(),
segments
.
end
(),
current
);
srs_assert
(
it
==
segments
.
end
());
// valid, add to segments.
segments
.
push_back
(
current
);
// valid, add to segments.
segments
.
push_back
(
current
);
srs_trace
(
"reap ts segment, sequence_no=%d, uri=%s, duration=%.2f, start=%"
PRId64
""
,
current
->
sequence_no
,
current
->
uri
.
c_str
(),
current
->
duration
,
...
...
@@ -855,47 +708,11 @@ int SrsHls::reopen()
srs_error
(
"refresh m3u8 failed. ret=%d"
,
ret
);
return
ret
;
}
}
// new segment.
current
=
new
SrsM3u8Segment
();
current
->
sequence_no
=
file_index
++
;
current
->
segment_start_dts
=
video_stream_dts
;
// generate filename.
char
filename
[
128
];
snprintf
(
filename
,
sizeof
(
filename
),
"%s-%d.ts"
,
stream
.
c_str
(),
current
->
sequence_no
);
// TODO: use temp file and rename it.
current
->
full_path
=
hls_path
;
current
->
full_path
+=
"/"
;
current
->
full_path
+=
app
;
current
->
full_path
+=
"/"
;
current
->
full_path
+=
filename
;
// TODO: support base url, and so on.
current
->
uri
=
filename
;
if
((
ret
=
current
->
muxer
->
open
(
current
->
full_path
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"open hls muxer failed. ret=%d"
,
ret
);
return
ret
;
}
srs_info
(
"open HLS muxer success. vhost=%s, path=%s"
,
vhost
.
c_str
(),
current
->
full_path
.
c_str
());
// segment open, flush the audio.
// @see: ngx_rtmp_hls_open_fragment
/* start fragment with audio to make iPhone happy */
if
(
current
->
muxer
->
fresh
())
{
if
((
ret
=
flush_audio
())
!=
ERROR_SUCCESS
)
{
return
ret
;
}
}
return
ret
;
}
int
Srs
Hls
::
refresh_m3u8
()
int
Srs
M3u8Muxer
::
refresh_m3u8
()
{
int
ret
=
ERROR_SUCCESS
;
...
...
@@ -925,7 +742,7 @@ int SrsHls::refresh_m3u8()
return
ret
;
}
int
Srs
Hls
::
_refresh_m3u8
(
int
&
fd
,
std
::
string
m3u8_file
)
int
Srs
M3u8Muxer
::
_refresh_m3u8
(
int
&
fd
,
std
::
string
m3u8_file
)
{
int
ret
=
ERROR_SUCCESS
;
...
...
@@ -1016,7 +833,7 @@ int SrsHls::_refresh_m3u8(int& fd, std::string m3u8_file)
return
ret
;
}
int
Srs
Hls
::
create_dir
()
int
Srs
M3u8Muxer
::
create_dir
()
{
int
ret
=
ERROR_SUCCESS
;
...
...
@@ -1039,7 +856,284 @@ int SrsHls::create_dir()
return
ret
;
}
int
SrsHls
::
write_audio
()
SrsHls
::
SrsHls
()
{
hls_enabled
=
false
;
codec
=
new
SrsCodec
();
sample
=
new
SrsCodecSample
();
jitter
=
new
SrsRtmpJitter
();
aac_jitter
=
new
SrsHlsAacJitter
();
ab
=
new
SrsCodecBuffer
();
vb
=
new
SrsCodecBuffer
();
af
=
new
SrsMpegtsFrame
();
vf
=
new
SrsMpegtsFrame
();
muxer
=
new
SrsM3u8Muxer
();
}
SrsHls
::~
SrsHls
()
{
srs_freep
(
codec
);
srs_freep
(
sample
);
srs_freep
(
jitter
);
srs_freep
(
aac_jitter
);
ab
->
free
();
vb
->
free
();
srs_freep
(
ab
);
srs_freep
(
vb
);
srs_freep
(
af
);
srs_freep
(
vf
);
srs_freep
(
muxer
);
}
int
SrsHls
::
on_publish
(
SrsRequest
*
req
)
{
int
ret
=
ERROR_SUCCESS
;
std
::
string
vhost
=
req
->
vhost
;
std
::
string
stream
=
req
->
stream
;
std
::
string
app
=
req
->
app
;
// TODO: support reload.
if
(
!
config
->
get_hls_enabled
(
vhost
))
{
return
ret
;
}
// if enabled, open the muxer.
hls_enabled
=
true
;
// TODO: subscribe the reload event.
int
hls_fragment
=
0
;
int
hls_window
=
0
;
SrsConfDirective
*
conf
=
NULL
;
if
((
conf
=
config
->
get_hls_fragment
(
vhost
))
!=
NULL
&&
!
conf
->
arg0
().
empty
())
{
hls_fragment
=
::
atoi
(
conf
->
arg0
().
c_str
());
}
if
(
hls_fragment
<=
0
)
{
hls_fragment
=
SRS_CONF_DEFAULT_HLS_FRAGMENT
;
}
if
((
conf
=
config
->
get_hls_window
(
vhost
))
!=
NULL
&&
!
conf
->
arg0
().
empty
())
{
hls_window
=
::
atoi
(
conf
->
arg0
().
c_str
());
}
if
(
hls_window
<=
0
)
{
hls_window
=
SRS_CONF_DEFAULT_HLS_WINDOW
;
}
// get the hls path config
std
::
string
hls_path
=
SRS_CONF_DEFAULT_HLS_PATH
;
if
((
conf
=
config
->
get_hls_path
(
vhost
))
!=
NULL
)
{
hls_path
=
conf
->
arg0
();
}
// 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
);
return
ret
;
}
return
ret
;
}
void
SrsHls
::
on_unpublish
()
{
int
ret
=
ERROR_SUCCESS
;
// close muxer when unpublish.
ret
=
muxer
->
flush_audio
(
af
,
ab
);
ret
+=
muxer
->
segment_close
();
if
(
ret
!=
ERROR_SUCCESS
)
{
srs_error
(
"ignore m3u8 muxer flush/close audio failed. ret=%d"
,
ret
);
return
;
}
hls_enabled
=
false
;
}
int
SrsHls
::
on_meta_data
(
SrsOnMetaDataPacket
*
metadata
)
{
int
ret
=
ERROR_SUCCESS
;
if
(
!
metadata
||
!
metadata
->
metadata
)
{
srs_trace
(
"no metadata persent, hls ignored it."
);
return
ret
;
}
SrsAmf0Object
*
obj
=
metadata
->
metadata
;
if
(
obj
->
size
()
<=
0
)
{
srs_trace
(
"no metadata persent, hls ignored it."
);
return
ret
;
}
// finger out the codec info from metadata if possible.
SrsAmf0Any
*
prop
=
NULL
;
if
((
prop
=
obj
->
get_property
(
"duration"
))
!=
NULL
&&
prop
->
is_number
())
{
codec
->
duration
=
(
int
)
srs_amf0_convert
<
SrsAmf0Number
>
(
prop
)
->
value
;
}
if
((
prop
=
obj
->
get_property
(
"width"
))
!=
NULL
&&
prop
->
is_number
())
{
codec
->
width
=
(
int
)
srs_amf0_convert
<
SrsAmf0Number
>
(
prop
)
->
value
;
}
if
((
prop
=
obj
->
get_property
(
"height"
))
!=
NULL
&&
prop
->
is_number
())
{
codec
->
height
=
(
int
)
srs_amf0_convert
<
SrsAmf0Number
>
(
prop
)
->
value
;
}
if
((
prop
=
obj
->
get_property
(
"framerate"
))
!=
NULL
&&
prop
->
is_number
())
{
codec
->
frame_rate
=
(
int
)
srs_amf0_convert
<
SrsAmf0Number
>
(
prop
)
->
value
;
}
if
((
prop
=
obj
->
get_property
(
"videocodecid"
))
!=
NULL
&&
prop
->
is_number
())
{
codec
->
video_codec_id
=
(
int
)
srs_amf0_convert
<
SrsAmf0Number
>
(
prop
)
->
value
;
}
if
((
prop
=
obj
->
get_property
(
"videodatarate"
))
!=
NULL
&&
prop
->
is_number
())
{
codec
->
video_data_rate
=
(
int
)(
1000
*
srs_amf0_convert
<
SrsAmf0Number
>
(
prop
)
->
value
);
}
if
((
prop
=
obj
->
get_property
(
"audiocodecid"
))
!=
NULL
&&
prop
->
is_number
())
{
codec
->
audio_codec_id
=
(
int
)
srs_amf0_convert
<
SrsAmf0Number
>
(
prop
)
->
value
;
}
if
((
prop
=
obj
->
get_property
(
"audiodatarate"
))
!=
NULL
&&
prop
->
is_number
())
{
codec
->
audio_data_rate
=
(
int
)(
1000
*
srs_amf0_convert
<
SrsAmf0Number
>
(
prop
)
->
value
);
}
// ignore the following, for each flv/rtmp packet contains them:
// audiosamplerate, sample->sound_rate
// audiosamplesize, sample->sound_size
// stereo, sample->sound_type
return
ret
;
}
int
SrsHls
::
on_audio
(
SrsSharedPtrMessage
*
audio
)
{
int
ret
=
ERROR_SUCCESS
;
SrsAutoFree
(
SrsSharedPtrMessage
,
audio
,
false
);
// TODO: maybe donot need to demux the aac?
if
(
!
hls_enabled
)
{
return
ret
;
}
sample
->
clear
();
if
((
ret
=
codec
->
audio_aac_demux
(
audio
->
payload
,
audio
->
size
,
sample
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
if
(
codec
->
audio_codec_id
!=
SrsCodecAudioAAC
)
{
return
ret
;
}
// ignore sequence header
if
(
sample
->
aac_packet_type
==
SrsCodecAudioTypeSequenceHeader
)
{
return
ret
;
}
int64_t
corrected_time
=
0
;
if
((
ret
=
jitter
->
correct
(
audio
,
0
,
0
,
&
corrected_time
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
// the pts calc from rtmp/flv header.
int64_t
pts
=
corrected_time
*
90
;
// start buffer, set the af
if
(
ab
->
size
==
0
)
{
pts
=
aac_jitter
->
on_buffer_start
(
pts
,
sample
->
sound_rate
);
af
->
dts
=
af
->
pts
=
audio_buffer_start_pts
=
pts
;
af
->
pid
=
TS_AUDIO_PID
;
af
->
sid
=
TS_AUDIO_AAC
;
}
else
{
aac_jitter
->
on_buffer_continue
();
}
// write audio to cache.
if
((
ret
=
cache_audio
())
!=
ERROR_SUCCESS
)
{
return
ret
;
}
// flush if buffer exceed max size.
if
(
ab
->
size
>
SRS_HLS_AUDIO_CACHE_SIZE
)
{
if
((
ret
=
muxer
->
flush_audio
(
af
,
ab
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
}
// TODO: config it.
// in ms, audio delay to flush the audios.
int64_t
audio_delay
=
SRS_CONF_DEFAULT_AAC_DELAY
;
// flush if audio delay exceed
if
(
pts
-
audio_buffer_start_pts
>
audio_delay
*
90
)
{
if
((
ret
=
muxer
->
flush_audio
(
af
,
ab
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
}
return
ret
;
}
int
SrsHls
::
on_video
(
SrsSharedPtrMessage
*
video
)
{
int
ret
=
ERROR_SUCCESS
;
SrsAutoFree
(
SrsSharedPtrMessage
,
video
,
false
);
// TODO: maybe donot need to demux the avc?
if
(
!
hls_enabled
)
{
return
ret
;
}
sample
->
clear
();
if
((
ret
=
codec
->
video_avc_demux
(
video
->
payload
,
video
->
size
,
sample
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
if
(
codec
->
video_codec_id
!=
SrsCodecVideoAVC
)
{
return
ret
;
}
// ignore sequence header
if
(
sample
->
frame_type
==
SrsCodecVideoAVCFrameKeyFrame
&&
sample
->
avc_packet_type
==
SrsCodecVideoAVCTypeSequenceHeader
)
{
return
ret
;
}
int64_t
corrected_time
=
0
;
if
((
ret
=
jitter
->
correct
(
video
,
0
,
0
,
&
corrected_time
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
// write video to cache.
if
((
ret
=
cache_video
())
!=
ERROR_SUCCESS
)
{
return
ret
;
}
vf
->
dts
=
corrected_time
*
90
;
vf
->
pts
=
vf
->
dts
+
sample
->
cts
*
90
;
vf
->
pid
=
TS_VIDEO_PID
;
vf
->
sid
=
TS_VIDEO_AVC
;
vf
->
key
=
sample
->
frame_type
==
SrsCodecVideoAVCFrameKeyFrame
;
// 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
SrsHls
::
cache_audio
()
{
int
ret
=
ERROR_SUCCESS
;
...
...
@@ -1099,19 +1193,19 @@ int SrsHls::write_audio()
adts_header
[
5
]
|=
0x1f
;
// copy to audio buffer
audio_buffer
->
append
(
adts_header
,
sizeof
(
adts_header
));
audio_buffer
->
append
(
buf
->
bytes
,
buf
->
size
);
ab
->
append
(
adts_header
,
sizeof
(
adts_header
));
ab
->
append
(
buf
->
bytes
,
buf
->
size
);
}
return
ret
;
}
int
SrsHls
::
writ
e_video
()
int
SrsHls
::
cach
e_video
()
{
int
ret
=
ERROR_SUCCESS
;
static
u_int8_t
aud_nal
[]
=
{
0x00
,
0x00
,
0x00
,
0x01
,
0x09
,
0xf0
};
v
ideo_buffer
->
append
(
aud_nal
,
sizeof
(
aud_nal
));
v
b
->
append
(
aud_nal
,
sizeof
(
aud_nal
));
bool
sps_pps_sent
=
false
;
for
(
int
i
=
0
;
i
<
sample
->
nb_buffers
;
i
++
)
{
...
...
@@ -1138,21 +1232,21 @@ int SrsHls::write_video()
// 5: Coded slice of an IDR picture.
// insert sps/pps before IDR or key frame is ok.
if
(
nal_unit_type
==
5
&&
!
sps_pps_sent
)
{
//if (v
ideo_frame
->key && !sps_pps_sent) {
//if (v
f
->key && !sps_pps_sent) {
sps_pps_sent
=
true
;
// ngx_rtmp_hls_append_sps_pps
if
(
codec
->
sequenceParameterSetLength
>
0
)
{
// AnnexB prefix
v
ideo_buffer
->
append
(
aud_nal
,
4
);
v
b
->
append
(
aud_nal
,
4
);
// sps
v
ideo_buffer
->
append
(
codec
->
sequenceParameterSetNALUnit
,
codec
->
sequenceParameterSetLength
);
v
b
->
append
(
codec
->
sequenceParameterSetNALUnit
,
codec
->
sequenceParameterSetLength
);
}
if
(
codec
->
pictureParameterSetLength
>
0
)
{
// AnnexB prefix
v
ideo_buffer
->
append
(
aud_nal
,
4
);
v
b
->
append
(
aud_nal
,
4
);
// pps
v
ideo_buffer
->
append
(
codec
->
pictureParameterSetNALUnit
,
codec
->
pictureParameterSetLength
);
v
b
->
append
(
codec
->
pictureParameterSetNALUnit
,
codec
->
pictureParameterSetLength
);
}
}
...
...
@@ -1164,30 +1258,12 @@ int SrsHls::write_video()
if
(
i
==
0
)
{
p
=
aud_nal
;
}
v
ideo_buffer
->
append
(
p
,
end
-
p
);
v
b
->
append
(
p
,
end
-
p
);
// sample data
video_buffer
->
append
(
buf
->
bytes
,
buf
->
size
);
}
return
ret
;
}
int
SrsHls
::
flush_audio
()
{
int
ret
=
ERROR_SUCCESS
;
if
(
audio_buffer
->
size
<=
0
)
{
return
ret
;
vb
->
append
(
buf
->
bytes
,
buf
->
size
);
}
if
((
ret
=
current
->
muxer
->
write_audio
(
audio_frame
,
audio_buffer
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
// write success, clear and free the buffer
audio_buffer
->
free
();
return
ret
;
}
...
...
trunk/src/core/srs_core_hls.hpp
查看文件 @
1c2f505
...
...
@@ -74,6 +74,22 @@ public:
virtual
void
on_buffer_continue
();
};
//TODO: refine the ts muxer, do more jobs.
class
SrsTSMuxer
{
private
:
int
fd
;
std
::
string
path
;
public
:
SrsTSMuxer
();
virtual
~
SrsTSMuxer
();
public
:
virtual
int
open
(
std
::
string
_path
);
virtual
int
write_audio
(
SrsMpegtsFrame
*
af
,
SrsCodecBuffer
*
ab
);
virtual
int
write_video
(
SrsMpegtsFrame
*
vf
,
SrsCodecBuffer
*
vb
);
virtual
void
close
();
};
/**
* 3.3.2. EXTINF
* The EXTINF tag specifies the duration of a media segment.
...
...
@@ -102,33 +118,15 @@ struct SrsM3u8Segment
virtual
double
update_duration
(
int64_t
video_stream_dts
);
};
//TODO: refine the ts muxer, do more jobs.
class
SrsTSMuxer
{
private
:
int
fd
;
std
::
string
path
;
bool
_fresh
;
public
:
SrsTSMuxer
();
virtual
~
SrsTSMuxer
();
public
:
virtual
int
open
(
std
::
string
_path
);
virtual
int
write_audio
(
SrsMpegtsFrame
*
audio_frame
,
SrsCodecBuffer
*
audio_buffer
);
virtual
int
write_video
(
SrsMpegtsFrame
*
video_frame
,
SrsCodecBuffer
*
video_buffer
);
virtual
void
close
();
virtual
bool
fresh
();
};
/**
*
write m3u8 hl
s.
*
muxer the m3u8 and ts file
s.
*/
class
Srs
Hls
class
Srs
M3u8Muxer
{
private
:
std
::
string
vhost
;
std
::
string
stream
;
std
::
string
app
;
std
::
string
stream
;
private
:
std
::
string
hls_path
;
int
hls_fragment
;
int
hls_window
;
...
...
@@ -144,16 +142,37 @@ private:
* current writing segment.
*/
SrsM3u8Segment
*
current
;
// current frame and buffer
SrsMpegtsFrame
*
audio_frame
;
SrsCodecBuffer
*
audio_buffer
;
SrsMpegtsFrame
*
video_frame
;
SrsCodecBuffer
*
video_buffer
;
// last known dts
int64_t
video_stream_dts
;
public
:
SrsM3u8Muxer
();
virtual
~
SrsM3u8Muxer
();
public
:
virtual
int
update_config
(
std
::
string
_app
,
std
::
string
_stream
,
std
::
string
path
,
int
fragment
,
int
window
);
virtual
int
segment_open
();
virtual
int
flush_audio
(
SrsMpegtsFrame
*
af
,
SrsCodecBuffer
*
ab
);
virtual
int
flush_video
(
SrsMpegtsFrame
*
af
,
SrsCodecBuffer
*
ab
,
SrsMpegtsFrame
*
vf
,
SrsCodecBuffer
*
vb
);
virtual
int
segment_close
();
private
:
virtual
int
refresh_m3u8
();
virtual
int
_refresh_m3u8
(
int
&
fd
,
std
::
string
m3u8_file
);
virtual
int
create_dir
();
};
/**
* write m3u8 hls.
*/
class
SrsHls
{
private
:
SrsM3u8Muxer
*
muxer
;
// current frame and buffer
SrsMpegtsFrame
*
af
;
SrsCodecBuffer
*
ab
;
SrsMpegtsFrame
*
vf
;
SrsCodecBuffer
*
vb
;
// the audio cache buffer start pts, to flush audio if full.
int64_t
audio_buffer_start_pts
;
// in ms, audio delay to flush the audios.
int64_t
audio_delay
;
private
:
bool
hls_enabled
;
SrsCodec
*
codec
;
...
...
@@ -170,14 +189,8 @@ public:
virtual
int
on_audio
(
SrsSharedPtrMessage
*
audio
);
virtual
int
on_video
(
SrsSharedPtrMessage
*
video
);
private
:
virtual
int
reopen
();
virtual
int
refresh_m3u8
();
virtual
int
_refresh_m3u8
(
int
&
fd
,
std
::
string
m3u8_file
);
virtual
int
create_dir
();
private
:
virtual
int
write_audio
();
virtual
int
write_video
();
virtual
int
flush_audio
();
virtual
int
cache_audio
();
virtual
int
cache_video
();
};
#endif
...
...
请
注册
或
登录
后发表评论