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
wenjie.zhao
2015-03-11 13:34:58 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
d4198ee8ba1ca8178d577adf931e9655ba961247
d4198ee8
1 parent
1cb8e44b
add hds supported.
显示空白字符变更
内嵌
并排对比
正在显示
8 个修改的文件
包含
939 行增加
和
2 行删除
trunk/configure
trunk/src/app/srs_app_config.cpp
trunk/src/app/srs_app_config.hpp
trunk/src/app/srs_app_hds.cpp
trunk/src/app/srs_app_hds.hpp
trunk/src/app/srs_app_source.cpp
trunk/src/app/srs_app_source.hpp
trunk/src/kernel/srs_kernel_error.hpp
trunk/configure
查看文件 @
d4198ee
...
...
@@ -392,7 +392,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
"srs_app_json"
"srs_app_ingest"
"srs_app_ffmpeg"
"srs_app_utility"
"srs_app_dvr"
"srs_app_edge"
"srs_app_kbps"
"srs_app_heartbeat"
"srs_app_empty"
"srs_app_http_client"
"srs_app_recv_thread"
"srs_app_security"
"srs_app_statistic"
"srs_app_mpegts_udp"
"srs_app_rtsp"
"srs_app_listener"
)
"srs_app_mpegts_udp"
"srs_app_rtsp"
"srs_app_listener"
"srs_app_hds"
)
APP_INCS
=
"src/app"
;
MODULE_DIR
=
${
APP_INCS
}
. auto/modules.sh
APP_OBJS
=
"
${
MODULE_OBJS
[@]
}
"
fi
...
...
trunk/src/app/srs_app_config.cpp
查看文件 @
d4198ee
...
...
@@ -1407,6 +1407,7 @@ int SrsConfig::check_config()
&&
n
!=
"mr"
&&
n
!=
"mw_latency"
&&
n
!=
"min_latency"
&&
n
!=
"security"
&&
n
!=
"http_remux"
&&
n
!=
"http"
&&
n
!=
"http_static"
&&
n
!=
"hds"
)
{
ret
=
ERROR_SYSTEM_CONFIG_INVALID
;
srs_error
(
"unsupported vhost directive %s, ret=%d"
,
n
.
c_str
(),
ret
);
...
...
@@ -3310,6 +3311,85 @@ string SrsConfig::get_hls_vcodec(string vhost)
return
conf
->
arg0
();
}
SrsConfDirective
*
SrsConfig
::
get_hds
(
const
string
&
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
if
(
!
conf
)
{
return
NULL
;
}
return
conf
->
get
(
"hds"
);
}
bool
SrsConfig
::
get_hds_enabled
(
const
string
&
vhost
)
{
SrsConfDirective
*
hds
=
get_hds
(
vhost
);
if
(
!
hds
)
{
return
false
;
}
SrsConfDirective
*
conf
=
hds
->
get
(
"enabled"
);
if
(
!
conf
)
{
return
false
;
}
return
conf
->
arg0
()
==
"on"
;
}
string
SrsConfig
::
get_hds_path
(
const
string
&
vhost
)
{
SrsConfDirective
*
hds
=
get_hds
(
vhost
);
if
(
!
hds
)
{
return
SRS_CONF_DEFAULT_HDS_PATH
;
}
SrsConfDirective
*
conf
=
hds
->
get
(
"hds_path"
);
if
(
!
conf
)
{
return
SRS_CONF_DEFAULT_HDS_PATH
;
}
return
conf
->
arg0
();
}
double
SrsConfig
::
get_hds_fragment
(
const
string
&
vhost
)
{
SrsConfDirective
*
hds
=
get_hds
(
vhost
);
if
(
!
hds
)
{
return
SRS_CONF_DEFAULT_HDS_FRAGMENT
;
}
SrsConfDirective
*
conf
=
hds
->
get
(
"hds_fragment"
);
if
(
!
conf
)
{
return
SRS_CONF_DEFAULT_HDS_FRAGMENT
;
}
return
::
atof
(
conf
->
arg0
().
c_str
());
}
double
SrsConfig
::
get_hds_window
(
const
string
&
vhost
)
{
SrsConfDirective
*
hds
=
get_hds
(
vhost
);
if
(
!
hds
)
{
return
SRS_CONF_DEFAULT_HDS_WINDOW
;
}
SrsConfDirective
*
conf
=
hds
->
get
(
"hds_window"
);
if
(
!
conf
)
{
return
SRS_CONF_DEFAULT_HDS_WINDOW
;
}
return
::
atof
(
conf
->
arg0
().
c_str
());
}
SrsConfDirective
*
SrsConfig
::
get_dvr
(
string
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
...
...
trunk/src/app/srs_app_config.hpp
查看文件 @
d4198ee
...
...
@@ -102,10 +102,15 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define SRS_CONF_DEFAULT_TRANSCODE_IFORMAT "flv"
#define SRS_CONF_DEFAULT_TRANSCODE_OFORMAT "flv"
// hds default value
#define SRS_CONF_DEFAULT_HDS_PATH "./objs/nginx/html"
#define SRS_CONF_DEFAULT_HDS_WINDOW (60)
#define SRS_CONF_DEFAULT_HDS_FRAGMENT (10)
namespace
_srs_internal
{
class
SrsConfigBuffer
;
}
;
}
/**
* the config directive.
...
...
@@ -912,6 +917,32 @@ public:
* get the HLS default video codec.
*/
virtual
std
::
string
get_hls_vcodec
(
std
::
string
vhost
);
// hds section
private:
/**
* get the hds directive of vhost.
*/
virtual
SrsConfDirective
*
get_hds
(
const
std
::
string
&
vhost
);
public
:
/**
* whether HDS is enabled.
*/
virtual
bool
get_hds_enabled
(
const
std
::
string
&
vhost
);
/**
* get the HDS file store path.
*/
virtual
std
::
string
get_hds_path
(
const
std
::
string
&
vhost
);
/**
* get the hds fragment time, in seconds.
*/
virtual
double
get_hds_fragment
(
const
std
::
string
&
vhost
);
/**
* get the hds window time, in seconds.
* a window is a set of hds fragments.
*/
virtual
double
get_hds_window
(
const
std
::
string
&
vhost
);
// dvr section
private:
/**
...
...
trunk/src/app/srs_app_hds.cpp
0 → 100644
查看文件 @
d4198ee
/*
The MIT License (MIT)
Copyright (c) 2013-2015 wenjiegit
Copyright (c) 2013-2015 winlin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <string>
#include <vector>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <srs_app_hds.hpp>
#include <srs_rtmp_sdk.hpp>
#include <srs_kernel_log.hpp>
#include <srs_kernel_codec.hpp>
#include <srs_rtmp_stack.hpp>
#include <srs_kernel_stream.hpp>
#include <srs_core_autofree.hpp>
#include <srs_kernel_utility.hpp>
#include <srs_app_config.hpp>
using
namespace
std
;
static
void
update_box
(
char
*
start
,
int
size
)
{
char
*
p_size
=
(
char
*
)
&
size
;
start
[
0
]
=
p_size
[
3
];
start
[
1
]
=
p_size
[
2
];
start
[
2
]
=
p_size
[
1
];
start
[
3
]
=
p_size
[
0
];
}
char
flv_header
[]
=
{
'F'
,
'L'
,
'V'
,
0x01
,
0x05
,
0x00
,
0x00
,
0x00
,
0x09
,
0x00
,
0x00
,
0x00
,
0x00
};
string
serialFlv
(
SrsSharedPtrMessage
*
msg
)
{
SrsStream
*
stream
=
new
SrsStream
;
int
size
=
15
+
msg
->
size
;
char
*
byte
=
new
char
[
size
];
stream
->
initialize
(
byte
,
size
);
// tag header
long
long
dts
=
msg
->
timestamp
;
char
type
=
msg
->
is_video
()
?
0x09
:
0x08
;
stream
->
write_1bytes
(
type
);
stream
->
write_3bytes
(
msg
->
size
);
stream
->
write_3bytes
(
dts
);
stream
->
write_1bytes
(
dts
>>
24
&
0xFF
);
stream
->
write_3bytes
(
0
);
stream
->
write_bytes
(
msg
->
payload
,
msg
->
size
);
// pre tag size
int
preTagSize
=
msg
->
size
+
11
;
stream
->
write_4bytes
(
preTagSize
);
string
ret
(
stream
->
data
(),
stream
->
size
());
delete
stream
;
delete
[]
byte
;
return
ret
;
}
class
SrsHdsFragment
{
public
:
SrsHdsFragment
(
SrsRequest
*
r
)
:
req
(
r
)
,
index
(
-
1
)
,
start_time
(
0
)
,
videoSh
(
NULL
)
,
audioSh
(
NULL
)
{
}
~
SrsHdsFragment
()
{
srs_freep
(
videoSh
);
srs_freep
(
audioSh
);
// clean msgs
list
<
SrsSharedPtrMessage
*>::
iterator
iter
;
for
(
iter
=
msgs
.
begin
();
iter
!=
msgs
.
end
();
++
iter
)
{
SrsSharedPtrMessage
*
msg
=
*
iter
;
srs_freep
(
msg
);
}
}
void
on_video
(
SrsSharedPtrMessage
*
msg
)
{
SrsSharedPtrMessage
*
_msg
=
msg
->
copy
();
msgs
.
push_back
(
_msg
);
}
void
on_audio
(
SrsSharedPtrMessage
*
msg
)
{
SrsSharedPtrMessage
*
_msg
=
msg
->
copy
();
msgs
.
push_back
(
_msg
);
}
/*!
flush data to disk.
*/
int
flush
()
{
string
data
;
if
(
videoSh
)
{
videoSh
->
timestamp
=
start_time
;
data
.
append
(
serialFlv
(
videoSh
));
}
if
(
audioSh
)
{
audioSh
->
timestamp
=
start_time
;
data
.
append
(
serialFlv
(
audioSh
));
}
list
<
SrsSharedPtrMessage
*>::
iterator
iter
;
for
(
iter
=
msgs
.
begin
();
iter
!=
msgs
.
end
();
++
iter
)
{
SrsSharedPtrMessage
*
msg
=
*
iter
;
data
.
append
(
serialFlv
(
msg
));
}
static
char
box_header
[
8
];
SrsStream
ss
;
ss
.
initialize
(
box_header
,
8
);
ss
.
write_4bytes
(
8
+
data
.
size
());
ss
.
write_string
(
"mdat"
);
data
=
string
(
ss
.
data
(),
ss
.
size
())
+
data
;
char
file_path
[
1024
]
=
{
0
};
sprintf
(
file_path
,
"%s/%s/%sSeg1-Frag%d"
,
_srs_config
->
get_hds_path
(
req
->
vhost
).
c_str
()
,
req
->
app
.
c_str
(),
req
->
stream
.
c_str
(),
index
);
int
fd
=
open
(
file_path
,
O_WRONLY
|
O_CREAT
,
S_IRWXU
|
S_IRGRP
|
S_IROTH
);
if
(
fd
<
0
)
{
srs_error
(
"open fragment file failed, path=%s"
,
file_path
);
return
-
1
;
}
if
(
write
(
fd
,
data
.
data
(),
data
.
size
())
!=
(
int
)
data
.
size
())
{
srs_error
(
"write fragment file failed, path="
,
file_path
);
close
(
fd
);
return
-
1
;
}
close
(
fd
);
srs_trace
(
"build fragment success=%s"
,
file_path
);
return
ERROR_SUCCESS
;
}
/*!
calc the segment duration in milliseconds.
@return 0 if no msgs
or the last msg dts minus the first msg dts.
*/
int
duration
()
{
int
duration_ms
=
0
;
long
long
first_msg_ts
=
0
;
long
long
last_msg_ts
=
0
;
if
(
msgs
.
size
()
>=
2
)
{
SrsSharedPtrMessage
*
first_msg
=
msgs
.
front
();
first_msg_ts
=
first_msg
->
timestamp
;
SrsSharedPtrMessage
*
last_msg
=
msgs
.
back
();
last_msg_ts
=
last_msg
->
timestamp
;
duration_ms
=
last_msg_ts
-
first_msg_ts
;
}
return
duration_ms
;
}
/*!
set/get index
*/
inline
void
set_index
(
int
idx
)
{
char
fg_name
[
1024
]
=
{
0
};
sprintf
(
fg_name
,
"/var/www/live/stream0Seg1-Frag%d"
,
idx
);
path
=
fg_name
;
index
=
idx
;
}
inline
int
get_index
()
{
return
index
;
}
/*!
set/get start time
*/
inline
void
set_start_time
(
long
long
st
)
{
start_time
=
st
;
}
inline
long
long
get_start_time
()
{
return
start_time
;
}
void
set_video_sh
(
SrsSharedPtrMessage
*
msg
)
{
srs_freep
(
videoSh
);
videoSh
=
msg
->
copy
();
}
void
set_audio_sh
(
SrsSharedPtrMessage
*
msg
)
{
srs_freep
(
audioSh
);
audioSh
=
msg
->
copy
();
}
string
fragment_path
()
{
return
path
;
}
private
:
SrsRequest
*
req
;
list
<
SrsSharedPtrMessage
*>
msgs
;
/*!
the index of this fragment
*/
int
index
;
long
long
start_time
;
SrsSharedPtrMessage
*
videoSh
;
SrsSharedPtrMessage
*
audioSh
;
string
path
;
};
SrsHds
::
SrsHds
(
SrsSource
*
s
)
:
currentSegment
(
NULL
)
,
source
(
s
)
,
fragment_index
(
1
)
,
video_sh
(
NULL
)
,
audio_sh
(
NULL
)
,
hds_req
(
NULL
)
{
}
SrsHds
::~
SrsHds
()
{
}
int
SrsHds
::
on_publish
(
SrsRequest
*
req
)
{
hds_req
=
req
->
copy
();
return
flush_mainfest
();
}
int
SrsHds
::
on_unpublish
()
{
int
ret
=
ERROR_SUCCESS
;
srs_freep
(
video_sh
);
srs_freep
(
audio_sh
);
srs_freep
(
hds_req
);
// clean fragments
list
<
SrsHdsFragment
*>::
iterator
iter
;
for
(
iter
=
fragments
.
begin
();
iter
!=
fragments
.
end
();
++
iter
)
{
SrsHdsFragment
*
st
=
*
iter
;
srs_freep
(
st
);
}
fragments
.
clear
();
srs_freep
(
currentSegment
);
srs_trace
(
"HDS un-published"
);
return
ret
;
}
int
SrsHds
::
on_video
(
SrsSharedPtrMessage
*
msg
)
{
int
ret
=
ERROR_SUCCESS
;
if
(
SrsFlvCodec
::
video_is_sequence_header
(
msg
->
payload
,
msg
->
size
))
{
srs_freep
(
video_sh
);
video_sh
=
msg
->
copy
();
}
if
(
!
currentSegment
)
{
currentSegment
=
new
SrsHdsFragment
(
hds_req
);
currentSegment
->
set_index
(
fragment_index
++
);
currentSegment
->
set_start_time
(
msg
->
timestamp
);
if
(
video_sh
)
currentSegment
->
set_video_sh
(
video_sh
);
if
(
audio_sh
)
currentSegment
->
set_audio_sh
(
audio_sh
);
}
currentSegment
->
on_video
(
msg
);
double
fragment_duration
=
_srs_config
->
get_hds_fragment
(
hds_req
->
vhost
)
*
1000
;
if
(
currentSegment
->
duration
()
>=
fragment_duration
)
{
// flush segment
if
((
ret
=
currentSegment
->
flush
())
!=
ERROR_SUCCESS
)
{
srs_error
(
"flush segment failed."
);
return
ret
;
}
srs_trace
(
"flush Segment success."
);
fragments
.
push_back
(
currentSegment
);
currentSegment
=
NULL
;
adjust_windows
();
// flush bootstrap
if
((
ret
=
flush_bootstrap
())
!=
ERROR_SUCCESS
)
{
srs_error
(
"flush bootstrap failed."
);
return
ret
;
}
srs_trace
(
"flush BootStrap success."
);
}
return
ret
;
}
int
SrsHds
::
on_audio
(
SrsSharedPtrMessage
*
msg
)
{
int
ret
=
ERROR_SUCCESS
;
if
(
SrsFlvCodec
::
audio_is_sequence_header
(
msg
->
payload
,
msg
->
size
))
{
srs_freep
(
audio_sh
);
audio_sh
=
msg
->
copy
();
}
if
(
!
currentSegment
)
{
currentSegment
=
new
SrsHdsFragment
(
hds_req
);
currentSegment
->
set_index
(
fragment_index
++
);
currentSegment
->
set_start_time
(
msg
->
timestamp
);
if
(
video_sh
)
currentSegment
->
set_video_sh
(
video_sh
);
if
(
audio_sh
)
currentSegment
->
set_audio_sh
(
audio_sh
);
}
currentSegment
->
on_audio
(
msg
);
double
fragment_duration
=
_srs_config
->
get_hds_fragment
(
hds_req
->
vhost
)
*
1000
;
if
(
currentSegment
->
duration
()
>=
fragment_duration
)
{
// flush segment
if
((
ret
=
currentSegment
->
flush
())
!=
ERROR_SUCCESS
)
{
srs_error
(
"flush segment failed."
);
return
ret
;
}
srs_info
(
"flush Segment success."
);
// reset the current segment
fragments
.
push_back
(
currentSegment
);
currentSegment
=
NULL
;
adjust_windows
();
// flush bootstrap
if
((
ret
=
flush_bootstrap
())
!=
ERROR_SUCCESS
)
{
srs_error
(
"flush bootstrap failed."
);
return
ret
;
}
srs_info
(
"flush BootStrap success."
);
}
return
ret
;
}
int
SrsHds
::
flush_mainfest
()
{
int
ret
=
ERROR_SUCCESS
;
char
buf
[
1024
]
=
{
0
};
sprintf
(
buf
,
"<?xml version=
\"
1.0
\"
encoding=
\"
utf-8
\"
?>
\n
"
"<manifest xmlns=
\"
http://ns.adobe.com/f4m/1.0
\"
>
\n\t
"
"<id>%s.f4m</id>
\n\t
"
"<streamType>live</streamType>
\n\t
"
"<deliveryType>streaming</deliveryType>
\n\t
"
"<bootstrapInfo profile=
\"
named
\"
url=
\"
%s.abst
\"
id=
\"
bootstrap0
\"
/>
\n\t
"
"<media bitrate=
\"
0
\"
url=
\"
%s
\"
bootstrapInfoId=
\"
bootstrap0
\"
></media>
\n
"
"</manifest>"
,
hds_req
->
stream
.
c_str
(),
hds_req
->
stream
.
c_str
(),
hds_req
->
stream
.
c_str
());
string
dir
=
_srs_config
->
get_hds_path
(
hds_req
->
vhost
)
+
"/"
+
hds_req
->
app
;
if
((
ret
=
srs_create_dir_recursively
(
dir
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"hds create dir failed. ret=%d"
,
ret
);
return
ret
;
}
string
path
=
dir
+
"/"
+
hds_req
->
stream
+
".f4m"
;
int
fd
=
open
(
path
.
c_str
(),
O_WRONLY
|
O_CREAT
,
S_IRWXU
|
S_IRGRP
|
S_IROTH
);
if
(
fd
<
0
)
{
srs_error
(
"open manifest file failed, path=%s"
,
path
.
c_str
());
ret
=
ERROR_HDS_OPEN_F4M_FAILED
;
return
ret
;
}
int
f4m_size
=
strlen
(
buf
);
if
(
write
(
fd
,
buf
,
f4m_size
)
!=
f4m_size
)
{
srs_error
(
"write manifest file failed, path="
,
path
.
c_str
());
close
(
fd
);
ret
=
ERROR_HDS_WRITE_F4M_FAILED
;
return
ret
;
}
close
(
fd
);
srs_trace
(
"build manifest success=%s"
,
path
.
c_str
());
return
ERROR_SUCCESS
;
}
int
SrsHds
::
flush_bootstrap
()
{
int
ret
=
ERROR_SUCCESS
;
SrsStream
abst
;
int
size
=
1024
*
100
;
char
*
start_abst
=
new
char
[
1024
*
100
];
SrsAutoFree
(
char
,
start_abst
);
int
size_abst
=
0
;
char
*
start_asrt
=
NULL
;
int
size_asrt
=
0
;
char
*
start_afrt
=
NULL
;
int
size_afrt
=
0
;
abst
.
initialize
(
start_abst
,
size
);
// @see video_file_format_spec_v10_1
// page: 46
abst
.
write_4bytes
(
0
);
abst
.
write_string
(
"abst"
);
abst
.
write_1bytes
(
0x00
);
// Either 0 or 1
abst
.
write_3bytes
(
0x00
);
// Flags always 0
size_abst
+=
12
;
/*!
@BootstrapinfoVersion UI32
The version number of the bootstrap information.
When the Update field is set, BootstrapinfoVersion
indicates the version number that is being updated.
we assume this is the last.
*/
abst
.
write_4bytes
(
fragment_index
-
1
);
// BootstrapinfoVersion
abst
.
write_1bytes
(
0x20
);
// profile, live, update
abst
.
write_4bytes
(
1000
);
// TimeScale Typically, the value is 1000, for a unit of milliseconds
size_abst
+=
9
;
/*!
The timestamp in TimeScale units of the latest available Fragment in the media presentation.
This timestamp is used to request the right fragment number.
The CurrentMedia Time can be the total duration.
For media presentations that are not live, CurrentMediaTime can be 0.
*/
SrsHdsFragment
*
st
=
fragments
.
back
();
abst
.
write_8bytes
(
st
->
get_start_time
());
// SmpteTimeCodeOffset
abst
.
write_8bytes
(
0
);
size_abst
+=
16
;
/*!
@MovieIdentifier STRING
The identifier of this presentation.
we write null string.
*/
abst
.
write_1bytes
(
0
);
size_abst
+=
1
;
/*!
@ServerEntryCount UI8
The number of ServerEntryTable entries.
The minimum value is 0.
*/
abst
.
write_1bytes
(
0
);
size_abst
+=
1
;
/*!
@ServerEntryTable
because we write 0 of ServerEntryCount, so this feild is ignored.
*/
/*!
@QualityEntryCount UI8
The number of QualityEntryTable entries, which is
also the number of available quality levels. The
minimum value is 0. Available quality levels are for,
for example, multi bit rate files or trick files.
*/
abst
.
write_1bytes
(
0
);
size_abst
+=
1
;
/*!
@QualityEntryTable
because we write 0 of QualityEntryCount, so this feild is ignored.
*/
/*!
@DrmData STRING
Null or null-terminated UTF-8 string. This string
holds Digital Rights Management metadata.
Encrypted files use this metadata to get the
necessary keys and licenses for decryption and play back.
we write null string.
*/
abst
.
write_1bytes
(
0
);
size_abst
+=
1
;
/*!
@MetaData STRING
Null or null-terminated UTF - 8 string that holds metadata.
we write null string.
*/
abst
.
write_1bytes
(
0
);
size_abst
+=
1
;
/*!
@SegmentRunTableCount UI8
The number of entries in SegmentRunTableEntries.
The minimum value is 1. Typically, one table
contains all segment runs. However, this count
provides the flexibility to define the segment runs
individually for each quality level (or trick file).
*/
abst
.
write_1bytes
(
1
);
size_abst
+=
1
;
///////////////////////////////////////////////////
start_asrt
=
start_abst
+
size_abst
;
// MStream asrt;
// follows by asrt
abst
.
write_4bytes
(
0
);
abst
.
write_string
(
"asrt"
);
size_asrt
+=
8
;
/*!
@Version UI8
@Flags UI24
*/
abst
.
write_4bytes
(
0
);
size_asrt
+=
4
;
/*!
@QualityEntryCount UI8
The number of QualitySegmen tUrlModifiers
(quality level references) that follow. If 0, this
Segment Run Table applies to all quality levels,
and there shall be only one Segment Run Table
box in the Bootstrap Info box.
*/
abst
.
write_1bytes
(
0
);
size_asrt
+=
1
;
/*!
@QualitySegmentUrlModifiers
ignored.
*/
/*!
@SegmentRunEntryCount
The number of items in this
SegmentRunEn tryTable. The minimum value is 1.
*/
abst
.
write_4bytes
(
1
);
size_asrt
+=
4
;
/*!
@SegmentRunEntryTable
*/
for
(
int
i
=
0
;
i
<
1
;
++
i
)
{
/*!
@FirstSegment UI32
The identifying number of the first segment in the run of
segments containing the same number of fragments.
The segment corresponding to the FirstSegment in the next
SEGMENTRUNENTRY will terminate this run.
*/
abst
.
write_4bytes
(
1
);
/*!
@FragmentsPerSegment UI32
The number of fragments in each segment in this run.
*/
abst
.
write_4bytes
(
fragment_index
-
1
);
size_asrt
+=
8
;
}
update_box
(
start_asrt
,
size_asrt
);
size_abst
+=
size_asrt
;
/*!
@FragmentRunTableCount UI8
The number of entries in FragmentRunTable-Entries.
The min i mum value is 1.
*/
abst
.
write_1bytes
(
1
);
size_abst
+=
1
;
/////////////////////////////////////
//MStream afrt;
// follows by afrt
start_afrt
=
start_abst
+
size_abst
;
abst
.
write_4bytes
(
0
);
abst
.
write_string
(
"afrt"
);
size_afrt
+=
8
;
/*!
@Version UI8
@Flags UI24
*/
abst
.
write_4bytes
(
0
);
size_afrt
+=
4
;
/*!
@TimeScale UI32
The number of time units per second, used in the FirstFragmentTime stamp and
Fragment Duration fields.
Typically, the value is 1000.
*/
abst
.
write_4bytes
(
1000
);
size_afrt
+=
4
;
/*!
@QualityEntryCount UI8
The number of QualitySegment Url Modifiers
(quality level references) that follow.
If 0, this Fragment Run Table applies to all quality levels,
and there shall be only one Fragment Run Table
box in the Bootstrap Info box.
*/
abst
.
write_1bytes
(
0
);
size_afrt
+=
1
;
/*!
@FragmentRunEntryCount UI32
The number of items in this FragmentRunEntryTable.
The minimum value is 1.
*/
abst
.
write_4bytes
(
fragments
.
size
());
size_afrt
+=
4
;
list
<
SrsHdsFragment
*>::
iterator
iter
;
for
(
iter
=
fragments
.
begin
();
iter
!=
fragments
.
end
();
++
iter
)
{
SrsHdsFragment
*
st
=
*
iter
;
abst
.
write_4bytes
(
st
->
get_index
());
abst
.
write_8bytes
(
st
->
get_start_time
());
abst
.
write_4bytes
(
st
->
duration
());
size_afrt
+=
16
;
}
update_box
(
start_afrt
,
size_afrt
);
// abst.append(afrt);
size_abst
+=
size_afrt
;
update_box
(
start_abst
,
size_abst
);
string
path
=
_srs_config
->
get_hds_path
(
hds_req
->
vhost
)
+
"/"
+
hds_req
->
app
+
"/"
+
hds_req
->
stream
+
".abst"
;
int
fd
=
open
(
path
.
c_str
(),
O_WRONLY
|
O_CREAT
,
S_IRWXU
|
S_IRGRP
|
S_IROTH
);
if
(
fd
<
0
)
{
srs_error
(
"open bootstrap file failed, path=%s"
,
path
.
c_str
());
ret
=
ERROR_HDS_OPEN_BOOTSTRAP_FAILED
;
return
ret
;
}
if
(
write
(
fd
,
start_abst
,
size_abst
)
!=
size_abst
)
{
srs_error
(
"write bootstrap file failed, path="
,
path
.
c_str
());
close
(
fd
);
ret
=
ERROR_HDS_WRITE_BOOTSTRAP_FAILED
;
return
ret
;
}
close
(
fd
);
srs_trace
(
"build bootstrap success=%s"
,
path
.
c_str
());
return
ERROR_SUCCESS
;
}
void
SrsHds
::
adjust_windows
()
{
int
windows_size
=
0
;
list
<
SrsHdsFragment
*>::
iterator
iter
;
for
(
iter
=
fragments
.
begin
();
iter
!=
fragments
.
end
();
++
iter
)
{
SrsHdsFragment
*
fragment
=
*
iter
;
windows_size
+=
fragment
->
duration
();
}
double
windows_size_limit
=
_srs_config
->
get_hds_window
(
hds_req
->
vhost
)
*
1000
;
if
(
windows_size
>
windows_size_limit
)
{
SrsHdsFragment
*
fragment
=
fragments
.
front
();
unlink
(
fragment
->
fragment_path
().
c_str
());
fragments
.
erase
(
fragments
.
begin
());
srs_freep
(
fragment
);
}
}
...
...
trunk/src/app/srs_app_hds.hpp
0 → 100644
查看文件 @
d4198ee
/*
The MIT License (MIT)
Copyright (c) 2013-2015 wenjiegit
Copyright (c) 2013-2015 winlin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef SRS_APP_HDS_HPP
#define SRS_APP_HDS_HPP
#include <list>
class
SrsRequest
;
class
SrsSharedPtrMessage
;
class
SrsHdsFragment
;
class
SrsSource
;
using
namespace
std
;
class
SrsHds
{
public
:
SrsHds
(
SrsSource
*
s
);
~
SrsHds
();
int
on_publish
(
SrsRequest
*
req
);
int
on_unpublish
();
int
on_video
(
SrsSharedPtrMessage
*
msg
);
int
on_audio
(
SrsSharedPtrMessage
*
msg
);
private
:
int
flush_mainfest
();
int
flush_bootstrap
();
void
adjust_windows
();
private
:
list
<
SrsHdsFragment
*>
fragments
;
SrsHdsFragment
*
currentSegment
;
SrsSource
*
source
;
int
fragment_index
;
SrsSharedPtrMessage
*
video_sh
;
SrsSharedPtrMessage
*
audio_sh
;
SrsRequest
*
hds_req
;
};
#endif // SRS_APP_HDS_HPP
...
...
trunk/src/app/srs_app_source.cpp
查看文件 @
d4198ee
...
...
@@ -42,6 +42,7 @@ using namespace std;
#include <srs_kernel_utility.hpp>
#include <srs_kernel_codec.hpp>
#include <srs_rtmp_msg_array.hpp>
#include <srs_app_hds.hpp>
#define CONST_MAX_JITTER_MS 500
#define DEFAULT_FRAME_TIME_MS 40
...
...
@@ -770,6 +771,8 @@ SrsSource::SrsSource(ISrsHlsHandler* hh)
encoder
=
new
SrsEncoder
();
#endif
hds
=
new
SrsHds
(
this
);
cache_metadata
=
cache_sh_video
=
cache_sh_audio
=
NULL
;
frame_rate
=
sample_rate
=
0
;
...
...
@@ -1324,6 +1327,15 @@ int SrsSource::on_audio(SrsCommonMessage* __audio)
}
#endif
if
((
ret
=
hds
->
on_audio
(
&
msg
))
!=
ERROR_SUCCESS
)
{
// unpublish, ignore ret.
hds
->
on_unpublish
();
// ignore.
ret
=
ERROR_SUCCESS
;
srs_warn
(
"hds process audio message failed, ignore and disable dvr. ret=%d"
,
ret
);
}
// copy to all consumer
int
nb_consumers
=
(
int
)
consumers
.
size
();
if
(
nb_consumers
>
0
)
{
...
...
@@ -1456,6 +1468,15 @@ int SrsSource::on_video(SrsCommonMessage* __video)
}
#endif
if
((
ret
=
hds
->
on_video
(
&
msg
))
!=
ERROR_SUCCESS
)
{
// unpublish, ignore ret.
hds
->
on_unpublish
();
// ignore.
ret
=
ERROR_SUCCESS
;
srs_warn
(
"hds process video message failed, ignore and disable dvr. ret=%d"
,
ret
);
}
// copy to all consumer
if
(
true
)
{
for
(
int
i
=
0
;
i
<
(
int
)
consumers
.
size
();
i
++
)
{
...
...
@@ -1693,6 +1714,11 @@ int SrsSource::on_publish()
}
#endif
if
((
ret
=
hds
->
on_publish
(
_req
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"start hds failed. ret=%d"
,
ret
);
return
ret
;
}
// notify the handler.
srs_assert
(
handler
);
if
((
ret
=
handler
->
on_publish
(
this
,
_req
))
!=
ERROR_SUCCESS
)
{
...
...
@@ -1720,6 +1746,8 @@ void SrsSource::on_unpublish()
dvr
->
on_unpublish
();
#endif
hds
->
on_unpublish
();
gop_cache
->
clear
();
srs_freep
(
cache_metadata
);
...
...
trunk/src/app/srs_app_source.hpp
查看文件 @
d4198ee
...
...
@@ -62,6 +62,7 @@ class SrsEncoder;
#endif
class
SrsStream
;
class
ISrsHlsHandler
;
class
SrsHds
;
/**
* the time jitter algorithm:
...
...
@@ -411,6 +412,7 @@ private:
#ifdef SRS_AUTO_TRANSCODE
SrsEncoder
*
encoder
;
#endif
SrsHds
*
hds
;
// edge control service
SrsPlayEdge
*
play_edge
;
SrsPublishEdge
*
publish_edge
;
...
...
trunk/src/kernel/srs_kernel_error.hpp
查看文件 @
d4198ee
...
...
@@ -213,6 +213,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define ERROR_HTTP_DVR_REQUEST 3051
#define ERROR_HTTP_JSON_REQUIRED 3052
#define ERROR_HTTP_DVR_CREATE_REQUEST 3053
// HDS error code
#define ERROR_HDS_OPEN_F4M_FAILED 3054
#define ERROR_HDS_WRITE_F4M_FAILED 3055
#define ERROR_HDS_OPEN_BOOTSTRAP_FAILED 3056
#define ERROR_HDS_WRITE_BOOTSTRAP_FAILED 3057
#define ERROR_HDS_OPEN_FRAGMENT_FAILED 3058
#define ERROR_HDS_WRITE_FRAGMENT_FAILED 3059
///////////////////////////////////////////////////////
// HTTP/StreamCaster protocol error.
...
...
请
注册
或
登录
后发表评论