Merge remote-tracking branch 'remotes/upstream/master'

This commit is contained in:
nanguantong 2019-10-24 16:05:58 +08:00
commit 4dd02e2de7
18 changed files with 172 additions and 78 deletions

2
.gitattributes vendored
View File

@ -1,2 +1,4 @@
release/ filter=lfs diff=lfs merge=lfs -text
*.a filter=lfs diff=lfs merge=lfs -text
*.h linguist-language=cpp
*.c linguist-language=cpp

5
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,5 @@
# These are supported funding model platforms
custom: ['https://www.paypal.me/xiachu']
ko_fi: xiachu
issuehunt: xiongziliang
liberapay: xiachu

@ -1 +1 @@
Subproject commit 665f53b6a4385e2312d3bf09aa305d7e3bf079e6
Subproject commit 4a6029b74b4f2339e32b8c546388de51e4ec1bcb

View File

@ -6,12 +6,13 @@ apiDebug=1
secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc
[ffmpeg]
#FFmpeg可执行程序路径
#FFmpeg可执行程序绝对路径
bin=/usr/local/bin/ffmpeg
#FFmpeg拉流再推流的命令模板通过该模板可以设置再编码的一些参数
cmd=%s -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s
#FFmpeg日志的路径如果置空则不生成FFmpeg日志
log=/Users/xzl/git/ZLMediaKit/release/mac/Release/ffmpeg/ffmpeg.log
#可以为相对(相对于本可执行程序目录)或绝对路径
log=./ffmpeg/ffmpeg.log
[general]
#是否启用虚拟主机
@ -40,7 +41,8 @@ resetWhenRePlay=1
#hls写文件的buf大小调整参数可以提高文件io性能
fileBufSize=65536
#hls保存文件路径
filePath=/Users/xzl/git/ZLMediaKit/release/mac/Release/httpRoot
#可以为相对(相对于本可执行程序目录)或绝对路径
filePath=./httpRoot
#hls最大切片时间
segDur=3
#m3u8索引中,hls保留切片个数(实际保留切片个数大2~3个)
@ -93,7 +95,8 @@ notFound=<html><head><title>404 Not Found</title></head><body bgcolor="white"><c
#http服务器监听端口
port=80
#http文件服务器根目录
rootPath=/Users/xzl/git/ZLMediaKit/release/mac/Release/httpRoot
#可以为相对(相对于本可执行程序目录)或绝对路径
rootPath=./httpRoot
#http文件服务器读文件缓存大小单位BYTE调整该参数可以优化文件io性能
sendBufSize=65536
#https服务器监听端口
@ -114,7 +117,8 @@ appName=record
#mp4录制写文件缓存单位BYTE,调整参数可以提高文件io性能
fileBufSize=65536
#mp4录制保存、mp4点播根路径
filePath=/Users/xzl/git/ZLMediaKit/release/mac/Release/httpRoot
#可以为相对(相对于本可执行程序目录)或绝对路径
filePath=./httpRoot
#mp4录制切片时间单位秒
fileSecond=3600
#mp4点播每次流化数据量单位毫秒

View File

