1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-02-15 04:42:04 +00:00

Merge branch 3.0release of https://github.com/harlanc/srs into harlanc-3.0release

This commit is contained in:
winlin 2018-08-25 12:43:52 +08:00
commit 74d5d15c9f
10 changed files with 408 additions and 14 deletions

View file

@ -17,6 +17,6 @@ dependencies:
test:
override:
- (cd trunk && ./configure --without-ssl --without-valgrind && make)
- (cd trunk && ./configure --with-ssl=openssl --without-valgrind && make)
- (cd trunk && ./objs/srs_utest)

View file

@ -1102,6 +1102,26 @@ vhost hls.srs.com {
# default: 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.
# 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

View file

@ -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());
} else if (sdir->name == "hls_wait_keyframe") {
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"
&& 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_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());
}
@ -6278,6 +6289,93 @@ bool SrsConfig::get_hls_wait_keyframe(string vhost)
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* conf = get_vhost(vhost);

View file

@ -1236,6 +1236,26 @@ public:
* whether reap the ts when got keyframe.
*/
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,
* that is, to read max bytes of the bytes from the callback, or timeout or error.

View file

@ -52,6 +52,8 @@ using namespace std;
#include <srs_app_http_hooks.hpp>
#include <srs_protocol_format.hpp>
#include <openssl/rand.h>
// drop the segment when duration of ts too small.
#define SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS 100
@ -60,17 +62,25 @@ using namespace std;
// reset the piece id when deviation overflow this.
#define SRS_JUMP_WHEN_PIECE_DEVIATION 20
SrsHlsSegment::SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc)
SrsHlsSegment::SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc, SrsFileWriter *srswriter)
{
sequence_no = 0;
writer = new SrsFileWriter();
writer = srswriter;
tscw = new SrsTsContextWriter(writer, c, ac, vc);
}
void SrsHlsSegment::SrsSetEncCfg(unsigned char* keyval,unsigned char *ivval)
{
memcpy(iv,ivval,16);
dynamic_cast<SrsEncFileWriter*>(writer)->SetEncCfg(keyval,ivval);
}
SrsHlsSegment::~SrsHlsSegment()
{
srs_freep(tscw);
srs_freep(writer);
//srs_freep(writer);
}
SrsDvrAsyncCallOnHls::SrsDvrAsyncCallOnHls(int c, SrsRequest* r, string p, string t, string m, string mu, int s, double d)
@ -193,6 +203,8 @@ SrsHlsMuxer::SrsHlsMuxer()
max_td = 0;
_sequence_no = 0;
current = NULL;
hls_keys = false;
hls_fragments_per_key = 10;
async = new SrsAsyncCallWorker();
context = new SrsTsContext();
segments = new SrsFragmentWindow();
@ -204,6 +216,7 @@ SrsHlsMuxer::~SrsHlsMuxer()
srs_freep(req);
srs_freep(async);
srs_freep(context);
srs_freep(writer);
}
void SrsHlsMuxer::dispose()
@ -265,8 +278,9 @@ srs_error_t SrsHlsMuxer::initialize()
srs_error_t SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix,
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_freep(req);
@ -284,7 +298,13 @@ srs_error_t SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix,
accept_floor_ts = 0;
hls_window = window;
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.
m3u8_url = srs_path_build_stream(m3u8_file, req->vhost, req->app, req->stream);
m3u8 = path + "/" + m3u8_url;
@ -297,7 +317,27 @@ srs_error_t SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix,
if ((err = srs_create_dir_recursively(m3u8_dir)) != srs_success) {
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)
{
writer = new SrsEncFileWriter();
}
else
{
writer = new SrsFileWriter();
}
return err;
}
@ -342,8 +382,53 @@ srs_error_t SrsHlsMuxer::segment_open()
}
// new segment.
current = new SrsHlsSegment(context, default_acodec, default_vcodec);
current = new SrsHlsSegment(context, default_acodec, default_vcodec,writer);
current->sequence_no = _sequence_no++;
if(hls_keys){
if(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.");
}
int flags = O_CREAT|O_WRONLY|O_TRUNC;
mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH;
int fd;
if ((fd = ::open(key_full_path.c_str(), flags, mode)) < 0) {
return srs_error_new(ERROR_SYSTEM_FILE_OPENE, "open file %s failed", key_full_path.c_str());
}
ssize_t nwrite;
if ((nwrite = ::write(fd, key, 16)) != 16) {
return srs_error_new(ERROR_SYSTEM_FILE_WRITE, "write to file %s failed", key_full_path.c_str());
}
if (::close(fd) < 0) {
srs_warn("close file %s failed",key_full_path.c_str());
}
}
current->SrsSetEncCfg(key,iv);
}
// generate filename.
std::string ts_file = hls_ts_file;
@ -681,6 +766,22 @@ srs_error_t SrsHlsMuxer::_refresh_m3u8(string m3u8_file)
// #EXT-X-DISCONTINUITY\n
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"
ss.precision(3);
@ -778,13 +879,20 @@ srs_error_t SrsHlsController::on_publish(SrsRequest* req)
bool ts_floor = _srs_config->get_hls_ts_floor(vhost);
// the seconds to dispose the hls.
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.
// for the HLS donot requires the EXT-X-MEDIA-SEQUENCE be monotonically increase.
// open muxer
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");
}

