2017-03-25 09:21:39 +00:00
|
|
|
/**
|
|
|
|
* The MIT License (MIT)
|
|
|
|
*
|
2019-01-01 13:37:28 +00:00
|
|
|
* Copyright (c) 2013-2019 Winlin
|
2017-03-25 09:21:39 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
2014-03-21 09:56:27 +00:00
|
|
|
|
|
|
|
#include <srs_kernel_utility.hpp>
|
|
|
|
|
2015-11-11 02:37:50 +00:00
|
|
|
// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213
|
2014-11-19 08:16:04 +00:00
|
|
|
#ifndef _WIN32
|
2014-05-06 03:04:12 +00:00
|
|
|
#include <unistd.h>
|
2014-06-08 05:03:03 +00:00
|
|
|
#include <netdb.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <sys/time.h>
|
2014-11-19 08:16:04 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <string.h>
|
2015-01-03 04:54:54 +00:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
2015-10-27 09:54:07 +00:00
|
|
|
#include <stdlib.h>
|
2014-06-08 05:03:03 +00:00
|
|
|
|
2015-08-25 14:29:00 +00:00
|
|
|
#include <vector>
|
2014-06-08 05:03:03 +00:00
|
|
|
using namespace std;
|
2014-03-21 09:56:27 +00:00
|
|
|
|
2017-10-14 04:09:53 +00:00
|
|
|
#include <srs_core_autofree.hpp>
|
2014-03-21 09:56:27 +00:00
|
|
|
#include <srs_kernel_log.hpp>
|
2015-01-03 04:54:54 +00:00
|
|
|
#include <srs_kernel_error.hpp>
|
2015-09-22 00:48:55 +00:00
|
|
|
#include <srs_kernel_buffer.hpp>
|
2015-05-24 17:02:06 +00:00
|
|
|
#include <srs_kernel_flv.hpp>
|
2014-03-21 09:56:27 +00:00
|
|
|
|
2014-06-27 05:35:28 +00:00
|
|
|
// this value must:
|
|
|
|
// equals to (SRS_SYS_CYCLE_INTERVAL*SRS_SYS_TIME_RESOLUTION_MS_TIMES)*1000
|
|
|
|
// @see SRS_SYS_TIME_RESOLUTION_MS_TIMES
|
|
|
|
#define SYS_TIME_RESOLUTION_US 300*1000
|
|
|
|
|
2017-12-31 04:11:48 +00:00
|
|
|
srs_error_t srs_avc_nalu_read_uev(SrsBitBuffer* stream, int32_t& v)
|
2015-04-03 15:17:50 +00:00
|
|
|
{
|
2017-12-31 04:11:48 +00:00
|
|
|
srs_error_t err = srs_success;
|
2015-04-03 15:17:50 +00:00
|
|
|
|
|
|
|
if (stream->empty()) {
|
2017-12-31 04:11:48 +00:00
|
|
|
return srs_error_new(ERROR_AVC_NALU_UEV, "empty stream");
|
2015-04-03 15:17:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ue(v) in 9.1 Parsing process for Exp-Golomb codes
|
2017-01-26 09:28:49 +00:00
|
|
|
// ISO_IEC_14496-10-AVC-2012.pdf, page 227.
|
2015-04-03 15:17:50 +00:00
|
|
|
// Syntax elements coded as ue(v), me(v), or se(v) are Exp-Golomb-coded.
|
|
|
|
// leadingZeroBits = -1;
|
|
|
|
// for( b = 0; !b; leadingZeroBits++ )
|
|
|
|
// b = read_bits( 1 )
|
|
|
|
// The variable codeNum is then assigned as follows:
|
2015-05-09 15:12:12 +00:00
|
|
|
// codeNum = (2<<leadingZeroBits) - 1 + read_bits( leadingZeroBits )
|
2015-04-03 15:17:50 +00:00
|
|
|
int leadingZeroBits = -1;
|
|
|
|
for (int8_t b = 0; !b && !stream->empty(); leadingZeroBits++) {
|
|
|
|
b = stream->read_bit();
|
|
|
|
}
|
|
|
|
|
2015-04-04 07:53:36 +00:00
|
|
|
if (leadingZeroBits >= 31) {
|
2017-12-31 04:11:48 +00:00
|
|
|
return srs_error_new(ERROR_AVC_NALU_UEV, "%dbits overflow 31bits", leadingZeroBits);
|
2015-04-03 15:17:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
v = (1 << leadingZeroBits) - 1;
|
2018-12-09 14:48:47 +00:00
|
|
|
for (int i = 0; i < (int)leadingZeroBits; i++) {
|
2019-02-04 07:11:41 +00:00
|
|
|
if (stream->empty()) {
|
|
|
|
return srs_error_new(ERROR_AVC_NALU_UEV, "no bytes for leadingZeroBits=%d", leadingZeroBits);
|
|
|
|
}
|
|
|
|
|
2015-04-04 10:24:07 +00:00
|
|
|
int32_t b = stream->read_bit();
|
2016-02-18 06:15:23 +00:00
|
|
|
v += b << (leadingZeroBits - 1 - i);
|
2015-04-03 15:17:50 +00:00
|
|
|
}
|
|
|
|
|
2017-12-31 04:11:48 +00:00
|
|
|
return err;
|
2015-04-03 15:17:50 +00:00
|
|
|
}
|
|
|
|
|
2017-12-31 04:11:48 +00:00
|
|
|
srs_error_t srs_avc_nalu_read_bit(SrsBitBuffer* stream, int8_t& v)
|
2015-04-03 15:17:50 +00:00
|
|
|
{
|
2017-12-31 04:11:48 +00:00
|
|
|
srs_error_t err = srs_success;
|
2015-04-03 15:17:50 +00:00
|
|
|
|
|
|
|
if (stream->empty()) {
|
2017-12-31 04:11:48 +00:00
|
|
|
return srs_error_new(ERROR_AVC_NALU_UEV, "empty stream");
|
2015-04-03 15:17:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
v = stream->read_bit();
|
|
|
|
|
2017-12-31 04:11:48 +00:00
|
|
|
return err;
|
2015-04-03 15:17:50 +00:00
|
|
|
}
|
|
|
|
|
2019-04-10 01:07:03 +00:00
|
|
|
srs_utime_t _srs_system_time_us_cache = 0;
|
|
|
|
srs_utime_t _srs_system_time_startup_time = 0;
|
2014-03-21 09:56:27 +00:00
|
|
|
|
2019-04-10 01:07:03 +00:00
|
|
|
srs_utime_t srs_get_system_time()
|
2014-03-21 09:56:27 +00:00
|
|
|
{
|
2014-04-17 09:35:21 +00:00
|
|
|
if (_srs_system_time_us_cache <= 0) {
|
2019-04-10 00:32:46 +00:00
|
|
|
srs_update_system_time();
|
2014-04-17 09:35:21 +00:00
|
|
|
}
|
|
|
|
|
2019-04-10 01:07:03 +00:00
|
|
|
return _srs_system_time_us_cache;
|
2014-03-21 09:56:27 +00:00
|
|
|
}
|
2018-07-21 11:09:32 +00:00
|
|
|
|
2019-04-10 01:11:54 +00:00
|
|
|
srs_utime_t srs_get_system_startup_time()
|
2014-06-27 05:35:28 +00:00
|
|
|
{
|
|
|
|
if (_srs_system_time_startup_time <= 0) {
|
2019-04-10 00:32:46 +00:00
|
|
|
srs_update_system_time();
|
2014-06-27 05:35:28 +00:00
|
|
|
}
|
|
|
|
|
2019-04-10 01:07:03 +00:00
|
|
|
return _srs_system_time_startup_time;
|
2014-06-27 05:35:28 +00:00
|
|
|
}
|
2018-07-21 11:09:32 +00:00
|
|
|
|
2019-05-15 00:57:46 +00:00
|
|
|
// For utest to mock it.
|
|
|
|
_srs_gettimeofday_t _srs_gettimeofday = ::gettimeofday;
|
|
|
|
|
2019-04-10 00:32:46 +00:00
|
|
|
srs_utime_t srs_update_system_time()
|
2014-03-21 09:56:27 +00:00
|
|
|
{
|
|
|
|
timeval now;
|
|
|
|
|
2019-05-15 00:57:46 +00:00
|
|
|
if (_srs_gettimeofday(&now, NULL) < 0) {
|
2014-03-21 09:56:27 +00:00
|
|
|
srs_warn("gettimeofday failed, ignore");
|
2014-12-12 13:51:06 +00:00
|
|
|
return -1;
|
2014-03-21 09:56:27 +00:00
|
|
|
}
|
2017-03-25 09:21:39 +00:00
|
|
|
|
2015-11-11 02:37:50 +00:00
|
|
|
// @see: https://github.com/ossrs/srs/issues/35
|
2014-03-21 09:56:27 +00:00
|
|
|
// we must convert the tv_sec/tv_usec to int64_t.
|
|
|
|
int64_t now_us = ((int64_t)now.tv_sec) * 1000 * 1000 + (int64_t)now.tv_usec;
|
2014-06-27 05:35:28 +00:00
|
|
|
|
|
|
|
// for some ARM os, the starttime maybe invalid,
|
|
|
|
// for example, on the cubieboard2, the srs_startup_time is 1262304014640,
|
|
|
|
// while now is 1403842979210 in ms, diff is 141538964570 ms, 1638 days
|
|
|
|
// it's impossible, and maybe the problem of startup time is invalid.
|
|
|
|
// use date +%s to get system time is 1403844851.
|
|
|
|
// so we use relative time.
|
|
|
|
if (_srs_system_time_us_cache <= 0) {
|
2017-01-17 04:25:30 +00:00
|
|
|
_srs_system_time_startup_time = _srs_system_time_us_cache = now_us;
|
2019-04-10 00:32:46 +00:00
|
|
|
return _srs_system_time_us_cache;
|
2014-06-27 05:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// use relative time.
|
|
|
|
int64_t diff = now_us - _srs_system_time_us_cache;
|
|
|
|
diff = srs_max(0, diff);
|
|
|
|
if (diff < 0 || diff > 1000 * SYS_TIME_RESOLUTION_US) {
|
2017-05-06 06:05:22 +00:00
|
|
|
srs_warn("clock jump, history=%" PRId64 "us, now=%" PRId64 "us, diff=%" PRId64 "us", _srs_system_time_us_cache, now_us, diff);
|
2015-11-11 02:37:50 +00:00
|
|
|
// @see: https://github.com/ossrs/srs/issues/109
|
2014-06-27 05:35:28 +00:00
|
|
|
_srs_system_time_startup_time += diff;
|
2014-03-21 09:56:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_srs_system_time_us_cache = now_us;
|
2017-05-06 06:05:22 +00:00
|
|
|
srs_info("clock updated, startup=%" PRId64 "us, now=%" PRId64 "us", _srs_system_time_startup_time, _srs_system_time_us_cache);
|
2014-12-12 13:51:06 +00:00
|
|
|
|
2019-04-10 00:32:46 +00:00
|
|
|
return _srs_system_time_us_cache;
|
2014-03-21 09:56:27 +00:00
|
|
|
}
|
2014-06-08 05:03:03 +00:00
|
|
|
|
2019-04-11 00:35:57 +00:00
|
|
|
// TODO: FIXME: Replace by ST dns resolve.
|
2017-10-14 03:29:33 +00:00
|
|
|
string srs_dns_resolve(string host, int& family)
|
2014-06-08 05:03:03 +00:00
|
|
|
{
|
2017-10-14 03:29:33 +00:00
|
|
|
addrinfo hints;
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
|
|
hints.ai_family = family;
|
2017-10-14 04:09:53 +00:00
|
|
|
|
|
|
|
addrinfo* r = NULL;
|
|
|
|
SrsAutoFree(addrinfo, r);
|
|
|
|
|
2018-03-20 11:50:46 +00:00
|
|
|
if(getaddrinfo(host.c_str(), NULL, NULL, &r)) {
|
2014-06-08 05:03:03 +00:00
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2017-10-14 04:09:53 +00:00
|
|
|
char saddr[64];
|
|
|
|
char* h = (char*)saddr;
|
|
|
|
socklen_t nbh = sizeof(saddr);
|
|
|
|
const int r0 = getnameinfo(r->ai_addr, r->ai_addrlen, h, nbh, NULL, 0, NI_NUMERICHOST);
|
2017-10-14 03:29:33 +00:00
|
|
|
|
2018-03-20 11:50:46 +00:00
|
|
|
if(!r0) {
|
2017-10-14 04:09:53 +00:00
|
|
|
family = r->ai_family;
|
|
|
|
return string(saddr);
|
2014-06-08 05:03:03 +00:00
|
|
|
}
|
2017-10-14 03:29:33 +00:00
|
|
|
return "";
|
2014-06-08 05:03:03 +00:00
|
|
|
}
|
|
|
|
|
2015-09-24 09:54:58 +00:00
|
|
|
void srs_parse_hostport(const string& hostport, string& host, int& port)
|
|
|
|
{
|
2017-10-14 03:29:33 +00:00
|
|
|
const size_t pos = hostport.rfind(":"); // Look for ":" from the end, to work with IPv6.
|
2015-09-24 09:54:58 +00:00
|
|
|
if (pos != std::string::npos) {
|
2017-10-14 03:29:33 +00:00
|
|
|
const string p = hostport.substr(pos + 1);
|
|
|
|
if ((pos >= 1) &&
|
|
|
|
(hostport[0] == '[') &&
|
|
|
|
(hostport[pos - 1] == ']')) {
|
|
|
|
// Handle IPv6 in RFC 2732 format, e.g. [3ffe:dead:beef::1]:1935
|
|
|
|
host = hostport.substr(1, pos - 2);
|
2017-10-14 04:09:53 +00:00
|
|
|
} else {
|
2017-10-14 03:29:33 +00:00
|
|
|
// Handle IP address
|
|
|
|
host = hostport.substr(0, pos);
|
|
|
|
}
|
2015-09-24 09:54:58 +00:00
|
|
|
port = ::atoi(p.c_str());
|
2015-09-24 10:33:07 +00:00
|
|
|
} else {
|
|
|
|
host = hostport;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-07 02:34:55 +00:00
|
|
|
string srs_any_address_for_listener()
|
2017-10-14 03:29:33 +00:00
|
|
|
{
|
2019-10-07 02:34:55 +00:00
|
|
|
bool ipv4_active = false;
|
|
|
|
bool ipv6_active = false;
|
|
|
|
|
|
|
|
if (true) {
|
|
|
|
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
|
|
if(fd != -1) {
|
|
|
|
ipv4_active = true;
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (true) {
|
|
|
|
int fd = socket(AF_INET6, SOCK_DGRAM, 0);
|
|
|
|
if(fd != -1) {
|
|
|
|
ipv6_active = true;
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ipv6_active && !ipv4_active) {
|
2017-10-14 04:09:53 +00:00
|
|
|
return "::";
|
2017-10-14 03:29:33 +00:00
|
|
|
}
|
2017-10-14 04:09:53 +00:00
|
|
|
return "0.0.0.0";
|
2017-10-14 03:29:33 +00:00
|
|
|
}
|
|
|
|
|
2015-09-24 10:33:07 +00:00
|
|
|
void srs_parse_endpoint(string hostport, string& ip, int& port)
|
|
|
|
{
|
2017-10-14 03:29:33 +00:00
|
|
|
const size_t pos = hostport.rfind(":"); // Look for ":" from the end, to work with IPv6.
|
|
|
|
if (pos != std::string::npos) {
|
2017-10-14 04:09:53 +00:00
|
|
|
if ((pos >= 1) && (hostport[0] == '[') && (hostport[pos - 1] == ']')) {
|
2017-10-14 03:29:33 +00:00
|
|
|
// Handle IPv6 in RFC 2732 format, e.g. [3ffe:dead:beef::1]:1935
|
|
|
|
ip = hostport.substr(1, pos - 2);
|
2017-10-14 04:09:53 +00:00
|
|
|
} else {
|
2017-10-14 03:29:33 +00:00
|
|
|
// Handle IP address
|
|
|
|
ip = hostport.substr(0, pos);
|
|
|
|
}
|
2017-10-14 04:09:53 +00:00
|
|
|
|
2017-10-14 03:29:33 +00:00
|
|
|
const string sport = hostport.substr(pos + 1);
|
2015-09-24 10:33:07 +00:00
|
|
|
port = ::atoi(sport.c_str());
|
|
|
|
} else {
|
2019-10-07 02:34:55 +00:00
|
|
|
ip = srs_any_address_for_listener();
|
2015-09-24 10:33:07 +00:00
|
|
|
port = ::atoi(hostport.c_str());
|
2015-09-24 09:54:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
string srs_int2str(int64_t value)
|
|
|
|
{
|
|
|
|
// len(max int64_t) is 20, plus one "+-."
|
|
|
|
char tmp[22];
|
2017-05-06 06:05:22 +00:00
|
|
|
snprintf(tmp, 22, "%" PRId64, value);
|
2015-09-24 09:54:58 +00:00
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
string srs_float2str(double value)
|
|
|
|
{
|
|
|
|
// len(max int64_t) is 20, plus one "+-."
|
|
|
|
char tmp[22];
|
|
|
|
snprintf(tmp, 22, "%.2f", value);
|
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
|
2015-09-24 10:33:07 +00:00
|
|
|
string srs_bool2switch(bool v) {
|
|
|
|
return v? "on" : "off";
|
|
|
|
}
|
|
|
|
|
2014-06-08 05:03:03 +00:00
|
|
|
bool srs_is_little_endian()
|
|
|
|
{
|
2017-03-25 09:21:39 +00:00
|
|
|
// convert to network(big-endian) order, if not equals,
|
2014-06-08 05:03:03 +00:00
|
|
|
// the system is little-endian, so need to convert the int64
|
|
|
|
static int little_endian_check = -1;
|
|
|
|
|
|
|
|
if(little_endian_check == -1) {
|
|
|
|
union {
|
|
|
|
int32_t i;
|
|
|
|
int8_t c;
|
|
|
|
} little_check_union;
|
|
|
|
|
|
|
|
little_check_union.i = 0x01;
|
|
|
|
little_endian_check = little_check_union.c;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (little_endian_check == 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
string srs_string_replace(string str, string old_str, string new_str)
|
|
|
|
{
|
|
|
|
std::string ret = str;
|
|
|
|
|
|
|
|
if (old_str == new_str) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t pos = 0;
|
|
|
|
while ((pos = ret.find(old_str, pos)) != std::string::npos) {
|
|
|
|
ret = ret.replace(pos, old_str.length(), new_str);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
string srs_string_trim_end(string str, string trim_chars)
|
|
|
|
{
|
|
|
|
std::string ret = str;
|
|
|
|
|
|
|
|
for (int i = 0; i < (int)trim_chars.length(); i++) {
|
|
|
|
char ch = trim_chars.at(i);
|
|
|
|
|
|
|
|
while (!ret.empty() && ret.at(ret.length() - 1) == ch) {
|
|
|
|
ret.erase(ret.end() - 1);
|
|
|
|
|
|
|
|
// ok, matched, should reset the search
|
2018-11-11 05:27:33 +00:00
|
|
|
i = -1;
|
2014-06-08 05:03:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
string srs_string_trim_start(string str, string trim_chars)
|
|
|
|
{
|
|
|
|
std::string ret = str;
|
|
|
|
|
|
|
|
for (int i = 0; i < (int)trim_chars.length(); i++) {
|
|
|
|
char ch = trim_chars.at(i);
|
|
|
|
|
|
|
|
while (!ret.empty() && ret.at(0) == ch) {
|
|
|
|
ret.erase(ret.begin());
|
|
|
|
|
|
|
|
// ok, matched, should reset the search
|
2018-11-11 05:27:33 +00:00
|
|
|
i = -1;
|
2014-06-08 05:03:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
string srs_string_remove(string str, string remove_chars)
|
|
|
|
{
|
|
|
|
std::string ret = str;
|
|
|
|
|
|
|
|
for (int i = 0; i < (int)remove_chars.length(); i++) {
|
|
|
|
char ch = remove_chars.at(i);
|
|
|
|
|
|
|
|
for (std::string::iterator it = ret.begin(); it != ret.end();) {
|
|
|
|
if (ch == *it) {
|
|
|
|
it = ret.erase(it);
|
|
|
|
|
|
|
|
// ok, matched, should reset the search
|
2018-11-11 05:27:33 +00:00
|
|
|
i = -1;
|
2014-06-08 05:03:03 +00:00
|
|
|
} else {
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-11-11 05:31:29 +00:00
|
|
|
string srs_erase_first_substr(string str, string erase_string)
|
|
|
|
{
|
|
|
|
std::string ret = str;
|
|
|
|
|
|
|
|
size_t pos = ret.find(erase_string);
|
|
|
|
|
2018-11-11 05:51:10 +00:00
|
|
|
if (pos != std::string::npos) {
|
2018-11-11 05:31:29 +00:00
|
|
|
ret.erase(pos, erase_string.length());
|
|
|
|
}
|
2018-11-11 05:51:10 +00:00
|
|
|
|
2018-11-11 05:31:29 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
string srs_erase_last_substr(string str, string erase_string)
|
|
|
|
{
|
|
|
|
std::string ret = str;
|
|
|
|
|
|
|
|
size_t pos = ret.rfind(erase_string);
|
|
|
|
|
2018-11-11 05:51:10 +00:00
|
|
|
if (pos != std::string::npos) {
|
2018-11-11 05:31:29 +00:00
|
|
|
ret.erase(pos, erase_string.length());
|
|
|
|
}
|
2018-11-11 05:51:10 +00:00
|
|
|
|
2018-11-11 05:31:29 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-06-08 05:03:03 +00:00
|
|
|
bool srs_string_ends_with(string str, string flag)
|
|
|
|
{
|
2017-10-14 03:29:33 +00:00
|
|
|
const size_t pos = str.rfind(flag);
|
2017-06-10 00:42:42 +00:00
|
|
|
return (pos != string::npos) && (pos == str.length() - flag.length());
|
2014-06-08 05:03:03 +00:00
|
|
|
}
|
2014-08-02 14:18:39 +00:00
|
|
|
|
2017-02-07 13:56:20 +00:00
|
|
|
bool srs_string_ends_with(string str, string flag0, string flag1)
|
|
|
|
{
|
|
|
|
return srs_string_ends_with(str, flag0) || srs_string_ends_with(str, flag1);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool srs_string_ends_with(string str, string flag0, string flag1, string flag2)
|
|
|
|
{
|
|
|
|
return srs_string_ends_with(str, flag0) || srs_string_ends_with(str, flag1) || srs_string_ends_with(str, flag2);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool srs_string_ends_with(string str, string flag0, string flag1, string flag2, string flag3)
|
|
|
|
{
|
|
|
|
return srs_string_ends_with(str, flag0) || srs_string_ends_with(str, flag1) || srs_string_ends_with(str, flag2) || srs_string_ends_with(str, flag3);
|
|
|
|
}
|
|
|
|
|
2015-03-30 06:08:17 +00:00
|
|
|
bool srs_string_starts_with(string str, string flag)
|
|
|
|
{
|
|
|
|
return str.find(flag) == 0;
|
|
|
|
}
|
|
|
|
|
2015-09-09 13:46:29 +00:00
|
|
|
bool srs_string_starts_with(string str, string flag0, string flag1)
|
|
|
|
{
|
|
|
|
return srs_string_starts_with(str, flag0) || srs_string_starts_with(str, flag1);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool srs_string_starts_with(string str, string flag0, string flag1, string flag2)
|
|
|
|
{
|
|
|
|
return srs_string_starts_with(str, flag0, flag1) || srs_string_starts_with(str, flag2);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool srs_string_starts_with(string str, string flag0, string flag1, string flag2, string flag3)
|
|
|
|
{
|
|
|
|
return srs_string_starts_with(str, flag0, flag1, flag2) || srs_string_starts_with(str, flag3);
|
|
|
|
}
|
|
|
|
|
2015-04-21 04:53:45 +00:00
|
|
|
bool srs_string_contains(string str, string flag)
|
|
|
|
{
|
|
|
|
return str.find(flag) != string::npos;
|
|
|
|
}
|
|
|
|
|
2015-12-31 07:57:16 +00:00
|
|
|
bool srs_string_contains(string str, string flag0, string flag1)
|
|
|
|
{
|
|
|
|
return str.find(flag0) != string::npos || str.find(flag1) != string::npos;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool srs_string_contains(string str, string flag0, string flag1, string flag2)
|
|
|
|
{
|
|
|
|
return str.find(flag0) != string::npos || str.find(flag1) != string::npos || str.find(flag2) != string::npos;
|
|
|
|
}
|
|
|
|
|
2015-08-25 14:29:00 +00:00
|
|
|
vector<string> srs_string_split(string str, string flag)
|
|
|
|
{
|
|
|
|
vector<string> arr;
|
|
|
|
|
2019-01-01 12:19:44 +00:00
|
|
|
if (flag.empty()) {
|
|
|
|
arr.push_back(str);
|
|
|
|
return arr;
|
|
|
|
}
|
|
|
|
|
2015-08-25 14:29:00 +00:00
|
|
|
size_t pos;
|
|
|
|
string s = str;
|
|
|
|
|
|
|
|
while ((pos = s.find(flag)) != string::npos) {
|
2015-12-08 10:32:37 +00:00
|
|
|
if (pos != 0) {
|
|
|
|
arr.push_back(s.substr(0, pos));
|
|
|
|
}
|
2015-12-07 10:22:55 +00:00
|
|
|
s = s.substr(pos + flag.length());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!s.empty()) {
|
|
|
|
arr.push_back(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
return arr;
|
|
|
|
}
|
|
|
|
|
|
|
|
string srs_string_min_match(string str, vector<string> flags)
|
|
|
|
{
|
|
|
|
string match;
|
|
|
|
|
2019-01-01 12:19:44 +00:00
|
|
|
if (flags.empty()) {
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2015-12-07 10:22:55 +00:00
|
|
|
size_t min_pos = string::npos;
|
|
|
|
for (vector<string>::iterator it = flags.begin(); it != flags.end(); ++it) {
|
|
|
|
string flag = *it;
|
|
|
|
|
|
|
|
size_t pos = str.find(flag);
|
|
|
|
if (pos == string::npos) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (min_pos == string::npos || pos < min_pos) {
|
|
|
|
min_pos = pos;
|
|
|
|
match = flag;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return match;
|
|
|
|
}
|
|
|
|
|
|
|
|
vector<string> srs_string_split(string str, vector<string> flags)
|
|
|
|
{
|
|
|
|
vector<string> arr;
|
|
|
|
|
|
|
|
size_t pos = string::npos;
|
|
|
|
string s = str;
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
string flag = srs_string_min_match(s, flags);
|
|
|
|
if (flag.empty()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((pos = s.find(flag)) == string::npos) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-12-08 10:32:37 +00:00
|
|
|
if (pos != 0) {
|
|
|
|
arr.push_back(s.substr(0, pos));
|
|
|
|
}
|
2015-12-07 10:22:55 +00:00
|
|
|
s = s.substr(pos + flag.length());
|
2015-08-25 14:29:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!s.empty()) {
|
|
|
|
arr.push_back(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
return arr;
|
|
|
|
}
|
|
|
|
|
2015-03-21 03:55:28 +00:00
|
|
|
int srs_do_create_dir_recursively(string dir)
|
2015-01-03 04:54:54 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
// stat current dir, if exists, return error.
|
2015-02-19 11:50:10 +00:00
|
|
|
if (srs_path_exists(dir)) {
|
2015-01-03 04:54:54 +00:00
|
|
|
return ERROR_SYSTEM_DIR_EXISTS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// create parent first.
|
|
|
|
size_t pos;
|
|
|
|
if ((pos = dir.rfind("/")) != std::string::npos) {
|
|
|
|
std::string parent = dir.substr(0, pos);
|
2015-03-21 03:55:28 +00:00
|
|
|
ret = srs_do_create_dir_recursively(parent);
|
2015-01-03 04:54:54 +00:00
|
|
|
// return for error.
|
|
|
|
if (ret != ERROR_SUCCESS && ret != ERROR_SYSTEM_DIR_EXISTS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
// parent exists, set to ok.
|
|
|
|
ret = ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// create curren dir.
|
2015-11-11 02:37:50 +00:00
|
|
|
// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213
|
2015-08-18 06:08:54 +00:00
|
|
|
#ifndef _WIN32
|
2015-01-03 04:54:54 +00:00
|
|
|
mode_t mode = S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IXOTH;
|
|
|
|
if (::mkdir(dir.c_str(), mode) < 0) {
|
2015-08-18 06:08:54 +00:00
|
|
|
#else
|
2018-07-21 11:09:32 +00:00
|
|
|
if (::mkdir(dir.c_str()) < 0) {
|
2015-08-18 06:08:54 +00:00
|
|
|
#endif
|
2018-07-21 11:09:32 +00:00
|
|
|
if (errno == EEXIST) {
|
|
|
|
return ERROR_SYSTEM_DIR_EXISTS;
|
2015-01-03 04:54:54 +00:00
|
|
|
}
|
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
ret = ERROR_SYSTEM_CREATE_DIR;
|
|
|
|
srs_error("create dir %s failed. ret=%d", dir.c_str(), ret);
|
2015-01-03 04:54:54 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
srs_info("create dir %s success.", dir.c_str());
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool srs_bytes_equals(void* pa, void* pb, int size)
|
|
|
|
{
|
|
|
|
uint8_t* a = (uint8_t*)pa;
|
|
|
|
uint8_t* b = (uint8_t*)pb;
|
|
|
|
|
|
|
|
if (!a && !b) {
|
2017-02-07 13:56:20 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
if (!a || !b) {
|
|
|
|
return false;
|
2017-02-07 13:56:20 +00:00
|
|
|
}
|
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
for(int i = 0; i < size; i++){
|
|
|
|
if(a[i] != b[i]){
|
|
|
|
return false;
|
2017-03-25 09:21:39 +00:00
|
|
|
}
|
2015-01-03 04:54:54 +00:00
|
|
|
}
|
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
srs_error_t srs_create_dir_recursively(string dir)
|
|
|
|
{
|
|
|
|
int ret = srs_do_create_dir_recursively(dir);
|
|
|
|
|
|
|
|
if (ret == ERROR_SYSTEM_DIR_EXISTS || ret == ERROR_SUCCESS) {
|
|
|
|
return srs_success;
|
2015-02-19 11:50:10 +00:00
|
|
|
}
|
2015-04-03 08:42:32 +00:00
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
return srs_error_new(ret, "create dir %s", dir.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool srs_path_exists(std::string path)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
// stat current dir, if exists, return error.
|
|
|
|
if (stat(path.c_str(), &st) == 0) {
|
|
|
|
return true;
|
2015-04-03 08:42:32 +00:00
|
|
|
}
|
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
string srs_path_dirname(string path)
|
|
|
|
{
|
|
|
|
std::string dirname = path;
|
|
|
|
size_t pos = string::npos;
|
|
|
|
|
|
|
|
if ((pos = dirname.rfind("/")) != string::npos) {
|
|
|
|
if (pos == 0) {
|
|
|
|
return "/";
|
2015-04-21 04:53:45 +00:00
|
|
|
}
|
2018-07-21 11:09:32 +00:00
|
|
|
dirname = dirname.substr(0, pos);
|
2015-04-21 04:53:45 +00:00
|
|
|
}
|
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
return dirname;
|
|
|
|
}
|
|
|
|
|
|
|
|
string srs_path_basename(string path)
|
|
|
|
{
|
|
|
|
std::string dirname = path;
|
|
|
|
size_t pos = string::npos;
|
|
|
|
|
|
|
|
if ((pos = dirname.rfind("/")) != string::npos) {
|
|
|
|
// the basename("/") is "/"
|
|
|
|
if (dirname.length() == 1) {
|
|
|
|
return dirname;
|
2017-03-25 09:21:39 +00:00
|
|
|
}
|
2018-07-21 11:09:32 +00:00
|
|
|
dirname = dirname.substr(pos + 1);
|
2015-10-13 08:06:37 +00:00
|
|
|
}
|
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
return dirname;
|
|
|
|
}
|
|
|
|
|
|
|
|
string srs_path_filename(string path)
|
|
|
|
{
|
|
|
|
std::string filename = path;
|
|
|
|
size_t pos = string::npos;
|
|
|
|
|
|
|
|
if ((pos = filename.rfind(".")) != string::npos) {
|
|
|
|
return filename.substr(0, pos);
|
2015-10-13 08:06:37 +00:00
|
|
|
}
|
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
return filename;
|
|
|
|
}
|
|
|
|
|
|
|
|
string srs_path_filext(string path)
|
|
|
|
{
|
|
|
|
size_t pos = string::npos;
|
|
|
|
|
|
|
|
if ((pos = path.rfind(".")) != string::npos) {
|
|
|
|
return path.substr(pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
bool srs_avc_startswith_annexb(SrsBuffer* stream, int* pnb_start_code)
|
|
|
|
{
|
2019-01-01 12:19:44 +00:00
|
|
|
if (!stream) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
char* bytes = stream->data() + stream->pos();
|
|
|
|
char* p = bytes;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
if (!stream->require((int)(p - bytes + 3))) {
|
2015-01-22 09:08:38 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
// not match
|
|
|
|
if (p[0] != (char)0x00 || p[1] != (char)0x00) {
|
2015-01-22 09:08:38 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
// match N[00] 00 00 01, where N>=0
|
|
|
|
if (p[2] == (char)0x01) {
|
|
|
|
if (pnb_start_code) {
|
|
|
|
*pnb_start_code = (int)(p - bytes) + 3;
|
|
|
|
}
|
|
|
|
return true;
|
2015-01-22 09:08:38 +00:00
|
|
|
}
|
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
p++;
|
2015-01-22 09:08:38 +00:00
|
|
|
}
|
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool srs_aac_startswith_adts(SrsBuffer* stream)
|
|
|
|
{
|
2019-01-01 12:19:44 +00:00
|
|
|
if (!stream) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
char* bytes = stream->data() + stream->pos();
|
|
|
|
char* p = bytes;
|
2015-01-22 09:08:38 +00:00
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
if (!stream->require((int)(p - bytes) + 2)) {
|
|
|
|
return false;
|
2015-01-22 09:08:38 +00:00
|
|
|
}
|
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
// matched 12bits 0xFFF,
|
|
|
|
// @remark, we must cast the 0xff to char to compare.
|
|
|
|
if (p[0] != (char)0xff || (char)(p[1] & 0xf0) != (char)0xf0) {
|
|
|
|
return false;
|
2015-01-22 09:08:38 +00:00
|
|
|
}
|
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
return true;
|
|
|
|
}
|
2018-07-22 07:21:05 +00:00
|
|
|
|
|
|
|
// @see pycrc reflect at https://github.com/winlinvip/pycrc/blob/master/pycrc/algorithms.py#L107
|
|
|
|
uint64_t __crc32_reflect(uint64_t data, int width)
|
2018-07-21 11:09:32 +00:00
|
|
|
{
|
2018-07-22 07:21:05 +00:00
|
|
|
uint64_t res = data & 0x01;
|
|
|
|
|
2018-12-09 14:48:47 +00:00
|
|
|
for (int i = 0; i < (int)width - 1; i++) {
|
2018-07-22 07:21:05 +00:00
|
|
|
data >>= 1;
|
|
|
|
res = (res << 1) | (data & 0x01);
|
2017-03-25 09:21:39 +00:00
|
|
|
}
|
|
|
|
|
2018-07-22 07:21:05 +00:00
|
|
|
return res;
|
2018-07-21 12:58:20 +00:00
|
|
|
}
|
|
|
|
|
2018-07-22 07:21:05 +00:00
|
|
|
// @see pycrc gen_table at https://github.com/winlinvip/pycrc/blob/master/pycrc/algorithms.py#L178
|
|
|
|
void __crc32_make_table(uint32_t t[256], uint32_t poly, bool reflect_in)
|
2018-07-21 12:58:20 +00:00
|
|
|
{
|
2018-07-22 07:21:05 +00:00
|
|
|
int width = 32; // 32bits checksum.
|
|
|
|
uint64_t msb_mask = (uint32_t)(0x01 << (width - 1));
|
|
|
|
uint64_t mask = (uint32_t)(((msb_mask - 1) << 1) | 1);
|
|
|
|
|
|
|
|
int tbl_idx_width = 8; // table index size.
|
|
|
|
int tbl_width = 0x01 << tbl_idx_width; // table size: 256
|
|
|
|
|
2018-12-09 14:48:47 +00:00
|
|
|
for (int i = 0; i < (int)tbl_width; i++) {
|
2018-07-22 07:21:05 +00:00
|
|
|
uint64_t reg = uint64_t(i);
|
2017-03-25 09:21:39 +00:00
|
|
|
|
2018-07-22 07:21:05 +00:00
|
|
|
if (reflect_in) {
|
|
|
|
reg = __crc32_reflect(reg, tbl_idx_width);
|
|
|
|
}
|
2018-07-21 12:58:20 +00:00
|
|
|
|
2018-07-22 07:21:05 +00:00
|
|
|
reg = reg << (width - tbl_idx_width);
|
|
|
|
for (int j = 0; j < tbl_idx_width; j++) {
|
|
|
|
if ((reg&msb_mask) != 0) {
|
|
|
|
reg = (reg << 1) ^ poly;
|
2018-07-21 12:58:20 +00:00
|
|
|
} else {
|
2018-07-22 07:21:05 +00:00
|
|
|
reg = reg << 1;
|
2018-07-21 12:58:20 +00:00
|
|
|
}
|
|
|
|
}
|
2017-03-25 09:21:39 +00:00
|
|
|
|
2018-07-22 07:21:05 +00:00
|
|
|
if (reflect_in) {
|
|
|
|
reg = __crc32_reflect(reg, width);
|
2015-01-22 09:08:38 +00:00
|
|
|
}
|
|
|
|
|
2018-07-22 07:21:05 +00:00
|
|
|
t[i] = (uint32_t)(reg & mask);
|
2015-01-22 09:08:38 +00:00
|
|
|
}
|
2018-07-21 12:58:20 +00:00
|
|
|
}
|
2018-07-22 07:21:05 +00:00
|
|
|
|
|
|
|
// @see pycrc table_driven at https://github.com/winlinvip/pycrc/blob/master/pycrc/algorithms.py#L207
|
|
|
|
uint32_t __crc32_table_driven(uint32_t* t, const void* buf, int size, uint32_t previous, bool reflect_in, uint32_t xor_in, bool reflect_out, uint32_t xor_out)
|
|
|
|
{
|
|
|
|
int width = 32; // 32bits checksum.
|
|
|
|
uint64_t msb_mask = (uint32_t)(0x01 << (width - 1));
|
|
|
|
uint64_t mask = (uint32_t)(((msb_mask - 1) << 1) | 1);
|
2015-01-22 09:08:38 +00:00
|
|
|
|
2018-07-22 07:21:05 +00:00
|
|
|
int tbl_idx_width = 8; // table index size.
|
2018-07-21 11:09:32 +00:00
|
|
|
|
2018-07-22 07:21:05 +00:00
|
|
|
uint8_t* p = (uint8_t*)buf;
|
|
|
|
uint64_t reg = 0;
|
|
|
|
|
|
|
|
if (!reflect_in) {
|
|
|
|
reg = xor_in;
|
2017-03-25 09:21:39 +00:00
|
|
|
|
2018-12-09 14:48:47 +00:00
|
|
|
for (int i = 0; i < (int)size; i++) {
|
2018-07-22 07:21:05 +00:00
|
|
|
uint8_t tblidx = (uint8_t)((reg >> (width - tbl_idx_width)) ^ p[i]);
|
|
|
|
reg = t[tblidx] ^ (reg << tbl_idx_width);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
reg = previous ^ __crc32_reflect(xor_in, width);
|
2017-03-25 09:21:39 +00:00
|
|
|
|
2018-12-09 14:48:47 +00:00
|
|
|
for (int i = 0; i < (int)size; i++) {
|
2018-07-22 07:21:05 +00:00
|
|
|
uint8_t tblidx = (uint8_t)(reg ^ p[i]);
|
|
|
|
reg = t[tblidx] ^ (reg >> tbl_idx_width);
|
2017-03-25 09:21:39 +00:00
|
|
|
}
|
|
|
|
|
2018-07-22 07:21:05 +00:00
|
|
|
reg = __crc32_reflect(reg, width);
|
2017-03-25 09:21:39 +00:00
|
|
|
}
|
2015-01-22 09:08:38 +00:00
|
|
|
|
2018-07-22 07:21:05 +00:00
|
|
|
if (reflect_out) {
|
|
|
|
reg = __crc32_reflect(reg, width);
|
2015-01-22 09:08:38 +00:00
|
|
|
}
|
|
|
|
|
2018-07-22 07:21:05 +00:00
|
|
|
reg ^= xor_out;
|
|
|
|
return (uint32_t)(reg & mask);
|
2018-07-21 11:09:32 +00:00
|
|
|
}
|
2018-07-21 12:58:20 +00:00
|
|
|
|
2018-07-22 07:21:05 +00:00
|
|
|
// @see pycrc https://github.com/winlinvip/pycrc/blob/master/pycrc/algorithms.py#L207
|
2018-07-21 12:58:20 +00:00
|
|
|
// IEEETable is the table for the IEEE polynomial.
|
|
|
|
static uint32_t __crc32_IEEE_table[256];
|
|
|
|
static bool __crc32_IEEE_table_initialized = false;
|
2018-07-21 11:09:32 +00:00
|
|
|
|
2018-07-22 07:21:05 +00:00
|
|
|
// @see pycrc https://github.com/winlinvip/pycrc/blob/master/pycrc/models.py#L220
|
|
|
|
// crc32('123456789') = 0xcbf43926
|
|
|
|
// where it's defined as model:
|
|
|
|
// 'name': 'crc-32',
|
|
|
|
// 'width': 32,
|
|
|
|
// 'poly': 0x4c11db7,
|
|
|
|
// 'reflect_in': True,
|
|
|
|
// 'xor_in': 0xffffffff,
|
|
|
|
// 'reflect_out': True,
|
|
|
|
// 'xor_out': 0xffffffff,
|
|
|
|
// 'check': 0xcbf43926,
|
2018-07-21 11:09:32 +00:00
|
|
|
uint32_t srs_crc32_ieee(const void* buf, int size, uint32_t previous)
|
|
|
|
{
|
2018-07-22 07:21:05 +00:00
|
|
|
// @see golang IEEE of hash/crc32/crc32.go
|
|
|
|
// IEEE is by far and away the most common CRC-32 polynomial.
|
|
|
|
// Used by ethernet (IEEE 802.3), v.42, fddi, gzip, zip, png, ...
|
|
|
|
// @remark The poly of CRC32 IEEE is 0x04C11DB7, its reverse is 0xEDB88320,
|
|
|
|
// please read https://en.wikipedia.org/wiki/Cyclic_redundancy_check
|
|
|
|
uint32_t poly = 0x04C11DB7;
|
|
|
|
|
|
|
|
bool reflect_in = true;
|
|
|
|
uint32_t xor_in = 0xffffffff;
|
|
|
|
bool reflect_out = true;
|
|
|
|
uint32_t xor_out = 0xffffffff;
|
|
|
|
|
2018-07-21 12:58:20 +00:00
|
|
|
if (!__crc32_IEEE_table_initialized) {
|
2018-07-22 07:21:05 +00:00
|
|
|
__crc32_make_table(__crc32_IEEE_table, poly, reflect_in);
|
2018-07-21 12:58:20 +00:00
|
|
|
__crc32_IEEE_table_initialized = true;
|
2015-01-22 09:08:38 +00:00
|
|
|
}
|
|
|
|
|
2018-07-22 07:21:05 +00:00
|
|
|
return __crc32_table_driven(__crc32_IEEE_table, buf, size, previous, reflect_in, xor_in, reflect_out, xor_out);
|
|
|
|
}
|
2017-03-25 09:21:39 +00:00
|
|
|
|
2018-07-22 07:21:05 +00:00
|
|
|
// @see pycrc https://github.com/winlinvip/pycrc/blob/master/pycrc/algorithms.py#L238
|
|
|
|
// IEEETable is the table for the MPEG polynomial.
|
|
|
|
static uint32_t __crc32_MPEG_table[256];
|
|
|
|
static bool __crc32_MPEG_table_initialized = false;
|
|
|
|
|
|
|
|
// @see pycrc https://github.com/winlinvip/pycrc/blob/master/pycrc/models.py#L238
|
|
|
|
// crc32('123456789') = 0x0376e6e7
|
|
|
|
// where it's defined as model:
|
|
|
|
// 'name': 'crc-32',
|
|
|
|
// 'width': 32,
|
|
|
|
// 'poly': 0x4c11db7,
|
|
|
|
// 'reflect_in': False,
|
|
|
|
// 'xor_in': 0xffffffff,
|
|
|
|
// 'reflect_out': False,
|
|
|
|
// 'xor_out': 0x0,
|
|
|
|
// 'check': 0x0376e6e7,
|
|
|
|
uint32_t srs_crc32_mpegts(const void* buf, int size)
|
|
|
|
{
|
|
|
|
// @see golang IEEE of hash/crc32/crc32.go
|
|
|
|
// IEEE is by far and away the most common CRC-32 polynomial.
|
|
|
|
// Used by ethernet (IEEE 802.3), v.42, fddi, gzip, zip, png, ...
|
|
|
|
// @remark The poly of CRC32 IEEE is 0x04C11DB7, its reverse is 0xEDB88320,
|
|
|
|
// please read https://en.wikipedia.org/wiki/Cyclic_redundancy_check
|
|
|
|
uint32_t poly = 0x04C11DB7;
|
|
|
|
|
|
|
|
bool reflect_in = false;
|
|
|
|
uint32_t xor_in = 0xffffffff;
|
|
|
|
bool reflect_out = false;
|
|
|
|
uint32_t xor_out = 0x0;
|
|
|
|
|
|
|
|
if (!__crc32_MPEG_table_initialized) {
|
|
|
|
__crc32_make_table(__crc32_MPEG_table, poly, reflect_in);
|
|
|
|
__crc32_MPEG_table_initialized = true;
|
|
|
|
}
|
2017-03-25 09:21:39 +00:00
|
|
|
|
2018-07-22 07:21:05 +00:00
|
|
|
return __crc32_table_driven(__crc32_MPEG_table, buf, size, 0x00, reflect_in, xor_in, reflect_out, xor_out);
|
2018-07-21 11:09:32 +00:00
|
|
|
}
|
|
|
|
|
2018-07-22 09:56:38 +00:00
|
|
|
// @see golang encoding/base64/base64.go
|
|
|
|
srs_error_t srs_av_base64_decode(string cipher, string& plaintext)
|
|
|
|
{
|
|
|
|
srs_error_t err = srs_success;
|
2017-03-25 09:21:39 +00:00
|
|
|
|
2018-07-22 09:56:38 +00:00
|
|
|
// We use the standard encoding:
|
|
|
|
// var StdEncoding = NewEncoding(encodeStd)
|
|
|
|
// StdEncoding is the standard base64 encoding, as defined in RFC 4648.
|
|
|
|
char padding = '=';
|
|
|
|
string encoder = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
2017-03-25 09:21:39 +00:00
|
|
|
|
2018-07-22 09:56:38 +00:00
|
|
|
uint8_t decodeMap[256];
|
|
|
|
memset(decodeMap, 0xff, sizeof(decodeMap));
|
2017-03-25 09:21:39 +00:00
|
|
|
|
2018-12-09 14:48:47 +00:00
|
|
|
for (int i = 0; i < (int)encoder.length(); i++) {
|
2018-07-22 09:56:38 +00:00
|
|
|
decodeMap[(uint8_t)encoder.at(i)] = uint8_t(i);
|
|
|
|
}
|
2017-03-25 09:21:39 +00:00
|
|
|
|
2018-07-22 09:56:38 +00:00
|
|
|
// decode is like Decode but returns an additional 'end' value, which
|
|
|
|
// indicates if end-of-message padding or a partial quantum was encountered
|
|
|
|
// and thus any additional data is an error.
|
|
|
|
int si = 0;
|
2017-03-25 09:21:39 +00:00
|
|
|
|
2018-07-22 09:56:38 +00:00
|
|
|
// skip over newlines
|
2018-12-09 14:48:47 +00:00
|
|
|
for (; si < (int)cipher.length() && (cipher.at(si) == '\n' || cipher.at(si) == '\r'); si++) {
|
2017-03-25 09:21:39 +00:00
|
|
|
}
|
|
|
|
|
2018-12-09 14:48:47 +00:00
|
|
|
for (bool end = false; si < (int)cipher.length() && !end;) {
|
2018-07-22 09:56:38 +00:00
|
|
|
// Decode quantum using the base64 alphabet
|
|
|
|
uint8_t dbuf[4];
|
|
|
|
memset(dbuf, 0x00, sizeof(dbuf));
|
2017-03-25 09:21:39 +00:00
|
|
|
|
2018-07-22 09:56:38 +00:00
|
|
|
int dinc = 3;
|
|
|
|
int dlen = 4;
|
2018-12-09 14:48:47 +00:00
|
|
|
srs_assert(dinc > 0);
|
2017-03-25 09:21:39 +00:00
|
|
|
|
2018-12-09 14:48:47 +00:00
|
|
|
for (int j = 0; j < (int)sizeof(dbuf); j++) {
|
|
|
|
if (si == (int)cipher.length()) {
|
2018-07-22 09:56:38 +00:00
|
|
|
if (padding != -1 || j < 2) {
|
|
|
|
return srs_error_new(ERROR_BASE64_DECODE, "corrupt input at %d", si);
|
|
|
|
}
|
|
|
|
|
|
|
|
dinc = j - 1;
|
|
|
|
dlen = j;
|
|
|
|
end = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
char in = cipher.at(si);
|
|
|
|
|
|
|
|
si++;
|
|
|
|
// skip over newlines
|
2018-12-09 14:48:47 +00:00
|
|
|
for (; si < (int)cipher.length() && (cipher.at(si) == '\n' || cipher.at(si) == '\r'); si++) {
|
2018-07-22 09:56:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (in == padding) {
|
|
|
|
// We've reached the end and there's padding
|
|
|
|
switch (j) {
|
|
|
|
case 0:
|
|
|
|
case 1:
|
|
|
|
// incorrect padding
|
|
|
|
return srs_error_new(ERROR_BASE64_DECODE, "corrupt input at %d", si);
|
|
|
|
case 2:
|
|
|
|
// "==" is expected, the first "=" is already consumed.
|
2018-12-09 14:48:47 +00:00
|
|
|
if (si == (int)cipher.length()) {
|
2018-07-22 09:56:38 +00:00
|
|
|
return srs_error_new(ERROR_BASE64_DECODE, "corrupt input at %d", si);
|
|
|
|
}
|
|
|
|
if (cipher.at(si) != padding) {
|
|
|
|
// incorrect padding
|
|
|
|
return srs_error_new(ERROR_BASE64_DECODE, "corrupt input at %d", si);
|
|
|
|
}
|
|
|
|
|
|
|
|
si++;
|
|
|
|
// skip over newlines
|
2018-12-09 14:48:47 +00:00
|
|
|
for (; si < (int)cipher.length() && (cipher.at(si) == '\n' || cipher.at(si) == '\r'); si++) {
|
2018-07-22 09:56:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-09 14:48:47 +00:00
|
|
|
if (si < (int)cipher.length()) {
|
2018-07-22 09:56:38 +00:00
|
|
|
// trailing garbage
|
|
|
|
err = srs_error_new(ERROR_BASE64_DECODE, "corrupt input at %d", si);
|
|
|
|
}
|
|
|
|
dinc = 3;
|
|
|
|
dlen = j;
|
|
|
|
end = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
dbuf[j] = decodeMap[(uint8_t)in];
|
|
|
|
if (dbuf[j] == 0xff) {
|
|
|
|
return srs_error_new(ERROR_BASE64_DECODE, "corrupt input at %d", si);
|
|
|
|
}
|
2017-03-25 09:21:39 +00:00
|
|
|
}
|
|
|
|
|
2018-07-22 09:56:38 +00:00
|
|
|
// Convert 4x 6bit source bytes into 3 bytes
|
|
|
|
uint32_t val = uint32_t(dbuf[0])<<18 | uint32_t(dbuf[1])<<12 | uint32_t(dbuf[2])<<6 | uint32_t(dbuf[3]);
|
|
|
|
if (dlen >= 2) {
|
|
|
|
plaintext.append(1, char(val >> 16));
|
2017-03-25 09:21:39 +00:00
|
|
|
}
|
2018-07-22 09:56:38 +00:00
|
|
|
if (dlen >= 3) {
|
|
|
|
plaintext.append(1, char(val >> 8));
|
2017-03-25 09:21:39 +00:00
|
|
|
}
|
2018-07-22 09:56:38 +00:00
|
|
|
if (dlen >= 4) {
|
|
|
|
plaintext.append(1, char(val));
|
2017-03-25 09:21:39 +00:00
|
|
|
}
|
2015-02-17 06:16:02 +00:00
|
|
|
}
|
2017-03-25 09:21:39 +00:00
|
|
|
|
2018-07-22 09:56:38 +00:00
|
|
|
return err;
|
2018-07-21 11:09:32 +00:00
|
|
|
}
|
|
|
|
|
2015-02-18 14:28:39 +00:00
|
|
|
#define SPACE_CHARS " \t\r\n"
|
2018-07-21 11:09:32 +00:00
|
|
|
|
|
|
|
int av_toupper(int c)
|
|
|
|
{
|
|
|
|
if (c >= 'a' && c <= 'z') {
|
|
|
|
c ^= 0x20;
|
|
|
|
}
|
|
|
|
return c;
|
|
|
|
}
|
2017-03-25 09:21:39 +00:00
|
|
|
|
2018-07-22 10:47:38 +00:00
|
|
|
// fromHexChar converts a hex character into its value and a success flag.
|
|
|
|
uint8_t srs_from_hex_char(uint8_t c)
|
|
|
|
{
|
|
|
|
if ('0' <= c && c <= '9') {
|
|
|
|
return c - '0';
|
|
|
|
}
|
|
|
|
if ('a' <= c && c <= 'f') {
|
|
|
|
return c - 'a' + 10;
|
|
|
|
}
|
|
|
|
if ('A' <= c && c <= 'F') {
|
|
|
|
return c - 'A' + 10;
|
2015-05-24 17:02:06 +00:00
|
|
|
}
|
2018-03-25 04:05:52 +00:00
|
|
|
|
2018-07-22 10:47:38 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2018-08-12 06:10:18 +00:00
|
|
|
|
2019-02-07 12:58:33 +00:00
|
|
|
char* srs_data_to_hex(char* des, const u_int8_t* src, int len)
|
2018-08-14 23:37:12 +00:00
|
|
|
{
|
|
|
|
if(src == NULL || len == 0 || des == NULL){
|
|
|
|
return NULL;
|
2018-03-25 04:05:52 +00:00
|
|
|
}
|
2018-08-14 23:37:12 +00:00
|
|
|
|
|
|
|
const char *hex_table = "0123456789ABCDEF";
|
2015-05-24 17:02:06 +00:00
|
|
|
|
2018-08-14 23:37:12 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-07-22 10:47:38 +00:00
|
|
|
int srs_hex_to_data(uint8_t* data, const char* p, int size)
|
2018-07-21 11:09:32 +00:00
|
|
|
{
|
2018-07-22 10:47:38 +00:00
|
|
|
if (size <= 0 || (size%2) == 1) {
|
|
|
|
return -1;
|
2015-05-24 17:02:06 +00:00
|
|
|
}
|
|
|
|
|
2018-12-09 14:48:47 +00:00
|
|
|
for (int i = 0; i < (int)size / 2; i++) {
|
2018-07-22 10:47:38 +00:00
|
|
|
uint8_t a = srs_from_hex_char(p[i*2]);
|
|
|
|
if (a == (uint8_t)-1) {
|
|
|
|
return -1;
|
2017-03-25 09:21:39 +00:00
|
|
|
}
|
|
|
|
|
2018-07-22 10:47:38 +00:00
|
|
|
uint8_t b = srs_from_hex_char(p[i*2 + 1]);
|
|
|
|
if (b == (uint8_t)-1) {
|
|
|
|
return -1;
|
2017-03-25 09:21:39 +00:00
|
|
|
}
|
|
|
|
|
2018-07-22 10:47:38 +00:00
|
|
|
data[i] = (a << 4) | b;
|
2015-05-24 17:02:06 +00:00
|
|
|
}
|
2018-07-22 10:47:38 +00:00
|
|
|
|
|
|
|
return size / 2;
|
2018-07-21 11:09:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int srs_chunk_header_c0(int perfer_cid, uint32_t timestamp, int32_t payload_length, int8_t message_type, int32_t stream_id, char* cache, int nb_cache)
|
|
|
|
{
|
|
|
|
// to directly set the field.
|
|
|
|
char* pp = NULL;
|
2015-05-24 17:02:06 +00:00
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
// generate the header.
|
|
|
|
char* p = cache;
|
|
|
|
|
|
|
|
// no header.
|
|
|
|
if (nb_cache < SRS_CONSTS_RTMP_MAX_FMT0_HEADER_SIZE) {
|
|
|
|
return 0;
|
2015-05-24 17:02:06 +00:00
|
|
|
}
|
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
// write new chunk stream header, fmt is 0
|
|
|
|
*p++ = 0x00 | (perfer_cid & 0x3F);
|
|
|
|
|
|
|
|
// chunk message header, 11 bytes
|
|
|
|
// timestamp, 3bytes, big-endian
|
|
|
|
if (timestamp < RTMP_EXTENDED_TIMESTAMP) {
|
|
|
|
pp = (char*)×tamp;
|
2015-05-24 17:02:06 +00:00
|
|
|
*p++ = pp[2];
|
|
|
|
*p++ = pp[1];
|
|
|
|
*p++ = pp[0];
|
2018-07-21 11:09:32 +00:00
|
|
|
} else {
|
|
|
|
*p++ = 0xFF;
|
|
|
|
*p++ = 0xFF;
|
|
|
|
*p++ = 0xFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
// message_length, 3bytes, big-endian
|
|
|
|
pp = (char*)&payload_length;
|
|
|
|
*p++ = pp[2];
|
|
|
|
*p++ = pp[1];
|
|
|
|
*p++ = pp[0];
|
|
|
|
|
|
|
|
// message_type, 1bytes
|
|
|
|
*p++ = message_type;
|
|
|
|
|
|
|
|
// stream_id, 4bytes, little-endian
|
|
|
|
pp = (char*)&stream_id;
|
|
|
|
*p++ = pp[0];
|
|
|
|
*p++ = pp[1];
|
|
|
|
*p++ = pp[2];
|
|
|
|
*p++ = pp[3];
|
|
|
|
|
|
|
|
// for c0
|
|
|
|
// chunk extended timestamp header, 0 or 4 bytes, big-endian
|
|
|
|
//
|
|
|
|
// for c3:
|
|
|
|
// chunk extended timestamp header, 0 or 4 bytes, big-endian
|
|
|
|
// 6.1.3. Extended Timestamp
|
|
|
|
// This field is transmitted only when the normal time stamp in the
|
|
|
|
// chunk message header is set to 0x00ffffff. If normal time stamp is
|
|
|
|
// set to any value less than 0x00ffffff, this field MUST NOT be
|
|
|
|
// present. This field MUST NOT be present if the timestamp field is not
|
|
|
|
// present. Type 3 chunks MUST NOT have this field.
|
|
|
|
// adobe changed for Type3 chunk:
|
|
|
|
// FMLE always sendout the extended-timestamp,
|
|
|
|
// must send the extended-timestamp to FMS,
|
|
|
|
// must send the extended-timestamp to flash-player.
|
|
|
|
// @see: ngx_rtmp_prepare_message
|
|
|
|
// @see: http://blog.csdn.net/win_lin/article/details/13363699
|
|
|
|
// TODO: FIXME: extract to outer.
|
|
|
|
if (timestamp >= RTMP_EXTENDED_TIMESTAMP) {
|
|
|
|
pp = (char*)×tamp;
|
2017-03-25 09:21:39 +00:00
|
|
|
*p++ = pp[3];
|
2018-07-21 11:09:32 +00:00
|
|
|
*p++ = pp[2];
|
|
|
|
*p++ = pp[1];
|
|
|
|
*p++ = pp[0];
|
2015-05-24 17:02:06 +00:00
|
|
|
}
|
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
// always has header
|
|
|
|
return (int)(p - cache);
|
|
|
|
}
|
|
|
|
|
|
|
|
int srs_chunk_header_c3(int perfer_cid, uint32_t timestamp, char* cache, int nb_cache)
|
|
|
|
{
|
|
|
|
// to directly set the field.
|
|
|
|
char* pp = NULL;
|
|
|
|
|
|
|
|
// generate the header.
|
|
|
|
char* p = cache;
|
|
|
|
|
|
|
|
// no header.
|
|
|
|
if (nb_cache < SRS_CONSTS_RTMP_MAX_FMT3_HEADER_SIZE) {
|
|
|
|
return 0;
|
2015-05-24 17:02:06 +00:00
|
|
|
}
|
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
// write no message header chunk stream, fmt is 3
|
|
|
|
// @remark, if perfer_cid > 0x3F, that is, use 2B/3B chunk header,
|
|
|
|
// SRS will rollback to 1B chunk header.
|
|
|
|
*p++ = 0xC0 | (perfer_cid & 0x3F);
|
|
|
|
|
|
|
|
// for c0
|
|
|
|
// chunk extended timestamp header, 0 or 4 bytes, big-endian
|
|
|
|
//
|
|
|
|
// for c3:
|
|
|
|
// chunk extended timestamp header, 0 or 4 bytes, big-endian
|
|
|
|
// 6.1.3. Extended Timestamp
|
|
|
|
// This field is transmitted only when the normal time stamp in the
|
|
|
|
// chunk message header is set to 0x00ffffff. If normal time stamp is
|
|
|
|
// set to any value less than 0x00ffffff, this field MUST NOT be
|
|
|
|
// present. This field MUST NOT be present if the timestamp field is not
|
|
|
|
// present. Type 3 chunks MUST NOT have this field.
|
|
|
|
// adobe changed for Type3 chunk:
|
|
|
|
// FMLE always sendout the extended-timestamp,
|
|
|
|
// must send the extended-timestamp to FMS,
|
|
|
|
// must send the extended-timestamp to flash-player.
|
|
|
|
// @see: ngx_rtmp_prepare_message
|
|
|
|
// @see: http://blog.csdn.net/win_lin/article/details/13363699
|
|
|
|
// TODO: FIXME: extract to outer.
|
|
|
|
if (timestamp >= RTMP_EXTENDED_TIMESTAMP) {
|
|
|
|
pp = (char*)×tamp;
|
|
|
|
*p++ = pp[3];
|
|
|
|
*p++ = pp[2];
|
|
|
|
*p++ = pp[1];
|
|
|
|
*p++ = pp[0];
|
2015-05-24 17:02:06 +00:00
|
|
|
}
|
|
|
|
|
2018-07-21 11:09:32 +00:00
|
|
|
// always has header
|
|
|
|
return (int)(p - cache);
|
|
|
|
}
|
|
|
|
|