@ -39,7 +39,7 @@ const char kLog[] = FFmpeg_FIELD"log";
onceToken token([]() {
mINI::Instance()[kBin] = trim(System::execute("which ffmpeg"));
mINI::Instance()[kCmd] = "%s -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s";
mINI::Instance()[kLog] = exeDir() + "ffmpeg/ffmpeg.log";
mINI::Instance()[kLog] = "./ffmpeg/ffmpeg.log";
});
}
@ -64,7 +64,7 @@ void FFmpegSource::play(const string &src_url,const string &dst_url,int timeout_
char cmd[1024] = {0};
snprintf(cmd, sizeof(cmd),ffmpeg_cmd.data(),ffmpeg_bin.data(),src_url.data(),dst_url.data());
_process.run(cmd,ffmpeg_log);
_process.run(cmd,File::absolutePath("",ffmpeg_log));
InfoL << cmd;
if(_media_info._host == "127.0.0.1"){

View File

@ -34,9 +34,8 @@
#include "Util/File.h"
#include "Util/logger.h"
#include "Util/uv_errno.h"
#include "Util/TimeTicker.h"
#include "Thread/WorkThreadPool.h"
#include "Process.h"
#include "Poller/Timer.h"
using namespace toolkit;
void Process::run(const string &cmd, const string &log_file_tmp) {
@ -46,12 +45,11 @@ void Process::run(const string &cmd, const string &log_file_tmp) {
throw std::runtime_error(StrPrinter << "fork child process falied,err:" << get_uv_errmsg());
}
if (_pid == 0) {
//子进程
//子进程关闭core文件生成
struct rlimit rlim = {0,0};
setrlimit(RLIMIT_CORE, &rlim);
//在启动子进程时暂时禁用SIGINT、SIGTERM信号
// ignore the SIGINT and SIGTERM
signal(SIGINT, SIG_IGN);
signal(SIGTERM, SIG_IGN);
@ -109,24 +107,73 @@ void Process::run(const string &cmd, const string &log_file_tmp) {
InfoL << "start child proces " << _pid;
}
void Process::kill(int max_delay) {
/**
*
* @param pid
* @param exit_code_ptr
* @param block
* @return
*/
static bool s_wait(pid_t pid,int *exit_code_ptr,bool block) {
if (pid <= 0) {
return false;
}
int status = 0;
pid_t p = waitpid(pid, &status, block ? 0 : WNOHANG);
int exit_code = (status & 0xFF00) >> 8;
if(exit_code_ptr){
*exit_code_ptr = (status & 0xFF00) >> 8;
}
if (p < 0) {
WarnL << "waitpid failed, pid=" << pid << ", err=" << get_uv_errmsg();
return false;
}
if (p > 0) {
InfoL << "process terminated, pid=" << pid << ", exit code=" << exit_code;
return false;
}
//WarnL << "process is running, pid=" << _pid;
return true;
}
static void s_kill(pid_t pid,int max_delay,bool force){
if (pid <= 0) {
//pid无效
return;
}
if (::kill(pid, force ? SIGKILL : SIGTERM) == -1) {
//进程可能已经退出了
WarnL << "kill process " << pid << " failed:" << get_uv_errmsg();
return;
}
if(force){
//发送SIGKILL信号后阻塞等待退出
s_wait(pid, NULL, true);
DebugL << "force kill " << pid << " success!";
return;
}
//发送SIGTERM信号后2秒后检查子进程是否已经退出
WorkThreadPool::Instance().getPoller()->doDelayTask(max_delay,[pid](){
if (!s_wait(pid, nullptr, false)) {
//进程已经退出了
return 0;
}
//进程还在运行
WarnL << "process still working,force kill it:" << pid;
s_kill(pid,0, true);
return 0;
});
}
void Process::kill(int max_delay,bool force) {
if (_pid <= 0) {
return;
}
if (::kill(_pid, SIGTERM) == -1) {
WarnL << "kill process " << _pid << " falied,err:" << get_uv_errmsg();
} else {
//等待子进程退出
auto pid = _pid;
EventPollerPool::Instance().getPoller()->doDelayTask(max_delay,[pid](){
//最多等待2秒,2秒后强制杀掉程序
if (waitpid(pid, NULL, WNOHANG) == 0) {
::kill(pid, SIGKILL);
WarnL << "force kill process " << pid;
}
return 0;
});
}
s_kill(_pid,max_delay,force);
_pid = -1;
}
@ -134,28 +181,10 @@ Process::~Process() {
kill(2000);
}
Process::Process() {
}
Process::Process() {}
bool Process::wait(bool block) {
if (_pid <= 0) {
return false;
}
int status = 0;
pid_t p = waitpid(_pid, &status, block ? 0 : WNOHANG);
_exit_code = (status & 0xFF00) >> 8;
if (p < 0) {
WarnL << "waitpid failed, pid=" << _pid << ", err=" << get_uv_errmsg();
return false;
}
if (p > 0) {
InfoL << "process terminated, pid=" << _pid << ", exit code=" << _exit_code;
return false;
}
//WarnL << "process is running, pid=" << _pid;
return true;
return s_wait(_pid,&_exit_code,block);
}
int Process::exit_code() {

View File

@ -36,7 +36,7 @@ public:
Process();
~Process();
void run(const string &cmd,const string &log_file);
void kill(int max_delay);
void kill(int max_delay,bool force = false);
bool wait(bool block = true);
int exit_code();
private:

View File

@ -45,6 +45,7 @@
#include "Util/MD5.h"
#include "WebApi.h"
#include "WebHook.h"
#include "Thread/WorkThreadPool.h"
#if !defined(_WIN32)
#include "FFmpegSource.h"
@ -281,6 +282,23 @@ void installWebApi() {
});
});
//获取后台工作线程负载
//测试url http://127.0.0.1/index/api/getWorkThreadsLoad
API_REGIST_INVOKER(api, getWorkThreadsLoad, {
WorkThreadPool::Instance().getExecutorDelay([invoker, headerOut](const vector<int> &vecDelay) {
Value val;
auto vec = WorkThreadPool::Instance().getExecutorLoad();
int i = 0;
for (auto load : vec) {
Value obj(objectValue);
obj["load"] = load;
obj["delay"] = vecDelay[i++];
val["data"].append(obj);
}
invoker("200 OK", headerOut, val.toStyledString());
});
});
//获取服务器配置
//测试url http://127.0.0.1/index/api/getServerConfig
API_REGIST(api, getServerConfig, {

View File

@ -122,7 +122,7 @@ const string kMaxReqCount = HTTP_FIELD"maxReqCount";
const string kCharSet = HTTP_FIELD"charSet";
//http 服务器根目录
#define HTTP_ROOT_PATH (exeDir() + "httpRoot")
#define HTTP_ROOT_PATH "./httpRoot"
const string kRootPath = HTTP_FIELD"rootPath";
//http 404错误提示内容

View File

@ -41,8 +41,8 @@ Track::Ptr Factory::getTrackBySdp(const SdpTrack::Ptr &track) {
aac_cfg_str = FindField(track->_fmtp.data(), "config=", ";");
}
if (aac_cfg_str.empty()) {
//延后获取adts头
return std::make_shared<AACTrack>();
//如果sdp中获取不到aac config信息那么在rtp也无法获取那么忽略该Track
return nullptr;
}
string aac_cfg;

View File

@ -169,21 +169,32 @@ void HttpSession::onRecv(const Buffer::Ptr &pBuf) {
}
void HttpSession::onError(const SockException& err) {
if(_is_flv_stream){
//flv播放器
WarnP(this) << "播放器("
<< _mediaInfo._vhost << "/"
<< _mediaInfo._app << "/"
<< _mediaInfo._streamid
<< ")断开:" << err.what();
GET_CONFIG(uint32_t,iFlowThreshold,General::kFlowThreshold);
if(_ui64TotalBytes > iFlowThreshold * 1024){
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport,
_mediaInfo,
_ui64TotalBytes,
_ticker.createdTime()/1000,
true,
*this);
}
return;
}
//http客户端
if(_ticker.createdTime() < 10 * 1000){
TraceP(this) << err.what();
}else{
WarnP(this) << err.what();
}
GET_CONFIG(uint32_t,iFlowThreshold,General::kFlowThreshold);
if(_ui64TotalBytes > iFlowThreshold * 1024){
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport,
_mediaInfo,
_ui64TotalBytes,
_ticker.createdTime()/1000,
true,
*this);
}
}
void HttpSession::onManager() {
@ -291,6 +302,7 @@ bool HttpSession::checkLiveFlvStream(const function<void()> &cb){
try{
start(getPoller(),rtmp_src);
_is_flv_stream = true;
}catch (std::exception &ex){
//该rtmp源不存在
shutdown(SockException(Err_shutdown,"rtmp mediasource released"));
@ -375,10 +387,7 @@ static bool checkHls(BroadcastHttpAccessArgs){
return NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed,args_copy,mediaAuthInvoker,sender);
}
void HttpSession::canAccessPath(const string &path_in,bool is_dir,const function<void(const string &errMsg,const HttpServerCookie::Ptr &cookie)> &callback_in){
auto path = path_in;
replace(const_cast<string &>(path),"//","/");
void HttpSession::canAccessPath(const string &path,bool is_dir,const function<void(const string &errMsg,const HttpServerCookie::Ptr &cookie)> &callback_in){
auto callback = [callback_in,this](const string &errMsg,const HttpServerCookie::Ptr &cookie){
try {
callback_in(errMsg,cookie);
@ -507,7 +516,7 @@ void HttpSession::Handle_Req_GET(int64_t &content_len) {
GET_CONFIG(uint32_t,reqCnt,Http::kMaxReqCount);
GET_CONFIG(bool,enableVhost,General::kEnableVhost);
GET_CONFIG(string,rootPath,Http::kRootPath);
string strFile = enableVhost ? rootPath + "/" + _mediaInfo._vhost + _parser.Url() :rootPath + _parser.Url();
auto strFile = File::absolutePath(enableVhost ? _mediaInfo._vhost + _parser.Url() : _parser.Url(),rootPath);
bool bClose = (strcasecmp(_parser["Connection"].data(),"close") == 0) || ( ++_iReqCnt > reqCnt);
do{

View File

@ -160,6 +160,7 @@ private:
//处理content数据的callback
function<bool (const char *data,uint64_t len) > _contentCallBack;
bool _flv_over_websocket = false;
bool _is_flv_stream = false;
};

View File

@ -153,6 +153,14 @@ void MP4Recorder::onTrackReady(const Track::Ptr & track){
}
}
void MP4Recorder::resetTracks() {
closeFile();
_tracks.clear();
_haveVideo = false;
_createFileTicker.resetTime();
MediaSink::resetTracks();
}
} /* namespace mediakit */

View File

@ -63,6 +63,11 @@ public:
const string &strApp,
const string &strStreamId);
virtual ~MP4Recorder();
/**
* Track
*/
void resetTracks() override;
private:
/**
* Track输出frameonAllTrackReady触发后才会调用此方法

View File

@ -44,10 +44,11 @@ MediaReader::MediaReader(const string &strVhost,const string &strApp, const stri
GET_CONFIG(string,recordPath,Record::kFilePath);
GET_CONFIG(bool,enableVhost,General::kEnableVhost);
if(enableVhost){
strFileName = recordPath + "/" + strVhost + "/" + strApp + "/" + strId;
strFileName = strVhost + "/" + strApp + "/" + strId;
}else{
strFileName = recordPath + "/" + strApp + "/" + strId;
strFileName = strApp + "/" + strId;
}
strFileName = File::absolutePath(strFileName,recordPath);
}
_hMP4File = MP4Read(strFileName.data());

View File

@ -56,13 +56,15 @@ MediaRecorder::MediaRecorder(const string &strVhost_tmp,
#if defined(ENABLE_HLS)
if(enableHls) {
string m3u8FilePath;
string params;
if(enableVhost){
m3u8FilePath = hlsPath + "/" + strVhost + "/" + strApp + "/" + strId + "/hls.m3u8";
_hlsRecorder.reset(new HlsRecorder(m3u8FilePath,string(VHOST_KEY) + "=" + strVhost ,hlsBufSize, hlsDuration, hlsNum));
m3u8FilePath = strVhost + "/" + strApp + "/" + strId + "/hls.m3u8";
params = string(VHOST_KEY) + "=" + strVhost;
}else{
m3u8FilePath = hlsPath + "/" + strApp + "/" + strId + "/hls.m3u8";
_hlsRecorder.reset(new HlsRecorder(m3u8FilePath,"",hlsBufSize, hlsDuration, hlsNum));
m3u8FilePath = strApp + "/" + strId + "/hls.m3u8";
}
m3u8FilePath = File::absolutePath(m3u8FilePath,hlsPath);
_hlsRecorder.reset(new HlsRecorder(m3u8FilePath,params,hlsBufSize, hlsDuration, hlsNum));
}
#endif //defined(ENABLE_HLS)
@ -73,10 +75,11 @@ MediaRecorder::MediaRecorder(const string &strVhost_tmp,
if(enableMp4){
string mp4FilePath;
if(enableVhost){
mp4FilePath = recordPath + "/" + strVhost + "/" + recordAppName + "/" + strApp + "/" + strId + "/";
mp4FilePath = strVhost + "/" + recordAppName + "/" + strApp + "/" + strId + "/";
} else {
mp4FilePath = recordPath + "/" + recordAppName + "/" + strApp + "/" + strId + "/";
mp4FilePath = recordAppName + "/" + strApp + "/" + strId + "/";
}
mp4FilePath = File::absolutePath(mp4FilePath,recordPath);
_mp4Recorder.reset(new MP4Recorder(mp4FilePath,strVhost,strApp,strId));
}
#endif //defined(ENABLE_MP4RECORD)

View File

@ -44,13 +44,17 @@ RtmpSession::~RtmpSession() {
}
void RtmpSession::onError(const SockException& err) {
WarnP(this) << err.what();
bool isPlayer = !_pPublisherSrc;
WarnP(this) << (isPlayer ? "播放器(" : "推流器(")
<< _mediaInfo._vhost << "/"
<< _mediaInfo._app << "/"
<< _mediaInfo._streamid
<< ")断开:" << err.what();
//流量统计事件广播
GET_CONFIG(uint32_t,iFlowThreshold,General::kFlowThreshold);
if(_ui64TotalBytes > iFlowThreshold * 1024){
bool isPlayer = !_pPublisherSrc;
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport,
_mediaInfo,
_ui64TotalBytes,

View File

@ -85,7 +85,13 @@ RtspSession::~RtspSession() {
}
void RtspSession::onError(const SockException& err) {
WarnP(this) << err.what();
bool isPlayer = !_pushSrc;
WarnP(this) << (isPlayer ? "播放器(" : "推流器(")
<< _mediaInfo._vhost << "/"
<< _mediaInfo._app << "/"
<< _mediaInfo._streamid
<< ")断开:" << err.what();
if (_rtpType == Rtsp::RTP_MULTICAST) {
//取消UDP端口监听
UDPServer::Instance().stopListenPeer(get_peer_ip().data(), this);
@ -100,7 +106,6 @@ void RtspSession::onError(const SockException& err) {
//流量统计事件广播
GET_CONFIG(uint32_t,iFlowThreshold,General::kFlowThreshold);
if(_ui64TotalBytes > iFlowThreshold * 1024){
bool isPlayer = !_pushSrc;
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport,
_mediaInfo,
_ui64TotalBytes,