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-11-26 16:06:58 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
0c72c56f745bca5bba6332d7739ffff1bc7ec241
0c72c56f
1 parent
749b7bdb
support HLS(m3u8)
隐藏空白字符变更
内嵌
并排对比
正在显示
5 个修改的文件
包含
449 行增加
和
227 行删除
trunk/src/core/srs_core_client.cpp
trunk/src/core/srs_core_hls.cpp
trunk/src/core/srs_core_hls.hpp
trunk/src/core/srs_core_source.cpp
trunk/src/core/srs_core_source.hpp
trunk/src/core/srs_core_client.cpp
查看文件 @
0c72c56
...
...
@@ -332,7 +332,7 @@ int SrsClient::publish(SrsSource* source, bool is_fmle)
SrsPithyPrint
pithy_print
(
SRS_STAGE_PUBLISH_USER
);
// notify the hls to prepare when publish start.
if
((
ret
=
source
->
on_publish
(
req
->
vhost
))
!=
ERROR_SUCCESS
)
{
if
((
ret
=
source
->
on_publish
(
req
->
vhost
,
req
->
stream
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"hls on_publish failed. ret=%d"
,
ret
);
return
ret
;
}
...
...
trunk/src/core/srs_core_hls.cpp
查看文件 @
0c72c56
...
...
@@ -37,194 +37,18 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_core_source.hpp>
#include <srs_core_autofree.hpp>
SrsHLS
::
SrsHLS
()
{
hls_enabled
=
false
;
codec
=
new
SrsCodec
();
sample
=
new
SrsCodecSample
();
muxer
=
NULL
;
jitter
=
new
SrsRtmpJitter
();
}
SrsHLS
::~
SrsHLS
()
{
srs_freep
(
codec
);
srs_freep
(
sample
);
srs_freep
(
muxer
);
srs_freep
(
jitter
);
}
int
SrsHLS
::
on_publish
(
std
::
string
_vhost
)
{
int
ret
=
ERROR_SUCCESS
;
// TODO: check config.
if
(
muxer
)
{
hls_enabled
=
true
;
srs_trace
(
"hls is reopen, continue streaming HLS, vhost=%s"
,
_vhost
.
c_str
());
return
ret
;
}
vhost
=
_vhost
;
muxer
=
new
SrsTSMuxer
();
// try to open the HLS muxer
SrsConfDirective
*
conf
=
config
->
get_hls
(
vhost
);
if
(
!
conf
&&
conf
->
arg0
()
==
"off"
)
{
return
ret
;
}
// TODO: check the audio and video, ensure both exsists.
// for use fixed mpegts header specifeid the audio and video pid.
hls_enabled
=
true
;
std
::
string
path
=
SRS_CONF_DEFAULT_HLS_PATH
;
if
((
conf
=
config
->
get_hls_path
(
vhost
))
!=
NULL
)
{
path
=
conf
->
arg0
();
}
// TODO: generate by m3u8 muxer.
path
+=
"/1.ts"
;
if
((
ret
=
muxer
->
open
(
path
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"open hls muxer failed. ret=%d"
,
ret
);
return
ret
;
}
return
ret
;
}
void
SrsHLS
::
on_unpublish
()
{
hls_enabled
=
false
;
//muxer->close();
//srs_freep(muxer);
}
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
;
}
// @see: NGX_RTMP_HLS_DELAY,
// 63000: 700ms, ts_tbn=90000
#define SRS_HLS_DELAY 63000
int
SrsHLS
::
on_audio
(
SrsSharedPtrMessage
*
audio
)
{
int
ret
=
ERROR_SUCCESS
;
SrsAutoFree
(
SrsSharedPtrMessage
,
audio
,
false
);
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
;
}
// TODO: maybe donot need to demux the aac?
if
(
!
hls_enabled
)
{
return
ret
;
}
// ignore sequence header
if
(
sample
->
aac_packet_type
==
SrsCodecAudioTypeSequenceHeader
)
{
return
ret
;
}
if
((
ret
=
jitter
->
correct
(
audio
,
0
,
0
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
if
((
ret
=
muxer
->
write_audio
(
audio
->
header
.
timestamp
,
codec
,
sample
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
return
ret
;
}
// the mpegts header specifed the video/audio pid.
#define TS_VIDEO_PID 256
#define TS_AUDIO_PID 257
int
SrsHLS
::
on_video
(
SrsSharedPtrMessage
*
video
)
{
int
ret
=
ERROR_SUCCESS
;
SrsAutoFree
(
SrsSharedPtrMessage
,
video
,
false
);
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
;
}
// TODO: maybe donot need to demux the avc?
if
(
!
hls_enabled
)
{
return
ret
;
}
// ignore sequence header
if
(
sample
->
frame_type
==
SrsCodecVideoAVCFrameKeyFrame
&&
sample
->
avc_packet_type
==
SrsCodecVideoAVCTypeSequenceHeader
)
{
return
ret
;
}
if
((
ret
=
jitter
->
correct
(
video
,
0
,
0
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
if
((
ret
=
muxer
->
write_video
(
video
->
header
.
timestamp
,
codec
,
sample
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
return
ret
;
}
// ts aac stream id.
#define TS_AUDIO_AAC 0xc0
// ts avc stream id.
#define TS_VIDEO_AVC 0xe0
// @see: ngx_rtmp_mpegts_header
u_int8_t
mpegts_header
[]
=
{
...
...
@@ -287,10 +111,6 @@ u_int8_t mpegts_header[] = {
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
};
// @see: NGX_RTMP_HLS_DELAY,
// 63000: 700ms, ts_tbn=90000
#define SRS_HLS_DELAY 63000
// @see: ngx_rtmp_SrsMpegtsFrame_t
struct
SrsMpegtsFrame
{
...
...
@@ -527,18 +347,29 @@ private:
}
};
// the mpegts header specifed the video/audio pid.
#define TS_VIDEO_PID 256
#define TS_AUDIO_PID 257
SrsM3u8Segment
::
SrsM3u8Segment
()
{
duration
=
0
;
sequence_no
=
0
;
muxer
=
new
SrsTSMuxer
();
segment_start_dts
=
0
;
}
// ts aac stream id.
#define TS_AUDIO_AAC 0xc0
// ts avc stream id.
#define TS_VIDEO_AVC 0xe0
SrsM3u8Segment
::~
SrsM3u8Segment
()
{
muxer
->
close
();
srs_freep
(
muxer
);
}
Srs
TSMuxer
::
SrsTSMuxer
()
Srs
HLS
::
SrsHLS
()
{
fd
=
-
1
;
hls_enabled
=
false
;
codec
=
new
SrsCodec
();
sample
=
new
SrsCodecSample
();
current
=
NULL
;
jitter
=
new
SrsRtmpJitter
();
file_index
=
0
;
m3u8_dts
=
stream_dts
=
0
;
audio_buffer
=
new
SrsCodecBuffer
();
video_buffer
=
new
SrsCodecBuffer
();
...
...
@@ -547,9 +378,20 @@ SrsTSMuxer::SrsTSMuxer()
video_frame
=
new
SrsMpegtsFrame
();
}
Srs
TSMuxer
::~
SrsTSMuxer
()
Srs
HLS
::~
SrsHLS
()
{
close
();
srs_freep
(
codec
);
srs_freep
(
sample
);
srs_freep
(
jitter
);
std
::
vector
<
SrsM3u8Segment
*>::
iterator
it
;
for
(
it
=
segments
.
begin
();
it
!=
segments
.
end
();
++
it
)
{
SrsM3u8Segment
*
segment
=
*
it
;
srs_freep
(
segment
);
}
segments
.
clear
();
srs_freep
(
current
);
audio_buffer
->
free
();
video_buffer
->
free
();
...
...
@@ -561,6 +403,346 @@ SrsTSMuxer::~SrsTSMuxer()
srs_freep
(
video_frame
);
}
int
SrsHLS
::
on_publish
(
std
::
string
_vhost
,
std
::
string
_stream
)
{
int
ret
=
ERROR_SUCCESS
;
vhost
=
_vhost
;
stream
=
_stream
;
if
((
ret
=
reopen
())
!=
ERROR_SUCCESS
)
{
return
ret
;
}
return
ret
;
}
void
SrsHLS
::
on_unpublish
()
{
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
;
}
if
((
ret
=
jitter
->
correct
(
audio
,
0
,
0
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
srs_assert
(
current
);
stream_dts
=
audio_frame
->
dts
=
audio_frame
->
pts
=
audio
->
header
.
timestamp
*
90
;
audio_frame
->
pid
=
TS_AUDIO_PID
;
audio_frame
->
sid
=
TS_AUDIO_AAC
;
if
((
ret
=
current
->
muxer
->
write_audio
(
audio_frame
,
audio_buffer
,
codec
,
sample
))
!=
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
;
}
if
((
ret
=
jitter
->
correct
(
video
,
0
,
0
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
stream_dts
=
video_frame
->
dts
=
video
->
header
.
timestamp
*
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
if
(
sample
->
frame_type
==
SrsCodecVideoAVCFrameKeyFrame
)
{
int64_t
diff
=
stream_dts
-
m3u8_dts
;
// 10s.
if
(
diff
/
90000
>=
10
)
{
if
((
ret
=
reopen
())
!=
ERROR_SUCCESS
)
{
return
ret
;
}
}
}
srs_assert
(
current
);
if
((
ret
=
current
->
muxer
->
write_video
(
video_frame
,
video_buffer
,
codec
,
sample
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
return
ret
;
}
int
SrsHLS
::
reopen
()
{
int
ret
=
ERROR_SUCCESS
;
// try to open the HLS muxer
SrsConfDirective
*
conf
=
config
->
get_hls
(
vhost
);
if
(
!
conf
&&
conf
->
arg0
()
==
"off"
)
{
return
ret
;
}
// TODO: check the audio and video, ensure both exsists.
// for use fixed mpegts header specifeid the audio and video pid.
hls_enabled
=
true
;
hls_path
=
SRS_CONF_DEFAULT_HLS_PATH
;
if
((
conf
=
config
->
get_hls_path
(
vhost
))
!=
NULL
)
{
hls_path
=
conf
->
arg0
();
}
// start new segment.
if
(
current
)
{
current
->
duration
=
(
stream_dts
-
current
->
segment_start_dts
)
/
90000.0
;
segments
.
push_back
(
current
);
current
=
NULL
;
if
((
ret
=
refresh_m3u8
())
!=
ERROR_SUCCESS
)
{
return
ret
;
}
}
// new segment.
current
=
new
SrsM3u8Segment
();
m3u8_dts
=
current
->
segment_start_dts
=
stream_dts
;
// generate filename.
char
filename
[
128
];
snprintf
(
filename
,
sizeof
(
filename
),
"%s-%d.ts"
,
stream
.
c_str
(),
file_index
++
);
current
->
full_path
=
hls_path
;
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_trace
(
"open HLS muxer success. vhost=%s, path=%s"
,
vhost
.
c_str
(),
current
->
full_path
.
c_str
());
return
ret
;
}
int
SrsHLS
::
refresh_m3u8
()
{
int
ret
=
ERROR_SUCCESS
;
int
fd
=
-
1
;
ret
=
_refresh_m3u8
(
fd
);
if
(
fd
>=
0
)
{
close
(
fd
);
}
return
ret
;
}
int
SrsHLS
::
_refresh_m3u8
(
int
&
fd
)
{
int
ret
=
ERROR_SUCCESS
;
// no segments, return.
if
(
segments
.
size
()
==
0
)
{
return
ret
;
}
m3u8
=
hls_path
;
m3u8
+=
"/"
;
m3u8
+=
stream
;
m3u8
+=
".m3u8"
;
int
flags
=
O_CREAT
|
O_WRONLY
|
O_TRUNC
;
mode_t
mode
=
S_IRUSR
|
S_IWUSR
|
S_IRGRP
|
S_IWGRP
|
S_IROTH
;
if
((
fd
=
::
open
(
m3u8
.
c_str
(),
flags
,
mode
))
<
0
)
{
ret
=
ERROR_HLS_OPEN_FAILED
;
srs_error
(
"open m3u8 file %s failed. ret=%d"
,
m3u8
.
c_str
(),
ret
);
return
ret
;
}
srs_info
(
"open m3u8 file %s success."
,
m3u8
.
c_str
());
// #EXTM3U\n#EXT-X-VERSION:3\n
char
header
[]
=
{
// #EXTM3U\n
0x23
,
0x45
,
0x58
,
0x54
,
0x4d
,
0x33
,
0x55
,
0xa
,
// #EXT-X-VERSION:3\n
0x23
,
0x45
,
0x58
,
0x54
,
0x2d
,
0x58
,
0x2d
,
0x56
,
0x45
,
0x52
,
0x53
,
0x49
,
0x4f
,
0x4e
,
0x3a
,
0x33
,
0xa
};
if
(
::
write
(
fd
,
header
,
sizeof
(
header
))
!=
sizeof
(
header
))
{
ret
=
ERROR_HLS_WRITE_FAILED
;
srs_error
(
"write m3u8 header failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"write m3u8 header success."
);
// #EXT-X-MEDIA-SEQUENCE:4294967295\n
SrsM3u8Segment
*
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
)
{
ret
=
ERROR_HLS_WRITE_FAILED
;
srs_error
(
"write m3u8 sequence failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"write m3u8 sequence success."
);
// #EXT-X-TARGETDURATION:4294967295\n
int
target_duration
=
0
;
std
::
vector
<
SrsM3u8Segment
*>::
iterator
it
;
for
(
it
=
segments
.
begin
();
it
!=
segments
.
end
();
++
it
)
{
SrsM3u8Segment
*
segment
=
*
it
;
target_duration
=
srs_max
(
target_duration
,
(
int
)
segment
->
duration
);
}
// TODO: maybe need to take an around value
target_duration
+=
1
;
char
duration
[
34
];
len
=
snprintf
(
duration
,
sizeof
(
duration
),
"#EXT-X-TARGETDURATION:%d
\n
"
,
target_duration
);
if
(
::
write
(
fd
,
duration
,
len
)
!=
len
)
{
ret
=
ERROR_HLS_WRITE_FAILED
;
srs_error
(
"write m3u8 duration failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"write m3u8 duration success."
);
// write all segments
for
(
it
=
segments
.
begin
();
it
!=
segments
.
end
();
++
it
)
{
SrsM3u8Segment
*
segment
=
*
it
;
// "#EXTINF:4294967295.208,\n"
char
ext_info
[
25
];
len
=
snprintf
(
ext_info
,
sizeof
(
ext_info
),
"#EXTINF:%.3f
\n
"
,
segment
->
duration
);
if
(
::
write
(
fd
,
ext_info
,
len
)
!=
len
)
{
ret
=
ERROR_HLS_WRITE_FAILED
;
srs_error
(
"write m3u8 segment failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"write m3u8 segment success."
);
// file name
std
::
string
filename
=
segment
->
uri
;
filename
+=
"
\n
"
;
if
(
::
write
(
fd
,
filename
.
c_str
(),
filename
.
length
())
!=
(
int
)
filename
.
length
())
{
ret
=
ERROR_HLS_WRITE_FAILED
;
srs_error
(
"write m3u8 segment uri failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"write m3u8 segment uri success."
);
}
srs_info
(
"write m3u8 %s success."
,
m3u8
.
c_str
());
return
ret
;
}
SrsTSMuxer
::
SrsTSMuxer
()
{
fd
=
-
1
;
}
SrsTSMuxer
::~
SrsTSMuxer
()
{
close
();
}
int
SrsTSMuxer
::
open
(
std
::
string
_path
)
{
int
ret
=
ERROR_SUCCESS
;
...
...
@@ -585,14 +767,10 @@ int SrsTSMuxer::open(std::string _path)
return
ret
;
}
int
SrsTSMuxer
::
write_audio
(
u_int32_t
time
,
SrsCodec
*
codec
,
SrsCodecSample
*
sample
)
int
SrsTSMuxer
::
write_audio
(
SrsMpegtsFrame
*
audio_frame
,
SrsCodecBuffer
*
audio_buffer
,
SrsCodec
*
codec
,
SrsCodecSample
*
sample
)
{
int
ret
=
ERROR_SUCCESS
;
audio_frame
->
dts
=
audio_frame
->
pts
=
time
*
90
;
audio_frame
->
pid
=
TS_AUDIO_PID
;
audio_frame
->
sid
=
TS_AUDIO_AAC
;
for
(
int
i
=
0
;
i
<
sample
->
nb_buffers
;
i
++
)
{
SrsCodecBuffer
*
buf
=
&
sample
->
buffers
[
i
];
int32_t
size
=
buf
->
size
;
...
...
@@ -660,16 +838,10 @@ int SrsTSMuxer::write_audio(u_int32_t time, SrsCodec* codec, SrsCodecSample* sam
return
ret
;
}
int
SrsTSMuxer
::
write_video
(
u_int32_t
time
,
SrsCodec
*
codec
,
SrsCodecSample
*
sample
)
int
SrsTSMuxer
::
write_video
(
SrsMpegtsFrame
*
video_frame
,
SrsCodecBuffer
*
video_buffer
,
SrsCodec
*
codec
,
SrsCodecSample
*
sample
)
{
int
ret
=
ERROR_SUCCESS
;
video_frame
->
dts
=
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
;
static
u_int8_t
aud_nal
[]
=
{
0x00
,
0x00
,
0x00
,
0x01
,
0x09
,
0xf0
};
video_buffer
->
append
(
aud_nal
,
sizeof
(
aud_nal
));
...
...
trunk/src/core/srs_core_hls.hpp
查看文件 @
0c72c56
...
...
@@ -30,34 +30,88 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_core.hpp>
#include <string>
#include <vector>
class
SrsOnMetaDataPacket
;
class
SrsSharedPtrMessage
;
class
SrsCodecSample
;
class
SrsCodecBuffer
;
class
SrsMpegtsFrame
;
class
SrsRtmpJitter
;
class
SrsTSMuxer
;
class
SrsCodec
;
class
SrsRtmpJitter
;
/**
* 3.3.2. EXTINF
* The EXTINF tag specifies the duration of a media segment.
*/
struct
SrsM3u8Segment
{
// 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 muxer to write ts.
SrsTSMuxer
*
muxer
;
// current segment start dts for m3u8
int64_t
segment_start_dts
;
SrsM3u8Segment
();
virtual
~
SrsM3u8Segment
();
};
/**
* write m3u8 hls.
*/
class
SrsHLS
{
private
:
std
::
string
vhost
;
std
::
string
stream
;
std
::
string
hls_path
;
private
:
int
file_index
;
std
::
string
m3u8
;
private
:
/**
* m3u8 segments.
*/
std
::
vector
<
SrsM3u8Segment
*>
segments
;
/**
* 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
stream_dts
;
// last segment dts in m3u8
int64_t
m3u8_dts
;
private
:
bool
hls_enabled
;
SrsCodec
*
codec
;
SrsCodecSample
*
sample
;
SrsTSMuxer
*
muxer
;
SrsRtmpJitter
*
jitter
;
public
:
SrsHLS
();
virtual
~
SrsHLS
();
public
:
virtual
int
on_publish
(
std
::
string
_vhost
);
virtual
int
on_publish
(
std
::
string
_vhost
,
std
::
string
_stream
);
virtual
void
on_unpublish
();
virtual
int
on_meta_data
(
SrsOnMetaDataPacket
*
metadata
);
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
);
};
class
SrsTSMuxer
...
...
@@ -65,18 +119,13 @@ class SrsTSMuxer
private
:
int
fd
;
std
::
string
path
;
private
:
SrsMpegtsFrame
*
audio_frame
;
SrsCodecBuffer
*
audio_buffer
;
SrsMpegtsFrame
*
video_frame
;
SrsCodecBuffer
*
video_buffer
;
public
:
SrsTSMuxer
();
virtual
~
SrsTSMuxer
();
public
:
virtual
int
open
(
std
::
string
_path
);
virtual
int
write_audio
(
u_int32_t
time
,
SrsCodec
*
codec
,
SrsCodecSample
*
sample
);
virtual
int
write_video
(
u_int32_t
time
,
SrsCodec
*
codec
,
SrsCodecSample
*
sample
);
virtual
int
write_audio
(
SrsMpegtsFrame
*
audio_frame
,
SrsCodecBuffer
*
audio_buffer
,
SrsCodec
*
codec
,
SrsCodecSample
*
sample
);
virtual
int
write_video
(
SrsMpegtsFrame
*
video_frame
,
SrsCodecBuffer
*
video_buffer
,
SrsCodec
*
codec
,
SrsCodecSample
*
sample
);
virtual
void
close
();
};
...
...
trunk/src/core/srs_core_source.cpp
查看文件 @
0c72c56
...
...
@@ -413,6 +413,7 @@ int SrsSource::on_video(SrsCommonMessage* video)
}
srs_verbose
(
"initialize shared ptr video success."
);
// TODO: when return error, crash.
if
((
ret
=
hls
->
on_video
(
msg
->
copy
()))
!=
ERROR_SUCCESS
)
{
srs_error
(
"hls process video message failed. ret=%d"
,
ret
);
return
ret
;
...
...
@@ -451,9 +452,9 @@ int SrsSource::on_video(SrsCommonMessage* video)
return
ret
;
}
int
SrsSource
::
on_publish
(
std
::
string
vhost
)
int
SrsSource
::
on_publish
(
std
::
string
vhost
,
std
::
string
stream
)
{
return
hls
->
on_publish
(
vhost
);
return
hls
->
on_publish
(
vhost
,
stream
);
}
void
SrsSource
::
on_unpublish
()
...
...
trunk/src/core/srs_core_source.hpp
查看文件 @
0c72c56
...
...
@@ -166,7 +166,7 @@ public:
virtual
int
on_meta_data
(
SrsCommonMessage
*
msg
,
SrsOnMetaDataPacket
*
metadata
);
virtual
int
on_audio
(
SrsCommonMessage
*
audio
);
virtual
int
on_video
(
SrsCommonMessage
*
video
);
virtual
int
on_publish
(
std
::
string
vhost
);
virtual
int
on_publish
(
std
::
string
vhost
,
std
::
string
stream
);
virtual
void
on_unpublish
();
public
:
virtual
int
create_consumer
(
SrsConsumer
*&
consumer
);
...
...
请
注册
或
登录
后发表评论