View file

@ -67,9 +67,19 @@ public:
SrsFileWriter* writer;
// The TS context writer to write TS to file.
SrsTsContextWriter* tscw;
// Will be saved in m3u8 file.
unsigned char iv[16];
// The full key path.
std::string keypath;
public:
SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc);
SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc, SrsFileWriter *srswriter);
virtual ~SrsHlsSegment();
public:
void SrsSetEncCfg(unsigned char* keyval,unsigned char * ivval);
};
/**
@ -146,6 +156,24 @@ private:
// used to detect the dup or jmp or ts.
int64_t accept_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;
unsigned char key[16];
unsigned char iv[16];
SrsFileWriter *writer;
private:
int _sequence_no;
int max_td;
@ -182,7 +210,8 @@ public:
virtual srs_error_t update_config(SrsRequest* r, std::string entry_prefix,
std::string path, std::string m3u8_file, std::string ts_file,
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)
*/

View file

@ -2610,6 +2610,65 @@ SrsVideoCodecId SrsTsContextWriter::video_codec()
return vcodec;
}
srs_error_t SrsEncFileWriter::write(void* buf, size_t count, ssize_t* pnwrite)
{
srs_assert(count == SRS_TS_PACKET_SIZE);
srs_error_t err = srs_success;
if(buflength != HLS_AES_ENCRYPT_BLOCK_LENGTH)
{
memcpy(tmpbuf+buflength,(char*)buf,SRS_TS_PACKET_SIZE);
buflength += SRS_TS_PACKET_SIZE;
}
if(buflength == HLS_AES_ENCRYPT_BLOCK_LENGTH)
{
unsigned char encryptedbuf[HLS_AES_ENCRYPT_BLOCK_LENGTH];
memset(encryptedbuf,0,HLS_AES_ENCRYPT_BLOCK_LENGTH);
AES_cbc_encrypt((unsigned char *)tmpbuf, (unsigned char *)encryptedbuf, HLS_AES_ENCRYPT_BLOCK_LENGTH, &key, iv, AES_ENCRYPT);
buflength = 0;
memset(tmpbuf,0,HLS_AES_ENCRYPT_BLOCK_LENGTH);
return SrsFileWriter::write(encryptedbuf,HLS_AES_ENCRYPT_BLOCK_LENGTH,pnwrite);
}
else
{
return err;
}
};
srs_error_t SrsEncFileWriter::SetEncCfg(unsigned char* keyval,unsigned char *ivval)
{
srs_error_t err = srs_success;
if (AES_set_encrypt_key(keyval, 16*8, &key))
{
return srs_error_new(ERROR_SYSTEM_FILE_WRITE, "set aes key failed");
}
memcpy(iv,ivval,16);
return err;
}
void SrsEncFileWriter::close()
{
if(buflength > 0)
{
int addBytes = 16 - buflength % 16;
memset(tmpbuf + buflength, addBytes, addBytes);
unsigned char encryptedbuf[buflength+addBytes];
memset(encryptedbuf,0,buflength+addBytes);
AES_cbc_encrypt((unsigned char *)tmpbuf, (unsigned char *)encryptedbuf, buflength+addBytes, &key, iv, AES_ENCRYPT);
SrsFileWriter::write(encryptedbuf,buflength+addBytes,NULL);
buflength = 0;
memset(tmpbuf,0,HLS_AES_ENCRYPT_BLOCK_LENGTH);
}
SrsFileWriter::close();
}
SrsTsMessageCache::SrsTsMessageCache()
{
audio = NULL;

View file

@ -31,8 +31,12 @@
#include <string>
#include <map>
#include <vector>
#include <openssl/aes.h>
#include <cstring>
#include <srs_kernel_codec.hpp>
#include <srs_kernel_file.hpp>
class SrsBuffer;
class SrsTsMessageCache;
@ -1571,6 +1575,41 @@ public:
virtual SrsVideoCodecId video_codec();
};
/*
* Used for HLS Encryption
*/
#define HLS_AES_ENCRYPT_BLOCK_LENGTH 188*4
class SrsEncFileWriter: public SrsFileWriter
{
public:
SrsEncFileWriter()
{
memset(iv,0,16);
memset(tmpbuf,0,HLS_AES_ENCRYPT_BLOCK_LENGTH);
buflength = 0;
}
virtual ~SrsEncFileWriter(){}
virtual srs_error_t write(void* buf, size_t count, ssize_t* pnwrite);
srs_error_t SetEncCfg(unsigned char* key,unsigned char *iv);
virtual void close();
private:
AES_KEY key;
unsigned char iv[16];
private:
char tmpbuf[HLS_AES_ENCRYPT_BLOCK_LENGTH];
int buflength;
};
/**
* TS messages cache, to group frames to TS message,
* for example, we may write multiple AAC RAW frames to a TS message.

View file

@ -961,10 +961,26 @@ uint8_t srs_from_hex_char(uint8_t c)
if ('A' <= c && c <= 'F') {
return c - 'A' + 10;
}
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)
{
if (size <= 0 || (size%2) == 1) {

View file

@ -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);
/**
* 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.
* @param cache, the cache to write header.