mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
Merge branch harlanc-3.0release into 3.0release
This commit is contained in:
commit
37bc0a253d
12 changed files with 447 additions and 20 deletions
|
@ -184,6 +184,7 @@ Please select according to languages:
|
||||||
|
|
||||||
### V3 changes
|
### V3 changes
|
||||||
|
|
||||||
|
* v3.0, 2018-08-25, Fix [#1093][bug #1093], Support HLS encryption. 3.0.42
|
||||||
* v3.0, 2018-08-25, Fix [#1051][bug #1051], Drop ts when republishing stream. 3.0.41
|
* v3.0, 2018-08-25, Fix [#1051][bug #1051], Drop ts when republishing stream. 3.0.41
|
||||||
* v3.0, 2018-08-12, For [#1202][bug #1202], Support edge/forward to Aliyun CDN. 3.0.40
|
* v3.0, 2018-08-12, For [#1202][bug #1202], Support edge/forward to Aliyun CDN. 3.0.40
|
||||||
* v3.0, 2018-08-11, For [#910][bug #910], Support HTTP FLV with HTTP callback. 3.0.39
|
* v3.0, 2018-08-11, For [#910][bug #910], Support HTTP FLV with HTTP callback. 3.0.39
|
||||||
|
@ -1468,6 +1469,7 @@ Winlin
|
||||||
[bug #727]: https://github.com/ossrs/srs/issues/727
|
[bug #727]: https://github.com/ossrs/srs/issues/727
|
||||||
[bug #1087]: https://github.com/ossrs/srs/issues/1087
|
[bug #1087]: https://github.com/ossrs/srs/issues/1087
|
||||||
[bug #1051]: https://github.com/ossrs/srs/issues/1051
|
[bug #1051]: https://github.com/ossrs/srs/issues/1051
|
||||||
|
[bug #1093]: https://github.com/ossrs/srs/issues/1093
|
||||||
[bug #xxxxxxxxxxxxx]: https://github.com/ossrs/srs/issues/xxxxxxxxxxxxx
|
[bug #xxxxxxxxxxxxx]: https://github.com/ossrs/srs/issues/xxxxxxxxxxxxx
|
||||||
|
|
||||||
[exo #828]: https://github.com/google/ExoPlayer/pull/828
|
[exo #828]: https://github.com/google/ExoPlayer/pull/828
|
||||||
|
|
|
@ -1102,6 +1102,26 @@ vhost hls.srs.com {
|
||||||
# default: on
|
# default: on
|
||||||
hls_wait_keyframe on;
|
hls_wait_keyframe on;
|
||||||
|
|
||||||
|
# whether using AES encryption.
|
||||||
|
# default: off
|
||||||
|
hls_keys on;
|
||||||
|
# the number of clear ts which one key can encrypt.
|
||||||
|
# default: 10
|
||||||
|
hls_fragments_per_key 10;
|
||||||
|
# the hls key file name.
|
||||||
|
# we supports some variables to generate the filename.
|
||||||
|
# [vhost], the vhost of stream.
|
||||||
|
# [app], the app of stream.
|
||||||
|
# [stream], the stream name of stream.
|
||||||
|
# [seq], the sequence number of key corresponding to the ts.
|
||||||
|
hls_key_file [app]/[stream]-[seq].key;
|
||||||
|
# the key output path.
|
||||||
|
# the key file is configed by hls_path/hls_key_file, the default is:
|
||||||
|
# ./objs/nginx/html/[app]/[stream]-[seq].key
|
||||||
|
hls_key_file_path ./objs/nginx/html;
|
||||||
|
# the key root URL, use this can support https.
|
||||||
|
hls_key_url https://localhost:8080;
|
||||||
|
|
||||||
# on_hls, never config in here, should config in http_hooks.
|
# on_hls, never config in here, should config in http_hooks.
|
||||||
# for the hls http callback, @see http_hooks.on_hls of vhost hooks.callback.srs.com
|
# for the hls http callback, @see http_hooks.on_hls of vhost hooks.callback.srs.com
|
||||||
# @read https://github.com/ossrs/srs/wiki/v2_CN_DeliveryHLS#http-callback
|
# @read https://github.com/ossrs/srs/wiki/v2_CN_DeliveryHLS#http-callback
|
||||||
|
|
10
trunk/configure
vendored
10
trunk/configure
vendored
|
@ -154,7 +154,7 @@ CORE_OBJS="${MODULE_OBJS[@]}"
|
||||||
#Kernel, depends on core, provides error/log/config, nothing about stream information.
|
#Kernel, depends on core, provides error/log/config, nothing about stream information.
|
||||||
MODULE_ID="KERNEL"
|
MODULE_ID="KERNEL"
|
||||||
MODULE_DEPENDS=("CORE")
|
MODULE_DEPENDS=("CORE")
|
||||||
ModuleLibIncs=(${SRS_OBJS_DIR})
|
ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSSLRoot})
|
||||||
MODULE_FILES=("srs_kernel_error" "srs_kernel_log" "srs_kernel_buffer"
|
MODULE_FILES=("srs_kernel_error" "srs_kernel_log" "srs_kernel_buffer"
|
||||||
"srs_kernel_utility" "srs_kernel_flv" "srs_kernel_codec" "srs_kernel_io"
|
"srs_kernel_utility" "srs_kernel_flv" "srs_kernel_codec" "srs_kernel_io"
|
||||||
"srs_kernel_consts" "srs_kernel_aac" "srs_kernel_mp3" "srs_kernel_ts"
|
"srs_kernel_consts" "srs_kernel_aac" "srs_kernel_mp3" "srs_kernel_ts"
|
||||||
|
@ -177,7 +177,7 @@ PROTOCOL_OBJS="${MODULE_OBJS[@]}"
|
||||||
if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
|
if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
|
||||||
MODULE_ID="SERVICE"
|
MODULE_ID="SERVICE"
|
||||||
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL")
|
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL")
|
||||||
ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR})
|
ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibSSLRoot})
|
||||||
MODULE_FILES=("srs_service_log" "srs_service_st" "srs_service_http_client"
|
MODULE_FILES=("srs_service_log" "srs_service_st" "srs_service_http_client"
|
||||||
"srs_service_http_conn" "srs_service_rtmp_conn" "srs_service_utility"
|
"srs_service_http_conn" "srs_service_rtmp_conn" "srs_service_utility"
|
||||||
"srs_service_conn")
|
"srs_service_conn")
|
||||||
|
@ -190,7 +190,7 @@ fi
|
||||||
if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
|
if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
|
||||||
MODULE_ID="APP"
|
MODULE_ID="APP"
|
||||||
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE")
|
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE")
|
||||||
ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR})
|
ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibSSLRoot})
|
||||||
MODULE_FILES=("srs_app_server" "srs_app_conn" "srs_app_rtmp_conn" "srs_app_source"
|
MODULE_FILES=("srs_app_server" "srs_app_conn" "srs_app_rtmp_conn" "srs_app_source"
|
||||||
"srs_app_refer" "srs_app_hls" "srs_app_forward" "srs_app_encoder" "srs_app_http_stream"
|
"srs_app_refer" "srs_app_hls" "srs_app_forward" "srs_app_encoder" "srs_app_http_stream"
|
||||||
"srs_app_thread" "srs_app_bandwidth" "srs_app_st" "srs_app_log" "srs_app_config"
|
"srs_app_thread" "srs_app_bandwidth" "srs_app_st" "srs_app_log" "srs_app_config"
|
||||||
|
@ -225,7 +225,7 @@ LIBS_OBJS="${MODULE_OBJS[@]}"
|
||||||
if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
|
if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
|
||||||
MODULE_ID="SERVER"
|
MODULE_ID="SERVER"
|
||||||
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE" "APP")
|
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE" "APP")
|
||||||
ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot})
|
ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot})
|
||||||
MODULE_FILES=("srs_main_server")
|
MODULE_FILES=("srs_main_server")
|
||||||
SERVER_INCS="src/main"; MODULE_DIR=${SERVER_INCS} . auto/modules.sh
|
SERVER_INCS="src/main"; MODULE_DIR=${SERVER_INCS} . auto/modules.sh
|
||||||
SERVER_OBJS="${MODULE_OBJS[@]}"
|
SERVER_OBJS="${MODULE_OBJS[@]}"
|
||||||
|
@ -235,7 +235,7 @@ fi
|
||||||
if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
|
if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
|
||||||
MODULE_ID="MAIN"
|
MODULE_ID="MAIN"
|
||||||
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE")
|
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE")
|
||||||
ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot})
|
ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot})
|
||||||
MODULE_FILES=()
|
MODULE_FILES=()
|
||||||
DEFINES=""
|
DEFINES=""
|
||||||
# add each modules for main
|
# add each modules for main
|
||||||
|
|
|
@ -2606,6 +2606,16 @@ srs_error_t SrsConfig::vhost_to_json(SrsConfDirective* vhost, SrsJsonObject* obj
|
||||||
hls->set("hls_nb_notify", sdir->dumps_arg0_to_integer());
|
hls->set("hls_nb_notify", sdir->dumps_arg0_to_integer());
|
||||||
} else if (sdir->name == "hls_wait_keyframe") {
|
} else if (sdir->name == "hls_wait_keyframe") {
|
||||||
hls->set("hls_wait_keyframe", sdir->dumps_arg0_to_boolean());
|
hls->set("hls_wait_keyframe", sdir->dumps_arg0_to_boolean());
|
||||||
|
} else if (sdir->name == "hls_keys") {
|
||||||
|
hls->set("hls_keys", sdir->dumps_arg0_to_boolean());
|
||||||
|
} else if (sdir->name == "hls_fragments_per_key") {
|
||||||
|
hls->set("hls_fragments_per_key", sdir->dumps_arg0_to_number());
|
||||||
|
} else if (sdir->name == "hls_key_file") {
|
||||||
|
hls->set("hls_key_file", sdir->dumps_arg0_to_str());
|
||||||
|
} else if (sdir->name == "hls_key_file_path") {
|
||||||
|
hls->set("hls_key_file_path", sdir->dumps_arg0_to_str());
|
||||||
|
} else if (sdir->name == "hls_key_url") {
|
||||||
|
hls->set("hls_key_url", sdir->dumps_arg0_to_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3773,7 +3783,8 @@ srs_error_t SrsConfig::check_normal_config()
|
||||||
if (m != "enabled" && m != "hls_entry_prefix" && m != "hls_path" && m != "hls_fragment" && m != "hls_window" && m != "hls_on_error"
|
if (m != "enabled" && m != "hls_entry_prefix" && m != "hls_path" && m != "hls_fragment" && m != "hls_window" && m != "hls_on_error"
|
||||||
&& m != "hls_storage" && m != "hls_mount" && m != "hls_td_ratio" && m != "hls_aof_ratio" && m != "hls_acodec" && m != "hls_vcodec"
|
&& m != "hls_storage" && m != "hls_mount" && m != "hls_td_ratio" && m != "hls_aof_ratio" && m != "hls_acodec" && m != "hls_vcodec"
|
||||||
&& m != "hls_m3u8_file" && m != "hls_ts_file" && m != "hls_ts_floor" && m != "hls_cleanup" && m != "hls_nb_notify"
|
&& m != "hls_m3u8_file" && m != "hls_ts_file" && m != "hls_ts_floor" && m != "hls_cleanup" && m != "hls_nb_notify"
|
||||||
&& m != "hls_wait_keyframe" && m != "hls_dispose") {
|
&& m != "hls_wait_keyframe" && m != "hls_dispose" && m != "hls_keys" && m != "hls_fragments_per_key" && m != "hls_key_file"
|
||||||
|
&& m != "hls_key_file_path" && m != "hls_key_url") {
|
||||||
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal vhost.hls.%s of %s", m.c_str(), vhost->arg0().c_str());
|
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal vhost.hls.%s of %s", m.c_str(), vhost->arg0().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6278,6 +6289,93 @@ bool SrsConfig::get_hls_wait_keyframe(string vhost)
|
||||||
return SRS_CONF_PERFER_TRUE(conf->arg0());
|
return SRS_CONF_PERFER_TRUE(conf->arg0());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SrsConfig::get_hls_keys(string vhost)
|
||||||
|
{
|
||||||
|
static bool DEFAULT = false;
|
||||||
|
|
||||||
|
SrsConfDirective* conf = get_hls(vhost);
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("hls_keys");
|
||||||
|
if (!conf || conf->arg0().empty()) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRS_CONF_PERFER_TRUE(conf->arg0());
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsConfig::get_hls_fragments_per_key(string vhost)
|
||||||
|
{
|
||||||
|
static int DEFAULT = 10;
|
||||||
|
|
||||||
|
SrsConfDirective* conf = get_hls(vhost);
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("hls_fragments_per_key");
|
||||||
|
if (!conf || conf->arg0().empty()) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ::atoi(conf->arg0().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
string SrsConfig::get_hls_key_file(string vhost)
|
||||||
|
{
|
||||||
|
static string DEFAULT = "[app]/[stream]-[seq].key";
|
||||||
|
|
||||||
|
SrsConfDirective* conf = get_hls(vhost);
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("hls_key_file");
|
||||||
|
if (!conf || conf->arg0().empty()) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf->arg0();
|
||||||
|
}
|
||||||
|
|
||||||
|
string SrsConfig::get_hls_key_file_path(std::string vhost)
|
||||||
|
{
|
||||||
|
//put the key in ts path defaultly.
|
||||||
|
static string DEFAULT = get_hls_path(vhost);
|
||||||
|
|
||||||
|
SrsConfDirective* conf = get_hls(vhost);
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("hls_key_file_path");
|
||||||
|
if (!conf || conf->arg0().empty()) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf->arg0();
|
||||||
|
}
|
||||||
|
|
||||||
|
string SrsConfig::get_hls_key_url(std::string vhost)
|
||||||
|
{
|
||||||
|
//put the key in ts path defaultly.
|
||||||
|
static string DEFAULT = get_hls_path(vhost);
|
||||||
|
|
||||||
|
SrsConfDirective* conf = get_hls(vhost);
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("hls_key_url");
|
||||||
|
if (!conf || conf->arg0().empty()) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf->arg0();
|
||||||
|
}
|
||||||
|
|
||||||
SrsConfDirective *SrsConfig::get_hds(const string &vhost)
|
SrsConfDirective *SrsConfig::get_hds(const string &vhost)
|
||||||
{
|
{
|
||||||
SrsConfDirective* conf = get_vhost(vhost);
|
SrsConfDirective* conf = get_vhost(vhost);
|
||||||
|
|
|
@ -1236,6 +1236,26 @@ public:
|
||||||
* whether reap the ts when got keyframe.
|
* whether reap the ts when got keyframe.
|
||||||
*/
|
*/
|
||||||
virtual bool get_hls_wait_keyframe(std::string vhost);
|
virtual bool get_hls_wait_keyframe(std::string vhost);
|
||||||
|
/**
|
||||||
|
* encrypt ts or not
|
||||||
|
*/
|
||||||
|
virtual bool get_hls_keys(std::string vhost);
|
||||||
|
/**
|
||||||
|
* how many fragments can one key encrypted.
|
||||||
|
*/
|
||||||
|
virtual int get_hls_fragments_per_key(std::string vhost);
|
||||||
|
/**
|
||||||
|
* get the HLS key file path template.
|
||||||
|
*/
|
||||||
|
virtual std::string get_hls_key_file(std::string vhost);
|
||||||
|
/**
|
||||||
|
* get the HLS key file store path.
|
||||||
|
*/
|
||||||
|
virtual std::string get_hls_key_file_path(std::string vhost);
|
||||||
|
/**
|
||||||
|
* get the HLS key file url which will be put in m3u8
|
||||||
|
*/
|
||||||
|
virtual std::string get_hls_key_url(std::string vhost);
|
||||||
/**
|
/**
|
||||||
* get the size of bytes to read from cdn network, for the on_hls_notify callback,
|
* get the size of bytes to read from cdn network, for the on_hls_notify callback,
|
||||||
* that is, to read max bytes of the bytes from the callback, or timeout or error.
|
* that is, to read max bytes of the bytes from the callback, or timeout or error.
|
||||||
|
|
|
@ -52,6 +52,10 @@ using namespace std;
|
||||||
#include <srs_app_http_hooks.hpp>
|
#include <srs_app_http_hooks.hpp>
|
||||||
#include <srs_protocol_format.hpp>
|
#include <srs_protocol_format.hpp>
|
||||||
|
|
||||||
|
#ifdef SRS_AUTO_SSL
|
||||||
|
#include <openssl/rand.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
// drop the segment when duration of ts too small.
|
// drop the segment when duration of ts too small.
|
||||||
#define SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS 100
|
#define SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS 100
|
||||||
|
|
||||||
|
@ -60,17 +64,26 @@ using namespace std;
|
||||||
// reset the piece id when deviation overflow this.
|
// reset the piece id when deviation overflow this.
|
||||||
#define SRS_JUMP_WHEN_PIECE_DEVIATION 20
|
#define SRS_JUMP_WHEN_PIECE_DEVIATION 20
|
||||||
|
|
||||||
SrsHlsSegment::SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc)
|
SrsHlsSegment::SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc, SrsFileWriter* w)
|
||||||
{
|
{
|
||||||
sequence_no = 0;
|
sequence_no = 0;
|
||||||
writer = new SrsFileWriter();
|
writer = w;
|
||||||
tscw = new SrsTsContextWriter(writer, c, ac, vc);
|
tscw = new SrsTsContextWriter(writer, c, ac, vc);
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsHlsSegment::~SrsHlsSegment()
|
SrsHlsSegment::~SrsHlsSegment()
|
||||||
{
|
{
|
||||||
srs_freep(tscw);
|
srs_freep(tscw);
|
||||||
srs_freep(writer);
|
}
|
||||||
|
|
||||||
|
void SrsHlsSegment::config_cipher(unsigned char* key,unsigned char* iv)
|
||||||
|
{
|
||||||
|
memcpy(this->iv, iv,16);
|
||||||
|
|
||||||
|
#ifdef SRS_AUTO_SSL
|
||||||
|
SrsEncFileWriter* fw = (SrsEncFileWriter*)writer;
|
||||||
|
fw->config_cipher(key, iv);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsDvrAsyncCallOnHls::SrsDvrAsyncCallOnHls(int c, SrsRequest* r, string p, string t, string m, string mu, int s, double d)
|
SrsDvrAsyncCallOnHls::SrsDvrAsyncCallOnHls(int c, SrsRequest* r, string p, string t, string m, string mu, int s, double d)
|
||||||
|
@ -193,9 +206,14 @@ SrsHlsMuxer::SrsHlsMuxer()
|
||||||
max_td = 0;
|
max_td = 0;
|
||||||
_sequence_no = 0;
|
_sequence_no = 0;
|
||||||
current = NULL;
|
current = NULL;
|
||||||
|
hls_keys = false;
|
||||||
|
hls_fragments_per_key = 10;
|
||||||
async = new SrsAsyncCallWorker();
|
async = new SrsAsyncCallWorker();
|
||||||
context = new SrsTsContext();
|
context = new SrsTsContext();
|
||||||
segments = new SrsFragmentWindow();
|
segments = new SrsFragmentWindow();
|
||||||
|
|
||||||
|
memset(key, 0, 16);
|
||||||
|
memset(iv, 0, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsHlsMuxer::~SrsHlsMuxer()
|
SrsHlsMuxer::~SrsHlsMuxer()
|
||||||
|
@ -204,6 +222,7 @@ SrsHlsMuxer::~SrsHlsMuxer()
|
||||||
srs_freep(req);
|
srs_freep(req);
|
||||||
srs_freep(async);
|
srs_freep(async);
|
||||||
srs_freep(context);
|
srs_freep(context);
|
||||||
|
srs_freep(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsHlsMuxer::dispose()
|
void SrsHlsMuxer::dispose()
|
||||||
|
@ -265,8 +284,9 @@ srs_error_t SrsHlsMuxer::initialize()
|
||||||
|
|
||||||
srs_error_t SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix,
|
srs_error_t SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix,
|
||||||
string path, string m3u8_file, string ts_file, double fragment, double window,
|
string path, string m3u8_file, string ts_file, double fragment, double window,
|
||||||
bool ts_floor, double aof_ratio, bool cleanup, bool wait_keyframe
|
bool ts_floor, double aof_ratio, bool cleanup, bool wait_keyframe, bool keys,
|
||||||
) {
|
int fragments_per_key, string key_file ,string key_file_path, string key_url)
|
||||||
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
srs_freep(req);
|
srs_freep(req);
|
||||||
|
@ -284,7 +304,13 @@ srs_error_t SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix,
|
||||||
accept_floor_ts = 0;
|
accept_floor_ts = 0;
|
||||||
hls_window = window;
|
hls_window = window;
|
||||||
deviation_ts = 0;
|
deviation_ts = 0;
|
||||||
|
|
||||||
|
hls_keys = keys;
|
||||||
|
hls_fragments_per_key = fragments_per_key;
|
||||||
|
hls_key_file = key_file;
|
||||||
|
hls_key_file_path = key_file_path;
|
||||||
|
hls_key_url = key_url;
|
||||||
|
|
||||||
// generate the m3u8 dir and path.
|
// generate the m3u8 dir and path.
|
||||||
m3u8_url = srs_path_build_stream(m3u8_file, req->vhost, req->app, req->stream);
|
m3u8_url = srs_path_build_stream(m3u8_file, req->vhost, req->app, req->stream);
|
||||||
m3u8 = path + "/" + m3u8_url;
|
m3u8 = path + "/" + m3u8_url;
|
||||||
|
@ -297,7 +323,27 @@ srs_error_t SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix,
|
||||||
if ((err = srs_create_dir_recursively(m3u8_dir)) != srs_success) {
|
if ((err = srs_create_dir_recursively(m3u8_dir)) != srs_success) {
|
||||||
return srs_error_wrap(err, "create dir");
|
return srs_error_wrap(err, "create dir");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hls_keys && (hls_path != hls_key_file_path)) {
|
||||||
|
string key_file = hls_key_file;
|
||||||
|
key_file = srs_path_build_stream(key_file, req->vhost, req->app, req->stream);
|
||||||
|
|
||||||
|
string key_dir = srs_path_dirname(hls_key_file_path + "/" + key_file);
|
||||||
|
if ((err = srs_create_dir_recursively(key_dir)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "create dir");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(hls_keys) {
|
||||||
|
#ifdef SRS_AUTO_SSL
|
||||||
|
writer = new SrsEncFileWriter();
|
||||||
|
#else
|
||||||
|
writer = new SrsFileWriter();
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
writer = new SrsFileWriter();
|
||||||
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,8 +388,12 @@ srs_error_t SrsHlsMuxer::segment_open()
|
||||||
}
|
}
|
||||||
|
|
||||||
// new segment.
|
// new segment.
|
||||||
current = new SrsHlsSegment(context, default_acodec, default_vcodec);
|
current = new SrsHlsSegment(context, default_acodec, default_vcodec, writer);
|
||||||
current->sequence_no = _sequence_no++;
|
current->sequence_no = _sequence_no++;
|
||||||
|
|
||||||
|
if ((err = write_hls_key()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "write hls key");
|
||||||
|
}
|
||||||
|
|
||||||
// generate filename.
|
// generate filename.
|
||||||
std::string ts_file = hls_ts_file;
|
std::string ts_file = hls_ts_file;
|
||||||
|
@ -603,6 +653,58 @@ srs_error_t SrsHlsMuxer::segment_close()
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsHlsMuxer::write_hls_key()
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
#ifndef SRS_AUTO_SSL
|
||||||
|
if (hls_keys) {
|
||||||
|
srs_warn("SSL is disabled, ignore HLS key");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SRS_AUTO_SSL
|
||||||
|
if (hls_keys && current->sequence_no % hls_fragments_per_key == 0) {
|
||||||
|
string key_file = hls_key_file;
|
||||||
|
key_file = srs_path_build_stream(key_file, req->vhost, req->app, req->stream);
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << current->sequence_no;
|
||||||
|
key_file = srs_string_replace(key_file, "[seq]", ss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
string key_full_path = hls_key_file_path + "/" + key_file;
|
||||||
|
|
||||||
|
if (RAND_bytes(key, 16) < 0) {
|
||||||
|
srs_error_wrap(err, "rand key failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RAND_bytes(iv, 16) < 0) {
|
||||||
|
srs_error_wrap(err, "rand iv failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsFileWriter fw;
|
||||||
|
|
||||||
|
if ((err = fw.open(key_full_path)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "open file %s", key_full_path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = fw.write(key, 16, NULL)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "write key");
|
||||||
|
}
|
||||||
|
|
||||||
|
fw.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hls_keys) {
|
||||||
|
current->config_cipher(key, iv);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
srs_error_t SrsHlsMuxer::refresh_m3u8()
|
srs_error_t SrsHlsMuxer::refresh_m3u8()
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
@ -681,6 +783,22 @@ srs_error_t SrsHlsMuxer::_refresh_m3u8(string m3u8_file)
|
||||||
// #EXT-X-DISCONTINUITY\n
|
// #EXT-X-DISCONTINUITY\n
|
||||||
ss << "#EXT-X-DISCONTINUITY" << SRS_CONSTS_LF;
|
ss << "#EXT-X-DISCONTINUITY" << SRS_CONSTS_LF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(hls_keys && (segment->sequence_no%hls_fragments_per_key == 0))
|
||||||
|
{
|
||||||
|
string filename = req->stream+"-"+srs_int2str(segment->sequence_no)+".key";
|
||||||
|
char hexiv[33];
|
||||||
|
srs_data_to_hex(hexiv,segment->iv,16);
|
||||||
|
hexiv[32] = '\0';
|
||||||
|
string key_path;
|
||||||
|
//if key_url is not set,only use the file name
|
||||||
|
if(hls_key_url == hls_key_file_path){
|
||||||
|
key_path = filename;
|
||||||
|
}else{
|
||||||
|
key_path = hls_key_url+"/"+filename;
|
||||||
|
}
|
||||||
|
ss << "#EXT-X-KEY:METHOD=AES-128,URI=" << "\""<< key_path <<"\",IV=0x"<<hexiv<< SRS_CONSTS_LF;
|
||||||
|
}
|
||||||
|
|
||||||
// "#EXTINF:4294967295.208,\n"
|
// "#EXTINF:4294967295.208,\n"
|
||||||
ss.precision(3);
|
ss.precision(3);
|
||||||
|
@ -778,13 +896,20 @@ srs_error_t SrsHlsController::on_publish(SrsRequest* req)
|
||||||
bool ts_floor = _srs_config->get_hls_ts_floor(vhost);
|
bool ts_floor = _srs_config->get_hls_ts_floor(vhost);
|
||||||
// the seconds to dispose the hls.
|
// the seconds to dispose the hls.
|
||||||
int hls_dispose = _srs_config->get_hls_dispose(vhost);
|
int hls_dispose = _srs_config->get_hls_dispose(vhost);
|
||||||
|
|
||||||
|
bool hls_keys = _srs_config->get_hls_keys(vhost);
|
||||||
|
int hls_fragments_per_key = _srs_config->get_hls_fragments_per_key(vhost);
|
||||||
|
string hls_key_file = _srs_config->get_hls_key_file(vhost);
|
||||||
|
string hls_key_file_path = _srs_config->get_hls_key_file_path(vhost);
|
||||||
|
string hls_key_url = _srs_config->get_hls_key_url(vhost);
|
||||||
|
|
||||||
// TODO: FIXME: support load exists m3u8, to continue publish stream.
|
// TODO: FIXME: support load exists m3u8, to continue publish stream.
|
||||||
// for the HLS donot requires the EXT-X-MEDIA-SEQUENCE be monotonically increase.
|
// for the HLS donot requires the EXT-X-MEDIA-SEQUENCE be monotonically increase.
|
||||||
|
|
||||||
// open muxer
|
// open muxer
|
||||||
if ((err = muxer->update_config(req, entry_prefix, path, m3u8_file, ts_file, hls_fragment,
|
if ((err = muxer->update_config(req, entry_prefix, path, m3u8_file, ts_file, hls_fragment,
|
||||||
hls_window, ts_floor, hls_aof_ratio, cleanup, wait_keyframe)) != srs_success ) {
|
hls_window, ts_floor, hls_aof_ratio, cleanup, wait_keyframe,hls_keys,hls_fragments_per_key,
|
||||||
|
hls_key_file, hls_key_file_path, hls_key_url)) != srs_success ) {
|
||||||
return srs_error_wrap(err, "hls: update config");
|
return srs_error_wrap(err, "hls: update config");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,9 +67,15 @@ public:
|
||||||
SrsFileWriter* writer;
|
SrsFileWriter* writer;
|
||||||
// The TS context writer to write TS to file.
|
// The TS context writer to write TS to file.
|
||||||
SrsTsContextWriter* tscw;
|
SrsTsContextWriter* tscw;
|
||||||
|
// Will be saved in m3u8 file.
|
||||||
|
unsigned char iv[16];
|
||||||
|
// The full key path.
|
||||||
|
std::string keypath;
|
||||||
public:
|
public:
|
||||||
SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc);
|
SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc, SrsFileWriter* w);
|
||||||
virtual ~SrsHlsSegment();
|
virtual ~SrsHlsSegment();
|
||||||
|
public:
|
||||||
|
void config_cipher(unsigned char* key,unsigned char* iv);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -146,6 +152,20 @@ private:
|
||||||
// used to detect the dup or jmp or ts.
|
// used to detect the dup or jmp or ts.
|
||||||
int64_t accept_floor_ts;
|
int64_t accept_floor_ts;
|
||||||
int64_t previous_floor_ts;
|
int64_t previous_floor_ts;
|
||||||
|
private:
|
||||||
|
// encrypted or not
|
||||||
|
bool hls_keys;
|
||||||
|
int hls_fragments_per_key;
|
||||||
|
// key file name
|
||||||
|
std::string hls_key_file;
|
||||||
|
// key file path
|
||||||
|
std::string hls_key_file_path;
|
||||||
|
// key file url
|
||||||
|
std::string hls_key_url;
|
||||||
|
// key and iv.
|
||||||
|
unsigned char key[16];
|
||||||
|
unsigned char iv[16];
|
||||||
|
SrsFileWriter *writer;
|
||||||
private:
|
private:
|
||||||
int _sequence_no;
|
int _sequence_no;
|
||||||
int max_td;
|
int max_td;
|
||||||
|
@ -182,7 +202,8 @@ public:
|
||||||
virtual srs_error_t update_config(SrsRequest* r, std::string entry_prefix,
|
virtual srs_error_t update_config(SrsRequest* r, std::string entry_prefix,
|
||||||
std::string path, std::string m3u8_file, std::string ts_file,
|
std::string path, std::string m3u8_file, std::string ts_file,
|
||||||
double fragment, double window, bool ts_floor, double aof_ratio,
|
double fragment, double window, bool ts_floor, double aof_ratio,
|
||||||
bool cleanup, bool wait_keyframe);
|
bool cleanup, bool wait_keyframe, bool keys, int fragments_per_key,
|
||||||
|
std::string key_file, std::string key_file_path, std::string key_url);
|
||||||
/**
|
/**
|
||||||
* open a new segment(a new ts file)
|
* open a new segment(a new ts file)
|
||||||
*/
|
*/
|
||||||
|
@ -215,6 +236,7 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual srs_error_t segment_close();
|
virtual srs_error_t segment_close();
|
||||||
private:
|
private:
|
||||||
|
virtual srs_error_t write_hls_key();
|
||||||
virtual srs_error_t refresh_m3u8();
|
virtual srs_error_t refresh_m3u8();
|
||||||
virtual srs_error_t _refresh_m3u8(std::string m3u8_file);
|
virtual srs_error_t _refresh_m3u8(std::string m3u8_file);
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
// current release version
|
// current release version
|
||||||
#define VERSION_MAJOR 3
|
#define VERSION_MAJOR 3
|
||||||
#define VERSION_MINOR 0
|
#define VERSION_MINOR 0
|
||||||
#define VERSION_REVISION 41
|
#define VERSION_REVISION 42
|
||||||
|
|
||||||
// generated by configure, only macros.
|
// generated by configure, only macros.
|
||||||
#include <srs_auto_headers.hpp>
|
#include <srs_auto_headers.hpp>
|
||||||
|
|
|
@ -34,15 +34,21 @@
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
#ifdef SRS_AUTO_SSL
|
||||||
|
#include <openssl/aes.h>
|
||||||
|
#include <cstring>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <srs_kernel_log.hpp>
|
#include <srs_kernel_log.hpp>
|
||||||
#include <srs_kernel_error.hpp>
|
#include <srs_kernel_error.hpp>
|
||||||
#include <srs_kernel_file.hpp>
|
|
||||||
#include <srs_kernel_codec.hpp>
|
#include <srs_kernel_codec.hpp>
|
||||||
#include <srs_kernel_stream.hpp>
|
#include <srs_kernel_stream.hpp>
|
||||||
#include <srs_kernel_utility.hpp>
|
#include <srs_kernel_utility.hpp>
|
||||||
#include <srs_kernel_buffer.hpp>
|
#include <srs_kernel_buffer.hpp>
|
||||||
#include <srs_core_autofree.hpp>
|
#include <srs_core_autofree.hpp>
|
||||||
|
|
||||||
|
#define HLS_AES_ENCRYPT_BLOCK_LENGTH SRS_TS_PACKET_SIZE * 4
|
||||||
|
|
||||||
// in ms, for HLS aac sync time.
|
// in ms, for HLS aac sync time.
|
||||||
#define SRS_CONF_DEFAULT_AAC_SYNC 100
|
#define SRS_CONF_DEFAULT_AAC_SYNC 100
|
||||||
|
|
||||||
|
@ -2610,6 +2616,95 @@ SrsVideoCodecId SrsTsContextWriter::video_codec()
|
||||||
return vcodec;
|
return vcodec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef SRS_AUTO_SSL
|
||||||
|
SrsEncFileWriter::SrsEncFileWriter()
|
||||||
|
{
|
||||||
|
memset(iv,0,16);
|
||||||
|
|
||||||
|
buf = new char[HLS_AES_ENCRYPT_BLOCK_LENGTH];
|
||||||
|
memset(buf, 0, HLS_AES_ENCRYPT_BLOCK_LENGTH);
|
||||||
|
|
||||||
|
nb_buf = 0;
|
||||||
|
key = (unsigned char*)new AES_KEY();
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsEncFileWriter::~SrsEncFileWriter()
|
||||||
|
{
|
||||||
|
srs_freepa(buf);
|
||||||
|
|
||||||
|
AES_KEY* k = (AES_KEY*)key;
|
||||||
|
srs_freep(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsEncFileWriter::write(void* data, size_t count, ssize_t* pnwrite)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
srs_assert(count == SRS_TS_PACKET_SIZE);
|
||||||
|
|
||||||
|
if (nb_buf < HLS_AES_ENCRYPT_BLOCK_LENGTH) {
|
||||||
|
memcpy(buf + nb_buf, (char*)data, SRS_TS_PACKET_SIZE);
|
||||||
|
nb_buf += SRS_TS_PACKET_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nb_buf == HLS_AES_ENCRYPT_BLOCK_LENGTH) {
|
||||||
|
nb_buf = 0;
|
||||||
|
|
||||||
|
char* cipher = new char[HLS_AES_ENCRYPT_BLOCK_LENGTH];
|
||||||
|
SrsAutoFreeA(char, cipher);
|
||||||
|
|
||||||
|
AES_KEY* k = (AES_KEY*)key;
|
||||||
|
AES_cbc_encrypt((unsigned char *)buf, (unsigned char *)cipher, HLS_AES_ENCRYPT_BLOCK_LENGTH, k, iv, AES_ENCRYPT);
|
||||||
|
|
||||||
|
if ((err = SrsFileWriter::write(cipher, HLS_AES_ENCRYPT_BLOCK_LENGTH, pnwrite)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "write cipher");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsEncFileWriter::config_cipher(unsigned char* key, unsigned char* iv)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
memcpy(this->iv, iv, 16);
|
||||||
|
|
||||||
|
AES_KEY* k = (AES_KEY*)this->key;
|
||||||
|
if (AES_set_encrypt_key(key, 16 * 8, k)) {
|
||||||
|
return srs_error_new(ERROR_SYSTEM_FILE_WRITE, "set aes key failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsEncFileWriter::close()
|
||||||
|
{
|
||||||
|
if(nb_buf > 0) {
|
||||||
|
int nb_padding = 16 - (nb_buf % 16);
|
||||||
|
if (nb_padding > 0) {
|
||||||
|
memset(buf + nb_buf, nb_padding, nb_padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* cipher = new char[nb_buf + nb_padding];
|
||||||
|
SrsAutoFreeA(char, cipher);
|
||||||
|
|
||||||
|
AES_KEY* k = (AES_KEY*)key;
|
||||||
|
AES_cbc_encrypt((unsigned char *)buf, (unsigned char *)cipher, nb_buf + nb_padding, k, iv, AES_ENCRYPT);
|
||||||
|
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
if ((err = SrsFileWriter::write(cipher, nb_buf + nb_padding, NULL)) != srs_success) {
|
||||||
|
srs_warn("ignore err %s", srs_error_desc(err).c_str());
|
||||||
|
srs_error_reset(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
nb_buf = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsFileWriter::close();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
SrsTsMessageCache::SrsTsMessageCache()
|
SrsTsMessageCache::SrsTsMessageCache()
|
||||||
{
|
{
|
||||||
audio = NULL;
|
audio = NULL;
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <srs_kernel_codec.hpp>
|
#include <srs_kernel_codec.hpp>
|
||||||
|
#include <srs_kernel_file.hpp>
|
||||||
|
|
||||||
class SrsBuffer;
|
class SrsBuffer;
|
||||||
class SrsTsMessageCache;
|
class SrsTsMessageCache;
|
||||||
|
@ -1571,6 +1572,29 @@ public:
|
||||||
virtual SrsVideoCodecId video_codec();
|
virtual SrsVideoCodecId video_codec();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef SRS_AUTO_SSL
|
||||||
|
/*
|
||||||
|
* Used for HLS Encryption
|
||||||
|
*/
|
||||||
|
class SrsEncFileWriter: public SrsFileWriter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SrsEncFileWriter();
|
||||||
|
virtual ~SrsEncFileWriter();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t write(void* data, size_t count, ssize_t* pnwrite);
|
||||||
|
virtual void close();
|
||||||
|
public:
|
||||||
|
srs_error_t config_cipher(unsigned char* key, unsigned char* iv);
|
||||||
|
private:
|
||||||
|
unsigned char* key;
|
||||||
|
unsigned char iv[16];
|
||||||
|
private:
|
||||||
|
char* buf;
|
||||||
|
int nb_buf;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TS messages cache, to group frames to TS message,
|
* TS messages cache, to group frames to TS message,
|
||||||
* for example, we may write multiple AAC RAW frames to a TS message.
|
* for example, we may write multiple AAC RAW frames to a TS message.
|
||||||
|
|
|
@ -961,10 +961,26 @@ uint8_t srs_from_hex_char(uint8_t c)
|
||||||
if ('A' <= c && c <= 'F') {
|
if ('A' <= c && c <= 'F') {
|
||||||
return c - 'A' + 10;
|
return c - 'A' + 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *srs_data_to_hex(char *des,const u_int8_t *src,int len)
|
||||||
|
{
|
||||||
|
if(src == NULL || len == 0 || des == NULL){
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *hex_table = "0123456789ABCDEF";
|
||||||
|
|
||||||
|
for (int i=0; i<len; i++) {
|
||||||
|
des[i * 2] = hex_table[src[i] >> 4];
|
||||||
|
des[i * 2 + 1] = hex_table[src[i] & 0x0F];
|
||||||
|
}
|
||||||
|
|
||||||
|
return des;
|
||||||
|
}
|
||||||
|
|
||||||
int srs_hex_to_data(uint8_t* data, const char* p, int size)
|
int srs_hex_to_data(uint8_t* data, const char* p, int size)
|
||||||
{
|
{
|
||||||
if (size <= 0 || (size%2) == 1) {
|
if (size <= 0 || (size%2) == 1) {
|
||||||
|
|
|
@ -163,6 +163,11 @@ extern srs_error_t srs_av_base64_decode(std::string cipher, std::string& plainte
|
||||||
*/
|
*/
|
||||||
extern int srs_hex_to_data(uint8_t* data, const char* p, int size);
|
extern int srs_hex_to_data(uint8_t* data, const char* p, int size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert data string to hex.
|
||||||
|
*/
|
||||||
|
extern char *srs_data_to_hex(char *des, const uint8_t *src, int len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* generate the c0 chunk header for msg.
|
* generate the c0 chunk header for msg.
|
||||||
* @param cache, the cache to write header.
|
* @param cache, the cache to write header.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue